init
This commit is contained in:
833
vendor/yiisoft/yii2/db/ActiveQuery.php
vendored
Normal file
833
vendor/yiisoft/yii2/db/ActiveQuery.php
vendored
Normal file
@@ -0,0 +1,833 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db;
|
||||
|
||||
use yii\base\InvalidConfigException;
|
||||
|
||||
/**
|
||||
* ActiveQuery represents a DB query associated with an Active Record class.
|
||||
*
|
||||
* An ActiveQuery can be a normal query or be used in a relational context.
|
||||
*
|
||||
* ActiveQuery instances are usually created by [[ActiveRecord::find()]] and [[ActiveRecord::findBySql()]].
|
||||
* Relational queries are created by [[ActiveRecord::hasOne()]] and [[ActiveRecord::hasMany()]].
|
||||
*
|
||||
* Normal Query
|
||||
* ------------
|
||||
*
|
||||
* ActiveQuery mainly provides the following methods to retrieve the query results:
|
||||
*
|
||||
* - [[one()]]: returns a single record populated with the first row of data.
|
||||
* - [[all()]]: returns all records based on the query results.
|
||||
* - [[count()]]: returns the number of records.
|
||||
* - [[sum()]]: returns the sum over the specified column.
|
||||
* - [[average()]]: returns the average over the specified column.
|
||||
* - [[min()]]: returns the min over the specified column.
|
||||
* - [[max()]]: returns the max over the specified column.
|
||||
* - [[scalar()]]: returns the value of the first column in the first row of the query result.
|
||||
* - [[column()]]: returns the value of the first column in the query result.
|
||||
* - [[exists()]]: returns a value indicating whether the query result has data or not.
|
||||
*
|
||||
* Because ActiveQuery extends from [[Query]], one can use query methods, such as [[where()]],
|
||||
* [[orderBy()]] to customize the query options.
|
||||
*
|
||||
* ActiveQuery also provides the following additional query options:
|
||||
*
|
||||
* - [[with()]]: list of relations that this query should be performed with.
|
||||
* - [[joinWith()]]: reuse a relation query definition to add a join to a query.
|
||||
* - [[indexBy()]]: the name of the column by which the query result should be indexed.
|
||||
* - [[asArray()]]: whether to return each record as an array.
|
||||
*
|
||||
* These options can be configured using methods of the same name. For example:
|
||||
*
|
||||
* ```php
|
||||
* $customers = Customer::find()->with('orders')->asArray()->all();
|
||||
* ```
|
||||
*
|
||||
* Relational query
|
||||
* ----------------
|
||||
*
|
||||
* In relational context ActiveQuery represents a relation between two Active Record classes.
|
||||
*
|
||||
* Relational ActiveQuery instances are usually created by calling [[ActiveRecord::hasOne()]] and
|
||||
* [[ActiveRecord::hasMany()]]. An Active Record class declares a relation by defining
|
||||
* a getter method which calls one of the above methods and returns the created ActiveQuery object.
|
||||
*
|
||||
* A relation is specified by [[link]] which represents the association between columns
|
||||
* of different tables; and the multiplicity of the relation is indicated by [[multiple]].
|
||||
*
|
||||
* If a relation involves a junction table, it may be specified by [[via()]] or [[viaTable()]] method.
|
||||
* These methods may only be called in a relational context. Same is true for [[inverseOf()]], which
|
||||
* marks a relation as inverse of another relation and [[onCondition()]] which adds a condition that
|
||||
* is to be added to relational query join condition.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @author Carsten Brandt <mail@cebe.cc>
|
||||
* @since 2.0
|
||||
*/
|
||||
class ActiveQuery extends Query implements ActiveQueryInterface
|
||||
{
|
||||
use ActiveQueryTrait;
|
||||
use ActiveRelationTrait;
|
||||
|
||||
/**
|
||||
* @event Event an event that is triggered when the query is initialized via [[init()]].
|
||||
*/
|
||||
const EVENT_INIT = 'init';
|
||||
|
||||
/**
|
||||
* @var string the SQL statement to be executed for retrieving AR records.
|
||||
* This is set by [[ActiveRecord::findBySql()]].
|
||||
*/
|
||||
public $sql;
|
||||
/**
|
||||
* @var string|array the join condition to be used when this query is used in a relational context.
|
||||
* The condition will be used in the ON part when [[ActiveQuery::joinWith()]] is called.
|
||||
* Otherwise, the condition will be used in the WHERE part of a query.
|
||||
* Please refer to [[Query::where()]] on how to specify this parameter.
|
||||
* @see onCondition()
|
||||
*/
|
||||
public $on;
|
||||
/**
|
||||
* @var array a list of relations that this query should be joined with
|
||||
*/
|
||||
public $joinWith;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param string $modelClass the model class associated with this query
|
||||
* @param array $config configurations to be applied to the newly created query object
|
||||
*/
|
||||
public function __construct($modelClass, $config = [])
|
||||
{
|
||||
$this->modelClass = $modelClass;
|
||||
parent::__construct($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the object.
|
||||
* This method is called at the end of the constructor. The default implementation will trigger
|
||||
* an [[EVENT_INIT]] event. If you override this method, make sure you call the parent implementation at the end
|
||||
* to ensure triggering of the event.
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
parent::init();
|
||||
$this->trigger(self::EVENT_INIT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes query and returns all results as an array.
|
||||
* @param Connection $db the DB connection used to create the DB command.
|
||||
* If null, the DB connection returned by [[modelClass]] will be used.
|
||||
* @return array|ActiveRecord[] the query results. If the query results in nothing, an empty array will be returned.
|
||||
*/
|
||||
public function all($db = null)
|
||||
{
|
||||
return parent::all($db);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function prepare($builder)
|
||||
{
|
||||
// NOTE: because the same ActiveQuery may be used to build different SQL statements
|
||||
// (e.g. by ActiveDataProvider, one for count query, the other for row data query,
|
||||
// it is important to make sure the same ActiveQuery can be used to build SQL statements
|
||||
// multiple times.
|
||||
if (!empty($this->joinWith)) {
|
||||
$this->buildJoinWith();
|
||||
$this->joinWith = null; // clean it up to avoid issue https://github.com/yiisoft/yii2/issues/2687
|
||||
}
|
||||
|
||||
if (empty($this->from)) {
|
||||
$this->from = [$this->getPrimaryTableName()];
|
||||
}
|
||||
|
||||
if (empty($this->select) && !empty($this->join)) {
|
||||
list(, $alias) = $this->getTableNameAndAlias();
|
||||
$this->select = ["$alias.*"];
|
||||
}
|
||||
|
||||
if ($this->primaryModel === null) {
|
||||
// eager loading
|
||||
$query = Query::create($this);
|
||||
} else {
|
||||
// lazy loading of a relation
|
||||
$where = $this->where;
|
||||
|
||||
if ($this->via instanceof self) {
|
||||
// via junction table
|
||||
$viaModels = $this->via->findJunctionRows([$this->primaryModel]);
|
||||
$this->filterByModels($viaModels);
|
||||
} elseif (is_array($this->via)) {
|
||||
// via relation
|
||||
/* @var $viaQuery ActiveQuery */
|
||||
list($viaName, $viaQuery) = $this->via;
|
||||
if ($viaQuery->multiple) {
|
||||
$viaModels = $viaQuery->all();
|
||||
$this->primaryModel->populateRelation($viaName, $viaModels);
|
||||
} else {
|
||||
$model = $viaQuery->one();
|
||||
$this->primaryModel->populateRelation($viaName, $model);
|
||||
$viaModels = $model === null ? [] : [$model];
|
||||
}
|
||||
$this->filterByModels($viaModels);
|
||||
} else {
|
||||
$this->filterByModels([$this->primaryModel]);
|
||||
}
|
||||
|
||||
$query = Query::create($this);
|
||||
$this->where = $where;
|
||||
}
|
||||
|
||||
if (!empty($this->on)) {
|
||||
$query->andWhere($this->on);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function populate($rows)
|
||||
{
|
||||
if (empty($rows)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$models = $this->createModels($rows);
|
||||
if (!empty($this->join) && $this->indexBy === null) {
|
||||
$models = $this->removeDuplicatedModels($models);
|
||||
}
|
||||
if (!empty($this->with)) {
|
||||
$this->findWith($this->with, $models);
|
||||
}
|
||||
|
||||
if ($this->inverseOf !== null) {
|
||||
$this->addInverseRelations($models);
|
||||
}
|
||||
|
||||
if (!$this->asArray) {
|
||||
foreach ($models as $model) {
|
||||
$model->afterFind();
|
||||
}
|
||||
}
|
||||
|
||||
return parent::populate($models);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes duplicated models by checking their primary key values.
|
||||
* This method is mainly called when a join query is performed, which may cause duplicated rows being returned.
|
||||
* @param array $models the models to be checked
|
||||
* @throws InvalidConfigException if model primary key is empty
|
||||
* @return array the distinctive models
|
||||
*/
|
||||
private function removeDuplicatedModels($models)
|
||||
{
|
||||
$hash = [];
|
||||
/* @var $class ActiveRecord */
|
||||
$class = $this->modelClass;
|
||||
$pks = $class::primaryKey();
|
||||
|
||||
if (count($pks) > 1) {
|
||||
// composite primary key
|
||||
foreach ($models as $i => $model) {
|
||||
$key = [];
|
||||
foreach ($pks as $pk) {
|
||||
if (!isset($model[$pk])) {
|
||||
// do not continue if the primary key is not part of the result set
|
||||
break 2;
|
||||
}
|
||||
$key[] = $model[$pk];
|
||||
}
|
||||
$key = serialize($key);
|
||||
if (isset($hash[$key])) {
|
||||
unset($models[$i]);
|
||||
} else {
|
||||
$hash[$key] = true;
|
||||
}
|
||||
}
|
||||
} elseif (empty($pks)) {
|
||||
throw new InvalidConfigException("Primary key of '{$class}' can not be empty.");
|
||||
} else {
|
||||
// single column primary key
|
||||
$pk = reset($pks);
|
||||
foreach ($models as $i => $model) {
|
||||
if (!isset($model[$pk])) {
|
||||
// do not continue if the primary key is not part of the result set
|
||||
break;
|
||||
}
|
||||
$key = $model[$pk];
|
||||
if (isset($hash[$key])) {
|
||||
unset($models[$i]);
|
||||
} elseif ($key !== null) {
|
||||
$hash[$key] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array_values($models);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes query and returns a single row of result.
|
||||
* @param Connection|null $db the DB connection used to create the DB command.
|
||||
* If `null`, the DB connection returned by [[modelClass]] will be used.
|
||||
* @return ActiveRecord|array|null a single row of query result. Depending on the setting of [[asArray]],
|
||||
* the query result may be either an array or an ActiveRecord object. `null` will be returned
|
||||
* if the query results in nothing.
|
||||
*/
|
||||
public function one($db = null)
|
||||
{
|
||||
$row = parent::one($db);
|
||||
if ($row !== false) {
|
||||
$models = $this->populate([$row]);
|
||||
return reset($models) ?: null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a DB command that can be used to execute this query.
|
||||
* @param Connection|null $db the DB connection used to create the DB command.
|
||||
* If `null`, the DB connection returned by [[modelClass]] will be used.
|
||||
* @return Command the created DB command instance.
|
||||
*/
|
||||
public function createCommand($db = null)
|
||||
{
|
||||
/* @var $modelClass ActiveRecord */
|
||||
$modelClass = $this->modelClass;
|
||||
if ($db === null) {
|
||||
$db = $modelClass::getDb();
|
||||
}
|
||||
|
||||
if ($this->sql === null) {
|
||||
list($sql, $params) = $db->getQueryBuilder()->build($this);
|
||||
} else {
|
||||
$sql = $this->sql;
|
||||
$params = $this->params;
|
||||
}
|
||||
|
||||
$command = $db->createCommand($sql, $params);
|
||||
$this->setCommandCache($command);
|
||||
|
||||
return $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function queryScalar($selectExpression, $db)
|
||||
{
|
||||
/* @var $modelClass ActiveRecord */
|
||||
$modelClass = $this->modelClass;
|
||||
if ($db === null) {
|
||||
$db = $modelClass::getDb();
|
||||
}
|
||||
|
||||
if ($this->sql === null) {
|
||||
return parent::queryScalar($selectExpression, $db);
|
||||
}
|
||||
|
||||
$command = (new Query())->select([$selectExpression])
|
||||
->from(['c' => "({$this->sql})"])
|
||||
->params($this->params)
|
||||
->createCommand($db);
|
||||
$this->setCommandCache($command);
|
||||
|
||||
return $command->queryScalar();
|
||||
}
|
||||
|
||||
/**
|
||||
* Joins with the specified relations.
|
||||
*
|
||||
* This method allows you to reuse existing relation definitions to perform JOIN queries.
|
||||
* Based on the definition of the specified relation(s), the method will append one or multiple
|
||||
* JOIN statements to the current query.
|
||||
*
|
||||
* If the `$eagerLoading` parameter is true, the method will also perform eager loading for the specified relations,
|
||||
* which is equivalent to calling [[with()]] using the specified relations.
|
||||
*
|
||||
* Note that because a JOIN query will be performed, you are responsible to disambiguate column names.
|
||||
*
|
||||
* This method differs from [[with()]] in that it will build up and execute a JOIN SQL statement
|
||||
* for the primary table. And when `$eagerLoading` is true, it will call [[with()]] in addition with the specified relations.
|
||||
*
|
||||
* @param string|array $with the relations to be joined. This can either be a string, representing a relation name or
|
||||
* an array with the following semantics:
|
||||
*
|
||||
* - Each array element represents a single relation.
|
||||
* - You may specify the relation name as the array key and provide an anonymous functions that
|
||||
* can be used to modify the relation queries on-the-fly as the array value.
|
||||
* - If a relation query does not need modification, you may use the relation name as the array value.
|
||||
*
|
||||
* The relation name may optionally contain an alias for the relation table (e.g. `books b`).
|
||||
*
|
||||
* Sub-relations can also be specified, see [[with()]] for the syntax.
|
||||
*
|
||||
* In the following you find some examples:
|
||||
*
|
||||
* ```php
|
||||
* // find all orders that contain books, and eager loading "books"
|
||||
* Order::find()->joinWith('books', true, 'INNER JOIN')->all();
|
||||
* // find all orders, eager loading "books", and sort the orders and books by the book names.
|
||||
* Order::find()->joinWith([
|
||||
* 'books' => function (\yii\db\ActiveQuery $query) {
|
||||
* $query->orderBy('item.name');
|
||||
* }
|
||||
* ])->all();
|
||||
* // find all orders that contain books of the category 'Science fiction', using the alias "b" for the books table
|
||||
* Order::find()->joinWith(['books b'], true, 'INNER JOIN')->where(['b.category' => 'Science fiction'])->all();
|
||||
* ```
|
||||
*
|
||||
* The alias syntax is available since version 2.0.7.
|
||||
*
|
||||
* @param bool|array $eagerLoading whether to eager load the relations
|
||||
* specified in `$with`. When this is a boolean, it applies to all
|
||||
* relations specified in `$with`. Use an array to explicitly list which
|
||||
* relations in `$with` need to be eagerly loaded. Note, that this does
|
||||
* not mean, that the relations are populated from the query result. An
|
||||
* extra query will still be performed to bring in the related data.
|
||||
* Defaults to `true`.
|
||||
* @param string|array $joinType the join type of the relations specified in `$with`.
|
||||
* When this is a string, it applies to all relations specified in `$with`. Use an array
|
||||
* in the format of `relationName => joinType` to specify different join types for different relations.
|
||||
* @return $this the query object itself
|
||||
*/
|
||||
public function joinWith($with, $eagerLoading = true, $joinType = 'LEFT JOIN')
|
||||
{
|
||||
$relations = [];
|
||||
foreach ((array) $with as $name => $callback) {
|
||||
if (is_int($name)) {
|
||||
$name = $callback;
|
||||
$callback = null;
|
||||
}
|
||||
|
||||
if (preg_match('/^(.*?)(?:\s+AS\s+|\s+)(\w+)$/i', $name, $matches)) {
|
||||
// relation is defined with an alias, adjust callback to apply alias
|
||||
list(, $relation, $alias) = $matches;
|
||||
$name = $relation;
|
||||
$callback = function ($query) use ($callback, $alias) {
|
||||
/* @var $query ActiveQuery */
|
||||
$query->alias($alias);
|
||||
if ($callback !== null) {
|
||||
call_user_func($callback, $query);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if ($callback === null) {
|
||||
$relations[] = $name;
|
||||
} else {
|
||||
$relations[$name] = $callback;
|
||||
}
|
||||
}
|
||||
$this->joinWith[] = [$relations, $eagerLoading, $joinType];
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function buildJoinWith()
|
||||
{
|
||||
$join = $this->join;
|
||||
$this->join = [];
|
||||
|
||||
/* @var $modelClass ActiveRecordInterface */
|
||||
$modelClass = $this->modelClass;
|
||||
$model = $modelClass::instance();
|
||||
foreach ($this->joinWith as $config) {
|
||||
list($with, $eagerLoading, $joinType) = $config;
|
||||
$this->joinWithRelations($model, $with, $joinType);
|
||||
|
||||
if (is_array($eagerLoading)) {
|
||||
foreach ($with as $name => $callback) {
|
||||
if (is_int($name)) {
|
||||
if (!in_array($callback, $eagerLoading, true)) {
|
||||
unset($with[$name]);
|
||||
}
|
||||
} elseif (!in_array($name, $eagerLoading, true)) {
|
||||
unset($with[$name]);
|
||||
}
|
||||
}
|
||||
} elseif (!$eagerLoading) {
|
||||
$with = [];
|
||||
}
|
||||
|
||||
$this->with($with);
|
||||
}
|
||||
|
||||
// remove duplicated joins added by joinWithRelations that may be added
|
||||
// e.g. when joining a relation and a via relation at the same time
|
||||
$uniqueJoins = [];
|
||||
foreach ($this->join as $j) {
|
||||
$uniqueJoins[serialize($j)] = $j;
|
||||
}
|
||||
$this->join = array_values($uniqueJoins);
|
||||
|
||||
if (!empty($join)) {
|
||||
// append explicit join to joinWith()
|
||||
// https://github.com/yiisoft/yii2/issues/2880
|
||||
$this->join = empty($this->join) ? $join : array_merge($this->join, $join);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inner joins with the specified relations.
|
||||
* This is a shortcut method to [[joinWith()]] with the join type set as "INNER JOIN".
|
||||
* Please refer to [[joinWith()]] for detailed usage of this method.
|
||||
* @param string|array $with the relations to be joined with.
|
||||
* @param bool|array $eagerLoading whether to eager load the relations.
|
||||
* Note, that this does not mean, that the relations are populated from the
|
||||
* query result. An extra query will still be performed to bring in the
|
||||
* related data.
|
||||
* @return $this the query object itself
|
||||
* @see joinWith()
|
||||
*/
|
||||
public function innerJoinWith($with, $eagerLoading = true)
|
||||
{
|
||||
return $this->joinWith($with, $eagerLoading, 'INNER JOIN');
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies the current query by adding join fragments based on the given relations.
|
||||
* @param ActiveRecord $model the primary model
|
||||
* @param array $with the relations to be joined
|
||||
* @param string|array $joinType the join type
|
||||
*/
|
||||
private function joinWithRelations($model, $with, $joinType)
|
||||
{
|
||||
$relations = [];
|
||||
|
||||
foreach ($with as $name => $callback) {
|
||||
if (is_int($name)) {
|
||||
$name = $callback;
|
||||
$callback = null;
|
||||
}
|
||||
|
||||
$primaryModel = $model;
|
||||
$parent = $this;
|
||||
$prefix = '';
|
||||
while (($pos = strpos($name, '.')) !== false) {
|
||||
$childName = substr($name, $pos + 1);
|
||||
$name = substr($name, 0, $pos);
|
||||
$fullName = $prefix === '' ? $name : "$prefix.$name";
|
||||
if (!isset($relations[$fullName])) {
|
||||
$relations[$fullName] = $relation = $primaryModel->getRelation($name);
|
||||
$this->joinWithRelation($parent, $relation, $this->getJoinType($joinType, $fullName));
|
||||
} else {
|
||||
$relation = $relations[$fullName];
|
||||
}
|
||||
/* @var $relationModelClass ActiveRecordInterface */
|
||||
$relationModelClass = $relation->modelClass;
|
||||
$primaryModel = $relationModelClass::instance();
|
||||
$parent = $relation;
|
||||
$prefix = $fullName;
|
||||
$name = $childName;
|
||||
}
|
||||
|
||||
$fullName = $prefix === '' ? $name : "$prefix.$name";
|
||||
if (!isset($relations[$fullName])) {
|
||||
$relations[$fullName] = $relation = $primaryModel->getRelation($name);
|
||||
if ($callback !== null) {
|
||||
call_user_func($callback, $relation);
|
||||
}
|
||||
if (!empty($relation->joinWith)) {
|
||||
$relation->buildJoinWith();
|
||||
}
|
||||
$this->joinWithRelation($parent, $relation, $this->getJoinType($joinType, $fullName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the join type based on the given join type parameter and the relation name.
|
||||
* @param string|array $joinType the given join type(s)
|
||||
* @param string $name relation name
|
||||
* @return string the real join type
|
||||
*/
|
||||
private function getJoinType($joinType, $name)
|
||||
{
|
||||
if (is_array($joinType) && isset($joinType[$name])) {
|
||||
return $joinType[$name];
|
||||
}
|
||||
|
||||
return is_string($joinType) ? $joinType : 'INNER JOIN';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the table name and the table alias for [[modelClass]].
|
||||
* @return array the table name and the table alias.
|
||||
* @internal
|
||||
*/
|
||||
private function getTableNameAndAlias()
|
||||
{
|
||||
if (empty($this->from)) {
|
||||
$tableName = $this->getPrimaryTableName();
|
||||
} else {
|
||||
$tableName = '';
|
||||
foreach ($this->from as $alias => $tableName) {
|
||||
if (is_string($alias)) {
|
||||
return [$tableName, $alias];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (preg_match('/^(.*?)\s+({{\w+}}|\w+)$/', $tableName, $matches)) {
|
||||
$alias = $matches[2];
|
||||
} else {
|
||||
$alias = $tableName;
|
||||
}
|
||||
|
||||
return [$tableName, $alias];
|
||||
}
|
||||
|
||||
/**
|
||||
* Joins a parent query with a child query.
|
||||
* The current query object will be modified accordingly.
|
||||
* @param ActiveQuery $parent
|
||||
* @param ActiveQuery $child
|
||||
* @param string $joinType
|
||||
*/
|
||||
private function joinWithRelation($parent, $child, $joinType)
|
||||
{
|
||||
$via = $child->via;
|
||||
$child->via = null;
|
||||
if ($via instanceof self) {
|
||||
// via table
|
||||
$this->joinWithRelation($parent, $via, $joinType);
|
||||
$this->joinWithRelation($via, $child, $joinType);
|
||||
return;
|
||||
} elseif (is_array($via)) {
|
||||
// via relation
|
||||
$this->joinWithRelation($parent, $via[1], $joinType);
|
||||
$this->joinWithRelation($via[1], $child, $joinType);
|
||||
return;
|
||||
}
|
||||
|
||||
list($parentTable, $parentAlias) = $parent->getTableNameAndAlias();
|
||||
list($childTable, $childAlias) = $child->getTableNameAndAlias();
|
||||
|
||||
if (!empty($child->link)) {
|
||||
if (strpos($parentAlias, '{{') === false) {
|
||||
$parentAlias = '{{' . $parentAlias . '}}';
|
||||
}
|
||||
if (strpos($childAlias, '{{') === false) {
|
||||
$childAlias = '{{' . $childAlias . '}}';
|
||||
}
|
||||
|
||||
$on = [];
|
||||
foreach ($child->link as $childColumn => $parentColumn) {
|
||||
$on[] = "$parentAlias.[[$parentColumn]] = $childAlias.[[$childColumn]]";
|
||||
}
|
||||
$on = implode(' AND ', $on);
|
||||
if (!empty($child->on)) {
|
||||
$on = ['and', $on, $child->on];
|
||||
}
|
||||
} else {
|
||||
$on = $child->on;
|
||||
}
|
||||
$this->join($joinType, empty($child->from) ? $childTable : $child->from, $on);
|
||||
|
||||
if (!empty($child->where)) {
|
||||
$this->andWhere($child->where);
|
||||
}
|
||||
if (!empty($child->having)) {
|
||||
$this->andHaving($child->having);
|
||||
}
|
||||
if (!empty($child->orderBy)) {
|
||||
$this->addOrderBy($child->orderBy);
|
||||
}
|
||||
if (!empty($child->groupBy)) {
|
||||
$this->addGroupBy($child->groupBy);
|
||||
}
|
||||
if (!empty($child->params)) {
|
||||
$this->addParams($child->params);
|
||||
}
|
||||
if (!empty($child->join)) {
|
||||
foreach ($child->join as $join) {
|
||||
$this->join[] = $join;
|
||||
}
|
||||
}
|
||||
if (!empty($child->union)) {
|
||||
foreach ($child->union as $union) {
|
||||
$this->union[] = $union;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the ON condition for a relational query.
|
||||
* The condition will be used in the ON part when [[ActiveQuery::joinWith()]] is called.
|
||||
* Otherwise, the condition will be used in the WHERE part of a query.
|
||||
*
|
||||
* Use this method to specify additional conditions when declaring a relation in the [[ActiveRecord]] class:
|
||||
*
|
||||
* ```php
|
||||
* public function getActiveUsers()
|
||||
* {
|
||||
* return $this->hasMany(User::className(), ['id' => 'user_id'])
|
||||
* ->onCondition(['active' => true]);
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Note that this condition is applied in case of a join as well as when fetching the related records.
|
||||
* Thus only fields of the related table can be used in the condition. Trying to access fields of the primary
|
||||
* record will cause an error in a non-join-query.
|
||||
*
|
||||
* @param string|array $condition the ON condition. Please refer to [[Query::where()]] on how to specify this parameter.
|
||||
* @param array $params the parameters (name => value) to be bound to the query.
|
||||
* @return $this the query object itself
|
||||
*/
|
||||
public function onCondition($condition, $params = [])
|
||||
{
|
||||
$this->on = $condition;
|
||||
$this->addParams($params);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an additional ON condition to the existing one.
|
||||
* The new condition and the existing one will be joined using the 'AND' operator.
|
||||
* @param string|array $condition the new ON condition. Please refer to [[where()]]
|
||||
* on how to specify this parameter.
|
||||
* @param array $params the parameters (name => value) to be bound to the query.
|
||||
* @return $this the query object itself
|
||||
* @see onCondition()
|
||||
* @see orOnCondition()
|
||||
*/
|
||||
public function andOnCondition($condition, $params = [])
|
||||
{
|
||||
if ($this->on === null) {
|
||||
$this->on = $condition;
|
||||
} else {
|
||||
$this->on = ['and', $this->on, $condition];
|
||||
}
|
||||
$this->addParams($params);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an additional ON condition to the existing one.
|
||||
* The new condition and the existing one will be joined using the 'OR' operator.
|
||||
* @param string|array $condition the new ON condition. Please refer to [[where()]]
|
||||
* on how to specify this parameter.
|
||||
* @param array $params the parameters (name => value) to be bound to the query.
|
||||
* @return $this the query object itself
|
||||
* @see onCondition()
|
||||
* @see andOnCondition()
|
||||
*/
|
||||
public function orOnCondition($condition, $params = [])
|
||||
{
|
||||
if ($this->on === null) {
|
||||
$this->on = $condition;
|
||||
} else {
|
||||
$this->on = ['or', $this->on, $condition];
|
||||
}
|
||||
$this->addParams($params);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the junction table for a relational query.
|
||||
*
|
||||
* Use this method to specify a junction table when declaring a relation in the [[ActiveRecord]] class:
|
||||
*
|
||||
* ```php
|
||||
* public function getItems()
|
||||
* {
|
||||
* return $this->hasMany(Item::className(), ['id' => 'item_id'])
|
||||
* ->viaTable('order_item', ['order_id' => 'id']);
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param string $tableName the name of the junction table.
|
||||
* @param array $link the link between the junction table and the table associated with [[primaryModel]].
|
||||
* The keys of the array represent the columns in the junction table, and the values represent the columns
|
||||
* in the [[primaryModel]] table.
|
||||
* @param callable $callable a PHP callback for customizing the relation associated with the junction table.
|
||||
* Its signature should be `function($query)`, where `$query` is the query to be customized.
|
||||
* @return $this the query object itself
|
||||
* @see via()
|
||||
*/
|
||||
public function viaTable($tableName, $link, callable $callable = null)
|
||||
{
|
||||
$modelClass = $this->primaryModel !== null ? get_class($this->primaryModel) : __CLASS__;
|
||||
|
||||
$relation = new self($modelClass, [
|
||||
'from' => [$tableName],
|
||||
'link' => $link,
|
||||
'multiple' => true,
|
||||
'asArray' => true,
|
||||
]);
|
||||
$this->via = $relation;
|
||||
if ($callable !== null) {
|
||||
call_user_func($callable, $relation);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define an alias for the table defined in [[modelClass]].
|
||||
*
|
||||
* This method will adjust [[from]] so that an already defined alias will be overwritten.
|
||||
* If none was defined, [[from]] will be populated with the given alias.
|
||||
*
|
||||
* @param string $alias the table alias.
|
||||
* @return $this the query object itself
|
||||
* @since 2.0.7
|
||||
*/
|
||||
public function alias($alias)
|
||||
{
|
||||
if (empty($this->from) || count($this->from) < 2) {
|
||||
list($tableName) = $this->getTableNameAndAlias();
|
||||
$this->from = [$alias => $tableName];
|
||||
} else {
|
||||
$tableName = $this->getPrimaryTableName();
|
||||
|
||||
foreach ($this->from as $key => $table) {
|
||||
if ($table === $tableName) {
|
||||
unset($this->from[$key]);
|
||||
$this->from[$alias] = $tableName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @since 2.0.12
|
||||
*/
|
||||
public function getTablesUsedInFrom()
|
||||
{
|
||||
if (empty($this->from)) {
|
||||
return $this->cleanUpTableNames([$this->getPrimaryTableName()]);
|
||||
}
|
||||
|
||||
return parent::getTablesUsedInFrom();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string primary table name
|
||||
* @since 2.0.12
|
||||
*/
|
||||
protected function getPrimaryTableName()
|
||||
{
|
||||
/* @var $modelClass ActiveRecord */
|
||||
$modelClass = $this->modelClass;
|
||||
return $modelClass::tableName();
|
||||
}
|
||||
}
|
||||
109
vendor/yiisoft/yii2/db/ActiveQueryInterface.php
vendored
Normal file
109
vendor/yiisoft/yii2/db/ActiveQueryInterface.php
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db;
|
||||
|
||||
/**
|
||||
* ActiveQueryInterface defines the common interface to be implemented by active record query classes.
|
||||
*
|
||||
* That are methods for either normal queries that return active records but also relational queries
|
||||
* in which the query represents a relation between two active record classes and will return related
|
||||
* records only.
|
||||
*
|
||||
* A class implementing this interface should also use [[ActiveQueryTrait]] and [[ActiveRelationTrait]].
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @author Carsten Brandt <mail@cebe.cc>
|
||||
* @since 2.0
|
||||
*/
|
||||
interface ActiveQueryInterface extends QueryInterface
|
||||
{
|
||||
/**
|
||||
* Sets the [[asArray]] property.
|
||||
* @param bool $value whether to return the query results in terms of arrays instead of Active Records.
|
||||
* @return $this the query object itself
|
||||
*/
|
||||
public function asArray($value = true);
|
||||
|
||||
/**
|
||||
* Executes query and returns a single row of result.
|
||||
* @param Connection $db the DB connection used to create the DB command.
|
||||
* If `null`, the DB connection returned by [[ActiveQueryTrait::$modelClass|modelClass]] will be used.
|
||||
* @return ActiveRecordInterface|array|null a single row of query result. Depending on the setting of [[asArray]],
|
||||
* the query result may be either an array or an ActiveRecord object. `null` will be returned
|
||||
* if the query results in nothing.
|
||||
*/
|
||||
public function one($db = null);
|
||||
|
||||
/**
|
||||
* Sets the [[indexBy]] property.
|
||||
* @param string|callable $column the name of the column by which the query results should be indexed by.
|
||||
* This can also be a callable (e.g. anonymous function) that returns the index value based on the given
|
||||
* row or model data. The signature of the callable should be:
|
||||
*
|
||||
* ```php
|
||||
* // $model is an AR instance when `asArray` is false,
|
||||
* // or an array of column values when `asArray` is true.
|
||||
* function ($model)
|
||||
* {
|
||||
* // return the index value corresponding to $model
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @return $this the query object itself
|
||||
*/
|
||||
public function indexBy($column);
|
||||
|
||||
/**
|
||||
* Specifies the relations with which this query should be performed.
|
||||
*
|
||||
* The parameters to this method can be either one or multiple strings, or a single array
|
||||
* of relation names and the optional callbacks to customize the relations.
|
||||
*
|
||||
* A relation name can refer to a relation defined in [[ActiveQueryTrait::modelClass|modelClass]]
|
||||
* or a sub-relation that stands for a relation of a related record.
|
||||
* For example, `orders.address` means the `address` relation defined
|
||||
* in the model class corresponding to the `orders` relation.
|
||||
*
|
||||
* The following are some usage examples:
|
||||
*
|
||||
* ```php
|
||||
* // find customers together with their orders and country
|
||||
* Customer::find()->with('orders', 'country')->all();
|
||||
* // find customers together with their orders and the orders' shipping address
|
||||
* Customer::find()->with('orders.address')->all();
|
||||
* // find customers together with their country and orders of status 1
|
||||
* Customer::find()->with([
|
||||
* 'orders' => function (\yii\db\ActiveQuery $query) {
|
||||
* $query->andWhere('status = 1');
|
||||
* },
|
||||
* 'country',
|
||||
* ])->all();
|
||||
* ```
|
||||
*
|
||||
* @return $this the query object itself
|
||||
*/
|
||||
public function with();
|
||||
|
||||
/**
|
||||
* Specifies the relation associated with the junction table for use in relational query.
|
||||
* @param string $relationName the relation name. This refers to a relation declared in the [[ActiveRelationTrait::primaryModel|primaryModel]] of the relation.
|
||||
* @param callable $callable a PHP callback for customizing the relation associated with the junction table.
|
||||
* Its signature should be `function($query)`, where `$query` is the query to be customized.
|
||||
* @return $this the relation object itself.
|
||||
*/
|
||||
public function via($relationName, callable $callable = null);
|
||||
|
||||
/**
|
||||
* Finds the related records for the specified primary record.
|
||||
* This method is invoked when a relation of an ActiveRecord is being accessed in a lazy fashion.
|
||||
* @param string $name the relation name
|
||||
* @param ActiveRecordInterface $model the primary model
|
||||
* @return mixed the related record(s)
|
||||
*/
|
||||
public function findFor($name, $model);
|
||||
}
|
||||
193
vendor/yiisoft/yii2/db/ActiveQueryTrait.php
vendored
Normal file
193
vendor/yiisoft/yii2/db/ActiveQueryTrait.php
vendored
Normal file
@@ -0,0 +1,193 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db;
|
||||
|
||||
/**
|
||||
* ActiveQueryTrait implements the common methods and properties for active record query classes.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @author Carsten Brandt <mail@cebe.cc>
|
||||
* @since 2.0
|
||||
*/
|
||||
trait ActiveQueryTrait
|
||||
{
|
||||
/**
|
||||
* @var string the name of the ActiveRecord class.
|
||||
*/
|
||||
public $modelClass;
|
||||
/**
|
||||
* @var array a list of relations that this query should be performed with
|
||||
*/
|
||||
public $with;
|
||||
/**
|
||||
* @var bool whether to return each record as an array. If false (default), an object
|
||||
* of [[modelClass]] will be created to represent each record.
|
||||
*/
|
||||
public $asArray;
|
||||
|
||||
|
||||
/**
|
||||
* Sets the [[asArray]] property.
|
||||
* @param bool $value whether to return the query results in terms of arrays instead of Active Records.
|
||||
* @return $this the query object itself
|
||||
*/
|
||||
public function asArray($value = true)
|
||||
{
|
||||
$this->asArray = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the relations with which this query should be performed.
|
||||
*
|
||||
* The parameters to this method can be either one or multiple strings, or a single array
|
||||
* of relation names and the optional callbacks to customize the relations.
|
||||
*
|
||||
* A relation name can refer to a relation defined in [[modelClass]]
|
||||
* or a sub-relation that stands for a relation of a related record.
|
||||
* For example, `orders.address` means the `address` relation defined
|
||||
* in the model class corresponding to the `orders` relation.
|
||||
*
|
||||
* The following are some usage examples:
|
||||
*
|
||||
* ```php
|
||||
* // find customers together with their orders and country
|
||||
* Customer::find()->with('orders', 'country')->all();
|
||||
* // find customers together with their orders and the orders' shipping address
|
||||
* Customer::find()->with('orders.address')->all();
|
||||
* // find customers together with their country and orders of status 1
|
||||
* Customer::find()->with([
|
||||
* 'orders' => function (\yii\db\ActiveQuery $query) {
|
||||
* $query->andWhere('status = 1');
|
||||
* },
|
||||
* 'country',
|
||||
* ])->all();
|
||||
* ```
|
||||
*
|
||||
* You can call `with()` multiple times. Each call will add relations to the existing ones.
|
||||
* For example, the following two statements are equivalent:
|
||||
*
|
||||
* ```php
|
||||
* Customer::find()->with('orders', 'country')->all();
|
||||
* Customer::find()->with('orders')->with('country')->all();
|
||||
* ```
|
||||
*
|
||||
* @return $this the query object itself
|
||||
*/
|
||||
public function with()
|
||||
{
|
||||
$with = func_get_args();
|
||||
if (isset($with[0]) && is_array($with[0])) {
|
||||
// the parameter is given as an array
|
||||
$with = $with[0];
|
||||
}
|
||||
|
||||
if (empty($this->with)) {
|
||||
$this->with = $with;
|
||||
} elseif (!empty($with)) {
|
||||
foreach ($with as $name => $value) {
|
||||
if (is_int($name)) {
|
||||
// repeating relation is fine as normalizeRelations() handle it well
|
||||
$this->with[] = $value;
|
||||
} else {
|
||||
$this->with[$name] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts found rows into model instances.
|
||||
* @param array $rows
|
||||
* @return array|ActiveRecord[]
|
||||
* @since 2.0.11
|
||||
*/
|
||||
protected function createModels($rows)
|
||||
{
|
||||
if ($this->asArray) {
|
||||
return $rows;
|
||||
} else {
|
||||
$models = [];
|
||||
/* @var $class ActiveRecord */
|
||||
$class = $this->modelClass;
|
||||
foreach ($rows as $row) {
|
||||
$model = $class::instantiate($row);
|
||||
$modelClass = get_class($model);
|
||||
$modelClass::populateRecord($model, $row);
|
||||
$models[] = $model;
|
||||
}
|
||||
return $models;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds records corresponding to one or multiple relations and populates them into the primary models.
|
||||
* @param array $with a list of relations that this query should be performed with. Please
|
||||
* refer to [[with()]] for details about specifying this parameter.
|
||||
* @param array|ActiveRecord[] $models the primary models (can be either AR instances or arrays)
|
||||
*/
|
||||
public function findWith($with, &$models)
|
||||
{
|
||||
$primaryModel = reset($models);
|
||||
if (!$primaryModel instanceof ActiveRecordInterface) {
|
||||
/* @var $modelClass ActiveRecordInterface */
|
||||
$modelClass = $this->modelClass;
|
||||
$primaryModel = $modelClass::instance();
|
||||
}
|
||||
$relations = $this->normalizeRelations($primaryModel, $with);
|
||||
/* @var $relation ActiveQuery */
|
||||
foreach ($relations as $name => $relation) {
|
||||
if ($relation->asArray === null) {
|
||||
// inherit asArray from primary query
|
||||
$relation->asArray($this->asArray);
|
||||
}
|
||||
$relation->populateRelation($name, $models);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ActiveRecord $model
|
||||
* @param array $with
|
||||
* @return ActiveQueryInterface[]
|
||||
*/
|
||||
private function normalizeRelations($model, $with)
|
||||
{
|
||||
$relations = [];
|
||||
foreach ($with as $name => $callback) {
|
||||
if (is_int($name)) {
|
||||
$name = $callback;
|
||||
$callback = null;
|
||||
}
|
||||
if (($pos = strpos($name, '.')) !== false) {
|
||||
// with sub-relations
|
||||
$childName = substr($name, $pos + 1);
|
||||
$name = substr($name, 0, $pos);
|
||||
} else {
|
||||
$childName = null;
|
||||
}
|
||||
|
||||
if (!isset($relations[$name])) {
|
||||
$relation = $model->getRelation($name);
|
||||
$relation->primaryModel = null;
|
||||
$relations[$name] = $relation;
|
||||
} else {
|
||||
$relation = $relations[$name];
|
||||
}
|
||||
|
||||
if (isset($childName)) {
|
||||
$relation->with[$childName] = $callback;
|
||||
} elseif ($callback !== null) {
|
||||
call_user_func($callback, $relation);
|
||||
}
|
||||
}
|
||||
|
||||
return $relations;
|
||||
}
|
||||
}
|
||||
748
vendor/yiisoft/yii2/db/ActiveRecord.php
vendored
Normal file
748
vendor/yiisoft/yii2/db/ActiveRecord.php
vendored
Normal file
@@ -0,0 +1,748 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db;
|
||||
|
||||
use Yii;
|
||||
use yii\base\InvalidArgumentException;
|
||||
use yii\base\InvalidConfigException;
|
||||
use yii\helpers\ArrayHelper;
|
||||
use yii\helpers\Inflector;
|
||||
use yii\helpers\StringHelper;
|
||||
|
||||
/**
|
||||
* ActiveRecord is the base class for classes representing relational data in terms of objects.
|
||||
*
|
||||
* Active Record implements the [Active Record design pattern](http://en.wikipedia.org/wiki/Active_record).
|
||||
* The premise behind Active Record is that an individual [[ActiveRecord]] object is associated with a specific
|
||||
* row in a database table. The object's attributes are mapped to the columns of the corresponding table.
|
||||
* Referencing an Active Record attribute is equivalent to accessing the corresponding table column for that record.
|
||||
*
|
||||
* As an example, say that the `Customer` ActiveRecord class is associated with the `customer` table.
|
||||
* This would mean that the class's `name` attribute is automatically mapped to the `name` column in `customer` table.
|
||||
* Thanks to Active Record, assuming the variable `$customer` is an object of type `Customer`, to get the value of
|
||||
* the `name` column for the table row, you can use the expression `$customer->name`.
|
||||
* In this example, Active Record is providing an object-oriented interface for accessing data stored in the database.
|
||||
* But Active Record provides much more functionality than this.
|
||||
*
|
||||
* To declare an ActiveRecord class you need to extend [[\yii\db\ActiveRecord]] and
|
||||
* implement the `tableName` method:
|
||||
*
|
||||
* ```php
|
||||
* <?php
|
||||
*
|
||||
* class Customer extends \yii\db\ActiveRecord
|
||||
* {
|
||||
* public static function tableName()
|
||||
* {
|
||||
* return 'customer';
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* The `tableName` method only has to return the name of the database table associated with the class.
|
||||
*
|
||||
* > Tip: You may also use the [Gii code generator](guide:start-gii) to generate ActiveRecord classes from your
|
||||
* > database tables.
|
||||
*
|
||||
* Class instances are obtained in one of two ways:
|
||||
*
|
||||
* * Using the `new` operator to create a new, empty object
|
||||
* * Using a method to fetch an existing record (or records) from the database
|
||||
*
|
||||
* Below is an example showing some typical usage of ActiveRecord:
|
||||
*
|
||||
* ```php
|
||||
* $user = new User();
|
||||
* $user->name = 'Qiang';
|
||||
* $user->save(); // a new row is inserted into user table
|
||||
*
|
||||
* // the following will retrieve the user 'CeBe' from the database
|
||||
* $user = User::find()->where(['name' => 'CeBe'])->one();
|
||||
*
|
||||
* // this will get related records from orders table when relation is defined
|
||||
* $orders = $user->orders;
|
||||
* ```
|
||||
*
|
||||
* For more details and usage information on ActiveRecord, see the [guide article on ActiveRecord](guide:db-active-record).
|
||||
*
|
||||
* @method ActiveQuery hasMany($class, array $link) see [[BaseActiveRecord::hasMany()]] for more info
|
||||
* @method ActiveQuery hasOne($class, array $link) see [[BaseActiveRecord::hasOne()]] for more info
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @author Carsten Brandt <mail@cebe.cc>
|
||||
* @since 2.0
|
||||
*/
|
||||
class ActiveRecord extends BaseActiveRecord
|
||||
{
|
||||
/**
|
||||
* The insert operation. This is mainly used when overriding [[transactions()]] to specify which operations are transactional.
|
||||
*/
|
||||
const OP_INSERT = 0x01;
|
||||
/**
|
||||
* The update operation. This is mainly used when overriding [[transactions()]] to specify which operations are transactional.
|
||||
*/
|
||||
const OP_UPDATE = 0x02;
|
||||
/**
|
||||
* The delete operation. This is mainly used when overriding [[transactions()]] to specify which operations are transactional.
|
||||
*/
|
||||
const OP_DELETE = 0x04;
|
||||
/**
|
||||
* All three operations: insert, update, delete.
|
||||
* This is a shortcut of the expression: OP_INSERT | OP_UPDATE | OP_DELETE.
|
||||
*/
|
||||
const OP_ALL = 0x07;
|
||||
|
||||
|
||||
/**
|
||||
* Loads default values from database table schema.
|
||||
*
|
||||
* You may call this method to load default values after creating a new instance:
|
||||
*
|
||||
* ```php
|
||||
* // class Customer extends \yii\db\ActiveRecord
|
||||
* $customer = new Customer();
|
||||
* $customer->loadDefaultValues();
|
||||
* ```
|
||||
*
|
||||
* @param bool $skipIfSet whether existing value should be preserved.
|
||||
* This will only set defaults for attributes that are `null`.
|
||||
* @return $this the model instance itself.
|
||||
*/
|
||||
public function loadDefaultValues($skipIfSet = true)
|
||||
{
|
||||
foreach (static::getTableSchema()->columns as $column) {
|
||||
if ($column->defaultValue !== null && (!$skipIfSet || $this->{$column->name} === null)) {
|
||||
$this->{$column->name} = $column->defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the database connection used by this AR class.
|
||||
* By default, the "db" application component is used as the database connection.
|
||||
* You may override this method if you want to use a different database connection.
|
||||
* @return Connection the database connection used by this AR class.
|
||||
*/
|
||||
public static function getDb()
|
||||
{
|
||||
return Yii::$app->getDb();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an [[ActiveQuery]] instance with a given SQL statement.
|
||||
*
|
||||
* Note that because the SQL statement is already specified, calling additional
|
||||
* query modification methods (such as `where()`, `order()`) on the created [[ActiveQuery]]
|
||||
* instance will have no effect. However, calling `with()`, `asArray()` or `indexBy()` is
|
||||
* still fine.
|
||||
*
|
||||
* Below is an example:
|
||||
*
|
||||
* ```php
|
||||
* $customers = Customer::findBySql('SELECT * FROM customer')->all();
|
||||
* ```
|
||||
*
|
||||
* @param string $sql the SQL statement to be executed
|
||||
* @param array $params parameters to be bound to the SQL statement during execution.
|
||||
* @return ActiveQuery the newly created [[ActiveQuery]] instance
|
||||
*/
|
||||
public static function findBySql($sql, $params = [])
|
||||
{
|
||||
$query = static::find();
|
||||
$query->sql = $sql;
|
||||
|
||||
return $query->params($params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds ActiveRecord instance(s) by the given condition.
|
||||
* This method is internally called by [[findOne()]] and [[findAll()]].
|
||||
* @param mixed $condition please refer to [[findOne()]] for the explanation of this parameter
|
||||
* @return ActiveQueryInterface the newly created [[ActiveQueryInterface|ActiveQuery]] instance.
|
||||
* @throws InvalidConfigException if there is no primary key defined.
|
||||
* @internal
|
||||
*/
|
||||
protected static function findByCondition($condition)
|
||||
{
|
||||
$query = static::find();
|
||||
|
||||
if (!ArrayHelper::isAssociative($condition)) {
|
||||
// query by primary key
|
||||
$primaryKey = static::primaryKey();
|
||||
if (isset($primaryKey[0])) {
|
||||
$pk = $primaryKey[0];
|
||||
if (!empty($query->join) || !empty($query->joinWith)) {
|
||||
$pk = static::tableName() . '.' . $pk;
|
||||
}
|
||||
// if condition is scalar, search for a single primary key, if it is array, search for multiple primary key values
|
||||
$condition = [$pk => is_array($condition) ? array_values($condition) : $condition];
|
||||
} else {
|
||||
throw new InvalidConfigException('"' . get_called_class() . '" must have a primary key.');
|
||||
}
|
||||
} elseif (is_array($condition)) {
|
||||
$condition = static::filterCondition($condition);
|
||||
}
|
||||
|
||||
return $query->andWhere($condition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters array condition before it is assiged to a Query filter.
|
||||
*
|
||||
* This method will ensure that an array condition only filters on existing table columns.
|
||||
*
|
||||
* @param array $condition condition to filter.
|
||||
* @return array filtered condition.
|
||||
* @throws InvalidArgumentException in case array contains unsafe values.
|
||||
* @since 2.0.15
|
||||
* @internal
|
||||
*/
|
||||
protected static function filterCondition(array $condition)
|
||||
{
|
||||
$result = [];
|
||||
// valid column names are table column names or column names prefixed with table name
|
||||
$columnNames = static::getTableSchema()->getColumnNames();
|
||||
$tableName = static::tableName();
|
||||
$columnNames = array_merge($columnNames, array_map(function($columnName) use ($tableName) {
|
||||
return "$tableName.$columnName";
|
||||
}, $columnNames));
|
||||
foreach ($condition as $key => $value) {
|
||||
if (is_string($key) && !in_array($key, $columnNames, true)) {
|
||||
throw new InvalidArgumentException('Key "' . $key . '" is not a column name and can not be used as a filter');
|
||||
}
|
||||
$result[$key] = is_array($value) ? array_values($value) : $value;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function refresh()
|
||||
{
|
||||
$query = static::find();
|
||||
$tableName = key($query->getTablesUsedInFrom());
|
||||
$pk = [];
|
||||
// disambiguate column names in case ActiveQuery adds a JOIN
|
||||
foreach ($this->getPrimaryKey(true) as $key => $value) {
|
||||
$pk[$tableName . '.' . $key] = $value;
|
||||
}
|
||||
$query->where($pk);
|
||||
|
||||
/* @var $record BaseActiveRecord */
|
||||
$record = $query->one();
|
||||
return $this->refreshInternal($record);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the whole table using the provided attribute values and conditions.
|
||||
*
|
||||
* For example, to change the status to be 1 for all customers whose status is 2:
|
||||
*
|
||||
* ```php
|
||||
* Customer::updateAll(['status' => 1], 'status = 2');
|
||||
* ```
|
||||
*
|
||||
* > Warning: If you do not specify any condition, this method will update **all** rows in the table.
|
||||
*
|
||||
* Note that this method will not trigger any events. If you need [[EVENT_BEFORE_UPDATE]] or
|
||||
* [[EVENT_AFTER_UPDATE]] to be triggered, you need to [[find()|find]] the models first and then
|
||||
* call [[update()]] on each of them. For example an equivalent of the example above would be:
|
||||
*
|
||||
* ```php
|
||||
* $models = Customer::find()->where('status = 2')->all();
|
||||
* foreach ($models as $model) {
|
||||
* $model->status = 1;
|
||||
* $model->update(false); // skipping validation as no user input is involved
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* For a large set of models you might consider using [[ActiveQuery::each()]] to keep memory usage within limits.
|
||||
*
|
||||
* @param array $attributes attribute values (name-value pairs) to be saved into the table
|
||||
* @param string|array $condition the conditions that will be put in the WHERE part of the UPDATE SQL.
|
||||
* Please refer to [[Query::where()]] on how to specify this parameter.
|
||||
* @param array $params the parameters (name => value) to be bound to the query.
|
||||
* @return int the number of rows updated
|
||||
*/
|
||||
public static function updateAll($attributes, $condition = '', $params = [])
|
||||
{
|
||||
$command = static::getDb()->createCommand();
|
||||
$command->update(static::tableName(), $attributes, $condition, $params);
|
||||
|
||||
return $command->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the whole table using the provided counter changes and conditions.
|
||||
*
|
||||
* For example, to increment all customers' age by 1,
|
||||
*
|
||||
* ```php
|
||||
* Customer::updateAllCounters(['age' => 1]);
|
||||
* ```
|
||||
*
|
||||
* Note that this method will not trigger any events.
|
||||
*
|
||||
* @param array $counters the counters to be updated (attribute name => increment value).
|
||||
* Use negative values if you want to decrement the counters.
|
||||
* @param string|array $condition the conditions that will be put in the WHERE part of the UPDATE SQL.
|
||||
* Please refer to [[Query::where()]] on how to specify this parameter.
|
||||
* @param array $params the parameters (name => value) to be bound to the query.
|
||||
* Do not name the parameters as `:bp0`, `:bp1`, etc., because they are used internally by this method.
|
||||
* @return int the number of rows updated
|
||||
*/
|
||||
public static function updateAllCounters($counters, $condition = '', $params = [])
|
||||
{
|
||||
$n = 0;
|
||||
foreach ($counters as $name => $value) {
|
||||
$counters[$name] = new Expression("[[$name]]+:bp{$n}", [":bp{$n}" => $value]);
|
||||
$n++;
|
||||
}
|
||||
$command = static::getDb()->createCommand();
|
||||
$command->update(static::tableName(), $counters, $condition, $params);
|
||||
|
||||
return $command->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes rows in the table using the provided conditions.
|
||||
*
|
||||
* For example, to delete all customers whose status is 3:
|
||||
*
|
||||
* ```php
|
||||
* Customer::deleteAll('status = 3');
|
||||
* ```
|
||||
*
|
||||
* > Warning: If you do not specify any condition, this method will delete **all** rows in the table.
|
||||
*
|
||||
* Note that this method will not trigger any events. If you need [[EVENT_BEFORE_DELETE]] or
|
||||
* [[EVENT_AFTER_DELETE]] to be triggered, you need to [[find()|find]] the models first and then
|
||||
* call [[delete()]] on each of them. For example an equivalent of the example above would be:
|
||||
*
|
||||
* ```php
|
||||
* $models = Customer::find()->where('status = 3')->all();
|
||||
* foreach ($models as $model) {
|
||||
* $model->delete();
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* For a large set of models you might consider using [[ActiveQuery::each()]] to keep memory usage within limits.
|
||||
*
|
||||
* @param string|array $condition the conditions that will be put in the WHERE part of the DELETE SQL.
|
||||
* Please refer to [[Query::where()]] on how to specify this parameter.
|
||||
* @param array $params the parameters (name => value) to be bound to the query.
|
||||
* @return int the number of rows deleted
|
||||
*/
|
||||
public static function deleteAll($condition = null, $params = [])
|
||||
{
|
||||
$command = static::getDb()->createCommand();
|
||||
$command->delete(static::tableName(), $condition, $params);
|
||||
|
||||
return $command->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return ActiveQuery the newly created [[ActiveQuery]] instance.
|
||||
*/
|
||||
public static function find()
|
||||
{
|
||||
return Yii::createObject(ActiveQuery::className(), [get_called_class()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Declares the name of the database table associated with this AR class.
|
||||
* By default this method returns the class name as the table name by calling [[Inflector::camel2id()]]
|
||||
* with prefix [[Connection::tablePrefix]]. For example if [[Connection::tablePrefix]] is `tbl_`,
|
||||
* `Customer` becomes `tbl_customer`, and `OrderItem` becomes `tbl_order_item`. You may override this method
|
||||
* if the table is not named after this convention.
|
||||
* @return string the table name
|
||||
*/
|
||||
public static function tableName()
|
||||
{
|
||||
return '{{%' . Inflector::camel2id(StringHelper::basename(get_called_class()), '_') . '}}';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the schema information of the DB table associated with this AR class.
|
||||
* @return TableSchema the schema information of the DB table associated with this AR class.
|
||||
* @throws InvalidConfigException if the table for the AR class does not exist.
|
||||
*/
|
||||
public static function getTableSchema()
|
||||
{
|
||||
$tableSchema = static::getDb()
|
||||
->getSchema()
|
||||
->getTableSchema(static::tableName());
|
||||
|
||||
if ($tableSchema === null) {
|
||||
throw new InvalidConfigException('The table does not exist: ' . static::tableName());
|
||||
}
|
||||
|
||||
return $tableSchema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the primary key name(s) for this AR class.
|
||||
* The default implementation will return the primary key(s) as declared
|
||||
* in the DB table that is associated with this AR class.
|
||||
*
|
||||
* If the DB table does not declare any primary key, you should override
|
||||
* this method to return the attributes that you want to use as primary keys
|
||||
* for this AR class.
|
||||
*
|
||||
* Note that an array should be returned even for a table with single primary key.
|
||||
*
|
||||
* @return string[] the primary keys of the associated database table.
|
||||
*/
|
||||
public static function primaryKey()
|
||||
{
|
||||
return static::getTableSchema()->primaryKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of all attribute names of the model.
|
||||
* The default implementation will return all column names of the table associated with this AR class.
|
||||
* @return array list of attribute names.
|
||||
*/
|
||||
public function attributes()
|
||||
{
|
||||
return array_keys(static::getTableSchema()->columns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Declares which DB operations should be performed within a transaction in different scenarios.
|
||||
* The supported DB operations are: [[OP_INSERT]], [[OP_UPDATE]] and [[OP_DELETE]],
|
||||
* which correspond to the [[insert()]], [[update()]] and [[delete()]] methods, respectively.
|
||||
* By default, these methods are NOT enclosed in a DB transaction.
|
||||
*
|
||||
* In some scenarios, to ensure data consistency, you may want to enclose some or all of them
|
||||
* in transactions. You can do so by overriding this method and returning the operations
|
||||
* that need to be transactional. For example,
|
||||
*
|
||||
* ```php
|
||||
* return [
|
||||
* 'admin' => self::OP_INSERT,
|
||||
* 'api' => self::OP_INSERT | self::OP_UPDATE | self::OP_DELETE,
|
||||
* // the above is equivalent to the following:
|
||||
* // 'api' => self::OP_ALL,
|
||||
*
|
||||
* ];
|
||||
* ```
|
||||
*
|
||||
* The above declaration specifies that in the "admin" scenario, the insert operation ([[insert()]])
|
||||
* should be done in a transaction; and in the "api" scenario, all the operations should be done
|
||||
* in a transaction.
|
||||
*
|
||||
* @return array the declarations of transactional operations. The array keys are scenarios names,
|
||||
* and the array values are the corresponding transaction operations.
|
||||
*/
|
||||
public function transactions()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function populateRecord($record, $row)
|
||||
{
|
||||
$columns = static::getTableSchema()->columns;
|
||||
foreach ($row as $name => $value) {
|
||||
if (isset($columns[$name])) {
|
||||
$row[$name] = $columns[$name]->phpTypecast($value);
|
||||
}
|
||||
}
|
||||
parent::populateRecord($record, $row);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a row into the associated database table using the attribute values of this record.
|
||||
*
|
||||
* This method performs the following steps in order:
|
||||
*
|
||||
* 1. call [[beforeValidate()]] when `$runValidation` is `true`. If [[beforeValidate()]]
|
||||
* returns `false`, the rest of the steps will be skipped;
|
||||
* 2. call [[afterValidate()]] when `$runValidation` is `true`. If validation
|
||||
* failed, the rest of the steps will be skipped;
|
||||
* 3. call [[beforeSave()]]. If [[beforeSave()]] returns `false`,
|
||||
* the rest of the steps will be skipped;
|
||||
* 4. insert the record into database. If this fails, it will skip the rest of the steps;
|
||||
* 5. call [[afterSave()]];
|
||||
*
|
||||
* In the above step 1, 2, 3 and 5, events [[EVENT_BEFORE_VALIDATE]],
|
||||
* [[EVENT_AFTER_VALIDATE]], [[EVENT_BEFORE_INSERT]], and [[EVENT_AFTER_INSERT]]
|
||||
* will be raised by the corresponding methods.
|
||||
*
|
||||
* Only the [[dirtyAttributes|changed attribute values]] will be inserted into database.
|
||||
*
|
||||
* If the table's primary key is auto-incremental and is `null` during insertion,
|
||||
* it will be populated with the actual value after insertion.
|
||||
*
|
||||
* For example, to insert a customer record:
|
||||
*
|
||||
* ```php
|
||||
* $customer = new Customer;
|
||||
* $customer->name = $name;
|
||||
* $customer->email = $email;
|
||||
* $customer->insert();
|
||||
* ```
|
||||
*
|
||||
* @param bool $runValidation whether to perform validation (calling [[validate()]])
|
||||
* before saving the record. Defaults to `true`. If the validation fails, the record
|
||||
* will not be saved to the database and this method will return `false`.
|
||||
* @param array $attributes list of attributes that need to be saved. Defaults to `null`,
|
||||
* meaning all attributes that are loaded from DB will be saved.
|
||||
* @return bool whether the attributes are valid and the record is inserted successfully.
|
||||
* @throws \Exception|\Throwable in case insert failed.
|
||||
*/
|
||||
public function insert($runValidation = true, $attributes = null)
|
||||
{
|
||||
if ($runValidation && !$this->validate($attributes)) {
|
||||
Yii::info('Model not inserted due to validation error.', __METHOD__);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$this->isTransactional(self::OP_INSERT)) {
|
||||
return $this->insertInternal($attributes);
|
||||
}
|
||||
|
||||
$transaction = static::getDb()->beginTransaction();
|
||||
try {
|
||||
$result = $this->insertInternal($attributes);
|
||||
if ($result === false) {
|
||||
$transaction->rollBack();
|
||||
} else {
|
||||
$transaction->commit();
|
||||
}
|
||||
|
||||
return $result;
|
||||
} catch (\Exception $e) {
|
||||
$transaction->rollBack();
|
||||
throw $e;
|
||||
} catch (\Throwable $e) {
|
||||
$transaction->rollBack();
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts an ActiveRecord into DB without considering transaction.
|
||||
* @param array $attributes list of attributes that need to be saved. Defaults to `null`,
|
||||
* meaning all attributes that are loaded from DB will be saved.
|
||||
* @return bool whether the record is inserted successfully.
|
||||
*/
|
||||
protected function insertInternal($attributes = null)
|
||||
{
|
||||
if (!$this->beforeSave(true)) {
|
||||
return false;
|
||||
}
|
||||
$values = $this->getDirtyAttributes($attributes);
|
||||
if (($primaryKeys = static::getDb()->schema->insert(static::tableName(), $values)) === false) {
|
||||
return false;
|
||||
}
|
||||
foreach ($primaryKeys as $name => $value) {
|
||||
$id = static::getTableSchema()->columns[$name]->phpTypecast($value);
|
||||
$this->setAttribute($name, $id);
|
||||
$values[$name] = $id;
|
||||
}
|
||||
|
||||
$changedAttributes = array_fill_keys(array_keys($values), null);
|
||||
$this->setOldAttributes($values);
|
||||
$this->afterSave(true, $changedAttributes);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the changes to this active record into the associated database table.
|
||||
*
|
||||
* This method performs the following steps in order:
|
||||
*
|
||||
* 1. call [[beforeValidate()]] when `$runValidation` is `true`. If [[beforeValidate()]]
|
||||
* returns `false`, the rest of the steps will be skipped;
|
||||
* 2. call [[afterValidate()]] when `$runValidation` is `true`. If validation
|
||||
* failed, the rest of the steps will be skipped;
|
||||
* 3. call [[beforeSave()]]. If [[beforeSave()]] returns `false`,
|
||||
* the rest of the steps will be skipped;
|
||||
* 4. save the record into database. If this fails, it will skip the rest of the steps;
|
||||
* 5. call [[afterSave()]];
|
||||
*
|
||||
* In the above step 1, 2, 3 and 5, events [[EVENT_BEFORE_VALIDATE]],
|
||||
* [[EVENT_AFTER_VALIDATE]], [[EVENT_BEFORE_UPDATE]], and [[EVENT_AFTER_UPDATE]]
|
||||
* will be raised by the corresponding methods.
|
||||
*
|
||||
* Only the [[dirtyAttributes|changed attribute values]] will be saved into database.
|
||||
*
|
||||
* For example, to update a customer record:
|
||||
*
|
||||
* ```php
|
||||
* $customer = Customer::findOne($id);
|
||||
* $customer->name = $name;
|
||||
* $customer->email = $email;
|
||||
* $customer->update();
|
||||
* ```
|
||||
*
|
||||
* Note that it is possible the update does not affect any row in the table.
|
||||
* In this case, this method will return 0. For this reason, you should use the following
|
||||
* code to check if update() is successful or not:
|
||||
*
|
||||
* ```php
|
||||
* if ($customer->update() !== false) {
|
||||
* // update successful
|
||||
* } else {
|
||||
* // update failed
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param bool $runValidation whether to perform validation (calling [[validate()]])
|
||||
* before saving the record. Defaults to `true`. If the validation fails, the record
|
||||
* will not be saved to the database and this method will return `false`.
|
||||
* @param array $attributeNames list of attributes that need to be saved. Defaults to `null`,
|
||||
* meaning all attributes that are loaded from DB will be saved.
|
||||
* @return int|false the number of rows affected, or false if validation fails
|
||||
* or [[beforeSave()]] stops the updating process.
|
||||
* @throws StaleObjectException if [[optimisticLock|optimistic locking]] is enabled and the data
|
||||
* being updated is outdated.
|
||||
* @throws \Exception|\Throwable in case update failed.
|
||||
*/
|
||||
public function update($runValidation = true, $attributeNames = null)
|
||||
{
|
||||
if ($runValidation && !$this->validate($attributeNames)) {
|
||||
Yii::info('Model not updated due to validation error.', __METHOD__);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$this->isTransactional(self::OP_UPDATE)) {
|
||||
return $this->updateInternal($attributeNames);
|
||||
}
|
||||
|
||||
$transaction = static::getDb()->beginTransaction();
|
||||
try {
|
||||
$result = $this->updateInternal($attributeNames);
|
||||
if ($result === false) {
|
||||
$transaction->rollBack();
|
||||
} else {
|
||||
$transaction->commit();
|
||||
}
|
||||
|
||||
return $result;
|
||||
} catch (\Exception $e) {
|
||||
$transaction->rollBack();
|
||||
throw $e;
|
||||
} catch (\Throwable $e) {
|
||||
$transaction->rollBack();
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the table row corresponding to this active record.
|
||||
*
|
||||
* This method performs the following steps in order:
|
||||
*
|
||||
* 1. call [[beforeDelete()]]. If the method returns `false`, it will skip the
|
||||
* rest of the steps;
|
||||
* 2. delete the record from the database;
|
||||
* 3. call [[afterDelete()]].
|
||||
*
|
||||
* In the above step 1 and 3, events named [[EVENT_BEFORE_DELETE]] and [[EVENT_AFTER_DELETE]]
|
||||
* will be raised by the corresponding methods.
|
||||
*
|
||||
* @return int|false the number of rows deleted, or `false` if the deletion is unsuccessful for some reason.
|
||||
* Note that it is possible the number of rows deleted is 0, even though the deletion execution is successful.
|
||||
* @throws StaleObjectException if [[optimisticLock|optimistic locking]] is enabled and the data
|
||||
* being deleted is outdated.
|
||||
* @throws \Exception|\Throwable in case delete failed.
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
if (!$this->isTransactional(self::OP_DELETE)) {
|
||||
return $this->deleteInternal();
|
||||
}
|
||||
|
||||
$transaction = static::getDb()->beginTransaction();
|
||||
try {
|
||||
$result = $this->deleteInternal();
|
||||
if ($result === false) {
|
||||
$transaction->rollBack();
|
||||
} else {
|
||||
$transaction->commit();
|
||||
}
|
||||
|
||||
return $result;
|
||||
} catch (\Exception $e) {
|
||||
$transaction->rollBack();
|
||||
throw $e;
|
||||
} catch (\Throwable $e) {
|
||||
$transaction->rollBack();
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes an ActiveRecord without considering transaction.
|
||||
* @return int|false the number of rows deleted, or `false` if the deletion is unsuccessful for some reason.
|
||||
* Note that it is possible the number of rows deleted is 0, even though the deletion execution is successful.
|
||||
* @throws StaleObjectException
|
||||
*/
|
||||
protected function deleteInternal()
|
||||
{
|
||||
if (!$this->beforeDelete()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// we do not check the return value of deleteAll() because it's possible
|
||||
// the record is already deleted in the database and thus the method will return 0
|
||||
$condition = $this->getOldPrimaryKey(true);
|
||||
$lock = $this->optimisticLock();
|
||||
if ($lock !== null) {
|
||||
$condition[$lock] = $this->$lock;
|
||||
}
|
||||
$result = static::deleteAll($condition);
|
||||
if ($lock !== null && !$result) {
|
||||
throw new StaleObjectException('The object being deleted is outdated.');
|
||||
}
|
||||
$this->setOldAttributes(null);
|
||||
$this->afterDelete();
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a value indicating whether the given active record is the same as the current one.
|
||||
* The comparison is made by comparing the table names and the primary key values of the two active records.
|
||||
* If one of the records [[isNewRecord|is new]] they are also considered not equal.
|
||||
* @param ActiveRecord $record record to compare to
|
||||
* @return bool whether the two active records refer to the same row in the same database table.
|
||||
*/
|
||||
public function equals($record)
|
||||
{
|
||||
if ($this->isNewRecord || $record->isNewRecord) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return static::tableName() === $record->tableName() && $this->getPrimaryKey() === $record->getPrimaryKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a value indicating whether the specified operation is transactional in the current [[$scenario]].
|
||||
* @param int $operation the operation to check. Possible values are [[OP_INSERT]], [[OP_UPDATE]] and [[OP_DELETE]].
|
||||
* @return bool whether the specified operation is transactional in the current [[scenario]].
|
||||
*/
|
||||
public function isTransactional($operation)
|
||||
{
|
||||
$scenario = $this->getScenario();
|
||||
$transactions = $this->transactions();
|
||||
|
||||
return isset($transactions[$scenario]) && ($transactions[$scenario] & $operation);
|
||||
}
|
||||
}
|
||||
471
vendor/yiisoft/yii2/db/ActiveRecordInterface.php
vendored
Normal file
471
vendor/yiisoft/yii2/db/ActiveRecordInterface.php
vendored
Normal file
@@ -0,0 +1,471 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db;
|
||||
|
||||
use yii\base\StaticInstanceInterface;
|
||||
|
||||
/**
|
||||
* ActiveRecordInterface.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @author Carsten Brandt <mail@cebe.cc>
|
||||
* @since 2.0
|
||||
*/
|
||||
interface ActiveRecordInterface extends StaticInstanceInterface
|
||||
{
|
||||
/**
|
||||
* Returns the primary key **name(s)** for this AR class.
|
||||
*
|
||||
* Note that an array should be returned even when the record only has a single primary key.
|
||||
*
|
||||
* For the primary key **value** see [[getPrimaryKey()]] instead.
|
||||
*
|
||||
* @return string[] the primary key name(s) for this AR class.
|
||||
*/
|
||||
public static function primaryKey();
|
||||
|
||||
/**
|
||||
* Returns the list of all attribute names of the record.
|
||||
* @return array list of attribute names.
|
||||
*/
|
||||
public function attributes();
|
||||
|
||||
/**
|
||||
* Returns the named attribute value.
|
||||
* If this record is the result of a query and the attribute is not loaded,
|
||||
* `null` will be returned.
|
||||
* @param string $name the attribute name
|
||||
* @return mixed the attribute value. `null` if the attribute is not set or does not exist.
|
||||
* @see hasAttribute()
|
||||
*/
|
||||
public function getAttribute($name);
|
||||
|
||||
/**
|
||||
* Sets the named attribute value.
|
||||
* @param string $name the attribute name.
|
||||
* @param mixed $value the attribute value.
|
||||
* @see hasAttribute()
|
||||
*/
|
||||
public function setAttribute($name, $value);
|
||||
|
||||
/**
|
||||
* Returns a value indicating whether the record has an attribute with the specified name.
|
||||
* @param string $name the name of the attribute
|
||||
* @return bool whether the record has an attribute with the specified name.
|
||||
*/
|
||||
public function hasAttribute($name);
|
||||
|
||||
/**
|
||||
* Returns the primary key value(s).
|
||||
* @param bool $asArray whether to return the primary key value as an array. If true,
|
||||
* the return value will be an array with attribute names as keys and attribute values as values.
|
||||
* Note that for composite primary keys, an array will always be returned regardless of this parameter value.
|
||||
* @return mixed the primary key value. An array (attribute name => attribute value) is returned if the primary key
|
||||
* is composite or `$asArray` is true. A string is returned otherwise (`null` will be returned if
|
||||
* the key value is `null`).
|
||||
*/
|
||||
public function getPrimaryKey($asArray = false);
|
||||
|
||||
/**
|
||||
* Returns the old primary key value(s).
|
||||
* This refers to the primary key value that is populated into the record
|
||||
* after executing a find method (e.g. find(), findOne()).
|
||||
* The value remains unchanged even if the primary key attribute is manually assigned with a different value.
|
||||
* @param bool $asArray whether to return the primary key value as an array. If true,
|
||||
* the return value will be an array with column name as key and column value as value.
|
||||
* If this is `false` (default), a scalar value will be returned for non-composite primary key.
|
||||
* @property mixed The old primary key value. An array (column name => column value) is
|
||||
* returned if the primary key is composite. A string is returned otherwise (`null` will be
|
||||
* returned if the key value is `null`).
|
||||
* @return mixed the old primary key value. An array (column name => column value) is returned if the primary key
|
||||
* is composite or `$asArray` is true. A string is returned otherwise (`null` will be returned if
|
||||
* the key value is `null`).
|
||||
*/
|
||||
public function getOldPrimaryKey($asArray = false);
|
||||
|
||||
/**
|
||||
* Returns a value indicating whether the given set of attributes represents the primary key for this model.
|
||||
* @param array $keys the set of attributes to check
|
||||
* @return bool whether the given set of attributes represents the primary key for this model
|
||||
*/
|
||||
public static function isPrimaryKey($keys);
|
||||
|
||||
/**
|
||||
* Creates an [[ActiveQueryInterface]] instance for query purpose.
|
||||
*
|
||||
* The returned [[ActiveQueryInterface]] instance can be further customized by calling
|
||||
* methods defined in [[ActiveQueryInterface]] before `one()` or `all()` is called to return
|
||||
* populated ActiveRecord instances. For example,
|
||||
*
|
||||
* ```php
|
||||
* // find the customer whose ID is 1
|
||||
* $customer = Customer::find()->where(['id' => 1])->one();
|
||||
*
|
||||
* // find all active customers and order them by their age:
|
||||
* $customers = Customer::find()
|
||||
* ->where(['status' => 1])
|
||||
* ->orderBy('age')
|
||||
* ->all();
|
||||
* ```
|
||||
*
|
||||
* This method is also called by [[BaseActiveRecord::hasOne()]] and [[BaseActiveRecord::hasMany()]] to
|
||||
* create a relational query.
|
||||
*
|
||||
* You may override this method to return a customized query. For example,
|
||||
*
|
||||
* ```php
|
||||
* class Customer extends ActiveRecord
|
||||
* {
|
||||
* public static function find()
|
||||
* {
|
||||
* // use CustomerQuery instead of the default ActiveQuery
|
||||
* return new CustomerQuery(get_called_class());
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* The following code shows how to apply a default condition for all queries:
|
||||
*
|
||||
* ```php
|
||||
* class Customer extends ActiveRecord
|
||||
* {
|
||||
* public static function find()
|
||||
* {
|
||||
* return parent::find()->where(['deleted' => false]);
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* // Use andWhere()/orWhere() to apply the default condition
|
||||
* // SELECT FROM customer WHERE `deleted`=:deleted AND age>30
|
||||
* $customers = Customer::find()->andWhere('age>30')->all();
|
||||
*
|
||||
* // Use where() to ignore the default condition
|
||||
* // SELECT FROM customer WHERE age>30
|
||||
* $customers = Customer::find()->where('age>30')->all();
|
||||
*
|
||||
* @return ActiveQueryInterface the newly created [[ActiveQueryInterface]] instance.
|
||||
*/
|
||||
public static function find();
|
||||
|
||||
/**
|
||||
* Returns a single active record model instance by a primary key or an array of column values.
|
||||
*
|
||||
* The method accepts:
|
||||
*
|
||||
* - a scalar value (integer or string): query by a single primary key value and return the
|
||||
* corresponding record (or `null` if not found).
|
||||
* - a non-associative array: query by a list of primary key values and return the
|
||||
* first record (or `null` if not found).
|
||||
* - an associative array of name-value pairs: query by a set of attribute values and return a single record
|
||||
* matching all of them (or `null` if not found). Note that `['id' => 1, 2]` is treated as a non-associative array.
|
||||
* Column names are limited to current records table columns for SQL DBMS, or filtered otherwise to be limited to simple filter conditions.
|
||||
*
|
||||
* That this method will automatically call the `one()` method and return an [[ActiveRecordInterface|ActiveRecord]]
|
||||
* instance.
|
||||
*
|
||||
* > Note: As this is a short-hand method only, using more complex conditions, like ['!=', 'id', 1] will not work.
|
||||
* > If you need to specify more complex conditions, use [[find()]] in combination with [[ActiveQuery::where()|where()]] instead.
|
||||
*
|
||||
* See the following code for usage examples:
|
||||
*
|
||||
* ```php
|
||||
* // find a single customer whose primary key value is 10
|
||||
* $customer = Customer::findOne(10);
|
||||
*
|
||||
* // the above code is equivalent to:
|
||||
* $customer = Customer::find()->where(['id' => 10])->one();
|
||||
*
|
||||
* // find the customers whose primary key value is 10, 11 or 12.
|
||||
* $customers = Customer::findOne([10, 11, 12]);
|
||||
*
|
||||
* // the above code is equivalent to:
|
||||
* $customers = Customer::find()->where(['id' => [10, 11, 12]])->one();
|
||||
*
|
||||
* // find the first customer whose age is 30 and whose status is 1
|
||||
* $customer = Customer::findOne(['age' => 30, 'status' => 1]);
|
||||
*
|
||||
* // the above code is equivalent to:
|
||||
* $customer = Customer::find()->where(['age' => 30, 'status' => 1])->one();
|
||||
* ```
|
||||
*
|
||||
* If you need to pass user input to this method, make sure the input value is scalar or in case of
|
||||
* array condition, make sure the array structure can not be changed from the outside:
|
||||
*
|
||||
* ```php
|
||||
* // yii\web\Controller ensures that $id is scalar
|
||||
* public function actionView($id)
|
||||
* {
|
||||
* $model = Post::findOne($id);
|
||||
* // ...
|
||||
* }
|
||||
*
|
||||
* // explicitly specifying the colum to search, passing a scalar or array here will always result in finding a single record
|
||||
* $model = Post::findOne(['id' => Yii::$app->request->get('id')]);
|
||||
*
|
||||
* // do NOT use the following code! it is possible to inject an array condition to filter by arbitrary column values!
|
||||
* $model = Post::findOne(Yii::$app->request->get('id'));
|
||||
* ```
|
||||
*
|
||||
* @param mixed $condition primary key value or a set of column values
|
||||
* @return static ActiveRecord instance matching the condition, or `null` if nothing matches.
|
||||
*/
|
||||
public static function findOne($condition);
|
||||
|
||||
/**
|
||||
* Returns a list of active record models that match the specified primary key value(s) or a set of column values.
|
||||
*
|
||||
* The method accepts:
|
||||
*
|
||||
* - a scalar value (integer or string): query by a single primary key value and return an array containing the
|
||||
* corresponding record (or an empty array if not found).
|
||||
* - a non-associative array: query by a list of primary key values and return the
|
||||
* corresponding records (or an empty array if none was found).
|
||||
* Note that an empty condition will result in an empty result as it will be interpreted as a search for
|
||||
* primary keys and not an empty `WHERE` condition.
|
||||
* - an associative array of name-value pairs: query by a set of attribute values and return an array of records
|
||||
* matching all of them (or an empty array if none was found). Note that `['id' => 1, 2]` is treated as
|
||||
* a non-associative array.
|
||||
* Column names are limited to current records table columns for SQL DBMS, or filtered otherwise to be limted to simple filter conditions.
|
||||
*
|
||||
* This method will automatically call the `all()` method and return an array of [[ActiveRecordInterface|ActiveRecord]]
|
||||
* instances.
|
||||
*
|
||||
* > Note: As this is a short-hand method only, using more complex conditions, like ['!=', 'id', 1] will not work.
|
||||
* > If you need to specify more complex conditions, use [[find()]] in combination with [[ActiveQuery::where()|where()]] instead.
|
||||
*
|
||||
* See the following code for usage examples:
|
||||
*
|
||||
* ```php
|
||||
* // find the customers whose primary key value is 10
|
||||
* $customers = Customer::findAll(10);
|
||||
*
|
||||
* // the above code is equivalent to:
|
||||
* $customers = Customer::find()->where(['id' => 10])->all();
|
||||
*
|
||||
* // find the customers whose primary key value is 10, 11 or 12.
|
||||
* $customers = Customer::findAll([10, 11, 12]);
|
||||
*
|
||||
* // the above code is equivalent to:
|
||||
* $customers = Customer::find()->where(['id' => [10, 11, 12]])->all();
|
||||
*
|
||||
* // find customers whose age is 30 and whose status is 1
|
||||
* $customers = Customer::findAll(['age' => 30, 'status' => 1]);
|
||||
*
|
||||
* // the above code is equivalent to:
|
||||
* $customers = Customer::find()->where(['age' => 30, 'status' => 1])->all();
|
||||
* ```
|
||||
*
|
||||
* If you need to pass user input to this method, make sure the input value is scalar or in case of
|
||||
* array condition, make sure the array structure can not be changed from the outside:
|
||||
*
|
||||
* ```php
|
||||
* // yii\web\Controller ensures that $id is scalar
|
||||
* public function actionView($id)
|
||||
* {
|
||||
* $model = Post::findOne($id);
|
||||
* // ...
|
||||
* }
|
||||
*
|
||||
* // explicitly specifying the colum to search, passing a scalar or array here will always result in finding a single record
|
||||
* $model = Post::findOne(['id' => Yii::$app->request->get('id')]);
|
||||
*
|
||||
* // do NOT use the following code! it is possible to inject an array condition to filter by arbitrary column values!
|
||||
* $model = Post::findOne(Yii::$app->request->get('id'));
|
||||
* ```
|
||||
*
|
||||
* @param mixed $condition primary key value or a set of column values
|
||||
* @return array an array of ActiveRecord instance, or an empty array if nothing matches.
|
||||
*/
|
||||
public static function findAll($condition);
|
||||
|
||||
/**
|
||||
* Updates records using the provided attribute values and conditions.
|
||||
*
|
||||
* For example, to change the status to be 1 for all customers whose status is 2:
|
||||
*
|
||||
* ```php
|
||||
* Customer::updateAll(['status' => 1], ['status' => '2']);
|
||||
* ```
|
||||
*
|
||||
* @param array $attributes attribute values (name-value pairs) to be saved for the record.
|
||||
* Unlike [[update()]] these are not going to be validated.
|
||||
* @param array $condition the condition that matches the records that should get updated.
|
||||
* Please refer to [[QueryInterface::where()]] on how to specify this parameter.
|
||||
* An empty condition will match all records.
|
||||
* @return int the number of rows updated
|
||||
*/
|
||||
public static function updateAll($attributes, $condition = null);
|
||||
|
||||
/**
|
||||
* Deletes records using the provided conditions.
|
||||
* WARNING: If you do not specify any condition, this method will delete ALL rows in the table.
|
||||
*
|
||||
* For example, to delete all customers whose status is 3:
|
||||
*
|
||||
* ```php
|
||||
* Customer::deleteAll([status = 3]);
|
||||
* ```
|
||||
*
|
||||
* @param array $condition the condition that matches the records that should get deleted.
|
||||
* Please refer to [[QueryInterface::where()]] on how to specify this parameter.
|
||||
* An empty condition will match all records.
|
||||
* @return int the number of rows deleted
|
||||
*/
|
||||
public static function deleteAll($condition = null);
|
||||
|
||||
/**
|
||||
* Saves the current record.
|
||||
*
|
||||
* This method will call [[insert()]] when [[getIsNewRecord()|isNewRecord]] is true, or [[update()]]
|
||||
* when [[getIsNewRecord()|isNewRecord]] is false.
|
||||
*
|
||||
* For example, to save a customer record:
|
||||
*
|
||||
* ```php
|
||||
* $customer = new Customer; // or $customer = Customer::findOne($id);
|
||||
* $customer->name = $name;
|
||||
* $customer->email = $email;
|
||||
* $customer->save();
|
||||
* ```
|
||||
*
|
||||
* @param bool $runValidation whether to perform validation (calling [[\yii\base\Model::validate()|validate()]])
|
||||
* before saving the record. Defaults to `true`. If the validation fails, the record
|
||||
* will not be saved to the database and this method will return `false`.
|
||||
* @param array $attributeNames list of attribute names that need to be saved. Defaults to `null`,
|
||||
* meaning all attributes that are loaded from DB will be saved.
|
||||
* @return bool whether the saving succeeded (i.e. no validation errors occurred).
|
||||
*/
|
||||
public function save($runValidation = true, $attributeNames = null);
|
||||
|
||||
/**
|
||||
* Inserts the record into the database using the attribute values of this record.
|
||||
*
|
||||
* Usage example:
|
||||
*
|
||||
* ```php
|
||||
* $customer = new Customer;
|
||||
* $customer->name = $name;
|
||||
* $customer->email = $email;
|
||||
* $customer->insert();
|
||||
* ```
|
||||
*
|
||||
* @param bool $runValidation whether to perform validation (calling [[\yii\base\Model::validate()|validate()]])
|
||||
* before saving the record. Defaults to `true`. If the validation fails, the record
|
||||
* will not be saved to the database and this method will return `false`.
|
||||
* @param array $attributes list of attributes that need to be saved. Defaults to `null`,
|
||||
* meaning all attributes that are loaded from DB will be saved.
|
||||
* @return bool whether the attributes are valid and the record is inserted successfully.
|
||||
*/
|
||||
public function insert($runValidation = true, $attributes = null);
|
||||
|
||||
/**
|
||||
* Saves the changes to this active record into the database.
|
||||
*
|
||||
* Usage example:
|
||||
*
|
||||
* ```php
|
||||
* $customer = Customer::findOne($id);
|
||||
* $customer->name = $name;
|
||||
* $customer->email = $email;
|
||||
* $customer->update();
|
||||
* ```
|
||||
*
|
||||
* @param bool $runValidation whether to perform validation (calling [[\yii\base\Model::validate()|validate()]])
|
||||
* before saving the record. Defaults to `true`. If the validation fails, the record
|
||||
* will not be saved to the database and this method will return `false`.
|
||||
* @param array $attributeNames list of attributes that need to be saved. Defaults to `null`,
|
||||
* meaning all attributes that are loaded from DB will be saved.
|
||||
* @return int|bool the number of rows affected, or `false` if validation fails
|
||||
* or updating process is stopped for other reasons.
|
||||
* Note that it is possible that the number of rows affected is 0, even though the
|
||||
* update execution is successful.
|
||||
*/
|
||||
public function update($runValidation = true, $attributeNames = null);
|
||||
|
||||
/**
|
||||
* Deletes the record from the database.
|
||||
*
|
||||
* @return int|bool the number of rows deleted, or `false` if the deletion is unsuccessful for some reason.
|
||||
* Note that it is possible that the number of rows deleted is 0, even though the deletion execution is successful.
|
||||
*/
|
||||
public function delete();
|
||||
|
||||
/**
|
||||
* Returns a value indicating whether the current record is new (not saved in the database).
|
||||
* @return bool whether the record is new and should be inserted when calling [[save()]].
|
||||
*/
|
||||
public function getIsNewRecord();
|
||||
|
||||
/**
|
||||
* Returns a value indicating whether the given active record is the same as the current one.
|
||||
* Two [[getIsNewRecord()|new]] records are considered to be not equal.
|
||||
* @param static $record record to compare to
|
||||
* @return bool whether the two active records refer to the same row in the same database table.
|
||||
*/
|
||||
public function equals($record);
|
||||
|
||||
/**
|
||||
* Returns the relation object with the specified name.
|
||||
* A relation is defined by a getter method which returns an object implementing the [[ActiveQueryInterface]]
|
||||
* (normally this would be a relational [[ActiveQuery]] object).
|
||||
* It can be declared in either the ActiveRecord class itself or one of its behaviors.
|
||||
* @param string $name the relation name, e.g. `orders` for a relation defined via `getOrders()` method (case-sensitive).
|
||||
* @param bool $throwException whether to throw exception if the relation does not exist.
|
||||
* @return ActiveQueryInterface the relational query object
|
||||
*/
|
||||
public function getRelation($name, $throwException = true);
|
||||
|
||||
/**
|
||||
* Populates the named relation with the related records.
|
||||
* Note that this method does not check if the relation exists or not.
|
||||
* @param string $name the relation name, e.g. `orders` for a relation defined via `getOrders()` method (case-sensitive).
|
||||
* @param ActiveRecordInterface|array|null $records the related records to be populated into the relation.
|
||||
* @since 2.0.8
|
||||
*/
|
||||
public function populateRelation($name, $records);
|
||||
|
||||
/**
|
||||
* Establishes the relationship between two records.
|
||||
*
|
||||
* The relationship is established by setting the foreign key value(s) in one record
|
||||
* to be the corresponding primary key value(s) in the other record.
|
||||
* The record with the foreign key will be saved into database without performing validation.
|
||||
*
|
||||
* If the relationship involves a junction table, a new row will be inserted into the
|
||||
* junction table which contains the primary key values from both records.
|
||||
*
|
||||
* This method requires that the primary key value is not `null`.
|
||||
*
|
||||
* @param string $name the case sensitive name of the relationship, e.g. `orders` for a relation defined via `getOrders()` method.
|
||||
* @param static $model the record to be linked with the current one.
|
||||
* @param array $extraColumns additional column values to be saved into the junction table.
|
||||
* This parameter is only meaningful for a relationship involving a junction table
|
||||
* (i.e., a relation set with [[ActiveQueryInterface::via()]]).
|
||||
*/
|
||||
public function link($name, $model, $extraColumns = []);
|
||||
|
||||
/**
|
||||
* Destroys the relationship between two records.
|
||||
*
|
||||
* The record with the foreign key of the relationship will be deleted if `$delete` is true.
|
||||
* Otherwise, the foreign key will be set `null` and the record will be saved without validation.
|
||||
*
|
||||
* @param string $name the case sensitive name of the relationship, e.g. `orders` for a relation defined via `getOrders()` method.
|
||||
* @param static $model the model to be unlinked from the current one.
|
||||
* @param bool $delete whether to delete the model that contains the foreign key.
|
||||
* If false, the model's foreign key will be set `null` and saved.
|
||||
* If true, the model containing the foreign key will be deleted.
|
||||
*/
|
||||
public function unlink($name, $model, $delete = false);
|
||||
|
||||
/**
|
||||
* Returns the connection used by this AR class.
|
||||
* @return mixed the database connection used by this AR class.
|
||||
*/
|
||||
public static function getDb();
|
||||
}
|
||||
578
vendor/yiisoft/yii2/db/ActiveRelationTrait.php
vendored
Normal file
578
vendor/yiisoft/yii2/db/ActiveRelationTrait.php
vendored
Normal file
@@ -0,0 +1,578 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db;
|
||||
|
||||
use yii\base\InvalidArgumentException;
|
||||
use yii\base\InvalidConfigException;
|
||||
|
||||
/**
|
||||
* ActiveRelationTrait implements the common methods and properties for active record relational queries.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @author Carsten Brandt <mail@cebe.cc>
|
||||
* @since 2.0
|
||||
*
|
||||
* @method ActiveRecordInterface one()
|
||||
* @method ActiveRecordInterface[] all()
|
||||
* @property ActiveRecord $modelClass
|
||||
*/
|
||||
trait ActiveRelationTrait
|
||||
{
|
||||
/**
|
||||
* @var bool whether this query represents a relation to more than one record.
|
||||
* This property is only used in relational context. If true, this relation will
|
||||
* populate all query results into AR instances using [[Query::all()|all()]].
|
||||
* If false, only the first row of the results will be retrieved using [[Query::one()|one()]].
|
||||
*/
|
||||
public $multiple;
|
||||
/**
|
||||
* @var ActiveRecord the primary model of a relational query.
|
||||
* This is used only in lazy loading with dynamic query options.
|
||||
*/
|
||||
public $primaryModel;
|
||||
/**
|
||||
* @var array the columns of the primary and foreign tables that establish a relation.
|
||||
* The array keys must be columns of the table for this relation, and the array values
|
||||
* must be the corresponding columns from the primary table.
|
||||
* Do not prefix or quote the column names as this will be done automatically by Yii.
|
||||
* This property is only used in relational context.
|
||||
*/
|
||||
public $link;
|
||||
/**
|
||||
* @var array|object the query associated with the junction table. Please call [[via()]]
|
||||
* to set this property instead of directly setting it.
|
||||
* This property is only used in relational context.
|
||||
* @see via()
|
||||
*/
|
||||
public $via;
|
||||
/**
|
||||
* @var string the name of the relation that is the inverse of this relation.
|
||||
* For example, an order has a customer, which means the inverse of the "customer" relation
|
||||
* is the "orders", and the inverse of the "orders" relation is the "customer".
|
||||
* If this property is set, the primary record(s) will be referenced through the specified relation.
|
||||
* For example, `$customer->orders[0]->customer` and `$customer` will be the same object,
|
||||
* and accessing the customer of an order will not trigger new DB query.
|
||||
* This property is only used in relational context.
|
||||
* @see inverseOf()
|
||||
*/
|
||||
public $inverseOf;
|
||||
|
||||
|
||||
/**
|
||||
* Clones internal objects.
|
||||
*/
|
||||
public function __clone()
|
||||
{
|
||||
parent::__clone();
|
||||
// make a clone of "via" object so that the same query object can be reused multiple times
|
||||
if (is_object($this->via)) {
|
||||
$this->via = clone $this->via;
|
||||
} elseif (is_array($this->via)) {
|
||||
$this->via = [$this->via[0], clone $this->via[1]];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the relation associated with the junction table.
|
||||
*
|
||||
* Use this method to specify a pivot record/table when declaring a relation in the [[ActiveRecord]] class:
|
||||
*
|
||||
* ```php
|
||||
* class Order extends ActiveRecord
|
||||
* {
|
||||
* public function getOrderItems() {
|
||||
* return $this->hasMany(OrderItem::className(), ['order_id' => 'id']);
|
||||
* }
|
||||
*
|
||||
* public function getItems() {
|
||||
* return $this->hasMany(Item::className(), ['id' => 'item_id'])
|
||||
* ->via('orderItems');
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param string $relationName the relation name. This refers to a relation declared in [[primaryModel]].
|
||||
* @param callable $callable a PHP callback for customizing the relation associated with the junction table.
|
||||
* Its signature should be `function($query)`, where `$query` is the query to be customized.
|
||||
* @return $this the relation object itself.
|
||||
*/
|
||||
public function via($relationName, callable $callable = null)
|
||||
{
|
||||
$relation = $this->primaryModel->getRelation($relationName);
|
||||
$this->via = [$relationName, $relation];
|
||||
if ($callable !== null) {
|
||||
call_user_func($callable, $relation);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of the relation that is the inverse of this relation.
|
||||
* For example, a customer has orders, which means the inverse of the "orders" relation is the "customer".
|
||||
* If this property is set, the primary record(s) will be referenced through the specified relation.
|
||||
* For example, `$customer->orders[0]->customer` and `$customer` will be the same object,
|
||||
* and accessing the customer of an order will not trigger a new DB query.
|
||||
*
|
||||
* Use this method when declaring a relation in the [[ActiveRecord]] class, e.g. in Customer model:
|
||||
*
|
||||
* ```php
|
||||
* public function getOrders()
|
||||
* {
|
||||
* return $this->hasMany(Order::className(), ['customer_id' => 'id'])->inverseOf('customer');
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* This also may be used for Order model, but with caution:
|
||||
*
|
||||
* ```php
|
||||
* public function getCustomer()
|
||||
* {
|
||||
* return $this->hasOne(Customer::className(), ['id' => 'customer_id'])->inverseOf('orders');
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* in this case result will depend on how order(s) was loaded.
|
||||
* Let's suppose customer has several orders. If only one order was loaded:
|
||||
*
|
||||
* ```php
|
||||
* $orders = Order::find()->where(['id' => 1])->all();
|
||||
* $customerOrders = $orders[0]->customer->orders;
|
||||
* ```
|
||||
*
|
||||
* variable `$customerOrders` will contain only one order. If orders was loaded like this:
|
||||
*
|
||||
* ```php
|
||||
* $orders = Order::find()->with('customer')->where(['customer_id' => 1])->all();
|
||||
* $customerOrders = $orders[0]->customer->orders;
|
||||
* ```
|
||||
*
|
||||
* variable `$customerOrders` will contain all orders of the customer.
|
||||
*
|
||||
* @param string $relationName the name of the relation that is the inverse of this relation.
|
||||
* @return $this the relation object itself.
|
||||
*/
|
||||
public function inverseOf($relationName)
|
||||
{
|
||||
$this->inverseOf = $relationName;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the related records for the specified primary record.
|
||||
* This method is invoked when a relation of an ActiveRecord is being accessed in a lazy fashion.
|
||||
* @param string $name the relation name
|
||||
* @param ActiveRecordInterface|BaseActiveRecord $model the primary model
|
||||
* @return mixed the related record(s)
|
||||
* @throws InvalidArgumentException if the relation is invalid
|
||||
*/
|
||||
public function findFor($name, $model)
|
||||
{
|
||||
if (method_exists($model, 'get' . $name)) {
|
||||
$method = new \ReflectionMethod($model, 'get' . $name);
|
||||
$realName = lcfirst(substr($method->getName(), 3));
|
||||
if ($realName !== $name) {
|
||||
throw new InvalidArgumentException('Relation names are case sensitive. ' . get_class($model) . " has a relation named \"$realName\" instead of \"$name\".");
|
||||
}
|
||||
}
|
||||
|
||||
return $this->multiple ? $this->all() : $this->one();
|
||||
}
|
||||
|
||||
/**
|
||||
* If applicable, populate the query's primary model into the related records' inverse relationship.
|
||||
* @param array $result the array of related records as generated by [[populate()]]
|
||||
* @since 2.0.9
|
||||
*/
|
||||
private function addInverseRelations(&$result)
|
||||
{
|
||||
if ($this->inverseOf === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($result as $i => $relatedModel) {
|
||||
if ($relatedModel instanceof ActiveRecordInterface) {
|
||||
if (!isset($inverseRelation)) {
|
||||
$inverseRelation = $relatedModel->getRelation($this->inverseOf);
|
||||
}
|
||||
$relatedModel->populateRelation($this->inverseOf, $inverseRelation->multiple ? [$this->primaryModel] : $this->primaryModel);
|
||||
} else {
|
||||
if (!isset($inverseRelation)) {
|
||||
/* @var $modelClass ActiveRecordInterface */
|
||||
$modelClass = $this->modelClass;
|
||||
$inverseRelation = $modelClass::instance()->getRelation($this->inverseOf);
|
||||
}
|
||||
$result[$i][$this->inverseOf] = $inverseRelation->multiple ? [$this->primaryModel] : $this->primaryModel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the related records and populates them into the primary models.
|
||||
* @param string $name the relation name
|
||||
* @param array $primaryModels primary models
|
||||
* @return array the related models
|
||||
* @throws InvalidConfigException if [[link]] is invalid
|
||||
*/
|
||||
public function populateRelation($name, &$primaryModels)
|
||||
{
|
||||
if (!is_array($this->link)) {
|
||||
throw new InvalidConfigException('Invalid link: it must be an array of key-value pairs.');
|
||||
}
|
||||
|
||||
if ($this->via instanceof self) {
|
||||
// via junction table
|
||||
/* @var $viaQuery ActiveRelationTrait */
|
||||
$viaQuery = $this->via;
|
||||
$viaModels = $viaQuery->findJunctionRows($primaryModels);
|
||||
$this->filterByModels($viaModels);
|
||||
} elseif (is_array($this->via)) {
|
||||
// via relation
|
||||
/* @var $viaQuery ActiveRelationTrait|ActiveQueryTrait */
|
||||
list($viaName, $viaQuery) = $this->via;
|
||||
if ($viaQuery->asArray === null) {
|
||||
// inherit asArray from primary query
|
||||
$viaQuery->asArray($this->asArray);
|
||||
}
|
||||
$viaQuery->primaryModel = null;
|
||||
$viaModels = $viaQuery->populateRelation($viaName, $primaryModels);
|
||||
$this->filterByModels($viaModels);
|
||||
} else {
|
||||
$this->filterByModels($primaryModels);
|
||||
}
|
||||
|
||||
if (!$this->multiple && count($primaryModels) === 1) {
|
||||
$model = $this->one();
|
||||
$primaryModel = reset($primaryModels);
|
||||
if ($primaryModel instanceof ActiveRecordInterface) {
|
||||
$primaryModel->populateRelation($name, $model);
|
||||
} else {
|
||||
$primaryModels[key($primaryModels)][$name] = $model;
|
||||
}
|
||||
if ($this->inverseOf !== null) {
|
||||
$this->populateInverseRelation($primaryModels, [$model], $name, $this->inverseOf);
|
||||
}
|
||||
|
||||
return [$model];
|
||||
}
|
||||
|
||||
// https://github.com/yiisoft/yii2/issues/3197
|
||||
// delay indexing related models after buckets are built
|
||||
$indexBy = $this->indexBy;
|
||||
$this->indexBy = null;
|
||||
$models = $this->all();
|
||||
|
||||
if (isset($viaModels, $viaQuery)) {
|
||||
$buckets = $this->buildBuckets($models, $this->link, $viaModels, $viaQuery->link);
|
||||
} else {
|
||||
$buckets = $this->buildBuckets($models, $this->link);
|
||||
}
|
||||
|
||||
$this->indexBy = $indexBy;
|
||||
if ($this->indexBy !== null && $this->multiple) {
|
||||
$buckets = $this->indexBuckets($buckets, $this->indexBy);
|
||||
}
|
||||
|
||||
$link = array_values(isset($viaQuery) ? $viaQuery->link : $this->link);
|
||||
foreach ($primaryModels as $i => $primaryModel) {
|
||||
if ($this->multiple && count($link) === 1 && is_array($keys = $primaryModel[reset($link)])) {
|
||||
$value = [];
|
||||
foreach ($keys as $key) {
|
||||
$key = $this->normalizeModelKey($key);
|
||||
if (isset($buckets[$key])) {
|
||||
if ($this->indexBy !== null) {
|
||||
// if indexBy is set, array_merge will cause renumbering of numeric array
|
||||
foreach ($buckets[$key] as $bucketKey => $bucketValue) {
|
||||
$value[$bucketKey] = $bucketValue;
|
||||
}
|
||||
} else {
|
||||
$value = array_merge($value, $buckets[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$key = $this->getModelKey($primaryModel, $link);
|
||||
$value = isset($buckets[$key]) ? $buckets[$key] : ($this->multiple ? [] : null);
|
||||
}
|
||||
if ($primaryModel instanceof ActiveRecordInterface) {
|
||||
$primaryModel->populateRelation($name, $value);
|
||||
} else {
|
||||
$primaryModels[$i][$name] = $value;
|
||||
}
|
||||
}
|
||||
if ($this->inverseOf !== null) {
|
||||
$this->populateInverseRelation($primaryModels, $models, $name, $this->inverseOf);
|
||||
}
|
||||
|
||||
return $models;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ActiveRecordInterface[] $primaryModels primary models
|
||||
* @param ActiveRecordInterface[] $models models
|
||||
* @param string $primaryName the primary relation name
|
||||
* @param string $name the relation name
|
||||
*/
|
||||
private function populateInverseRelation(&$primaryModels, $models, $primaryName, $name)
|
||||
{
|
||||
if (empty($models) || empty($primaryModels)) {
|
||||
return;
|
||||
}
|
||||
$model = reset($models);
|
||||
/* @var $relation ActiveQueryInterface|ActiveQuery */
|
||||
if ($model instanceof ActiveRecordInterface) {
|
||||
$relation = $model->getRelation($name);
|
||||
} else {
|
||||
/* @var $modelClass ActiveRecordInterface */
|
||||
$modelClass = $this->modelClass;
|
||||
$relation = $modelClass::instance()->getRelation($name);
|
||||
}
|
||||
|
||||
if ($relation->multiple) {
|
||||
$buckets = $this->buildBuckets($primaryModels, $relation->link, null, null, false);
|
||||
if ($model instanceof ActiveRecordInterface) {
|
||||
foreach ($models as $model) {
|
||||
$key = $this->getModelKey($model, $relation->link);
|
||||
$model->populateRelation($name, isset($buckets[$key]) ? $buckets[$key] : []);
|
||||
}
|
||||
} else {
|
||||
foreach ($primaryModels as $i => $primaryModel) {
|
||||
if ($this->multiple) {
|
||||
foreach ($primaryModel as $j => $m) {
|
||||
$key = $this->getModelKey($m, $relation->link);
|
||||
$primaryModels[$i][$j][$name] = isset($buckets[$key]) ? $buckets[$key] : [];
|
||||
}
|
||||
} elseif (!empty($primaryModel[$primaryName])) {
|
||||
$key = $this->getModelKey($primaryModel[$primaryName], $relation->link);
|
||||
$primaryModels[$i][$primaryName][$name] = isset($buckets[$key]) ? $buckets[$key] : [];
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ($this->multiple) {
|
||||
foreach ($primaryModels as $i => $primaryModel) {
|
||||
foreach ($primaryModel[$primaryName] as $j => $m) {
|
||||
if ($m instanceof ActiveRecordInterface) {
|
||||
$m->populateRelation($name, $primaryModel);
|
||||
} else {
|
||||
$primaryModels[$i][$primaryName][$j][$name] = $primaryModel;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
foreach ($primaryModels as $i => $primaryModel) {
|
||||
if ($primaryModels[$i][$primaryName] instanceof ActiveRecordInterface) {
|
||||
$primaryModels[$i][$primaryName]->populateRelation($name, $primaryModel);
|
||||
} elseif (!empty($primaryModels[$i][$primaryName])) {
|
||||
$primaryModels[$i][$primaryName][$name] = $primaryModel;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $models
|
||||
* @param array $link
|
||||
* @param array $viaModels
|
||||
* @param array $viaLink
|
||||
* @param bool $checkMultiple
|
||||
* @return array
|
||||
*/
|
||||
private function buildBuckets($models, $link, $viaModels = null, $viaLink = null, $checkMultiple = true)
|
||||
{
|
||||
if ($viaModels !== null) {
|
||||
$map = [];
|
||||
$viaLinkKeys = array_keys($viaLink);
|
||||
$linkValues = array_values($link);
|
||||
foreach ($viaModels as $viaModel) {
|
||||
$key1 = $this->getModelKey($viaModel, $viaLinkKeys);
|
||||
$key2 = $this->getModelKey($viaModel, $linkValues);
|
||||
$map[$key2][$key1] = true;
|
||||
}
|
||||
}
|
||||
|
||||
$buckets = [];
|
||||
$linkKeys = array_keys($link);
|
||||
|
||||
if (isset($map)) {
|
||||
foreach ($models as $model) {
|
||||
$key = $this->getModelKey($model, $linkKeys);
|
||||
if (isset($map[$key])) {
|
||||
foreach (array_keys($map[$key]) as $key2) {
|
||||
$buckets[$key2][] = $model;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
foreach ($models as $model) {
|
||||
$key = $this->getModelKey($model, $linkKeys);
|
||||
$buckets[$key][] = $model;
|
||||
}
|
||||
}
|
||||
|
||||
if ($checkMultiple && !$this->multiple) {
|
||||
foreach ($buckets as $i => $bucket) {
|
||||
$buckets[$i] = reset($bucket);
|
||||
}
|
||||
}
|
||||
|
||||
return $buckets;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Indexes buckets by column name.
|
||||
*
|
||||
* @param array $buckets
|
||||
* @param string|callable $indexBy the name of the column by which the query results should be indexed by.
|
||||
* This can also be a callable (e.g. anonymous function) that returns the index value based on the given row data.
|
||||
* @return array
|
||||
*/
|
||||
private function indexBuckets($buckets, $indexBy)
|
||||
{
|
||||
$result = [];
|
||||
foreach ($buckets as $key => $models) {
|
||||
$result[$key] = [];
|
||||
foreach ($models as $model) {
|
||||
$index = is_string($indexBy) ? $model[$indexBy] : call_user_func($indexBy, $model);
|
||||
$result[$key][$index] = $model;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $attributes the attributes to prefix
|
||||
* @return array
|
||||
*/
|
||||
private function prefixKeyColumns($attributes)
|
||||
{
|
||||
if ($this instanceof ActiveQuery && (!empty($this->join) || !empty($this->joinWith))) {
|
||||
if (empty($this->from)) {
|
||||
/* @var $modelClass ActiveRecord */
|
||||
$modelClass = $this->modelClass;
|
||||
$alias = $modelClass::tableName();
|
||||
} else {
|
||||
foreach ($this->from as $alias => $table) {
|
||||
if (!is_string($alias)) {
|
||||
$alias = $table;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isset($alias)) {
|
||||
foreach ($attributes as $i => $attribute) {
|
||||
$attributes[$i] = "$alias.$attribute";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $models
|
||||
*/
|
||||
private function filterByModels($models)
|
||||
{
|
||||
$attributes = array_keys($this->link);
|
||||
|
||||
$attributes = $this->prefixKeyColumns($attributes);
|
||||
|
||||
$values = [];
|
||||
if (count($attributes) === 1) {
|
||||
// single key
|
||||
$attribute = reset($this->link);
|
||||
foreach ($models as $model) {
|
||||
if (($value = $model[$attribute]) !== null) {
|
||||
if (is_array($value)) {
|
||||
$values = array_merge($values, $value);
|
||||
} else {
|
||||
$values[] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (empty($values)) {
|
||||
$this->emulateExecution();
|
||||
}
|
||||
} else {
|
||||
// composite keys
|
||||
|
||||
// ensure keys of $this->link are prefixed the same way as $attributes
|
||||
$prefixedLink = array_combine(
|
||||
$attributes,
|
||||
array_values($this->link)
|
||||
);
|
||||
foreach ($models as $model) {
|
||||
$v = [];
|
||||
foreach ($prefixedLink as $attribute => $link) {
|
||||
$v[$attribute] = $model[$link];
|
||||
}
|
||||
$values[] = $v;
|
||||
if (empty($v)) {
|
||||
$this->emulateExecution();
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->andWhere(['in', $attributes, array_unique($values, SORT_REGULAR)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ActiveRecordInterface|array $model
|
||||
* @param array $attributes
|
||||
* @return string
|
||||
*/
|
||||
private function getModelKey($model, $attributes)
|
||||
{
|
||||
$key = [];
|
||||
foreach ($attributes as $attribute) {
|
||||
$key[] = $this->normalizeModelKey($model[$attribute]);
|
||||
}
|
||||
if (count($key) > 1) {
|
||||
return serialize($key);
|
||||
}
|
||||
$key = reset($key);
|
||||
return is_scalar($key) ? $key : serialize($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value raw key value.
|
||||
* @return string normalized key value.
|
||||
*/
|
||||
private function normalizeModelKey($value)
|
||||
{
|
||||
if (is_object($value) && method_exists($value, '__toString')) {
|
||||
// ensure matching to special objects, which are convertable to string, for cross-DBMS relations, for example: `|MongoId`
|
||||
$value = $value->__toString();
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $primaryModels either array of AR instances or arrays
|
||||
* @return array
|
||||
*/
|
||||
private function findJunctionRows($primaryModels)
|
||||
{
|
||||
if (empty($primaryModels)) {
|
||||
return [];
|
||||
}
|
||||
$this->filterByModels($primaryModels);
|
||||
/* @var $primaryModel ActiveRecord */
|
||||
$primaryModel = reset($primaryModels);
|
||||
if (!$primaryModel instanceof ActiveRecordInterface) {
|
||||
// when primaryModels are array of arrays (asArray case)
|
||||
$primaryModel = $this->modelClass;
|
||||
}
|
||||
|
||||
return $this->asArray()->all($primaryModel::getDb());
|
||||
}
|
||||
}
|
||||
24
vendor/yiisoft/yii2/db/AfterSaveEvent.php
vendored
Normal file
24
vendor/yiisoft/yii2/db/AfterSaveEvent.php
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db;
|
||||
|
||||
use yii\base\Event;
|
||||
|
||||
/**
|
||||
* AfterSaveEvent represents the information available in [[ActiveRecord::EVENT_AFTER_INSERT]] and [[ActiveRecord::EVENT_AFTER_UPDATE]].
|
||||
*
|
||||
* @author Carsten Brandt <mail@cebe.cc>
|
||||
* @since 2.0
|
||||
*/
|
||||
class AfterSaveEvent extends Event
|
||||
{
|
||||
/**
|
||||
* @var array The attribute values that had changed and were saved.
|
||||
*/
|
||||
public $changedAttributes;
|
||||
}
|
||||
199
vendor/yiisoft/yii2/db/ArrayExpression.php
vendored
Normal file
199
vendor/yiisoft/yii2/db/ArrayExpression.php
vendored
Normal file
@@ -0,0 +1,199 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db;
|
||||
|
||||
use Traversable;
|
||||
use yii\base\InvalidConfigException;
|
||||
|
||||
/**
|
||||
* Class ArrayExpression represents an array SQL expression.
|
||||
*
|
||||
* Expressions of this type can be used in conditions as well:
|
||||
*
|
||||
* ```php
|
||||
* $query->andWhere(['@>', 'items', new ArrayExpression([1, 2, 3], 'integer')])
|
||||
* ```
|
||||
*
|
||||
* which, depending on DBMS, will result in a well-prepared condition. For example, in
|
||||
* PostgreSQL it will be compiled to `WHERE "items" @> ARRAY[1, 2, 3]::integer[]`.
|
||||
*
|
||||
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
|
||||
* @since 2.0.14
|
||||
*/
|
||||
class ArrayExpression implements ExpressionInterface, \ArrayAccess, \Countable, \IteratorAggregate
|
||||
{
|
||||
/**
|
||||
* @var null|string the type of the array elements. Defaults to `null` which means the type is
|
||||
* not explicitly specified.
|
||||
*
|
||||
* Note that in case when type is not specified explicitly and DBMS can not guess it from the context,
|
||||
* SQL error will be raised.
|
||||
*/
|
||||
private $type;
|
||||
/**
|
||||
* @var array|QueryInterface the array's content.
|
||||
* In can be represented as an array of values or a [[Query]] that returns these values.
|
||||
*/
|
||||
private $value;
|
||||
/**
|
||||
* @var int the number of indices needed to select an element
|
||||
*/
|
||||
private $dimension;
|
||||
|
||||
|
||||
/**
|
||||
* ArrayExpression constructor.
|
||||
*
|
||||
* @param array|QueryInterface|mixed $value the array content. Either represented as an array of values or a Query that
|
||||
* returns these values. A single value will be considered as an array containing one element.
|
||||
* @param string|null $type the type of the array elements. Defaults to `null` which means the type is
|
||||
* not explicitly specified. In case when type is not specified explicitly and DBMS can not guess it from the context,
|
||||
* SQL error will be raised.
|
||||
* @param int $dimension the number of indices needed to select an element
|
||||
*/
|
||||
public function __construct($value, $type = null, $dimension = 1)
|
||||
{
|
||||
if ($value instanceof self) {
|
||||
$value = $value->getValue();
|
||||
}
|
||||
|
||||
$this->value = $value;
|
||||
$this->type = $type;
|
||||
$this->dimension = $dimension;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null|string
|
||||
* @see type
|
||||
*/
|
||||
public function getType()
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|mixed|QueryInterface
|
||||
* @see value
|
||||
*/
|
||||
public function getValue()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int the number of indices needed to select an element
|
||||
* @see dimensions
|
||||
*/
|
||||
public function getDimension()
|
||||
{
|
||||
return $this->dimension;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a offset exists
|
||||
*
|
||||
* @link http://php.net/manual/en/arrayaccess.offsetexists.php
|
||||
* @param mixed $offset <p>
|
||||
* An offset to check for.
|
||||
* </p>
|
||||
* @return bool true on success or false on failure.
|
||||
* </p>
|
||||
* <p>
|
||||
* The return value will be casted to boolean if non-boolean was returned.
|
||||
* @since 2.0.14
|
||||
*/
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return isset($this->value[$offset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset to retrieve
|
||||
*
|
||||
* @link http://php.net/manual/en/arrayaccess.offsetget.php
|
||||
* @param mixed $offset <p>
|
||||
* The offset to retrieve.
|
||||
* </p>
|
||||
* @return mixed Can return all value types.
|
||||
* @since 2.0.14
|
||||
*/
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return $this->value[$offset];
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset to set
|
||||
*
|
||||
* @link http://php.net/manual/en/arrayaccess.offsetset.php
|
||||
* @param mixed $offset <p>
|
||||
* The offset to assign the value to.
|
||||
* </p>
|
||||
* @param mixed $value <p>
|
||||
* The value to set.
|
||||
* </p>
|
||||
* @return void
|
||||
* @since 2.0.14
|
||||
*/
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
$this->value[$offset] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset to unset
|
||||
*
|
||||
* @link http://php.net/manual/en/arrayaccess.offsetunset.php
|
||||
* @param mixed $offset <p>
|
||||
* The offset to unset.
|
||||
* </p>
|
||||
* @return void
|
||||
* @since 2.0.14
|
||||
*/
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
unset($this->value[$offset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Count elements of an object
|
||||
*
|
||||
* @link http://php.net/manual/en/countable.count.php
|
||||
* @return int The custom count as an integer.
|
||||
* </p>
|
||||
* <p>
|
||||
* The return value is cast to an integer.
|
||||
* @since 2.0.14
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
return count($this->value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an external iterator
|
||||
*
|
||||
* @link http://php.net/manual/en/iteratoraggregate.getiterator.php
|
||||
* @return Traversable An instance of an object implementing <b>Iterator</b> or
|
||||
* <b>Traversable</b>
|
||||
* @since 2.0.14.1
|
||||
* @throws InvalidConfigException when ArrayExpression contains QueryInterface object
|
||||
*/
|
||||
public function getIterator()
|
||||
{
|
||||
$value = $this->getValue();
|
||||
if ($value instanceof QueryInterface) {
|
||||
throw new InvalidConfigException('The ArrayExpression class can not be iterated when the value is a QueryInterface object');
|
||||
}
|
||||
if ($value === null) {
|
||||
$value = [];
|
||||
}
|
||||
|
||||
return new \ArrayIterator($value);
|
||||
}
|
||||
}
|
||||
1743
vendor/yiisoft/yii2/db/BaseActiveRecord.php
vendored
Normal file
1743
vendor/yiisoft/yii2/db/BaseActiveRecord.php
vendored
Normal file
File diff suppressed because it is too large
Load Diff
179
vendor/yiisoft/yii2/db/BatchQueryResult.php
vendored
Normal file
179
vendor/yiisoft/yii2/db/BatchQueryResult.php
vendored
Normal file
@@ -0,0 +1,179 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db;
|
||||
|
||||
use yii\base\BaseObject;
|
||||
|
||||
/**
|
||||
* BatchQueryResult represents a batch query from which you can retrieve data in batches.
|
||||
*
|
||||
* You usually do not instantiate BatchQueryResult directly. Instead, you obtain it by
|
||||
* calling [[Query::batch()]] or [[Query::each()]]. Because BatchQueryResult implements the [[\Iterator]] interface,
|
||||
* you can iterate it to obtain a batch of data in each iteration. For example,
|
||||
*
|
||||
* ```php
|
||||
* $query = (new Query)->from('user');
|
||||
* foreach ($query->batch() as $i => $users) {
|
||||
* // $users represents the rows in the $i-th batch
|
||||
* }
|
||||
* foreach ($query->each() as $user) {
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class BatchQueryResult extends BaseObject implements \Iterator
|
||||
{
|
||||
/**
|
||||
* @var Connection the DB connection to be used when performing batch query.
|
||||
* If null, the "db" application component will be used.
|
||||
*/
|
||||
public $db;
|
||||
/**
|
||||
* @var Query the query object associated with this batch query.
|
||||
* Do not modify this property directly unless after [[reset()]] is called explicitly.
|
||||
*/
|
||||
public $query;
|
||||
/**
|
||||
* @var int the number of rows to be returned in each batch.
|
||||
*/
|
||||
public $batchSize = 100;
|
||||
/**
|
||||
* @var bool whether to return a single row during each iteration.
|
||||
* If false, a whole batch of rows will be returned in each iteration.
|
||||
*/
|
||||
public $each = false;
|
||||
|
||||
/**
|
||||
* @var DataReader the data reader associated with this batch query.
|
||||
*/
|
||||
private $_dataReader;
|
||||
/**
|
||||
* @var array the data retrieved in the current batch
|
||||
*/
|
||||
private $_batch;
|
||||
/**
|
||||
* @var mixed the value for the current iteration
|
||||
*/
|
||||
private $_value;
|
||||
/**
|
||||
* @var string|int the key for the current iteration
|
||||
*/
|
||||
private $_key;
|
||||
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
// make sure cursor is closed
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the batch query.
|
||||
* This method will clean up the existing batch query so that a new batch query can be performed.
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
if ($this->_dataReader !== null) {
|
||||
$this->_dataReader->close();
|
||||
}
|
||||
$this->_dataReader = null;
|
||||
$this->_batch = null;
|
||||
$this->_value = null;
|
||||
$this->_key = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the iterator to the initial state.
|
||||
* This method is required by the interface [[\Iterator]].
|
||||
*/
|
||||
public function rewind()
|
||||
{
|
||||
$this->reset();
|
||||
$this->next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the internal pointer to the next dataset.
|
||||
* This method is required by the interface [[\Iterator]].
|
||||
*/
|
||||
public function next()
|
||||
{
|
||||
if ($this->_batch === null || !$this->each || $this->each && next($this->_batch) === false) {
|
||||
$this->_batch = $this->fetchData();
|
||||
reset($this->_batch);
|
||||
}
|
||||
|
||||
if ($this->each) {
|
||||
$this->_value = current($this->_batch);
|
||||
if ($this->query->indexBy !== null) {
|
||||
$this->_key = key($this->_batch);
|
||||
} elseif (key($this->_batch) !== null) {
|
||||
$this->_key = $this->_key === null ? 0 : $this->_key + 1;
|
||||
} else {
|
||||
$this->_key = null;
|
||||
}
|
||||
} else {
|
||||
$this->_value = $this->_batch;
|
||||
$this->_key = $this->_key === null ? 0 : $this->_key + 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the next batch of data.
|
||||
* @return array the data fetched
|
||||
*/
|
||||
protected function fetchData()
|
||||
{
|
||||
if ($this->_dataReader === null) {
|
||||
$this->_dataReader = $this->query->createCommand($this->db)->query();
|
||||
}
|
||||
|
||||
$rows = [];
|
||||
$count = 0;
|
||||
while ($count++ < $this->batchSize && ($row = $this->_dataReader->read())) {
|
||||
$rows[] = $row;
|
||||
}
|
||||
|
||||
return $this->query->populate($rows);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of the current dataset.
|
||||
* This method is required by the interface [[\Iterator]].
|
||||
* @return int the index of the current row.
|
||||
*/
|
||||
public function key()
|
||||
{
|
||||
return $this->_key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current dataset.
|
||||
* This method is required by the interface [[\Iterator]].
|
||||
* @return mixed the current dataset.
|
||||
*/
|
||||
public function current()
|
||||
{
|
||||
return $this->_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether there is a valid dataset at the current position.
|
||||
* This method is required by the interface [[\Iterator]].
|
||||
* @return bool whether there is a valid dataset at the current position.
|
||||
*/
|
||||
public function valid()
|
||||
{
|
||||
return !empty($this->_batch);
|
||||
}
|
||||
}
|
||||
22
vendor/yiisoft/yii2/db/CheckConstraint.php
vendored
Normal file
22
vendor/yiisoft/yii2/db/CheckConstraint.php
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db;
|
||||
|
||||
/**
|
||||
* CheckConstraint represents the metadata of a table `CHECK` constraint.
|
||||
*
|
||||
* @author Sergey Makinen <sergey@makinen.ru>
|
||||
* @since 2.0.13
|
||||
*/
|
||||
class CheckConstraint extends Constraint
|
||||
{
|
||||
/**
|
||||
* @var string the SQL of the `CHECK` constraint.
|
||||
*/
|
||||
public $expression;
|
||||
}
|
||||
178
vendor/yiisoft/yii2/db/ColumnSchema.php
vendored
Normal file
178
vendor/yiisoft/yii2/db/ColumnSchema.php
vendored
Normal file
@@ -0,0 +1,178 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db;
|
||||
|
||||
use yii\base\BaseObject;
|
||||
use yii\helpers\StringHelper;
|
||||
|
||||
/**
|
||||
* ColumnSchema class describes the metadata of a column in a database table.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class ColumnSchema extends BaseObject
|
||||
{
|
||||
/**
|
||||
* @var string name of this column (without quotes).
|
||||
*/
|
||||
public $name;
|
||||
/**
|
||||
* @var bool whether this column can be null.
|
||||
*/
|
||||
public $allowNull;
|
||||
/**
|
||||
* @var string abstract type of this column. Possible abstract types include:
|
||||
* char, string, text, boolean, smallint, integer, bigint, float, decimal, datetime,
|
||||
* timestamp, time, date, binary, and money.
|
||||
*/
|
||||
public $type;
|
||||
/**
|
||||
* @var string the PHP type of this column. Possible PHP types include:
|
||||
* `string`, `boolean`, `integer`, `double`, `array`.
|
||||
*/
|
||||
public $phpType;
|
||||
/**
|
||||
* @var string the DB type of this column. Possible DB types vary according to the type of DBMS.
|
||||
*/
|
||||
public $dbType;
|
||||
/**
|
||||
* @var mixed default value of this column
|
||||
*/
|
||||
public $defaultValue;
|
||||
/**
|
||||
* @var array enumerable values. This is set only if the column is declared to be an enumerable type.
|
||||
*/
|
||||
public $enumValues;
|
||||
/**
|
||||
* @var int display size of the column.
|
||||
*/
|
||||
public $size;
|
||||
/**
|
||||
* @var int precision of the column data, if it is numeric.
|
||||
*/
|
||||
public $precision;
|
||||
/**
|
||||
* @var int scale of the column data, if it is numeric.
|
||||
*/
|
||||
public $scale;
|
||||
/**
|
||||
* @var bool whether this column is a primary key
|
||||
*/
|
||||
public $isPrimaryKey;
|
||||
/**
|
||||
* @var bool whether this column is auto-incremental
|
||||
*/
|
||||
public $autoIncrement = false;
|
||||
/**
|
||||
* @var bool whether this column is unsigned. This is only meaningful
|
||||
* when [[type]] is `smallint`, `integer` or `bigint`.
|
||||
*/
|
||||
public $unsigned;
|
||||
/**
|
||||
* @var string comment of this column. Not all DBMS support this.
|
||||
*/
|
||||
public $comment;
|
||||
|
||||
|
||||
/**
|
||||
* Converts the input value according to [[phpType]] after retrieval from the database.
|
||||
* If the value is null or an [[Expression]], it will not be converted.
|
||||
* @param mixed $value input value
|
||||
* @return mixed converted value
|
||||
*/
|
||||
public function phpTypecast($value)
|
||||
{
|
||||
return $this->typecast($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the input value according to [[type]] and [[dbType]] for use in a db query.
|
||||
* If the value is null or an [[Expression]], it will not be converted.
|
||||
* @param mixed $value input value
|
||||
* @return mixed converted value. This may also be an array containing the value as the first element
|
||||
* and the PDO type as the second element.
|
||||
*/
|
||||
public function dbTypecast($value)
|
||||
{
|
||||
// the default implementation does the same as casting for PHP, but it should be possible
|
||||
// to override this with annotation of explicit PDO type.
|
||||
return $this->typecast($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the input value according to [[phpType]] after retrieval from the database.
|
||||
* If the value is null or an [[Expression]], it will not be converted.
|
||||
* @param mixed $value input value
|
||||
* @return mixed converted value
|
||||
* @since 2.0.3
|
||||
*/
|
||||
protected function typecast($value)
|
||||
{
|
||||
if ($value === ''
|
||||
&& !in_array(
|
||||
$this->type,
|
||||
[
|
||||
Schema::TYPE_TEXT,
|
||||
Schema::TYPE_STRING,
|
||||
Schema::TYPE_BINARY,
|
||||
Schema::TYPE_CHAR
|
||||
],
|
||||
true)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($value === null
|
||||
|| gettype($value) === $this->phpType
|
||||
|| $value instanceof ExpressionInterface
|
||||
|| $value instanceof Query
|
||||
) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if (is_array($value)
|
||||
&& count($value) === 2
|
||||
&& isset($value[1])
|
||||
&& in_array($value[1], $this->getPdoParamTypes(), true)
|
||||
) {
|
||||
return new PdoValue($value[0], $value[1]);
|
||||
}
|
||||
|
||||
switch ($this->phpType) {
|
||||
case 'resource':
|
||||
case 'string':
|
||||
if (is_resource($value)) {
|
||||
return $value;
|
||||
}
|
||||
if (is_float($value)) {
|
||||
// ensure type cast always has . as decimal separator in all locales
|
||||
return StringHelper::floatToString($value);
|
||||
}
|
||||
return (string) $value;
|
||||
case 'integer':
|
||||
return (int) $value;
|
||||
case 'boolean':
|
||||
// treating a 0 bit value as false too
|
||||
// https://github.com/yiisoft/yii2/issues/9006
|
||||
return (bool) $value && $value !== "\0";
|
||||
case 'double':
|
||||
return (float) $value;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int[] array of numbers that represent possible PDO parameter types
|
||||
*/
|
||||
private function getPdoParamTypes()
|
||||
{
|
||||
return [\PDO::PARAM_BOOL, \PDO::PARAM_INT, \PDO::PARAM_STR, \PDO::PARAM_LOB, \PDO::PARAM_NULL, \PDO::PARAM_STMT];
|
||||
}
|
||||
}
|
||||
456
vendor/yiisoft/yii2/db/ColumnSchemaBuilder.php
vendored
Normal file
456
vendor/yiisoft/yii2/db/ColumnSchemaBuilder.php
vendored
Normal file
@@ -0,0 +1,456 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db;
|
||||
|
||||
use Yii;
|
||||
use yii\base\BaseObject;
|
||||
use yii\helpers\StringHelper;
|
||||
|
||||
/**
|
||||
* ColumnSchemaBuilder helps to define database schema types using a PHP interface.
|
||||
*
|
||||
* See [[SchemaBuilderTrait]] for more detailed description and usage examples.
|
||||
*
|
||||
* @author Vasenin Matvey <vaseninm@gmail.com>
|
||||
* @since 2.0.6
|
||||
*/
|
||||
class ColumnSchemaBuilder extends BaseObject
|
||||
{
|
||||
// Internally used constants representing categories that abstract column types fall under.
|
||||
// See [[$categoryMap]] for mappings of abstract column types to category.
|
||||
// @since 2.0.8
|
||||
const CATEGORY_PK = 'pk';
|
||||
const CATEGORY_STRING = 'string';
|
||||
const CATEGORY_NUMERIC = 'numeric';
|
||||
const CATEGORY_TIME = 'time';
|
||||
const CATEGORY_OTHER = 'other';
|
||||
|
||||
/**
|
||||
* @var string the column type definition such as INTEGER, VARCHAR, DATETIME, etc.
|
||||
*/
|
||||
protected $type;
|
||||
/**
|
||||
* @var int|string|array column size or precision definition. This is what goes into the parenthesis after
|
||||
* the column type. This can be either a string, an integer or an array. If it is an array, the array values will
|
||||
* be joined into a string separated by comma.
|
||||
*/
|
||||
protected $length;
|
||||
/**
|
||||
* @var bool|null whether the column is or not nullable. If this is `true`, a `NOT NULL` constraint will be added.
|
||||
* If this is `false`, a `NULL` constraint will be added.
|
||||
*/
|
||||
protected $isNotNull;
|
||||
/**
|
||||
* @var bool whether the column values should be unique. If this is `true`, a `UNIQUE` constraint will be added.
|
||||
*/
|
||||
protected $isUnique = false;
|
||||
/**
|
||||
* @var string the `CHECK` constraint for the column.
|
||||
*/
|
||||
protected $check;
|
||||
/**
|
||||
* @var mixed default value of the column.
|
||||
*/
|
||||
protected $default;
|
||||
/**
|
||||
* @var mixed SQL string to be appended to column schema definition.
|
||||
* @since 2.0.9
|
||||
*/
|
||||
protected $append;
|
||||
/**
|
||||
* @var bool whether the column values should be unsigned. If this is `true`, an `UNSIGNED` keyword will be added.
|
||||
* @since 2.0.7
|
||||
*/
|
||||
protected $isUnsigned = false;
|
||||
/**
|
||||
* @var string the column after which this column will be added.
|
||||
* @since 2.0.8
|
||||
*/
|
||||
protected $after;
|
||||
/**
|
||||
* @var bool whether this column is to be inserted at the beginning of the table.
|
||||
* @since 2.0.8
|
||||
*/
|
||||
protected $isFirst;
|
||||
|
||||
|
||||
/**
|
||||
* @var array mapping of abstract column types (keys) to type categories (values).
|
||||
* @since 2.0.8
|
||||
*/
|
||||
public $categoryMap = [
|
||||
Schema::TYPE_PK => self::CATEGORY_PK,
|
||||
Schema::TYPE_UPK => self::CATEGORY_PK,
|
||||
Schema::TYPE_BIGPK => self::CATEGORY_PK,
|
||||
Schema::TYPE_UBIGPK => self::CATEGORY_PK,
|
||||
Schema::TYPE_CHAR => self::CATEGORY_STRING,
|
||||
Schema::TYPE_STRING => self::CATEGORY_STRING,
|
||||
Schema::TYPE_TEXT => self::CATEGORY_STRING,
|
||||
Schema::TYPE_TINYINT => self::CATEGORY_NUMERIC,
|
||||
Schema::TYPE_SMALLINT => self::CATEGORY_NUMERIC,
|
||||
Schema::TYPE_INTEGER => self::CATEGORY_NUMERIC,
|
||||
Schema::TYPE_BIGINT => self::CATEGORY_NUMERIC,
|
||||
Schema::TYPE_FLOAT => self::CATEGORY_NUMERIC,
|
||||
Schema::TYPE_DOUBLE => self::CATEGORY_NUMERIC,
|
||||
Schema::TYPE_DECIMAL => self::CATEGORY_NUMERIC,
|
||||
Schema::TYPE_DATETIME => self::CATEGORY_TIME,
|
||||
Schema::TYPE_TIMESTAMP => self::CATEGORY_TIME,
|
||||
Schema::TYPE_TIME => self::CATEGORY_TIME,
|
||||
Schema::TYPE_DATE => self::CATEGORY_TIME,
|
||||
Schema::TYPE_BINARY => self::CATEGORY_OTHER,
|
||||
Schema::TYPE_BOOLEAN => self::CATEGORY_NUMERIC,
|
||||
Schema::TYPE_MONEY => self::CATEGORY_NUMERIC,
|
||||
];
|
||||
/**
|
||||
* @var \yii\db\Connection the current database connection. It is used mainly to escape strings
|
||||
* safely when building the final column schema string.
|
||||
* @since 2.0.8
|
||||
*/
|
||||
public $db;
|
||||
/**
|
||||
* @var string comment value of the column.
|
||||
* @since 2.0.8
|
||||
*/
|
||||
public $comment;
|
||||
|
||||
/**
|
||||
* Create a column schema builder instance giving the type and value precision.
|
||||
*
|
||||
* @param string $type type of the column. See [[$type]].
|
||||
* @param int|string|array $length length or precision of the column. See [[$length]].
|
||||
* @param \yii\db\Connection $db the current database connection. See [[$db]].
|
||||
* @param array $config name-value pairs that will be used to initialize the object properties
|
||||
*/
|
||||
public function __construct($type, $length = null, $db = null, $config = [])
|
||||
{
|
||||
$this->type = $type;
|
||||
$this->length = $length;
|
||||
$this->db = $db;
|
||||
parent::__construct($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a `NOT NULL` constraint to the column.
|
||||
* @return $this
|
||||
*/
|
||||
public function notNull()
|
||||
{
|
||||
$this->isNotNull = true;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a `NULL` constraint to the column.
|
||||
* @return $this
|
||||
* @since 2.0.9
|
||||
*/
|
||||
public function null()
|
||||
{
|
||||
$this->isNotNull = false;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a `UNIQUE` constraint to the column.
|
||||
* @return $this
|
||||
*/
|
||||
public function unique()
|
||||
{
|
||||
$this->isUnique = true;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a `CHECK` constraint for the column.
|
||||
* @param string $check the SQL of the `CHECK` constraint to be added.
|
||||
* @return $this
|
||||
*/
|
||||
public function check($check)
|
||||
{
|
||||
$this->check = $check;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the default value for the column.
|
||||
* @param mixed $default the default value.
|
||||
* @return $this
|
||||
*/
|
||||
public function defaultValue($default)
|
||||
{
|
||||
if ($default === null) {
|
||||
$this->null();
|
||||
}
|
||||
|
||||
$this->default = $default;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the comment for column.
|
||||
* @param string $comment the comment
|
||||
* @return $this
|
||||
* @since 2.0.8
|
||||
*/
|
||||
public function comment($comment)
|
||||
{
|
||||
$this->comment = $comment;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks column as unsigned.
|
||||
* @return $this
|
||||
* @since 2.0.7
|
||||
*/
|
||||
public function unsigned()
|
||||
{
|
||||
switch ($this->type) {
|
||||
case Schema::TYPE_PK:
|
||||
$this->type = Schema::TYPE_UPK;
|
||||
break;
|
||||
case Schema::TYPE_BIGPK:
|
||||
$this->type = Schema::TYPE_UBIGPK;
|
||||
break;
|
||||
}
|
||||
$this->isUnsigned = true;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an `AFTER` constraint to the column.
|
||||
* Note: MySQL, Oracle and Cubrid support only.
|
||||
* @param string $after the column after which $this column will be added.
|
||||
* @return $this
|
||||
* @since 2.0.8
|
||||
*/
|
||||
public function after($after)
|
||||
{
|
||||
$this->after = $after;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an `FIRST` constraint to the column.
|
||||
* Note: MySQL, Oracle and Cubrid support only.
|
||||
* @return $this
|
||||
* @since 2.0.8
|
||||
*/
|
||||
public function first()
|
||||
{
|
||||
$this->isFirst = true;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the default SQL expression for the column.
|
||||
* @param string $default the default value expression.
|
||||
* @return $this
|
||||
* @since 2.0.7
|
||||
*/
|
||||
public function defaultExpression($default)
|
||||
{
|
||||
$this->default = new Expression($default);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify additional SQL to be appended to column definition.
|
||||
* Position modifiers will be appended after column definition in databases that support them.
|
||||
* @param string $sql the SQL string to be appended.
|
||||
* @return $this
|
||||
* @since 2.0.9
|
||||
*/
|
||||
public function append($sql)
|
||||
{
|
||||
$this->append = $sql;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the full string for the column's schema.
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
switch ($this->getTypeCategory()) {
|
||||
case self::CATEGORY_PK:
|
||||
$format = '{type}{check}{comment}{append}';
|
||||
break;
|
||||
default:
|
||||
$format = '{type}{length}{notnull}{unique}{default}{check}{comment}{append}';
|
||||
}
|
||||
|
||||
return $this->buildCompleteString($format);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the length/precision part of the column.
|
||||
* @return string
|
||||
*/
|
||||
protected function buildLengthString()
|
||||
{
|
||||
if ($this->length === null || $this->length === []) {
|
||||
return '';
|
||||
}
|
||||
if (is_array($this->length)) {
|
||||
$this->length = implode(',', $this->length);
|
||||
}
|
||||
|
||||
return "({$this->length})";
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the not null constraint for the column.
|
||||
* @return string returns 'NOT NULL' if [[isNotNull]] is true,
|
||||
* 'NULL' if [[isNotNull]] is false or an empty string otherwise.
|
||||
*/
|
||||
protected function buildNotNullString()
|
||||
{
|
||||
if ($this->isNotNull === true) {
|
||||
return ' NOT NULL';
|
||||
} elseif ($this->isNotNull === false) {
|
||||
return ' NULL';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the unique constraint for the column.
|
||||
* @return string returns string 'UNIQUE' if [[isUnique]] is true, otherwise it returns an empty string.
|
||||
*/
|
||||
protected function buildUniqueString()
|
||||
{
|
||||
return $this->isUnique ? ' UNIQUE' : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the default value specification for the column.
|
||||
* @return string string with default value of column.
|
||||
*/
|
||||
protected function buildDefaultString()
|
||||
{
|
||||
if ($this->default === null) {
|
||||
return $this->isNotNull === false ? ' DEFAULT NULL' : '';
|
||||
}
|
||||
|
||||
$string = ' DEFAULT ';
|
||||
switch (gettype($this->default)) {
|
||||
case 'integer':
|
||||
$string .= (string) $this->default;
|
||||
break;
|
||||
case 'double':
|
||||
// ensure type cast always has . as decimal separator in all locales
|
||||
$string .= StringHelper::floatToString($this->default);
|
||||
break;
|
||||
case 'boolean':
|
||||
$string .= $this->default ? 'TRUE' : 'FALSE';
|
||||
break;
|
||||
case 'object':
|
||||
$string .= (string) $this->default;
|
||||
break;
|
||||
default:
|
||||
$string .= "'{$this->default}'";
|
||||
}
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the check constraint for the column.
|
||||
* @return string a string containing the CHECK constraint.
|
||||
*/
|
||||
protected function buildCheckString()
|
||||
{
|
||||
return $this->check !== null ? " CHECK ({$this->check})" : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the unsigned string for column. Defaults to unsupported.
|
||||
* @return string a string containing UNSIGNED keyword.
|
||||
* @since 2.0.7
|
||||
*/
|
||||
protected function buildUnsignedString()
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the after constraint for the column. Defaults to unsupported.
|
||||
* @return string a string containing the AFTER constraint.
|
||||
* @since 2.0.8
|
||||
*/
|
||||
protected function buildAfterString()
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the first constraint for the column. Defaults to unsupported.
|
||||
* @return string a string containing the FIRST constraint.
|
||||
* @since 2.0.8
|
||||
*/
|
||||
protected function buildFirstString()
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the custom string that's appended to column definition.
|
||||
* @return string custom string to append.
|
||||
* @since 2.0.9
|
||||
*/
|
||||
protected function buildAppendString()
|
||||
{
|
||||
return $this->append !== null ? ' ' . $this->append : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the category of the column type.
|
||||
* @return string a string containing the column type category name.
|
||||
* @since 2.0.8
|
||||
*/
|
||||
protected function getTypeCategory()
|
||||
{
|
||||
return isset($this->categoryMap[$this->type]) ? $this->categoryMap[$this->type] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the comment specification for the column.
|
||||
* @return string a string containing the COMMENT keyword and the comment itself
|
||||
* @since 2.0.8
|
||||
*/
|
||||
protected function buildCommentString()
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the complete column definition from input format.
|
||||
* @param string $format the format of the definition.
|
||||
* @return string a string containing the complete column definition.
|
||||
* @since 2.0.8
|
||||
*/
|
||||
protected function buildCompleteString($format)
|
||||
{
|
||||
$placeholderValues = [
|
||||
'{type}' => $this->type,
|
||||
'{length}' => $this->buildLengthString(),
|
||||
'{unsigned}' => $this->buildUnsignedString(),
|
||||
'{notnull}' => $this->buildNotNullString(),
|
||||
'{unique}' => $this->buildUniqueString(),
|
||||
'{default}' => $this->buildDefaultString(),
|
||||
'{check}' => $this->buildCheckString(),
|
||||
'{comment}' => $this->buildCommentString(),
|
||||
'{pos}' => $this->isFirst ? $this->buildFirstString() : $this->buildAfterString(),
|
||||
'{append}' => $this->buildAppendString(),
|
||||
];
|
||||
return strtr($format, $placeholderValues);
|
||||
}
|
||||
}
|
||||
1285
vendor/yiisoft/yii2/db/Command.php
vendored
Normal file
1285
vendor/yiisoft/yii2/db/Command.php
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1176
vendor/yiisoft/yii2/db/Connection.php
vendored
Normal file
1176
vendor/yiisoft/yii2/db/Connection.php
vendored
Normal file
File diff suppressed because it is too large
Load Diff
28
vendor/yiisoft/yii2/db/Constraint.php
vendored
Normal file
28
vendor/yiisoft/yii2/db/Constraint.php
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db;
|
||||
|
||||
use yii\base\BaseObject;
|
||||
|
||||
/**
|
||||
* Constraint represents the metadata of a table constraint.
|
||||
*
|
||||
* @author Sergey Makinen <sergey@makinen.ru>
|
||||
* @since 2.0.13
|
||||
*/
|
||||
class Constraint extends BaseObject
|
||||
{
|
||||
/**
|
||||
* @var string[]|null list of column names the constraint belongs to.
|
||||
*/
|
||||
public $columnNames;
|
||||
/**
|
||||
* @var string|null the constraint name.
|
||||
*/
|
||||
public $name;
|
||||
}
|
||||
125
vendor/yiisoft/yii2/db/ConstraintFinderInterface.php
vendored
Normal file
125
vendor/yiisoft/yii2/db/ConstraintFinderInterface.php
vendored
Normal file
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db;
|
||||
|
||||
/**
|
||||
* ConstraintFinderInterface defines methods for getting a table constraint information.
|
||||
*
|
||||
* @author Sergey Makinen <sergey@makinen.ru>
|
||||
* @since 2.0.14
|
||||
*/
|
||||
interface ConstraintFinderInterface
|
||||
{
|
||||
/**
|
||||
* Obtains the primary key for the named table.
|
||||
* @param string $name table name. The table name may contain schema name if any. Do not quote the table name.
|
||||
* @param bool $refresh whether to reload the information even if it is found in the cache.
|
||||
* @return Constraint|null table primary key, `null` if the table has no primary key.
|
||||
*/
|
||||
public function getTablePrimaryKey($name, $refresh = false);
|
||||
|
||||
/**
|
||||
* Returns primary keys for all tables in the database.
|
||||
* @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema name.
|
||||
* @param bool $refresh whether to fetch the latest available table schemas. If this is `false`,
|
||||
* cached data may be returned if available.
|
||||
* @return Constraint[] primary keys for all tables in the database.
|
||||
* Each array element is an instance of [[Constraint]] or its child class.
|
||||
*/
|
||||
public function getSchemaPrimaryKeys($schema = '', $refresh = false);
|
||||
|
||||
/**
|
||||
* Obtains the foreign keys information for the named table.
|
||||
* @param string $name table name. The table name may contain schema name if any. Do not quote the table name.
|
||||
* @param bool $refresh whether to reload the information even if it is found in the cache.
|
||||
* @return ForeignKeyConstraint[] table foreign keys.
|
||||
*/
|
||||
public function getTableForeignKeys($name, $refresh = false);
|
||||
|
||||
/**
|
||||
* Returns foreign keys for all tables in the database.
|
||||
* @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema name.
|
||||
* @param bool $refresh whether to fetch the latest available table schemas. If this is false,
|
||||
* cached data may be returned if available.
|
||||
* @return ForeignKeyConstraint[][] foreign keys for all tables in the database.
|
||||
* Each array element is an array of [[ForeignKeyConstraint]] or its child classes.
|
||||
*/
|
||||
public function getSchemaForeignKeys($schema = '', $refresh = false);
|
||||
|
||||
/**
|
||||
* Obtains the indexes information for the named table.
|
||||
* @param string $name table name. The table name may contain schema name if any. Do not quote the table name.
|
||||
* @param bool $refresh whether to reload the information even if it is found in the cache.
|
||||
* @return IndexConstraint[] table indexes.
|
||||
*/
|
||||
public function getTableIndexes($name, $refresh = false);
|
||||
|
||||
/**
|
||||
* Returns indexes for all tables in the database.
|
||||
* @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema name.
|
||||
* @param bool $refresh whether to fetch the latest available table schemas. If this is false,
|
||||
* cached data may be returned if available.
|
||||
* @return IndexConstraint[][] indexes for all tables in the database.
|
||||
* Each array element is an array of [[IndexConstraint]] or its child classes.
|
||||
*/
|
||||
public function getSchemaIndexes($schema = '', $refresh = false);
|
||||
|
||||
/**
|
||||
* Obtains the unique constraints information for the named table.
|
||||
* @param string $name table name. The table name may contain schema name if any. Do not quote the table name.
|
||||
* @param bool $refresh whether to reload the information even if it is found in the cache.
|
||||
* @return Constraint[] table unique constraints.
|
||||
*/
|
||||
public function getTableUniques($name, $refresh = false);
|
||||
|
||||
/**
|
||||
* Returns unique constraints for all tables in the database.
|
||||
* @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema name.
|
||||
* @param bool $refresh whether to fetch the latest available table schemas. If this is false,
|
||||
* cached data may be returned if available.
|
||||
* @return Constraint[][] unique constraints for all tables in the database.
|
||||
* Each array element is an array of [[Constraint]] or its child classes.
|
||||
*/
|
||||
public function getSchemaUniques($schema = '', $refresh = false);
|
||||
|
||||
/**
|
||||
* Obtains the check constraints information for the named table.
|
||||
* @param string $name table name. The table name may contain schema name if any. Do not quote the table name.
|
||||
* @param bool $refresh whether to reload the information even if it is found in the cache.
|
||||
* @return CheckConstraint[] table check constraints.
|
||||
*/
|
||||
public function getTableChecks($name, $refresh = false);
|
||||
|
||||
/**
|
||||
* Returns check constraints for all tables in the database.
|
||||
* @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema name.
|
||||
* @param bool $refresh whether to fetch the latest available table schemas. If this is false,
|
||||
* cached data may be returned if available.
|
||||
* @return CheckConstraint[][] check constraints for all tables in the database.
|
||||
* Each array element is an array of [[CheckConstraint]] or its child classes.
|
||||
*/
|
||||
public function getSchemaChecks($schema = '', $refresh = false);
|
||||
|
||||
/**
|
||||
* Obtains the default value constraints information for the named table.
|
||||
* @param string $name table name. The table name may contain schema name if any. Do not quote the table name.
|
||||
* @param bool $refresh whether to reload the information even if it is found in the cache.
|
||||
* @return DefaultValueConstraint[] table default value constraints.
|
||||
*/
|
||||
public function getTableDefaultValues($name, $refresh = false);
|
||||
|
||||
/**
|
||||
* Returns default value constraints for all tables in the database.
|
||||
* @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema name.
|
||||
* @param bool $refresh whether to fetch the latest available table schemas. If this is false,
|
||||
* cached data may be returned if available.
|
||||
* @return DefaultValueConstraint[] default value constraints for all tables in the database.
|
||||
* Each array element is an array of [[DefaultValueConstraint]] or its child classes.
|
||||
*/
|
||||
public function getSchemaDefaultValues($schema = '', $refresh = false);
|
||||
}
|
||||
236
vendor/yiisoft/yii2/db/ConstraintFinderTrait.php
vendored
Normal file
236
vendor/yiisoft/yii2/db/ConstraintFinderTrait.php
vendored
Normal file
@@ -0,0 +1,236 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db;
|
||||
|
||||
/**
|
||||
* ConstraintFinderTrait provides methods for getting a table constraint information.
|
||||
*
|
||||
* @property CheckConstraint[][] $schemaChecks Check constraints for all tables in the database.
|
||||
* Each array element is an array of [[CheckConstraint]] or its child classes. This property is read-only.
|
||||
* @property DefaultValueConstraint[] $schemaDefaultValues Default value constraints for all tables in the database.
|
||||
* Each array element is an array of [[DefaultValueConstraint]] or its child classes. This property is read-only.
|
||||
* @property ForeignKeyConstraint[][] $schemaForeignKeys Foreign keys for all tables in the database. Each
|
||||
* array element is an array of [[ForeignKeyConstraint]] or its child classes. This property is read-only.
|
||||
* @property IndexConstraint[][] $schemaIndexes Indexes for all tables in the database. Each array element is
|
||||
* an array of [[IndexConstraint]] or its child classes. This property is read-only.
|
||||
* @property Constraint[] $schemaPrimaryKeys Primary keys for all tables in the database. Each array element
|
||||
* is an instance of [[Constraint]] or its child class. This property is read-only.
|
||||
* @property IndexConstraint[][] $schemaUniques Unique constraints for all tables in the database.
|
||||
* Each array element is an array of [[IndexConstraint]] or its child classes. This property is read-only.
|
||||
*
|
||||
* @author Sergey Makinen <sergey@makinen.ru>
|
||||
* @since 2.0.13
|
||||
*/
|
||||
trait ConstraintFinderTrait
|
||||
{
|
||||
/**
|
||||
* Returns the metadata of the given type for the given table.
|
||||
* @param string $name table name. The table name may contain schema name if any. Do not quote the table name.
|
||||
* @param string $type metadata type.
|
||||
* @param bool $refresh whether to reload the table metadata even if it is found in the cache.
|
||||
* @return mixed metadata.
|
||||
*/
|
||||
abstract protected function getTableMetadata($name, $type, $refresh);
|
||||
|
||||
/**
|
||||
* Returns the metadata of the given type for all tables in the given schema.
|
||||
* @param string $schema the schema of the metadata. Defaults to empty string, meaning the current or default schema name.
|
||||
* @param string $type metadata type.
|
||||
* @param bool $refresh whether to fetch the latest available table metadata. If this is `false`,
|
||||
* cached data may be returned if available.
|
||||
* @return array array of metadata.
|
||||
*/
|
||||
abstract protected function getSchemaMetadata($schema, $type, $refresh);
|
||||
|
||||
/**
|
||||
* Loads a primary key for the given table.
|
||||
* @param string $tableName table name.
|
||||
* @return Constraint|null primary key for the given table, `null` if the table has no primary key.
|
||||
*/
|
||||
abstract protected function loadTablePrimaryKey($tableName);
|
||||
|
||||
/**
|
||||
* Loads all foreign keys for the given table.
|
||||
* @param string $tableName table name.
|
||||
* @return ForeignKeyConstraint[] foreign keys for the given table.
|
||||
*/
|
||||
abstract protected function loadTableForeignKeys($tableName);
|
||||
|
||||
/**
|
||||
* Loads all indexes for the given table.
|
||||
* @param string $tableName table name.
|
||||
* @return IndexConstraint[] indexes for the given table.
|
||||
*/
|
||||
abstract protected function loadTableIndexes($tableName);
|
||||
|
||||
/**
|
||||
* Loads all unique constraints for the given table.
|
||||
* @param string $tableName table name.
|
||||
* @return Constraint[] unique constraints for the given table.
|
||||
*/
|
||||
abstract protected function loadTableUniques($tableName);
|
||||
|
||||
/**
|
||||
* Loads all check constraints for the given table.
|
||||
* @param string $tableName table name.
|
||||
* @return CheckConstraint[] check constraints for the given table.
|
||||
*/
|
||||
abstract protected function loadTableChecks($tableName);
|
||||
|
||||
/**
|
||||
* Loads all default value constraints for the given table.
|
||||
*
|
||||
* @param string $tableName table name.
|
||||
* @return DefaultValueConstraint[] default value constraints for the given table.
|
||||
*/
|
||||
abstract protected function loadTableDefaultValues($tableName);
|
||||
|
||||
/**
|
||||
* Obtains the primary key for the named table.
|
||||
* @param string $name table name. The table name may contain schema name if any. Do not quote the table name.
|
||||
* @param bool $refresh whether to reload the information even if it is found in the cache.
|
||||
* @return Constraint|null table primary key, `null` if the table has no primary key.
|
||||
*/
|
||||
public function getTablePrimaryKey($name, $refresh = false)
|
||||
{
|
||||
return $this->getTableMetadata($name, 'primaryKey', $refresh);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns primary keys for all tables in the database.
|
||||
* @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema name.
|
||||
* @param bool $refresh whether to fetch the latest available table schemas. If this is `false`,
|
||||
* cached data may be returned if available.
|
||||
* @return Constraint[] primary keys for all tables in the database.
|
||||
* Each array element is an instance of [[Constraint]] or its child class.
|
||||
*/
|
||||
public function getSchemaPrimaryKeys($schema = '', $refresh = false)
|
||||
{
|
||||
return $this->getSchemaMetadata($schema, 'primaryKey', $refresh);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the foreign keys information for the named table.
|
||||
* @param string $name table name. The table name may contain schema name if any. Do not quote the table name.
|
||||
* @param bool $refresh whether to reload the information even if it is found in the cache.
|
||||
* @return ForeignKeyConstraint[] table foreign keys.
|
||||
*/
|
||||
public function getTableForeignKeys($name, $refresh = false)
|
||||
{
|
||||
return $this->getTableMetadata($name, 'foreignKeys', $refresh);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns foreign keys for all tables in the database.
|
||||
* @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema name.
|
||||
* @param bool $refresh whether to fetch the latest available table schemas. If this is false,
|
||||
* cached data may be returned if available.
|
||||
* @return ForeignKeyConstraint[][] foreign keys for all tables in the database.
|
||||
* Each array element is an array of [[ForeignKeyConstraint]] or its child classes.
|
||||
*/
|
||||
public function getSchemaForeignKeys($schema = '', $refresh = false)
|
||||
{
|
||||
return $this->getSchemaMetadata($schema, 'foreignKeys', $refresh);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the indexes information for the named table.
|
||||
* @param string $name table name. The table name may contain schema name if any. Do not quote the table name.
|
||||
* @param bool $refresh whether to reload the information even if it is found in the cache.
|
||||
* @return IndexConstraint[] table indexes.
|
||||
*/
|
||||
public function getTableIndexes($name, $refresh = false)
|
||||
{
|
||||
return $this->getTableMetadata($name, 'indexes', $refresh);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns indexes for all tables in the database.
|
||||
* @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema name.
|
||||
* @param bool $refresh whether to fetch the latest available table schemas. If this is false,
|
||||
* cached data may be returned if available.
|
||||
* @return IndexConstraint[][] indexes for all tables in the database.
|
||||
* Each array element is an array of [[IndexConstraint]] or its child classes.
|
||||
*/
|
||||
public function getSchemaIndexes($schema = '', $refresh = false)
|
||||
{
|
||||
return $this->getSchemaMetadata($schema, 'indexes', $refresh);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the unique constraints information for the named table.
|
||||
* @param string $name table name. The table name may contain schema name if any. Do not quote the table name.
|
||||
* @param bool $refresh whether to reload the information even if it is found in the cache.
|
||||
* @return Constraint[] table unique constraints.
|
||||
*/
|
||||
public function getTableUniques($name, $refresh = false)
|
||||
{
|
||||
return $this->getTableMetadata($name, 'uniques', $refresh);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns unique constraints for all tables in the database.
|
||||
* @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema name.
|
||||
* @param bool $refresh whether to fetch the latest available table schemas. If this is false,
|
||||
* cached data may be returned if available.
|
||||
* @return Constraint[][] unique constraints for all tables in the database.
|
||||
* Each array element is an array of [[Constraint]] or its child classes.
|
||||
*/
|
||||
public function getSchemaUniques($schema = '', $refresh = false)
|
||||
{
|
||||
return $this->getSchemaMetadata($schema, 'uniques', $refresh);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the check constraints information for the named table.
|
||||
* @param string $name table name. The table name may contain schema name if any. Do not quote the table name.
|
||||
* @param bool $refresh whether to reload the information even if it is found in the cache.
|
||||
* @return CheckConstraint[] table check constraints.
|
||||
*/
|
||||
public function getTableChecks($name, $refresh = false)
|
||||
{
|
||||
return $this->getTableMetadata($name, 'checks', $refresh);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns check constraints for all tables in the database.
|
||||
* @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema name.
|
||||
* @param bool $refresh whether to fetch the latest available table schemas. If this is false,
|
||||
* cached data may be returned if available.
|
||||
* @return CheckConstraint[][] check constraints for all tables in the database.
|
||||
* Each array element is an array of [[CheckConstraint]] or its child classes.
|
||||
*/
|
||||
public function getSchemaChecks($schema = '', $refresh = false)
|
||||
{
|
||||
return $this->getSchemaMetadata($schema, 'checks', $refresh);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the default value constraints information for the named table.
|
||||
* @param string $name table name. The table name may contain schema name if any. Do not quote the table name.
|
||||
* @param bool $refresh whether to reload the information even if it is found in the cache.
|
||||
* @return DefaultValueConstraint[] table default value constraints.
|
||||
*/
|
||||
public function getTableDefaultValues($name, $refresh = false)
|
||||
{
|
||||
return $this->getTableMetadata($name, 'defaultValues', $refresh);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns default value constraints for all tables in the database.
|
||||
* @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema name.
|
||||
* @param bool $refresh whether to fetch the latest available table schemas. If this is false,
|
||||
* cached data may be returned if available.
|
||||
* @return DefaultValueConstraint[] default value constraints for all tables in the database.
|
||||
* Each array element is an array of [[DefaultValueConstraint]] or its child classes.
|
||||
*/
|
||||
public function getSchemaDefaultValues($schema = '', $refresh = false)
|
||||
{
|
||||
return $this->getSchemaMetadata($schema, 'defaultValues', $refresh);
|
||||
}
|
||||
}
|
||||
268
vendor/yiisoft/yii2/db/DataReader.php
vendored
Normal file
268
vendor/yiisoft/yii2/db/DataReader.php
vendored
Normal file
@@ -0,0 +1,268 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db;
|
||||
|
||||
use yii\base\InvalidCallException;
|
||||
|
||||
/**
|
||||
* DataReader represents a forward-only stream of rows from a query result set.
|
||||
*
|
||||
* To read the current row of data, call [[read()]]. The method [[readAll()]]
|
||||
* returns all the rows in a single array. Rows of data can also be read by
|
||||
* iterating through the reader. For example,
|
||||
*
|
||||
* ```php
|
||||
* $command = $connection->createCommand('SELECT * FROM post');
|
||||
* $reader = $command->query();
|
||||
*
|
||||
* while ($row = $reader->read()) {
|
||||
* $rows[] = $row;
|
||||
* }
|
||||
*
|
||||
* // equivalent to:
|
||||
* foreach ($reader as $row) {
|
||||
* $rows[] = $row;
|
||||
* }
|
||||
*
|
||||
* // equivalent to:
|
||||
* $rows = $reader->readAll();
|
||||
* ```
|
||||
*
|
||||
* Note that since DataReader is a forward-only stream, you can only traverse it once.
|
||||
* Doing it the second time will throw an exception.
|
||||
*
|
||||
* It is possible to use a specific mode of data fetching by setting
|
||||
* [[fetchMode]]. See the [PHP manual](http://www.php.net/manual/en/function.PDOStatement-setFetchMode.php)
|
||||
* for more details about possible fetch mode.
|
||||
*
|
||||
* @property int $columnCount The number of columns in the result set. This property is read-only.
|
||||
* @property int $fetchMode Fetch mode. This property is write-only.
|
||||
* @property bool $isClosed Whether the reader is closed or not. This property is read-only.
|
||||
* @property int $rowCount Number of rows contained in the result. This property is read-only.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class DataReader extends \yii\base\BaseObject implements \Iterator, \Countable
|
||||
{
|
||||
/**
|
||||
* @var \PDOStatement the PDOStatement associated with the command
|
||||
*/
|
||||
private $_statement;
|
||||
private $_closed = false;
|
||||
private $_row;
|
||||
private $_index = -1;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param Command $command the command generating the query result
|
||||
* @param array $config name-value pairs that will be used to initialize the object properties
|
||||
*/
|
||||
public function __construct(Command $command, $config = [])
|
||||
{
|
||||
$this->_statement = $command->pdoStatement;
|
||||
$this->_statement->setFetchMode(\PDO::FETCH_ASSOC);
|
||||
parent::__construct($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds a column to a PHP variable.
|
||||
* When rows of data are being fetched, the corresponding column value
|
||||
* will be set in the variable. Note, the fetch mode must include PDO::FETCH_BOUND.
|
||||
* @param int|string $column Number of the column (1-indexed) or name of the column
|
||||
* in the result set. If using the column name, be aware that the name
|
||||
* should match the case of the column, as returned by the driver.
|
||||
* @param mixed $value Name of the PHP variable to which the column will be bound.
|
||||
* @param int $dataType Data type of the parameter
|
||||
* @see http://www.php.net/manual/en/function.PDOStatement-bindColumn.php
|
||||
*/
|
||||
public function bindColumn($column, &$value, $dataType = null)
|
||||
{
|
||||
if ($dataType === null) {
|
||||
$this->_statement->bindColumn($column, $value);
|
||||
} else {
|
||||
$this->_statement->bindColumn($column, $value, $dataType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default fetch mode for this statement.
|
||||
*
|
||||
* @param int $mode fetch mode
|
||||
* @see http://www.php.net/manual/en/function.PDOStatement-setFetchMode.php
|
||||
*/
|
||||
public function setFetchMode($mode)
|
||||
{
|
||||
$params = func_get_args();
|
||||
call_user_func_array([$this->_statement, 'setFetchMode'], $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Advances the reader to the next row in a result set.
|
||||
* @return array the current row, false if no more row available
|
||||
*/
|
||||
public function read()
|
||||
{
|
||||
return $this->_statement->fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a single column from the next row of a result set.
|
||||
* @param int $columnIndex zero-based column index
|
||||
* @return mixed the column of the current row, false if no more rows available
|
||||
*/
|
||||
public function readColumn($columnIndex)
|
||||
{
|
||||
return $this->_statement->fetchColumn($columnIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an object populated with the next row of data.
|
||||
* @param string $className class name of the object to be created and populated
|
||||
* @param array $fields Elements of this array are passed to the constructor
|
||||
* @return mixed the populated object, false if no more row of data available
|
||||
*/
|
||||
public function readObject($className, $fields)
|
||||
{
|
||||
return $this->_statement->fetchObject($className, $fields);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the whole result set into an array.
|
||||
* @return array the result set (each array element represents a row of data).
|
||||
* An empty array will be returned if the result contains no row.
|
||||
*/
|
||||
public function readAll()
|
||||
{
|
||||
return $this->_statement->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Advances the reader to the next result when reading the results of a batch of statements.
|
||||
* This method is only useful when there are multiple result sets
|
||||
* returned by the query. Not all DBMS support this feature.
|
||||
* @return bool Returns true on success or false on failure.
|
||||
*/
|
||||
public function nextResult()
|
||||
{
|
||||
if (($result = $this->_statement->nextRowset()) !== false) {
|
||||
$this->_index = -1;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the reader.
|
||||
* This frees up the resources allocated for executing this SQL statement.
|
||||
* Read attempts after this method call are unpredictable.
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
$this->_statement->closeCursor();
|
||||
$this->_closed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* whether the reader is closed or not.
|
||||
* @return bool whether the reader is closed or not.
|
||||
*/
|
||||
public function getIsClosed()
|
||||
{
|
||||
return $this->_closed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of rows in the result set.
|
||||
* Note, most DBMS may not give a meaningful count.
|
||||
* In this case, use "SELECT COUNT(*) FROM tableName" to obtain the number of rows.
|
||||
* @return int number of rows contained in the result.
|
||||
*/
|
||||
public function getRowCount()
|
||||
{
|
||||
return $this->_statement->rowCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of rows in the result set.
|
||||
* This method is required by the Countable interface.
|
||||
* Note, most DBMS may not give a meaningful count.
|
||||
* In this case, use "SELECT COUNT(*) FROM tableName" to obtain the number of rows.
|
||||
* @return int number of rows contained in the result.
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
return $this->getRowCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of columns in the result set.
|
||||
* Note, even there's no row in the reader, this still gives correct column number.
|
||||
* @return int the number of columns in the result set.
|
||||
*/
|
||||
public function getColumnCount()
|
||||
{
|
||||
return $this->_statement->columnCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the iterator to the initial state.
|
||||
* This method is required by the interface [[\Iterator]].
|
||||
* @throws InvalidCallException if this method is invoked twice
|
||||
*/
|
||||
public function rewind()
|
||||
{
|
||||
if ($this->_index < 0) {
|
||||
$this->_row = $this->_statement->fetch();
|
||||
$this->_index = 0;
|
||||
} else {
|
||||
throw new InvalidCallException('DataReader cannot rewind. It is a forward-only reader.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of the current row.
|
||||
* This method is required by the interface [[\Iterator]].
|
||||
* @return int the index of the current row.
|
||||
*/
|
||||
public function key()
|
||||
{
|
||||
return $this->_index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current row.
|
||||
* This method is required by the interface [[\Iterator]].
|
||||
* @return mixed the current row.
|
||||
*/
|
||||
public function current()
|
||||
{
|
||||
return $this->_row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the internal pointer to the next row.
|
||||
* This method is required by the interface [[\Iterator]].
|
||||
*/
|
||||
public function next()
|
||||
{
|
||||
$this->_row = $this->_statement->fetch();
|
||||
$this->_index++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether there is a row of data at current position.
|
||||
* This method is required by the interface [[\Iterator]].
|
||||
* @return bool whether there is a row of data at current position.
|
||||
*/
|
||||
public function valid()
|
||||
{
|
||||
return $this->_row !== false;
|
||||
}
|
||||
}
|
||||
22
vendor/yiisoft/yii2/db/DefaultValueConstraint.php
vendored
Normal file
22
vendor/yiisoft/yii2/db/DefaultValueConstraint.php
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db;
|
||||
|
||||
/**
|
||||
* DefaultValueConstraint represents the metadata of a table `DEFAULT` constraint.
|
||||
*
|
||||
* @author Sergey Makinen <sergey@makinen.ru>
|
||||
* @since 2.0.13
|
||||
*/
|
||||
class DefaultValueConstraint extends Constraint
|
||||
{
|
||||
/**
|
||||
* @var mixed default value as returned by the DBMS.
|
||||
*/
|
||||
public $value;
|
||||
}
|
||||
54
vendor/yiisoft/yii2/db/Exception.php
vendored
Normal file
54
vendor/yiisoft/yii2/db/Exception.php
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db;
|
||||
|
||||
/**
|
||||
* Exception represents an exception that is caused by some DB-related operations.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class Exception extends \yii\base\Exception
|
||||
{
|
||||
/**
|
||||
* @var array the error info provided by a PDO exception. This is the same as returned
|
||||
* by [PDO::errorInfo](http://www.php.net/manual/en/pdo.errorinfo.php).
|
||||
*/
|
||||
public $errorInfo = [];
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param string $message PDO error message
|
||||
* @param array $errorInfo PDO error info
|
||||
* @param int $code PDO error code
|
||||
* @param \Exception $previous The previous exception used for the exception chaining.
|
||||
*/
|
||||
public function __construct($message, $errorInfo = [], $code = 0, \Exception $previous = null)
|
||||
{
|
||||
$this->errorInfo = $errorInfo;
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string the user-friendly name of this exception
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'Database Exception';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string readable representation of exception
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return parent::__toString() . PHP_EOL
|
||||
. 'Additional Information:' . PHP_EOL . print_r($this->errorInfo, true);
|
||||
}
|
||||
}
|
||||
66
vendor/yiisoft/yii2/db/Expression.php
vendored
Normal file
66
vendor/yiisoft/yii2/db/Expression.php
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db;
|
||||
|
||||
/**
|
||||
* Expression represents a DB expression that does not need escaping or quoting.
|
||||
*
|
||||
* When an Expression object is embedded within a SQL statement or fragment,
|
||||
* it will be replaced with the [[expression]] property value without any
|
||||
* DB escaping or quoting. For example,
|
||||
*
|
||||
* ```php
|
||||
* $expression = new Expression('NOW()');
|
||||
* $now = (new \yii\db\Query)->select($expression)->scalar(); // SELECT NOW();
|
||||
* echo $now; // prints the current date
|
||||
* ```
|
||||
*
|
||||
* Expression objects are mainly created for passing raw SQL expressions to methods of
|
||||
* [[Query]], [[ActiveQuery]], and related classes.
|
||||
*
|
||||
* An expression can also be bound with parameters specified via [[params]].
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class Expression extends \yii\base\BaseObject implements ExpressionInterface
|
||||
{
|
||||
/**
|
||||
* @var string the DB expression
|
||||
*/
|
||||
public $expression;
|
||||
/**
|
||||
* @var array list of parameters that should be bound for this expression.
|
||||
* The keys are placeholders appearing in [[expression]] and the values
|
||||
* are the corresponding parameter values.
|
||||
*/
|
||||
public $params = [];
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param string $expression the DB expression
|
||||
* @param array $params parameters
|
||||
* @param array $config name-value pairs that will be used to initialize the object properties
|
||||
*/
|
||||
public function __construct($expression, $params = [], $config = [])
|
||||
{
|
||||
$this->expression = $expression;
|
||||
$this->params = $params;
|
||||
parent::__construct($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* String magic method.
|
||||
* @return string the DB expression.
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->expression;
|
||||
}
|
||||
}
|
||||
30
vendor/yiisoft/yii2/db/ExpressionBuilder.php
vendored
Normal file
30
vendor/yiisoft/yii2/db/ExpressionBuilder.php
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db;
|
||||
|
||||
/**
|
||||
* Class ExpressionBuilder builds objects of [[yii\db\Expression]] class.
|
||||
*
|
||||
* @author Dmitry Naumenko <d.naumenko.a@gmail.com>
|
||||
* @since 2.0.14
|
||||
*/
|
||||
class ExpressionBuilder implements ExpressionBuilderInterface
|
||||
{
|
||||
use ExpressionBuilderTrait;
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @param Expression|ExpressionInterface $expression the expression to be built
|
||||
*/
|
||||
public function build(ExpressionInterface $expression, array &$params = [])
|
||||
{
|
||||
$params = array_merge($params, $expression->params);
|
||||
return $expression->__toString();
|
||||
}
|
||||
}
|
||||
28
vendor/yiisoft/yii2/db/ExpressionBuilderInterface.php
vendored
Normal file
28
vendor/yiisoft/yii2/db/ExpressionBuilderInterface.php
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db;
|
||||
|
||||
/**
|
||||
* Interface ExpressionBuilderInterface is designed to build raw SQL from specific expression
|
||||
* objects that implement [[ExpressionInterface]].
|
||||
*
|
||||
* @author Dmitry Naumenko <d.naumenko.a@gmail.com>
|
||||
* @since 2.0.14
|
||||
*/
|
||||
interface ExpressionBuilderInterface
|
||||
{
|
||||
/**
|
||||
* Method builds the raw SQL from the $expression that will not be additionally
|
||||
* escaped or quoted.
|
||||
*
|
||||
* @param ExpressionInterface $expression the expression to be built.
|
||||
* @param array $params the binding parameters.
|
||||
* @return string the raw SQL that will not be additionally escaped or quoted.
|
||||
*/
|
||||
public function build(ExpressionInterface $expression, array &$params = []);
|
||||
}
|
||||
33
vendor/yiisoft/yii2/db/ExpressionBuilderTrait.php
vendored
Normal file
33
vendor/yiisoft/yii2/db/ExpressionBuilderTrait.php
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db;
|
||||
|
||||
/**
|
||||
* Trait ExpressionBuilderTrait provides common constructor for classes that
|
||||
* should implement [[ExpressionBuilderInterface]]
|
||||
*
|
||||
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
|
||||
* @since 2.0.14
|
||||
*/
|
||||
trait ExpressionBuilderTrait
|
||||
{
|
||||
/**
|
||||
* @var QueryBuilder
|
||||
*/
|
||||
protected $queryBuilder;
|
||||
|
||||
/**
|
||||
* ExpressionBuilderTrait constructor.
|
||||
*
|
||||
* @param QueryBuilder $queryBuilder
|
||||
*/
|
||||
public function __construct(QueryBuilder $queryBuilder)
|
||||
{
|
||||
$this->queryBuilder = $queryBuilder;
|
||||
}
|
||||
}
|
||||
24
vendor/yiisoft/yii2/db/ExpressionInterface.php
vendored
Normal file
24
vendor/yiisoft/yii2/db/ExpressionInterface.php
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db;
|
||||
|
||||
/**
|
||||
* Interface ExpressionInterface should be used to mark classes, that should be built
|
||||
* in a special way.
|
||||
*
|
||||
* The database abstraction layer of Yii framework supports objects that implement this
|
||||
* interface and will use [[ExpressionBuilderInterface]] to build them.
|
||||
*
|
||||
* The default implementation is a class [[Expression]].
|
||||
*
|
||||
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
|
||||
* @since 2.0.14
|
||||
*/
|
||||
interface ExpressionInterface
|
||||
{
|
||||
}
|
||||
38
vendor/yiisoft/yii2/db/ForeignKeyConstraint.php
vendored
Normal file
38
vendor/yiisoft/yii2/db/ForeignKeyConstraint.php
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db;
|
||||
|
||||
/**
|
||||
* ForeignKeyConstraint represents the metadata of a table `FOREIGN KEY` constraint.
|
||||
*
|
||||
* @author Sergey Makinen <sergey@makinen.ru>
|
||||
* @since 2.0.13
|
||||
*/
|
||||
class ForeignKeyConstraint extends Constraint
|
||||
{
|
||||
/**
|
||||
* @var string|null referenced table schema name.
|
||||
*/
|
||||
public $foreignSchemaName;
|
||||
/**
|
||||
* @var string referenced table name.
|
||||
*/
|
||||
public $foreignTableName;
|
||||
/**
|
||||
* @var string[] list of referenced table column names.
|
||||
*/
|
||||
public $foreignColumnNames;
|
||||
/**
|
||||
* @var string|null referential action if rows in a referenced table are to be updated.
|
||||
*/
|
||||
public $onUpdate;
|
||||
/**
|
||||
* @var string|null referential action if rows in a referenced table are to be deleted.
|
||||
*/
|
||||
public $onDelete;
|
||||
}
|
||||
26
vendor/yiisoft/yii2/db/IndexConstraint.php
vendored
Normal file
26
vendor/yiisoft/yii2/db/IndexConstraint.php
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db;
|
||||
|
||||
/**
|
||||
* IndexConstraint represents the metadata of a table `INDEX` constraint.
|
||||
*
|
||||
* @author Sergey Makinen <sergey@makinen.ru>
|
||||
* @since 2.0.13
|
||||
*/
|
||||
class IndexConstraint extends Constraint
|
||||
{
|
||||
/**
|
||||
* @var bool whether the index is unique.
|
||||
*/
|
||||
public $isUnique;
|
||||
/**
|
||||
* @var bool whether the index was created for a primary key.
|
||||
*/
|
||||
public $isPrimary;
|
||||
}
|
||||
25
vendor/yiisoft/yii2/db/IntegrityException.php
vendored
Normal file
25
vendor/yiisoft/yii2/db/IntegrityException.php
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db;
|
||||
|
||||
/**
|
||||
* Exception represents an exception that is caused by violation of DB constraints.
|
||||
*
|
||||
* @author Alexander Makarov <sam@rmcreative.ru>
|
||||
* @since 2.0
|
||||
*/
|
||||
class IntegrityException extends Exception
|
||||
{
|
||||
/**
|
||||
* @return string the user-friendly name of this exception
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'Integrity constraint violation';
|
||||
}
|
||||
}
|
||||
98
vendor/yiisoft/yii2/db/JsonExpression.php
vendored
Normal file
98
vendor/yiisoft/yii2/db/JsonExpression.php
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db;
|
||||
|
||||
use yii\base\InvalidConfigException;
|
||||
|
||||
/**
|
||||
* Class JsonExpression represents data that should be encoded to JSON.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* ```php
|
||||
* new JsonExpression(['a' => 1, 'b' => 2]); // will be encoded to '{"a": 1, "b": 2}'
|
||||
* ```
|
||||
*
|
||||
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
|
||||
* @since 2.0.14
|
||||
*/
|
||||
class JsonExpression implements ExpressionInterface, \JsonSerializable
|
||||
{
|
||||
const TYPE_JSON = 'json';
|
||||
const TYPE_JSONB = 'jsonb';
|
||||
|
||||
/**
|
||||
* @var mixed the value to be encoded to JSON.
|
||||
* The value must be compatible with [\yii\helpers\Json::encode()|Json::encode()]] input requirements.
|
||||
*/
|
||||
protected $value;
|
||||
/**
|
||||
* @var string|null Type of JSON, expression should be casted to. Defaults to `null`, meaning
|
||||
* no explicit casting will be performed.
|
||||
* This property will be encountered only for DBMSs that support different types of JSON.
|
||||
* For example, PostgreSQL has `json` and `jsonb` types.
|
||||
*/
|
||||
protected $type;
|
||||
|
||||
|
||||
/**
|
||||
* JsonExpression constructor.
|
||||
*
|
||||
* @param mixed $value the value to be encoded to JSON.
|
||||
* The value must be compatible with [\yii\helpers\Json::encode()|Json::encode()]] requirements.
|
||||
* @param string|null $type the type of the JSON. See [[JsonExpression::type]]
|
||||
*
|
||||
* @see type
|
||||
*/
|
||||
public function __construct($value, $type = null)
|
||||
{
|
||||
if ($value instanceof self) {
|
||||
$value = $value->getValue();
|
||||
}
|
||||
|
||||
$this->value = $value;
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
* @see value
|
||||
*/
|
||||
public function getValue()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null|string the type of JSON
|
||||
* @see type
|
||||
*/
|
||||
public function getType()
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify data which should be serialized to JSON
|
||||
*
|
||||
* @link http://php.net/manual/en/jsonserializable.jsonserialize.php
|
||||
* @return mixed data which can be serialized by <b>json_encode</b>,
|
||||
* which is a value of any type other than a resource.
|
||||
* @since 2.0.14.2
|
||||
* @throws InvalidConfigException when JsonExpression contains QueryInterface object
|
||||
*/
|
||||
public function jsonSerialize()
|
||||
{
|
||||
$value = $this->getValue();
|
||||
if ($value instanceof QueryInterface) {
|
||||
throw new InvalidConfigException('The JsonExpression class can not be serialized to JSON when the value is a QueryInterface object');
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
594
vendor/yiisoft/yii2/db/Migration.php
vendored
Normal file
594
vendor/yiisoft/yii2/db/Migration.php
vendored
Normal file
@@ -0,0 +1,594 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db;
|
||||
|
||||
use yii\base\Component;
|
||||
use yii\di\Instance;
|
||||
use yii\helpers\StringHelper;
|
||||
|
||||
/**
|
||||
* Migration is the base class for representing a database migration.
|
||||
*
|
||||
* Migration is designed to be used together with the "yii migrate" command.
|
||||
*
|
||||
* Each child class of Migration represents an individual database migration which
|
||||
* is identified by the child class name.
|
||||
*
|
||||
* Within each migration, the [[up()]] method should be overridden to contain the logic
|
||||
* for "upgrading" the database; while the [[down()]] method for the "downgrading"
|
||||
* logic. The "yii migrate" command manages all available migrations in an application.
|
||||
*
|
||||
* If the database supports transactions, you may also override [[safeUp()]] and
|
||||
* [[safeDown()]] so that if anything wrong happens during the upgrading or downgrading,
|
||||
* the whole migration can be reverted in a whole.
|
||||
*
|
||||
* Note that some DB queries in some DBMS cannot be put into a transaction. For some examples,
|
||||
* please refer to [implicit commit](http://dev.mysql.com/doc/refman/5.7/en/implicit-commit.html). If this is the case,
|
||||
* you should still implement `up()` and `down()`, instead.
|
||||
*
|
||||
* Migration provides a set of convenient methods for manipulating database data and schema.
|
||||
* For example, the [[insert()]] method can be used to easily insert a row of data into
|
||||
* a database table; the [[createTable()]] method can be used to create a database table.
|
||||
* Compared with the same methods in [[Command]], these methods will display extra
|
||||
* information showing the method parameters and execution time, which may be useful when
|
||||
* applying migrations.
|
||||
*
|
||||
* For more details and usage information on Migration, see the [guide article on Migration](guide:db-migrations).
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class Migration extends Component implements MigrationInterface
|
||||
{
|
||||
use SchemaBuilderTrait;
|
||||
|
||||
/**
|
||||
* @var Connection|array|string the DB connection object or the application component ID of the DB connection
|
||||
* that this migration should work with. Starting from version 2.0.2, this can also be a configuration array
|
||||
* for creating the object.
|
||||
*
|
||||
* Note that when a Migration object is created by the `migrate` command, this property will be overwritten
|
||||
* by the command. If you do not want to use the DB connection provided by the command, you may override
|
||||
* the [[init()]] method like the following:
|
||||
*
|
||||
* ```php
|
||||
* public function init()
|
||||
* {
|
||||
* $this->db = 'db2';
|
||||
* parent::init();
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
public $db = 'db';
|
||||
/**
|
||||
* @var int max number of characters of the SQL outputted. Useful for reduction of long statements and making
|
||||
* console output more compact.
|
||||
* @since 2.0.13
|
||||
*/
|
||||
public $maxSqlOutputLength;
|
||||
/**
|
||||
* @var bool indicates whether the console output should be compacted.
|
||||
* If this is set to true, the individual commands ran within the migration will not be output to the console.
|
||||
* Default is false, in other words the output is fully verbose by default.
|
||||
* @since 2.0.13
|
||||
*/
|
||||
public $compact = false;
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the migration.
|
||||
* This method will set [[db]] to be the 'db' application component, if it is `null`.
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
parent::init();
|
||||
$this->db = Instance::ensure($this->db, Connection::className());
|
||||
$this->db->getSchema()->refresh();
|
||||
$this->db->enableSlaves = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @since 2.0.6
|
||||
*/
|
||||
protected function getDb()
|
||||
{
|
||||
return $this->db;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method contains the logic to be executed when applying this migration.
|
||||
* Child classes may override this method to provide actual migration logic.
|
||||
* @return bool return a false value to indicate the migration fails
|
||||
* and should not proceed further. All other return values mean the migration succeeds.
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
$transaction = $this->db->beginTransaction();
|
||||
try {
|
||||
if ($this->safeUp() === false) {
|
||||
$transaction->rollBack();
|
||||
return false;
|
||||
}
|
||||
$transaction->commit();
|
||||
} catch (\Exception $e) {
|
||||
$this->printException($e);
|
||||
$transaction->rollBack();
|
||||
return false;
|
||||
} catch (\Throwable $e) {
|
||||
$this->printException($e);
|
||||
$transaction->rollBack();
|
||||
return false;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method contains the logic to be executed when removing this migration.
|
||||
* The default implementation throws an exception indicating the migration cannot be removed.
|
||||
* Child classes may override this method if the corresponding migrations can be removed.
|
||||
* @return bool return a false value to indicate the migration fails
|
||||
* and should not proceed further. All other return values mean the migration succeeds.
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
$transaction = $this->db->beginTransaction();
|
||||
try {
|
||||
if ($this->safeDown() === false) {
|
||||
$transaction->rollBack();
|
||||
return false;
|
||||
}
|
||||
$transaction->commit();
|
||||
} catch (\Exception $e) {
|
||||
$this->printException($e);
|
||||
$transaction->rollBack();
|
||||
return false;
|
||||
} catch (\Throwable $e) {
|
||||
$this->printException($e);
|
||||
$transaction->rollBack();
|
||||
return false;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Throwable|\Exception $e
|
||||
*/
|
||||
private function printException($e)
|
||||
{
|
||||
echo 'Exception: ' . $e->getMessage() . ' (' . $e->getFile() . ':' . $e->getLine() . ")\n";
|
||||
echo $e->getTraceAsString() . "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* This method contains the logic to be executed when applying this migration.
|
||||
* This method differs from [[up()]] in that the DB logic implemented here will
|
||||
* be enclosed within a DB transaction.
|
||||
* Child classes may implement this method instead of [[up()]] if the DB logic
|
||||
* needs to be within a transaction.
|
||||
*
|
||||
* Note: Not all DBMS support transactions. And some DB queries cannot be put into a transaction. For some examples,
|
||||
* please refer to [implicit commit](http://dev.mysql.com/doc/refman/5.7/en/implicit-commit.html).
|
||||
*
|
||||
* @return bool return a false value to indicate the migration fails
|
||||
* and should not proceed further. All other return values mean the migration succeeds.
|
||||
*/
|
||||
public function safeUp()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* This method contains the logic to be executed when removing this migration.
|
||||
* This method differs from [[down()]] in that the DB logic implemented here will
|
||||
* be enclosed within a DB transaction.
|
||||
* Child classes may implement this method instead of [[down()]] if the DB logic
|
||||
* needs to be within a transaction.
|
||||
*
|
||||
* Note: Not all DBMS support transactions. And some DB queries cannot be put into a transaction. For some examples,
|
||||
* please refer to [implicit commit](http://dev.mysql.com/doc/refman/5.7/en/implicit-commit.html).
|
||||
*
|
||||
* @return bool return a false value to indicate the migration fails
|
||||
* and should not proceed further. All other return values mean the migration succeeds.
|
||||
*/
|
||||
public function safeDown()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a SQL statement.
|
||||
* This method executes the specified SQL statement using [[db]].
|
||||
* @param string $sql the SQL statement to be executed
|
||||
* @param array $params input parameters (name => value) for the SQL execution.
|
||||
* See [[Command::execute()]] for more details.
|
||||
*/
|
||||
public function execute($sql, $params = [])
|
||||
{
|
||||
$sqlOutput = $sql;
|
||||
if ($this->maxSqlOutputLength !== null) {
|
||||
$sqlOutput = StringHelper::truncate($sql, $this->maxSqlOutputLength, '[... hidden]');
|
||||
}
|
||||
|
||||
$time = $this->beginCommand("execute SQL: $sqlOutput");
|
||||
$this->db->createCommand($sql)->bindValues($params)->execute();
|
||||
$this->endCommand($time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and executes an INSERT SQL statement.
|
||||
* The method will properly escape the column names, and bind the values to be inserted.
|
||||
* @param string $table the table that new rows will be inserted into.
|
||||
* @param array $columns the column data (name => value) to be inserted into the table.
|
||||
*/
|
||||
public function insert($table, $columns)
|
||||
{
|
||||
$time = $this->beginCommand("insert into $table");
|
||||
$this->db->createCommand()->insert($table, $columns)->execute();
|
||||
$this->endCommand($time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and executes a batch INSERT SQL statement.
|
||||
* The method will properly escape the column names, and bind the values to be inserted.
|
||||
* @param string $table the table that new rows will be inserted into.
|
||||
* @param array $columns the column names.
|
||||
* @param array $rows the rows to be batch inserted into the table
|
||||
*/
|
||||
public function batchInsert($table, $columns, $rows)
|
||||
{
|
||||
$time = $this->beginCommand("insert into $table");
|
||||
$this->db->createCommand()->batchInsert($table, $columns, $rows)->execute();
|
||||
$this->endCommand($time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and executes a command to insert rows into a database table if
|
||||
* they do not already exist (matching unique constraints),
|
||||
* or update them if they do.
|
||||
*
|
||||
* The method will properly escape the column names, and bind the values to be inserted.
|
||||
*
|
||||
* @param string $table the table that new rows will be inserted into/updated in.
|
||||
* @param array|Query $insertColumns the column data (name => value) to be inserted into the table or instance
|
||||
* of [[Query]] to perform `INSERT INTO ... SELECT` SQL statement.
|
||||
* @param array|bool $updateColumns the column data (name => value) to be updated if they already exist.
|
||||
* If `true` is passed, the column data will be updated to match the insert column data.
|
||||
* If `false` is passed, no update will be performed if the column data already exists.
|
||||
* @param array $params the parameters to be bound to the command.
|
||||
* @return $this the command object itself.
|
||||
* @since 2.0.14
|
||||
*/
|
||||
public function upsert($table, $insertColumns, $updateColumns = true, $params = [])
|
||||
{
|
||||
$time = $this->beginCommand("upsert into $table");
|
||||
$this->db->createCommand()->upsert($table, $insertColumns, $updateColumns, $params)->execute();
|
||||
$this->endCommand($time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and executes an UPDATE SQL statement.
|
||||
* The method will properly escape the column names and bind the values to be updated.
|
||||
* @param string $table the table to be updated.
|
||||
* @param array $columns the column data (name => value) to be updated.
|
||||
* @param array|string $condition the conditions that will be put in the WHERE part. Please
|
||||
* refer to [[Query::where()]] on how to specify conditions.
|
||||
* @param array $params the parameters to be bound to the query.
|
||||
*/
|
||||
public function update($table, $columns, $condition = '', $params = [])
|
||||
{
|
||||
$time = $this->beginCommand("update $table");
|
||||
$this->db->createCommand()->update($table, $columns, $condition, $params)->execute();
|
||||
$this->endCommand($time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and executes a DELETE SQL statement.
|
||||
* @param string $table the table where the data will be deleted from.
|
||||
* @param array|string $condition the conditions that will be put in the WHERE part. Please
|
||||
* refer to [[Query::where()]] on how to specify conditions.
|
||||
* @param array $params the parameters to be bound to the query.
|
||||
*/
|
||||
public function delete($table, $condition = '', $params = [])
|
||||
{
|
||||
$time = $this->beginCommand("delete from $table");
|
||||
$this->db->createCommand()->delete($table, $condition, $params)->execute();
|
||||
$this->endCommand($time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and executes a SQL statement for creating a new DB table.
|
||||
*
|
||||
* The columns in the new table should be specified as name-definition pairs (e.g. 'name' => 'string'),
|
||||
* where name stands for a column name which will be properly quoted by the method, and definition
|
||||
* stands for the column type which can contain an abstract DB type.
|
||||
*
|
||||
* The [[QueryBuilder::getColumnType()]] method will be invoked to convert any abstract type into a physical one.
|
||||
*
|
||||
* If a column is specified with definition only (e.g. 'PRIMARY KEY (name, type)'), it will be directly
|
||||
* put into the generated SQL.
|
||||
*
|
||||
* @param string $table the name of the table to be created. The name will be properly quoted by the method.
|
||||
* @param array $columns the columns (name => definition) in the new table.
|
||||
* @param string $options additional SQL fragment that will be appended to the generated SQL.
|
||||
*/
|
||||
public function createTable($table, $columns, $options = null)
|
||||
{
|
||||
$time = $this->beginCommand("create table $table");
|
||||
$this->db->createCommand()->createTable($table, $columns, $options)->execute();
|
||||
foreach ($columns as $column => $type) {
|
||||
if ($type instanceof ColumnSchemaBuilder && $type->comment !== null) {
|
||||
$this->db->createCommand()->addCommentOnColumn($table, $column, $type->comment)->execute();
|
||||
}
|
||||
}
|
||||
$this->endCommand($time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and executes a SQL statement for renaming a DB table.
|
||||
* @param string $table the table to be renamed. The name will be properly quoted by the method.
|
||||
* @param string $newName the new table name. The name will be properly quoted by the method.
|
||||
*/
|
||||
public function renameTable($table, $newName)
|
||||
{
|
||||
$time = $this->beginCommand("rename table $table to $newName");
|
||||
$this->db->createCommand()->renameTable($table, $newName)->execute();
|
||||
$this->endCommand($time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and executes a SQL statement for dropping a DB table.
|
||||
* @param string $table the table to be dropped. The name will be properly quoted by the method.
|
||||
*/
|
||||
public function dropTable($table)
|
||||
{
|
||||
$time = $this->beginCommand("drop table $table");
|
||||
$this->db->createCommand()->dropTable($table)->execute();
|
||||
$this->endCommand($time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and executes a SQL statement for truncating a DB table.
|
||||
* @param string $table the table to be truncated. The name will be properly quoted by the method.
|
||||
*/
|
||||
public function truncateTable($table)
|
||||
{
|
||||
$time = $this->beginCommand("truncate table $table");
|
||||
$this->db->createCommand()->truncateTable($table)->execute();
|
||||
$this->endCommand($time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and executes a SQL statement for adding a new DB column.
|
||||
* @param string $table the table that the new column will be added to. The table name will be properly quoted by the method.
|
||||
* @param string $column the name of the new column. The name will be properly quoted by the method.
|
||||
* @param string $type the column type. The [[QueryBuilder::getColumnType()]] method will be invoked to convert abstract column type (if any)
|
||||
* into the physical one. Anything that is not recognized as abstract type will be kept in the generated SQL.
|
||||
* For example, 'string' will be turned into 'varchar(255)', while 'string not null' will become 'varchar(255) not null'.
|
||||
*/
|
||||
public function addColumn($table, $column, $type)
|
||||
{
|
||||
$time = $this->beginCommand("add column $column $type to table $table");
|
||||
$this->db->createCommand()->addColumn($table, $column, $type)->execute();
|
||||
if ($type instanceof ColumnSchemaBuilder && $type->comment !== null) {
|
||||
$this->db->createCommand()->addCommentOnColumn($table, $column, $type->comment)->execute();
|
||||
}
|
||||
$this->endCommand($time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and executes a SQL statement for dropping a DB column.
|
||||
* @param string $table the table whose column is to be dropped. The name will be properly quoted by the method.
|
||||
* @param string $column the name of the column to be dropped. The name will be properly quoted by the method.
|
||||
*/
|
||||
public function dropColumn($table, $column)
|
||||
{
|
||||
$time = $this->beginCommand("drop column $column from table $table");
|
||||
$this->db->createCommand()->dropColumn($table, $column)->execute();
|
||||
$this->endCommand($time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and executes a SQL statement for renaming a column.
|
||||
* @param string $table the table whose column is to be renamed. The name will be properly quoted by the method.
|
||||
* @param string $name the old name of the column. The name will be properly quoted by the method.
|
||||
* @param string $newName the new name of the column. The name will be properly quoted by the method.
|
||||
*/
|
||||
public function renameColumn($table, $name, $newName)
|
||||
{
|
||||
$time = $this->beginCommand("rename column $name in table $table to $newName");
|
||||
$this->db->createCommand()->renameColumn($table, $name, $newName)->execute();
|
||||
$this->endCommand($time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and executes a SQL statement for changing the definition of a column.
|
||||
* @param string $table the table whose column is to be changed. The table name will be properly quoted by the method.
|
||||
* @param string $column the name of the column to be changed. The name will be properly quoted by the method.
|
||||
* @param string $type the new column type. The [[QueryBuilder::getColumnType()]] method will be invoked to convert abstract column type (if any)
|
||||
* into the physical one. Anything that is not recognized as abstract type will be kept in the generated SQL.
|
||||
* For example, 'string' will be turned into 'varchar(255)', while 'string not null' will become 'varchar(255) not null'.
|
||||
*/
|
||||
public function alterColumn($table, $column, $type)
|
||||
{
|
||||
$time = $this->beginCommand("alter column $column in table $table to $type");
|
||||
$this->db->createCommand()->alterColumn($table, $column, $type)->execute();
|
||||
if ($type instanceof ColumnSchemaBuilder && $type->comment !== null) {
|
||||
$this->db->createCommand()->addCommentOnColumn($table, $column, $type->comment)->execute();
|
||||
}
|
||||
$this->endCommand($time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and executes a SQL statement for creating a primary key.
|
||||
* The method will properly quote the table and column names.
|
||||
* @param string $name the name of the primary key constraint.
|
||||
* @param string $table the table that the primary key constraint will be added to.
|
||||
* @param string|array $columns comma separated string or array of columns that the primary key will consist of.
|
||||
*/
|
||||
public function addPrimaryKey($name, $table, $columns)
|
||||
{
|
||||
$time = $this->beginCommand("add primary key $name on $table (" . (is_array($columns) ? implode(',', $columns) : $columns) . ')');
|
||||
$this->db->createCommand()->addPrimaryKey($name, $table, $columns)->execute();
|
||||
$this->endCommand($time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and executes a SQL statement for dropping a primary key.
|
||||
* @param string $name the name of the primary key constraint to be removed.
|
||||
* @param string $table the table that the primary key constraint will be removed from.
|
||||
*/
|
||||
public function dropPrimaryKey($name, $table)
|
||||
{
|
||||
$time = $this->beginCommand("drop primary key $name");
|
||||
$this->db->createCommand()->dropPrimaryKey($name, $table)->execute();
|
||||
$this->endCommand($time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a SQL statement for adding a foreign key constraint to an existing table.
|
||||
* The method will properly quote the table and column names.
|
||||
* @param string $name the name of the foreign key constraint.
|
||||
* @param string $table the table that the foreign key constraint will be added to.
|
||||
* @param string|array $columns the name of the column to that the constraint will be added on. If there are multiple columns, separate them with commas or use an array.
|
||||
* @param string $refTable the table that the foreign key references to.
|
||||
* @param string|array $refColumns the name of the column that the foreign key references to. If there are multiple columns, separate them with commas or use an array.
|
||||
* @param string $delete the ON DELETE option. Most DBMS support these options: RESTRICT, CASCADE, NO ACTION, SET DEFAULT, SET NULL
|
||||
* @param string $update the ON UPDATE option. Most DBMS support these options: RESTRICT, CASCADE, NO ACTION, SET DEFAULT, SET NULL
|
||||
*/
|
||||
public function addForeignKey($name, $table, $columns, $refTable, $refColumns, $delete = null, $update = null)
|
||||
{
|
||||
$time = $this->beginCommand("add foreign key $name: $table (" . implode(',', (array) $columns) . ") references $refTable (" . implode(',', (array) $refColumns) . ')');
|
||||
$this->db->createCommand()->addForeignKey($name, $table, $columns, $refTable, $refColumns, $delete, $update)->execute();
|
||||
$this->endCommand($time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a SQL statement for dropping a foreign key constraint.
|
||||
* @param string $name the name of the foreign key constraint to be dropped. The name will be properly quoted by the method.
|
||||
* @param string $table the table whose foreign is to be dropped. The name will be properly quoted by the method.
|
||||
*/
|
||||
public function dropForeignKey($name, $table)
|
||||
{
|
||||
$time = $this->beginCommand("drop foreign key $name from table $table");
|
||||
$this->db->createCommand()->dropForeignKey($name, $table)->execute();
|
||||
$this->endCommand($time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and executes a SQL statement for creating a new index.
|
||||
* @param string $name the name of the index. The name will be properly quoted by the method.
|
||||
* @param string $table the table that the new index will be created for. The table name will be properly quoted by the method.
|
||||
* @param string|array $columns the column(s) that should be included in the index. If there are multiple columns, please separate them
|
||||
* by commas or use an array. Each column name will be properly quoted by the method. Quoting will be skipped for column names that
|
||||
* include a left parenthesis "(".
|
||||
* @param bool $unique whether to add UNIQUE constraint on the created index.
|
||||
*/
|
||||
public function createIndex($name, $table, $columns, $unique = false)
|
||||
{
|
||||
$time = $this->beginCommand('create' . ($unique ? ' unique' : '') . " index $name on $table (" . implode(',', (array) $columns) . ')');
|
||||
$this->db->createCommand()->createIndex($name, $table, $columns, $unique)->execute();
|
||||
$this->endCommand($time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and executes a SQL statement for dropping an index.
|
||||
* @param string $name the name of the index to be dropped. The name will be properly quoted by the method.
|
||||
* @param string $table the table whose index is to be dropped. The name will be properly quoted by the method.
|
||||
*/
|
||||
public function dropIndex($name, $table)
|
||||
{
|
||||
$time = $this->beginCommand("drop index $name on $table");
|
||||
$this->db->createCommand()->dropIndex($name, $table)->execute();
|
||||
$this->endCommand($time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and execute a SQL statement for adding comment to column.
|
||||
*
|
||||
* @param string $table the table whose column is to be commented. The table name will be properly quoted by the method.
|
||||
* @param string $column the name of the column to be commented. The column name will be properly quoted by the method.
|
||||
* @param string $comment the text of the comment to be added. The comment will be properly quoted by the method.
|
||||
* @since 2.0.8
|
||||
*/
|
||||
public function addCommentOnColumn($table, $column, $comment)
|
||||
{
|
||||
$time = $this->beginCommand("add comment on column $column");
|
||||
$this->db->createCommand()->addCommentOnColumn($table, $column, $comment)->execute();
|
||||
$this->endCommand($time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a SQL statement for adding comment to table.
|
||||
*
|
||||
* @param string $table the table to be commented. The table name will be properly quoted by the method.
|
||||
* @param string $comment the text of the comment to be added. The comment will be properly quoted by the method.
|
||||
* @since 2.0.8
|
||||
*/
|
||||
public function addCommentOnTable($table, $comment)
|
||||
{
|
||||
$time = $this->beginCommand("add comment on table $table");
|
||||
$this->db->createCommand()->addCommentOnTable($table, $comment)->execute();
|
||||
$this->endCommand($time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and execute a SQL statement for dropping comment from column.
|
||||
*
|
||||
* @param string $table the table whose column is to be commented. The table name will be properly quoted by the method.
|
||||
* @param string $column the name of the column to be commented. The column name will be properly quoted by the method.
|
||||
* @since 2.0.8
|
||||
*/
|
||||
public function dropCommentFromColumn($table, $column)
|
||||
{
|
||||
$time = $this->beginCommand("drop comment from column $column");
|
||||
$this->db->createCommand()->dropCommentFromColumn($table, $column)->execute();
|
||||
$this->endCommand($time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a SQL statement for dropping comment from table.
|
||||
*
|
||||
* @param string $table the table whose column is to be commented. The table name will be properly quoted by the method.
|
||||
* @since 2.0.8
|
||||
*/
|
||||
public function dropCommentFromTable($table)
|
||||
{
|
||||
$time = $this->beginCommand("drop comment from table $table");
|
||||
$this->db->createCommand()->dropCommentFromTable($table)->execute();
|
||||
$this->endCommand($time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares for a command to be executed, and outputs to the console.
|
||||
*
|
||||
* @param string $description the description for the command, to be output to the console.
|
||||
* @return float the time before the command is executed, for the time elapsed to be calculated.
|
||||
* @since 2.0.13
|
||||
*/
|
||||
protected function beginCommand($description)
|
||||
{
|
||||
if (!$this->compact) {
|
||||
echo " > $description ...";
|
||||
}
|
||||
return microtime(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finalizes after the command has been executed, and outputs to the console the time elapsed.
|
||||
*
|
||||
* @param float $time the time before the command was executed.
|
||||
* @since 2.0.13
|
||||
*/
|
||||
protected function endCommand($time)
|
||||
{
|
||||
if (!$this->compact) {
|
||||
echo ' done (time: ' . sprintf('%.3f', microtime(true) - $time) . "s)\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
35
vendor/yiisoft/yii2/db/MigrationInterface.php
vendored
Normal file
35
vendor/yiisoft/yii2/db/MigrationInterface.php
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db;
|
||||
|
||||
/**
|
||||
* The MigrationInterface defines the minimum set of methods to be implemented by a database migration.
|
||||
*
|
||||
* Each migration class should provide the [[up()]] method containing the logic for "upgrading" the database
|
||||
* and the [[down()]] method for the "downgrading" logic.
|
||||
*
|
||||
* @author Klimov Paul <klimov@zfort.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
interface MigrationInterface
|
||||
{
|
||||
/**
|
||||
* This method contains the logic to be executed when applying this migration.
|
||||
* @return bool return a false value to indicate the migration fails
|
||||
* and should not proceed further. All other return values mean the migration succeeds.
|
||||
*/
|
||||
public function up();
|
||||
|
||||
/**
|
||||
* This method contains the logic to be executed when removing this migration.
|
||||
* The default implementation throws an exception indicating the migration cannot be removed.
|
||||
* @return bool return a false value to indicate the migration fails
|
||||
* and should not proceed further. All other return values mean the migration succeeds.
|
||||
*/
|
||||
public function down();
|
||||
}
|
||||
65
vendor/yiisoft/yii2/db/PdoValue.php
vendored
Normal file
65
vendor/yiisoft/yii2/db/PdoValue.php
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db;
|
||||
|
||||
/**
|
||||
* Class PdoValue represents a $value that should be bound to PDO with exact $type.
|
||||
*
|
||||
* For example, it will be useful when you need to bind binary data to BLOB column in DBMS:
|
||||
*
|
||||
* ```php
|
||||
* [':name' => 'John', ':profile' => new PdoValue($profile, \PDO::PARAM_LOB)]`.
|
||||
* ```
|
||||
*
|
||||
* To see possible types, check [PDO::PARAM_* constants](http://php.net/manual/en/pdo.constants.php).
|
||||
*
|
||||
* @see http://php.net/manual/en/pdostatement.bindparam.php
|
||||
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
|
||||
* @since 2.0.14
|
||||
*/
|
||||
final class PdoValue implements ExpressionInterface
|
||||
{
|
||||
/**
|
||||
* @var mixed
|
||||
*/
|
||||
private $value;
|
||||
/**
|
||||
* @var int One of PDO_PARAM_* constants
|
||||
* @see http://php.net/manual/en/pdo.constants.php
|
||||
*/
|
||||
private $type;
|
||||
|
||||
|
||||
/**
|
||||
* PdoValue constructor.
|
||||
*
|
||||
* @param $value
|
||||
* @param $type
|
||||
*/
|
||||
public function __construct($value, $type)
|
||||
{
|
||||
$this->value = $value;
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getValue()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getType()
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
}
|
||||
31
vendor/yiisoft/yii2/db/PdoValueBuilder.php
vendored
Normal file
31
vendor/yiisoft/yii2/db/PdoValueBuilder.php
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db;
|
||||
|
||||
/**
|
||||
* Class PdoValueBuilder builds object of the [[PdoValue]] expression class.
|
||||
*
|
||||
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
|
||||
* @since 2.0.14
|
||||
*/
|
||||
class PdoValueBuilder implements ExpressionBuilderInterface
|
||||
{
|
||||
const PARAM_PREFIX = ':pv';
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function build(ExpressionInterface $expression, array &$params = [])
|
||||
{
|
||||
$placeholder = static::PARAM_PREFIX . count($params);
|
||||
$params[$placeholder] = $expression;
|
||||
|
||||
return $placeholder;
|
||||
}
|
||||
}
|
||||
1311
vendor/yiisoft/yii2/db/Query.php
vendored
Normal file
1311
vendor/yiisoft/yii2/db/Query.php
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1720
vendor/yiisoft/yii2/db/QueryBuilder.php
vendored
Normal file
1720
vendor/yiisoft/yii2/db/QueryBuilder.php
vendored
Normal file
File diff suppressed because it is too large
Load Diff
36
vendor/yiisoft/yii2/db/QueryExpressionBuilder.php
vendored
Normal file
36
vendor/yiisoft/yii2/db/QueryExpressionBuilder.php
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db;
|
||||
|
||||
/**
|
||||
* Class QueryExpressionBuilder is used internally to build [[Query]] object
|
||||
* using unified [[QueryBuilder]] expression building interface.
|
||||
*
|
||||
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
|
||||
* @since 2.0.14
|
||||
*/
|
||||
class QueryExpressionBuilder implements ExpressionBuilderInterface
|
||||
{
|
||||
use ExpressionBuilderTrait;
|
||||
|
||||
|
||||
/**
|
||||
* Method builds the raw SQL from the $expression that will not be additionally
|
||||
* escaped or quoted.
|
||||
*
|
||||
* @param ExpressionInterface|Query $expression the expression to be built.
|
||||
* @param array $params the binding parameters.
|
||||
* @return string the raw SQL that will not be additionally escaped or quoted.
|
||||
*/
|
||||
public function build(ExpressionInterface $expression, array &$params = [])
|
||||
{
|
||||
list($sql, $params) = $this->queryBuilder->build($expression, $params);
|
||||
|
||||
return "($sql)";
|
||||
}
|
||||
}
|
||||
269
vendor/yiisoft/yii2/db/QueryInterface.php
vendored
Normal file
269
vendor/yiisoft/yii2/db/QueryInterface.php
vendored
Normal file
@@ -0,0 +1,269 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db;
|
||||
|
||||
/**
|
||||
* The QueryInterface defines the minimum set of methods to be implemented by a database query.
|
||||
*
|
||||
* The default implementation of this interface is provided by [[QueryTrait]].
|
||||
*
|
||||
* It has support for getting [[one]] instance or [[all]].
|
||||
* Allows pagination via [[limit]] and [[offset]].
|
||||
* Sorting is supported via [[orderBy]] and items can be limited to match some conditions using [[where]].
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @author Carsten Brandt <mail@cebe.cc>
|
||||
* @since 2.0
|
||||
*/
|
||||
interface QueryInterface
|
||||
{
|
||||
/**
|
||||
* Executes the query and returns all results as an array.
|
||||
* @param Connection $db the database connection used to execute the query.
|
||||
* If this parameter is not given, the `db` application component will be used.
|
||||
* @return array the query results. If the query results in nothing, an empty array will be returned.
|
||||
*/
|
||||
public function all($db = null);
|
||||
|
||||
/**
|
||||
* Executes the query and returns a single row of result.
|
||||
* @param Connection $db the database connection used to execute the query.
|
||||
* If this parameter is not given, the `db` application component will be used.
|
||||
* @return array|bool the first row (in terms of an array) of the query result. False is returned if the query
|
||||
* results in nothing.
|
||||
*/
|
||||
public function one($db = null);
|
||||
|
||||
/**
|
||||
* Returns the number of records.
|
||||
* @param string $q the COUNT expression. Defaults to '*'.
|
||||
* @param Connection $db the database connection used to execute the query.
|
||||
* If this parameter is not given, the `db` application component will be used.
|
||||
* @return int number of records.
|
||||
*/
|
||||
public function count($q = '*', $db = null);
|
||||
|
||||
/**
|
||||
* Returns a value indicating whether the query result contains any row of data.
|
||||
* @param Connection $db the database connection used to execute the query.
|
||||
* If this parameter is not given, the `db` application component will be used.
|
||||
* @return bool whether the query result contains any row of data.
|
||||
*/
|
||||
public function exists($db = null);
|
||||
|
||||
/**
|
||||
* Sets the [[indexBy]] property.
|
||||
* @param string|callable $column the name of the column by which the query results should be indexed by.
|
||||
* This can also be a callable (e.g. anonymous function) that returns the index value based on the given
|
||||
* row data. The signature of the callable should be:
|
||||
*
|
||||
* ```php
|
||||
* function ($row)
|
||||
* {
|
||||
* // return the index value corresponding to $row
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @return $this the query object itself
|
||||
*/
|
||||
public function indexBy($column);
|
||||
|
||||
/**
|
||||
* Sets the WHERE part of the query.
|
||||
*
|
||||
* The `$condition` specified as an array can be in one of the following two formats:
|
||||
*
|
||||
* - hash format: `['column1' => value1, 'column2' => value2, ...]`
|
||||
* - operator format: `[operator, operand1, operand2, ...]`
|
||||
*
|
||||
* A condition in hash format represents the following SQL expression in general:
|
||||
* `column1=value1 AND column2=value2 AND ...`. In case when a value is an array,
|
||||
* an `IN` expression will be generated. And if a value is `null`, `IS NULL` will be used
|
||||
* in the generated expression. Below are some examples:
|
||||
*
|
||||
* - `['type' => 1, 'status' => 2]` generates `(type = 1) AND (status = 2)`.
|
||||
* - `['id' => [1, 2, 3], 'status' => 2]` generates `(id IN (1, 2, 3)) AND (status = 2)`.
|
||||
* - `['status' => null]` generates `status IS NULL`.
|
||||
*
|
||||
* A condition in operator format generates the SQL expression according to the specified operator, which
|
||||
* can be one of the following:
|
||||
*
|
||||
* - **and**: the operands should be concatenated together using `AND`. For example,
|
||||
* `['and', 'id=1', 'id=2']` will generate `id=1 AND id=2`. If an operand is an array,
|
||||
* it will be converted into a string using the rules described here. For example,
|
||||
* `['and', 'type=1', ['or', 'id=1', 'id=2']]` will generate `type=1 AND (id=1 OR id=2)`.
|
||||
* The method will *not* do any quoting or escaping.
|
||||
*
|
||||
* - **or**: similar to the `and` operator except that the operands are concatenated using `OR`. For example,
|
||||
* `['or', ['type' => [7, 8, 9]], ['id' => [1, 2, 3]]]` will generate `(type IN (7, 8, 9) OR (id IN (1, 2, 3)))`.
|
||||
*
|
||||
* - **not**: this will take only one operand and build the negation of it by prefixing the query string with `NOT`.
|
||||
* For example `['not', ['attribute' => null]]` will result in the condition `NOT (attribute IS NULL)`.
|
||||
*
|
||||
* - **between**: operand 1 should be the column name, and operand 2 and 3 should be the
|
||||
* starting and ending values of the range that the column is in.
|
||||
* For example, `['between', 'id', 1, 10]` will generate `id BETWEEN 1 AND 10`.
|
||||
*
|
||||
* - **not between**: similar to `between` except the `BETWEEN` is replaced with `NOT BETWEEN`
|
||||
* in the generated condition.
|
||||
*
|
||||
* - **in**: operand 1 should be a column or DB expression, and operand 2 be an array representing
|
||||
* the range of the values that the column or DB expression should be in. For example,
|
||||
* `['in', 'id', [1, 2, 3]]` will generate `id IN (1, 2, 3)`.
|
||||
* The method will properly quote the column name and escape values in the range.
|
||||
*
|
||||
* To create a composite `IN` condition you can use and array for the column name and value, where the values are indexed by the column name:
|
||||
* `['in', ['id', 'name'], [['id' => 1, 'name' => 'foo'], ['id' => 2, 'name' => 'bar']] ]`.
|
||||
*
|
||||
* You may also specify a sub-query that is used to get the values for the `IN`-condition:
|
||||
* `['in', 'user_id', (new Query())->select('id')->from('users')->where(['active' => 1])]`
|
||||
*
|
||||
* - **not in**: similar to the `in` operator except that `IN` is replaced with `NOT IN` in the generated condition.
|
||||
*
|
||||
* - **like**: operand 1 should be a column or DB expression, and operand 2 be a string or an array representing
|
||||
* the values that the column or DB expression should be like.
|
||||
* For example, `['like', 'name', 'tester']` will generate `name LIKE '%tester%'`.
|
||||
* When the value range is given as an array, multiple `LIKE` predicates will be generated and concatenated
|
||||
* using `AND`. For example, `['like', 'name', ['test', 'sample']]` will generate
|
||||
* `name LIKE '%test%' AND name LIKE '%sample%'`.
|
||||
* The method will properly quote the column name and escape special characters in the values.
|
||||
* Sometimes, you may want to add the percentage characters to the matching value by yourself, you may supply
|
||||
* a third operand `false` to do so. For example, `['like', 'name', '%tester', false]` will generate `name LIKE '%tester'`.
|
||||
*
|
||||
* - **or like**: similar to the `like` operator except that `OR` is used to concatenate the `LIKE`
|
||||
* predicates when operand 2 is an array.
|
||||
*
|
||||
* - **not like**: similar to the `like` operator except that `LIKE` is replaced with `NOT LIKE`
|
||||
* in the generated condition.
|
||||
*
|
||||
* - **or not like**: similar to the `not like` operator except that `OR` is used to concatenate
|
||||
* the `NOT LIKE` predicates.
|
||||
*
|
||||
* - **exists**: operand 1 is a query object that used to build an `EXISTS` condition. For example
|
||||
* `['exists', (new Query())->select('id')->from('users')->where(['active' => 1])]` will result in the following SQL expression:
|
||||
* `EXISTS (SELECT "id" FROM "users" WHERE "active"=1)`.
|
||||
*
|
||||
* - **not exists**: similar to the `exists` operator except that `EXISTS` is replaced with `NOT EXISTS` in the generated condition.
|
||||
*
|
||||
* - Additionally you can specify arbitrary operators as follows: A condition of `['>=', 'id', 10]` will result in the
|
||||
* following SQL expression: `id >= 10`.
|
||||
*
|
||||
* **Note that this method will override any existing WHERE condition. You might want to use [[andWhere()]] or [[orWhere()]] instead.**
|
||||
*
|
||||
* @param array $condition the conditions that should be put in the WHERE part.
|
||||
* @return $this the query object itself
|
||||
* @see andWhere()
|
||||
* @see orWhere()
|
||||
*/
|
||||
public function where($condition);
|
||||
|
||||
/**
|
||||
* Adds an additional WHERE condition to the existing one.
|
||||
* The new condition and the existing one will be joined using the 'AND' operator.
|
||||
* @param array $condition the new WHERE condition. Please refer to [[where()]]
|
||||
* on how to specify this parameter.
|
||||
* @return $this the query object itself
|
||||
* @see where()
|
||||
* @see orWhere()
|
||||
*/
|
||||
public function andWhere($condition);
|
||||
|
||||
/**
|
||||
* Adds an additional WHERE condition to the existing one.
|
||||
* The new condition and the existing one will be joined using the 'OR' operator.
|
||||
* @param array $condition the new WHERE condition. Please refer to [[where()]]
|
||||
* on how to specify this parameter.
|
||||
* @return $this the query object itself
|
||||
* @see where()
|
||||
* @see andWhere()
|
||||
*/
|
||||
public function orWhere($condition);
|
||||
|
||||
/**
|
||||
* Sets the WHERE part of the query ignoring empty parameters.
|
||||
*
|
||||
* @param array $condition the conditions that should be put in the WHERE part. Please refer to [[where()]]
|
||||
* on how to specify this parameter.
|
||||
* @return $this the query object itself
|
||||
* @see andFilterWhere()
|
||||
* @see orFilterWhere()
|
||||
*/
|
||||
public function filterWhere(array $condition);
|
||||
|
||||
/**
|
||||
* Adds an additional WHERE condition to the existing one ignoring empty parameters.
|
||||
* The new condition and the existing one will be joined using the 'AND' operator.
|
||||
* @param array $condition the new WHERE condition. Please refer to [[where()]]
|
||||
* on how to specify this parameter.
|
||||
* @return $this the query object itself
|
||||
* @see filterWhere()
|
||||
* @see orFilterWhere()
|
||||
*/
|
||||
public function andFilterWhere(array $condition);
|
||||
|
||||
/**
|
||||
* Adds an additional WHERE condition to the existing one ignoring empty parameters.
|
||||
* The new condition and the existing one will be joined using the 'OR' operator.
|
||||
* @param array $condition the new WHERE condition. Please refer to [[where()]]
|
||||
* on how to specify this parameter.
|
||||
* @return $this the query object itself
|
||||
* @see filterWhere()
|
||||
* @see andFilterWhere()
|
||||
*/
|
||||
public function orFilterWhere(array $condition);
|
||||
|
||||
/**
|
||||
* Sets the ORDER BY part of the query.
|
||||
* @param string|array $columns the columns (and the directions) to be ordered by.
|
||||
* Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array
|
||||
* (e.g. `['id' => SORT_ASC, 'name' => SORT_DESC]`).
|
||||
* The method will automatically quote the column names unless a column contains some parenthesis
|
||||
* (which means the column contains a DB expression).
|
||||
* @return $this the query object itself
|
||||
* @see addOrderBy()
|
||||
*/
|
||||
public function orderBy($columns);
|
||||
|
||||
/**
|
||||
* Adds additional ORDER BY columns to the query.
|
||||
* @param string|array $columns the columns (and the directions) to be ordered by.
|
||||
* Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array
|
||||
* (e.g. `['id' => SORT_ASC, 'name' => SORT_DESC]`).
|
||||
* The method will automatically quote the column names unless a column contains some parenthesis
|
||||
* (which means the column contains a DB expression).
|
||||
* @return $this the query object itself
|
||||
* @see orderBy()
|
||||
*/
|
||||
public function addOrderBy($columns);
|
||||
|
||||
/**
|
||||
* Sets the LIMIT part of the query.
|
||||
* @param int|null $limit the limit. Use null or negative value to disable limit.
|
||||
* @return $this the query object itself
|
||||
*/
|
||||
public function limit($limit);
|
||||
|
||||
/**
|
||||
* Sets the OFFSET part of the query.
|
||||
* @param int|null $offset the offset. Use null or negative value to disable offset.
|
||||
* @return $this the query object itself
|
||||
*/
|
||||
public function offset($offset);
|
||||
|
||||
/**
|
||||
* Sets whether to emulate query execution, preventing any interaction with data storage.
|
||||
* After this mode is enabled, methods, returning query results like [[one()]], [[all()]], [[exists()]]
|
||||
* and so on, will return empty or false values.
|
||||
* You should use this method in case your program logic indicates query should not return any results, like
|
||||
* in case you set false where condition like `0=1`.
|
||||
* @param bool $value whether to prevent query execution.
|
||||
* @return $this the query object itself.
|
||||
* @since 2.0.11
|
||||
*/
|
||||
public function emulateExecution($value = true);
|
||||
}
|
||||
422
vendor/yiisoft/yii2/db/QueryTrait.php
vendored
Normal file
422
vendor/yiisoft/yii2/db/QueryTrait.php
vendored
Normal file
@@ -0,0 +1,422 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db;
|
||||
|
||||
use yii\base\NotSupportedException;
|
||||
|
||||
/**
|
||||
* The BaseQuery trait represents the minimum method set of a database Query.
|
||||
*
|
||||
* It is supposed to be used in a class that implements the [[QueryInterface]].
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @author Carsten Brandt <mail@cebe.cc>
|
||||
* @since 2.0
|
||||
*/
|
||||
trait QueryTrait
|
||||
{
|
||||
/**
|
||||
* @var string|array query condition. This refers to the WHERE clause in a SQL statement.
|
||||
* For example, `['age' => 31, 'team' => 1]`.
|
||||
* @see where() for valid syntax on specifying this value.
|
||||
*/
|
||||
public $where;
|
||||
/**
|
||||
* @var int|ExpressionInterface maximum number of records to be returned. May be an instance of [[ExpressionInterface]].
|
||||
* If not set or less than 0, it means no limit.
|
||||
*/
|
||||
public $limit;
|
||||
/**
|
||||
* @var int|ExpressionInterface zero-based offset from where the records are to be returned.
|
||||
* May be an instance of [[ExpressionInterface]]. If not set or less than 0, it means starting from the beginning.
|
||||
*/
|
||||
public $offset;
|
||||
/**
|
||||
* @var array how to sort the query results. This is used to construct the ORDER BY clause in a SQL statement.
|
||||
* The array keys are the columns to be sorted by, and the array values are the corresponding sort directions which
|
||||
* can be either [SORT_ASC](http://php.net/manual/en/array.constants.php#constant.sort-asc)
|
||||
* or [SORT_DESC](http://php.net/manual/en/array.constants.php#constant.sort-desc).
|
||||
* The array may also contain [[ExpressionInterface]] objects. If that is the case, the expressions
|
||||
* will be converted into strings without any change.
|
||||
*/
|
||||
public $orderBy;
|
||||
/**
|
||||
* @var string|callable the name of the column by which the query results should be indexed by.
|
||||
* This can also be a callable (e.g. anonymous function) that returns the index value based on the given
|
||||
* row data. For more details, see [[indexBy()]]. This property is only used by [[QueryInterface::all()|all()]].
|
||||
*/
|
||||
public $indexBy;
|
||||
/**
|
||||
* @var bool whether to emulate the actual query execution, returning empty or false results.
|
||||
* @see emulateExecution()
|
||||
* @since 2.0.11
|
||||
*/
|
||||
public $emulateExecution = false;
|
||||
|
||||
|
||||
/**
|
||||
* Sets the [[indexBy]] property.
|
||||
* @param string|callable $column the name of the column by which the query results should be indexed by.
|
||||
* This can also be a callable (e.g. anonymous function) that returns the index value based on the given
|
||||
* row data. The signature of the callable should be:
|
||||
*
|
||||
* ```php
|
||||
* function ($row)
|
||||
* {
|
||||
* // return the index value corresponding to $row
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @return $this the query object itself
|
||||
*/
|
||||
public function indexBy($column)
|
||||
{
|
||||
$this->indexBy = $column;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the WHERE part of the query.
|
||||
*
|
||||
* See [[QueryInterface::where()]] for detailed documentation.
|
||||
*
|
||||
* @param array $condition the conditions that should be put in the WHERE part.
|
||||
* @return $this the query object itself
|
||||
* @see andWhere()
|
||||
* @see orWhere()
|
||||
*/
|
||||
public function where($condition)
|
||||
{
|
||||
$this->where = $condition;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an additional WHERE condition to the existing one.
|
||||
* The new condition and the existing one will be joined using the 'AND' operator.
|
||||
* @param array $condition the new WHERE condition. Please refer to [[where()]]
|
||||
* on how to specify this parameter.
|
||||
* @return $this the query object itself
|
||||
* @see where()
|
||||
* @see orWhere()
|
||||
*/
|
||||
public function andWhere($condition)
|
||||
{
|
||||
if ($this->where === null) {
|
||||
$this->where = $condition;
|
||||
} else {
|
||||
$this->where = ['and', $this->where, $condition];
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an additional WHERE condition to the existing one.
|
||||
* The new condition and the existing one will be joined using the 'OR' operator.
|
||||
* @param array $condition the new WHERE condition. Please refer to [[where()]]
|
||||
* on how to specify this parameter.
|
||||
* @return $this the query object itself
|
||||
* @see where()
|
||||
* @see andWhere()
|
||||
*/
|
||||
public function orWhere($condition)
|
||||
{
|
||||
if ($this->where === null) {
|
||||
$this->where = $condition;
|
||||
} else {
|
||||
$this->where = ['or', $this->where, $condition];
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the WHERE part of the query but ignores [[isEmpty()|empty operands]].
|
||||
*
|
||||
* This method is similar to [[where()]]. The main difference is that this method will
|
||||
* remove [[isEmpty()|empty query operands]]. As a result, this method is best suited
|
||||
* for building query conditions based on filter values entered by users.
|
||||
*
|
||||
* The following code shows the difference between this method and [[where()]]:
|
||||
*
|
||||
* ```php
|
||||
* // WHERE `age`=:age
|
||||
* $query->filterWhere(['name' => null, 'age' => 20]);
|
||||
* // WHERE `age`=:age
|
||||
* $query->where(['age' => 20]);
|
||||
* // WHERE `name` IS NULL AND `age`=:age
|
||||
* $query->where(['name' => null, 'age' => 20]);
|
||||
* ```
|
||||
*
|
||||
* Note that unlike [[where()]], you cannot pass binding parameters to this method.
|
||||
*
|
||||
* @param array $condition the conditions that should be put in the WHERE part.
|
||||
* See [[where()]] on how to specify this parameter.
|
||||
* @return $this the query object itself
|
||||
* @see where()
|
||||
* @see andFilterWhere()
|
||||
* @see orFilterWhere()
|
||||
*/
|
||||
public function filterWhere(array $condition)
|
||||
{
|
||||
$condition = $this->filterCondition($condition);
|
||||
if ($condition !== []) {
|
||||
$this->where($condition);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an additional WHERE condition to the existing one but ignores [[isEmpty()|empty operands]].
|
||||
* The new condition and the existing one will be joined using the 'AND' operator.
|
||||
*
|
||||
* This method is similar to [[andWhere()]]. The main difference is that this method will
|
||||
* remove [[isEmpty()|empty query operands]]. As a result, this method is best suited
|
||||
* for building query conditions based on filter values entered by users.
|
||||
*
|
||||
* @param array $condition the new WHERE condition. Please refer to [[where()]]
|
||||
* on how to specify this parameter.
|
||||
* @return $this the query object itself
|
||||
* @see filterWhere()
|
||||
* @see orFilterWhere()
|
||||
*/
|
||||
public function andFilterWhere(array $condition)
|
||||
{
|
||||
$condition = $this->filterCondition($condition);
|
||||
if ($condition !== []) {
|
||||
$this->andWhere($condition);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an additional WHERE condition to the existing one but ignores [[isEmpty()|empty operands]].
|
||||
* The new condition and the existing one will be joined using the 'OR' operator.
|
||||
*
|
||||
* This method is similar to [[orWhere()]]. The main difference is that this method will
|
||||
* remove [[isEmpty()|empty query operands]]. As a result, this method is best suited
|
||||
* for building query conditions based on filter values entered by users.
|
||||
*
|
||||
* @param array $condition the new WHERE condition. Please refer to [[where()]]
|
||||
* on how to specify this parameter.
|
||||
* @return $this the query object itself
|
||||
* @see filterWhere()
|
||||
* @see andFilterWhere()
|
||||
*/
|
||||
public function orFilterWhere(array $condition)
|
||||
{
|
||||
$condition = $this->filterCondition($condition);
|
||||
if ($condition !== []) {
|
||||
$this->orWhere($condition);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes [[isEmpty()|empty operands]] from the given query condition.
|
||||
*
|
||||
* @param array $condition the original condition
|
||||
* @return array the condition with [[isEmpty()|empty operands]] removed.
|
||||
* @throws NotSupportedException if the condition operator is not supported
|
||||
*/
|
||||
protected function filterCondition($condition)
|
||||
{
|
||||
if (!is_array($condition)) {
|
||||
return $condition;
|
||||
}
|
||||
|
||||
if (!isset($condition[0])) {
|
||||
// hash format: 'column1' => 'value1', 'column2' => 'value2', ...
|
||||
foreach ($condition as $name => $value) {
|
||||
if ($this->isEmpty($value)) {
|
||||
unset($condition[$name]);
|
||||
}
|
||||
}
|
||||
|
||||
return $condition;
|
||||
}
|
||||
|
||||
// operator format: operator, operand 1, operand 2, ...
|
||||
|
||||
$operator = array_shift($condition);
|
||||
|
||||
switch (strtoupper($operator)) {
|
||||
case 'NOT':
|
||||
case 'AND':
|
||||
case 'OR':
|
||||
foreach ($condition as $i => $operand) {
|
||||
$subCondition = $this->filterCondition($operand);
|
||||
if ($this->isEmpty($subCondition)) {
|
||||
unset($condition[$i]);
|
||||
} else {
|
||||
$condition[$i] = $subCondition;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($condition)) {
|
||||
return [];
|
||||
}
|
||||
break;
|
||||
case 'BETWEEN':
|
||||
case 'NOT BETWEEN':
|
||||
if (array_key_exists(1, $condition) && array_key_exists(2, $condition)) {
|
||||
if ($this->isEmpty($condition[1]) || $this->isEmpty($condition[2])) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (array_key_exists(1, $condition) && $this->isEmpty($condition[1])) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
array_unshift($condition, $operator);
|
||||
|
||||
return $condition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a value indicating whether the give value is "empty".
|
||||
*
|
||||
* The value is considered "empty", if one of the following conditions is satisfied:
|
||||
*
|
||||
* - it is `null`,
|
||||
* - an empty string (`''`),
|
||||
* - a string containing only whitespace characters,
|
||||
* - or an empty array.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @return bool if the value is empty
|
||||
*/
|
||||
protected function isEmpty($value)
|
||||
{
|
||||
return $value === '' || $value === [] || $value === null || is_string($value) && trim($value) === '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the ORDER BY part of the query.
|
||||
* @param string|array|ExpressionInterface $columns the columns (and the directions) to be ordered by.
|
||||
* Columns can be specified in either a string (e.g. `"id ASC, name DESC"`) or an array
|
||||
* (e.g. `['id' => SORT_ASC, 'name' => SORT_DESC]`).
|
||||
*
|
||||
* The method will automatically quote the column names unless a column contains some parenthesis
|
||||
* (which means the column contains a DB expression).
|
||||
*
|
||||
* Note that if your order-by is an expression containing commas, you should always use an array
|
||||
* to represent the order-by information. Otherwise, the method will not be able to correctly determine
|
||||
* the order-by columns.
|
||||
*
|
||||
* Since version 2.0.7, an [[ExpressionInterface]] object can be passed to specify the ORDER BY part explicitly in plain SQL.
|
||||
* @return $this the query object itself
|
||||
* @see addOrderBy()
|
||||
*/
|
||||
public function orderBy($columns)
|
||||
{
|
||||
$this->orderBy = $this->normalizeOrderBy($columns);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds additional ORDER BY columns to the query.
|
||||
* @param string|array|ExpressionInterface $columns the columns (and the directions) to be ordered by.
|
||||
* Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array
|
||||
* (e.g. `['id' => SORT_ASC, 'name' => SORT_DESC]`).
|
||||
*
|
||||
* The method will automatically quote the column names unless a column contains some parenthesis
|
||||
* (which means the column contains a DB expression).
|
||||
*
|
||||
* Note that if your order-by is an expression containing commas, you should always use an array
|
||||
* to represent the order-by information. Otherwise, the method will not be able to correctly determine
|
||||
* the order-by columns.
|
||||
*
|
||||
* Since version 2.0.7, an [[ExpressionInterface]] object can be passed to specify the ORDER BY part explicitly in plain SQL.
|
||||
* @return $this the query object itself
|
||||
* @see orderBy()
|
||||
*/
|
||||
public function addOrderBy($columns)
|
||||
{
|
||||
$columns = $this->normalizeOrderBy($columns);
|
||||
if ($this->orderBy === null) {
|
||||
$this->orderBy = $columns;
|
||||
} else {
|
||||
$this->orderBy = array_merge($this->orderBy, $columns);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes format of ORDER BY data.
|
||||
*
|
||||
* @param array|string|ExpressionInterface $columns the columns value to normalize. See [[orderBy]] and [[addOrderBy]].
|
||||
* @return array
|
||||
*/
|
||||
protected function normalizeOrderBy($columns)
|
||||
{
|
||||
if ($columns instanceof ExpressionInterface) {
|
||||
return [$columns];
|
||||
} elseif (is_array($columns)) {
|
||||
return $columns;
|
||||
}
|
||||
|
||||
$columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
|
||||
$result = [];
|
||||
foreach ($columns as $column) {
|
||||
if (preg_match('/^(.*?)\s+(asc|desc)$/i', $column, $matches)) {
|
||||
$result[$matches[1]] = strcasecmp($matches[2], 'desc') ? SORT_ASC : SORT_DESC;
|
||||
} else {
|
||||
$result[$column] = SORT_ASC;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the LIMIT part of the query.
|
||||
* @param int|ExpressionInterface|null $limit the limit. Use null or negative value to disable limit.
|
||||
* @return $this the query object itself
|
||||
*/
|
||||
public function limit($limit)
|
||||
{
|
||||
$this->limit = $limit;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the OFFSET part of the query.
|
||||
* @param int|ExpressionInterface|null $offset the offset. Use null or negative value to disable offset.
|
||||
* @return $this the query object itself
|
||||
*/
|
||||
public function offset($offset)
|
||||
{
|
||||
$this->offset = $offset;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether to emulate query execution, preventing any interaction with data storage.
|
||||
* After this mode is enabled, methods, returning query results like [[QueryInterface::one()]],
|
||||
* [[QueryInterface::all()]], [[QueryInterface::exists()]] and so on, will return empty or false values.
|
||||
* You should use this method in case your program logic indicates query should not return any results, like
|
||||
* in case you set false where condition like `0=1`.
|
||||
* @param bool $value whether to prevent query execution.
|
||||
* @return $this the query object itself.
|
||||
* @since 2.0.11
|
||||
*/
|
||||
public function emulateExecution($value = true)
|
||||
{
|
||||
$this->emulateExecution = $value;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
855
vendor/yiisoft/yii2/db/Schema.php
vendored
Normal file
855
vendor/yiisoft/yii2/db/Schema.php
vendored
Normal file
@@ -0,0 +1,855 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db;
|
||||
|
||||
use Yii;
|
||||
use yii\base\BaseObject;
|
||||
use yii\base\InvalidCallException;
|
||||
use yii\base\InvalidConfigException;
|
||||
use yii\base\NotSupportedException;
|
||||
use yii\caching\Cache;
|
||||
use yii\caching\CacheInterface;
|
||||
use yii\caching\TagDependency;
|
||||
use yii\helpers\StringHelper;
|
||||
|
||||
/**
|
||||
* Schema is the base class for concrete DBMS-specific schema classes.
|
||||
*
|
||||
* Schema represents the database schema information that is DBMS specific.
|
||||
*
|
||||
* @property string $lastInsertID The row ID of the last row inserted, or the last value retrieved from the
|
||||
* sequence object. This property is read-only.
|
||||
* @property QueryBuilder $queryBuilder The query builder for this connection. This property is read-only.
|
||||
* @property string[] $schemaNames All schema names in the database, except system schemas. This property is
|
||||
* read-only.
|
||||
* @property string $serverVersion Server version as a string. This property is read-only.
|
||||
* @property string[] $tableNames All table names in the database. This property is read-only.
|
||||
* @property TableSchema[] $tableSchemas The metadata for all tables in the database. Each array element is an
|
||||
* instance of [[TableSchema]] or its child class. This property is read-only.
|
||||
* @property string $transactionIsolationLevel The transaction isolation level to use for this transaction.
|
||||
* This can be one of [[Transaction::READ_UNCOMMITTED]], [[Transaction::READ_COMMITTED]],
|
||||
* [[Transaction::REPEATABLE_READ]] and [[Transaction::SERIALIZABLE]] but also a string containing DBMS specific
|
||||
* syntax to be used after `SET TRANSACTION ISOLATION LEVEL`. This property is write-only.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @author Sergey Makinen <sergey@makinen.ru>
|
||||
* @since 2.0
|
||||
*/
|
||||
abstract class Schema extends BaseObject
|
||||
{
|
||||
// The following are the supported abstract column data types.
|
||||
const TYPE_PK = 'pk';
|
||||
const TYPE_UPK = 'upk';
|
||||
const TYPE_BIGPK = 'bigpk';
|
||||
const TYPE_UBIGPK = 'ubigpk';
|
||||
const TYPE_CHAR = 'char';
|
||||
const TYPE_STRING = 'string';
|
||||
const TYPE_TEXT = 'text';
|
||||
const TYPE_TINYINT = 'tinyint';
|
||||
const TYPE_SMALLINT = 'smallint';
|
||||
const TYPE_INTEGER = 'integer';
|
||||
const TYPE_BIGINT = 'bigint';
|
||||
const TYPE_FLOAT = 'float';
|
||||
const TYPE_DOUBLE = 'double';
|
||||
const TYPE_DECIMAL = 'decimal';
|
||||
const TYPE_DATETIME = 'datetime';
|
||||
const TYPE_TIMESTAMP = 'timestamp';
|
||||
const TYPE_TIME = 'time';
|
||||
const TYPE_DATE = 'date';
|
||||
const TYPE_BINARY = 'binary';
|
||||
const TYPE_BOOLEAN = 'boolean';
|
||||
const TYPE_MONEY = 'money';
|
||||
const TYPE_JSON = 'json';
|
||||
/**
|
||||
* Schema cache version, to detect incompatibilities in cached values when the
|
||||
* data format of the cache changes.
|
||||
*/
|
||||
const SCHEMA_CACHE_VERSION = 1;
|
||||
|
||||
/**
|
||||
* @var Connection the database connection
|
||||
*/
|
||||
public $db;
|
||||
/**
|
||||
* @var string the default schema name used for the current session.
|
||||
*/
|
||||
public $defaultSchema;
|
||||
/**
|
||||
* @var array map of DB errors and corresponding exceptions
|
||||
* If left part is found in DB error message exception class from the right part is used.
|
||||
*/
|
||||
public $exceptionMap = [
|
||||
'SQLSTATE[23' => 'yii\db\IntegrityException',
|
||||
];
|
||||
/**
|
||||
* @var string|array column schema class or class config
|
||||
* @since 2.0.11
|
||||
*/
|
||||
public $columnSchemaClass = 'yii\db\ColumnSchema';
|
||||
|
||||
/**
|
||||
* @var string|string[] character used to quote schema, table, etc. names.
|
||||
* An array of 2 characters can be used in case starting and ending characters are different.
|
||||
* @since 2.0.14
|
||||
*/
|
||||
protected $tableQuoteCharacter = "'";
|
||||
/**
|
||||
* @var string|string[] character used to quote column names.
|
||||
* An array of 2 characters can be used in case starting and ending characters are different.
|
||||
* @since 2.0.14
|
||||
*/
|
||||
protected $columnQuoteCharacter = '"';
|
||||
|
||||
/**
|
||||
* @var array list of ALL schema names in the database, except system schemas
|
||||
*/
|
||||
private $_schemaNames;
|
||||
/**
|
||||
* @var array list of ALL table names in the database
|
||||
*/
|
||||
private $_tableNames = [];
|
||||
/**
|
||||
* @var array list of loaded table metadata (table name => metadata type => metadata).
|
||||
*/
|
||||
private $_tableMetadata = [];
|
||||
/**
|
||||
* @var QueryBuilder the query builder for this database
|
||||
*/
|
||||
private $_builder;
|
||||
/**
|
||||
* @var string server version as a string.
|
||||
*/
|
||||
private $_serverVersion;
|
||||
|
||||
|
||||
/**
|
||||
* Resolves the table name and schema name (if any).
|
||||
* @param string $name the table name
|
||||
* @return TableSchema [[TableSchema]] with resolved table, schema, etc. names.
|
||||
* @throws NotSupportedException if this method is not supported by the DBMS.
|
||||
* @since 2.0.13
|
||||
*/
|
||||
protected function resolveTableName($name)
|
||||
{
|
||||
throw new NotSupportedException(get_class($this) . ' does not support resolving table names.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all schema names in the database, including the default one but not system schemas.
|
||||
* This method should be overridden by child classes in order to support this feature
|
||||
* because the default implementation simply throws an exception.
|
||||
* @return array all schema names in the database, except system schemas.
|
||||
* @throws NotSupportedException if this method is not supported by the DBMS.
|
||||
* @since 2.0.4
|
||||
*/
|
||||
protected function findSchemaNames()
|
||||
{
|
||||
throw new NotSupportedException(get_class($this) . ' does not support fetching all schema names.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all table names in the database.
|
||||
* This method should be overridden by child classes in order to support this feature
|
||||
* because the default implementation simply throws an exception.
|
||||
* @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema.
|
||||
* @return array all table names in the database. The names have NO schema name prefix.
|
||||
* @throws NotSupportedException if this method is not supported by the DBMS.
|
||||
*/
|
||||
protected function findTableNames($schema = '')
|
||||
{
|
||||
throw new NotSupportedException(get_class($this) . ' does not support fetching all table names.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the metadata for the specified table.
|
||||
* @param string $name table name
|
||||
* @return TableSchema|null DBMS-dependent table metadata, `null` if the table does not exist.
|
||||
*/
|
||||
abstract protected function loadTableSchema($name);
|
||||
|
||||
/**
|
||||
* Creates a column schema for the database.
|
||||
* This method may be overridden by child classes to create a DBMS-specific column schema.
|
||||
* @return ColumnSchema column schema instance.
|
||||
* @throws InvalidConfigException if a column schema class cannot be created.
|
||||
*/
|
||||
protected function createColumnSchema()
|
||||
{
|
||||
return Yii::createObject($this->columnSchemaClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the metadata for the named table.
|
||||
* @param string $name table name. The table name may contain schema name if any. Do not quote the table name.
|
||||
* @param bool $refresh whether to reload the table schema even if it is found in the cache.
|
||||
* @return TableSchema|null table metadata. `null` if the named table does not exist.
|
||||
*/
|
||||
public function getTableSchema($name, $refresh = false)
|
||||
{
|
||||
return $this->getTableMetadata($name, 'schema', $refresh);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the metadata for all tables in the database.
|
||||
* @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema name.
|
||||
* @param bool $refresh whether to fetch the latest available table schemas. If this is `false`,
|
||||
* cached data may be returned if available.
|
||||
* @return TableSchema[] the metadata for all tables in the database.
|
||||
* Each array element is an instance of [[TableSchema]] or its child class.
|
||||
*/
|
||||
public function getTableSchemas($schema = '', $refresh = false)
|
||||
{
|
||||
return $this->getSchemaMetadata($schema, 'schema', $refresh);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all schema names in the database, except system schemas.
|
||||
* @param bool $refresh whether to fetch the latest available schema names. If this is false,
|
||||
* schema names fetched previously (if available) will be returned.
|
||||
* @return string[] all schema names in the database, except system schemas.
|
||||
* @since 2.0.4
|
||||
*/
|
||||
public function getSchemaNames($refresh = false)
|
||||
{
|
||||
if ($this->_schemaNames === null || $refresh) {
|
||||
$this->_schemaNames = $this->findSchemaNames();
|
||||
}
|
||||
|
||||
return $this->_schemaNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all table names in the database.
|
||||
* @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema name.
|
||||
* If not empty, the returned table names will be prefixed with the schema name.
|
||||
* @param bool $refresh whether to fetch the latest available table names. If this is false,
|
||||
* table names fetched previously (if available) will be returned.
|
||||
* @return string[] all table names in the database.
|
||||
*/
|
||||
public function getTableNames($schema = '', $refresh = false)
|
||||
{
|
||||
if (!isset($this->_tableNames[$schema]) || $refresh) {
|
||||
$this->_tableNames[$schema] = $this->findTableNames($schema);
|
||||
}
|
||||
|
||||
return $this->_tableNames[$schema];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return QueryBuilder the query builder for this connection.
|
||||
*/
|
||||
public function getQueryBuilder()
|
||||
{
|
||||
if ($this->_builder === null) {
|
||||
$this->_builder = $this->createQueryBuilder();
|
||||
}
|
||||
|
||||
return $this->_builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the PDO type for the given PHP data value.
|
||||
* @param mixed $data the data whose PDO type is to be determined
|
||||
* @return int the PDO type
|
||||
* @see http://www.php.net/manual/en/pdo.constants.php
|
||||
*/
|
||||
public function getPdoType($data)
|
||||
{
|
||||
static $typeMap = [
|
||||
// php type => PDO type
|
||||
'boolean' => \PDO::PARAM_BOOL,
|
||||
'integer' => \PDO::PARAM_INT,
|
||||
'string' => \PDO::PARAM_STR,
|
||||
'resource' => \PDO::PARAM_LOB,
|
||||
'NULL' => \PDO::PARAM_NULL,
|
||||
];
|
||||
$type = gettype($data);
|
||||
|
||||
return isset($typeMap[$type]) ? $typeMap[$type] : \PDO::PARAM_STR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the schema.
|
||||
* This method cleans up all cached table schemas so that they can be re-created later
|
||||
* to reflect the database schema change.
|
||||
*/
|
||||
public function refresh()
|
||||
{
|
||||
/* @var $cache CacheInterface */
|
||||
$cache = is_string($this->db->schemaCache) ? Yii::$app->get($this->db->schemaCache, false) : $this->db->schemaCache;
|
||||
if ($this->db->enableSchemaCache && $cache instanceof CacheInterface) {
|
||||
TagDependency::invalidate($cache, $this->getCacheTag());
|
||||
}
|
||||
$this->_tableNames = [];
|
||||
$this->_tableMetadata = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the particular table schema.
|
||||
* This method cleans up cached table schema so that it can be re-created later
|
||||
* to reflect the database schema change.
|
||||
* @param string $name table name.
|
||||
* @since 2.0.6
|
||||
*/
|
||||
public function refreshTableSchema($name)
|
||||
{
|
||||
$rawName = $this->getRawTableName($name);
|
||||
unset($this->_tableMetadata[$rawName]);
|
||||
$this->_tableNames = [];
|
||||
/* @var $cache CacheInterface */
|
||||
$cache = is_string($this->db->schemaCache) ? Yii::$app->get($this->db->schemaCache, false) : $this->db->schemaCache;
|
||||
if ($this->db->enableSchemaCache && $cache instanceof CacheInterface) {
|
||||
$cache->delete($this->getCacheKey($rawName));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a query builder for the database.
|
||||
* This method may be overridden by child classes to create a DBMS-specific query builder.
|
||||
* @return QueryBuilder query builder instance
|
||||
*/
|
||||
public function createQueryBuilder()
|
||||
{
|
||||
return new QueryBuilder($this->db);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a column schema builder instance giving the type and value precision.
|
||||
*
|
||||
* This method may be overridden by child classes to create a DBMS-specific column schema builder.
|
||||
*
|
||||
* @param string $type type of the column. See [[ColumnSchemaBuilder::$type]].
|
||||
* @param int|string|array $length length or precision of the column. See [[ColumnSchemaBuilder::$length]].
|
||||
* @return ColumnSchemaBuilder column schema builder instance
|
||||
* @since 2.0.6
|
||||
*/
|
||||
public function createColumnSchemaBuilder($type, $length = null)
|
||||
{
|
||||
return new ColumnSchemaBuilder($type, $length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all unique indexes for the given table.
|
||||
*
|
||||
* Each array element is of the following structure:
|
||||
*
|
||||
* ```php
|
||||
* [
|
||||
* 'IndexName1' => ['col1' [, ...]],
|
||||
* 'IndexName2' => ['col2' [, ...]],
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* This method should be overridden by child classes in order to support this feature
|
||||
* because the default implementation simply throws an exception
|
||||
* @param TableSchema $table the table metadata
|
||||
* @return array all unique indexes for the given table.
|
||||
* @throws NotSupportedException if this method is called
|
||||
*/
|
||||
public function findUniqueIndexes($table)
|
||||
{
|
||||
throw new NotSupportedException(get_class($this) . ' does not support getting unique indexes information.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ID of the last inserted row or sequence value.
|
||||
* @param string $sequenceName name of the sequence object (required by some DBMS)
|
||||
* @return string the row ID of the last row inserted, or the last value retrieved from the sequence object
|
||||
* @throws InvalidCallException if the DB connection is not active
|
||||
* @see http://www.php.net/manual/en/function.PDO-lastInsertId.php
|
||||
*/
|
||||
public function getLastInsertID($sequenceName = '')
|
||||
{
|
||||
if ($this->db->isActive) {
|
||||
return $this->db->pdo->lastInsertId($sequenceName === '' ? null : $this->quoteTableName($sequenceName));
|
||||
}
|
||||
|
||||
throw new InvalidCallException('DB Connection is not active.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool whether this DBMS supports [savepoint](http://en.wikipedia.org/wiki/Savepoint).
|
||||
*/
|
||||
public function supportsSavepoint()
|
||||
{
|
||||
return $this->db->enableSavepoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new savepoint.
|
||||
* @param string $name the savepoint name
|
||||
*/
|
||||
public function createSavepoint($name)
|
||||
{
|
||||
$this->db->createCommand("SAVEPOINT $name")->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases an existing savepoint.
|
||||
* @param string $name the savepoint name
|
||||
*/
|
||||
public function releaseSavepoint($name)
|
||||
{
|
||||
$this->db->createCommand("RELEASE SAVEPOINT $name")->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Rolls back to a previously created savepoint.
|
||||
* @param string $name the savepoint name
|
||||
*/
|
||||
public function rollBackSavepoint($name)
|
||||
{
|
||||
$this->db->createCommand("ROLLBACK TO SAVEPOINT $name")->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the isolation level of the current transaction.
|
||||
* @param string $level The transaction isolation level to use for this transaction.
|
||||
* This can be one of [[Transaction::READ_UNCOMMITTED]], [[Transaction::READ_COMMITTED]], [[Transaction::REPEATABLE_READ]]
|
||||
* and [[Transaction::SERIALIZABLE]] but also a string containing DBMS specific syntax to be used
|
||||
* after `SET TRANSACTION ISOLATION LEVEL`.
|
||||
* @see http://en.wikipedia.org/wiki/Isolation_%28database_systems%29#Isolation_levels
|
||||
*/
|
||||
public function setTransactionIsolationLevel($level)
|
||||
{
|
||||
$this->db->createCommand("SET TRANSACTION ISOLATION LEVEL $level")->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the INSERT command, returning primary key values.
|
||||
* @param string $table the table that new rows will be inserted into.
|
||||
* @param array $columns the column data (name => value) to be inserted into the table.
|
||||
* @return array|false primary key values or false if the command fails
|
||||
* @since 2.0.4
|
||||
*/
|
||||
public function insert($table, $columns)
|
||||
{
|
||||
$command = $this->db->createCommand()->insert($table, $columns);
|
||||
if (!$command->execute()) {
|
||||
return false;
|
||||
}
|
||||
$tableSchema = $this->getTableSchema($table);
|
||||
$result = [];
|
||||
foreach ($tableSchema->primaryKey as $name) {
|
||||
if ($tableSchema->columns[$name]->autoIncrement) {
|
||||
$result[$name] = $this->getLastInsertID($tableSchema->sequenceName);
|
||||
break;
|
||||
}
|
||||
|
||||
$result[$name] = isset($columns[$name]) ? $columns[$name] : $tableSchema->columns[$name]->defaultValue;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Quotes a string value for use in a query.
|
||||
* Note that if the parameter is not a string, it will be returned without change.
|
||||
* @param string $str string to be quoted
|
||||
* @return string the properly quoted string
|
||||
* @see http://www.php.net/manual/en/function.PDO-quote.php
|
||||
*/
|
||||
public function quoteValue($str)
|
||||
{
|
||||
if (!is_string($str)) {
|
||||
return $str;
|
||||
}
|
||||
|
||||
if (($value = $this->db->getSlavePdo()->quote($str)) !== false) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
// the driver doesn't support quote (e.g. oci)
|
||||
return "'" . addcslashes(str_replace("'", "''", $str), "\000\n\r\\\032") . "'";
|
||||
}
|
||||
|
||||
/**
|
||||
* Quotes a table name for use in a query.
|
||||
* If the table name contains schema prefix, the prefix will also be properly quoted.
|
||||
* If the table name is already quoted or contains '(' or '{{',
|
||||
* then this method will do nothing.
|
||||
* @param string $name table name
|
||||
* @return string the properly quoted table name
|
||||
* @see quoteSimpleTableName()
|
||||
*/
|
||||
public function quoteTableName($name)
|
||||
{
|
||||
if (strpos($name, '(') !== false || strpos($name, '{{') !== false) {
|
||||
return $name;
|
||||
}
|
||||
if (strpos($name, '.') === false) {
|
||||
return $this->quoteSimpleTableName($name);
|
||||
}
|
||||
$parts = explode('.', $name);
|
||||
foreach ($parts as $i => $part) {
|
||||
$parts[$i] = $this->quoteSimpleTableName($part);
|
||||
}
|
||||
|
||||
return implode('.', $parts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Quotes a column name for use in a query.
|
||||
* If the column name contains prefix, the prefix will also be properly quoted.
|
||||
* If the column name is already quoted or contains '(', '[[' or '{{',
|
||||
* then this method will do nothing.
|
||||
* @param string $name column name
|
||||
* @return string the properly quoted column name
|
||||
* @see quoteSimpleColumnName()
|
||||
*/
|
||||
public function quoteColumnName($name)
|
||||
{
|
||||
if (strpos($name, '(') !== false || strpos($name, '[[') !== false) {
|
||||
return $name;
|
||||
}
|
||||
if (($pos = strrpos($name, '.')) !== false) {
|
||||
$prefix = $this->quoteTableName(substr($name, 0, $pos)) . '.';
|
||||
$name = substr($name, $pos + 1);
|
||||
} else {
|
||||
$prefix = '';
|
||||
}
|
||||
if (strpos($name, '{{') !== false) {
|
||||
return $name;
|
||||
}
|
||||
|
||||
return $prefix . $this->quoteSimpleColumnName($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Quotes a simple table name for use in a query.
|
||||
* A simple table name should contain the table name only without any schema prefix.
|
||||
* If the table name is already quoted, this method will do nothing.
|
||||
* @param string $name table name
|
||||
* @return string the properly quoted table name
|
||||
*/
|
||||
public function quoteSimpleTableName($name)
|
||||
{
|
||||
if (is_string($this->tableQuoteCharacter)) {
|
||||
$startingCharacter = $endingCharacter = $this->tableQuoteCharacter;
|
||||
} else {
|
||||
list($startingCharacter, $endingCharacter) = $this->tableQuoteCharacter;
|
||||
}
|
||||
return strpos($name, $startingCharacter) !== false ? $name : $startingCharacter . $name . $endingCharacter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Quotes a simple column name for use in a query.
|
||||
* A simple column name should contain the column name only without any prefix.
|
||||
* If the column name is already quoted or is the asterisk character '*', this method will do nothing.
|
||||
* @param string $name column name
|
||||
* @return string the properly quoted column name
|
||||
*/
|
||||
public function quoteSimpleColumnName($name)
|
||||
{
|
||||
if (is_string($this->tableQuoteCharacter)) {
|
||||
$startingCharacter = $endingCharacter = $this->columnQuoteCharacter;
|
||||
} else {
|
||||
list($startingCharacter, $endingCharacter) = $this->columnQuoteCharacter;
|
||||
}
|
||||
return $name === '*' || strpos($name, $startingCharacter) !== false ? $name : $startingCharacter . $name . $endingCharacter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unquotes a simple table name.
|
||||
* A simple table name should contain the table name only without any schema prefix.
|
||||
* If the table name is not quoted, this method will do nothing.
|
||||
* @param string $name table name.
|
||||
* @return string unquoted table name.
|
||||
* @since 2.0.14
|
||||
*/
|
||||
public function unquoteSimpleTableName($name)
|
||||
{
|
||||
if (is_string($this->tableQuoteCharacter)) {
|
||||
$startingCharacter = $this->tableQuoteCharacter;
|
||||
} else {
|
||||
$startingCharacter = $this->tableQuoteCharacter[0];
|
||||
}
|
||||
return strpos($name, $startingCharacter) === false ? $name : substr($name, 1, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unquotes a simple column name.
|
||||
* A simple column name should contain the column name only without any prefix.
|
||||
* If the column name is not quoted or is the asterisk character '*', this method will do nothing.
|
||||
* @param string $name column name.
|
||||
* @return string unquoted column name.
|
||||
* @since 2.0.14
|
||||
*/
|
||||
public function unquoteSimpleColumnName($name)
|
||||
{
|
||||
if (is_string($this->columnQuoteCharacter)) {
|
||||
$startingCharacter = $this->columnQuoteCharacter;
|
||||
} else {
|
||||
$startingCharacter = $this->columnQuoteCharacter[0];
|
||||
}
|
||||
return strpos($name, $startingCharacter) === false ? $name : substr($name, 1, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the actual name of a given table name.
|
||||
* This method will strip off curly brackets from the given table name
|
||||
* and replace the percentage character '%' with [[Connection::tablePrefix]].
|
||||
* @param string $name the table name to be converted
|
||||
* @return string the real name of the given table name
|
||||
*/
|
||||
public function getRawTableName($name)
|
||||
{
|
||||
if (strpos($name, '{{') !== false) {
|
||||
$name = preg_replace('/\\{\\{(.*?)\\}\\}/', '\1', $name);
|
||||
|
||||
return str_replace('%', $this->db->tablePrefix, $name);
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the PHP type from abstract DB type.
|
||||
* @param ColumnSchema $column the column schema information
|
||||
* @return string PHP type name
|
||||
*/
|
||||
protected function getColumnPhpType($column)
|
||||
{
|
||||
static $typeMap = [
|
||||
// abstract type => php type
|
||||
self::TYPE_TINYINT => 'integer',
|
||||
self::TYPE_SMALLINT => 'integer',
|
||||
self::TYPE_INTEGER => 'integer',
|
||||
self::TYPE_BIGINT => 'integer',
|
||||
self::TYPE_BOOLEAN => 'boolean',
|
||||
self::TYPE_FLOAT => 'double',
|
||||
self::TYPE_DOUBLE => 'double',
|
||||
self::TYPE_BINARY => 'resource',
|
||||
self::TYPE_JSON => 'array',
|
||||
];
|
||||
if (isset($typeMap[$column->type])) {
|
||||
if ($column->type === 'bigint') {
|
||||
return PHP_INT_SIZE === 8 && !$column->unsigned ? 'integer' : 'string';
|
||||
} elseif ($column->type === 'integer') {
|
||||
return PHP_INT_SIZE === 4 && $column->unsigned ? 'string' : 'integer';
|
||||
}
|
||||
|
||||
return $typeMap[$column->type];
|
||||
}
|
||||
|
||||
return 'string';
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a DB exception to a more concrete one if possible.
|
||||
*
|
||||
* @param \Exception $e
|
||||
* @param string $rawSql SQL that produced exception
|
||||
* @return Exception
|
||||
*/
|
||||
public function convertException(\Exception $e, $rawSql)
|
||||
{
|
||||
if ($e instanceof Exception) {
|
||||
return $e;
|
||||
}
|
||||
|
||||
$exceptionClass = '\yii\db\Exception';
|
||||
foreach ($this->exceptionMap as $error => $class) {
|
||||
if (strpos($e->getMessage(), $error) !== false) {
|
||||
$exceptionClass = $class;
|
||||
}
|
||||
}
|
||||
$message = $e->getMessage() . "\nThe SQL being executed was: $rawSql";
|
||||
$errorInfo = $e instanceof \PDOException ? $e->errorInfo : null;
|
||||
return new $exceptionClass($message, $errorInfo, (int) $e->getCode(), $e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a value indicating whether a SQL statement is for read purpose.
|
||||
* @param string $sql the SQL statement
|
||||
* @return bool whether a SQL statement is for read purpose.
|
||||
*/
|
||||
public function isReadQuery($sql)
|
||||
{
|
||||
$pattern = '/^\s*(SELECT|SHOW|DESCRIBE)\b/i';
|
||||
return preg_match($pattern, $sql) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a server version as a string comparable by [[\version_compare()]].
|
||||
* @return string server version as a string.
|
||||
* @since 2.0.14
|
||||
*/
|
||||
public function getServerVersion()
|
||||
{
|
||||
if ($this->_serverVersion === null) {
|
||||
$this->_serverVersion = $this->db->getSlavePdo()->getAttribute(\PDO::ATTR_SERVER_VERSION);
|
||||
}
|
||||
return $this->_serverVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cache key for the specified table name.
|
||||
* @param string $name the table name.
|
||||
* @return mixed the cache key.
|
||||
*/
|
||||
protected function getCacheKey($name)
|
||||
{
|
||||
return [
|
||||
__CLASS__,
|
||||
$this->db->dsn,
|
||||
$this->db->username,
|
||||
$this->getRawTableName($name),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cache tag name.
|
||||
* This allows [[refresh()]] to invalidate all cached table schemas.
|
||||
* @return string the cache tag name
|
||||
*/
|
||||
protected function getCacheTag()
|
||||
{
|
||||
return md5(serialize([
|
||||
__CLASS__,
|
||||
$this->db->dsn,
|
||||
$this->db->username,
|
||||
]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the metadata of the given type for the given table.
|
||||
* If there's no metadata in the cache, this method will call
|
||||
* a `'loadTable' . ucfirst($type)` named method with the table name to obtain the metadata.
|
||||
* @param string $name table name. The table name may contain schema name if any. Do not quote the table name.
|
||||
* @param string $type metadata type.
|
||||
* @param bool $refresh whether to reload the table metadata even if it is found in the cache.
|
||||
* @return mixed metadata.
|
||||
* @since 2.0.13
|
||||
*/
|
||||
protected function getTableMetadata($name, $type, $refresh)
|
||||
{
|
||||
$cache = null;
|
||||
if ($this->db->enableSchemaCache && !in_array($name, $this->db->schemaCacheExclude, true)) {
|
||||
$schemaCache = is_string($this->db->schemaCache) ? Yii::$app->get($this->db->schemaCache, false) : $this->db->schemaCache;
|
||||
if ($schemaCache instanceof Cache) {
|
||||
$cache = $schemaCache;
|
||||
}
|
||||
}
|
||||
$rawName = $this->getRawTableName($name);
|
||||
if ($refresh || !isset($this->_tableMetadata[$rawName])) {
|
||||
$this->loadTableMetadataFromCache($cache, $rawName);
|
||||
}
|
||||
if (!array_key_exists($type, $this->_tableMetadata[$rawName])) {
|
||||
$this->_tableMetadata[$rawName][$type] = $this->{'loadTable' . ucfirst($type)}($rawName);
|
||||
$this->saveTableMetadataToCache($cache, $rawName);
|
||||
}
|
||||
|
||||
return $this->_tableMetadata[$rawName][$type];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the metadata of the given type for all tables in the given schema.
|
||||
* This method will call a `'getTable' . ucfirst($type)` named method with the table name
|
||||
* and the refresh flag to obtain the metadata.
|
||||
* @param string $schema the schema of the metadata. Defaults to empty string, meaning the current or default schema name.
|
||||
* @param string $type metadata type.
|
||||
* @param bool $refresh whether to fetch the latest available table metadata. If this is `false`,
|
||||
* cached data may be returned if available.
|
||||
* @return array array of metadata.
|
||||
* @since 2.0.13
|
||||
*/
|
||||
protected function getSchemaMetadata($schema, $type, $refresh)
|
||||
{
|
||||
$metadata = [];
|
||||
$methodName = 'getTable' . ucfirst($type);
|
||||
foreach ($this->getTableNames($schema, $refresh) as $name) {
|
||||
if ($schema !== '') {
|
||||
$name = $schema . '.' . $name;
|
||||
}
|
||||
$tableMetadata = $this->$methodName($name, $refresh);
|
||||
if ($tableMetadata !== null) {
|
||||
$metadata[] = $tableMetadata;
|
||||
}
|
||||
}
|
||||
|
||||
return $metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the metadata of the given type for the given table.
|
||||
* @param string $name table name.
|
||||
* @param string $type metadata type.
|
||||
* @param mixed $data metadata.
|
||||
* @since 2.0.13
|
||||
*/
|
||||
protected function setTableMetadata($name, $type, $data)
|
||||
{
|
||||
$this->_tableMetadata[$this->getRawTableName($name)][$type] = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes row's array key case to lower if PDO's one is set to uppercase.
|
||||
* @param array $row row's array or an array of row's arrays.
|
||||
* @param bool $multiple whether multiple rows or a single row passed.
|
||||
* @return array normalized row or rows.
|
||||
* @since 2.0.13
|
||||
*/
|
||||
protected function normalizePdoRowKeyCase(array $row, $multiple)
|
||||
{
|
||||
if ($this->db->getSlavePdo()->getAttribute(\PDO::ATTR_CASE) !== \PDO::CASE_UPPER) {
|
||||
return $row;
|
||||
}
|
||||
|
||||
if ($multiple) {
|
||||
return array_map(function (array $row) {
|
||||
return array_change_key_case($row, CASE_LOWER);
|
||||
}, $row);
|
||||
}
|
||||
|
||||
return array_change_key_case($row, CASE_LOWER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to load and populate table metadata from cache.
|
||||
* @param Cache|null $cache
|
||||
* @param string $name
|
||||
*/
|
||||
private function loadTableMetadataFromCache($cache, $name)
|
||||
{
|
||||
if ($cache === null) {
|
||||
$this->_tableMetadata[$name] = [];
|
||||
return;
|
||||
}
|
||||
|
||||
$metadata = $cache->get($this->getCacheKey($name));
|
||||
if (!is_array($metadata) || !isset($metadata['cacheVersion']) || $metadata['cacheVersion'] !== static::SCHEMA_CACHE_VERSION) {
|
||||
$this->_tableMetadata[$name] = [];
|
||||
return;
|
||||
}
|
||||
|
||||
unset($metadata['cacheVersion']);
|
||||
$this->_tableMetadata[$name] = $metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves table metadata to cache.
|
||||
* @param Cache|null $cache
|
||||
* @param string $name
|
||||
*/
|
||||
private function saveTableMetadataToCache($cache, $name)
|
||||
{
|
||||
if ($cache === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$metadata = $this->_tableMetadata[$name];
|
||||
$metadata['cacheVersion'] = static::SCHEMA_CACHE_VERSION;
|
||||
$cache->set(
|
||||
$this->getCacheKey($name),
|
||||
$metadata,
|
||||
$this->db->schemaCacheDuration,
|
||||
new TagDependency(['tags' => $this->getCacheTag()])
|
||||
);
|
||||
}
|
||||
}
|
||||
308
vendor/yiisoft/yii2/db/SchemaBuilderTrait.php
vendored
Normal file
308
vendor/yiisoft/yii2/db/SchemaBuilderTrait.php
vendored
Normal file
@@ -0,0 +1,308 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db;
|
||||
|
||||
/**
|
||||
* SchemaBuilderTrait contains shortcut methods to create instances of [[ColumnSchemaBuilder]].
|
||||
*
|
||||
* These can be used in database migrations to define database schema types using a PHP interface.
|
||||
* This is useful to define a schema in a DBMS independent way so that the application may run on
|
||||
* different DBMS the same way.
|
||||
*
|
||||
* For example you may use the following code inside your migration files:
|
||||
*
|
||||
* ```php
|
||||
* $this->createTable('example_table', [
|
||||
* 'id' => $this->primaryKey(),
|
||||
* 'name' => $this->string(64)->notNull(),
|
||||
* 'type' => $this->integer()->notNull()->defaultValue(10),
|
||||
* 'description' => $this->text(),
|
||||
* 'rule_name' => $this->string(64),
|
||||
* 'data' => $this->text(),
|
||||
* 'created_at' => $this->datetime()->notNull(),
|
||||
* 'updated_at' => $this->datetime(),
|
||||
* ]);
|
||||
* ```
|
||||
*
|
||||
* @author Vasenin Matvey <vaseninm@gmail.com>
|
||||
* @since 2.0.6
|
||||
*/
|
||||
trait SchemaBuilderTrait
|
||||
{
|
||||
/**
|
||||
* @return Connection the database connection to be used for schema building.
|
||||
*/
|
||||
abstract protected function getDb();
|
||||
|
||||
/**
|
||||
* Creates a primary key column.
|
||||
* @param int $length column size or precision definition.
|
||||
* This parameter will be ignored if not supported by the DBMS.
|
||||
* @return ColumnSchemaBuilder the column instance which can be further customized.
|
||||
* @since 2.0.6
|
||||
*/
|
||||
public function primaryKey($length = null)
|
||||
{
|
||||
return $this->getDb()->getSchema()->createColumnSchemaBuilder(Schema::TYPE_PK, $length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a big primary key column.
|
||||
* @param int $length column size or precision definition.
|
||||
* This parameter will be ignored if not supported by the DBMS.
|
||||
* @return ColumnSchemaBuilder the column instance which can be further customized.
|
||||
* @since 2.0.6
|
||||
*/
|
||||
public function bigPrimaryKey($length = null)
|
||||
{
|
||||
return $this->getDb()->getSchema()->createColumnSchemaBuilder(Schema::TYPE_BIGPK, $length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a char column.
|
||||
* @param int $length column size definition i.e. the maximum string length.
|
||||
* This parameter will be ignored if not supported by the DBMS.
|
||||
* @return ColumnSchemaBuilder the column instance which can be further customized.
|
||||
* @since 2.0.8
|
||||
*/
|
||||
public function char($length = null)
|
||||
{
|
||||
return $this->getDb()->getSchema()->createColumnSchemaBuilder(Schema::TYPE_CHAR, $length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a string column.
|
||||
* @param int $length column size definition i.e. the maximum string length.
|
||||
* This parameter will be ignored if not supported by the DBMS.
|
||||
* @return ColumnSchemaBuilder the column instance which can be further customized.
|
||||
* @since 2.0.6
|
||||
*/
|
||||
public function string($length = null)
|
||||
{
|
||||
return $this->getDb()->getSchema()->createColumnSchemaBuilder(Schema::TYPE_STRING, $length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a text column.
|
||||
* @return ColumnSchemaBuilder the column instance which can be further customized.
|
||||
* @since 2.0.6
|
||||
*/
|
||||
public function text()
|
||||
{
|
||||
return $this->getDb()->getSchema()->createColumnSchemaBuilder(Schema::TYPE_TEXT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a tinyint column. If tinyint is not supported by the DBMS, smallint will be used.
|
||||
* @param int $length column size or precision definition.
|
||||
* This parameter will be ignored if not supported by the DBMS.
|
||||
* @return ColumnSchemaBuilder the column instance which can be further customized.
|
||||
* @since 2.0.14
|
||||
*/
|
||||
public function tinyInteger($length = null)
|
||||
{
|
||||
return $this->getDb()->getSchema()->createColumnSchemaBuilder(Schema::TYPE_TINYINT, $length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a smallint column.
|
||||
* @param int $length column size or precision definition.
|
||||
* This parameter will be ignored if not supported by the DBMS.
|
||||
* @return ColumnSchemaBuilder the column instance which can be further customized.
|
||||
* @since 2.0.6
|
||||
*/
|
||||
public function smallInteger($length = null)
|
||||
{
|
||||
return $this->getDb()->getSchema()->createColumnSchemaBuilder(Schema::TYPE_SMALLINT, $length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an integer column.
|
||||
* @param int $length column size or precision definition.
|
||||
* This parameter will be ignored if not supported by the DBMS.
|
||||
* @return ColumnSchemaBuilder the column instance which can be further customized.
|
||||
* @since 2.0.6
|
||||
*/
|
||||
public function integer($length = null)
|
||||
{
|
||||
return $this->getDb()->getSchema()->createColumnSchemaBuilder(Schema::TYPE_INTEGER, $length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a bigint column.
|
||||
* @param int $length column size or precision definition.
|
||||
* This parameter will be ignored if not supported by the DBMS.
|
||||
* @return ColumnSchemaBuilder the column instance which can be further customized.
|
||||
* @since 2.0.6
|
||||
*/
|
||||
public function bigInteger($length = null)
|
||||
{
|
||||
return $this->getDb()->getSchema()->createColumnSchemaBuilder(Schema::TYPE_BIGINT, $length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a float column.
|
||||
* @param int $precision column value precision. First parameter passed to the column type, e.g. FLOAT(precision).
|
||||
* This parameter will be ignored if not supported by the DBMS.
|
||||
* @return ColumnSchemaBuilder the column instance which can be further customized.
|
||||
* @since 2.0.6
|
||||
*/
|
||||
public function float($precision = null)
|
||||
{
|
||||
return $this->getDb()->getSchema()->createColumnSchemaBuilder(Schema::TYPE_FLOAT, $precision);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a double column.
|
||||
* @param int $precision column value precision. First parameter passed to the column type, e.g. DOUBLE(precision).
|
||||
* This parameter will be ignored if not supported by the DBMS.
|
||||
* @return ColumnSchemaBuilder the column instance which can be further customized.
|
||||
* @since 2.0.6
|
||||
*/
|
||||
public function double($precision = null)
|
||||
{
|
||||
return $this->getDb()->getSchema()->createColumnSchemaBuilder(Schema::TYPE_DOUBLE, $precision);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a decimal column.
|
||||
* @param int $precision column value precision, which is usually the total number of digits.
|
||||
* First parameter passed to the column type, e.g. DECIMAL(precision, scale).
|
||||
* This parameter will be ignored if not supported by the DBMS.
|
||||
* @param int $scale column value scale, which is usually the number of digits after the decimal point.
|
||||
* Second parameter passed to the column type, e.g. DECIMAL(precision, scale).
|
||||
* This parameter will be ignored if not supported by the DBMS.
|
||||
* @return ColumnSchemaBuilder the column instance which can be further customized.
|
||||
* @since 2.0.6
|
||||
*/
|
||||
public function decimal($precision = null, $scale = null)
|
||||
{
|
||||
$length = [];
|
||||
if ($precision !== null) {
|
||||
$length[] = $precision;
|
||||
}
|
||||
if ($scale !== null) {
|
||||
$length[] = $scale;
|
||||
}
|
||||
|
||||
return $this->getDb()->getSchema()->createColumnSchemaBuilder(Schema::TYPE_DECIMAL, $length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a datetime column.
|
||||
* @param int $precision column value precision. First parameter passed to the column type, e.g. DATETIME(precision).
|
||||
* This parameter will be ignored if not supported by the DBMS.
|
||||
* @return ColumnSchemaBuilder the column instance which can be further customized.
|
||||
* @since 2.0.6
|
||||
*/
|
||||
public function dateTime($precision = null)
|
||||
{
|
||||
return $this->getDb()->getSchema()->createColumnSchemaBuilder(Schema::TYPE_DATETIME, $precision);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a timestamp column.
|
||||
* @param int $precision column value precision. First parameter passed to the column type, e.g. TIMESTAMP(precision).
|
||||
* This parameter will be ignored if not supported by the DBMS.
|
||||
* @return ColumnSchemaBuilder the column instance which can be further customized.
|
||||
* @since 2.0.6
|
||||
*/
|
||||
public function timestamp($precision = null)
|
||||
{
|
||||
return $this->getDb()->getSchema()->createColumnSchemaBuilder(Schema::TYPE_TIMESTAMP, $precision);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a time column.
|
||||
* @param int $precision column value precision. First parameter passed to the column type, e.g. TIME(precision).
|
||||
* This parameter will be ignored if not supported by the DBMS.
|
||||
* @return ColumnSchemaBuilder the column instance which can be further customized.
|
||||
* @since 2.0.6
|
||||
*/
|
||||
public function time($precision = null)
|
||||
{
|
||||
return $this->getDb()->getSchema()->createColumnSchemaBuilder(Schema::TYPE_TIME, $precision);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a date column.
|
||||
* @return ColumnSchemaBuilder the column instance which can be further customized.
|
||||
* @since 2.0.6
|
||||
*/
|
||||
public function date()
|
||||
{
|
||||
return $this->getDb()->getSchema()->createColumnSchemaBuilder(Schema::TYPE_DATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a binary column.
|
||||
* @param int $length column size or precision definition.
|
||||
* This parameter will be ignored if not supported by the DBMS.
|
||||
* @return ColumnSchemaBuilder the column instance which can be further customized.
|
||||
* @since 2.0.6
|
||||
*/
|
||||
public function binary($length = null)
|
||||
{
|
||||
return $this->getDb()->getSchema()->createColumnSchemaBuilder(Schema::TYPE_BINARY, $length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a boolean column.
|
||||
* @return ColumnSchemaBuilder the column instance which can be further customized.
|
||||
* @since 2.0.6
|
||||
*/
|
||||
public function boolean()
|
||||
{
|
||||
return $this->getDb()->getSchema()->createColumnSchemaBuilder(Schema::TYPE_BOOLEAN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a money column.
|
||||
* @param int $precision column value precision, which is usually the total number of digits.
|
||||
* First parameter passed to the column type, e.g. DECIMAL(precision, scale).
|
||||
* This parameter will be ignored if not supported by the DBMS.
|
||||
* @param int $scale column value scale, which is usually the number of digits after the decimal point.
|
||||
* Second parameter passed to the column type, e.g. DECIMAL(precision, scale).
|
||||
* This parameter will be ignored if not supported by the DBMS.
|
||||
* @return ColumnSchemaBuilder the column instance which can be further customized.
|
||||
* @since 2.0.6
|
||||
*/
|
||||
public function money($precision = null, $scale = null)
|
||||
{
|
||||
$length = [];
|
||||
if ($precision !== null) {
|
||||
$length[] = $precision;
|
||||
}
|
||||
if ($scale !== null) {
|
||||
$length[] = $scale;
|
||||
}
|
||||
|
||||
return $this->getDb()->getSchema()->createColumnSchemaBuilder(Schema::TYPE_MONEY, $length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a JSON column.
|
||||
* @return ColumnSchemaBuilder the column instance which can be further customized.
|
||||
* @since 2.0.14
|
||||
* @throws \yii\base\Exception
|
||||
*/
|
||||
public function json()
|
||||
{
|
||||
/*
|
||||
* TODO Remove in Yii 2.1
|
||||
*
|
||||
* Disabled due to bug in MySQL extension
|
||||
* @link https://bugs.php.net/bug.php?id=70384
|
||||
*/
|
||||
if (version_compare(PHP_VERSION, '5.6', '<') && $this->getDb()->getDriverName() === 'mysql') {
|
||||
throw new \yii\base\Exception('JSON column type is not supported in PHP < 5.6');
|
||||
}
|
||||
|
||||
return $this->getDb()->getSchema()->createColumnSchemaBuilder(Schema::TYPE_JSON);
|
||||
}
|
||||
}
|
||||
312
vendor/yiisoft/yii2/db/SqlToken.php
vendored
Normal file
312
vendor/yiisoft/yii2/db/SqlToken.php
vendored
Normal file
@@ -0,0 +1,312 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db;
|
||||
|
||||
use yii\base\BaseObject;
|
||||
|
||||
/**
|
||||
* SqlToken represents SQL tokens produced by [[SqlTokenizer]] or its child classes.
|
||||
*
|
||||
* @property SqlToken[] $children Child tokens.
|
||||
* @property bool $hasChildren Whether the token has children. This property is read-only.
|
||||
* @property bool $isCollection Whether the token represents a collection of tokens. This property is
|
||||
* read-only.
|
||||
* @property string $sql SQL code. This property is read-only.
|
||||
*
|
||||
* @author Sergey Makinen <sergey@makinen.ru>
|
||||
* @since 2.0.13
|
||||
*/
|
||||
class SqlToken extends BaseObject implements \ArrayAccess
|
||||
{
|
||||
const TYPE_CODE = 0;
|
||||
const TYPE_STATEMENT = 1;
|
||||
const TYPE_TOKEN = 2;
|
||||
const TYPE_PARENTHESIS = 3;
|
||||
const TYPE_KEYWORD = 4;
|
||||
const TYPE_OPERATOR = 5;
|
||||
const TYPE_IDENTIFIER = 6;
|
||||
const TYPE_STRING_LITERAL = 7;
|
||||
|
||||
/**
|
||||
* @var int token type. It has to be one of the following constants:
|
||||
*
|
||||
* - [[TYPE_CODE]]
|
||||
* - [[TYPE_STATEMENT]]
|
||||
* - [[TYPE_TOKEN]]
|
||||
* - [[TYPE_PARENTHESIS]]
|
||||
* - [[TYPE_KEYWORD]]
|
||||
* - [[TYPE_OPERATOR]]
|
||||
* - [[TYPE_IDENTIFIER]]
|
||||
* - [[TYPE_STRING_LITERAL]]
|
||||
*/
|
||||
public $type = self::TYPE_TOKEN;
|
||||
/**
|
||||
* @var string|null token content.
|
||||
*/
|
||||
public $content;
|
||||
/**
|
||||
* @var int original SQL token start position.
|
||||
*/
|
||||
public $startOffset;
|
||||
/**
|
||||
* @var int original SQL token end position.
|
||||
*/
|
||||
public $endOffset;
|
||||
/**
|
||||
* @var SqlToken parent token.
|
||||
*/
|
||||
public $parent;
|
||||
|
||||
/**
|
||||
* @var SqlToken[] token children.
|
||||
*/
|
||||
private $_children = [];
|
||||
|
||||
|
||||
/**
|
||||
* Returns the SQL code representing the token.
|
||||
* @return string SQL code.
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->getSql();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether there is a child token at the specified offset.
|
||||
* This method is required by the SPL [[\ArrayAccess]] interface.
|
||||
* It is implicitly called when you use something like `isset($token[$offset])`.
|
||||
* @param int $offset child token offset.
|
||||
* @return bool whether the token exists.
|
||||
*/
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return isset($this->_children[$this->calculateOffset($offset)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a child token at the specified offset.
|
||||
* This method is required by the SPL [[\ArrayAccess]] interface.
|
||||
* It is implicitly called when you use something like `$child = $token[$offset];`.
|
||||
* @param int $offset child token offset.
|
||||
* @return SqlToken|null the child token at the specified offset, `null` if there's no token.
|
||||
*/
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
$offset = $this->calculateOffset($offset);
|
||||
return isset($this->_children[$offset]) ? $this->_children[$offset] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a child token to the token.
|
||||
* This method is required by the SPL [[\ArrayAccess]] interface.
|
||||
* It is implicitly called when you use something like `$token[$offset] = $child;`.
|
||||
* @param int|null $offset child token offset.
|
||||
* @param SqlToken $token token to be added.
|
||||
*/
|
||||
public function offsetSet($offset, $token)
|
||||
{
|
||||
$token->parent = $this;
|
||||
if ($offset === null) {
|
||||
$this->_children[] = $token;
|
||||
} else {
|
||||
$this->_children[$this->calculateOffset($offset)] = $token;
|
||||
}
|
||||
$this->updateCollectionOffsets();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a child token at the specified offset.
|
||||
* This method is required by the SPL [[\ArrayAccess]] interface.
|
||||
* It is implicitly called when you use something like `unset($token[$offset])`.
|
||||
* @param int $offset child token offset.
|
||||
*/
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
$offset = $this->calculateOffset($offset);
|
||||
if (isset($this->_children[$offset])) {
|
||||
array_splice($this->_children, $offset, 1);
|
||||
}
|
||||
$this->updateCollectionOffsets();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns child tokens.
|
||||
* @return SqlToken[] child tokens.
|
||||
*/
|
||||
public function getChildren()
|
||||
{
|
||||
return $this->_children;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a list of child tokens.
|
||||
* @param SqlToken[] $children child tokens.
|
||||
*/
|
||||
public function setChildren($children)
|
||||
{
|
||||
$this->_children = [];
|
||||
foreach ($children as $child) {
|
||||
$child->parent = $this;
|
||||
$this->_children[] = $child;
|
||||
}
|
||||
$this->updateCollectionOffsets();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the token represents a collection of tokens.
|
||||
* @return bool whether the token represents a collection of tokens.
|
||||
*/
|
||||
public function getIsCollection()
|
||||
{
|
||||
return in_array($this->type, [
|
||||
self::TYPE_CODE,
|
||||
self::TYPE_STATEMENT,
|
||||
self::TYPE_PARENTHESIS,
|
||||
], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the token represents a collection of tokens and has non-zero number of children.
|
||||
* @return bool whether the token has children.
|
||||
*/
|
||||
public function getHasChildren()
|
||||
{
|
||||
return $this->getIsCollection() && !empty($this->_children);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the SQL code representing the token.
|
||||
* @return string SQL code.
|
||||
*/
|
||||
public function getSql()
|
||||
{
|
||||
$code = $this;
|
||||
while ($code->parent !== null) {
|
||||
$code = $code->parent;
|
||||
}
|
||||
|
||||
return mb_substr($code->content, $this->startOffset, $this->endOffset - $this->startOffset, 'UTF-8');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this token (including its children) matches the specified "pattern" SQL code.
|
||||
*
|
||||
* Usage Example:
|
||||
*
|
||||
* ```php
|
||||
* $patternToken = (new \yii\db\sqlite\SqlTokenizer('SELECT any FROM any'))->tokenize();
|
||||
* if ($sqlToken->matches($patternToken, 0, $firstMatchIndex, $lastMatchIndex)) {
|
||||
* // ...
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param SqlToken $patternToken tokenized SQL code to match against. In addition to normal SQL, the
|
||||
* `any` keyword is supported which will match any number of keywords, identifiers, whitespaces.
|
||||
* @param int $offset token children offset to start lookup with.
|
||||
* @param int|null $firstMatchIndex token children offset where a successful match begins.
|
||||
* @param int|null $lastMatchIndex token children offset where a successful match ends.
|
||||
* @return bool whether this token matches the pattern SQL code.
|
||||
*/
|
||||
public function matches(SqlToken $patternToken, $offset = 0, &$firstMatchIndex = null, &$lastMatchIndex = null)
|
||||
{
|
||||
if (!$patternToken->getHasChildren()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$patternToken = $patternToken[0];
|
||||
return $this->tokensMatch($patternToken, $this, $offset, $firstMatchIndex, $lastMatchIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the given token to match the specified pattern token.
|
||||
* @param SqlToken $patternToken
|
||||
* @param SqlToken $token
|
||||
* @param int $offset
|
||||
* @param int|null $firstMatchIndex
|
||||
* @param int|null $lastMatchIndex
|
||||
* @return bool
|
||||
*/
|
||||
private function tokensMatch(SqlToken $patternToken, SqlToken $token, $offset = 0, &$firstMatchIndex = null, &$lastMatchIndex = null)
|
||||
{
|
||||
if (
|
||||
$patternToken->getIsCollection() !== $token->getIsCollection()
|
||||
|| (!$patternToken->getIsCollection() && $patternToken->content !== $token->content)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($patternToken->children === $token->children) {
|
||||
$firstMatchIndex = $lastMatchIndex = $offset;
|
||||
return true;
|
||||
}
|
||||
|
||||
$firstMatchIndex = $lastMatchIndex = null;
|
||||
$wildcard = false;
|
||||
for ($index = 0, $count = count($patternToken->children); $index < $count; $index++) {
|
||||
// Here we iterate token by token with an exception of "any" that toggles
|
||||
// an iteration until we matched with a next pattern token or EOF.
|
||||
if ($patternToken[$index]->content === 'any') {
|
||||
$wildcard = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
for ($limit = $wildcard ? count($token->children) : $offset + 1; $offset < $limit; $offset++) {
|
||||
if (!$wildcard && !isset($token[$offset])) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!$this->tokensMatch($patternToken[$index], $token[$offset])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($firstMatchIndex === null) {
|
||||
$firstMatchIndex = $offset;
|
||||
$lastMatchIndex = $offset;
|
||||
} else {
|
||||
$lastMatchIndex = $offset;
|
||||
}
|
||||
$wildcard = false;
|
||||
$offset++;
|
||||
continue 2;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an absolute offset in the children array.
|
||||
* @param int $offset
|
||||
* @return int
|
||||
*/
|
||||
private function calculateOffset($offset)
|
||||
{
|
||||
if ($offset >= 0) {
|
||||
return $offset;
|
||||
}
|
||||
|
||||
return count($this->_children) + $offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates token SQL code start and end offsets based on its children.
|
||||
*/
|
||||
private function updateCollectionOffsets()
|
||||
{
|
||||
if (!empty($this->_children)) {
|
||||
$this->startOffset = reset($this->_children)->startOffset;
|
||||
$this->endOffset = end($this->_children)->endOffset;
|
||||
}
|
||||
if ($this->parent !== null) {
|
||||
$this->parent->updateCollectionOffsets();
|
||||
}
|
||||
}
|
||||
}
|
||||
394
vendor/yiisoft/yii2/db/SqlTokenizer.php
vendored
Normal file
394
vendor/yiisoft/yii2/db/SqlTokenizer.php
vendored
Normal file
@@ -0,0 +1,394 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db;
|
||||
|
||||
use yii\base\Component;
|
||||
use yii\base\InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* SqlTokenizer splits an SQL query into individual SQL tokens.
|
||||
*
|
||||
* It can be used to obtain an addition information from an SQL code.
|
||||
*
|
||||
* Usage example:
|
||||
*
|
||||
* ```php
|
||||
* $tokenizer = new SqlTokenizer("SELECT * FROM user WHERE id = 1");
|
||||
* $root = $tokeinzer->tokenize();
|
||||
* $sqlTokens = $root->getChildren();
|
||||
* ```
|
||||
*
|
||||
* Tokens are instances of [[SqlToken]].
|
||||
*
|
||||
* @author Sergey Makinen <sergey@makinen.ru>
|
||||
* @since 2.0.13
|
||||
*/
|
||||
abstract class SqlTokenizer extends Component
|
||||
{
|
||||
/**
|
||||
* @var string SQL code.
|
||||
*/
|
||||
public $sql;
|
||||
|
||||
/**
|
||||
* @var int SQL code string length.
|
||||
*/
|
||||
protected $length;
|
||||
/**
|
||||
* @var int SQL code string current offset.
|
||||
*/
|
||||
protected $offset;
|
||||
|
||||
/**
|
||||
* @var \SplStack stack of active tokens.
|
||||
*/
|
||||
private $_tokenStack;
|
||||
/**
|
||||
* @var SqlToken active token. It's usually a top of the token stack.
|
||||
*/
|
||||
private $_currentToken;
|
||||
/**
|
||||
* @var string[] cached substrings.
|
||||
*/
|
||||
private $_substrings;
|
||||
/**
|
||||
* @var string current buffer value.
|
||||
*/
|
||||
private $_buffer = '';
|
||||
/**
|
||||
* @var SqlToken resulting token of a last [[tokenize()]] call.
|
||||
*/
|
||||
private $_token;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param string $sql SQL code to be tokenized.
|
||||
* @param array $config name-value pairs that will be used to initialize the object properties
|
||||
*/
|
||||
public function __construct($sql, $config = [])
|
||||
{
|
||||
$this->sql = $sql;
|
||||
parent::__construct($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tokenizes and returns a code type token.
|
||||
* @return SqlToken code type token.
|
||||
*/
|
||||
public function tokenize()
|
||||
{
|
||||
$this->length = mb_strlen($this->sql, 'UTF-8');
|
||||
$this->offset = 0;
|
||||
$this->_substrings = [];
|
||||
$this->_buffer = '';
|
||||
$this->_token = new SqlToken([
|
||||
'type' => SqlToken::TYPE_CODE,
|
||||
'content' => $this->sql,
|
||||
]);
|
||||
$this->_tokenStack = new \SplStack();
|
||||
$this->_tokenStack->push($this->_token);
|
||||
$this->_token[] = new SqlToken(['type' => SqlToken::TYPE_STATEMENT]);
|
||||
$this->_tokenStack->push($this->_token[0]);
|
||||
$this->_currentToken = $this->_tokenStack->top();
|
||||
while (!$this->isEof()) {
|
||||
if ($this->isWhitespace($length) || $this->isComment($length)) {
|
||||
$this->addTokenFromBuffer();
|
||||
$this->advance($length);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($this->tokenizeOperator($length) || $this->tokenizeDelimitedString($length)) {
|
||||
$this->advance($length);
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->_buffer .= $this->substring(1);
|
||||
$this->advance(1);
|
||||
}
|
||||
$this->addTokenFromBuffer();
|
||||
if ($this->_token->getHasChildren() && !$this->_token[-1]->getHasChildren()) {
|
||||
unset($this->_token[-1]);
|
||||
}
|
||||
|
||||
return $this->_token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether there's a whitespace at the current offset.
|
||||
* If this methos returns `true`, it has to set the `$length` parameter to the length of the matched string.
|
||||
* @param int $length length of the matched string.
|
||||
* @return bool whether there's a whitespace at the current offset.
|
||||
*/
|
||||
abstract protected function isWhitespace(&$length);
|
||||
|
||||
/**
|
||||
* Returns whether there's a commentary at the current offset.
|
||||
* If this methos returns `true`, it has to set the `$length` parameter to the length of the matched string.
|
||||
* @param int $length length of the matched string.
|
||||
* @return bool whether there's a commentary at the current offset.
|
||||
*/
|
||||
abstract protected function isComment(&$length);
|
||||
|
||||
/**
|
||||
* Returns whether there's an operator at the current offset.
|
||||
* If this methos returns `true`, it has to set the `$length` parameter to the length of the matched string.
|
||||
* It may also set `$content` to a string that will be used as a token content.
|
||||
* @param int $length length of the matched string.
|
||||
* @param string $content optional content instead of the matched string.
|
||||
* @return bool whether there's an operator at the current offset.
|
||||
*/
|
||||
abstract protected function isOperator(&$length, &$content);
|
||||
|
||||
/**
|
||||
* Returns whether there's an identifier at the current offset.
|
||||
* If this methos returns `true`, it has to set the `$length` parameter to the length of the matched string.
|
||||
* It may also set `$content` to a string that will be used as a token content.
|
||||
* @param int $length length of the matched string.
|
||||
* @param string $content optional content instead of the matched string.
|
||||
* @return bool whether there's an identifier at the current offset.
|
||||
*/
|
||||
abstract protected function isIdentifier(&$length, &$content);
|
||||
|
||||
/**
|
||||
* Returns whether there's a string literal at the current offset.
|
||||
* If this methos returns `true`, it has to set the `$length` parameter to the length of the matched string.
|
||||
* It may also set `$content` to a string that will be used as a token content.
|
||||
* @param int $length length of the matched string.
|
||||
* @param string $content optional content instead of the matched string.
|
||||
* @return bool whether there's a string literal at the current offset.
|
||||
*/
|
||||
abstract protected function isStringLiteral(&$length, &$content);
|
||||
|
||||
/**
|
||||
* Returns whether the given string is a keyword.
|
||||
* The method may set `$content` to a string that will be used as a token content.
|
||||
* @param string $string string to be matched.
|
||||
* @param string $content optional content instead of the matched string.
|
||||
* @return bool whether the given string is a keyword.
|
||||
*/
|
||||
abstract protected function isKeyword($string, &$content);
|
||||
|
||||
/**
|
||||
* Returns whether the longest common prefix equals to the SQL code of the same length at the current offset.
|
||||
* @param string[] $with strings to be tested.
|
||||
* The method **will** modify this parameter to speed up lookups.
|
||||
* @param bool $caseSensitive whether to perform a case sensitive comparison.
|
||||
* @param int|null $length length of the matched string.
|
||||
* @param string|null $content matched string.
|
||||
* @return bool whether a match is found.
|
||||
*/
|
||||
protected function startsWithAnyLongest(array &$with, $caseSensitive, &$length = null, &$content = null)
|
||||
{
|
||||
if (empty($with)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!is_array(reset($with))) {
|
||||
usort($with, function ($string1, $string2) {
|
||||
return mb_strlen($string2, 'UTF-8') - mb_strlen($string1, 'UTF-8');
|
||||
});
|
||||
$map = [];
|
||||
foreach ($with as $string) {
|
||||
$map[mb_strlen($string, 'UTF-8')][$caseSensitive ? $string : mb_strtoupper($string, 'UTF-8')] = true;
|
||||
}
|
||||
$with = $map;
|
||||
}
|
||||
foreach ($with as $testLength => $testValues) {
|
||||
$content = $this->substring($testLength, $caseSensitive);
|
||||
if (isset($testValues[$content])) {
|
||||
$length = $testLength;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string of the given length starting with the specified offset.
|
||||
* @param int $length string length to be returned.
|
||||
* @param bool $caseSensitive if it's `false`, the string will be uppercased.
|
||||
* @param int|null $offset SQL code offset, defaults to current if `null` is passed.
|
||||
* @return string result string, it may be empty if there's nothing to return.
|
||||
*/
|
||||
protected function substring($length, $caseSensitive = true, $offset = null)
|
||||
{
|
||||
if ($offset === null) {
|
||||
$offset = $this->offset;
|
||||
}
|
||||
if ($offset + $length > $this->length) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$cacheKey = $offset . ',' . $length;
|
||||
if (!isset($this->_substrings[$cacheKey . ',1'])) {
|
||||
$this->_substrings[$cacheKey . ',1'] = mb_substr($this->sql, $offset, $length, 'UTF-8');
|
||||
}
|
||||
if (!$caseSensitive && !isset($this->_substrings[$cacheKey . ',0'])) {
|
||||
$this->_substrings[$cacheKey . ',0'] = mb_strtoupper($this->_substrings[$cacheKey . ',1'], 'UTF-8');
|
||||
}
|
||||
|
||||
return $this->_substrings[$cacheKey . ',' . (int) $caseSensitive];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an index after the given string in the SQL code starting with the specified offset.
|
||||
* @param string $string string to be found.
|
||||
* @param int|null $offset SQL code offset, defaults to current if `null` is passed.
|
||||
* @return int index after the given string or end of string index.
|
||||
*/
|
||||
protected function indexAfter($string, $offset = null)
|
||||
{
|
||||
if ($offset === null) {
|
||||
$offset = $this->offset;
|
||||
}
|
||||
if ($offset + mb_strlen($string, 'UTF-8') > $this->length) {
|
||||
return $this->length;
|
||||
}
|
||||
|
||||
$afterIndexOf = mb_strpos($this->sql, $string, $offset, 'UTF-8');
|
||||
if ($afterIndexOf === false) {
|
||||
$afterIndexOf = $this->length;
|
||||
} else {
|
||||
$afterIndexOf += mb_strlen($string, 'UTF-8');
|
||||
}
|
||||
|
||||
return $afterIndexOf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether there is a delimited string at the current offset and adds it to the token children.
|
||||
* @param int $length
|
||||
* @return bool
|
||||
*/
|
||||
private function tokenizeDelimitedString(&$length)
|
||||
{
|
||||
$isIdentifier = $this->isIdentifier($length, $content);
|
||||
$isStringLiteral = !$isIdentifier && $this->isStringLiteral($length, $content);
|
||||
if (!$isIdentifier && !$isStringLiteral) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->addTokenFromBuffer();
|
||||
$this->_currentToken[] = new SqlToken([
|
||||
'type' => $isIdentifier ? SqlToken::TYPE_IDENTIFIER : SqlToken::TYPE_STRING_LITERAL,
|
||||
'content' => is_string($content) ? $content : $this->substring($length),
|
||||
'startOffset' => $this->offset,
|
||||
'endOffset' => $this->offset + $length,
|
||||
]);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether there is an operator at the current offset and adds it to the token children.
|
||||
* @param int $length
|
||||
* @return bool
|
||||
*/
|
||||
private function tokenizeOperator(&$length)
|
||||
{
|
||||
if (!$this->isOperator($length, $content)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->addTokenFromBuffer();
|
||||
switch ($this->substring($length)) {
|
||||
case '(':
|
||||
$this->_currentToken[] = new SqlToken([
|
||||
'type' => SqlToken::TYPE_OPERATOR,
|
||||
'content' => is_string($content) ? $content : $this->substring($length),
|
||||
'startOffset' => $this->offset,
|
||||
'endOffset' => $this->offset + $length,
|
||||
]);
|
||||
$this->_currentToken[] = new SqlToken(['type' => SqlToken::TYPE_PARENTHESIS]);
|
||||
$this->_tokenStack->push($this->_currentToken[-1]);
|
||||
$this->_currentToken = $this->_tokenStack->top();
|
||||
break;
|
||||
case ')':
|
||||
$this->_tokenStack->pop();
|
||||
$this->_currentToken = $this->_tokenStack->top();
|
||||
$this->_currentToken[] = new SqlToken([
|
||||
'type' => SqlToken::TYPE_OPERATOR,
|
||||
'content' => ')',
|
||||
'startOffset' => $this->offset,
|
||||
'endOffset' => $this->offset + $length,
|
||||
]);
|
||||
break;
|
||||
case ';':
|
||||
if (!$this->_currentToken->getHasChildren()) {
|
||||
break;
|
||||
}
|
||||
|
||||
$this->_currentToken[] = new SqlToken([
|
||||
'type' => SqlToken::TYPE_OPERATOR,
|
||||
'content' => is_string($content) ? $content : $this->substring($length),
|
||||
'startOffset' => $this->offset,
|
||||
'endOffset' => $this->offset + $length,
|
||||
]);
|
||||
$this->_tokenStack->pop();
|
||||
$this->_currentToken = $this->_tokenStack->top();
|
||||
$this->_currentToken[] = new SqlToken(['type' => SqlToken::TYPE_STATEMENT]);
|
||||
$this->_tokenStack->push($this->_currentToken[-1]);
|
||||
$this->_currentToken = $this->_tokenStack->top();
|
||||
break;
|
||||
default:
|
||||
$this->_currentToken[] = new SqlToken([
|
||||
'type' => SqlToken::TYPE_OPERATOR,
|
||||
'content' => is_string($content) ? $content : $this->substring($length),
|
||||
'startOffset' => $this->offset,
|
||||
'endOffset' => $this->offset + $length,
|
||||
]);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines a type of text in the buffer, tokenizes it and adds it to the token children.
|
||||
*/
|
||||
private function addTokenFromBuffer()
|
||||
{
|
||||
if ($this->_buffer === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
$isKeyword = $this->isKeyword($this->_buffer, $content);
|
||||
$this->_currentToken[] = new SqlToken([
|
||||
'type' => $isKeyword ? SqlToken::TYPE_KEYWORD : SqlToken::TYPE_TOKEN,
|
||||
'content' => is_string($content) ? $content : $this->_buffer,
|
||||
'startOffset' => $this->offset - mb_strlen($this->_buffer, 'UTF-8'),
|
||||
'endOffset' => $this->offset,
|
||||
]);
|
||||
$this->_buffer = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the specified length to the current offset.
|
||||
* @param int $length
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
private function advance($length)
|
||||
{
|
||||
if ($length <= 0) {
|
||||
throw new InvalidArgumentException('Length must be greater than 0.');
|
||||
}
|
||||
|
||||
$this->offset += $length;
|
||||
$this->_substrings = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the SQL code is completely traversed.
|
||||
* @return bool
|
||||
*/
|
||||
private function isEof()
|
||||
{
|
||||
return $this->offset >= $this->length;
|
||||
}
|
||||
}
|
||||
23
vendor/yiisoft/yii2/db/StaleObjectException.php
vendored
Normal file
23
vendor/yiisoft/yii2/db/StaleObjectException.php
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db;
|
||||
|
||||
/**
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class StaleObjectException extends Exception
|
||||
{
|
||||
/**
|
||||
* @return string the user-friendly name of this exception
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'Stale Object Exception';
|
||||
}
|
||||
}
|
||||
103
vendor/yiisoft/yii2/db/TableSchema.php
vendored
Normal file
103
vendor/yiisoft/yii2/db/TableSchema.php
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db;
|
||||
|
||||
use yii\base\BaseObject;
|
||||
use yii\base\InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* TableSchema represents the metadata of a database table.
|
||||
*
|
||||
* @property array $columnNames List of column names. This property is read-only.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class TableSchema extends BaseObject
|
||||
{
|
||||
/**
|
||||
* @var string the name of the schema that this table belongs to.
|
||||
*/
|
||||
public $schemaName;
|
||||
/**
|
||||
* @var string the name of this table. The schema name is not included. Use [[fullName]] to get the name with schema name prefix.
|
||||
*/
|
||||
public $name;
|
||||
/**
|
||||
* @var string the full name of this table, which includes the schema name prefix, if any.
|
||||
* Note that if the schema name is the same as the [[Schema::defaultSchema|default schema name]],
|
||||
* the schema name will not be included.
|
||||
*/
|
||||
public $fullName;
|
||||
/**
|
||||
* @var string[] primary keys of this table.
|
||||
*/
|
||||
public $primaryKey = [];
|
||||
/**
|
||||
* @var string sequence name for the primary key. Null if no sequence.
|
||||
*/
|
||||
public $sequenceName;
|
||||
/**
|
||||
* @var array foreign keys of this table. Each array element is of the following structure:
|
||||
*
|
||||
* ```php
|
||||
* [
|
||||
* 'ForeignTableName',
|
||||
* 'fk1' => 'pk1', // pk1 is in foreign table
|
||||
* 'fk2' => 'pk2', // if composite foreign key
|
||||
* ]
|
||||
* ```
|
||||
*/
|
||||
public $foreignKeys = [];
|
||||
/**
|
||||
* @var ColumnSchema[] column metadata of this table. Each array element is a [[ColumnSchema]] object, indexed by column names.
|
||||
*/
|
||||
public $columns = [];
|
||||
|
||||
|
||||
/**
|
||||
* Gets the named column metadata.
|
||||
* This is a convenient method for retrieving a named column even if it does not exist.
|
||||
* @param string $name column name
|
||||
* @return ColumnSchema metadata of the named column. Null if the named column does not exist.
|
||||
*/
|
||||
public function getColumn($name)
|
||||
{
|
||||
return isset($this->columns[$name]) ? $this->columns[$name] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the names of all columns in this table.
|
||||
* @return array list of column names
|
||||
*/
|
||||
public function getColumnNames()
|
||||
{
|
||||
return array_keys($this->columns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Manually specifies the primary key for this table.
|
||||
* @param string|array $keys the primary key (can be composite)
|
||||
* @throws InvalidArgumentException if the specified key cannot be found in the table.
|
||||
*/
|
||||
public function fixPrimaryKey($keys)
|
||||
{
|
||||
$keys = (array) $keys;
|
||||
$this->primaryKey = $keys;
|
||||
foreach ($this->columns as $column) {
|
||||
$column->isPrimaryKey = false;
|
||||
}
|
||||
foreach ($keys as $key) {
|
||||
if (isset($this->columns[$key])) {
|
||||
$this->columns[$key]->isPrimaryKey = true;
|
||||
} else {
|
||||
throw new InvalidArgumentException("Primary key '$key' cannot be found in table '{$this->name}'.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
231
vendor/yiisoft/yii2/db/Transaction.php
vendored
Normal file
231
vendor/yiisoft/yii2/db/Transaction.php
vendored
Normal file
@@ -0,0 +1,231 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db;
|
||||
|
||||
use Yii;
|
||||
use yii\base\InvalidConfigException;
|
||||
|
||||
/**
|
||||
* Transaction represents a DB transaction.
|
||||
*
|
||||
* It is usually created by calling [[Connection::beginTransaction()]].
|
||||
*
|
||||
* The following code is a typical example of using transactions (note that some
|
||||
* DBMS may not support transactions):
|
||||
*
|
||||
* ```php
|
||||
* $transaction = $connection->beginTransaction();
|
||||
* try {
|
||||
* $connection->createCommand($sql1)->execute();
|
||||
* $connection->createCommand($sql2)->execute();
|
||||
* //.... other SQL executions
|
||||
* $transaction->commit();
|
||||
* } catch (\Exception $e) {
|
||||
* $transaction->rollBack();
|
||||
* throw $e;
|
||||
* } catch (\Throwable $e) {
|
||||
* $transaction->rollBack();
|
||||
* throw $e;
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* > Note: in the above code we have two catch-blocks for compatibility
|
||||
* > with PHP 5.x and PHP 7.x. `\Exception` implements the [`\Throwable` interface](http://php.net/manual/en/class.throwable.php)
|
||||
* > since PHP 7.0, so you can skip the part with `\Exception` if your app uses only PHP 7.0 and higher.
|
||||
*
|
||||
* @property bool $isActive Whether this transaction is active. Only an active transaction can [[commit()]] or
|
||||
* [[rollBack()]]. This property is read-only.
|
||||
* @property string $isolationLevel The transaction isolation level to use for this transaction. This can be
|
||||
* one of [[READ_UNCOMMITTED]], [[READ_COMMITTED]], [[REPEATABLE_READ]] and [[SERIALIZABLE]] but also a string
|
||||
* containing DBMS specific syntax to be used after `SET TRANSACTION ISOLATION LEVEL`. This property is
|
||||
* write-only.
|
||||
* @property int $level The current nesting level of the transaction. This property is read-only.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class Transaction extends \yii\base\BaseObject
|
||||
{
|
||||
/**
|
||||
* A constant representing the transaction isolation level `READ UNCOMMITTED`.
|
||||
* @see http://en.wikipedia.org/wiki/Isolation_%28database_systems%29#Isolation_levels
|
||||
*/
|
||||
const READ_UNCOMMITTED = 'READ UNCOMMITTED';
|
||||
/**
|
||||
* A constant representing the transaction isolation level `READ COMMITTED`.
|
||||
* @see http://en.wikipedia.org/wiki/Isolation_%28database_systems%29#Isolation_levels
|
||||
*/
|
||||
const READ_COMMITTED = 'READ COMMITTED';
|
||||
/**
|
||||
* A constant representing the transaction isolation level `REPEATABLE READ`.
|
||||
* @see http://en.wikipedia.org/wiki/Isolation_%28database_systems%29#Isolation_levels
|
||||
*/
|
||||
const REPEATABLE_READ = 'REPEATABLE READ';
|
||||
/**
|
||||
* A constant representing the transaction isolation level `SERIALIZABLE`.
|
||||
* @see http://en.wikipedia.org/wiki/Isolation_%28database_systems%29#Isolation_levels
|
||||
*/
|
||||
const SERIALIZABLE = 'SERIALIZABLE';
|
||||
|
||||
/**
|
||||
* @var Connection the database connection that this transaction is associated with.
|
||||
*/
|
||||
public $db;
|
||||
|
||||
/**
|
||||
* @var int the nesting level of the transaction. 0 means the outermost level.
|
||||
*/
|
||||
private $_level = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Returns a value indicating whether this transaction is active.
|
||||
* @return bool whether this transaction is active. Only an active transaction
|
||||
* can [[commit()]] or [[rollBack()]].
|
||||
*/
|
||||
public function getIsActive()
|
||||
{
|
||||
return $this->_level > 0 && $this->db && $this->db->isActive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Begins a transaction.
|
||||
* @param string|null $isolationLevel The [isolation level][] to use for this transaction.
|
||||
* This can be one of [[READ_UNCOMMITTED]], [[READ_COMMITTED]], [[REPEATABLE_READ]] and [[SERIALIZABLE]] but
|
||||
* also a string containing DBMS specific syntax to be used after `SET TRANSACTION ISOLATION LEVEL`.
|
||||
* If not specified (`null`) the isolation level will not be set explicitly and the DBMS default will be used.
|
||||
*
|
||||
* > Note: This setting does not work for PostgreSQL, where setting the isolation level before the transaction
|
||||
* has no effect. You have to call [[setIsolationLevel()]] in this case after the transaction has started.
|
||||
*
|
||||
* > Note: Some DBMS allow setting of the isolation level only for the whole connection so subsequent transactions
|
||||
* may get the same isolation level even if you did not specify any. When using this feature
|
||||
* you may need to set the isolation level for all transactions explicitly to avoid conflicting settings.
|
||||
* At the time of this writing affected DBMS are MSSQL and SQLite.
|
||||
*
|
||||
* [isolation level]: http://en.wikipedia.org/wiki/Isolation_%28database_systems%29#Isolation_levels
|
||||
* @throws InvalidConfigException if [[db]] is `null`.
|
||||
*/
|
||||
public function begin($isolationLevel = null)
|
||||
{
|
||||
if ($this->db === null) {
|
||||
throw new InvalidConfigException('Transaction::db must be set.');
|
||||
}
|
||||
$this->db->open();
|
||||
|
||||
if ($this->_level === 0) {
|
||||
if ($isolationLevel !== null) {
|
||||
$this->db->getSchema()->setTransactionIsolationLevel($isolationLevel);
|
||||
}
|
||||
Yii::debug('Begin transaction' . ($isolationLevel ? ' with isolation level ' . $isolationLevel : ''), __METHOD__);
|
||||
|
||||
$this->db->trigger(Connection::EVENT_BEGIN_TRANSACTION);
|
||||
$this->db->pdo->beginTransaction();
|
||||
$this->_level = 1;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$schema = $this->db->getSchema();
|
||||
if ($schema->supportsSavepoint()) {
|
||||
Yii::debug('Set savepoint ' . $this->_level, __METHOD__);
|
||||
$schema->createSavepoint('LEVEL' . $this->_level);
|
||||
} else {
|
||||
Yii::info('Transaction not started: nested transaction not supported', __METHOD__);
|
||||
}
|
||||
$this->_level++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Commits a transaction.
|
||||
* @throws Exception if the transaction is not active
|
||||
*/
|
||||
public function commit()
|
||||
{
|
||||
if (!$this->getIsActive()) {
|
||||
throw new Exception('Failed to commit transaction: transaction was inactive.');
|
||||
}
|
||||
|
||||
$this->_level--;
|
||||
if ($this->_level === 0) {
|
||||
Yii::debug('Commit transaction', __METHOD__);
|
||||
$this->db->pdo->commit();
|
||||
$this->db->trigger(Connection::EVENT_COMMIT_TRANSACTION);
|
||||
return;
|
||||
}
|
||||
|
||||
$schema = $this->db->getSchema();
|
||||
if ($schema->supportsSavepoint()) {
|
||||
Yii::debug('Release savepoint ' . $this->_level, __METHOD__);
|
||||
$schema->releaseSavepoint('LEVEL' . $this->_level);
|
||||
} else {
|
||||
Yii::info('Transaction not committed: nested transaction not supported', __METHOD__);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rolls back a transaction.
|
||||
* @throws Exception if the transaction is not active
|
||||
*/
|
||||
public function rollBack()
|
||||
{
|
||||
if (!$this->getIsActive()) {
|
||||
// do nothing if transaction is not active: this could be the transaction is committed
|
||||
// but the event handler to "commitTransaction" throw an exception
|
||||
return;
|
||||
}
|
||||
|
||||
$this->_level--;
|
||||
if ($this->_level === 0) {
|
||||
Yii::debug('Roll back transaction', __METHOD__);
|
||||
$this->db->pdo->rollBack();
|
||||
$this->db->trigger(Connection::EVENT_ROLLBACK_TRANSACTION);
|
||||
return;
|
||||
}
|
||||
|
||||
$schema = $this->db->getSchema();
|
||||
if ($schema->supportsSavepoint()) {
|
||||
Yii::debug('Roll back to savepoint ' . $this->_level, __METHOD__);
|
||||
$schema->rollBackSavepoint('LEVEL' . $this->_level);
|
||||
} else {
|
||||
Yii::info('Transaction not rolled back: nested transaction not supported', __METHOD__);
|
||||
// throw an exception to fail the outer transaction
|
||||
throw new Exception('Roll back failed: nested transaction not supported.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the transaction isolation level for this transaction.
|
||||
*
|
||||
* This method can be used to set the isolation level while the transaction is already active.
|
||||
* However this is not supported by all DBMS so you might rather specify the isolation level directly
|
||||
* when calling [[begin()]].
|
||||
* @param string $level The transaction isolation level to use for this transaction.
|
||||
* This can be one of [[READ_UNCOMMITTED]], [[READ_COMMITTED]], [[REPEATABLE_READ]] and [[SERIALIZABLE]] but
|
||||
* also a string containing DBMS specific syntax to be used after `SET TRANSACTION ISOLATION LEVEL`.
|
||||
* @throws Exception if the transaction is not active
|
||||
* @see http://en.wikipedia.org/wiki/Isolation_%28database_systems%29#Isolation_levels
|
||||
*/
|
||||
public function setIsolationLevel($level)
|
||||
{
|
||||
if (!$this->getIsActive()) {
|
||||
throw new Exception('Failed to set isolation level: transaction was inactive.');
|
||||
}
|
||||
Yii::debug('Setting transaction isolation level to ' . $level, __METHOD__);
|
||||
$this->db->getSchema()->setTransactionIsolationLevel($level);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int The current nesting level of the transaction.
|
||||
* @since 2.0.8
|
||||
*/
|
||||
public function getLevel()
|
||||
{
|
||||
return $this->_level;
|
||||
}
|
||||
}
|
||||
47
vendor/yiisoft/yii2/db/ViewFinderTrait.php
vendored
Normal file
47
vendor/yiisoft/yii2/db/ViewFinderTrait.php
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db;
|
||||
|
||||
/**
|
||||
* ViewFinderTrait implements the method getViewNames for finding views in a database.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @author Bob Olde Hampsink <b.oldehampsink@nerds.company>
|
||||
* @since 2.0.12
|
||||
*/
|
||||
trait ViewFinderTrait
|
||||
{
|
||||
/**
|
||||
* @var array list of ALL view names in the database
|
||||
*/
|
||||
private $_viewNames = [];
|
||||
|
||||
/**
|
||||
* Returns all views names in the database.
|
||||
* @param string $schema the schema of the views. Defaults to empty string, meaning the current or default schema.
|
||||
* @return array all views names in the database. The names have NO schema name prefix.
|
||||
*/
|
||||
abstract protected function findViewNames($schema = '');
|
||||
|
||||
/**
|
||||
* Returns all view names in the database.
|
||||
* @param string $schema the schema of the views. Defaults to empty string, meaning the current or default schema name.
|
||||
* If not empty, the returned view names will be prefixed with the schema name.
|
||||
* @param bool $refresh whether to fetch the latest available view names. If this is false,
|
||||
* view names fetched previously (if available) will be returned.
|
||||
* @return string[] all view names in the database.
|
||||
*/
|
||||
public function getViewNames($schema = '', $refresh = false)
|
||||
{
|
||||
if (!isset($this->_viewNames[$schema]) || $refresh) {
|
||||
$this->_viewNames[$schema] = $this->findViewNames($schema);
|
||||
}
|
||||
|
||||
return $this->_viewNames[$schema];
|
||||
}
|
||||
}
|
||||
27
vendor/yiisoft/yii2/db/conditions/AndCondition.php
vendored
Normal file
27
vendor/yiisoft/yii2/db/conditions/AndCondition.php
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\conditions;
|
||||
|
||||
/**
|
||||
* Condition that connects two or more SQL expressions with the `AND` operator.
|
||||
*
|
||||
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
|
||||
* @since 2.0.14
|
||||
*/
|
||||
class AndCondition extends ConjunctionCondition
|
||||
{
|
||||
/**
|
||||
* Returns the operator that is represented by this condition class, e.g. `AND`, `OR`.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getOperator()
|
||||
{
|
||||
return 'AND';
|
||||
}
|
||||
}
|
||||
121
vendor/yiisoft/yii2/db/conditions/BetweenColumnsCondition.php
vendored
Normal file
121
vendor/yiisoft/yii2/db/conditions/BetweenColumnsCondition.php
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\conditions;
|
||||
|
||||
use yii\base\InvalidArgumentException;
|
||||
use yii\db\ExpressionInterface;
|
||||
use yii\db\Query;
|
||||
|
||||
/**
|
||||
* Class BetweenColumnCondition represents a `BETWEEN` condition where
|
||||
* values is between two columns. For example:
|
||||
*
|
||||
* ```php
|
||||
* new BetweenColumnsCondition(42, 'BETWEEN', 'min_value', 'max_value')
|
||||
* // Will be build to:
|
||||
* // 42 BETWEEN min_value AND max_value
|
||||
* ```
|
||||
*
|
||||
* And a more complex example:
|
||||
*
|
||||
* ```php
|
||||
* new BetweenColumnsCondition(
|
||||
* new Expression('NOW()'),
|
||||
* 'NOT BETWEEN',
|
||||
* (new Query)->select('time')->from('log')->orderBy('id ASC')->limit(1),
|
||||
* 'update_time'
|
||||
* );
|
||||
*
|
||||
* // Will be built to:
|
||||
* // NOW() NOT BETWEEN (SELECT time FROM log ORDER BY id ASC LIMIT 1) AND update_time
|
||||
* ```
|
||||
*
|
||||
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
|
||||
* @since 2.0.14
|
||||
*/
|
||||
class BetweenColumnsCondition implements ConditionInterface
|
||||
{
|
||||
/**
|
||||
* @var string $operator the operator to use (e.g. `BETWEEN` or `NOT BETWEEN`)
|
||||
*/
|
||||
private $operator;
|
||||
/**
|
||||
* @var mixed the value to compare against
|
||||
*/
|
||||
private $value;
|
||||
/**
|
||||
* @var string|ExpressionInterface|Query the column name or expression that is a beginning of the interval
|
||||
*/
|
||||
private $intervalStartColumn;
|
||||
/**
|
||||
* @var string|ExpressionInterface|Query the column name or expression that is an end of the interval
|
||||
*/
|
||||
private $intervalEndColumn;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a condition with the `BETWEEN` operator.
|
||||
*
|
||||
* @param mixed the value to compare against
|
||||
* @param string $operator the operator to use (e.g. `BETWEEN` or `NOT BETWEEN`)
|
||||
* @param string|ExpressionInterface $intervalStartColumn the column name or expression that is a beginning of the interval
|
||||
* @param string|ExpressionInterface $intervalEndColumn the column name or expression that is an end of the interval
|
||||
*/
|
||||
public function __construct($value, $operator, $intervalStartColumn, $intervalEndColumn)
|
||||
{
|
||||
$this->value = $value;
|
||||
$this->operator = $operator;
|
||||
$this->intervalStartColumn = $intervalStartColumn;
|
||||
$this->intervalEndColumn = $intervalEndColumn;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getOperator()
|
||||
{
|
||||
return $this->operator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getValue()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|ExpressionInterface|Query
|
||||
*/
|
||||
public function getIntervalStartColumn()
|
||||
{
|
||||
return $this->intervalStartColumn;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|ExpressionInterface|Query
|
||||
*/
|
||||
public function getIntervalEndColumn()
|
||||
{
|
||||
return $this->intervalEndColumn;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @throws InvalidArgumentException if wrong number of operands have been given.
|
||||
*/
|
||||
public static function fromArrayDefinition($operator, $operands)
|
||||
{
|
||||
if (!isset($operands[0], $operands[1], $operands[2])) {
|
||||
throw new InvalidArgumentException("Operator '$operator' requires three operands.");
|
||||
}
|
||||
|
||||
return new static($operands[0], $operator, $operands[1], $operands[2]);
|
||||
}
|
||||
}
|
||||
81
vendor/yiisoft/yii2/db/conditions/BetweenColumnsConditionBuilder.php
vendored
Normal file
81
vendor/yiisoft/yii2/db/conditions/BetweenColumnsConditionBuilder.php
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\conditions;
|
||||
|
||||
use yii\db\ExpressionBuilderInterface;
|
||||
use yii\db\ExpressionBuilderTrait;
|
||||
use yii\db\ExpressionInterface;
|
||||
use yii\db\Query;
|
||||
|
||||
/**
|
||||
* Class BetweenColumnsConditionBuilder builds objects of [[BetweenColumnsCondition]]
|
||||
*
|
||||
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
|
||||
* @since 2.0.14
|
||||
*/
|
||||
class BetweenColumnsConditionBuilder implements ExpressionBuilderInterface
|
||||
{
|
||||
use ExpressionBuilderTrait;
|
||||
|
||||
|
||||
/**
|
||||
* Method builds the raw SQL from the $expression that will not be additionally
|
||||
* escaped or quoted.
|
||||
*
|
||||
* @param ExpressionInterface|BetweenColumnsCondition $expression the expression to be built.
|
||||
* @param array $params the binding parameters.
|
||||
* @return string the raw SQL that will not be additionally escaped or quoted.
|
||||
*/
|
||||
public function build(ExpressionInterface $expression, array &$params = [])
|
||||
{
|
||||
$operator = $expression->getOperator();
|
||||
|
||||
$startColumn = $this->escapeColumnName($expression->getIntervalStartColumn(), $params);
|
||||
$endColumn = $this->escapeColumnName($expression->getIntervalEndColumn(), $params);
|
||||
$value = $this->createPlaceholder($expression->getValue(), $params);
|
||||
|
||||
return "$value $operator $startColumn AND $endColumn";
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares column name to be used in SQL statement.
|
||||
*
|
||||
* @param Query|ExpressionInterface|string $columnName
|
||||
* @param array $params the binding parameters.
|
||||
* @return string
|
||||
*/
|
||||
protected function escapeColumnName($columnName, &$params = [])
|
||||
{
|
||||
if ($columnName instanceof Query) {
|
||||
list($sql, $params) = $this->queryBuilder->build($columnName, $params);
|
||||
return "($sql)";
|
||||
} elseif ($columnName instanceof ExpressionInterface) {
|
||||
return $this->queryBuilder->buildExpression($columnName, $params);
|
||||
} elseif (strpos($columnName, '(') === false) {
|
||||
return $this->queryBuilder->db->quoteColumnName($columnName);
|
||||
}
|
||||
|
||||
return $columnName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches $value to $params array and returns placeholder.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param array $params passed by reference
|
||||
* @return string
|
||||
*/
|
||||
protected function createPlaceholder($value, &$params)
|
||||
{
|
||||
if ($value instanceof ExpressionInterface) {
|
||||
return $this->queryBuilder->buildExpression($value, $params);
|
||||
}
|
||||
|
||||
return $this->queryBuilder->bindParam($value, $params);
|
||||
}
|
||||
}
|
||||
98
vendor/yiisoft/yii2/db/conditions/BetweenCondition.php
vendored
Normal file
98
vendor/yiisoft/yii2/db/conditions/BetweenCondition.php
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\conditions;
|
||||
|
||||
use yii\base\InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Class BetweenCondition represents a `BETWEEN` condition.
|
||||
*
|
||||
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
|
||||
* @since 2.0.14
|
||||
*/
|
||||
class BetweenCondition implements ConditionInterface
|
||||
{
|
||||
/**
|
||||
* @var string $operator the operator to use (e.g. `BETWEEN` or `NOT BETWEEN`)
|
||||
*/
|
||||
private $operator;
|
||||
/**
|
||||
* @var mixed the column name to the left of [[operator]]
|
||||
*/
|
||||
private $column;
|
||||
/**
|
||||
* @var mixed beginning of the interval
|
||||
*/
|
||||
private $intervalStart;
|
||||
/**
|
||||
* @var mixed end of the interval
|
||||
*/
|
||||
private $intervalEnd;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a condition with the `BETWEEN` operator.
|
||||
*
|
||||
* @param mixed $column the literal to the left of $operator
|
||||
* @param string $operator the operator to use (e.g. `BETWEEN` or `NOT BETWEEN`)
|
||||
* @param mixed $intervalStart beginning of the interval
|
||||
* @param mixed $intervalEnd end of the interval
|
||||
*/
|
||||
public function __construct($column, $operator, $intervalStart, $intervalEnd)
|
||||
{
|
||||
$this->column = $column;
|
||||
$this->operator = $operator;
|
||||
$this->intervalStart = $intervalStart;
|
||||
$this->intervalEnd = $intervalEnd;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getOperator()
|
||||
{
|
||||
return $this->operator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getColumn()
|
||||
{
|
||||
return $this->column;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getIntervalStart()
|
||||
{
|
||||
return $this->intervalStart;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getIntervalEnd()
|
||||
{
|
||||
return $this->intervalEnd;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @throws InvalidArgumentException if wrong number of operands have been given.
|
||||
*/
|
||||
public static function fromArrayDefinition($operator, $operands)
|
||||
{
|
||||
if (!isset($operands[0], $operands[1], $operands[2])) {
|
||||
throw new InvalidArgumentException("Operator '$operator' requires three operands.");
|
||||
}
|
||||
|
||||
return new static($operands[0], $operator, $operands[1], $operands[2]);
|
||||
}
|
||||
}
|
||||
63
vendor/yiisoft/yii2/db/conditions/BetweenConditionBuilder.php
vendored
Normal file
63
vendor/yiisoft/yii2/db/conditions/BetweenConditionBuilder.php
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\conditions;
|
||||
|
||||
use yii\db\ExpressionBuilderInterface;
|
||||
use yii\db\ExpressionBuilderTrait;
|
||||
use yii\db\ExpressionInterface;
|
||||
|
||||
/**
|
||||
* Class BetweenConditionBuilder builds objects of [[BetweenCondition]]
|
||||
*
|
||||
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
|
||||
* @since 2.0.14
|
||||
*/
|
||||
class BetweenConditionBuilder implements ExpressionBuilderInterface
|
||||
{
|
||||
use ExpressionBuilderTrait;
|
||||
|
||||
|
||||
/**
|
||||
* Method builds the raw SQL from the $expression that will not be additionally
|
||||
* escaped or quoted.
|
||||
*
|
||||
* @param ExpressionInterface|BetweenCondition $expression the expression to be built.
|
||||
* @param array $params the binding parameters.
|
||||
* @return string the raw SQL that will not be additionally escaped or quoted.
|
||||
*/
|
||||
public function build(ExpressionInterface $expression, array &$params = [])
|
||||
{
|
||||
$operator = $expression->getOperator();
|
||||
$column = $expression->getColumn();
|
||||
|
||||
if (strpos($column, '(') === false) {
|
||||
$column = $this->queryBuilder->db->quoteColumnName($column);
|
||||
}
|
||||
|
||||
$phName1 = $this->createPlaceholder($expression->getIntervalStart(), $params);
|
||||
$phName2 = $this->createPlaceholder($expression->getIntervalEnd(), $params);
|
||||
|
||||
return "$column $operator $phName1 AND $phName2";
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches $value to $params array and returns placeholder.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param array $params passed by reference
|
||||
* @return string
|
||||
*/
|
||||
protected function createPlaceholder($value, &$params)
|
||||
{
|
||||
if ($value instanceof ExpressionInterface) {
|
||||
return $this->queryBuilder->buildExpression($value, $params);
|
||||
}
|
||||
|
||||
return $this->queryBuilder->bindParam($value, $params);
|
||||
}
|
||||
}
|
||||
33
vendor/yiisoft/yii2/db/conditions/ConditionInterface.php
vendored
Normal file
33
vendor/yiisoft/yii2/db/conditions/ConditionInterface.php
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\conditions;
|
||||
|
||||
use yii\base\InvalidParamException;
|
||||
use yii\db\ExpressionInterface;
|
||||
|
||||
/**
|
||||
* Interface ConditionInterface should be implemented by classes that represent a condition
|
||||
* in DBAL of framework.
|
||||
*
|
||||
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
|
||||
* @since 2.0.14
|
||||
*/
|
||||
interface ConditionInterface extends ExpressionInterface
|
||||
{
|
||||
/**
|
||||
* Creates object by array-definition as described in
|
||||
* [Query Builder – Operator format](guide:db-query-builder#operator-format) guide article.
|
||||
*
|
||||
* @param string $operator operator in uppercase.
|
||||
* @param array $operands array of corresponding operands
|
||||
*
|
||||
* @return $this
|
||||
* @throws InvalidParamException if input parameters are not suitable for this condition
|
||||
*/
|
||||
public static function fromArrayDefinition($operator, $operands);
|
||||
}
|
||||
53
vendor/yiisoft/yii2/db/conditions/ConjunctionCondition.php
vendored
Normal file
53
vendor/yiisoft/yii2/db/conditions/ConjunctionCondition.php
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\conditions;
|
||||
|
||||
/**
|
||||
* Class ConjunctionCondition
|
||||
*
|
||||
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
|
||||
* @since 2.0.14
|
||||
*/
|
||||
abstract class ConjunctionCondition implements ConditionInterface
|
||||
{
|
||||
/**
|
||||
* @var mixed[]
|
||||
*/
|
||||
protected $expressions;
|
||||
|
||||
|
||||
/**
|
||||
* @param mixed $expressions
|
||||
*/
|
||||
public function __construct($expressions) // TODO: use variadic params when PHP>5.6
|
||||
{
|
||||
$this->expressions = $expressions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed[]
|
||||
*/
|
||||
public function getExpressions()
|
||||
{
|
||||
return $this->expressions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the operator that is represented by this condition class, e.g. `AND`, `OR`.
|
||||
* @return string
|
||||
*/
|
||||
abstract public function getOperator();
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function fromArrayDefinition($operator, $operands)
|
||||
{
|
||||
return new static($operands);
|
||||
}
|
||||
}
|
||||
72
vendor/yiisoft/yii2/db/conditions/ConjunctionConditionBuilder.php
vendored
Normal file
72
vendor/yiisoft/yii2/db/conditions/ConjunctionConditionBuilder.php
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\conditions;
|
||||
|
||||
use yii\db\ExpressionBuilderInterface;
|
||||
use yii\db\ExpressionBuilderTrait;
|
||||
use yii\db\ExpressionInterface;
|
||||
|
||||
/**
|
||||
* Class ConjunctionConditionBuilder builds objects of abstract class [[ConjunctionCondition]]
|
||||
*
|
||||
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
|
||||
* @since 2.0.14
|
||||
*/
|
||||
class ConjunctionConditionBuilder implements ExpressionBuilderInterface
|
||||
{
|
||||
use ExpressionBuilderTrait;
|
||||
|
||||
|
||||
/**
|
||||
* Method builds the raw SQL from the $expression that will not be additionally
|
||||
* escaped or quoted.
|
||||
*
|
||||
* @param ExpressionInterface|ConjunctionCondition $condition the expression to be built.
|
||||
* @param array $params the binding parameters.
|
||||
* @return string the raw SQL that will not be additionally escaped or quoted.
|
||||
*/
|
||||
public function build(ExpressionInterface $condition, array &$params = [])
|
||||
{
|
||||
$parts = $this->buildExpressionsFrom($condition, $params);
|
||||
|
||||
if (empty($parts)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (count($parts) === 1) {
|
||||
return reset($parts);
|
||||
}
|
||||
|
||||
return '(' . implode(") {$condition->getOperator()} (", $parts) . ')';
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds expressions, that are stored in $condition
|
||||
*
|
||||
* @param ExpressionInterface|ConjunctionCondition $condition the expression to be built.
|
||||
* @param array $params the binding parameters.
|
||||
* @return string[]
|
||||
*/
|
||||
private function buildExpressionsFrom(ExpressionInterface $condition, &$params = [])
|
||||
{
|
||||
$parts = [];
|
||||
foreach ($condition->getExpressions() as $condition) {
|
||||
if (is_array($condition)) {
|
||||
$condition = $this->queryBuilder->buildCondition($condition, $params);
|
||||
}
|
||||
if ($condition instanceof ExpressionInterface) {
|
||||
$condition = $this->queryBuilder->buildExpression($condition, $params);
|
||||
}
|
||||
if ($condition !== '') {
|
||||
$parts[] = $condition;
|
||||
}
|
||||
}
|
||||
|
||||
return $parts;
|
||||
}
|
||||
}
|
||||
70
vendor/yiisoft/yii2/db/conditions/ExistsCondition.php
vendored
Normal file
70
vendor/yiisoft/yii2/db/conditions/ExistsCondition.php
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\conditions;
|
||||
|
||||
use yii\base\InvalidArgumentException;
|
||||
use yii\db\Query;
|
||||
|
||||
/**
|
||||
* Condition that represents `EXISTS` operator.
|
||||
*
|
||||
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
|
||||
* @since 2.0.14
|
||||
*/
|
||||
class ExistsCondition implements ConditionInterface
|
||||
{
|
||||
/**
|
||||
* @var string $operator the operator to use (e.g. `EXISTS` or `NOT EXISTS`)
|
||||
*/
|
||||
private $operator;
|
||||
/**
|
||||
* @var Query the [[Query]] object representing the sub-query.
|
||||
*/
|
||||
private $query;
|
||||
|
||||
|
||||
/**
|
||||
* ExistsCondition constructor.
|
||||
*
|
||||
* @param string $operator the operator to use (e.g. `EXISTS` or `NOT EXISTS`)
|
||||
* @param Query $query the [[Query]] object representing the sub-query.
|
||||
*/
|
||||
public function __construct($operator, $query)
|
||||
{
|
||||
$this->operator = $operator;
|
||||
$this->query = $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function fromArrayDefinition($operator, $operands)
|
||||
{
|
||||
if (!isset($operands[0]) || !$operands[0] instanceof Query) {
|
||||
throw new InvalidArgumentException('Subquery for EXISTS operator must be a Query object.');
|
||||
}
|
||||
|
||||
return new static($operator, $operands[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getOperator()
|
||||
{
|
||||
return $this->operator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Query
|
||||
*/
|
||||
public function getQuery()
|
||||
{
|
||||
return $this->query;
|
||||
}
|
||||
}
|
||||
42
vendor/yiisoft/yii2/db/conditions/ExistsConditionBuilder.php
vendored
Normal file
42
vendor/yiisoft/yii2/db/conditions/ExistsConditionBuilder.php
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\conditions;
|
||||
|
||||
use yii\db\ExpressionBuilderInterface;
|
||||
use yii\db\ExpressionBuilderTrait;
|
||||
use yii\db\ExpressionInterface;
|
||||
|
||||
/**
|
||||
* Class ExistsConditionBuilder builds objects of [[ExistsCondition]]
|
||||
*
|
||||
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
|
||||
* @since 2.0.14
|
||||
*/
|
||||
class ExistsConditionBuilder implements ExpressionBuilderInterface
|
||||
{
|
||||
use ExpressionBuilderTrait;
|
||||
|
||||
|
||||
/**
|
||||
* Method builds the raw SQL from the $expression that will not be additionally
|
||||
* escaped or quoted.
|
||||
*
|
||||
* @param ExpressionInterface|ExistsCondition $expression the expression to be built.
|
||||
* @param array $params the binding parameters.
|
||||
* @return string the raw SQL that will not be additionally escaped or quoted.
|
||||
*/
|
||||
public function build(ExpressionInterface $expression, array &$params = [])
|
||||
{
|
||||
$operator = $expression->getOperator();
|
||||
$query = $expression->getQuery();
|
||||
|
||||
$sql = $this->queryBuilder->buildExpression($query, $params);
|
||||
|
||||
return "$operator $sql";
|
||||
}
|
||||
}
|
||||
49
vendor/yiisoft/yii2/db/conditions/HashCondition.php
vendored
Normal file
49
vendor/yiisoft/yii2/db/conditions/HashCondition.php
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\conditions;
|
||||
|
||||
/**
|
||||
* Condition based on column-value pairs.
|
||||
*
|
||||
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
|
||||
* @since 2.0.14
|
||||
*/
|
||||
class HashCondition implements ConditionInterface
|
||||
{
|
||||
/**
|
||||
* @var array|null the condition specification.
|
||||
*/
|
||||
private $hash;
|
||||
|
||||
|
||||
/**
|
||||
* HashCondition constructor.
|
||||
*
|
||||
* @param array|null $hash
|
||||
*/
|
||||
public function __construct($hash)
|
||||
{
|
||||
$this->hash = $hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|null
|
||||
*/
|
||||
public function getHash()
|
||||
{
|
||||
return $this->hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function fromArrayDefinition($operator, $operands)
|
||||
{
|
||||
return new static($operands);
|
||||
}
|
||||
}
|
||||
60
vendor/yiisoft/yii2/db/conditions/HashConditionBuilder.php
vendored
Normal file
60
vendor/yiisoft/yii2/db/conditions/HashConditionBuilder.php
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\conditions;
|
||||
|
||||
use yii\db\ExpressionBuilderInterface;
|
||||
use yii\db\ExpressionBuilderTrait;
|
||||
use yii\db\ExpressionInterface;
|
||||
use yii\db\Query;
|
||||
use yii\helpers\ArrayHelper;
|
||||
|
||||
/**
|
||||
* Class HashConditionBuilder builds objects of [[HashCondition]]
|
||||
*
|
||||
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
|
||||
* @since 2.0.14
|
||||
*/
|
||||
class HashConditionBuilder implements ExpressionBuilderInterface
|
||||
{
|
||||
use ExpressionBuilderTrait;
|
||||
|
||||
|
||||
/**
|
||||
* Method builds the raw SQL from the $expression that will not be additionally
|
||||
* escaped or quoted.
|
||||
*
|
||||
* @param ExpressionInterface|HashCondition $expression the expression to be built.
|
||||
* @param array $params the binding parameters.
|
||||
* @return string the raw SQL that will not be additionally escaped or quoted.
|
||||
*/
|
||||
public function build(ExpressionInterface $expression, array &$params = [])
|
||||
{
|
||||
$hash = $expression->getHash();
|
||||
$parts = [];
|
||||
foreach ($hash as $column => $value) {
|
||||
if (ArrayHelper::isTraversable($value) || $value instanceof Query) {
|
||||
// IN condition
|
||||
$parts[] = $this->queryBuilder->buildCondition(new InCondition($column, 'IN', $value), $params);
|
||||
} else {
|
||||
if (strpos($column, '(') === false) {
|
||||
$column = $this->queryBuilder->db->quoteColumnName($column);
|
||||
}
|
||||
if ($value === null) {
|
||||
$parts[] = "$column IS NULL";
|
||||
} elseif ($value instanceof ExpressionInterface) {
|
||||
$parts[] = "$column=" . $this->queryBuilder->buildExpression($value, $params);
|
||||
} else {
|
||||
$phName = $this->queryBuilder->bindParam($value, $params);
|
||||
$parts[] = "$column=$phName";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return count($parts) === 1 ? $parts[0] : '(' . implode(') AND (', $parts) . ')';
|
||||
}
|
||||
}
|
||||
89
vendor/yiisoft/yii2/db/conditions/InCondition.php
vendored
Normal file
89
vendor/yiisoft/yii2/db/conditions/InCondition.php
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\conditions;
|
||||
|
||||
use yii\base\InvalidArgumentException;
|
||||
use yii\db\ExpressionInterface;
|
||||
|
||||
/**
|
||||
* Class InCondition represents `IN` condition.
|
||||
*
|
||||
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
|
||||
* @since 2.0.14
|
||||
*/
|
||||
class InCondition implements ConditionInterface
|
||||
{
|
||||
/**
|
||||
* @var string $operator the operator to use (e.g. `IN` or `NOT IN`)
|
||||
*/
|
||||
private $operator;
|
||||
/**
|
||||
* @var string|string[] the column name. If it is an array, a composite `IN` condition
|
||||
* will be generated.
|
||||
*/
|
||||
private $column;
|
||||
/**
|
||||
* @var ExpressionInterface[]|string[]|int[] an array of values that [[column]] value should be among.
|
||||
* If it is an empty array the generated expression will be a `false` value if
|
||||
* [[operator]] is `IN` and empty if operator is `NOT IN`.
|
||||
*/
|
||||
private $values;
|
||||
|
||||
|
||||
/**
|
||||
* SimpleCondition constructor
|
||||
*
|
||||
* @param string|string[] the column name. If it is an array, a composite `IN` condition
|
||||
* will be generated.
|
||||
* @param string $operator the operator to use (e.g. `IN` or `NOT IN`)
|
||||
* @param array an array of values that [[column]] value should be among. If it is an empty array the generated
|
||||
* expression will be a `false` value if [[operator]] is `IN` and empty if operator is `NOT IN`.
|
||||
*/
|
||||
public function __construct($column, $operator, $values)
|
||||
{
|
||||
$this->column = $column;
|
||||
$this->operator = $operator;
|
||||
$this->values = $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getOperator()
|
||||
{
|
||||
return $this->operator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getColumn()
|
||||
{
|
||||
return $this->column;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ExpressionInterface[]|string[]|int[]
|
||||
*/
|
||||
public function getValues()
|
||||
{
|
||||
return $this->values;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @throws InvalidArgumentException if wrong number of operands have been given.
|
||||
*/
|
||||
public static function fromArrayDefinition($operator, $operands)
|
||||
{
|
||||
if (!isset($operands[0], $operands[1])) {
|
||||
throw new InvalidArgumentException("Operator '$operator' requires two operands.");
|
||||
}
|
||||
|
||||
return new static($operands[0], $operator, $operands[1]);
|
||||
}
|
||||
}
|
||||
172
vendor/yiisoft/yii2/db/conditions/InConditionBuilder.php
vendored
Normal file
172
vendor/yiisoft/yii2/db/conditions/InConditionBuilder.php
vendored
Normal file
@@ -0,0 +1,172 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\conditions;
|
||||
|
||||
use yii\db\ExpressionBuilderInterface;
|
||||
use yii\db\ExpressionBuilderTrait;
|
||||
use yii\db\ExpressionInterface;
|
||||
use yii\db\Query;
|
||||
|
||||
/**
|
||||
* Class InConditionBuilder builds objects of [[InCondition]]
|
||||
*
|
||||
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
|
||||
* @since 2.0.14
|
||||
*/
|
||||
class InConditionBuilder implements ExpressionBuilderInterface
|
||||
{
|
||||
use ExpressionBuilderTrait;
|
||||
|
||||
|
||||
/**
|
||||
* Method builds the raw SQL from the $expression that will not be additionally
|
||||
* escaped or quoted.
|
||||
*
|
||||
* @param ExpressionInterface|InCondition $expression the expression to be built.
|
||||
* @param array $params the binding parameters.
|
||||
* @return string the raw SQL that will not be additionally escaped or quoted.
|
||||
*/
|
||||
public function build(ExpressionInterface $expression, array &$params = [])
|
||||
{
|
||||
$operator = $expression->getOperator();
|
||||
$column = $expression->getColumn();
|
||||
$values = $expression->getValues();
|
||||
|
||||
if ($column === []) {
|
||||
// no columns to test against
|
||||
return $operator === 'IN' ? '0=1' : '';
|
||||
}
|
||||
|
||||
if ($values instanceof Query) {
|
||||
return $this->buildSubqueryInCondition($operator, $column, $values, $params);
|
||||
}
|
||||
|
||||
if (!is_array($values) && !$values instanceof \Traversable) {
|
||||
// ensure values is an array
|
||||
$values = (array) $values;
|
||||
}
|
||||
if ($column instanceof \Traversable || ((is_array($column) || $column instanceof \Countable) && count($column) > 1)) {
|
||||
return $this->buildCompositeInCondition($operator, $column, $values, $params);
|
||||
}
|
||||
|
||||
if (is_array($column)) {
|
||||
$column = reset($column);
|
||||
}
|
||||
|
||||
$sqlValues = $this->buildValues($expression, $values, $params);
|
||||
if (empty($sqlValues)) {
|
||||
return $operator === 'IN' ? '0=1' : '';
|
||||
}
|
||||
|
||||
if (strpos($column, '(') === false) {
|
||||
$column = $this->queryBuilder->db->quoteColumnName($column);
|
||||
}
|
||||
if (count($sqlValues) > 1) {
|
||||
return "$column $operator (" . implode(', ', $sqlValues) . ')';
|
||||
}
|
||||
|
||||
$operator = $operator === 'IN' ? '=' : '<>';
|
||||
|
||||
return $column . $operator . reset($sqlValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds $values to be used in [[InCondition]]
|
||||
*
|
||||
* @param ConditionInterface|InCondition $condition
|
||||
* @param array $values
|
||||
* @param array $params the binding parameters
|
||||
* @return array of prepared for SQL placeholders
|
||||
*/
|
||||
protected function buildValues(ConditionInterface $condition, $values, &$params)
|
||||
{
|
||||
$sqlValues = [];
|
||||
$column = $condition->getColumn();
|
||||
|
||||
foreach ($values as $i => $value) {
|
||||
if (is_array($value) || $value instanceof \ArrayAccess) {
|
||||
$value = isset($value[$column]) ? $value[$column] : null;
|
||||
}
|
||||
if ($value === null) {
|
||||
$sqlValues[$i] = 'NULL';
|
||||
} elseif ($value instanceof ExpressionInterface) {
|
||||
$sqlValues[$i] = $this->queryBuilder->buildExpression($value, $params);
|
||||
} else {
|
||||
$sqlValues[$i] = $this->queryBuilder->bindParam($value, $params);
|
||||
}
|
||||
}
|
||||
|
||||
return $sqlValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds SQL for IN condition.
|
||||
*
|
||||
* @param string $operator
|
||||
* @param array|string $columns
|
||||
* @param Query $values
|
||||
* @param array $params
|
||||
* @return string SQL
|
||||
*/
|
||||
protected function buildSubqueryInCondition($operator, $columns, $values, &$params)
|
||||
{
|
||||
$sql = $this->queryBuilder->buildExpression($values, $params);
|
||||
|
||||
if (is_array($columns)) {
|
||||
foreach ($columns as $i => $col) {
|
||||
if (strpos($col, '(') === false) {
|
||||
$columns[$i] = $this->queryBuilder->db->quoteColumnName($col);
|
||||
}
|
||||
}
|
||||
|
||||
return '(' . implode(', ', $columns) . ") $operator $sql";
|
||||
}
|
||||
|
||||
if (strpos($columns, '(') === false) {
|
||||
$columns = $this->queryBuilder->db->quoteColumnName($columns);
|
||||
}
|
||||
|
||||
return "$columns $operator $sql";
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds SQL for IN condition.
|
||||
*
|
||||
* @param string $operator
|
||||
* @param array|\Traversable $columns
|
||||
* @param array $values
|
||||
* @param array $params
|
||||
* @return string SQL
|
||||
*/
|
||||
protected function buildCompositeInCondition($operator, $columns, $values, &$params)
|
||||
{
|
||||
$vss = [];
|
||||
foreach ($values as $value) {
|
||||
$vs = [];
|
||||
foreach ($columns as $column) {
|
||||
if (isset($value[$column])) {
|
||||
$vs[] = $this->queryBuilder->bindParam($value[$column], $params);
|
||||
} else {
|
||||
$vs[] = 'NULL';
|
||||
}
|
||||
}
|
||||
$vss[] = '(' . implode(', ', $vs) . ')';
|
||||
}
|
||||
|
||||
if (empty($vss)) {
|
||||
return $operator === 'IN' ? '0=1' : '';
|
||||
}
|
||||
|
||||
$sqlColumns = [];
|
||||
foreach ($columns as $i => $column) {
|
||||
$sqlColumns[] = strpos($column, '(') === false ? $this->queryBuilder->db->quoteColumnName($column) : $column;
|
||||
}
|
||||
|
||||
return '(' . implode(', ', $sqlColumns) . ") $operator (" . implode(', ', $vss) . ')';
|
||||
}
|
||||
}
|
||||
78
vendor/yiisoft/yii2/db/conditions/LikeCondition.php
vendored
Normal file
78
vendor/yiisoft/yii2/db/conditions/LikeCondition.php
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\conditions;
|
||||
|
||||
use yii\base\InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Class LikeCondition represents a `LIKE` condition.
|
||||
*
|
||||
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
|
||||
* @since 2.0.14
|
||||
*/
|
||||
class LikeCondition extends SimpleCondition
|
||||
{
|
||||
/**
|
||||
* @var array|false map of chars to their replacements, false if characters should not be escaped
|
||||
* or either null or empty array if escaping is condition builder responsibility.
|
||||
* By default it's set to `null`.
|
||||
*/
|
||||
protected $escapingReplacements;
|
||||
|
||||
|
||||
/**
|
||||
* @param string $column the column name.
|
||||
* @param string $operator the operator to use (e.g. `LIKE`, `NOT LIKE`, `OR LIKE` or `OR NOT LIKE`)
|
||||
* @param string[]|string $value single value or an array of values that $column should be compared with.
|
||||
* If it is an empty array the generated expression will be a `false` value if operator is `LIKE` or `OR LIKE`
|
||||
* and empty if operator is `NOT LIKE` or `OR NOT LIKE`.
|
||||
*/
|
||||
public function __construct($column, $operator, $value)
|
||||
{
|
||||
parent::__construct($column, $operator, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method allows to specify how to escape special characters in the value(s).
|
||||
*
|
||||
* @param array an array of mappings from the special characters to their escaped counterparts.
|
||||
* You may use `false` or an empty array to indicate the values are already escaped and no escape
|
||||
* should be applied. Note that when using an escape mapping (or the third operand is not provided),
|
||||
* the values will be automatically enclosed within a pair of percentage characters.
|
||||
*/
|
||||
public function setEscapingReplacements($escapingReplacements)
|
||||
{
|
||||
$this->escapingReplacements = $escapingReplacements;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|false
|
||||
*/
|
||||
public function getEscapingReplacements()
|
||||
{
|
||||
return $this->escapingReplacements;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @throws InvalidArgumentException if wrong number of operands have been given.
|
||||
*/
|
||||
public static function fromArrayDefinition($operator, $operands)
|
||||
{
|
||||
if (!isset($operands[0], $operands[1])) {
|
||||
throw new InvalidArgumentException("Operator '$operator' requires two operands.");
|
||||
}
|
||||
|
||||
$condition = new static($operands[0], $operator, $operands[1]);
|
||||
if (isset($operands[2])) {
|
||||
$condition->escapingReplacements = $operands[2];
|
||||
}
|
||||
|
||||
return $condition;
|
||||
}
|
||||
}
|
||||
114
vendor/yiisoft/yii2/db/conditions/LikeConditionBuilder.php
vendored
Normal file
114
vendor/yiisoft/yii2/db/conditions/LikeConditionBuilder.php
vendored
Normal file
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\conditions;
|
||||
|
||||
use yii\base\InvalidArgumentException;
|
||||
use yii\db\ExpressionBuilderInterface;
|
||||
use yii\db\ExpressionBuilderTrait;
|
||||
use yii\db\ExpressionInterface;
|
||||
|
||||
/**
|
||||
* Class LikeConditionBuilder builds objects of [[LikeCondition]]
|
||||
*
|
||||
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
|
||||
* @since 2.0.14
|
||||
*/
|
||||
class LikeConditionBuilder implements ExpressionBuilderInterface
|
||||
{
|
||||
use ExpressionBuilderTrait;
|
||||
|
||||
/**
|
||||
* @var array map of chars to their replacements in LIKE conditions.
|
||||
* By default it's configured to escape `%`, `_` and `\` with `\`.
|
||||
*/
|
||||
protected $escapingReplacements = [
|
||||
'%' => '\%',
|
||||
'_' => '\_',
|
||||
'\\' => '\\\\',
|
||||
];
|
||||
/**
|
||||
* @var string|null character used to escape special characters in LIKE conditions.
|
||||
* By default it's assumed to be `\`.
|
||||
*/
|
||||
protected $escapeCharacter;
|
||||
|
||||
|
||||
/**
|
||||
* Method builds the raw SQL from the $expression that will not be additionally
|
||||
* escaped or quoted.
|
||||
*
|
||||
* @param ExpressionInterface|LikeCondition $expression the expression to be built.
|
||||
* @param array $params the binding parameters.
|
||||
* @return string the raw SQL that will not be additionally escaped or quoted.
|
||||
*/
|
||||
public function build(ExpressionInterface $expression, array &$params = [])
|
||||
{
|
||||
$operator = $expression->getOperator();
|
||||
$column = $expression->getColumn();
|
||||
$values = $expression->getValue();
|
||||
$escape = $expression->getEscapingReplacements();
|
||||
if ($escape === null || $escape === []) {
|
||||
$escape = $this->escapingReplacements;
|
||||
}
|
||||
|
||||
list($andor, $not, $operator) = $this->parseOperator($operator);
|
||||
|
||||
if (!is_array($values)) {
|
||||
$values = [$values];
|
||||
}
|
||||
|
||||
if (empty($values)) {
|
||||
return $not ? '' : '0=1';
|
||||
}
|
||||
|
||||
if (strpos($column, '(') === false) {
|
||||
$column = $this->queryBuilder->db->quoteColumnName($column);
|
||||
}
|
||||
|
||||
$escapeSql = $this->getEscapeSql();
|
||||
$parts = [];
|
||||
foreach ($values as $value) {
|
||||
if ($value instanceof ExpressionInterface) {
|
||||
$phName = $this->queryBuilder->buildExpression($value, $params);
|
||||
} else {
|
||||
$phName = $this->queryBuilder->bindParam(empty($escape) ? $value : ('%' . strtr($value, $escape) . '%'), $params);
|
||||
}
|
||||
$parts[] = "{$column} {$operator} {$phName}{$escapeSql}";
|
||||
}
|
||||
|
||||
return implode($andor, $parts);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
private function getEscapeSql()
|
||||
{
|
||||
if ($this->escapeCharacter !== null) {
|
||||
return " ESCAPE '{$this->escapeCharacter}'";
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $operator
|
||||
* @return array
|
||||
*/
|
||||
protected function parseOperator($operator)
|
||||
{
|
||||
if (!preg_match('/^(AND |OR |)(((NOT |))I?LIKE)/', $operator, $matches)) {
|
||||
throw new InvalidArgumentException("Invalid operator '$operator'.");
|
||||
}
|
||||
$andor = ' ' . (!empty($matches[1]) ? $matches[1] : 'AND ');
|
||||
$not = !empty($matches[3]);
|
||||
$operator = $matches[2];
|
||||
|
||||
return [$andor, $not, $operator];
|
||||
}
|
||||
}
|
||||
56
vendor/yiisoft/yii2/db/conditions/NotCondition.php
vendored
Normal file
56
vendor/yiisoft/yii2/db/conditions/NotCondition.php
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\conditions;
|
||||
|
||||
use yii\base\InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Condition that inverts passed [[condition]].
|
||||
*
|
||||
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
|
||||
* @since 2.0.14
|
||||
*/
|
||||
class NotCondition implements ConditionInterface
|
||||
{
|
||||
/**
|
||||
* @var mixed the condition to be negated
|
||||
*/
|
||||
private $condition;
|
||||
|
||||
|
||||
/**
|
||||
* NotCondition constructor.
|
||||
*
|
||||
* @param mixed $condition the condition to be negated
|
||||
*/
|
||||
public function __construct($condition)
|
||||
{
|
||||
$this->condition = $condition;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getCondition()
|
||||
{
|
||||
return $this->condition;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @throws InvalidArgumentException if wrong number of operands have been given.
|
||||
*/
|
||||
public static function fromArrayDefinition($operator, $operands)
|
||||
{
|
||||
if (count($operands) !== 1) {
|
||||
throw new InvalidArgumentException("Operator '$operator' requires exactly one operand.");
|
||||
}
|
||||
|
||||
return new static(array_shift($operands));
|
||||
}
|
||||
}
|
||||
51
vendor/yiisoft/yii2/db/conditions/NotConditionBuilder.php
vendored
Normal file
51
vendor/yiisoft/yii2/db/conditions/NotConditionBuilder.php
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\conditions;
|
||||
|
||||
use yii\db\ExpressionBuilderInterface;
|
||||
use yii\db\ExpressionBuilderTrait;
|
||||
use yii\db\ExpressionInterface;
|
||||
|
||||
/**
|
||||
* Class NotConditionBuilder builds objects of [[NotCondition]]
|
||||
*
|
||||
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
|
||||
* @since 2.0.14
|
||||
*/
|
||||
class NotConditionBuilder implements ExpressionBuilderInterface
|
||||
{
|
||||
use ExpressionBuilderTrait;
|
||||
|
||||
|
||||
/**
|
||||
* Method builds the raw SQL from the $expression that will not be additionally
|
||||
* escaped or quoted.
|
||||
*
|
||||
* @param ExpressionInterface|NotCondition $expression the expression to be built.
|
||||
* @param array $params the binding parameters.
|
||||
* @return string the raw SQL that will not be additionally escaped or quoted.
|
||||
*/
|
||||
public function build(ExpressionInterface $expression, array &$params = [])
|
||||
{
|
||||
$operand = $expression->getCondition();
|
||||
if ($operand === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
$expession = $this->queryBuilder->buildCondition($operand, $params);
|
||||
return "{$this->getNegationOperator()} ($expession)";
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function getNegationOperator()
|
||||
{
|
||||
return 'NOT';
|
||||
}
|
||||
}
|
||||
27
vendor/yiisoft/yii2/db/conditions/OrCondition.php
vendored
Normal file
27
vendor/yiisoft/yii2/db/conditions/OrCondition.php
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\conditions;
|
||||
|
||||
/**
|
||||
* Condition that connects two or more SQL expressions with the `AND` operator.
|
||||
*
|
||||
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
|
||||
* @since 2.0.14
|
||||
*/
|
||||
class OrCondition extends ConjunctionCondition
|
||||
{
|
||||
/**
|
||||
* Returns the operator that is represented by this condition class, e.g. `AND`, `OR`.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getOperator()
|
||||
{
|
||||
return 'OR';
|
||||
}
|
||||
}
|
||||
84
vendor/yiisoft/yii2/db/conditions/SimpleCondition.php
vendored
Normal file
84
vendor/yiisoft/yii2/db/conditions/SimpleCondition.php
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\conditions;
|
||||
|
||||
use yii\base\InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Class SimpleCondition represents a simple condition like `"column" operator value`.
|
||||
*
|
||||
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
|
||||
* @since 2.0.14
|
||||
*/
|
||||
class SimpleCondition implements ConditionInterface
|
||||
{
|
||||
/**
|
||||
* @var string $operator the operator to use. Anything could be used e.g. `>`, `<=`, etc.
|
||||
*/
|
||||
private $operator;
|
||||
/**
|
||||
* @var mixed the column name to the left of [[operator]]
|
||||
*/
|
||||
private $column;
|
||||
/**
|
||||
* @var mixed the value to the right of the [[operator]]
|
||||
*/
|
||||
private $value;
|
||||
|
||||
|
||||
/**
|
||||
* SimpleCondition constructor
|
||||
*
|
||||
* @param mixed $column the literal to the left of $operator
|
||||
* @param string $operator the operator to use. Anything could be used e.g. `>`, `<=`, etc.
|
||||
* @param mixed $value the literal to the right of $operator
|
||||
*/
|
||||
public function __construct($column, $operator, $value)
|
||||
{
|
||||
$this->column = $column;
|
||||
$this->operator = $operator;
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getOperator()
|
||||
{
|
||||
return $this->operator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getColumn()
|
||||
{
|
||||
return $this->column;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getValue()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @throws InvalidArgumentException if wrong number of operands have been given.
|
||||
*/
|
||||
public static function fromArrayDefinition($operator, $operands)
|
||||
{
|
||||
if (count($operands) !== 2) {
|
||||
throw new InvalidArgumentException("Operator '$operator' requires two operands.");
|
||||
}
|
||||
|
||||
return new static($operands[0], $operator, $operands[1]);
|
||||
}
|
||||
}
|
||||
54
vendor/yiisoft/yii2/db/conditions/SimpleConditionBuilder.php
vendored
Normal file
54
vendor/yiisoft/yii2/db/conditions/SimpleConditionBuilder.php
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\conditions;
|
||||
|
||||
use yii\db\ExpressionBuilderInterface;
|
||||
use yii\db\ExpressionBuilderTrait;
|
||||
use yii\db\ExpressionInterface;
|
||||
use yii\db\Query;
|
||||
|
||||
/**
|
||||
* Class NotConditionBuilder builds objects of [[SimpleCondition]]
|
||||
*
|
||||
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
|
||||
* @since 2.0.14
|
||||
*/
|
||||
class SimpleConditionBuilder implements ExpressionBuilderInterface
|
||||
{
|
||||
use ExpressionBuilderTrait;
|
||||
|
||||
|
||||
/**
|
||||
* Method builds the raw SQL from the $expression that will not be additionally
|
||||
* escaped or quoted.
|
||||
*
|
||||
* @param ExpressionInterface|SimpleCondition $expression the expression to be built.
|
||||
* @param array $params the binding parameters.
|
||||
* @return string the raw SQL that will not be additionally escaped or quoted.
|
||||
*/
|
||||
public function build(ExpressionInterface $expression, array &$params = [])
|
||||
{
|
||||
$operator = $expression->getOperator();
|
||||
$column = $expression->getColumn();
|
||||
$value = $expression->getValue();
|
||||
|
||||
if (strpos($column, '(') === false) {
|
||||
$column = $this->queryBuilder->db->quoteColumnName($column);
|
||||
}
|
||||
|
||||
if ($value === null) {
|
||||
return "$column $operator NULL";
|
||||
}
|
||||
if ($value instanceof ExpressionInterface) {
|
||||
return "$column $operator {$this->queryBuilder->buildExpression($value, $params)}";
|
||||
}
|
||||
|
||||
$phName = $this->queryBuilder->bindParam($value, $params);
|
||||
return "$column $operator $phName";
|
||||
}
|
||||
}
|
||||
72
vendor/yiisoft/yii2/db/cubrid/ColumnSchemaBuilder.php
vendored
Normal file
72
vendor/yiisoft/yii2/db/cubrid/ColumnSchemaBuilder.php
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\cubrid;
|
||||
|
||||
use yii\db\ColumnSchemaBuilder as AbstractColumnSchemaBuilder;
|
||||
|
||||
/**
|
||||
* ColumnSchemaBuilder is the schema builder for Cubrid databases.
|
||||
*
|
||||
* @author Chris Harris <chris@buckshotsoftware.com>
|
||||
* @since 2.0.8
|
||||
*/
|
||||
class ColumnSchemaBuilder extends AbstractColumnSchemaBuilder
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function buildUnsignedString()
|
||||
{
|
||||
return $this->isUnsigned ? ' UNSIGNED' : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function buildAfterString()
|
||||
{
|
||||
return $this->after !== null ?
|
||||
' AFTER ' . $this->db->quoteColumnName($this->after) :
|
||||
'';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function buildFirstString()
|
||||
{
|
||||
return $this->isFirst ? ' FIRST' : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function buildCommentString()
|
||||
{
|
||||
return $this->comment !== null ? ' COMMENT ' . $this->db->quoteValue($this->comment) : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
switch ($this->getTypeCategory()) {
|
||||
case self::CATEGORY_PK:
|
||||
$format = '{type}{check}{comment}{append}{pos}';
|
||||
break;
|
||||
case self::CATEGORY_NUMERIC:
|
||||
$format = '{type}{length}{unsigned}{notnull}{unique}{default}{check}{comment}{append}{pos}';
|
||||
break;
|
||||
default:
|
||||
$format = '{type}{length}{notnull}{unique}{default}{check}{comment}{append}{pos}';
|
||||
}
|
||||
|
||||
return $this->buildCompleteString($format);
|
||||
}
|
||||
}
|
||||
290
vendor/yiisoft/yii2/db/cubrid/QueryBuilder.php
vendored
Normal file
290
vendor/yiisoft/yii2/db/cubrid/QueryBuilder.php
vendored
Normal file
@@ -0,0 +1,290 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\cubrid;
|
||||
|
||||
use yii\base\InvalidArgumentException;
|
||||
use yii\base\NotSupportedException;
|
||||
use yii\db\Constraint;
|
||||
use yii\db\Exception;
|
||||
use yii\db\Expression;
|
||||
|
||||
/**
|
||||
* QueryBuilder is the query builder for CUBRID databases (version 9.3.x and higher).
|
||||
*
|
||||
* @author Carsten Brandt <mail@cebe.cc>
|
||||
* @since 2.0
|
||||
*/
|
||||
class QueryBuilder extends \yii\db\QueryBuilder
|
||||
{
|
||||
/**
|
||||
* @var array mapping from abstract column types (keys) to physical column types (values).
|
||||
*/
|
||||
public $typeMap = [
|
||||
Schema::TYPE_PK => 'int NOT NULL AUTO_INCREMENT PRIMARY KEY',
|
||||
Schema::TYPE_UPK => 'int UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY',
|
||||
Schema::TYPE_BIGPK => 'bigint NOT NULL AUTO_INCREMENT PRIMARY KEY',
|
||||
Schema::TYPE_UBIGPK => 'bigint UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY',
|
||||
Schema::TYPE_CHAR => 'char(1)',
|
||||
Schema::TYPE_STRING => 'varchar(255)',
|
||||
Schema::TYPE_TEXT => 'varchar',
|
||||
Schema::TYPE_TINYINT => 'smallint',
|
||||
Schema::TYPE_SMALLINT => 'smallint',
|
||||
Schema::TYPE_INTEGER => 'int',
|
||||
Schema::TYPE_BIGINT => 'bigint',
|
||||
Schema::TYPE_FLOAT => 'float(7)',
|
||||
Schema::TYPE_DOUBLE => 'double(15)',
|
||||
Schema::TYPE_DECIMAL => 'decimal(10,0)',
|
||||
Schema::TYPE_DATETIME => 'datetime',
|
||||
Schema::TYPE_TIMESTAMP => 'timestamp',
|
||||
Schema::TYPE_TIME => 'time',
|
||||
Schema::TYPE_DATE => 'date',
|
||||
Schema::TYPE_BINARY => 'blob',
|
||||
Schema::TYPE_BOOLEAN => 'smallint',
|
||||
Schema::TYPE_MONEY => 'decimal(19,4)',
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function defaultExpressionBuilders()
|
||||
{
|
||||
return array_merge(parent::defaultExpressionBuilders(), [
|
||||
'yii\db\conditions\LikeCondition' => 'yii\db\cubrid\conditions\LikeConditionBuilder',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @see https://www.cubrid.org/manual/en/9.3.0/sql/query/merge.html
|
||||
*/
|
||||
public function upsert($table, $insertColumns, $updateColumns, &$params)
|
||||
{
|
||||
/** @var Constraint[] $constraints */
|
||||
list($uniqueNames, $insertNames, $updateNames) = $this->prepareUpsertColumns($table, $insertColumns, $updateColumns, $constraints);
|
||||
if (empty($uniqueNames)) {
|
||||
return $this->insert($table, $insertColumns, $params);
|
||||
}
|
||||
|
||||
$onCondition = ['or'];
|
||||
$quotedTableName = $this->db->quoteTableName($table);
|
||||
foreach ($constraints as $constraint) {
|
||||
$constraintCondition = ['and'];
|
||||
foreach ($constraint->columnNames as $name) {
|
||||
$quotedName = $this->db->quoteColumnName($name);
|
||||
$constraintCondition[] = "$quotedTableName.$quotedName=\"EXCLUDED\".$quotedName";
|
||||
}
|
||||
$onCondition[] = $constraintCondition;
|
||||
}
|
||||
$on = $this->buildCondition($onCondition, $params);
|
||||
list(, $placeholders, $values, $params) = $this->prepareInsertValues($table, $insertColumns, $params);
|
||||
$mergeSql = 'MERGE INTO ' . $this->db->quoteTableName($table) . ' '
|
||||
. 'USING (' . (!empty($placeholders) ? 'VALUES (' . implode(', ', $placeholders) . ')' : ltrim($values, ' ')) . ') AS "EXCLUDED" (' . implode(', ', $insertNames) . ') '
|
||||
. "ON ($on)";
|
||||
$insertValues = [];
|
||||
foreach ($insertNames as $name) {
|
||||
$quotedName = $this->db->quoteColumnName($name);
|
||||
if (strrpos($quotedName, '.') === false) {
|
||||
$quotedName = '"EXCLUDED".' . $quotedName;
|
||||
}
|
||||
$insertValues[] = $quotedName;
|
||||
}
|
||||
$insertSql = 'INSERT (' . implode(', ', $insertNames) . ')'
|
||||
. ' VALUES (' . implode(', ', $insertValues) . ')';
|
||||
if ($updateColumns === false) {
|
||||
return "$mergeSql WHEN NOT MATCHED THEN $insertSql";
|
||||
}
|
||||
|
||||
if ($updateColumns === true) {
|
||||
$updateColumns = [];
|
||||
foreach ($updateNames as $name) {
|
||||
$quotedName = $this->db->quoteColumnName($name);
|
||||
if (strrpos($quotedName, '.') === false) {
|
||||
$quotedName = '"EXCLUDED".' . $quotedName;
|
||||
}
|
||||
$updateColumns[$name] = new Expression($quotedName);
|
||||
}
|
||||
}
|
||||
list($updates, $params) = $this->prepareUpdateSets($table, $updateColumns, $params);
|
||||
$updateSql = 'UPDATE SET ' . implode(', ', $updates);
|
||||
return "$mergeSql WHEN MATCHED THEN $updateSql WHEN NOT MATCHED THEN $insertSql";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a SQL statement for resetting the sequence value of a table's primary key.
|
||||
* The sequence will be reset such that the primary key of the next new row inserted
|
||||
* will have the specified value or 1.
|
||||
* @param string $tableName the name of the table whose primary key sequence will be reset
|
||||
* @param mixed $value the value for the primary key of the next new row inserted. If this is not set,
|
||||
* the next new row's primary key will have a value 1.
|
||||
* @return string the SQL statement for resetting sequence
|
||||
* @throws InvalidArgumentException if the table does not exist or there is no sequence associated with the table.
|
||||
*/
|
||||
public function resetSequence($tableName, $value = null)
|
||||
{
|
||||
$table = $this->db->getTableSchema($tableName);
|
||||
if ($table !== null && $table->sequenceName !== null) {
|
||||
$tableName = $this->db->quoteTableName($tableName);
|
||||
if ($value === null) {
|
||||
$key = reset($table->primaryKey);
|
||||
$value = (int) $this->db->createCommand("SELECT MAX(`$key`) FROM " . $this->db->schema->quoteTableName($tableName))->queryScalar() + 1;
|
||||
} else {
|
||||
$value = (int) $value;
|
||||
}
|
||||
|
||||
return 'ALTER TABLE ' . $this->db->schema->quoteTableName($tableName) . " AUTO_INCREMENT=$value;";
|
||||
} elseif ($table === null) {
|
||||
throw new InvalidArgumentException("Table not found: $tableName");
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException("There is not sequence associated with table '$tableName'.");
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildLimit($limit, $offset)
|
||||
{
|
||||
$sql = '';
|
||||
// limit is not optional in CUBRID
|
||||
// http://www.cubrid.org/manual/90/en/LIMIT%20Clause
|
||||
// "You can specify a very big integer for row_count to display to the last row, starting from a specific row."
|
||||
if ($this->hasLimit($limit)) {
|
||||
$sql = 'LIMIT ' . $limit;
|
||||
if ($this->hasOffset($offset)) {
|
||||
$sql .= ' OFFSET ' . $offset;
|
||||
}
|
||||
} elseif ($this->hasOffset($offset)) {
|
||||
$sql = "LIMIT 9223372036854775807 OFFSET $offset"; // 2^63-1
|
||||
}
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @since 2.0.8
|
||||
*/
|
||||
public function selectExists($rawSql)
|
||||
{
|
||||
return 'SELECT CASE WHEN EXISTS(' . $rawSql . ') THEN 1 ELSE 0 END';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @see http://www.cubrid.org/manual/93/en/sql/schema/table.html#drop-index-clause
|
||||
*/
|
||||
public function dropIndex($name, $table)
|
||||
{
|
||||
/** @var Schema $schema */
|
||||
$schema = $this->db->getSchema();
|
||||
foreach ($schema->getTableUniques($table) as $unique) {
|
||||
if ($unique->name === $name) {
|
||||
return $this->dropUnique($name, $table);
|
||||
}
|
||||
}
|
||||
|
||||
return 'DROP INDEX ' . $this->db->quoteTableName($name) . ' ON ' . $this->db->quoteTableName($table);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @throws NotSupportedException this is not supported by CUBRID.
|
||||
*/
|
||||
public function addCheck($name, $table, $expression)
|
||||
{
|
||||
throw new NotSupportedException(__METHOD__ . ' is not supported by CUBRID.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @throws NotSupportedException this is not supported by CUBRID.
|
||||
*/
|
||||
public function dropCheck($name, $table)
|
||||
{
|
||||
throw new NotSupportedException(__METHOD__ . ' is not supported by CUBRID.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @since 2.0.8
|
||||
*/
|
||||
public function addCommentOnColumn($table, $column, $comment)
|
||||
{
|
||||
$definition = $this->getColumnDefinition($table, $column);
|
||||
$definition = trim(preg_replace("/COMMENT '(.*?)'/i", '', $definition));
|
||||
|
||||
return 'ALTER TABLE ' . $this->db->quoteTableName($table)
|
||||
. ' CHANGE ' . $this->db->quoteColumnName($column)
|
||||
. ' ' . $this->db->quoteColumnName($column)
|
||||
. (empty($definition) ? '' : ' ' . $definition)
|
||||
. ' COMMENT ' . $this->db->quoteValue($comment);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @since 2.0.8
|
||||
*/
|
||||
public function addCommentOnTable($table, $comment)
|
||||
{
|
||||
return 'ALTER TABLE ' . $this->db->quoteTableName($table) . ' COMMENT ' . $this->db->quoteValue($comment);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @since 2.0.8
|
||||
*/
|
||||
public function dropCommentFromColumn($table, $column)
|
||||
{
|
||||
return $this->addCommentOnColumn($table, $column, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @since 2.0.8
|
||||
*/
|
||||
public function dropCommentFromTable($table)
|
||||
{
|
||||
return $this->addCommentOnTable($table, '');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets column definition.
|
||||
*
|
||||
* @param string $table table name
|
||||
* @param string $column column name
|
||||
* @return null|string the column definition
|
||||
* @throws Exception in case when table does not contain column
|
||||
* @since 2.0.8
|
||||
*/
|
||||
private function getColumnDefinition($table, $column)
|
||||
{
|
||||
$row = $this->db->createCommand('SHOW CREATE TABLE ' . $this->db->quoteTableName($table))->queryOne();
|
||||
if ($row === false) {
|
||||
throw new Exception("Unable to find column '$column' in table '$table'.");
|
||||
}
|
||||
if (isset($row['Create Table'])) {
|
||||
$sql = $row['Create Table'];
|
||||
} else {
|
||||
$row = array_values($row);
|
||||
$sql = $row[1];
|
||||
}
|
||||
$sql = preg_replace('/^[^(]+\((.*)\).*$/', '\1', $sql);
|
||||
$sql = str_replace(', [', ",\n[", $sql);
|
||||
if (preg_match_all('/^\s*\[(.*?)\]\s+(.*?),?$/m', $sql, $matches)) {
|
||||
foreach ($matches[1] as $i => $c) {
|
||||
if ($c === $column) {
|
||||
return $matches[2][$i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
418
vendor/yiisoft/yii2/db/cubrid/Schema.php
vendored
Normal file
418
vendor/yiisoft/yii2/db/cubrid/Schema.php
vendored
Normal file
@@ -0,0 +1,418 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\cubrid;
|
||||
|
||||
use yii\base\NotSupportedException;
|
||||
use yii\db\Constraint;
|
||||
use yii\db\ConstraintFinderInterface;
|
||||
use yii\db\ConstraintFinderTrait;
|
||||
use yii\db\Expression;
|
||||
use yii\db\ForeignKeyConstraint;
|
||||
use yii\db\IndexConstraint;
|
||||
use yii\db\TableSchema;
|
||||
use yii\db\Transaction;
|
||||
use yii\helpers\ArrayHelper;
|
||||
|
||||
/**
|
||||
* Schema is the class for retrieving metadata from a CUBRID database (version 9.3.x and higher).
|
||||
*
|
||||
* @author Carsten Brandt <mail@cebe.cc>
|
||||
* @since 2.0
|
||||
*/
|
||||
class Schema extends \yii\db\Schema implements ConstraintFinderInterface
|
||||
{
|
||||
use ConstraintFinderTrait;
|
||||
|
||||
/**
|
||||
* @var array mapping from physical column types (keys) to abstract column types (values)
|
||||
* Please refer to [CUBRID manual](http://www.cubrid.org/manual/91/en/sql/datatype.html) for
|
||||
* details on data types.
|
||||
*/
|
||||
public $typeMap = [
|
||||
// Numeric data types
|
||||
'short' => self::TYPE_SMALLINT,
|
||||
'smallint' => self::TYPE_SMALLINT,
|
||||
'int' => self::TYPE_INTEGER,
|
||||
'integer' => self::TYPE_INTEGER,
|
||||
'bigint' => self::TYPE_BIGINT,
|
||||
'numeric' => self::TYPE_DECIMAL,
|
||||
'decimal' => self::TYPE_DECIMAL,
|
||||
'float' => self::TYPE_FLOAT,
|
||||
'real' => self::TYPE_FLOAT,
|
||||
'double' => self::TYPE_DOUBLE,
|
||||
'double precision' => self::TYPE_DOUBLE,
|
||||
'monetary' => self::TYPE_MONEY,
|
||||
// Date/Time data types
|
||||
'date' => self::TYPE_DATE,
|
||||
'time' => self::TYPE_TIME,
|
||||
'timestamp' => self::TYPE_TIMESTAMP,
|
||||
'datetime' => self::TYPE_DATETIME,
|
||||
// String data types
|
||||
'char' => self::TYPE_CHAR,
|
||||
'varchar' => self::TYPE_STRING,
|
||||
'char varying' => self::TYPE_STRING,
|
||||
'nchar' => self::TYPE_CHAR,
|
||||
'nchar varying' => self::TYPE_STRING,
|
||||
'string' => self::TYPE_STRING,
|
||||
// BLOB/CLOB data types
|
||||
'blob' => self::TYPE_BINARY,
|
||||
'clob' => self::TYPE_BINARY,
|
||||
// Bit string data types
|
||||
'bit' => self::TYPE_INTEGER,
|
||||
'bit varying' => self::TYPE_INTEGER,
|
||||
// Collection data types (considered strings for now)
|
||||
'set' => self::TYPE_STRING,
|
||||
'multiset' => self::TYPE_STRING,
|
||||
'list' => self::TYPE_STRING,
|
||||
'sequence' => self::TYPE_STRING,
|
||||
'enum' => self::TYPE_STRING,
|
||||
];
|
||||
/**
|
||||
* @var array map of DB errors and corresponding exceptions
|
||||
* If left part is found in DB error message exception class from the right part is used.
|
||||
*/
|
||||
public $exceptionMap = [
|
||||
'Operation would have caused one or more unique constraint violations' => 'yii\db\IntegrityException',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $tableQuoteCharacter = '"';
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function findTableNames($schema = '')
|
||||
{
|
||||
$pdo = $this->db->getSlavePdo();
|
||||
$tables = $pdo->cubrid_schema(\PDO::CUBRID_SCH_TABLE);
|
||||
$tableNames = [];
|
||||
foreach ($tables as $table) {
|
||||
// do not list system tables
|
||||
if ($table['TYPE'] != 0) {
|
||||
$tableNames[] = $table['NAME'];
|
||||
}
|
||||
}
|
||||
|
||||
return $tableNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function loadTableSchema($name)
|
||||
{
|
||||
$pdo = $this->db->getSlavePdo();
|
||||
|
||||
$tableInfo = $pdo->cubrid_schema(\PDO::CUBRID_SCH_TABLE, $name);
|
||||
|
||||
if (!isset($tableInfo[0]['NAME'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$table = new TableSchema();
|
||||
$table->fullName = $table->name = $tableInfo[0]['NAME'];
|
||||
|
||||
$sql = 'SHOW FULL COLUMNS FROM ' . $this->quoteSimpleTableName($table->name);
|
||||
$columns = $this->db->createCommand($sql)->queryAll();
|
||||
|
||||
foreach ($columns as $info) {
|
||||
$column = $this->loadColumnSchema($info);
|
||||
$table->columns[$column->name] = $column;
|
||||
}
|
||||
|
||||
$primaryKeys = $pdo->cubrid_schema(\PDO::CUBRID_SCH_PRIMARY_KEY, $table->name);
|
||||
foreach ($primaryKeys as $key) {
|
||||
$column = $table->columns[$key['ATTR_NAME']];
|
||||
$column->isPrimaryKey = true;
|
||||
$table->primaryKey[] = $column->name;
|
||||
if ($column->autoIncrement) {
|
||||
$table->sequenceName = '';
|
||||
}
|
||||
}
|
||||
|
||||
$foreignKeys = $pdo->cubrid_schema(\PDO::CUBRID_SCH_IMPORTED_KEYS, $table->name);
|
||||
foreach ($foreignKeys as $key) {
|
||||
if (isset($table->foreignKeys[$key['FK_NAME']])) {
|
||||
$table->foreignKeys[$key['FK_NAME']][$key['FKCOLUMN_NAME']] = $key['PKCOLUMN_NAME'];
|
||||
} else {
|
||||
$table->foreignKeys[$key['FK_NAME']] = [
|
||||
$key['PKTABLE_NAME'],
|
||||
$key['FKCOLUMN_NAME'] => $key['PKCOLUMN_NAME'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $table;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function loadTablePrimaryKey($tableName)
|
||||
{
|
||||
$primaryKey = $this->db->getSlavePdo()->cubrid_schema(\PDO::CUBRID_SCH_PRIMARY_KEY, $tableName);
|
||||
if (empty($primaryKey)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ArrayHelper::multisort($primaryKey, 'KEY_SEQ', SORT_ASC, SORT_NUMERIC);
|
||||
return new Constraint([
|
||||
'name' => $primaryKey[0]['KEY_NAME'],
|
||||
'columnNames' => ArrayHelper::getColumn($primaryKey, 'ATTR_NAME'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function loadTableForeignKeys($tableName)
|
||||
{
|
||||
static $actionTypes = [
|
||||
0 => 'CASCADE',
|
||||
1 => 'RESTRICT',
|
||||
2 => 'NO ACTION',
|
||||
3 => 'SET NULL',
|
||||
];
|
||||
|
||||
$foreignKeys = $this->db->getSlavePdo()->cubrid_schema(\PDO::CUBRID_SCH_IMPORTED_KEYS, $tableName);
|
||||
$foreignKeys = ArrayHelper::index($foreignKeys, null, 'FK_NAME');
|
||||
ArrayHelper::multisort($foreignKeys, 'KEY_SEQ', SORT_ASC, SORT_NUMERIC);
|
||||
$result = [];
|
||||
foreach ($foreignKeys as $name => $foreignKey) {
|
||||
$result[] = new ForeignKeyConstraint([
|
||||
'name' => $name,
|
||||
'columnNames' => ArrayHelper::getColumn($foreignKey, 'FKCOLUMN_NAME'),
|
||||
'foreignTableName' => $foreignKey[0]['PKTABLE_NAME'],
|
||||
'foreignColumnNames' => ArrayHelper::getColumn($foreignKey, 'PKCOLUMN_NAME'),
|
||||
'onDelete' => isset($actionTypes[$foreignKey[0]['DELETE_RULE']]) ? $actionTypes[$foreignKey[0]['DELETE_RULE']] : null,
|
||||
'onUpdate' => isset($actionTypes[$foreignKey[0]['UPDATE_RULE']]) ? $actionTypes[$foreignKey[0]['UPDATE_RULE']] : null,
|
||||
]);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function loadTableIndexes($tableName)
|
||||
{
|
||||
return $this->loadTableConstraints($tableName, 'indexes');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function loadTableUniques($tableName)
|
||||
{
|
||||
return $this->loadTableConstraints($tableName, 'uniques');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @throws NotSupportedException if this method is called.
|
||||
*/
|
||||
protected function loadTableChecks($tableName)
|
||||
{
|
||||
throw new NotSupportedException('CUBRID does not support check constraints.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @throws NotSupportedException if this method is called.
|
||||
*/
|
||||
protected function loadTableDefaultValues($tableName)
|
||||
{
|
||||
throw new NotSupportedException('CUBRID does not support default value constraints.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function releaseSavepoint($name)
|
||||
{
|
||||
// does nothing as cubrid does not support this
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a query builder for the CUBRID database.
|
||||
* @return QueryBuilder query builder instance
|
||||
*/
|
||||
public function createQueryBuilder()
|
||||
{
|
||||
return new QueryBuilder($this->db);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the column information into a [[ColumnSchema]] object.
|
||||
* @param array $info column information
|
||||
* @return \yii\db\ColumnSchema the column schema object
|
||||
*/
|
||||
protected function loadColumnSchema($info)
|
||||
{
|
||||
$column = $this->createColumnSchema();
|
||||
|
||||
$column->name = $info['Field'];
|
||||
$column->allowNull = $info['Null'] === 'YES';
|
||||
$column->isPrimaryKey = false; // primary key will be set by loadTableSchema() later
|
||||
$column->autoIncrement = stripos($info['Extra'], 'auto_increment') !== false;
|
||||
|
||||
$column->dbType = $info['Type'];
|
||||
$column->unsigned = strpos($column->dbType, 'unsigned') !== false;
|
||||
|
||||
$column->type = self::TYPE_STRING;
|
||||
if (preg_match('/^([\w ]+)(?:\(([^\)]+)\))?$/', $column->dbType, $matches)) {
|
||||
$type = strtolower($matches[1]);
|
||||
$column->dbType = $type . (isset($matches[2]) ? "({$matches[2]})" : '');
|
||||
if (isset($this->typeMap[$type])) {
|
||||
$column->type = $this->typeMap[$type];
|
||||
}
|
||||
if (!empty($matches[2])) {
|
||||
if ($type === 'enum') {
|
||||
$values = preg_split('/\s*,\s*/', $matches[2]);
|
||||
foreach ($values as $i => $value) {
|
||||
$values[$i] = trim($value, "'");
|
||||
}
|
||||
$column->enumValues = $values;
|
||||
} else {
|
||||
$values = explode(',', $matches[2]);
|
||||
$column->size = $column->precision = (int) $values[0];
|
||||
if (isset($values[1])) {
|
||||
$column->scale = (int) $values[1];
|
||||
}
|
||||
if ($column->size === 1 && $type === 'bit') {
|
||||
$column->type = 'boolean';
|
||||
} elseif ($type === 'bit') {
|
||||
if ($column->size > 32) {
|
||||
$column->type = 'bigint';
|
||||
} elseif ($column->size === 32) {
|
||||
$column->type = 'integer';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$column->phpType = $this->getColumnPhpType($column);
|
||||
|
||||
if ($column->isPrimaryKey) {
|
||||
return $column;
|
||||
}
|
||||
|
||||
if ($column->type === 'timestamp' && $info['Default'] === 'SYS_TIMESTAMP' ||
|
||||
$column->type === 'datetime' && $info['Default'] === 'SYS_DATETIME' ||
|
||||
$column->type === 'date' && $info['Default'] === 'SYS_DATE' ||
|
||||
$column->type === 'time' && $info['Default'] === 'SYS_TIME'
|
||||
) {
|
||||
$column->defaultValue = new Expression($info['Default']);
|
||||
} elseif (isset($type) && $type === 'bit') {
|
||||
$column->defaultValue = hexdec(trim($info['Default'], 'X\''));
|
||||
} else {
|
||||
$column->defaultValue = $column->phpTypecast($info['Default']);
|
||||
}
|
||||
|
||||
return $column;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the PDO type for the given PHP data value.
|
||||
* @param mixed $data the data whose PDO type is to be determined
|
||||
* @return int the PDO type
|
||||
* @see http://www.php.net/manual/en/pdo.constants.php
|
||||
*/
|
||||
public function getPdoType($data)
|
||||
{
|
||||
static $typeMap = [
|
||||
// php type => PDO type
|
||||
'boolean' => \PDO::PARAM_INT, // PARAM_BOOL is not supported by CUBRID PDO
|
||||
'integer' => \PDO::PARAM_INT,
|
||||
'string' => \PDO::PARAM_STR,
|
||||
'resource' => \PDO::PARAM_LOB,
|
||||
'NULL' => \PDO::PARAM_NULL,
|
||||
];
|
||||
$type = gettype($data);
|
||||
|
||||
return isset($typeMap[$type]) ? $typeMap[$type] : \PDO::PARAM_STR;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @see http://www.cubrid.org/manual/91/en/sql/transaction.html#database-concurrency
|
||||
*/
|
||||
public function setTransactionIsolationLevel($level)
|
||||
{
|
||||
// translate SQL92 levels to CUBRID levels:
|
||||
switch ($level) {
|
||||
case Transaction::SERIALIZABLE:
|
||||
$level = '6'; // SERIALIZABLE
|
||||
break;
|
||||
case Transaction::REPEATABLE_READ:
|
||||
$level = '5'; // REPEATABLE READ CLASS with REPEATABLE READ INSTANCES
|
||||
break;
|
||||
case Transaction::READ_COMMITTED:
|
||||
$level = '4'; // REPEATABLE READ CLASS with READ COMMITTED INSTANCES
|
||||
break;
|
||||
case Transaction::READ_UNCOMMITTED:
|
||||
$level = '3'; // REPEATABLE READ CLASS with READ UNCOMMITTED INSTANCES
|
||||
break;
|
||||
}
|
||||
parent::setTransactionIsolationLevel($level);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createColumnSchemaBuilder($type, $length = null)
|
||||
{
|
||||
return new ColumnSchemaBuilder($type, $length, $this->db);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads multiple types of constraints and returns the specified ones.
|
||||
* @param string $tableName table name.
|
||||
* @param string $returnType return type:
|
||||
* - indexes
|
||||
* - uniques
|
||||
* @return mixed constraints.
|
||||
*/
|
||||
private function loadTableConstraints($tableName, $returnType)
|
||||
{
|
||||
$constraints = $this->db->getSlavePdo()->cubrid_schema(\PDO::CUBRID_SCH_CONSTRAINT, $tableName);
|
||||
$constraints = ArrayHelper::index($constraints, null, ['TYPE', 'NAME']);
|
||||
ArrayHelper::multisort($constraints, 'KEY_ORDER', SORT_ASC, SORT_NUMERIC);
|
||||
$result = [
|
||||
'indexes' => [],
|
||||
'uniques' => [],
|
||||
];
|
||||
foreach ($constraints as $type => $names) {
|
||||
foreach ($names as $name => $constraint) {
|
||||
$isUnique = in_array((int) $type, [0, 2], true);
|
||||
$result['indexes'][] = new IndexConstraint([
|
||||
'isPrimary' => (bool) $constraint[0]['PRIMARY_KEY'],
|
||||
'isUnique' => $isUnique,
|
||||
'name' => $name,
|
||||
'columnNames' => ArrayHelper::getColumn($constraint, 'ATTR_NAME'),
|
||||
]);
|
||||
if ($isUnique) {
|
||||
$result['uniques'][] = new Constraint([
|
||||
'name' => $name,
|
||||
'columnNames' => ArrayHelper::getColumn($constraint, 'ATTR_NAME'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($result as $type => $data) {
|
||||
$this->setTableMetadata($tableName, $type, $data);
|
||||
}
|
||||
|
||||
return $result[$returnType];
|
||||
}
|
||||
}
|
||||
29
vendor/yiisoft/yii2/db/cubrid/conditions/LikeConditionBuilder.php
vendored
Normal file
29
vendor/yiisoft/yii2/db/cubrid/conditions/LikeConditionBuilder.php
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\cubrid\conditions;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class LikeConditionBuilder extends \yii\db\conditions\LikeConditionBuilder
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $escapeCharacter = '!';
|
||||
/**
|
||||
* `\` is initialized in [[buildLikeCondition()]] method since
|
||||
* we need to choose replacement value based on [[\yii\db\Schema::quoteValue()]].
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $escapingReplacements = [
|
||||
'%' => '!%',
|
||||
'_' => '!_',
|
||||
'!' => '!!',
|
||||
];
|
||||
}
|
||||
87
vendor/yiisoft/yii2/db/mssql/PDO.php
vendored
Normal file
87
vendor/yiisoft/yii2/db/mssql/PDO.php
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\mssql;
|
||||
|
||||
/**
|
||||
* This is an extension of the default PDO class of MSSQL and DBLIB drivers.
|
||||
* It provides workarounds for improperly implemented functionalities of the MSSQL and DBLIB drivers.
|
||||
*
|
||||
* @author Timur Ruziev <resurtm@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class PDO extends \PDO
|
||||
{
|
||||
/**
|
||||
* Returns value of the last inserted ID.
|
||||
* @param string|null $sequence the sequence name. Defaults to null.
|
||||
* @return int last inserted ID value.
|
||||
*/
|
||||
public function lastInsertId($sequence = null)
|
||||
{
|
||||
return $this->query('SELECT CAST(COALESCE(SCOPE_IDENTITY(), @@IDENTITY) AS bigint)')->fetchColumn();
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a transaction. It is necessary to override PDO's method as MSSQL PDO driver does not
|
||||
* natively support transactions.
|
||||
* @return bool the result of a transaction start.
|
||||
*/
|
||||
public function beginTransaction()
|
||||
{
|
||||
$this->exec('BEGIN TRANSACTION');
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Commits a transaction. It is necessary to override PDO's method as MSSQL PDO driver does not
|
||||
* natively support transactions.
|
||||
* @return bool the result of a transaction commit.
|
||||
*/
|
||||
public function commit()
|
||||
{
|
||||
$this->exec('COMMIT TRANSACTION');
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rollbacks a transaction. It is necessary to override PDO's method as MSSQL PDO driver does not
|
||||
* natively support transactions.
|
||||
* @return bool the result of a transaction roll back.
|
||||
*/
|
||||
public function rollBack()
|
||||
{
|
||||
$this->exec('ROLLBACK TRANSACTION');
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a database connection attribute.
|
||||
*
|
||||
* It is necessary to override PDO's method as some MSSQL PDO driver (e.g. dblib) does not
|
||||
* support getting attributes.
|
||||
* @param int $attribute One of the PDO::ATTR_* constants.
|
||||
* @return mixed A successful call returns the value of the requested PDO attribute.
|
||||
* An unsuccessful call returns null.
|
||||
*/
|
||||
public function getAttribute($attribute)
|
||||
{
|
||||
try {
|
||||
return parent::getAttribute($attribute);
|
||||
} catch (\PDOException $e) {
|
||||
switch ($attribute) {
|
||||
case self::ATTR_SERVER_VERSION:
|
||||
return $this->query("SELECT CAST(SERVERPROPERTY('productversion') AS VARCHAR)")->fetchColumn();
|
||||
default:
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
420
vendor/yiisoft/yii2/db/mssql/QueryBuilder.php
vendored
Normal file
420
vendor/yiisoft/yii2/db/mssql/QueryBuilder.php
vendored
Normal file
@@ -0,0 +1,420 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\mssql;
|
||||
|
||||
use yii\base\InvalidArgumentException;
|
||||
use yii\db\Constraint;
|
||||
use yii\db\Expression;
|
||||
|
||||
/**
|
||||
* QueryBuilder is the query builder for MS SQL Server databases (version 2008 and above).
|
||||
*
|
||||
* @author Timur Ruziev <resurtm@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class QueryBuilder extends \yii\db\QueryBuilder
|
||||
{
|
||||
/**
|
||||
* @var array mapping from abstract column types (keys) to physical column types (values).
|
||||
*/
|
||||
public $typeMap = [
|
||||
Schema::TYPE_PK => 'int IDENTITY PRIMARY KEY',
|
||||
Schema::TYPE_UPK => 'int IDENTITY PRIMARY KEY',
|
||||
Schema::TYPE_BIGPK => 'bigint IDENTITY PRIMARY KEY',
|
||||
Schema::TYPE_UBIGPK => 'bigint IDENTITY PRIMARY KEY',
|
||||
Schema::TYPE_CHAR => 'nchar(1)',
|
||||
Schema::TYPE_STRING => 'nvarchar(255)',
|
||||
Schema::TYPE_TEXT => 'nvarchar(max)',
|
||||
Schema::TYPE_TINYINT => 'tinyint',
|
||||
Schema::TYPE_SMALLINT => 'smallint',
|
||||
Schema::TYPE_INTEGER => 'int',
|
||||
Schema::TYPE_BIGINT => 'bigint',
|
||||
Schema::TYPE_FLOAT => 'float',
|
||||
Schema::TYPE_DOUBLE => 'float',
|
||||
Schema::TYPE_DECIMAL => 'decimal(18,0)',
|
||||
Schema::TYPE_DATETIME => 'datetime',
|
||||
Schema::TYPE_TIMESTAMP => 'datetime',
|
||||
Schema::TYPE_TIME => 'time',
|
||||
Schema::TYPE_DATE => 'date',
|
||||
Schema::TYPE_BINARY => 'varbinary(max)',
|
||||
Schema::TYPE_BOOLEAN => 'bit',
|
||||
Schema::TYPE_MONEY => 'decimal(19,4)',
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function defaultExpressionBuilders()
|
||||
{
|
||||
return array_merge(parent::defaultExpressionBuilders(), [
|
||||
'yii\db\conditions\InCondition' => 'yii\db\mssql\conditions\InConditionBuilder',
|
||||
'yii\db\conditions\LikeCondition' => 'yii\db\mssql\conditions\LikeConditionBuilder',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildOrderByAndLimit($sql, $orderBy, $limit, $offset)
|
||||
{
|
||||
if (!$this->hasOffset($offset) && !$this->hasLimit($limit)) {
|
||||
$orderBy = $this->buildOrderBy($orderBy);
|
||||
return $orderBy === '' ? $sql : $sql . $this->separator . $orderBy;
|
||||
}
|
||||
|
||||
if (version_compare($this->db->getSchema()->getServerVersion(), '11', '<')) {
|
||||
return $this->oldBuildOrderByAndLimit($sql, $orderBy, $limit, $offset);
|
||||
}
|
||||
|
||||
return $this->newBuildOrderByAndLimit($sql, $orderBy, $limit, $offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the ORDER BY/LIMIT/OFFSET clauses for SQL SERVER 2012 or newer.
|
||||
* @param string $sql the existing SQL (without ORDER BY/LIMIT/OFFSET)
|
||||
* @param array $orderBy the order by columns. See [[\yii\db\Query::orderBy]] for more details on how to specify this parameter.
|
||||
* @param int $limit the limit number. See [[\yii\db\Query::limit]] for more details.
|
||||
* @param int $offset the offset number. See [[\yii\db\Query::offset]] for more details.
|
||||
* @return string the SQL completed with ORDER BY/LIMIT/OFFSET (if any)
|
||||
*/
|
||||
protected function newBuildOrderByAndLimit($sql, $orderBy, $limit, $offset)
|
||||
{
|
||||
$orderBy = $this->buildOrderBy($orderBy);
|
||||
if ($orderBy === '') {
|
||||
// ORDER BY clause is required when FETCH and OFFSET are in the SQL
|
||||
$orderBy = 'ORDER BY (SELECT NULL)';
|
||||
}
|
||||
$sql .= $this->separator . $orderBy;
|
||||
|
||||
// http://technet.microsoft.com/en-us/library/gg699618.aspx
|
||||
$offset = $this->hasOffset($offset) ? $offset : '0';
|
||||
$sql .= $this->separator . "OFFSET $offset ROWS";
|
||||
if ($this->hasLimit($limit)) {
|
||||
$sql .= $this->separator . "FETCH NEXT $limit ROWS ONLY";
|
||||
}
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the ORDER BY/LIMIT/OFFSET clauses for SQL SERVER 2005 to 2008.
|
||||
* @param string $sql the existing SQL (without ORDER BY/LIMIT/OFFSET)
|
||||
* @param array $orderBy the order by columns. See [[\yii\db\Query::orderBy]] for more details on how to specify this parameter.
|
||||
* @param int $limit the limit number. See [[\yii\db\Query::limit]] for more details.
|
||||
* @param int $offset the offset number. See [[\yii\db\Query::offset]] for more details.
|
||||
* @return string the SQL completed with ORDER BY/LIMIT/OFFSET (if any)
|
||||
*/
|
||||
protected function oldBuildOrderByAndLimit($sql, $orderBy, $limit, $offset)
|
||||
{
|
||||
$orderBy = $this->buildOrderBy($orderBy);
|
||||
if ($orderBy === '') {
|
||||
// ROW_NUMBER() requires an ORDER BY clause
|
||||
$orderBy = 'ORDER BY (SELECT NULL)';
|
||||
}
|
||||
|
||||
$sql = preg_replace('/^([\s(])*SELECT(\s+DISTINCT)?(?!\s*TOP\s*\()/i', "\\1SELECT\\2 rowNum = ROW_NUMBER() over ($orderBy),", $sql);
|
||||
|
||||
if ($this->hasLimit($limit)) {
|
||||
$sql = "SELECT TOP $limit * FROM ($sql) sub";
|
||||
} else {
|
||||
$sql = "SELECT * FROM ($sql) sub";
|
||||
}
|
||||
if ($this->hasOffset($offset)) {
|
||||
$sql .= $this->separator . "WHERE rowNum > $offset";
|
||||
}
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a SQL statement for renaming a DB table.
|
||||
* @param string $oldName the table to be renamed. The name will be properly quoted by the method.
|
||||
* @param string $newName the new table name. The name will be properly quoted by the method.
|
||||
* @return string the SQL statement for renaming a DB table.
|
||||
*/
|
||||
public function renameTable($oldName, $newName)
|
||||
{
|
||||
return 'sp_rename ' . $this->db->quoteTableName($oldName) . ', ' . $this->db->quoteTableName($newName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a SQL statement for renaming a column.
|
||||
* @param string $table the table whose column is to be renamed. The name will be properly quoted by the method.
|
||||
* @param string $oldName the old name of the column. The name will be properly quoted by the method.
|
||||
* @param string $newName the new name of the column. The name will be properly quoted by the method.
|
||||
* @return string the SQL statement for renaming a DB column.
|
||||
*/
|
||||
public function renameColumn($table, $oldName, $newName)
|
||||
{
|
||||
$table = $this->db->quoteTableName($table);
|
||||
$oldName = $this->db->quoteColumnName($oldName);
|
||||
$newName = $this->db->quoteColumnName($newName);
|
||||
return "sp_rename '{$table}.{$oldName}', {$newName}, 'COLUMN'";
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a SQL statement for changing the definition of a column.
|
||||
* @param string $table the table whose column is to be changed. The table name will be properly quoted by the method.
|
||||
* @param string $column the name of the column to be changed. The name will be properly quoted by the method.
|
||||
* @param string $type the new column type. The [[getColumnType]] method will be invoked to convert abstract column type (if any)
|
||||
* into the physical one. Anything that is not recognized as abstract type will be kept in the generated SQL.
|
||||
* For example, 'string' will be turned into 'varchar(255)', while 'string not null' will become 'varchar(255) not null'.
|
||||
* @return string the SQL statement for changing the definition of a column.
|
||||
*/
|
||||
public function alterColumn($table, $column, $type)
|
||||
{
|
||||
$type = $this->getColumnType($type);
|
||||
$sql = 'ALTER TABLE ' . $this->db->quoteTableName($table) . ' ALTER COLUMN '
|
||||
. $this->db->quoteColumnName($column) . ' '
|
||||
. $this->getColumnType($type);
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addDefaultValue($name, $table, $column, $value)
|
||||
{
|
||||
return 'ALTER TABLE ' . $this->db->quoteTableName($table) . ' ADD CONSTRAINT '
|
||||
. $this->db->quoteColumnName($name) . ' DEFAULT ' . $this->db->quoteValue($value) . ' FOR '
|
||||
. $this->db->quoteColumnName($column);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function dropDefaultValue($name, $table)
|
||||
{
|
||||
return 'ALTER TABLE ' . $this->db->quoteTableName($table)
|
||||
. ' DROP CONSTRAINT ' . $this->db->quoteColumnName($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a SQL statement for resetting the sequence value of a table's primary key.
|
||||
* The sequence will be reset such that the primary key of the next new row inserted
|
||||
* will have the specified value or 1.
|
||||
* @param string $tableName the name of the table whose primary key sequence will be reset
|
||||
* @param mixed $value the value for the primary key of the next new row inserted. If this is not set,
|
||||
* the next new row's primary key will have a value 1.
|
||||
* @return string the SQL statement for resetting sequence
|
||||
* @throws InvalidArgumentException if the table does not exist or there is no sequence associated with the table.
|
||||
*/
|
||||
public function resetSequence($tableName, $value = null)
|
||||
{
|
||||
$table = $this->db->getTableSchema($tableName);
|
||||
if ($table !== null && $table->sequenceName !== null) {
|
||||
$tableName = $this->db->quoteTableName($tableName);
|
||||
if ($value === null) {
|
||||
$key = $this->db->quoteColumnName(reset($table->primaryKey));
|
||||
$value = "(SELECT COALESCE(MAX({$key}),0) FROM {$tableName})+1";
|
||||
} else {
|
||||
$value = (int) $value;
|
||||
}
|
||||
|
||||
return "DBCC CHECKIDENT ('{$tableName}', RESEED, {$value})";
|
||||
} elseif ($table === null) {
|
||||
throw new InvalidArgumentException("Table not found: $tableName");
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException("There is not sequence associated with table '$tableName'.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a SQL statement for enabling or disabling integrity check.
|
||||
* @param bool $check whether to turn on or off the integrity check.
|
||||
* @param string $schema the schema of the tables.
|
||||
* @param string $table the table name.
|
||||
* @return string the SQL statement for checking integrity
|
||||
*/
|
||||
public function checkIntegrity($check = true, $schema = '', $table = '')
|
||||
{
|
||||
$enable = $check ? 'CHECK' : 'NOCHECK';
|
||||
$schema = $schema ?: $this->db->getSchema()->defaultSchema;
|
||||
$tableNames = $this->db->getTableSchema($table) ? [$table] : $this->db->getSchema()->getTableNames($schema);
|
||||
$viewNames = $this->db->getSchema()->getViewNames($schema);
|
||||
$tableNames = array_diff($tableNames, $viewNames);
|
||||
$command = '';
|
||||
|
||||
foreach ($tableNames as $tableName) {
|
||||
$tableName = $this->db->quoteTableName("{$schema}.{$tableName}");
|
||||
$command .= "ALTER TABLE $tableName $enable CONSTRAINT ALL; ";
|
||||
}
|
||||
|
||||
return $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @since 2.0.8
|
||||
*/
|
||||
public function addCommentOnColumn($table, $column, $comment)
|
||||
{
|
||||
return "sp_updateextendedproperty @name = N'MS_Description', @value = {$this->db->quoteValue($comment)}, @level1type = N'Table', @level1name = {$this->db->quoteTableName($table)}, @level2type = N'Column', @level2name = {$this->db->quoteColumnName($column)}";
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @since 2.0.8
|
||||
*/
|
||||
public function addCommentOnTable($table, $comment)
|
||||
{
|
||||
return "sp_updateextendedproperty @name = N'MS_Description', @value = {$this->db->quoteValue($comment)}, @level1type = N'Table', @level1name = {$this->db->quoteTableName($table)}";
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @since 2.0.8
|
||||
*/
|
||||
public function dropCommentFromColumn($table, $column)
|
||||
{
|
||||
return "sp_dropextendedproperty @name = N'MS_Description', @level1type = N'Table', @level1name = {$this->db->quoteTableName($table)}, @level2type = N'Column', @level2name = {$this->db->quoteColumnName($column)}";
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @since 2.0.8
|
||||
*/
|
||||
public function dropCommentFromTable($table)
|
||||
{
|
||||
return "sp_dropextendedproperty @name = N'MS_Description', @level1type = N'Table', @level1name = {$this->db->quoteTableName($table)}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of column names given model name.
|
||||
*
|
||||
* @param string $modelClass name of the model class
|
||||
* @return array|null array of column names
|
||||
*/
|
||||
protected function getAllColumnNames($modelClass = null)
|
||||
{
|
||||
if (!$modelClass) {
|
||||
return null;
|
||||
}
|
||||
/* @var $modelClass \yii\db\ActiveRecord */
|
||||
$schema = $modelClass::getTableSchema();
|
||||
return array_keys($schema->columns);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool whether the version of the MSSQL being used is older than 2012.
|
||||
* @throws \yii\base\InvalidConfigException
|
||||
* @throws \yii\db\Exception
|
||||
* @deprecated 2.0.14 Use [[Schema::getServerVersion]] with [[\version_compare()]].
|
||||
*/
|
||||
protected function isOldMssql()
|
||||
{
|
||||
return version_compare($this->db->getSchema()->getServerVersion(), '11', '<');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @since 2.0.8
|
||||
*/
|
||||
public function selectExists($rawSql)
|
||||
{
|
||||
return 'SELECT CASE WHEN EXISTS(' . $rawSql . ') THEN 1 ELSE 0 END';
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes data to be saved into the table, performing extra preparations and type converting, if necessary.
|
||||
* @param string $table the table that data will be saved into.
|
||||
* @param array $columns the column data (name => value) to be saved into the table.
|
||||
* @return array normalized columns
|
||||
*/
|
||||
private function normalizeTableRowData($table, $columns, &$params)
|
||||
{
|
||||
if (($tableSchema = $this->db->getSchema()->getTableSchema($table)) !== null) {
|
||||
$columnSchemas = $tableSchema->columns;
|
||||
foreach ($columns as $name => $value) {
|
||||
// @see https://github.com/yiisoft/yii2/issues/12599
|
||||
if (isset($columnSchemas[$name]) && $columnSchemas[$name]->type === Schema::TYPE_BINARY && $columnSchemas[$name]->dbType === 'varbinary' && is_string($value)) {
|
||||
$exParams = [];
|
||||
$phName = $this->bindParam($value, $exParams);
|
||||
$columns[$name] = new Expression("CONVERT(VARBINARY, $phName)", $exParams);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function insert($table, $columns, &$params)
|
||||
{
|
||||
return parent::insert($table, $this->normalizeTableRowData($table, $columns, $params), $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @see https://docs.microsoft.com/en-us/sql/t-sql/statements/merge-transact-sql
|
||||
* @see http://weblogs.sqlteam.com/dang/archive/2009/01/31/UPSERT-Race-Condition-With-MERGE.aspx
|
||||
*/
|
||||
public function upsert($table, $insertColumns, $updateColumns, &$params)
|
||||
{
|
||||
/** @var Constraint[] $constraints */
|
||||
list($uniqueNames, $insertNames, $updateNames) = $this->prepareUpsertColumns($table, $insertColumns, $updateColumns, $constraints);
|
||||
if (empty($uniqueNames)) {
|
||||
return $this->insert($table, $insertColumns, $params);
|
||||
}
|
||||
|
||||
$onCondition = ['or'];
|
||||
$quotedTableName = $this->db->quoteTableName($table);
|
||||
foreach ($constraints as $constraint) {
|
||||
$constraintCondition = ['and'];
|
||||
foreach ($constraint->columnNames as $name) {
|
||||
$quotedName = $this->db->quoteColumnName($name);
|
||||
$constraintCondition[] = "$quotedTableName.$quotedName=[EXCLUDED].$quotedName";
|
||||
}
|
||||
$onCondition[] = $constraintCondition;
|
||||
}
|
||||
$on = $this->buildCondition($onCondition, $params);
|
||||
list(, $placeholders, $values, $params) = $this->prepareInsertValues($table, $insertColumns, $params);
|
||||
$mergeSql = 'MERGE ' . $this->db->quoteTableName($table) . ' WITH (HOLDLOCK) '
|
||||
. 'USING (' . (!empty($placeholders) ? 'VALUES (' . implode(', ', $placeholders) . ')' : ltrim($values, ' ')) . ') AS [EXCLUDED] (' . implode(', ', $insertNames) . ') '
|
||||
. "ON ($on)";
|
||||
$insertValues = [];
|
||||
foreach ($insertNames as $name) {
|
||||
$quotedName = $this->db->quoteColumnName($name);
|
||||
if (strrpos($quotedName, '.') === false) {
|
||||
$quotedName = '[EXCLUDED].' . $quotedName;
|
||||
}
|
||||
$insertValues[] = $quotedName;
|
||||
}
|
||||
$insertSql = 'INSERT (' . implode(', ', $insertNames) . ')'
|
||||
. ' VALUES (' . implode(', ', $insertValues) . ')';
|
||||
if ($updateColumns === false) {
|
||||
return "$mergeSql WHEN NOT MATCHED THEN $insertSql;";
|
||||
}
|
||||
|
||||
if ($updateColumns === true) {
|
||||
$updateColumns = [];
|
||||
foreach ($updateNames as $name) {
|
||||
$quotedName = $this->db->quoteColumnName($name);
|
||||
if (strrpos($quotedName, '.') === false) {
|
||||
$quotedName = '[EXCLUDED].' . $quotedName;
|
||||
}
|
||||
$updateColumns[$name] = new Expression($quotedName);
|
||||
}
|
||||
}
|
||||
list($updates, $params) = $this->prepareUpdateSets($table, $updateColumns, $params);
|
||||
$updateSql = 'UPDATE SET ' . implode(', ', $updates);
|
||||
return "$mergeSql WHEN MATCHED THEN $updateSql WHEN NOT MATCHED THEN $insertSql;";
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function update($table, $columns, $condition, &$params)
|
||||
{
|
||||
return parent::update($table, $this->normalizeTableRowData($table, $columns, $params), $condition, $params);
|
||||
}
|
||||
}
|
||||
705
vendor/yiisoft/yii2/db/mssql/Schema.php
vendored
Normal file
705
vendor/yiisoft/yii2/db/mssql/Schema.php
vendored
Normal file
@@ -0,0 +1,705 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\mssql;
|
||||
|
||||
use yii\db\CheckConstraint;
|
||||
use yii\db\ColumnSchema;
|
||||
use yii\db\Constraint;
|
||||
use yii\db\ConstraintFinderInterface;
|
||||
use yii\db\ConstraintFinderTrait;
|
||||
use yii\db\DefaultValueConstraint;
|
||||
use yii\db\ForeignKeyConstraint;
|
||||
use yii\db\IndexConstraint;
|
||||
use yii\db\ViewFinderTrait;
|
||||
use yii\helpers\ArrayHelper;
|
||||
|
||||
/**
|
||||
* Schema is the class for retrieving metadata from MS SQL Server databases (version 2008 and above).
|
||||
*
|
||||
* @author Timur Ruziev <resurtm@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class Schema extends \yii\db\Schema implements ConstraintFinderInterface
|
||||
{
|
||||
use ViewFinderTrait;
|
||||
use ConstraintFinderTrait;
|
||||
|
||||
/**
|
||||
* @var string the default schema used for the current session.
|
||||
*/
|
||||
public $defaultSchema = 'dbo';
|
||||
/**
|
||||
* @var array mapping from physical column types (keys) to abstract column types (values)
|
||||
*/
|
||||
public $typeMap = [
|
||||
// exact numbers
|
||||
'bigint' => self::TYPE_BIGINT,
|
||||
'numeric' => self::TYPE_DECIMAL,
|
||||
'bit' => self::TYPE_SMALLINT,
|
||||
'smallint' => self::TYPE_SMALLINT,
|
||||
'decimal' => self::TYPE_DECIMAL,
|
||||
'smallmoney' => self::TYPE_MONEY,
|
||||
'int' => self::TYPE_INTEGER,
|
||||
'tinyint' => self::TYPE_TINYINT,
|
||||
'money' => self::TYPE_MONEY,
|
||||
// approximate numbers
|
||||
'float' => self::TYPE_FLOAT,
|
||||
'double' => self::TYPE_DOUBLE,
|
||||
'real' => self::TYPE_FLOAT,
|
||||
// date and time
|
||||
'date' => self::TYPE_DATE,
|
||||
'datetimeoffset' => self::TYPE_DATETIME,
|
||||
'datetime2' => self::TYPE_DATETIME,
|
||||
'smalldatetime' => self::TYPE_DATETIME,
|
||||
'datetime' => self::TYPE_DATETIME,
|
||||
'time' => self::TYPE_TIME,
|
||||
// character strings
|
||||
'char' => self::TYPE_CHAR,
|
||||
'varchar' => self::TYPE_STRING,
|
||||
'text' => self::TYPE_TEXT,
|
||||
// unicode character strings
|
||||
'nchar' => self::TYPE_CHAR,
|
||||
'nvarchar' => self::TYPE_STRING,
|
||||
'ntext' => self::TYPE_TEXT,
|
||||
// binary strings
|
||||
'binary' => self::TYPE_BINARY,
|
||||
'varbinary' => self::TYPE_BINARY,
|
||||
'image' => self::TYPE_BINARY,
|
||||
// other data types
|
||||
// 'cursor' type cannot be used with tables
|
||||
'timestamp' => self::TYPE_TIMESTAMP,
|
||||
'hierarchyid' => self::TYPE_STRING,
|
||||
'uniqueidentifier' => self::TYPE_STRING,
|
||||
'sql_variant' => self::TYPE_STRING,
|
||||
'xml' => self::TYPE_STRING,
|
||||
'table' => self::TYPE_STRING,
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $tableQuoteCharacter = ['[', ']'];
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $columnQuoteCharacter = ['[', ']'];
|
||||
|
||||
|
||||
/**
|
||||
* Resolves the table name and schema name (if any).
|
||||
* @param string $name the table name
|
||||
* @return TableSchema resolved table, schema, etc. names.
|
||||
*/
|
||||
protected function resolveTableName($name)
|
||||
{
|
||||
$resolvedName = new TableSchema();
|
||||
$parts = explode('.', str_replace(['[', ']'], '', $name));
|
||||
$partCount = count($parts);
|
||||
if ($partCount === 4) {
|
||||
// server name, catalog name, schema name and table name passed
|
||||
$resolvedName->catalogName = $parts[1];
|
||||
$resolvedName->schemaName = $parts[2];
|
||||
$resolvedName->name = $parts[3];
|
||||
$resolvedName->fullName = $resolvedName->catalogName . '.' . $resolvedName->schemaName . '.' . $resolvedName->name;
|
||||
} elseif ($partCount === 3) {
|
||||
// catalog name, schema name and table name passed
|
||||
$resolvedName->catalogName = $parts[0];
|
||||
$resolvedName->schemaName = $parts[1];
|
||||
$resolvedName->name = $parts[2];
|
||||
$resolvedName->fullName = $resolvedName->catalogName . '.' . $resolvedName->schemaName . '.' . $resolvedName->name;
|
||||
} elseif ($partCount === 2) {
|
||||
// only schema name and table name passed
|
||||
$resolvedName->schemaName = $parts[0];
|
||||
$resolvedName->name = $parts[1];
|
||||
$resolvedName->fullName = ($resolvedName->schemaName !== $this->defaultSchema ? $resolvedName->schemaName . '.' : '') . $resolvedName->name;
|
||||
} else {
|
||||
// only table name passed
|
||||
$resolvedName->schemaName = $this->defaultSchema;
|
||||
$resolvedName->fullName = $resolvedName->name = $parts[0];
|
||||
}
|
||||
|
||||
return $resolvedName;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @see https://docs.microsoft.com/en-us/sql/relational-databases/system-catalog-views/sys-database-principals-transact-sql
|
||||
*/
|
||||
protected function findSchemaNames()
|
||||
{
|
||||
static $sql = <<<'SQL'
|
||||
SELECT [s].[name]
|
||||
FROM [sys].[schemas] AS [s]
|
||||
INNER JOIN [sys].[database_principals] AS [p] ON [p].[principal_id] = [s].[principal_id]
|
||||
WHERE [p].[is_fixed_role] = 0 AND [p].[sid] IS NOT NULL
|
||||
ORDER BY [s].[name] ASC
|
||||
SQL;
|
||||
|
||||
return $this->db->createCommand($sql)->queryColumn();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function findTableNames($schema = '')
|
||||
{
|
||||
if ($schema === '') {
|
||||
$schema = $this->defaultSchema;
|
||||
}
|
||||
|
||||
$sql = <<<'SQL'
|
||||
SELECT [t].[table_name]
|
||||
FROM [INFORMATION_SCHEMA].[TABLES] AS [t]
|
||||
WHERE [t].[table_schema] = :schema AND [t].[table_type] IN ('BASE TABLE', 'VIEW')
|
||||
ORDER BY [t].[table_name]
|
||||
SQL;
|
||||
|
||||
return $this->db->createCommand($sql, [':schema' => $schema])->queryColumn();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function loadTableSchema($name)
|
||||
{
|
||||
$table = new TableSchema();
|
||||
$this->resolveTableNames($table, $name);
|
||||
$this->findPrimaryKeys($table);
|
||||
if ($this->findColumns($table)) {
|
||||
$this->findForeignKeys($table);
|
||||
return $table;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function loadTablePrimaryKey($tableName)
|
||||
{
|
||||
return $this->loadTableConstraints($tableName, 'primaryKey');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function loadTableForeignKeys($tableName)
|
||||
{
|
||||
return $this->loadTableConstraints($tableName, 'foreignKeys');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function loadTableIndexes($tableName)
|
||||
{
|
||||
static $sql = <<<'SQL'
|
||||
SELECT
|
||||
[i].[name] AS [name],
|
||||
[iccol].[name] AS [column_name],
|
||||
[i].[is_unique] AS [index_is_unique],
|
||||
[i].[is_primary_key] AS [index_is_primary]
|
||||
FROM [sys].[indexes] AS [i]
|
||||
INNER JOIN [sys].[index_columns] AS [ic]
|
||||
ON [ic].[object_id] = [i].[object_id] AND [ic].[index_id] = [i].[index_id]
|
||||
INNER JOIN [sys].[columns] AS [iccol]
|
||||
ON [iccol].[object_id] = [ic].[object_id] AND [iccol].[column_id] = [ic].[column_id]
|
||||
WHERE [i].[object_id] = OBJECT_ID(:fullName)
|
||||
ORDER BY [ic].[key_ordinal] ASC
|
||||
SQL;
|
||||
|
||||
$resolvedName = $this->resolveTableName($tableName);
|
||||
$indexes = $this->db->createCommand($sql, [
|
||||
':fullName' => $resolvedName->fullName,
|
||||
])->queryAll();
|
||||
$indexes = $this->normalizePdoRowKeyCase($indexes, true);
|
||||
$indexes = ArrayHelper::index($indexes, null, 'name');
|
||||
$result = [];
|
||||
foreach ($indexes as $name => $index) {
|
||||
$result[] = new IndexConstraint([
|
||||
'isPrimary' => (bool) $index[0]['index_is_primary'],
|
||||
'isUnique' => (bool) $index[0]['index_is_unique'],
|
||||
'name' => $name,
|
||||
'columnNames' => ArrayHelper::getColumn($index, 'column_name'),
|
||||
]);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function loadTableUniques($tableName)
|
||||
{
|
||||
return $this->loadTableConstraints($tableName, 'uniques');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function loadTableChecks($tableName)
|
||||
{
|
||||
return $this->loadTableConstraints($tableName, 'checks');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function loadTableDefaultValues($tableName)
|
||||
{
|
||||
return $this->loadTableConstraints($tableName, 'defaults');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createSavepoint($name)
|
||||
{
|
||||
$this->db->createCommand("SAVE TRANSACTION $name")->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function releaseSavepoint($name)
|
||||
{
|
||||
// does nothing as MSSQL does not support this
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function rollBackSavepoint($name)
|
||||
{
|
||||
$this->db->createCommand("ROLLBACK TRANSACTION $name")->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a query builder for the MSSQL database.
|
||||
* @return QueryBuilder query builder interface.
|
||||
*/
|
||||
public function createQueryBuilder()
|
||||
{
|
||||
return new QueryBuilder($this->db);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the table name and schema name (if any).
|
||||
* @param TableSchema $table the table metadata object
|
||||
* @param string $name the table name
|
||||
*/
|
||||
protected function resolveTableNames($table, $name)
|
||||
{
|
||||
$parts = explode('.', str_replace(['[', ']'], '', $name));
|
||||
$partCount = count($parts);
|
||||
if ($partCount === 4) {
|
||||
// server name, catalog name, schema name and table name passed
|
||||
$table->catalogName = $parts[1];
|
||||
$table->schemaName = $parts[2];
|
||||
$table->name = $parts[3];
|
||||
$table->fullName = $table->catalogName . '.' . $table->schemaName . '.' . $table->name;
|
||||
} elseif ($partCount === 3) {
|
||||
// catalog name, schema name and table name passed
|
||||
$table->catalogName = $parts[0];
|
||||
$table->schemaName = $parts[1];
|
||||
$table->name = $parts[2];
|
||||
$table->fullName = $table->catalogName . '.' . $table->schemaName . '.' . $table->name;
|
||||
} elseif ($partCount === 2) {
|
||||
// only schema name and table name passed
|
||||
$table->schemaName = $parts[0];
|
||||
$table->name = $parts[1];
|
||||
$table->fullName = $table->schemaName !== $this->defaultSchema ? $table->schemaName . '.' . $table->name : $table->name;
|
||||
} else {
|
||||
// only table name passed
|
||||
$table->schemaName = $this->defaultSchema;
|
||||
$table->fullName = $table->name = $parts[0];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the column information into a [[ColumnSchema]] object.
|
||||
* @param array $info column information
|
||||
* @return ColumnSchema the column schema object
|
||||
*/
|
||||
protected function loadColumnSchema($info)
|
||||
{
|
||||
$column = $this->createColumnSchema();
|
||||
|
||||
$column->name = $info['column_name'];
|
||||
$column->allowNull = $info['is_nullable'] === 'YES';
|
||||
$column->dbType = $info['data_type'];
|
||||
$column->enumValues = []; // mssql has only vague equivalents to enum
|
||||
$column->isPrimaryKey = null; // primary key will be determined in findColumns() method
|
||||
$column->autoIncrement = $info['is_identity'] == 1;
|
||||
$column->unsigned = stripos($column->dbType, 'unsigned') !== false;
|
||||
$column->comment = $info['comment'] === null ? '' : $info['comment'];
|
||||
|
||||
$column->type = self::TYPE_STRING;
|
||||
if (preg_match('/^(\w+)(?:\(([^\)]+)\))?/', $column->dbType, $matches)) {
|
||||
$type = $matches[1];
|
||||
if (isset($this->typeMap[$type])) {
|
||||
$column->type = $this->typeMap[$type];
|
||||
}
|
||||
if (!empty($matches[2])) {
|
||||
$values = explode(',', $matches[2]);
|
||||
$column->size = $column->precision = (int) $values[0];
|
||||
if (isset($values[1])) {
|
||||
$column->scale = (int) $values[1];
|
||||
}
|
||||
if ($column->size === 1 && ($type === 'tinyint' || $type === 'bit')) {
|
||||
$column->type = 'boolean';
|
||||
} elseif ($type === 'bit') {
|
||||
if ($column->size > 32) {
|
||||
$column->type = 'bigint';
|
||||
} elseif ($column->size === 32) {
|
||||
$column->type = 'integer';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$column->phpType = $this->getColumnPhpType($column);
|
||||
|
||||
if ($info['column_default'] === '(NULL)') {
|
||||
$info['column_default'] = null;
|
||||
}
|
||||
if (!$column->isPrimaryKey && ($column->type !== 'timestamp' || $info['column_default'] !== 'CURRENT_TIMESTAMP')) {
|
||||
$column->defaultValue = $column->phpTypecast($info['column_default']);
|
||||
}
|
||||
|
||||
return $column;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects the metadata of table columns.
|
||||
* @param TableSchema $table the table metadata
|
||||
* @return bool whether the table exists in the database
|
||||
*/
|
||||
protected function findColumns($table)
|
||||
{
|
||||
$columnsTableName = 'INFORMATION_SCHEMA.COLUMNS';
|
||||
$whereSql = "[t1].[table_name] = '{$table->name}'";
|
||||
if ($table->catalogName !== null) {
|
||||
$columnsTableName = "{$table->catalogName}.{$columnsTableName}";
|
||||
$whereSql .= " AND [t1].[table_catalog] = '{$table->catalogName}'";
|
||||
}
|
||||
if ($table->schemaName !== null) {
|
||||
$whereSql .= " AND [t1].[table_schema] = '{$table->schemaName}'";
|
||||
}
|
||||
$columnsTableName = $this->quoteTableName($columnsTableName);
|
||||
|
||||
$sql = <<<SQL
|
||||
SELECT
|
||||
[t1].[column_name],
|
||||
[t1].[is_nullable],
|
||||
[t1].[data_type],
|
||||
[t1].[column_default],
|
||||
COLUMNPROPERTY(OBJECT_ID([t1].[table_schema] + '.' + [t1].[table_name]), [t1].[column_name], 'IsIdentity') AS is_identity,
|
||||
(
|
||||
SELECT CONVERT(VARCHAR, [t2].[value])
|
||||
FROM [sys].[extended_properties] AS [t2]
|
||||
WHERE
|
||||
[t2].[class] = 1 AND
|
||||
[t2].[class_desc] = 'OBJECT_OR_COLUMN' AND
|
||||
[t2].[name] = 'MS_Description' AND
|
||||
[t2].[major_id] = OBJECT_ID([t1].[TABLE_SCHEMA] + '.' + [t1].[table_name]) AND
|
||||
[t2].[minor_id] = COLUMNPROPERTY(OBJECT_ID([t1].[TABLE_SCHEMA] + '.' + [t1].[TABLE_NAME]), [t1].[COLUMN_NAME], 'ColumnID')
|
||||
) as comment
|
||||
FROM {$columnsTableName} AS [t1]
|
||||
WHERE {$whereSql}
|
||||
SQL;
|
||||
|
||||
try {
|
||||
$columns = $this->db->createCommand($sql)->queryAll();
|
||||
if (empty($columns)) {
|
||||
return false;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
return false;
|
||||
}
|
||||
foreach ($columns as $column) {
|
||||
$column = $this->loadColumnSchema($column);
|
||||
foreach ($table->primaryKey as $primaryKey) {
|
||||
if (strcasecmp($column->name, $primaryKey) === 0) {
|
||||
$column->isPrimaryKey = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($column->isPrimaryKey && $column->autoIncrement) {
|
||||
$table->sequenceName = '';
|
||||
}
|
||||
$table->columns[$column->name] = $column;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects the constraint details for the given table and constraint type.
|
||||
* @param TableSchema $table
|
||||
* @param string $type either PRIMARY KEY or UNIQUE
|
||||
* @return array each entry contains index_name and field_name
|
||||
* @since 2.0.4
|
||||
*/
|
||||
protected function findTableConstraints($table, $type)
|
||||
{
|
||||
$keyColumnUsageTableName = 'INFORMATION_SCHEMA.KEY_COLUMN_USAGE';
|
||||
$tableConstraintsTableName = 'INFORMATION_SCHEMA.TABLE_CONSTRAINTS';
|
||||
if ($table->catalogName !== null) {
|
||||
$keyColumnUsageTableName = $table->catalogName . '.' . $keyColumnUsageTableName;
|
||||
$tableConstraintsTableName = $table->catalogName . '.' . $tableConstraintsTableName;
|
||||
}
|
||||
$keyColumnUsageTableName = $this->quoteTableName($keyColumnUsageTableName);
|
||||
$tableConstraintsTableName = $this->quoteTableName($tableConstraintsTableName);
|
||||
|
||||
$sql = <<<SQL
|
||||
SELECT
|
||||
[kcu].[constraint_name] AS [index_name],
|
||||
[kcu].[column_name] AS [field_name]
|
||||
FROM {$keyColumnUsageTableName} AS [kcu]
|
||||
LEFT JOIN {$tableConstraintsTableName} AS [tc] ON
|
||||
[kcu].[table_schema] = [tc].[table_schema] AND
|
||||
[kcu].[table_name] = [tc].[table_name] AND
|
||||
[kcu].[constraint_name] = [tc].[constraint_name]
|
||||
WHERE
|
||||
[tc].[constraint_type] = :type AND
|
||||
[kcu].[table_name] = :tableName AND
|
||||
[kcu].[table_schema] = :schemaName
|
||||
SQL;
|
||||
|
||||
return $this->db
|
||||
->createCommand($sql, [
|
||||
':tableName' => $table->name,
|
||||
':schemaName' => $table->schemaName,
|
||||
':type' => $type,
|
||||
])
|
||||
->queryAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects the primary key column details for the given table.
|
||||
* @param TableSchema $table the table metadata
|
||||
*/
|
||||
protected function findPrimaryKeys($table)
|
||||
{
|
||||
$result = [];
|
||||
foreach ($this->findTableConstraints($table, 'PRIMARY KEY') as $row) {
|
||||
$result[] = $row['field_name'];
|
||||
}
|
||||
$table->primaryKey = $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects the foreign key column details for the given table.
|
||||
* @param TableSchema $table the table metadata
|
||||
*/
|
||||
protected function findForeignKeys($table)
|
||||
{
|
||||
$object = $table->name;
|
||||
if ($table->schemaName !== null) {
|
||||
$object = $table->schemaName . '.' . $object;
|
||||
}
|
||||
if ($table->catalogName !== null) {
|
||||
$object = $table->catalogName . '.' . $object;
|
||||
}
|
||||
|
||||
// please refer to the following page for more details:
|
||||
// http://msdn2.microsoft.com/en-us/library/aa175805(SQL.80).aspx
|
||||
$sql = <<<'SQL'
|
||||
SELECT
|
||||
[fk].[name] AS [fk_name],
|
||||
[cp].[name] AS [fk_column_name],
|
||||
OBJECT_NAME([fk].[referenced_object_id]) AS [uq_table_name],
|
||||
[cr].[name] AS [uq_column_name]
|
||||
FROM
|
||||
[sys].[foreign_keys] AS [fk]
|
||||
INNER JOIN [sys].[foreign_key_columns] AS [fkc] ON
|
||||
[fk].[object_id] = [fkc].[constraint_object_id]
|
||||
INNER JOIN [sys].[columns] AS [cp] ON
|
||||
[fk].[parent_object_id] = [cp].[object_id] AND
|
||||
[fkc].[parent_column_id] = [cp].[column_id]
|
||||
INNER JOIN [sys].[columns] AS [cr] ON
|
||||
[fk].[referenced_object_id] = [cr].[object_id] AND
|
||||
[fkc].[referenced_column_id] = [cr].[column_id]
|
||||
WHERE
|
||||
[fk].[parent_object_id] = OBJECT_ID(:object)
|
||||
SQL;
|
||||
|
||||
$rows = $this->db->createCommand($sql, [
|
||||
':object' => $object,
|
||||
])->queryAll();
|
||||
|
||||
$table->foreignKeys = [];
|
||||
foreach ($rows as $row) {
|
||||
$table->foreignKeys[$row['fk_name']] = [$row['uq_table_name'], $row['fk_column_name'] => $row['uq_column_name']];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function findViewNames($schema = '')
|
||||
{
|
||||
if ($schema === '') {
|
||||
$schema = $this->defaultSchema;
|
||||
}
|
||||
|
||||
$sql = <<<'SQL'
|
||||
SELECT [t].[table_name]
|
||||
FROM [INFORMATION_SCHEMA].[TABLES] AS [t]
|
||||
WHERE [t].[table_schema] = :schema AND [t].[table_type] = 'VIEW'
|
||||
ORDER BY [t].[table_name]
|
||||
SQL;
|
||||
|
||||
return $this->db->createCommand($sql, [':schema' => $schema])->queryColumn();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all unique indexes for the given table.
|
||||
*
|
||||
* Each array element is of the following structure:
|
||||
*
|
||||
* ```php
|
||||
* [
|
||||
* 'IndexName1' => ['col1' [, ...]],
|
||||
* 'IndexName2' => ['col2' [, ...]],
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* @param TableSchema $table the table metadata
|
||||
* @return array all unique indexes for the given table.
|
||||
* @since 2.0.4
|
||||
*/
|
||||
public function findUniqueIndexes($table)
|
||||
{
|
||||
$result = [];
|
||||
foreach ($this->findTableConstraints($table, 'UNIQUE') as $row) {
|
||||
$result[$row['index_name']][] = $row['field_name'];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads multiple types of constraints and returns the specified ones.
|
||||
* @param string $tableName table name.
|
||||
* @param string $returnType return type:
|
||||
* - primaryKey
|
||||
* - foreignKeys
|
||||
* - uniques
|
||||
* - checks
|
||||
* - defaults
|
||||
* @return mixed constraints.
|
||||
*/
|
||||
private function loadTableConstraints($tableName, $returnType)
|
||||
{
|
||||
static $sql = <<<'SQL'
|
||||
SELECT
|
||||
[o].[name] AS [name],
|
||||
COALESCE([ccol].[name], [dcol].[name], [fccol].[name], [kiccol].[name]) AS [column_name],
|
||||
RTRIM([o].[type]) AS [type],
|
||||
OBJECT_SCHEMA_NAME([f].[referenced_object_id]) AS [foreign_table_schema],
|
||||
OBJECT_NAME([f].[referenced_object_id]) AS [foreign_table_name],
|
||||
[ffccol].[name] AS [foreign_column_name],
|
||||
[f].[update_referential_action_desc] AS [on_update],
|
||||
[f].[delete_referential_action_desc] AS [on_delete],
|
||||
[c].[definition] AS [check_expr],
|
||||
[d].[definition] AS [default_expr]
|
||||
FROM (SELECT OBJECT_ID(:fullName) AS [object_id]) AS [t]
|
||||
INNER JOIN [sys].[objects] AS [o]
|
||||
ON [o].[parent_object_id] = [t].[object_id] AND [o].[type] IN ('PK', 'UQ', 'C', 'D', 'F')
|
||||
LEFT JOIN [sys].[check_constraints] AS [c]
|
||||
ON [c].[object_id] = [o].[object_id]
|
||||
LEFT JOIN [sys].[columns] AS [ccol]
|
||||
ON [ccol].[object_id] = [c].[parent_object_id] AND [ccol].[column_id] = [c].[parent_column_id]
|
||||
LEFT JOIN [sys].[default_constraints] AS [d]
|
||||
ON [d].[object_id] = [o].[object_id]
|
||||
LEFT JOIN [sys].[columns] AS [dcol]
|
||||
ON [dcol].[object_id] = [d].[parent_object_id] AND [dcol].[column_id] = [d].[parent_column_id]
|
||||
LEFT JOIN [sys].[key_constraints] AS [k]
|
||||
ON [k].[object_id] = [o].[object_id]
|
||||
LEFT JOIN [sys].[index_columns] AS [kic]
|
||||
ON [kic].[object_id] = [k].[parent_object_id] AND [kic].[index_id] = [k].[unique_index_id]
|
||||
LEFT JOIN [sys].[columns] AS [kiccol]
|
||||
ON [kiccol].[object_id] = [kic].[object_id] AND [kiccol].[column_id] = [kic].[column_id]
|
||||
LEFT JOIN [sys].[foreign_keys] AS [f]
|
||||
ON [f].[object_id] = [o].[object_id]
|
||||
LEFT JOIN [sys].[foreign_key_columns] AS [fc]
|
||||
ON [fc].[constraint_object_id] = [o].[object_id]
|
||||
LEFT JOIN [sys].[columns] AS [fccol]
|
||||
ON [fccol].[object_id] = [fc].[parent_object_id] AND [fccol].[column_id] = [fc].[parent_column_id]
|
||||
LEFT JOIN [sys].[columns] AS [ffccol]
|
||||
ON [ffccol].[object_id] = [fc].[referenced_object_id] AND [ffccol].[column_id] = [fc].[referenced_column_id]
|
||||
ORDER BY [kic].[key_ordinal] ASC, [fc].[constraint_column_id] ASC
|
||||
SQL;
|
||||
|
||||
$resolvedName = $this->resolveTableName($tableName);
|
||||
$constraints = $this->db->createCommand($sql, [
|
||||
':fullName' => $resolvedName->fullName,
|
||||
])->queryAll();
|
||||
$constraints = $this->normalizePdoRowKeyCase($constraints, true);
|
||||
$constraints = ArrayHelper::index($constraints, null, ['type', 'name']);
|
||||
$result = [
|
||||
'primaryKey' => null,
|
||||
'foreignKeys' => [],
|
||||
'uniques' => [],
|
||||
'checks' => [],
|
||||
'defaults' => [],
|
||||
];
|
||||
foreach ($constraints as $type => $names) {
|
||||
foreach ($names as $name => $constraint) {
|
||||
switch ($type) {
|
||||
case 'PK':
|
||||
$result['primaryKey'] = new Constraint([
|
||||
'name' => $name,
|
||||
'columnNames' => ArrayHelper::getColumn($constraint, 'column_name'),
|
||||
]);
|
||||
break;
|
||||
case 'F':
|
||||
$result['foreignKeys'][] = new ForeignKeyConstraint([
|
||||
'name' => $name,
|
||||
'columnNames' => ArrayHelper::getColumn($constraint, 'column_name'),
|
||||
'foreignSchemaName' => $constraint[0]['foreign_table_schema'],
|
||||
'foreignTableName' => $constraint[0]['foreign_table_name'],
|
||||
'foreignColumnNames' => ArrayHelper::getColumn($constraint, 'foreign_column_name'),
|
||||
'onDelete' => str_replace('_', '', $constraint[0]['on_delete']),
|
||||
'onUpdate' => str_replace('_', '', $constraint[0]['on_update']),
|
||||
]);
|
||||
break;
|
||||
case 'UQ':
|
||||
$result['uniques'][] = new Constraint([
|
||||
'name' => $name,
|
||||
'columnNames' => ArrayHelper::getColumn($constraint, 'column_name'),
|
||||
]);
|
||||
break;
|
||||
case 'C':
|
||||
$result['checks'][] = new CheckConstraint([
|
||||
'name' => $name,
|
||||
'columnNames' => ArrayHelper::getColumn($constraint, 'column_name'),
|
||||
'expression' => $constraint[0]['check_expr'],
|
||||
]);
|
||||
break;
|
||||
case 'D':
|
||||
$result['defaults'][] = new DefaultValueConstraint([
|
||||
'name' => $name,
|
||||
'columnNames' => ArrayHelper::getColumn($constraint, 'column_name'),
|
||||
'value' => $constraint[0]['default_expr'],
|
||||
]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($result as $type => $data) {
|
||||
$this->setTableMetadata($tableName, $type, $data);
|
||||
}
|
||||
|
||||
return $result[$returnType];
|
||||
}
|
||||
}
|
||||
33
vendor/yiisoft/yii2/db/mssql/SqlsrvPDO.php
vendored
Normal file
33
vendor/yiisoft/yii2/db/mssql/SqlsrvPDO.php
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\mssql;
|
||||
|
||||
/**
|
||||
* This is an extension of the default PDO class of SQLSRV driver.
|
||||
* It provides workarounds for improperly implemented functionalities of the SQLSRV driver.
|
||||
*
|
||||
* @author Timur Ruziev <resurtm@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class SqlsrvPDO extends \PDO
|
||||
{
|
||||
/**
|
||||
* Returns value of the last inserted ID.
|
||||
*
|
||||
* SQLSRV driver implements [[PDO::lastInsertId()]] method but with a single peculiarity:
|
||||
* when `$sequence` value is a null or an empty string it returns an empty string.
|
||||
* But when parameter is not specified it works as expected and returns actual
|
||||
* last inserted ID (like the other PDO drivers).
|
||||
* @param string|null $sequence the sequence name. Defaults to null.
|
||||
* @return int last inserted ID value.
|
||||
*/
|
||||
public function lastInsertId($sequence = null)
|
||||
{
|
||||
return !$sequence ? parent::lastInsertId() : parent::lastInsertId($sequence);
|
||||
}
|
||||
}
|
||||
23
vendor/yiisoft/yii2/db/mssql/TableSchema.php
vendored
Normal file
23
vendor/yiisoft/yii2/db/mssql/TableSchema.php
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\mssql;
|
||||
|
||||
/**
|
||||
* TableSchema represents the metadata of a database table.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class TableSchema extends \yii\db\TableSchema
|
||||
{
|
||||
/**
|
||||
* @var string name of the catalog (database) that this table belongs to.
|
||||
* Defaults to null, meaning no catalog (or the current database).
|
||||
*/
|
||||
public $catalogName;
|
||||
}
|
||||
58
vendor/yiisoft/yii2/db/mssql/conditions/InConditionBuilder.php
vendored
Normal file
58
vendor/yiisoft/yii2/db/mssql/conditions/InConditionBuilder.php
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\mssql\conditions;
|
||||
|
||||
use yii\base\NotSupportedException;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
|
||||
* @since 2.0.14
|
||||
*/
|
||||
class InConditionBuilder extends \yii\db\conditions\InConditionBuilder
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @throws NotSupportedException if `$columns` is an array
|
||||
*/
|
||||
protected function buildSubqueryInCondition($operator, $columns, $values, &$params)
|
||||
{
|
||||
if (is_array($columns)) {
|
||||
throw new NotSupportedException(__METHOD__ . ' is not supported by MSSQL.');
|
||||
}
|
||||
|
||||
return parent::buildSubqueryInCondition($operator, $columns, $values, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function buildCompositeInCondition($operator, $columns, $values, &$params)
|
||||
{
|
||||
$quotedColumns = [];
|
||||
foreach ($columns as $i => $column) {
|
||||
$quotedColumns[$i] = strpos($column, '(') === false ? $this->queryBuilder->db->quoteColumnName($column) : $column;
|
||||
}
|
||||
$vss = [];
|
||||
foreach ($values as $value) {
|
||||
$vs = [];
|
||||
foreach ($columns as $i => $column) {
|
||||
if (isset($value[$column])) {
|
||||
$phName = $this->queryBuilder->bindParam($value[$column], $params);
|
||||
$vs[] = $quotedColumns[$i] . ($operator === 'IN' ? ' = ' : ' != ') . $phName;
|
||||
} else {
|
||||
$vs[] = $quotedColumns[$i] . ($operator === 'IN' ? ' IS' : ' IS NOT') . ' NULL';
|
||||
}
|
||||
}
|
||||
$vss[] = '(' . implode($operator === 'IN' ? ' AND ' : ' OR ', $vs) . ')';
|
||||
}
|
||||
|
||||
return '(' . implode($operator === 'IN' ? ' OR ' : ' AND ', $vss) . ')';
|
||||
}
|
||||
}
|
||||
25
vendor/yiisoft/yii2/db/mssql/conditions/LikeConditionBuilder.php
vendored
Normal file
25
vendor/yiisoft/yii2/db/mssql/conditions/LikeConditionBuilder.php
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\mssql\conditions;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class LikeConditionBuilder extends \yii\db\conditions\LikeConditionBuilder
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $escapingReplacements = [
|
||||
'%' => '[%]',
|
||||
'_' => '[_]',
|
||||
'[' => '[[]',
|
||||
']' => '[]]',
|
||||
'\\' => '[\\]',
|
||||
];
|
||||
}
|
||||
67
vendor/yiisoft/yii2/db/mysql/ColumnSchema.php
vendored
Normal file
67
vendor/yiisoft/yii2/db/mysql/ColumnSchema.php
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\mysql;
|
||||
|
||||
use yii\db\ExpressionInterface;
|
||||
use yii\db\JsonExpression;
|
||||
|
||||
/**
|
||||
* Class ColumnSchema for MySQL database
|
||||
*
|
||||
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
|
||||
* @since 2.0.14.1
|
||||
*/
|
||||
class ColumnSchema extends \yii\db\ColumnSchema
|
||||
{
|
||||
/**
|
||||
* @var bool whether the column schema should OMIT using JSON support feature.
|
||||
* You can use this property to make upgrade to Yii 2.0.14 easier.
|
||||
* Default to `false`, meaning JSON support is enabled.
|
||||
*
|
||||
* @since 2.0.14.1
|
||||
* @deprecated Since 2.0.14.1 and will be removed in 2.1.
|
||||
*/
|
||||
public $disableJsonSupport = false;
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function dbTypecast($value)
|
||||
{
|
||||
if ($value === null) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if ($value instanceof ExpressionInterface) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if (!$this->disableJsonSupport && $this->dbType === Schema::TYPE_JSON) {
|
||||
return new JsonExpression($value, $this->type);
|
||||
}
|
||||
|
||||
return $this->typecast($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function phpTypecast($value)
|
||||
{
|
||||
if ($value === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!$this->disableJsonSupport && $this->type === Schema::TYPE_JSON) {
|
||||
return json_decode($value, true);
|
||||
}
|
||||
|
||||
return parent::phpTypecast($value);
|
||||
}
|
||||
}
|
||||
72
vendor/yiisoft/yii2/db/mysql/ColumnSchemaBuilder.php
vendored
Normal file
72
vendor/yiisoft/yii2/db/mysql/ColumnSchemaBuilder.php
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\mysql;
|
||||
|
||||
use yii\db\ColumnSchemaBuilder as AbstractColumnSchemaBuilder;
|
||||
|
||||
/**
|
||||
* ColumnSchemaBuilder is the schema builder for MySQL databases.
|
||||
*
|
||||
* @author Chris Harris <chris@buckshotsoftware.com>
|
||||
* @since 2.0.8
|
||||
*/
|
||||
class ColumnSchemaBuilder extends AbstractColumnSchemaBuilder
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function buildUnsignedString()
|
||||
{
|
||||
return $this->isUnsigned ? ' UNSIGNED' : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function buildAfterString()
|
||||
{
|
||||
return $this->after !== null ?
|
||||
' AFTER ' . $this->db->quoteColumnName($this->after) :
|
||||
'';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function buildFirstString()
|
||||
{
|
||||
return $this->isFirst ? ' FIRST' : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function buildCommentString()
|
||||
{
|
||||
return $this->comment !== null ? ' COMMENT ' . $this->db->quoteValue($this->comment) : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
switch ($this->getTypeCategory()) {
|
||||
case self::CATEGORY_PK:
|
||||
$format = '{type}{length}{check}{comment}{append}{pos}';
|
||||
break;
|
||||
case self::CATEGORY_NUMERIC:
|
||||
$format = '{type}{length}{unsigned}{notnull}{unique}{default}{check}{comment}{append}{pos}';
|
||||
break;
|
||||
default:
|
||||
$format = '{type}{length}{notnull}{unique}{default}{check}{comment}{append}{pos}';
|
||||
}
|
||||
|
||||
return $this->buildCompleteString($format);
|
||||
}
|
||||
}
|
||||
48
vendor/yiisoft/yii2/db/mysql/JsonExpressionBuilder.php
vendored
Normal file
48
vendor/yiisoft/yii2/db/mysql/JsonExpressionBuilder.php
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\mysql;
|
||||
|
||||
use yii\db\ExpressionBuilderInterface;
|
||||
use yii\db\ExpressionBuilderTrait;
|
||||
use yii\db\ExpressionInterface;
|
||||
use yii\db\JsonExpression;
|
||||
use yii\db\Query;
|
||||
use yii\helpers\Json;
|
||||
|
||||
/**
|
||||
* Class JsonExpressionBuilder builds [[JsonExpression]] for MySQL DBMS.
|
||||
*
|
||||
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
|
||||
* @since 2.0.14
|
||||
*/
|
||||
class JsonExpressionBuilder implements ExpressionBuilderInterface
|
||||
{
|
||||
use ExpressionBuilderTrait;
|
||||
|
||||
const PARAM_PREFIX = ':qp';
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @param JsonExpression|ExpressionInterface $expression the expression to be built
|
||||
*/
|
||||
public function build(ExpressionInterface $expression, array &$params = [])
|
||||
{
|
||||
$value = $expression->getValue();
|
||||
|
||||
if ($value instanceof Query) {
|
||||
list ($sql, $params) = $this->queryBuilder->build($value, $params);
|
||||
return "($sql)";
|
||||
}
|
||||
|
||||
$placeholder = static::PARAM_PREFIX . count($params);
|
||||
$params[$placeholder] = Json::encode($value);
|
||||
|
||||
return "CAST($placeholder AS JSON)";
|
||||
}
|
||||
}
|
||||
365
vendor/yiisoft/yii2/db/mysql/QueryBuilder.php
vendored
Normal file
365
vendor/yiisoft/yii2/db/mysql/QueryBuilder.php
vendored
Normal file
@@ -0,0 +1,365 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\mysql;
|
||||
|
||||
use yii\base\InvalidArgumentException;
|
||||
use yii\base\NotSupportedException;
|
||||
use yii\db\Exception;
|
||||
use yii\db\Expression;
|
||||
use yii\db\Query;
|
||||
|
||||
/**
|
||||
* QueryBuilder is the query builder for MySQL databases.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class QueryBuilder extends \yii\db\QueryBuilder
|
||||
{
|
||||
/**
|
||||
* @var array mapping from abstract column types (keys) to physical column types (values).
|
||||
*/
|
||||
public $typeMap = [
|
||||
Schema::TYPE_PK => 'int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY',
|
||||
Schema::TYPE_UPK => 'int(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY',
|
||||
Schema::TYPE_BIGPK => 'bigint(20) NOT NULL AUTO_INCREMENT PRIMARY KEY',
|
||||
Schema::TYPE_UBIGPK => 'bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY',
|
||||
Schema::TYPE_CHAR => 'char(1)',
|
||||
Schema::TYPE_STRING => 'varchar(255)',
|
||||
Schema::TYPE_TEXT => 'text',
|
||||
Schema::TYPE_TINYINT => 'tinyint(3)',
|
||||
Schema::TYPE_SMALLINT => 'smallint(6)',
|
||||
Schema::TYPE_INTEGER => 'int(11)',
|
||||
Schema::TYPE_BIGINT => 'bigint(20)',
|
||||
Schema::TYPE_FLOAT => 'float',
|
||||
Schema::TYPE_DOUBLE => 'double',
|
||||
Schema::TYPE_DECIMAL => 'decimal(10,0)',
|
||||
Schema::TYPE_DATETIME => 'datetime',
|
||||
Schema::TYPE_TIMESTAMP => 'timestamp',
|
||||
Schema::TYPE_TIME => 'time',
|
||||
Schema::TYPE_DATE => 'date',
|
||||
Schema::TYPE_BINARY => 'blob',
|
||||
Schema::TYPE_BOOLEAN => 'tinyint(1)',
|
||||
Schema::TYPE_MONEY => 'decimal(19,4)',
|
||||
Schema::TYPE_JSON => 'json'
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function defaultExpressionBuilders()
|
||||
{
|
||||
return array_merge(parent::defaultExpressionBuilders(), [
|
||||
'yii\db\JsonExpression' => 'yii\db\mysql\JsonExpressionBuilder',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a SQL statement for renaming a column.
|
||||
* @param string $table the table whose column is to be renamed. The name will be properly quoted by the method.
|
||||
* @param string $oldName the old name of the column. The name will be properly quoted by the method.
|
||||
* @param string $newName the new name of the column. The name will be properly quoted by the method.
|
||||
* @return string the SQL statement for renaming a DB column.
|
||||
* @throws Exception
|
||||
*/
|
||||
public function renameColumn($table, $oldName, $newName)
|
||||
{
|
||||
$quotedTable = $this->db->quoteTableName($table);
|
||||
$row = $this->db->createCommand('SHOW CREATE TABLE ' . $quotedTable)->queryOne();
|
||||
if ($row === false) {
|
||||
throw new Exception("Unable to find column '$oldName' in table '$table'.");
|
||||
}
|
||||
if (isset($row['Create Table'])) {
|
||||
$sql = $row['Create Table'];
|
||||
} else {
|
||||
$row = array_values($row);
|
||||
$sql = $row[1];
|
||||
}
|
||||
if (preg_match_all('/^\s*`(.*?)`\s+(.*?),?$/m', $sql, $matches)) {
|
||||
foreach ($matches[1] as $i => $c) {
|
||||
if ($c === $oldName) {
|
||||
return "ALTER TABLE $quotedTable CHANGE "
|
||||
. $this->db->quoteColumnName($oldName) . ' '
|
||||
. $this->db->quoteColumnName($newName) . ' '
|
||||
. $matches[2][$i];
|
||||
}
|
||||
}
|
||||
}
|
||||
// try to give back a SQL anyway
|
||||
return "ALTER TABLE $quotedTable CHANGE "
|
||||
. $this->db->quoteColumnName($oldName) . ' '
|
||||
. $this->db->quoteColumnName($newName);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @see https://bugs.mysql.com/bug.php?id=48875
|
||||
*/
|
||||
public function createIndex($name, $table, $columns, $unique = false)
|
||||
{
|
||||
return 'ALTER TABLE '
|
||||
. $this->db->quoteTableName($table)
|
||||
. ($unique ? ' ADD UNIQUE INDEX ' : ' ADD INDEX ')
|
||||
. $this->db->quoteTableName($name)
|
||||
. ' (' . $this->buildColumns($columns) . ')';
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a SQL statement for dropping a foreign key constraint.
|
||||
* @param string $name the name of the foreign key constraint to be dropped. The name will be properly quoted by the method.
|
||||
* @param string $table the table whose foreign is to be dropped. The name will be properly quoted by the method.
|
||||
* @return string the SQL statement for dropping a foreign key constraint.
|
||||
*/
|
||||
public function dropForeignKey($name, $table)
|
||||
{
|
||||
return 'ALTER TABLE ' . $this->db->quoteTableName($table)
|
||||
. ' DROP FOREIGN KEY ' . $this->db->quoteColumnName($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a SQL statement for removing a primary key constraint to an existing table.
|
||||
* @param string $name the name of the primary key constraint to be removed.
|
||||
* @param string $table the table that the primary key constraint will be removed from.
|
||||
* @return string the SQL statement for removing a primary key constraint from an existing table.
|
||||
*/
|
||||
public function dropPrimaryKey($name, $table)
|
||||
{
|
||||
return 'ALTER TABLE ' . $this->db->quoteTableName($table) . ' DROP PRIMARY KEY';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function dropUnique($name, $table)
|
||||
{
|
||||
return $this->dropIndex($name, $table);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @throws NotSupportedException this is not supported by MySQL.
|
||||
*/
|
||||
public function addCheck($name, $table, $expression)
|
||||
{
|
||||
throw new NotSupportedException(__METHOD__ . ' is not supported by MySQL.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @throws NotSupportedException this is not supported by MySQL.
|
||||
*/
|
||||
public function dropCheck($name, $table)
|
||||
{
|
||||
throw new NotSupportedException(__METHOD__ . ' is not supported by MySQL.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a SQL statement for resetting the sequence value of a table's primary key.
|
||||
* The sequence will be reset such that the primary key of the next new row inserted
|
||||
* will have the specified value or 1.
|
||||
* @param string $tableName the name of the table whose primary key sequence will be reset
|
||||
* @param mixed $value the value for the primary key of the next new row inserted. If this is not set,
|
||||
* the next new row's primary key will have a value 1.
|
||||
* @return string the SQL statement for resetting sequence
|
||||
* @throws InvalidArgumentException if the table does not exist or there is no sequence associated with the table.
|
||||
*/
|
||||
public function resetSequence($tableName, $value = null)
|
||||
{
|
||||
$table = $this->db->getTableSchema($tableName);
|
||||
if ($table !== null && $table->sequenceName !== null) {
|
||||
$tableName = $this->db->quoteTableName($tableName);
|
||||
if ($value === null) {
|
||||
$key = reset($table->primaryKey);
|
||||
$value = $this->db->createCommand("SELECT MAX(`$key`) FROM $tableName")->queryScalar() + 1;
|
||||
} else {
|
||||
$value = (int) $value;
|
||||
}
|
||||
|
||||
return "ALTER TABLE $tableName AUTO_INCREMENT=$value";
|
||||
} elseif ($table === null) {
|
||||
throw new InvalidArgumentException("Table not found: $tableName");
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException("There is no sequence associated with table '$tableName'.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a SQL statement for enabling or disabling integrity check.
|
||||
* @param bool $check whether to turn on or off the integrity check.
|
||||
* @param string $schema the schema of the tables. Meaningless for MySQL.
|
||||
* @param string $table the table name. Meaningless for MySQL.
|
||||
* @return string the SQL statement for checking integrity
|
||||
*/
|
||||
public function checkIntegrity($check = true, $schema = '', $table = '')
|
||||
{
|
||||
return 'SET FOREIGN_KEY_CHECKS = ' . ($check ? 1 : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildLimit($limit, $offset)
|
||||
{
|
||||
$sql = '';
|
||||
if ($this->hasLimit($limit)) {
|
||||
$sql = 'LIMIT ' . $limit;
|
||||
if ($this->hasOffset($offset)) {
|
||||
$sql .= ' OFFSET ' . $offset;
|
||||
}
|
||||
} elseif ($this->hasOffset($offset)) {
|
||||
// limit is not optional in MySQL
|
||||
// http://stackoverflow.com/a/271650/1106908
|
||||
// http://dev.mysql.com/doc/refman/5.0/en/select.html#idm47619502796240
|
||||
$sql = "LIMIT $offset, 18446744073709551615"; // 2^64-1
|
||||
}
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function hasLimit($limit)
|
||||
{
|
||||
// In MySQL limit argument must be nonnegative integer constant
|
||||
return ctype_digit((string) $limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function hasOffset($offset)
|
||||
{
|
||||
// In MySQL offset argument must be nonnegative integer constant
|
||||
$offset = (string) $offset;
|
||||
return ctype_digit($offset) && $offset !== '0';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function prepareInsertValues($table, $columns, $params = [])
|
||||
{
|
||||
list($names, $placeholders, $values, $params) = parent::prepareInsertValues($table, $columns, $params);
|
||||
if (!$columns instanceof Query && empty($names)) {
|
||||
$tableSchema = $this->db->getSchema()->getTableSchema($table);
|
||||
if ($tableSchema !== null) {
|
||||
$columns = !empty($tableSchema->primaryKey) ? $tableSchema->primaryKey : [reset($tableSchema->columns)->name];
|
||||
foreach ($columns as $name) {
|
||||
$names[] = $this->db->quoteColumnName($name);
|
||||
$placeholders[] = 'DEFAULT';
|
||||
}
|
||||
}
|
||||
}
|
||||
return [$names, $placeholders, $values, $params];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @see https://downloads.mysql.com/docs/refman-5.1-en.pdf
|
||||
*/
|
||||
public function upsert($table, $insertColumns, $updateColumns, &$params)
|
||||
{
|
||||
$insertSql = $this->insert($table, $insertColumns, $params);
|
||||
list($uniqueNames, , $updateNames) = $this->prepareUpsertColumns($table, $insertColumns, $updateColumns);
|
||||
if (empty($uniqueNames)) {
|
||||
return $insertSql;
|
||||
}
|
||||
|
||||
if ($updateColumns === true) {
|
||||
$updateColumns = [];
|
||||
foreach ($updateNames as $name) {
|
||||
$updateColumns[$name] = new Expression('VALUES(' . $this->db->quoteColumnName($name) . ')');
|
||||
}
|
||||
} elseif ($updateColumns === false) {
|
||||
$name = $this->db->quoteColumnName(reset($uniqueNames));
|
||||
$updateColumns = [$name => new Expression($this->db->quoteTableName($table) . '.' . $name)];
|
||||
}
|
||||
list($updates, $params) = $this->prepareUpdateSets($table, $updateColumns, $params);
|
||||
return $insertSql . ' ON DUPLICATE KEY UPDATE ' . implode(', ', $updates);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @since 2.0.8
|
||||
*/
|
||||
public function addCommentOnColumn($table, $column, $comment)
|
||||
{
|
||||
// Strip existing comment which may include escaped quotes
|
||||
$definition = trim(preg_replace("/COMMENT '(?:''|[^'])*'/i", '',
|
||||
$this->getColumnDefinition($table, $column)));
|
||||
|
||||
return 'ALTER TABLE ' . $this->db->quoteTableName($table)
|
||||
. ' CHANGE ' . $this->db->quoteColumnName($column)
|
||||
. ' ' . $this->db->quoteColumnName($column)
|
||||
. (empty($definition) ? '' : ' ' . $definition)
|
||||
. ' COMMENT ' . $this->db->quoteValue($comment);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @since 2.0.8
|
||||
*/
|
||||
public function addCommentOnTable($table, $comment)
|
||||
{
|
||||
return 'ALTER TABLE ' . $this->db->quoteTableName($table) . ' COMMENT ' . $this->db->quoteValue($comment);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @since 2.0.8
|
||||
*/
|
||||
public function dropCommentFromColumn($table, $column)
|
||||
{
|
||||
return $this->addCommentOnColumn($table, $column, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @since 2.0.8
|
||||
*/
|
||||
public function dropCommentFromTable($table)
|
||||
{
|
||||
return $this->addCommentOnTable($table, '');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets column definition.
|
||||
*
|
||||
* @param string $table table name
|
||||
* @param string $column column name
|
||||
* @return null|string the column definition
|
||||
* @throws Exception in case when table does not contain column
|
||||
*/
|
||||
private function getColumnDefinition($table, $column)
|
||||
{
|
||||
$quotedTable = $this->db->quoteTableName($table);
|
||||
$row = $this->db->createCommand('SHOW CREATE TABLE ' . $quotedTable)->queryOne();
|
||||
if ($row === false) {
|
||||
throw new Exception("Unable to find column '$column' in table '$table'.");
|
||||
}
|
||||
if (isset($row['Create Table'])) {
|
||||
$sql = $row['Create Table'];
|
||||
} else {
|
||||
$row = array_values($row);
|
||||
$sql = $row[1];
|
||||
}
|
||||
if (preg_match_all('/^\s*`(.*?)`\s+(.*?),?$/m', $sql, $matches)) {
|
||||
foreach ($matches[1] as $i => $c) {
|
||||
if ($c === $column) {
|
||||
return $matches[2][$i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
577
vendor/yiisoft/yii2/db/mysql/Schema.php
vendored
Normal file
577
vendor/yiisoft/yii2/db/mysql/Schema.php
vendored
Normal file
@@ -0,0 +1,577 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\mysql;
|
||||
|
||||
use yii\base\InvalidConfigException;
|
||||
use yii\base\NotSupportedException;
|
||||
use yii\db\Constraint;
|
||||
use yii\db\ConstraintFinderInterface;
|
||||
use yii\db\ConstraintFinderTrait;
|
||||
use yii\db\Exception;
|
||||
use yii\db\Expression;
|
||||
use yii\db\ForeignKeyConstraint;
|
||||
use yii\db\IndexConstraint;
|
||||
use yii\db\TableSchema;
|
||||
use yii\helpers\ArrayHelper;
|
||||
|
||||
/**
|
||||
* Schema is the class for retrieving metadata from a MySQL database (version 4.1.x and 5.x).
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class Schema extends \yii\db\Schema implements ConstraintFinderInterface
|
||||
{
|
||||
use ConstraintFinderTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $columnSchemaClass = 'yii\db\mysql\ColumnSchema';
|
||||
/**
|
||||
* @var bool whether MySQL used is older than 5.1.
|
||||
*/
|
||||
private $_oldMysql;
|
||||
|
||||
|
||||
/**
|
||||
* @var array mapping from physical column types (keys) to abstract column types (values)
|
||||
*/
|
||||
public $typeMap = [
|
||||
'tinyint' => self::TYPE_TINYINT,
|
||||
'bit' => self::TYPE_INTEGER,
|
||||
'smallint' => self::TYPE_SMALLINT,
|
||||
'mediumint' => self::TYPE_INTEGER,
|
||||
'int' => self::TYPE_INTEGER,
|
||||
'integer' => self::TYPE_INTEGER,
|
||||
'bigint' => self::TYPE_BIGINT,
|
||||
'float' => self::TYPE_FLOAT,
|
||||
'double' => self::TYPE_DOUBLE,
|
||||
'real' => self::TYPE_FLOAT,
|
||||
'decimal' => self::TYPE_DECIMAL,
|
||||
'numeric' => self::TYPE_DECIMAL,
|
||||
'tinytext' => self::TYPE_TEXT,
|
||||
'mediumtext' => self::TYPE_TEXT,
|
||||
'longtext' => self::TYPE_TEXT,
|
||||
'longblob' => self::TYPE_BINARY,
|
||||
'blob' => self::TYPE_BINARY,
|
||||
'text' => self::TYPE_TEXT,
|
||||
'varchar' => self::TYPE_STRING,
|
||||
'string' => self::TYPE_STRING,
|
||||
'char' => self::TYPE_CHAR,
|
||||
'datetime' => self::TYPE_DATETIME,
|
||||
'year' => self::TYPE_DATE,
|
||||
'date' => self::TYPE_DATE,
|
||||
'time' => self::TYPE_TIME,
|
||||
'timestamp' => self::TYPE_TIMESTAMP,
|
||||
'enum' => self::TYPE_STRING,
|
||||
'varbinary' => self::TYPE_BINARY,
|
||||
'json' => self::TYPE_JSON,
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $tableQuoteCharacter = '`';
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $columnQuoteCharacter = '`';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function resolveTableName($name)
|
||||
{
|
||||
$resolvedName = new TableSchema();
|
||||
$parts = explode('.', str_replace('`', '', $name));
|
||||
if (isset($parts[1])) {
|
||||
$resolvedName->schemaName = $parts[0];
|
||||
$resolvedName->name = $parts[1];
|
||||
} else {
|
||||
$resolvedName->schemaName = $this->defaultSchema;
|
||||
$resolvedName->name = $name;
|
||||
}
|
||||
$resolvedName->fullName = ($resolvedName->schemaName !== $this->defaultSchema ? $resolvedName->schemaName . '.' : '') . $resolvedName->name;
|
||||
return $resolvedName;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function findTableNames($schema = '')
|
||||
{
|
||||
$sql = 'SHOW TABLES';
|
||||
if ($schema !== '') {
|
||||
$sql .= ' FROM ' . $this->quoteSimpleTableName($schema);
|
||||
}
|
||||
|
||||
return $this->db->createCommand($sql)->queryColumn();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function loadTableSchema($name)
|
||||
{
|
||||
$table = new TableSchema();
|
||||
$this->resolveTableNames($table, $name);
|
||||
|
||||
if ($this->findColumns($table)) {
|
||||
$this->findConstraints($table);
|
||||
return $table;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function loadTablePrimaryKey($tableName)
|
||||
{
|
||||
return $this->loadTableConstraints($tableName, 'primaryKey');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function loadTableForeignKeys($tableName)
|
||||
{
|
||||
return $this->loadTableConstraints($tableName, 'foreignKeys');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function loadTableIndexes($tableName)
|
||||
{
|
||||
static $sql = <<<'SQL'
|
||||
SELECT
|
||||
`s`.`INDEX_NAME` AS `name`,
|
||||
`s`.`COLUMN_NAME` AS `column_name`,
|
||||
`s`.`NON_UNIQUE` ^ 1 AS `index_is_unique`,
|
||||
`s`.`INDEX_NAME` = 'PRIMARY' AS `index_is_primary`
|
||||
FROM `information_schema`.`STATISTICS` AS `s`
|
||||
WHERE `s`.`TABLE_SCHEMA` = COALESCE(:schemaName, DATABASE()) AND `s`.`INDEX_SCHEMA` = `s`.`TABLE_SCHEMA` AND `s`.`TABLE_NAME` = :tableName
|
||||
ORDER BY `s`.`SEQ_IN_INDEX` ASC
|
||||
SQL;
|
||||
|
||||
$resolvedName = $this->resolveTableName($tableName);
|
||||
$indexes = $this->db->createCommand($sql, [
|
||||
':schemaName' => $resolvedName->schemaName,
|
||||
':tableName' => $resolvedName->name,
|
||||
])->queryAll();
|
||||
$indexes = $this->normalizePdoRowKeyCase($indexes, true);
|
||||
$indexes = ArrayHelper::index($indexes, null, 'name');
|
||||
$result = [];
|
||||
foreach ($indexes as $name => $index) {
|
||||
$result[] = new IndexConstraint([
|
||||
'isPrimary' => (bool) $index[0]['index_is_primary'],
|
||||
'isUnique' => (bool) $index[0]['index_is_unique'],
|
||||
'name' => $name !== 'PRIMARY' ? $name : null,
|
||||
'columnNames' => ArrayHelper::getColumn($index, 'column_name'),
|
||||
]);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function loadTableUniques($tableName)
|
||||
{
|
||||
return $this->loadTableConstraints($tableName, 'uniques');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @throws NotSupportedException if this method is called.
|
||||
*/
|
||||
protected function loadTableChecks($tableName)
|
||||
{
|
||||
throw new NotSupportedException('MySQL does not support check constraints.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @throws NotSupportedException if this method is called.
|
||||
*/
|
||||
protected function loadTableDefaultValues($tableName)
|
||||
{
|
||||
throw new NotSupportedException('MySQL does not support default value constraints.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a query builder for the MySQL database.
|
||||
* @return QueryBuilder query builder instance
|
||||
*/
|
||||
public function createQueryBuilder()
|
||||
{
|
||||
return new QueryBuilder($this->db);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the table name and schema name (if any).
|
||||
* @param TableSchema $table the table metadata object
|
||||
* @param string $name the table name
|
||||
*/
|
||||
protected function resolveTableNames($table, $name)
|
||||
{
|
||||
$parts = explode('.', str_replace('`', '', $name));
|
||||
if (isset($parts[1])) {
|
||||
$table->schemaName = $parts[0];
|
||||
$table->name = $parts[1];
|
||||
$table->fullName = $table->schemaName . '.' . $table->name;
|
||||
} else {
|
||||
$table->fullName = $table->name = $parts[0];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the column information into a [[ColumnSchema]] object.
|
||||
* @param array $info column information
|
||||
* @return ColumnSchema the column schema object
|
||||
*/
|
||||
protected function loadColumnSchema($info)
|
||||
{
|
||||
$column = $this->createColumnSchema();
|
||||
|
||||
$column->name = $info['field'];
|
||||
$column->allowNull = $info['null'] === 'YES';
|
||||
$column->isPrimaryKey = strpos($info['key'], 'PRI') !== false;
|
||||
$column->autoIncrement = stripos($info['extra'], 'auto_increment') !== false;
|
||||
$column->comment = $info['comment'];
|
||||
|
||||
$column->dbType = $info['type'];
|
||||
$column->unsigned = stripos($column->dbType, 'unsigned') !== false;
|
||||
|
||||
$column->type = self::TYPE_STRING;
|
||||
if (preg_match('/^(\w+)(?:\(([^\)]+)\))?/', $column->dbType, $matches)) {
|
||||
$type = strtolower($matches[1]);
|
||||
if (isset($this->typeMap[$type])) {
|
||||
$column->type = $this->typeMap[$type];
|
||||
}
|
||||
if (!empty($matches[2])) {
|
||||
if ($type === 'enum') {
|
||||
preg_match_all("/'[^']*'/", $matches[2], $values);
|
||||
foreach ($values[0] as $i => $value) {
|
||||
$values[$i] = trim($value, "'");
|
||||
}
|
||||
$column->enumValues = $values;
|
||||
} else {
|
||||
$values = explode(',', $matches[2]);
|
||||
$column->size = $column->precision = (int) $values[0];
|
||||
if (isset($values[1])) {
|
||||
$column->scale = (int) $values[1];
|
||||
}
|
||||
if ($column->size === 1 && $type === 'bit') {
|
||||
$column->type = 'boolean';
|
||||
} elseif ($type === 'bit') {
|
||||
if ($column->size > 32) {
|
||||
$column->type = 'bigint';
|
||||
} elseif ($column->size === 32) {
|
||||
$column->type = 'integer';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$column->phpType = $this->getColumnPhpType($column);
|
||||
|
||||
if (!$column->isPrimaryKey) {
|
||||
if (($column->type === 'timestamp' || $column->type ==='datetime') && $info['default'] === 'CURRENT_TIMESTAMP') {
|
||||
$column->defaultValue = new Expression('CURRENT_TIMESTAMP');
|
||||
} elseif (isset($type) && $type === 'bit') {
|
||||
$column->defaultValue = bindec(trim($info['default'], 'b\''));
|
||||
} else {
|
||||
$column->defaultValue = $column->phpTypecast($info['default']);
|
||||
}
|
||||
}
|
||||
|
||||
return $column;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects the metadata of table columns.
|
||||
* @param TableSchema $table the table metadata
|
||||
* @return bool whether the table exists in the database
|
||||
* @throws \Exception if DB query fails
|
||||
*/
|
||||
protected function findColumns($table)
|
||||
{
|
||||
$sql = 'SHOW FULL COLUMNS FROM ' . $this->quoteTableName($table->fullName);
|
||||
try {
|
||||
$columns = $this->db->createCommand($sql)->queryAll();
|
||||
} catch (\Exception $e) {
|
||||
$previous = $e->getPrevious();
|
||||
if ($previous instanceof \PDOException && strpos($previous->getMessage(), 'SQLSTATE[42S02') !== false) {
|
||||
// table does not exist
|
||||
// https://dev.mysql.com/doc/refman/5.5/en/error-messages-server.html#error_er_bad_table_error
|
||||
return false;
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
foreach ($columns as $info) {
|
||||
if ($this->db->slavePdo->getAttribute(\PDO::ATTR_CASE) !== \PDO::CASE_LOWER) {
|
||||
$info = array_change_key_case($info, CASE_LOWER);
|
||||
}
|
||||
$column = $this->loadColumnSchema($info);
|
||||
$table->columns[$column->name] = $column;
|
||||
if ($column->isPrimaryKey) {
|
||||
$table->primaryKey[] = $column->name;
|
||||
if ($column->autoIncrement) {
|
||||
$table->sequenceName = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the CREATE TABLE sql string.
|
||||
* @param TableSchema $table the table metadata
|
||||
* @return string $sql the result of 'SHOW CREATE TABLE'
|
||||
*/
|
||||
protected function getCreateTableSql($table)
|
||||
{
|
||||
$row = $this->db->createCommand('SHOW CREATE TABLE ' . $this->quoteTableName($table->fullName))->queryOne();
|
||||
if (isset($row['Create Table'])) {
|
||||
$sql = $row['Create Table'];
|
||||
} else {
|
||||
$row = array_values($row);
|
||||
$sql = $row[1];
|
||||
}
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects the foreign key column details for the given table.
|
||||
* @param TableSchema $table the table metadata
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function findConstraints($table)
|
||||
{
|
||||
$sql = <<<'SQL'
|
||||
SELECT
|
||||
kcu.constraint_name,
|
||||
kcu.column_name,
|
||||
kcu.referenced_table_name,
|
||||
kcu.referenced_column_name
|
||||
FROM information_schema.referential_constraints AS rc
|
||||
JOIN information_schema.key_column_usage AS kcu ON
|
||||
(
|
||||
kcu.constraint_catalog = rc.constraint_catalog OR
|
||||
(kcu.constraint_catalog IS NULL AND rc.constraint_catalog IS NULL)
|
||||
) AND
|
||||
kcu.constraint_schema = rc.constraint_schema AND
|
||||
kcu.constraint_name = rc.constraint_name
|
||||
WHERE rc.constraint_schema = database() AND kcu.table_schema = database()
|
||||
AND rc.table_name = :tableName AND kcu.table_name = :tableName1
|
||||
SQL;
|
||||
|
||||
try {
|
||||
$rows = $this->db->createCommand($sql, [':tableName' => $table->name, ':tableName1' => $table->name])->queryAll();
|
||||
$constraints = [];
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$constraints[$row['constraint_name']]['referenced_table_name'] = $row['referenced_table_name'];
|
||||
$constraints[$row['constraint_name']]['columns'][$row['column_name']] = $row['referenced_column_name'];
|
||||
}
|
||||
|
||||
$table->foreignKeys = [];
|
||||
foreach ($constraints as $name => $constraint) {
|
||||
$table->foreignKeys[$name] = array_merge(
|
||||
[$constraint['referenced_table_name']],
|
||||
$constraint['columns']
|
||||
);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$previous = $e->getPrevious();
|
||||
if (!$previous instanceof \PDOException || strpos($previous->getMessage(), 'SQLSTATE[42S02') === false) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
// table does not exist, try to determine the foreign keys using the table creation sql
|
||||
$sql = $this->getCreateTableSql($table);
|
||||
$regexp = '/FOREIGN KEY\s+\(([^\)]+)\)\s+REFERENCES\s+([^\(^\s]+)\s*\(([^\)]+)\)/mi';
|
||||
if (preg_match_all($regexp, $sql, $matches, PREG_SET_ORDER)) {
|
||||
foreach ($matches as $match) {
|
||||
$fks = array_map('trim', explode(',', str_replace('`', '', $match[1])));
|
||||
$pks = array_map('trim', explode(',', str_replace('`', '', $match[3])));
|
||||
$constraint = [str_replace('`', '', $match[2])];
|
||||
foreach ($fks as $k => $name) {
|
||||
$constraint[$name] = $pks[$k];
|
||||
}
|
||||
$table->foreignKeys[md5(serialize($constraint))] = $constraint;
|
||||
}
|
||||
$table->foreignKeys = array_values($table->foreignKeys);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all unique indexes for the given table.
|
||||
*
|
||||
* Each array element is of the following structure:
|
||||
*
|
||||
* ```php
|
||||
* [
|
||||
* 'IndexName1' => ['col1' [, ...]],
|
||||
* 'IndexName2' => ['col2' [, ...]],
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* @param TableSchema $table the table metadata
|
||||
* @return array all unique indexes for the given table.
|
||||
*/
|
||||
public function findUniqueIndexes($table)
|
||||
{
|
||||
$sql = $this->getCreateTableSql($table);
|
||||
$uniqueIndexes = [];
|
||||
|
||||
$regexp = '/UNIQUE KEY\s+\`(.+)\`\s*\((\`.+\`)+\)/mi';
|
||||
if (preg_match_all($regexp, $sql, $matches, PREG_SET_ORDER)) {
|
||||
foreach ($matches as $match) {
|
||||
$indexName = $match[1];
|
||||
$indexColumns = array_map('trim', explode('`,`', trim($match[2], '`')));
|
||||
$uniqueIndexes[$indexName] = $indexColumns;
|
||||
}
|
||||
}
|
||||
|
||||
return $uniqueIndexes;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createColumnSchemaBuilder($type, $length = null)
|
||||
{
|
||||
return new ColumnSchemaBuilder($type, $length, $this->db);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool whether the version of the MySQL being used is older than 5.1.
|
||||
* @throws InvalidConfigException
|
||||
* @throws Exception
|
||||
* @since 2.0.13
|
||||
*/
|
||||
protected function isOldMysql()
|
||||
{
|
||||
if ($this->_oldMysql === null) {
|
||||
$version = $this->db->getSlavePdo()->getAttribute(\PDO::ATTR_SERVER_VERSION);
|
||||
$this->_oldMysql = version_compare($version, '5.1', '<=');
|
||||
}
|
||||
|
||||
return $this->_oldMysql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads multiple types of constraints and returns the specified ones.
|
||||
* @param string $tableName table name.
|
||||
* @param string $returnType return type:
|
||||
* - primaryKey
|
||||
* - foreignKeys
|
||||
* - uniques
|
||||
* @return mixed constraints.
|
||||
*/
|
||||
private function loadTableConstraints($tableName, $returnType)
|
||||
{
|
||||
static $sql = <<<'SQL'
|
||||
SELECT
|
||||
`kcu`.`CONSTRAINT_NAME` AS `name`,
|
||||
`kcu`.`COLUMN_NAME` AS `column_name`,
|
||||
`tc`.`CONSTRAINT_TYPE` AS `type`,
|
||||
CASE
|
||||
WHEN :schemaName IS NULL AND `kcu`.`REFERENCED_TABLE_SCHEMA` = DATABASE() THEN NULL
|
||||
ELSE `kcu`.`REFERENCED_TABLE_SCHEMA`
|
||||
END AS `foreign_table_schema`,
|
||||
`kcu`.`REFERENCED_TABLE_NAME` AS `foreign_table_name`,
|
||||
`kcu`.`REFERENCED_COLUMN_NAME` AS `foreign_column_name`,
|
||||
`rc`.`UPDATE_RULE` AS `on_update`,
|
||||
`rc`.`DELETE_RULE` AS `on_delete`,
|
||||
`kcu`.`ORDINAL_POSITION` AS `position`
|
||||
FROM
|
||||
`information_schema`.`KEY_COLUMN_USAGE` AS `kcu`,
|
||||
`information_schema`.`REFERENTIAL_CONSTRAINTS` AS `rc`,
|
||||
`information_schema`.`TABLE_CONSTRAINTS` AS `tc`
|
||||
WHERE
|
||||
`kcu`.`TABLE_SCHEMA` = COALESCE(:schemaName, DATABASE()) AND `kcu`.`CONSTRAINT_SCHEMA` = `kcu`.`TABLE_SCHEMA` AND `kcu`.`TABLE_NAME` = :tableName
|
||||
AND `rc`.`CONSTRAINT_SCHEMA` = `kcu`.`TABLE_SCHEMA` AND `rc`.`TABLE_NAME` = :tableName AND `rc`.`CONSTRAINT_NAME` = `kcu`.`CONSTRAINT_NAME`
|
||||
AND `tc`.`TABLE_SCHEMA` = `kcu`.`TABLE_SCHEMA` AND `tc`.`TABLE_NAME` = :tableName AND `tc`.`CONSTRAINT_NAME` = `kcu`.`CONSTRAINT_NAME` AND `tc`.`CONSTRAINT_TYPE` = 'FOREIGN KEY'
|
||||
UNION
|
||||
SELECT
|
||||
`kcu`.`CONSTRAINT_NAME` AS `name`,
|
||||
`kcu`.`COLUMN_NAME` AS `column_name`,
|
||||
`tc`.`CONSTRAINT_TYPE` AS `type`,
|
||||
NULL AS `foreign_table_schema`,
|
||||
NULL AS `foreign_table_name`,
|
||||
NULL AS `foreign_column_name`,
|
||||
NULL AS `on_update`,
|
||||
NULL AS `on_delete`,
|
||||
`kcu`.`ORDINAL_POSITION` AS `position`
|
||||
FROM
|
||||
`information_schema`.`KEY_COLUMN_USAGE` AS `kcu`,
|
||||
`information_schema`.`TABLE_CONSTRAINTS` AS `tc`
|
||||
WHERE
|
||||
`kcu`.`TABLE_SCHEMA` = COALESCE(:schemaName, DATABASE()) AND `kcu`.`TABLE_NAME` = :tableName
|
||||
AND `tc`.`TABLE_SCHEMA` = `kcu`.`TABLE_SCHEMA` AND `tc`.`TABLE_NAME` = :tableName AND `tc`.`CONSTRAINT_NAME` = `kcu`.`CONSTRAINT_NAME` AND `tc`.`CONSTRAINT_TYPE` IN ('PRIMARY KEY', 'UNIQUE')
|
||||
ORDER BY `position` ASC
|
||||
SQL;
|
||||
|
||||
$resolvedName = $this->resolveTableName($tableName);
|
||||
$constraints = $this->db->createCommand($sql, [
|
||||
':schemaName' => $resolvedName->schemaName,
|
||||
':tableName' => $resolvedName->name,
|
||||
])->queryAll();
|
||||
$constraints = $this->normalizePdoRowKeyCase($constraints, true);
|
||||
$constraints = ArrayHelper::index($constraints, null, ['type', 'name']);
|
||||
$result = [
|
||||
'primaryKey' => null,
|
||||
'foreignKeys' => [],
|
||||
'uniques' => [],
|
||||
];
|
||||
foreach ($constraints as $type => $names) {
|
||||
foreach ($names as $name => $constraint) {
|
||||
switch ($type) {
|
||||
case 'PRIMARY KEY':
|
||||
$result['primaryKey'] = new Constraint([
|
||||
'columnNames' => ArrayHelper::getColumn($constraint, 'column_name'),
|
||||
]);
|
||||
break;
|
||||
case 'FOREIGN KEY':
|
||||
$result['foreignKeys'][] = new ForeignKeyConstraint([
|
||||
'name' => $name,
|
||||
'columnNames' => ArrayHelper::getColumn($constraint, 'column_name'),
|
||||
'foreignSchemaName' => $constraint[0]['foreign_table_schema'],
|
||||
'foreignTableName' => $constraint[0]['foreign_table_name'],
|
||||
'foreignColumnNames' => ArrayHelper::getColumn($constraint, 'foreign_column_name'),
|
||||
'onDelete' => $constraint[0]['on_delete'],
|
||||
'onUpdate' => $constraint[0]['on_update'],
|
||||
]);
|
||||
break;
|
||||
case 'UNIQUE':
|
||||
$result['uniques'][] = new Constraint([
|
||||
'name' => $name,
|
||||
'columnNames' => ArrayHelper::getColumn($constraint, 'column_name'),
|
||||
]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($result as $type => $data) {
|
||||
$this->setTableMetadata($tableName, $type, $data);
|
||||
}
|
||||
|
||||
return $result[$returnType];
|
||||
}
|
||||
}
|
||||
47
vendor/yiisoft/yii2/db/oci/ColumnSchemaBuilder.php
vendored
Normal file
47
vendor/yiisoft/yii2/db/oci/ColumnSchemaBuilder.php
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\oci;
|
||||
|
||||
use yii\db\ColumnSchemaBuilder as AbstractColumnSchemaBuilder;
|
||||
|
||||
/**
|
||||
* ColumnSchemaBuilder is the schema builder for Oracle databases.
|
||||
*
|
||||
* @author Vasenin Matvey <vaseninm@gmail.com>
|
||||
* @author Chris Harris <chris@buckshotsoftware.com>
|
||||
* @since 2.0.6
|
||||
*/
|
||||
class ColumnSchemaBuilder extends AbstractColumnSchemaBuilder
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function buildUnsignedString()
|
||||
{
|
||||
return $this->isUnsigned ? ' UNSIGNED' : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
switch ($this->getTypeCategory()) {
|
||||
case self::CATEGORY_PK:
|
||||
$format = '{type}{length}{check}{append}';
|
||||
break;
|
||||
case self::CATEGORY_NUMERIC:
|
||||
$format = '{type}{length}{unsigned}{default}{notnull}{check}{append}';
|
||||
break;
|
||||
default:
|
||||
$format = '{type}{length}{default}{notnull}{check}{append}';
|
||||
}
|
||||
|
||||
return $this->buildCompleteString($format);
|
||||
}
|
||||
}
|
||||
365
vendor/yiisoft/yii2/db/oci/QueryBuilder.php
vendored
Normal file
365
vendor/yiisoft/yii2/db/oci/QueryBuilder.php
vendored
Normal file
@@ -0,0 +1,365 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\oci;
|
||||
|
||||
use yii\base\InvalidArgumentException;
|
||||
use yii\db\Connection;
|
||||
use yii\db\Constraint;
|
||||
use yii\db\Exception;
|
||||
use yii\db\Expression;
|
||||
use yii\db\Query;
|
||||
use yii\helpers\StringHelper;
|
||||
use yii\db\ExpressionInterface;
|
||||
|
||||
/**
|
||||
* QueryBuilder is the query builder for Oracle databases.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class QueryBuilder extends \yii\db\QueryBuilder
|
||||
{
|
||||
/**
|
||||
* @var array mapping from abstract column types (keys) to physical column types (values).
|
||||
*/
|
||||
public $typeMap = [
|
||||
Schema::TYPE_PK => 'NUMBER(10) NOT NULL PRIMARY KEY',
|
||||
Schema::TYPE_UPK => 'NUMBER(10) UNSIGNED NOT NULL PRIMARY KEY',
|
||||
Schema::TYPE_BIGPK => 'NUMBER(20) NOT NULL PRIMARY KEY',
|
||||
Schema::TYPE_UBIGPK => 'NUMBER(20) UNSIGNED NOT NULL PRIMARY KEY',
|
||||
Schema::TYPE_CHAR => 'CHAR(1)',
|
||||
Schema::TYPE_STRING => 'VARCHAR2(255)',
|
||||
Schema::TYPE_TEXT => 'CLOB',
|
||||
Schema::TYPE_TINYINT => 'NUMBER(3)',
|
||||
Schema::TYPE_SMALLINT => 'NUMBER(5)',
|
||||
Schema::TYPE_INTEGER => 'NUMBER(10)',
|
||||
Schema::TYPE_BIGINT => 'NUMBER(20)',
|
||||
Schema::TYPE_FLOAT => 'NUMBER',
|
||||
Schema::TYPE_DOUBLE => 'NUMBER',
|
||||
Schema::TYPE_DECIMAL => 'NUMBER',
|
||||
Schema::TYPE_DATETIME => 'TIMESTAMP',
|
||||
Schema::TYPE_TIMESTAMP => 'TIMESTAMP',
|
||||
Schema::TYPE_TIME => 'TIMESTAMP',
|
||||
Schema::TYPE_DATE => 'DATE',
|
||||
Schema::TYPE_BINARY => 'BLOB',
|
||||
Schema::TYPE_BOOLEAN => 'NUMBER(1)',
|
||||
Schema::TYPE_MONEY => 'NUMBER(19,4)',
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function defaultExpressionBuilders()
|
||||
{
|
||||
return array_merge(parent::defaultExpressionBuilders(), [
|
||||
'yii\db\conditions\InCondition' => 'yii\db\oci\conditions\InConditionBuilder',
|
||||
'yii\db\conditions\LikeCondition' => 'yii\db\oci\conditions\LikeConditionBuilder',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildOrderByAndLimit($sql, $orderBy, $limit, $offset)
|
||||
{
|
||||
$orderBy = $this->buildOrderBy($orderBy);
|
||||
if ($orderBy !== '') {
|
||||
$sql .= $this->separator . $orderBy;
|
||||
}
|
||||
|
||||
$filters = [];
|
||||
if ($this->hasOffset($offset)) {
|
||||
$filters[] = 'rowNumId > ' . $offset;
|
||||
}
|
||||
if ($this->hasLimit($limit)) {
|
||||
$filters[] = 'rownum <= ' . $limit;
|
||||
}
|
||||
if (empty($filters)) {
|
||||
return $sql;
|
||||
}
|
||||
|
||||
$filter = implode(' AND ', $filters);
|
||||
return <<<EOD
|
||||
WITH USER_SQL AS ($sql),
|
||||
PAGINATION AS (SELECT USER_SQL.*, rownum as rowNumId FROM USER_SQL)
|
||||
SELECT *
|
||||
FROM PAGINATION
|
||||
WHERE $filter
|
||||
EOD;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a SQL statement for renaming a DB table.
|
||||
*
|
||||
* @param string $table the table to be renamed. The name will be properly quoted by the method.
|
||||
* @param string $newName the new table name. The name will be properly quoted by the method.
|
||||
* @return string the SQL statement for renaming a DB table.
|
||||
*/
|
||||
public function renameTable($table, $newName)
|
||||
{
|
||||
return 'ALTER TABLE ' . $this->db->quoteTableName($table) . ' RENAME TO ' . $this->db->quoteTableName($newName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a SQL statement for changing the definition of a column.
|
||||
*
|
||||
* @param string $table the table whose column is to be changed. The table name will be properly quoted by the method.
|
||||
* @param string $column the name of the column to be changed. The name will be properly quoted by the method.
|
||||
* @param string $type the new column type. The [[getColumnType]] method will be invoked to convert abstract column type (if any)
|
||||
* into the physical one. Anything that is not recognized as abstract type will be kept in the generated SQL.
|
||||
* For example, 'string' will be turned into 'varchar(255)', while 'string not null' will become 'varchar(255) not null'.
|
||||
* @return string the SQL statement for changing the definition of a column.
|
||||
*/
|
||||
public function alterColumn($table, $column, $type)
|
||||
{
|
||||
$type = $this->getColumnType($type);
|
||||
|
||||
return 'ALTER TABLE ' . $this->db->quoteTableName($table) . ' MODIFY ' . $this->db->quoteColumnName($column) . ' ' . $this->getColumnType($type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a SQL statement for dropping an index.
|
||||
*
|
||||
* @param string $name the name of the index to be dropped. The name will be properly quoted by the method.
|
||||
* @param string $table the table whose index is to be dropped. The name will be properly quoted by the method.
|
||||
* @return string the SQL statement for dropping an index.
|
||||
*/
|
||||
public function dropIndex($name, $table)
|
||||
{
|
||||
return 'DROP INDEX ' . $this->db->quoteTableName($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function resetSequence($table, $value = null)
|
||||
{
|
||||
$tableSchema = $this->db->getTableSchema($table);
|
||||
if ($tableSchema === null) {
|
||||
throw new InvalidArgumentException("Unknown table: $table");
|
||||
}
|
||||
if ($tableSchema->sequenceName === null) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if ($value !== null) {
|
||||
$value = (int) $value;
|
||||
} else {
|
||||
// use master connection to get the biggest PK value
|
||||
$value = $this->db->useMaster(function (Connection $db) use ($tableSchema) {
|
||||
return $db->createCommand("SELECT MAX(\"{$tableSchema->primaryKey}\") FROM \"{$tableSchema->name}\"")->queryScalar();
|
||||
}) + 1;
|
||||
}
|
||||
|
||||
return "DROP SEQUENCE \"{$tableSchema->name}_SEQ\";"
|
||||
. "CREATE SEQUENCE \"{$tableSchema->name}_SEQ\" START WITH {$value} INCREMENT BY 1 NOMAXVALUE NOCACHE";
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addForeignKey($name, $table, $columns, $refTable, $refColumns, $delete = null, $update = null)
|
||||
{
|
||||
$sql = 'ALTER TABLE ' . $this->db->quoteTableName($table)
|
||||
. ' ADD CONSTRAINT ' . $this->db->quoteColumnName($name)
|
||||
. ' FOREIGN KEY (' . $this->buildColumns($columns) . ')'
|
||||
. ' REFERENCES ' . $this->db->quoteTableName($refTable)
|
||||
. ' (' . $this->buildColumns($refColumns) . ')';
|
||||
if ($delete !== null) {
|
||||
$sql .= ' ON DELETE ' . $delete;
|
||||
}
|
||||
if ($update !== null) {
|
||||
throw new Exception('Oracle does not support ON UPDATE clause.');
|
||||
}
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function prepareInsertValues($table, $columns, $params = [])
|
||||
{
|
||||
list($names, $placeholders, $values, $params) = parent::prepareInsertValues($table, $columns, $params);
|
||||
if (!$columns instanceof Query && empty($names)) {
|
||||
$tableSchema = $this->db->getSchema()->getTableSchema($table);
|
||||
if ($tableSchema !== null) {
|
||||
$columns = !empty($tableSchema->primaryKey) ? $tableSchema->primaryKey : [reset($tableSchema->columns)->name];
|
||||
foreach ($columns as $name) {
|
||||
$names[] = $this->db->quoteColumnName($name);
|
||||
$placeholders[] = 'DEFAULT';
|
||||
}
|
||||
}
|
||||
}
|
||||
return [$names, $placeholders, $values, $params];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @see https://docs.oracle.com/cd/B28359_01/server.111/b28286/statements_9016.htm#SQLRF01606
|
||||
*/
|
||||
public function upsert($table, $insertColumns, $updateColumns, &$params)
|
||||
{
|
||||
/** @var Constraint[] $constraints */
|
||||
list($uniqueNames, $insertNames, $updateNames) = $this->prepareUpsertColumns($table, $insertColumns, $updateColumns, $constraints);
|
||||
if (empty($uniqueNames)) {
|
||||
return $this->insert($table, $insertColumns, $params);
|
||||
}
|
||||
|
||||
$onCondition = ['or'];
|
||||
$quotedTableName = $this->db->quoteTableName($table);
|
||||
foreach ($constraints as $constraint) {
|
||||
$constraintCondition = ['and'];
|
||||
foreach ($constraint->columnNames as $name) {
|
||||
$quotedName = $this->db->quoteColumnName($name);
|
||||
$constraintCondition[] = "$quotedTableName.$quotedName=\"EXCLUDED\".$quotedName";
|
||||
}
|
||||
$onCondition[] = $constraintCondition;
|
||||
}
|
||||
$on = $this->buildCondition($onCondition, $params);
|
||||
list(, $placeholders, $values, $params) = $this->prepareInsertValues($table, $insertColumns, $params);
|
||||
if (!empty($placeholders)) {
|
||||
$usingSelectValues = [];
|
||||
foreach ($insertNames as $index => $name) {
|
||||
$usingSelectValues[$name] = new Expression($placeholders[$index]);
|
||||
}
|
||||
$usingSubQuery = (new Query())
|
||||
->select($usingSelectValues)
|
||||
->from('DUAL');
|
||||
list($usingValues, $params) = $this->build($usingSubQuery, $params);
|
||||
}
|
||||
$mergeSql = 'MERGE INTO ' . $this->db->quoteTableName($table) . ' '
|
||||
. 'USING (' . (isset($usingValues) ? $usingValues : ltrim($values, ' ')) . ') "EXCLUDED" '
|
||||
. "ON ($on)";
|
||||
$insertValues = [];
|
||||
foreach ($insertNames as $name) {
|
||||
$quotedName = $this->db->quoteColumnName($name);
|
||||
if (strrpos($quotedName, '.') === false) {
|
||||
$quotedName = '"EXCLUDED".' . $quotedName;
|
||||
}
|
||||
$insertValues[] = $quotedName;
|
||||
}
|
||||
$insertSql = 'INSERT (' . implode(', ', $insertNames) . ')'
|
||||
. ' VALUES (' . implode(', ', $insertValues) . ')';
|
||||
if ($updateColumns === false) {
|
||||
return "$mergeSql WHEN NOT MATCHED THEN $insertSql";
|
||||
}
|
||||
|
||||
if ($updateColumns === true) {
|
||||
$updateColumns = [];
|
||||
foreach ($updateNames as $name) {
|
||||
$quotedName = $this->db->quoteColumnName($name);
|
||||
if (strrpos($quotedName, '.') === false) {
|
||||
$quotedName = '"EXCLUDED".' . $quotedName;
|
||||
}
|
||||
$updateColumns[$name] = new Expression($quotedName);
|
||||
}
|
||||
}
|
||||
list($updates, $params) = $this->prepareUpdateSets($table, $updateColumns, $params);
|
||||
$updateSql = 'UPDATE SET ' . implode(', ', $updates);
|
||||
return "$mergeSql WHEN MATCHED THEN $updateSql WHEN NOT MATCHED THEN $insertSql";
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a batch INSERT SQL statement.
|
||||
*
|
||||
* For example,
|
||||
*
|
||||
* ```php
|
||||
* $sql = $queryBuilder->batchInsert('user', ['name', 'age'], [
|
||||
* ['Tom', 30],
|
||||
* ['Jane', 20],
|
||||
* ['Linda', 25],
|
||||
* ]);
|
||||
* ```
|
||||
*
|
||||
* Note that the values in each row must match the corresponding column names.
|
||||
*
|
||||
* @param string $table the table that new rows will be inserted into.
|
||||
* @param array $columns the column names
|
||||
* @param array|\Generator $rows the rows to be batch inserted into the table
|
||||
* @return string the batch INSERT SQL statement
|
||||
*/
|
||||
public function batchInsert($table, $columns, $rows, &$params = [])
|
||||
{
|
||||
if (empty($rows)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$schema = $this->db->getSchema();
|
||||
if (($tableSchema = $schema->getTableSchema($table)) !== null) {
|
||||
$columnSchemas = $tableSchema->columns;
|
||||
} else {
|
||||
$columnSchemas = [];
|
||||
}
|
||||
|
||||
$values = [];
|
||||
foreach ($rows as $row) {
|
||||
$vs = [];
|
||||
foreach ($row as $i => $value) {
|
||||
if (isset($columns[$i], $columnSchemas[$columns[$i]])) {
|
||||
$value = $columnSchemas[$columns[$i]]->dbTypecast($value);
|
||||
}
|
||||
if (is_string($value)) {
|
||||
$value = $schema->quoteValue($value);
|
||||
} elseif (is_float($value)) {
|
||||
// ensure type cast always has . as decimal separator in all locales
|
||||
$value = StringHelper::floatToString($value);
|
||||
} elseif ($value === false) {
|
||||
$value = 0;
|
||||
} elseif ($value === null) {
|
||||
$value = 'NULL';
|
||||
} elseif ($value instanceof ExpressionInterface) {
|
||||
$value = $this->buildExpression($value, $params);
|
||||
}
|
||||
$vs[] = $value;
|
||||
}
|
||||
$values[] = '(' . implode(', ', $vs) . ')';
|
||||
}
|
||||
if (empty($values)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
foreach ($columns as $i => $name) {
|
||||
$columns[$i] = $schema->quoteColumnName($name);
|
||||
}
|
||||
|
||||
$tableAndColumns = ' INTO ' . $schema->quoteTableName($table)
|
||||
. ' (' . implode(', ', $columns) . ') VALUES ';
|
||||
|
||||
return 'INSERT ALL ' . $tableAndColumns . implode($tableAndColumns, $values) . ' SELECT 1 FROM SYS.DUAL';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @since 2.0.8
|
||||
*/
|
||||
public function selectExists($rawSql)
|
||||
{
|
||||
return 'SELECT CASE WHEN EXISTS(' . $rawSql . ') THEN 1 ELSE 0 END FROM DUAL';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @since 2.0.8
|
||||
*/
|
||||
public function dropCommentFromColumn($table, $column)
|
||||
{
|
||||
return 'COMMENT ON COLUMN ' . $this->db->quoteTableName($table) . '.' . $this->db->quoteColumnName($column) . " IS ''";
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @since 2.0.8
|
||||
*/
|
||||
public function dropCommentFromTable($table)
|
||||
{
|
||||
return 'COMMENT ON TABLE ' . $this->db->quoteTableName($table) . " IS ''";
|
||||
}
|
||||
}
|
||||
738
vendor/yiisoft/yii2/db/oci/Schema.php
vendored
Normal file
738
vendor/yiisoft/yii2/db/oci/Schema.php
vendored
Normal file
@@ -0,0 +1,738 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\oci;
|
||||
|
||||
use yii\base\InvalidCallException;
|
||||
use yii\base\NotSupportedException;
|
||||
use yii\db\CheckConstraint;
|
||||
use yii\db\ColumnSchema;
|
||||
use yii\db\Connection;
|
||||
use yii\db\Constraint;
|
||||
use yii\db\ConstraintFinderInterface;
|
||||
use yii\db\ConstraintFinderTrait;
|
||||
use yii\db\Expression;
|
||||
use yii\db\ForeignKeyConstraint;
|
||||
use yii\db\IndexConstraint;
|
||||
use yii\db\TableSchema;
|
||||
use yii\helpers\ArrayHelper;
|
||||
|
||||
/**
|
||||
* Schema is the class for retrieving metadata from an Oracle database.
|
||||
*
|
||||
* @property string $lastInsertID The row ID of the last row inserted, or the last value retrieved from the
|
||||
* sequence object. This property is read-only.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class Schema extends \yii\db\Schema implements ConstraintFinderInterface
|
||||
{
|
||||
use ConstraintFinderTrait;
|
||||
|
||||
/**
|
||||
* @var array map of DB errors and corresponding exceptions
|
||||
* If left part is found in DB error message exception class from the right part is used.
|
||||
*/
|
||||
public $exceptionMap = [
|
||||
'ORA-00001: unique constraint' => 'yii\db\IntegrityException',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $tableQuoteCharacter = '"';
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
parent::init();
|
||||
if ($this->defaultSchema === null) {
|
||||
$username = $this->db->username;
|
||||
if (empty($username)) {
|
||||
$username = isset($this->db->masters[0]['username']) ? $this->db->masters[0]['username'] : '';
|
||||
}
|
||||
$this->defaultSchema = strtoupper($username);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function resolveTableName($name)
|
||||
{
|
||||
$resolvedName = new TableSchema();
|
||||
$parts = explode('.', str_replace('"', '', $name));
|
||||
if (isset($parts[1])) {
|
||||
$resolvedName->schemaName = $parts[0];
|
||||
$resolvedName->name = $parts[1];
|
||||
} else {
|
||||
$resolvedName->schemaName = $this->defaultSchema;
|
||||
$resolvedName->name = $name;
|
||||
}
|
||||
$resolvedName->fullName = ($resolvedName->schemaName !== $this->defaultSchema ? $resolvedName->schemaName . '.' : '') . $resolvedName->name;
|
||||
return $resolvedName;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @see https://docs.oracle.com/cd/B28359_01/server.111/b28337/tdpsg_user_accounts.htm
|
||||
*/
|
||||
protected function findSchemaNames()
|
||||
{
|
||||
static $sql = <<<'SQL'
|
||||
SELECT "u"."USERNAME"
|
||||
FROM "DBA_USERS" "u"
|
||||
WHERE "u"."DEFAULT_TABLESPACE" NOT IN ('SYSTEM', 'SYSAUX')
|
||||
ORDER BY "u"."USERNAME" ASC
|
||||
SQL;
|
||||
|
||||
return $this->db->createCommand($sql)->queryColumn();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function findTableNames($schema = '')
|
||||
{
|
||||
if ($schema === '') {
|
||||
$sql = <<<'SQL'
|
||||
SELECT
|
||||
TABLE_NAME
|
||||
FROM USER_TABLES
|
||||
UNION ALL
|
||||
SELECT
|
||||
VIEW_NAME AS TABLE_NAME
|
||||
FROM USER_VIEWS
|
||||
UNION ALL
|
||||
SELECT
|
||||
MVIEW_NAME AS TABLE_NAME
|
||||
FROM USER_MVIEWS
|
||||
ORDER BY TABLE_NAME
|
||||
SQL;
|
||||
$command = $this->db->createCommand($sql);
|
||||
} else {
|
||||
$sql = <<<'SQL'
|
||||
SELECT
|
||||
OBJECT_NAME AS TABLE_NAME
|
||||
FROM ALL_OBJECTS
|
||||
WHERE
|
||||
OBJECT_TYPE IN ('TABLE', 'VIEW', 'MATERIALIZED VIEW')
|
||||
AND OWNER = :schema
|
||||
ORDER BY OBJECT_NAME
|
||||
SQL;
|
||||
$command = $this->db->createCommand($sql, [':schema' => $schema]);
|
||||
}
|
||||
|
||||
$rows = $command->queryAll();
|
||||
$names = [];
|
||||
foreach ($rows as $row) {
|
||||
if ($this->db->slavePdo->getAttribute(\PDO::ATTR_CASE) === \PDO::CASE_LOWER) {
|
||||
$row = array_change_key_case($row, CASE_UPPER);
|
||||
}
|
||||
$names[] = $row['TABLE_NAME'];
|
||||
}
|
||||
|
||||
return $names;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function loadTableSchema($name)
|
||||
{
|
||||
$table = new TableSchema();
|
||||
$this->resolveTableNames($table, $name);
|
||||
if ($this->findColumns($table)) {
|
||||
$this->findConstraints($table);
|
||||
return $table;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function loadTablePrimaryKey($tableName)
|
||||
{
|
||||
return $this->loadTableConstraints($tableName, 'primaryKey');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function loadTableForeignKeys($tableName)
|
||||
{
|
||||
return $this->loadTableConstraints($tableName, 'foreignKeys');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function loadTableIndexes($tableName)
|
||||
{
|
||||
static $sql = <<<'SQL'
|
||||
SELECT
|
||||
/*+ PUSH_PRED("ui") PUSH_PRED("uicol") PUSH_PRED("uc") */
|
||||
"ui"."INDEX_NAME" AS "name",
|
||||
"uicol"."COLUMN_NAME" AS "column_name",
|
||||
CASE "ui"."UNIQUENESS" WHEN 'UNIQUE' THEN 1 ELSE 0 END AS "index_is_unique",
|
||||
CASE WHEN "uc"."CONSTRAINT_NAME" IS NOT NULL THEN 1 ELSE 0 END AS "index_is_primary"
|
||||
FROM "SYS"."USER_INDEXES" "ui"
|
||||
LEFT JOIN "SYS"."USER_IND_COLUMNS" "uicol"
|
||||
ON "uicol"."INDEX_NAME" = "ui"."INDEX_NAME"
|
||||
LEFT JOIN "SYS"."USER_CONSTRAINTS" "uc"
|
||||
ON "uc"."OWNER" = "ui"."TABLE_OWNER" AND "uc"."CONSTRAINT_NAME" = "ui"."INDEX_NAME" AND "uc"."CONSTRAINT_TYPE" = 'P'
|
||||
WHERE "ui"."TABLE_OWNER" = :schemaName AND "ui"."TABLE_NAME" = :tableName
|
||||
ORDER BY "uicol"."COLUMN_POSITION" ASC
|
||||
SQL;
|
||||
|
||||
$resolvedName = $this->resolveTableName($tableName);
|
||||
$indexes = $this->db->createCommand($sql, [
|
||||
':schemaName' => $resolvedName->schemaName,
|
||||
':tableName' => $resolvedName->name,
|
||||
])->queryAll();
|
||||
$indexes = $this->normalizePdoRowKeyCase($indexes, true);
|
||||
$indexes = ArrayHelper::index($indexes, null, 'name');
|
||||
$result = [];
|
||||
foreach ($indexes as $name => $index) {
|
||||
$result[] = new IndexConstraint([
|
||||
'isPrimary' => (bool) $index[0]['index_is_primary'],
|
||||
'isUnique' => (bool) $index[0]['index_is_unique'],
|
||||
'name' => $name,
|
||||
'columnNames' => ArrayHelper::getColumn($index, 'column_name'),
|
||||
]);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function loadTableUniques($tableName)
|
||||
{
|
||||
return $this->loadTableConstraints($tableName, 'uniques');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function loadTableChecks($tableName)
|
||||
{
|
||||
return $this->loadTableConstraints($tableName, 'checks');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @throws NotSupportedException if this method is called.
|
||||
*/
|
||||
protected function loadTableDefaultValues($tableName)
|
||||
{
|
||||
throw new NotSupportedException('Oracle does not support default value constraints.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function releaseSavepoint($name)
|
||||
{
|
||||
// does nothing as Oracle does not support this
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function quoteSimpleTableName($name)
|
||||
{
|
||||
return strpos($name, '"') !== false ? $name : '"' . $name . '"';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createQueryBuilder()
|
||||
{
|
||||
return new QueryBuilder($this->db);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createColumnSchemaBuilder($type, $length = null)
|
||||
{
|
||||
return new ColumnSchemaBuilder($type, $length, $this->db);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the table name and schema name (if any).
|
||||
*
|
||||
* @param TableSchema $table the table metadata object
|
||||
* @param string $name the table name
|
||||
*/
|
||||
protected function resolveTableNames($table, $name)
|
||||
{
|
||||
$parts = explode('.', str_replace('"', '', $name));
|
||||
if (isset($parts[1])) {
|
||||
$table->schemaName = $parts[0];
|
||||
$table->name = $parts[1];
|
||||
} else {
|
||||
$table->schemaName = $this->defaultSchema;
|
||||
$table->name = $name;
|
||||
}
|
||||
|
||||
$table->fullName = $table->schemaName !== $this->defaultSchema ? $table->schemaName . '.' . $table->name : $table->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects the table column metadata.
|
||||
* @param TableSchema $table the table schema
|
||||
* @return bool whether the table exists
|
||||
*/
|
||||
protected function findColumns($table)
|
||||
{
|
||||
$sql = <<<'SQL'
|
||||
SELECT
|
||||
A.COLUMN_NAME,
|
||||
A.DATA_TYPE,
|
||||
A.DATA_PRECISION,
|
||||
A.DATA_SCALE,
|
||||
(
|
||||
CASE A.CHAR_USED WHEN 'C' THEN A.CHAR_LENGTH
|
||||
ELSE A.DATA_LENGTH
|
||||
END
|
||||
) AS DATA_LENGTH,
|
||||
A.NULLABLE,
|
||||
A.DATA_DEFAULT,
|
||||
COM.COMMENTS AS COLUMN_COMMENT
|
||||
FROM ALL_TAB_COLUMNS A
|
||||
INNER JOIN ALL_OBJECTS B ON B.OWNER = A.OWNER AND LTRIM(B.OBJECT_NAME) = LTRIM(A.TABLE_NAME)
|
||||
LEFT JOIN ALL_COL_COMMENTS COM ON (A.OWNER = COM.OWNER AND A.TABLE_NAME = COM.TABLE_NAME AND A.COLUMN_NAME = COM.COLUMN_NAME)
|
||||
WHERE
|
||||
A.OWNER = :schemaName
|
||||
AND B.OBJECT_TYPE IN ('TABLE', 'VIEW', 'MATERIALIZED VIEW')
|
||||
AND B.OBJECT_NAME = :tableName
|
||||
ORDER BY A.COLUMN_ID
|
||||
SQL;
|
||||
|
||||
try {
|
||||
$columns = $this->db->createCommand($sql, [
|
||||
':tableName' => $table->name,
|
||||
':schemaName' => $table->schemaName,
|
||||
])->queryAll();
|
||||
} catch (\Exception $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (empty($columns)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($columns as $column) {
|
||||
if ($this->db->slavePdo->getAttribute(\PDO::ATTR_CASE) === \PDO::CASE_LOWER) {
|
||||
$column = array_change_key_case($column, CASE_UPPER);
|
||||
}
|
||||
$c = $this->createColumn($column);
|
||||
$table->columns[$c->name] = $c;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sequence name of table.
|
||||
*
|
||||
* @param string $tableName
|
||||
* @internal param \yii\db\TableSchema $table->name the table schema
|
||||
* @return string|null whether the sequence exists
|
||||
*/
|
||||
protected function getTableSequenceName($tableName)
|
||||
{
|
||||
$sequenceNameSql = <<<'SQL'
|
||||
SELECT
|
||||
UD.REFERENCED_NAME AS SEQUENCE_NAME
|
||||
FROM USER_DEPENDENCIES UD
|
||||
JOIN USER_TRIGGERS UT ON (UT.TRIGGER_NAME = UD.NAME)
|
||||
WHERE
|
||||
UT.TABLE_NAME = :tableName
|
||||
AND UD.TYPE = 'TRIGGER'
|
||||
AND UD.REFERENCED_TYPE = 'SEQUENCE'
|
||||
SQL;
|
||||
$sequenceName = $this->db->createCommand($sequenceNameSql, [':tableName' => $tableName])->queryScalar();
|
||||
return $sequenceName === false ? null : $sequenceName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Overrides method in class 'Schema'
|
||||
* @see http://www.php.net/manual/en/function.PDO-lastInsertId.php -> Oracle does not support this
|
||||
*
|
||||
* Returns the ID of the last inserted row or sequence value.
|
||||
* @param string $sequenceName name of the sequence object (required by some DBMS)
|
||||
* @return string the row ID of the last row inserted, or the last value retrieved from the sequence object
|
||||
* @throws InvalidCallException if the DB connection is not active
|
||||
*/
|
||||
public function getLastInsertID($sequenceName = '')
|
||||
{
|
||||
if ($this->db->isActive) {
|
||||
// get the last insert id from the master connection
|
||||
$sequenceName = $this->quoteSimpleTableName($sequenceName);
|
||||
return $this->db->useMaster(function (Connection $db) use ($sequenceName) {
|
||||
return $db->createCommand("SELECT {$sequenceName}.CURRVAL FROM DUAL")->queryScalar();
|
||||
});
|
||||
} else {
|
||||
throw new InvalidCallException('DB Connection is not active.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates ColumnSchema instance.
|
||||
*
|
||||
* @param array $column
|
||||
* @return ColumnSchema
|
||||
*/
|
||||
protected function createColumn($column)
|
||||
{
|
||||
$c = $this->createColumnSchema();
|
||||
$c->name = $column['COLUMN_NAME'];
|
||||
$c->allowNull = $column['NULLABLE'] === 'Y';
|
||||
$c->comment = $column['COLUMN_COMMENT'] === null ? '' : $column['COLUMN_COMMENT'];
|
||||
$c->isPrimaryKey = false;
|
||||
$this->extractColumnType($c, $column['DATA_TYPE'], $column['DATA_PRECISION'], $column['DATA_SCALE'], $column['DATA_LENGTH']);
|
||||
$this->extractColumnSize($c, $column['DATA_TYPE'], $column['DATA_PRECISION'], $column['DATA_SCALE'], $column['DATA_LENGTH']);
|
||||
|
||||
$c->phpType = $this->getColumnPhpType($c);
|
||||
|
||||
if (!$c->isPrimaryKey) {
|
||||
if (stripos($column['DATA_DEFAULT'], 'timestamp') !== false) {
|
||||
$c->defaultValue = null;
|
||||
} else {
|
||||
$defaultValue = $column['DATA_DEFAULT'];
|
||||
if ($c->type === 'timestamp' && $defaultValue === 'CURRENT_TIMESTAMP') {
|
||||
$c->defaultValue = new Expression('CURRENT_TIMESTAMP');
|
||||
} else {
|
||||
if ($defaultValue !== null) {
|
||||
if (($len = strlen($defaultValue)) > 2 && $defaultValue[0] === "'"
|
||||
&& $defaultValue[$len - 1] === "'"
|
||||
) {
|
||||
$defaultValue = substr($column['DATA_DEFAULT'], 1, -1);
|
||||
} else {
|
||||
$defaultValue = trim($defaultValue);
|
||||
}
|
||||
}
|
||||
$c->defaultValue = $c->phpTypecast($defaultValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds constraints and fills them into TableSchema object passed.
|
||||
* @param TableSchema $table
|
||||
*/
|
||||
protected function findConstraints($table)
|
||||
{
|
||||
$sql = <<<'SQL'
|
||||
SELECT
|
||||
/*+ PUSH_PRED(C) PUSH_PRED(D) PUSH_PRED(E) */
|
||||
D.CONSTRAINT_NAME,
|
||||
D.CONSTRAINT_TYPE,
|
||||
C.COLUMN_NAME,
|
||||
C.POSITION,
|
||||
D.R_CONSTRAINT_NAME,
|
||||
E.TABLE_NAME AS TABLE_REF,
|
||||
F.COLUMN_NAME AS COLUMN_REF,
|
||||
C.TABLE_NAME
|
||||
FROM ALL_CONS_COLUMNS C
|
||||
INNER JOIN ALL_CONSTRAINTS D ON D.OWNER = C.OWNER AND D.CONSTRAINT_NAME = C.CONSTRAINT_NAME
|
||||
LEFT JOIN ALL_CONSTRAINTS E ON E.OWNER = D.R_OWNER AND E.CONSTRAINT_NAME = D.R_CONSTRAINT_NAME
|
||||
LEFT JOIN ALL_CONS_COLUMNS F ON F.OWNER = E.OWNER AND F.CONSTRAINT_NAME = E.CONSTRAINT_NAME AND F.POSITION = C.POSITION
|
||||
WHERE
|
||||
C.OWNER = :schemaName
|
||||
AND C.TABLE_NAME = :tableName
|
||||
ORDER BY D.CONSTRAINT_NAME, C.POSITION
|
||||
SQL;
|
||||
$command = $this->db->createCommand($sql, [
|
||||
':tableName' => $table->name,
|
||||
':schemaName' => $table->schemaName,
|
||||
]);
|
||||
$constraints = [];
|
||||
foreach ($command->queryAll() as $row) {
|
||||
if ($this->db->slavePdo->getAttribute(\PDO::ATTR_CASE) === \PDO::CASE_LOWER) {
|
||||
$row = array_change_key_case($row, CASE_UPPER);
|
||||
}
|
||||
|
||||
if ($row['CONSTRAINT_TYPE'] === 'P') {
|
||||
$table->columns[$row['COLUMN_NAME']]->isPrimaryKey = true;
|
||||
$table->primaryKey[] = $row['COLUMN_NAME'];
|
||||
if (empty($table->sequenceName)) {
|
||||
$table->sequenceName = $this->getTableSequenceName($table->name);
|
||||
}
|
||||
}
|
||||
|
||||
if ($row['CONSTRAINT_TYPE'] !== 'R') {
|
||||
// this condition is not checked in SQL WHERE because of an Oracle Bug:
|
||||
// see https://github.com/yiisoft/yii2/pull/8844
|
||||
continue;
|
||||
}
|
||||
|
||||
$name = $row['CONSTRAINT_NAME'];
|
||||
if (!isset($constraints[$name])) {
|
||||
$constraints[$name] = [
|
||||
'tableName' => $row['TABLE_REF'],
|
||||
'columns' => [],
|
||||
];
|
||||
}
|
||||
$constraints[$name]['columns'][$row['COLUMN_NAME']] = $row['COLUMN_REF'];
|
||||
}
|
||||
|
||||
foreach ($constraints as $constraint) {
|
||||
$name = current(array_keys($constraint));
|
||||
|
||||
$table->foreignKeys[$name] = array_merge([$constraint['tableName']], $constraint['columns']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all unique indexes for the given table.
|
||||
* Each array element is of the following structure:.
|
||||
*
|
||||
* ```php
|
||||
* [
|
||||
* 'IndexName1' => ['col1' [, ...]],
|
||||
* 'IndexName2' => ['col2' [, ...]],
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* @param TableSchema $table the table metadata
|
||||
* @return array all unique indexes for the given table.
|
||||
* @since 2.0.4
|
||||
*/
|
||||
public function findUniqueIndexes($table)
|
||||
{
|
||||
$query = <<<'SQL'
|
||||
SELECT
|
||||
DIC.INDEX_NAME,
|
||||
DIC.COLUMN_NAME
|
||||
FROM ALL_INDEXES DI
|
||||
INNER JOIN ALL_IND_COLUMNS DIC ON DI.TABLE_NAME = DIC.TABLE_NAME AND DI.INDEX_NAME = DIC.INDEX_NAME
|
||||
WHERE
|
||||
DI.UNIQUENESS = 'UNIQUE'
|
||||
AND DIC.TABLE_OWNER = :schemaName
|
||||
AND DIC.TABLE_NAME = :tableName
|
||||
ORDER BY DIC.TABLE_NAME, DIC.INDEX_NAME, DIC.COLUMN_POSITION
|
||||
SQL;
|
||||
$result = [];
|
||||
$command = $this->db->createCommand($query, [
|
||||
':tableName' => $table->name,
|
||||
':schemaName' => $table->schemaName,
|
||||
]);
|
||||
foreach ($command->queryAll() as $row) {
|
||||
$result[$row['INDEX_NAME']][] = $row['COLUMN_NAME'];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the data types for the given column.
|
||||
* @param ColumnSchema $column
|
||||
* @param string $dbType DB type
|
||||
* @param string $precision total number of digits.
|
||||
* This parameter is available since version 2.0.4.
|
||||
* @param string $scale number of digits on the right of the decimal separator.
|
||||
* This parameter is available since version 2.0.4.
|
||||
* @param string $length length for character types.
|
||||
* This parameter is available since version 2.0.4.
|
||||
*/
|
||||
protected function extractColumnType($column, $dbType, $precision, $scale, $length)
|
||||
{
|
||||
$column->dbType = $dbType;
|
||||
|
||||
if (strpos($dbType, 'FLOAT') !== false || strpos($dbType, 'DOUBLE') !== false) {
|
||||
$column->type = 'double';
|
||||
} elseif (strpos($dbType, 'NUMBER') !== false) {
|
||||
if ($scale === null || $scale > 0) {
|
||||
$column->type = 'decimal';
|
||||
} else {
|
||||
$column->type = 'integer';
|
||||
}
|
||||
} elseif (strpos($dbType, 'INTEGER') !== false) {
|
||||
$column->type = 'integer';
|
||||
} elseif (strpos($dbType, 'BLOB') !== false) {
|
||||
$column->type = 'binary';
|
||||
} elseif (strpos($dbType, 'CLOB') !== false) {
|
||||
$column->type = 'text';
|
||||
} elseif (strpos($dbType, 'TIMESTAMP') !== false) {
|
||||
$column->type = 'timestamp';
|
||||
} else {
|
||||
$column->type = 'string';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts size, precision and scale information from column's DB type.
|
||||
* @param ColumnSchema $column
|
||||
* @param string $dbType the column's DB type
|
||||
* @param string $precision total number of digits.
|
||||
* This parameter is available since version 2.0.4.
|
||||
* @param string $scale number of digits on the right of the decimal separator.
|
||||
* This parameter is available since version 2.0.4.
|
||||
* @param string $length length for character types.
|
||||
* This parameter is available since version 2.0.4.
|
||||
*/
|
||||
protected function extractColumnSize($column, $dbType, $precision, $scale, $length)
|
||||
{
|
||||
$column->size = trim($length) === '' ? null : (int) $length;
|
||||
$column->precision = trim($precision) === '' ? null : (int) $precision;
|
||||
$column->scale = trim($scale) === '' ? null : (int) $scale;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function insert($table, $columns)
|
||||
{
|
||||
$params = [];
|
||||
$returnParams = [];
|
||||
$sql = $this->db->getQueryBuilder()->insert($table, $columns, $params);
|
||||
$tableSchema = $this->getTableSchema($table);
|
||||
$returnColumns = $tableSchema->primaryKey;
|
||||
if (!empty($returnColumns)) {
|
||||
$columnSchemas = $tableSchema->columns;
|
||||
$returning = [];
|
||||
foreach ((array) $returnColumns as $name) {
|
||||
$phName = QueryBuilder::PARAM_PREFIX . (count($params) + count($returnParams));
|
||||
$returnParams[$phName] = [
|
||||
'column' => $name,
|
||||
'value' => null,
|
||||
];
|
||||
if (!isset($columnSchemas[$name]) || $columnSchemas[$name]->phpType !== 'integer') {
|
||||
$returnParams[$phName]['dataType'] = \PDO::PARAM_STR;
|
||||
} else {
|
||||
$returnParams[$phName]['dataType'] = \PDO::PARAM_INT;
|
||||
}
|
||||
$returnParams[$phName]['size'] = isset($columnSchemas[$name]) && isset($columnSchemas[$name]->size) ? $columnSchemas[$name]->size : -1;
|
||||
$returning[] = $this->quoteColumnName($name);
|
||||
}
|
||||
$sql .= ' RETURNING ' . implode(', ', $returning) . ' INTO ' . implode(', ', array_keys($returnParams));
|
||||
}
|
||||
|
||||
$command = $this->db->createCommand($sql, $params);
|
||||
$command->prepare(false);
|
||||
|
||||
foreach ($returnParams as $name => &$value) {
|
||||
$command->pdoStatement->bindParam($name, $value['value'], $value['dataType'], $value['size']);
|
||||
}
|
||||
|
||||
if (!$command->execute()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$result = [];
|
||||
foreach ($returnParams as $value) {
|
||||
$result[$value['column']] = $value['value'];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads multiple types of constraints and returns the specified ones.
|
||||
* @param string $tableName table name.
|
||||
* @param string $returnType return type:
|
||||
* - primaryKey
|
||||
* - foreignKeys
|
||||
* - uniques
|
||||
* - checks
|
||||
* @return mixed constraints.
|
||||
*/
|
||||
private function loadTableConstraints($tableName, $returnType)
|
||||
{
|
||||
static $sql = <<<'SQL'
|
||||
SELECT
|
||||
/*+ PUSH_PRED("uc") PUSH_PRED("uccol") PUSH_PRED("fuc") */
|
||||
"uc"."CONSTRAINT_NAME" AS "name",
|
||||
"uccol"."COLUMN_NAME" AS "column_name",
|
||||
"uc"."CONSTRAINT_TYPE" AS "type",
|
||||
"fuc"."OWNER" AS "foreign_table_schema",
|
||||
"fuc"."TABLE_NAME" AS "foreign_table_name",
|
||||
"fuccol"."COLUMN_NAME" AS "foreign_column_name",
|
||||
"uc"."DELETE_RULE" AS "on_delete",
|
||||
"uc"."SEARCH_CONDITION" AS "check_expr"
|
||||
FROM "USER_CONSTRAINTS" "uc"
|
||||
INNER JOIN "USER_CONS_COLUMNS" "uccol"
|
||||
ON "uccol"."OWNER" = "uc"."OWNER" AND "uccol"."CONSTRAINT_NAME" = "uc"."CONSTRAINT_NAME"
|
||||
LEFT JOIN "USER_CONSTRAINTS" "fuc"
|
||||
ON "fuc"."OWNER" = "uc"."R_OWNER" AND "fuc"."CONSTRAINT_NAME" = "uc"."R_CONSTRAINT_NAME"
|
||||
LEFT JOIN "USER_CONS_COLUMNS" "fuccol"
|
||||
ON "fuccol"."OWNER" = "fuc"."OWNER" AND "fuccol"."CONSTRAINT_NAME" = "fuc"."CONSTRAINT_NAME" AND "fuccol"."POSITION" = "uccol"."POSITION"
|
||||
WHERE "uc"."OWNER" = :schemaName AND "uc"."TABLE_NAME" = :tableName
|
||||
ORDER BY "uccol"."POSITION" ASC
|
||||
SQL;
|
||||
|
||||
$resolvedName = $this->resolveTableName($tableName);
|
||||
$constraints = $this->db->createCommand($sql, [
|
||||
':schemaName' => $resolvedName->schemaName,
|
||||
':tableName' => $resolvedName->name,
|
||||
])->queryAll();
|
||||
$constraints = $this->normalizePdoRowKeyCase($constraints, true);
|
||||
$constraints = ArrayHelper::index($constraints, null, ['type', 'name']);
|
||||
$result = [
|
||||
'primaryKey' => null,
|
||||
'foreignKeys' => [],
|
||||
'uniques' => [],
|
||||
'checks' => [],
|
||||
];
|
||||
foreach ($constraints as $type => $names) {
|
||||
foreach ($names as $name => $constraint) {
|
||||
switch ($type) {
|
||||
case 'P':
|
||||
$result['primaryKey'] = new Constraint([
|
||||
'name' => $name,
|
||||
'columnNames' => ArrayHelper::getColumn($constraint, 'column_name'),
|
||||
]);
|
||||
break;
|
||||
case 'R':
|
||||
$result['foreignKeys'][] = new ForeignKeyConstraint([
|
||||
'name' => $name,
|
||||
'columnNames' => ArrayHelper::getColumn($constraint, 'column_name'),
|
||||
'foreignSchemaName' => $constraint[0]['foreign_table_schema'],
|
||||
'foreignTableName' => $constraint[0]['foreign_table_name'],
|
||||
'foreignColumnNames' => ArrayHelper::getColumn($constraint, 'foreign_column_name'),
|
||||
'onDelete' => $constraint[0]['on_delete'],
|
||||
'onUpdate' => null,
|
||||
]);
|
||||
break;
|
||||
case 'U':
|
||||
$result['uniques'][] = new Constraint([
|
||||
'name' => $name,
|
||||
'columnNames' => ArrayHelper::getColumn($constraint, 'column_name'),
|
||||
]);
|
||||
break;
|
||||
case 'C':
|
||||
$result['checks'][] = new CheckConstraint([
|
||||
'name' => $name,
|
||||
'columnNames' => ArrayHelper::getColumn($constraint, 'column_name'),
|
||||
'expression' => $constraint[0]['check_expr'],
|
||||
]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($result as $type => $data) {
|
||||
$this->setTableMetadata($tableName, $type, $data);
|
||||
}
|
||||
|
||||
return $result[$returnType];
|
||||
}
|
||||
}
|
||||
71
vendor/yiisoft/yii2/db/oci/conditions/InConditionBuilder.php
vendored
Normal file
71
vendor/yiisoft/yii2/db/oci/conditions/InConditionBuilder.php
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\oci\conditions;
|
||||
|
||||
use yii\db\conditions\InCondition;
|
||||
use yii\db\ExpressionInterface;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class InConditionBuilder extends \yii\db\conditions\InConditionBuilder
|
||||
{
|
||||
/**
|
||||
* Method builds the raw SQL from the $expression that will not be additionally
|
||||
* escaped or quoted.
|
||||
*
|
||||
* @param ExpressionInterface|InCondition $expression the expression to be built.
|
||||
* @param array $params the binding parameters.
|
||||
* @return string the raw SQL that will not be additionally escaped or quoted.
|
||||
*/
|
||||
public function build(ExpressionInterface $expression, array &$params = [])
|
||||
{
|
||||
$splitCondition = $this->splitCondition($expression, $params);
|
||||
if ($splitCondition !== null) {
|
||||
return $splitCondition;
|
||||
}
|
||||
|
||||
return parent::build($expression, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Oracle DBMS does not support more than 1000 parameters in `IN` condition.
|
||||
* This method splits long `IN` condition into series of smaller ones.
|
||||
*
|
||||
* @param ExpressionInterface|InCondition $condition the expression to be built.
|
||||
* @param array $params the binding parameters.
|
||||
* @return null|string null when split is not required. Otherwise - built SQL condition.
|
||||
*/
|
||||
protected function splitCondition(InCondition $condition, &$params)
|
||||
{
|
||||
$operator = $condition->getOperator();
|
||||
$values = $condition->getValues();
|
||||
$column = $condition->getColumn();
|
||||
|
||||
if ($values instanceof \Traversable) {
|
||||
$values = iterator_to_array($values);
|
||||
}
|
||||
|
||||
if (!is_array($values)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$maxParameters = 1000;
|
||||
$count = count($values);
|
||||
if ($count <= $maxParameters) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$slices = [];
|
||||
for ($i = 0; $i < $count; $i += $maxParameters) {
|
||||
$slices[] = $this->queryBuilder->createConditionFromArray([$operator, $column, array_slice($values, $i, $maxParameters)]);
|
||||
}
|
||||
|
||||
return $this->queryBuilder->buildCondition([($operator === 'IN') ? 'OR' : 'AND', $slices], $params);
|
||||
}
|
||||
}
|
||||
48
vendor/yiisoft/yii2/db/oci/conditions/LikeConditionBuilder.php
vendored
Normal file
48
vendor/yiisoft/yii2/db/oci/conditions/LikeConditionBuilder.php
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\oci\conditions;
|
||||
|
||||
use yii\db\ExpressionInterface;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class LikeConditionBuilder extends \yii\db\conditions\LikeConditionBuilder
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $escapeCharacter = '!';
|
||||
/**
|
||||
* `\` is initialized in [[buildLikeCondition()]] method since
|
||||
* we need to choose replacement value based on [[\yii\db\Schema::quoteValue()]].
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $escapingReplacements = [
|
||||
'%' => '!%',
|
||||
'_' => '!_',
|
||||
'!' => '!!',
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function build(ExpressionInterface $expression, array &$params = [])
|
||||
{
|
||||
if (!isset($this->escapingReplacements['\\'])) {
|
||||
/*
|
||||
* Different pdo_oci8 versions may or may not implement PDO::quote(), so
|
||||
* yii\db\Schema::quoteValue() may or may not quote \.
|
||||
*/
|
||||
$this->escapingReplacements['\\'] = substr($this->queryBuilder->db->quoteValue('\\'), 1, -1);
|
||||
}
|
||||
|
||||
return parent::build($expression, $params);
|
||||
}
|
||||
}
|
||||
149
vendor/yiisoft/yii2/db/pgsql/ArrayExpressionBuilder.php
vendored
Normal file
149
vendor/yiisoft/yii2/db/pgsql/ArrayExpressionBuilder.php
vendored
Normal file
@@ -0,0 +1,149 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\pgsql;
|
||||
|
||||
use yii\db\ArrayExpression;
|
||||
use yii\db\ExpressionBuilderInterface;
|
||||
use yii\db\ExpressionBuilderTrait;
|
||||
use yii\db\ExpressionInterface;
|
||||
use yii\db\JsonExpression;
|
||||
use yii\db\Query;
|
||||
|
||||
/**
|
||||
* Class ArrayExpressionBuilder builds [[ArrayExpression]] for PostgreSQL DBMS.
|
||||
*
|
||||
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
|
||||
* @since 2.0.14
|
||||
*/
|
||||
class ArrayExpressionBuilder implements ExpressionBuilderInterface
|
||||
{
|
||||
use ExpressionBuilderTrait;
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @param ArrayExpression|ExpressionInterface $expression the expression to be built
|
||||
*/
|
||||
public function build(ExpressionInterface $expression, array &$params = [])
|
||||
{
|
||||
$value = $expression->getValue();
|
||||
if ($value === null) {
|
||||
return 'NULL';
|
||||
}
|
||||
|
||||
if ($value instanceof Query) {
|
||||
list ($sql, $params) = $this->queryBuilder->build($value, $params);
|
||||
return $this->buildSubqueryArray($sql, $expression);
|
||||
}
|
||||
|
||||
$placeholders = $this->buildPlaceholders($expression, $params);
|
||||
|
||||
return 'ARRAY[' . implode(', ', $placeholders) . ']' . $this->getTypehint($expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds placeholders array out of $expression values
|
||||
* @param ExpressionInterface|ArrayExpression $expression
|
||||
* @param array $params the binding parameters.
|
||||
* @return array
|
||||
*/
|
||||
protected function buildPlaceholders(ExpressionInterface $expression, &$params)
|
||||
{
|
||||
$value = $expression->getValue();
|
||||
|
||||
$placeholders = [];
|
||||
if ($value === null || !is_array($value) && !$value instanceof \Traversable) {
|
||||
return $placeholders;
|
||||
}
|
||||
|
||||
if ($expression->getDimension() > 1) {
|
||||
foreach ($value as $item) {
|
||||
$placeholders[] = $this->build($this->unnestArrayExpression($expression, $item), $params);
|
||||
}
|
||||
return $placeholders;
|
||||
}
|
||||
|
||||
foreach ($value as $item) {
|
||||
if ($item instanceof Query) {
|
||||
list ($sql, $params) = $this->queryBuilder->build($item, $params);
|
||||
$placeholders[] = $this->buildSubqueryArray($sql, $expression);
|
||||
continue;
|
||||
}
|
||||
|
||||
$item = $this->typecastValue($expression, $item);
|
||||
if ($item instanceof ExpressionInterface) {
|
||||
$placeholders[] = $this->queryBuilder->buildExpression($item, $params);
|
||||
continue;
|
||||
}
|
||||
|
||||
$placeholders[] = $this->queryBuilder->bindParam($item, $params);
|
||||
}
|
||||
|
||||
return $placeholders;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ArrayExpression $expression
|
||||
* @param mixed $value
|
||||
* @return ArrayExpression
|
||||
*/
|
||||
private function unnestArrayExpression(ArrayExpression $expression, $value)
|
||||
{
|
||||
$expressionClass = get_class($expression);
|
||||
|
||||
return new $expressionClass($value, $expression->getType(), $expression->getDimension()-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ArrayExpression $expression
|
||||
* @return string the typecast expression based on [[type]].
|
||||
*/
|
||||
protected function getTypehint(ArrayExpression $expression)
|
||||
{
|
||||
if ($expression->getType() === null) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$result = '::' . $expression->getType();
|
||||
$result .= str_repeat('[]', $expression->getDimension());
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an array expression from a subquery SQL.
|
||||
*
|
||||
* @param string $sql the subquery SQL.
|
||||
* @param ArrayExpression $expression
|
||||
* @return string the subquery array expression.
|
||||
*/
|
||||
protected function buildSubqueryArray($sql, ArrayExpression $expression)
|
||||
{
|
||||
return 'ARRAY(' . $sql . ')' . $this->getTypehint($expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Casts $value to use in $expression
|
||||
*
|
||||
* @param ArrayExpression $expression
|
||||
* @param mixed $value
|
||||
* @return JsonExpression
|
||||
*/
|
||||
protected function typecastValue(ArrayExpression $expression, $value)
|
||||
{
|
||||
if ($value instanceof ExpressionInterface) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if (in_array($expression->getType(), [Schema::TYPE_JSON, Schema::TYPE_JSONB], true)) {
|
||||
return new JsonExpression($value);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
109
vendor/yiisoft/yii2/db/pgsql/ArrayParser.php
vendored
Normal file
109
vendor/yiisoft/yii2/db/pgsql/ArrayParser.php
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\pgsql;
|
||||
|
||||
/**
|
||||
* The class converts PostgreSQL array representation to PHP array
|
||||
*
|
||||
* @author Sergei Tigrov <rrr-r@ya.ru>
|
||||
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
|
||||
* @since 2.0.14
|
||||
*/
|
||||
class ArrayParser
|
||||
{
|
||||
/**
|
||||
* @var string Character used in array
|
||||
*/
|
||||
private $delimiter = ',';
|
||||
|
||||
|
||||
/**
|
||||
* Convert array from PostgreSQL to PHP
|
||||
*
|
||||
* @param string $value string to be converted
|
||||
* @return array|null
|
||||
*/
|
||||
public function parse($value)
|
||||
{
|
||||
if ($value === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($value === '{}') {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $this->parseArray($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pares PgSQL array encoded in string
|
||||
*
|
||||
* @param string $value
|
||||
* @param int $i parse starting position
|
||||
* @return array
|
||||
*/
|
||||
private function parseArray($value, &$i = 0)
|
||||
{
|
||||
$result = [];
|
||||
$len = strlen($value);
|
||||
for (++$i; $i < $len; ++$i) {
|
||||
switch ($value[$i]) {
|
||||
case '{':
|
||||
$result[] = $this->parseArray($value, $i);
|
||||
break;
|
||||
case '}':
|
||||
break 2;
|
||||
case $this->delimiter:
|
||||
if (empty($result)) { // `{}` case
|
||||
$result[] = null;
|
||||
}
|
||||
if (in_array($value[$i + 1], [$this->delimiter, '}'], true)) { // `{,}` case
|
||||
$result[] = null;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
$result[] = $this->parseString($value, $i);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses PgSQL encoded string
|
||||
*
|
||||
* @param string $value
|
||||
* @param int $i parse starting position
|
||||
* @return null|string
|
||||
*/
|
||||
private function parseString($value, &$i)
|
||||
{
|
||||
$isQuoted = $value[$i] === '"';
|
||||
$stringEndChars = $isQuoted ? ['"'] : [$this->delimiter, '}'];
|
||||
$result = '';
|
||||
$len = strlen($value);
|
||||
for ($i += $isQuoted ? 1 : 0; $i < $len; ++$i) {
|
||||
if (in_array($value[$i], ['\\', '"'], true) && in_array($value[$i + 1], [$value[$i], '"'], true)) {
|
||||
++$i;
|
||||
} elseif (in_array($value[$i], $stringEndChars, true)) {
|
||||
break;
|
||||
}
|
||||
|
||||
$result .= $value[$i];
|
||||
}
|
||||
|
||||
$i -= $isQuoted ? 0 : 1;
|
||||
|
||||
if (!$isQuoted && $result === 'NULL') {
|
||||
$result = null;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
152
vendor/yiisoft/yii2/db/pgsql/ColumnSchema.php
vendored
Normal file
152
vendor/yiisoft/yii2/db/pgsql/ColumnSchema.php
vendored
Normal file
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\pgsql;
|
||||
|
||||
use yii\db\ArrayExpression;
|
||||
use yii\db\ExpressionInterface;
|
||||
use yii\db\JsonExpression;
|
||||
|
||||
/**
|
||||
* Class ColumnSchema for PostgreSQL database.
|
||||
*
|
||||
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
|
||||
*/
|
||||
class ColumnSchema extends \yii\db\ColumnSchema
|
||||
{
|
||||
/**
|
||||
* @var int the dimension of array. Defaults to 0, means this column is not an array.
|
||||
*/
|
||||
public $dimension = 0;
|
||||
/**
|
||||
* @var bool whether the column schema should OMIT using JSON support feature.
|
||||
* You can use this property to make upgrade to Yii 2.0.14 easier.
|
||||
* Default to `false`, meaning JSON support is enabled.
|
||||
*
|
||||
* @since 2.0.14.1
|
||||
* @deprecated Since 2.0.14.1 and will be removed in 2.1.
|
||||
*/
|
||||
public $disableJsonSupport = false;
|
||||
/**
|
||||
* @var bool whether the column schema should OMIT using PgSQL Arrays support feature.
|
||||
* You can use this property to make upgrade to Yii 2.0.14 easier.
|
||||
* Default to `false`, meaning Arrays support is enabled.
|
||||
*
|
||||
* @since 2.0.14.1
|
||||
* @deprecated Since 2.0.14.1 and will be removed in 2.1.
|
||||
*/
|
||||
public $disableArraySupport = false;
|
||||
/**
|
||||
* @var bool whether the Array column value should be unserialized to an [[ArrayExpression]] object.
|
||||
* You can use this property to make upgrade to Yii 2.0.14 easier.
|
||||
* Default to `true`, meaning arrays are unserialized to [[ArrayExpression]] objects.
|
||||
*
|
||||
* @since 2.0.14.1
|
||||
* @deprecated Since 2.0.14.1 and will be removed in 2.1.
|
||||
*/
|
||||
public $deserializeArrayColumnToArrayExpression = true;
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function dbTypecast($value)
|
||||
{
|
||||
if ($value === null) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if ($value instanceof ExpressionInterface) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if ($this->dimension > 0) {
|
||||
return $this->disableArraySupport
|
||||
? (string) $value
|
||||
: new ArrayExpression($value, $this->dbType, $this->dimension);
|
||||
}
|
||||
if (!$this->disableJsonSupport && in_array($this->dbType, [Schema::TYPE_JSON, Schema::TYPE_JSONB], true)) {
|
||||
return new JsonExpression($value, $this->dbType);
|
||||
}
|
||||
|
||||
return $this->typecast($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function phpTypecast($value)
|
||||
{
|
||||
if ($this->dimension > 0) {
|
||||
if ($this->disableArraySupport) {
|
||||
return $value;
|
||||
}
|
||||
if (!is_array($value)) {
|
||||
$value = $this->getArrayParser()->parse($value);
|
||||
}
|
||||
if (is_array($value)) {
|
||||
array_walk_recursive($value, function (&$val, $key) {
|
||||
$val = $this->phpTypecastValue($val);
|
||||
});
|
||||
} elseif ($value === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->deserializeArrayColumnToArrayExpression
|
||||
? new ArrayExpression($value, $this->dbType, $this->dimension)
|
||||
: $value;
|
||||
}
|
||||
|
||||
return $this->phpTypecastValue($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Casts $value after retrieving from the DBMS to PHP representation.
|
||||
*
|
||||
* @param string|null $value
|
||||
* @return bool|mixed|null
|
||||
*/
|
||||
protected function phpTypecastValue($value)
|
||||
{
|
||||
if ($value === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch ($this->type) {
|
||||
case Schema::TYPE_BOOLEAN:
|
||||
switch (strtolower($value)) {
|
||||
case 't':
|
||||
case 'true':
|
||||
return true;
|
||||
case 'f':
|
||||
case 'false':
|
||||
return false;
|
||||
}
|
||||
return (bool) $value;
|
||||
case Schema::TYPE_JSON:
|
||||
return $this->disableJsonSupport ? $value : json_decode($value, true);
|
||||
}
|
||||
|
||||
return parent::phpTypecast($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates instance of ArrayParser
|
||||
*
|
||||
* @return ArrayParser
|
||||
*/
|
||||
protected function getArrayParser()
|
||||
{
|
||||
static $parser = null;
|
||||
|
||||
if ($parser === null) {
|
||||
$parser = new ArrayParser();
|
||||
}
|
||||
|
||||
return $parser;
|
||||
}
|
||||
}
|
||||
62
vendor/yiisoft/yii2/db/pgsql/JsonExpressionBuilder.php
vendored
Normal file
62
vendor/yiisoft/yii2/db/pgsql/JsonExpressionBuilder.php
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\pgsql;
|
||||
|
||||
use yii\db\ArrayExpression;
|
||||
use yii\db\ExpressionBuilderInterface;
|
||||
use yii\db\ExpressionBuilderTrait;
|
||||
use yii\db\ExpressionInterface;
|
||||
use yii\db\JsonExpression;
|
||||
use yii\db\Query;
|
||||
use yii\helpers\Json;
|
||||
|
||||
/**
|
||||
* Class JsonExpressionBuilder builds [[JsonExpression]] for PostgreSQL DBMS.
|
||||
*
|
||||
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
|
||||
* @since 2.0.14
|
||||
*/
|
||||
class JsonExpressionBuilder implements ExpressionBuilderInterface
|
||||
{
|
||||
use ExpressionBuilderTrait;
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @param JsonExpression|ExpressionInterface $expression the expression to be built
|
||||
*/
|
||||
public function build(ExpressionInterface $expression, array &$params = [])
|
||||
{
|
||||
$value = $expression->getValue();
|
||||
|
||||
if ($value instanceof Query) {
|
||||
list ($sql, $params) = $this->queryBuilder->build($value, $params);
|
||||
return "($sql)" . $this->getTypecast($expression);
|
||||
}
|
||||
if ($value instanceof ArrayExpression) {
|
||||
$placeholder = 'array_to_json(' . $this->queryBuilder->buildExpression($value, $params) . ')';
|
||||
} else {
|
||||
$placeholder = $this->queryBuilder->bindParam(Json::encode($value), $params);
|
||||
}
|
||||
|
||||
return $placeholder . $this->getTypecast($expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param JsonExpression $expression
|
||||
* @return string the typecast expression based on [[type]].
|
||||
*/
|
||||
protected function getTypecast(JsonExpression $expression)
|
||||
{
|
||||
if ($expression->getType() === null) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return '::' . $expression->getType();
|
||||
}
|
||||
}
|
||||
480
vendor/yiisoft/yii2/db/pgsql/QueryBuilder.php
vendored
Normal file
480
vendor/yiisoft/yii2/db/pgsql/QueryBuilder.php
vendored
Normal file
@@ -0,0 +1,480 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\pgsql;
|
||||
|
||||
use yii\base\InvalidArgumentException;
|
||||
use yii\db\Constraint;
|
||||
use yii\db\Expression;
|
||||
use yii\db\ExpressionInterface;
|
||||
use yii\db\Query;
|
||||
use yii\db\PdoValue;
|
||||
use yii\helpers\StringHelper;
|
||||
|
||||
/**
|
||||
* QueryBuilder is the query builder for PostgreSQL databases.
|
||||
*
|
||||
* @author Gevik Babakhani <gevikb@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class QueryBuilder extends \yii\db\QueryBuilder
|
||||
{
|
||||
/**
|
||||
* Defines a UNIQUE index for [[createIndex()]].
|
||||
* @since 2.0.6
|
||||
*/
|
||||
const INDEX_UNIQUE = 'unique';
|
||||
/**
|
||||
* Defines a B-tree index for [[createIndex()]].
|
||||
* @since 2.0.6
|
||||
*/
|
||||
const INDEX_B_TREE = 'btree';
|
||||
/**
|
||||
* Defines a hash index for [[createIndex()]].
|
||||
* @since 2.0.6
|
||||
*/
|
||||
const INDEX_HASH = 'hash';
|
||||
/**
|
||||
* Defines a GiST index for [[createIndex()]].
|
||||
* @since 2.0.6
|
||||
*/
|
||||
const INDEX_GIST = 'gist';
|
||||
/**
|
||||
* Defines a GIN index for [[createIndex()]].
|
||||
* @since 2.0.6
|
||||
*/
|
||||
const INDEX_GIN = 'gin';
|
||||
|
||||
/**
|
||||
* @var array mapping from abstract column types (keys) to physical column types (values).
|
||||
*/
|
||||
public $typeMap = [
|
||||
Schema::TYPE_PK => 'serial NOT NULL PRIMARY KEY',
|
||||
Schema::TYPE_UPK => 'serial NOT NULL PRIMARY KEY',
|
||||
Schema::TYPE_BIGPK => 'bigserial NOT NULL PRIMARY KEY',
|
||||
Schema::TYPE_UBIGPK => 'bigserial NOT NULL PRIMARY KEY',
|
||||
Schema::TYPE_CHAR => 'char(1)',
|
||||
Schema::TYPE_STRING => 'varchar(255)',
|
||||
Schema::TYPE_TEXT => 'text',
|
||||
Schema::TYPE_TINYINT => 'smallint',
|
||||
Schema::TYPE_SMALLINT => 'smallint',
|
||||
Schema::TYPE_INTEGER => 'integer',
|
||||
Schema::TYPE_BIGINT => 'bigint',
|
||||
Schema::TYPE_FLOAT => 'double precision',
|
||||
Schema::TYPE_DOUBLE => 'double precision',
|
||||
Schema::TYPE_DECIMAL => 'numeric(10,0)',
|
||||
Schema::TYPE_DATETIME => 'timestamp(0)',
|
||||
Schema::TYPE_TIMESTAMP => 'timestamp(0)',
|
||||
Schema::TYPE_TIME => 'time(0)',
|
||||
Schema::TYPE_DATE => 'date',
|
||||
Schema::TYPE_BINARY => 'bytea',
|
||||
Schema::TYPE_BOOLEAN => 'boolean',
|
||||
Schema::TYPE_MONEY => 'numeric(19,4)',
|
||||
Schema::TYPE_JSON => 'jsonb',
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function defaultConditionClasses()
|
||||
{
|
||||
return array_merge(parent::defaultConditionClasses(), [
|
||||
'ILIKE' => 'yii\db\conditions\LikeCondition',
|
||||
'NOT ILIKE' => 'yii\db\conditions\LikeCondition',
|
||||
'OR ILIKE' => 'yii\db\conditions\LikeCondition',
|
||||
'OR NOT ILIKE' => 'yii\db\conditions\LikeCondition',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function defaultExpressionBuilders()
|
||||
{
|
||||
return array_merge(parent::defaultExpressionBuilders(), [
|
||||
'yii\db\ArrayExpression' => 'yii\db\pgsql\ArrayExpressionBuilder',
|
||||
'yii\db\JsonExpression' => 'yii\db\pgsql\JsonExpressionBuilder',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a SQL statement for creating a new index.
|
||||
* @param string $name the name of the index. The name will be properly quoted by the method.
|
||||
* @param string $table the table that the new index will be created for. The table name will be properly quoted by the method.
|
||||
* @param string|array $columns the column(s) that should be included in the index. If there are multiple columns,
|
||||
* separate them with commas or use an array to represent them. Each column name will be properly quoted
|
||||
* by the method, unless a parenthesis is found in the name.
|
||||
* @param bool|string $unique whether to make this a UNIQUE index constraint. You can pass `true` or [[INDEX_UNIQUE]] to create
|
||||
* a unique index, `false` to make a non-unique index using the default index type, or one of the following constants to specify
|
||||
* the index method to use: [[INDEX_B_TREE]], [[INDEX_HASH]], [[INDEX_GIST]], [[INDEX_GIN]].
|
||||
* @return string the SQL statement for creating a new index.
|
||||
* @see http://www.postgresql.org/docs/8.2/static/sql-createindex.html
|
||||
*/
|
||||
public function createIndex($name, $table, $columns, $unique = false)
|
||||
{
|
||||
if ($unique === self::INDEX_UNIQUE || $unique === true) {
|
||||
$index = false;
|
||||
$unique = true;
|
||||
} else {
|
||||
$index = $unique;
|
||||
$unique = false;
|
||||
}
|
||||
|
||||
return ($unique ? 'CREATE UNIQUE INDEX ' : 'CREATE INDEX ') .
|
||||
$this->db->quoteTableName($name) . ' ON ' .
|
||||
$this->db->quoteTableName($table) .
|
||||
($index !== false ? " USING $index" : '') .
|
||||
' (' . $this->buildColumns($columns) . ')';
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a SQL statement for dropping an index.
|
||||
* @param string $name the name of the index to be dropped. The name will be properly quoted by the method.
|
||||
* @param string $table the table whose index is to be dropped. The name will be properly quoted by the method.
|
||||
* @return string the SQL statement for dropping an index.
|
||||
*/
|
||||
public function dropIndex($name, $table)
|
||||
{
|
||||
return 'DROP INDEX ' . $this->db->quoteTableName($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a SQL statement for renaming a DB table.
|
||||
* @param string $oldName the table to be renamed. The name will be properly quoted by the method.
|
||||
* @param string $newName the new table name. The name will be properly quoted by the method.
|
||||
* @return string the SQL statement for renaming a DB table.
|
||||
*/
|
||||
public function renameTable($oldName, $newName)
|
||||
{
|
||||
return 'ALTER TABLE ' . $this->db->quoteTableName($oldName) . ' RENAME TO ' . $this->db->quoteTableName($newName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a SQL statement for resetting the sequence value of a table's primary key.
|
||||
* The sequence will be reset such that the primary key of the next new row inserted
|
||||
* will have the specified value or 1.
|
||||
* @param string $tableName the name of the table whose primary key sequence will be reset
|
||||
* @param mixed $value the value for the primary key of the next new row inserted. If this is not set,
|
||||
* the next new row's primary key will have a value 1.
|
||||
* @return string the SQL statement for resetting sequence
|
||||
* @throws InvalidArgumentException if the table does not exist or there is no sequence associated with the table.
|
||||
*/
|
||||
public function resetSequence($tableName, $value = null)
|
||||
{
|
||||
$table = $this->db->getTableSchema($tableName);
|
||||
if ($table !== null && $table->sequenceName !== null) {
|
||||
// c.f. http://www.postgresql.org/docs/8.1/static/functions-sequence.html
|
||||
$sequence = $this->db->quoteTableName($table->sequenceName);
|
||||
$tableName = $this->db->quoteTableName($tableName);
|
||||
if ($value === null) {
|
||||
$key = $this->db->quoteColumnName(reset($table->primaryKey));
|
||||
$value = "(SELECT COALESCE(MAX({$key}),0) FROM {$tableName})+1";
|
||||
} else {
|
||||
$value = (int) $value;
|
||||
}
|
||||
|
||||
return "SELECT SETVAL('$sequence',$value,false)";
|
||||
} elseif ($table === null) {
|
||||
throw new InvalidArgumentException("Table not found: $tableName");
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException("There is not sequence associated with table '$tableName'.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a SQL statement for enabling or disabling integrity check.
|
||||
* @param bool $check whether to turn on or off the integrity check.
|
||||
* @param string $schema the schema of the tables.
|
||||
* @param string $table the table name.
|
||||
* @return string the SQL statement for checking integrity
|
||||
*/
|
||||
public function checkIntegrity($check = true, $schema = '', $table = '')
|
||||
{
|
||||
$enable = $check ? 'ENABLE' : 'DISABLE';
|
||||
$schema = $schema ?: $this->db->getSchema()->defaultSchema;
|
||||
$tableNames = $table ? [$table] : $this->db->getSchema()->getTableNames($schema);
|
||||
$viewNames = $this->db->getSchema()->getViewNames($schema);
|
||||
$tableNames = array_diff($tableNames, $viewNames);
|
||||
$command = '';
|
||||
|
||||
foreach ($tableNames as $tableName) {
|
||||
$tableName = $this->db->quoteTableName("{$schema}.{$tableName}");
|
||||
$command .= "ALTER TABLE $tableName $enable TRIGGER ALL; ";
|
||||
}
|
||||
|
||||
// enable to have ability to alter several tables
|
||||
$this->db->getMasterPdo()->setAttribute(\PDO::ATTR_EMULATE_PREPARES, true);
|
||||
|
||||
return $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a SQL statement for truncating a DB table.
|
||||
* Explicitly restarts identity for PGSQL to be consistent with other databases which all do this by default.
|
||||
* @param string $table the table to be truncated. The name will be properly quoted by the method.
|
||||
* @return string the SQL statement for truncating a DB table.
|
||||
*/
|
||||
public function truncateTable($table)
|
||||
{
|
||||
return 'TRUNCATE TABLE ' . $this->db->quoteTableName($table) . ' RESTART IDENTITY';
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a SQL statement for changing the definition of a column.
|
||||
* @param string $table the table whose column is to be changed. The table name will be properly quoted by the method.
|
||||
* @param string $column the name of the column to be changed. The name will be properly quoted by the method.
|
||||
* @param string $type the new column type. The [[getColumnType()]] method will be invoked to convert abstract
|
||||
* column type (if any) into the physical one. Anything that is not recognized as abstract type will be kept
|
||||
* in the generated SQL. For example, 'string' will be turned into 'varchar(255)', while 'string not null'
|
||||
* will become 'varchar(255) not null'. You can also use PostgreSQL-specific syntax such as `SET NOT NULL`.
|
||||
* @return string the SQL statement for changing the definition of a column.
|
||||
*/
|
||||
public function alterColumn($table, $column, $type)
|
||||
{
|
||||
// https://github.com/yiisoft/yii2/issues/4492
|
||||
// http://www.postgresql.org/docs/9.1/static/sql-altertable.html
|
||||
if (!preg_match('/^(DROP|SET|RESET)\s+/i', $type)) {
|
||||
$type = 'TYPE ' . $this->getColumnType($type);
|
||||
}
|
||||
|
||||
return 'ALTER TABLE ' . $this->db->quoteTableName($table) . ' ALTER COLUMN '
|
||||
. $this->db->quoteColumnName($column) . ' ' . $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function insert($table, $columns, &$params)
|
||||
{
|
||||
return parent::insert($table, $this->normalizeTableRowData($table, $columns), $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @see https://www.postgresql.org/docs/9.5/static/sql-insert.html#SQL-ON-CONFLICT
|
||||
* @see https://stackoverflow.com/questions/1109061/insert-on-duplicate-update-in-postgresql/8702291#8702291
|
||||
*/
|
||||
public function upsert($table, $insertColumns, $updateColumns, &$params)
|
||||
{
|
||||
$insertColumns = $this->normalizeTableRowData($table, $insertColumns);
|
||||
if (!is_bool($updateColumns)) {
|
||||
$updateColumns = $this->normalizeTableRowData($table, $updateColumns);
|
||||
}
|
||||
if (version_compare($this->db->getServerVersion(), '9.5', '<')) {
|
||||
return $this->oldUpsert($table, $insertColumns, $updateColumns, $params);
|
||||
}
|
||||
|
||||
return $this->newUpsert($table, $insertColumns, $updateColumns, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* [[upsert()]] implementation for PostgreSQL 9.5 or higher.
|
||||
* @param string $table
|
||||
* @param array|Query $insertColumns
|
||||
* @param array|bool $updateColumns
|
||||
* @param array $params
|
||||
* @return string
|
||||
*/
|
||||
private function newUpsert($table, $insertColumns, $updateColumns, &$params)
|
||||
{
|
||||
$insertSql = $this->insert($table, $insertColumns, $params);
|
||||
list($uniqueNames, , $updateNames) = $this->prepareUpsertColumns($table, $insertColumns, $updateColumns);
|
||||
if (empty($uniqueNames)) {
|
||||
return $insertSql;
|
||||
}
|
||||
|
||||
if ($updateColumns === false) {
|
||||
return "$insertSql ON CONFLICT DO NOTHING";
|
||||
}
|
||||
|
||||
if ($updateColumns === true) {
|
||||
$updateColumns = [];
|
||||
foreach ($updateNames as $name) {
|
||||
$updateColumns[$name] = new Expression('EXCLUDED.' . $this->db->quoteColumnName($name));
|
||||
}
|
||||
}
|
||||
list($updates, $params) = $this->prepareUpdateSets($table, $updateColumns, $params);
|
||||
return $insertSql . ' ON CONFLICT (' . implode(', ', $uniqueNames) . ') DO UPDATE SET ' . implode(', ', $updates);
|
||||
}
|
||||
|
||||
/**
|
||||
* [[upsert()]] implementation for PostgreSQL older than 9.5.
|
||||
* @param string $table
|
||||
* @param array|Query $insertColumns
|
||||
* @param array|bool $updateColumns
|
||||
* @param array $params
|
||||
* @return string
|
||||
*/
|
||||
private function oldUpsert($table, $insertColumns, $updateColumns, &$params)
|
||||
{
|
||||
/** @var Constraint[] $constraints */
|
||||
list($uniqueNames, $insertNames, $updateNames) = $this->prepareUpsertColumns($table, $insertColumns, $updateColumns, $constraints);
|
||||
if (empty($uniqueNames)) {
|
||||
return $this->insert($table, $insertColumns, $params);
|
||||
}
|
||||
|
||||
/** @var Schema $schema */
|
||||
$schema = $this->db->getSchema();
|
||||
if (!$insertColumns instanceof Query) {
|
||||
$tableSchema = $schema->getTableSchema($table);
|
||||
$columnSchemas = $tableSchema !== null ? $tableSchema->columns : [];
|
||||
foreach ($insertColumns as $name => $value) {
|
||||
// NULLs and numeric values must be type hinted in order to be used in SET assigments
|
||||
// NVM, let's cast them all
|
||||
if (isset($columnSchemas[$name])) {
|
||||
$phName = self::PARAM_PREFIX . count($params);
|
||||
$params[$phName] = $value;
|
||||
$insertColumns[$name] = new Expression("CAST($phName AS {$columnSchemas[$name]->dbType})");
|
||||
}
|
||||
}
|
||||
}
|
||||
list(, $placeholders, $values, $params) = $this->prepareInsertValues($table, $insertColumns, $params);
|
||||
$updateCondition = ['or'];
|
||||
$insertCondition = ['or'];
|
||||
$quotedTableName = $schema->quoteTableName($table);
|
||||
foreach ($constraints as $constraint) {
|
||||
$constraintUpdateCondition = ['and'];
|
||||
$constraintInsertCondition = ['and'];
|
||||
foreach ($constraint->columnNames as $name) {
|
||||
$quotedName = $schema->quoteColumnName($name);
|
||||
$constraintUpdateCondition[] = "$quotedTableName.$quotedName=\"EXCLUDED\".$quotedName";
|
||||
$constraintInsertCondition[] = "\"upsert\".$quotedName=\"EXCLUDED\".$quotedName";
|
||||
}
|
||||
$updateCondition[] = $constraintUpdateCondition;
|
||||
$insertCondition[] = $constraintInsertCondition;
|
||||
}
|
||||
$withSql = 'WITH "EXCLUDED" (' . implode(', ', $insertNames)
|
||||
. ') AS (' . (!empty($placeholders) ? 'VALUES (' . implode(', ', $placeholders) . ')' : ltrim($values, ' ')) . ')';
|
||||
if ($updateColumns === false) {
|
||||
$selectSubQuery = (new Query())
|
||||
->select(new Expression('1'))
|
||||
->from($table)
|
||||
->where($updateCondition);
|
||||
$insertSelectSubQuery = (new Query())
|
||||
->select($insertNames)
|
||||
->from('EXCLUDED')
|
||||
->where(['not exists', $selectSubQuery]);
|
||||
$insertSql = $this->insert($table, $insertSelectSubQuery, $params);
|
||||
return "$withSql $insertSql";
|
||||
}
|
||||
|
||||
if ($updateColumns === true) {
|
||||
$updateColumns = [];
|
||||
foreach ($updateNames as $name) {
|
||||
$quotedName = $this->db->quoteColumnName($name);
|
||||
if (strrpos($quotedName, '.') === false) {
|
||||
$quotedName = '"EXCLUDED".' . $quotedName;
|
||||
}
|
||||
$updateColumns[$name] = new Expression($quotedName);
|
||||
}
|
||||
}
|
||||
list($updates, $params) = $this->prepareUpdateSets($table, $updateColumns, $params);
|
||||
$updateSql = 'UPDATE ' . $this->db->quoteTableName($table) . ' SET ' . implode(', ', $updates)
|
||||
. ' FROM "EXCLUDED" ' . $this->buildWhere($updateCondition, $params)
|
||||
. ' RETURNING ' . $this->db->quoteTableName($table) .'.*';
|
||||
$selectUpsertSubQuery = (new Query())
|
||||
->select(new Expression('1'))
|
||||
->from('upsert')
|
||||
->where($insertCondition);
|
||||
$insertSelectSubQuery = (new Query())
|
||||
->select($insertNames)
|
||||
->from('EXCLUDED')
|
||||
->where(['not exists', $selectUpsertSubQuery]);
|
||||
$insertSql = $this->insert($table, $insertSelectSubQuery, $params);
|
||||
return "$withSql, \"upsert\" AS ($updateSql) $insertSql";
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function update($table, $columns, $condition, &$params)
|
||||
{
|
||||
return parent::update($table, $this->normalizeTableRowData($table, $columns), $condition, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes data to be saved into the table, performing extra preparations and type converting, if necessary.
|
||||
*
|
||||
* @param string $table the table that data will be saved into.
|
||||
* @param array|Query $columns the column data (name => value) to be saved into the table or instance
|
||||
* of [[yii\db\Query|Query]] to perform INSERT INTO ... SELECT SQL statement.
|
||||
* Passing of [[yii\db\Query|Query]] is available since version 2.0.11.
|
||||
* @return array normalized columns
|
||||
* @since 2.0.9
|
||||
*/
|
||||
private function normalizeTableRowData($table, $columns)
|
||||
{
|
||||
if ($columns instanceof Query) {
|
||||
return $columns;
|
||||
}
|
||||
|
||||
if (($tableSchema = $this->db->getSchema()->getTableSchema($table)) !== null) {
|
||||
$columnSchemas = $tableSchema->columns;
|
||||
foreach ($columns as $name => $value) {
|
||||
if (isset($columnSchemas[$name]) && $columnSchemas[$name]->type === Schema::TYPE_BINARY && is_string($value)) {
|
||||
$columns[$name] = new PdoValue($value, \PDO::PARAM_LOB); // explicitly setup PDO param type for binary column
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function batchInsert($table, $columns, $rows, &$params = [])
|
||||
{
|
||||
if (empty($rows)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$schema = $this->db->getSchema();
|
||||
if (($tableSchema = $schema->getTableSchema($table)) !== null) {
|
||||
$columnSchemas = $tableSchema->columns;
|
||||
} else {
|
||||
$columnSchemas = [];
|
||||
}
|
||||
|
||||
$values = [];
|
||||
foreach ($rows as $row) {
|
||||
$vs = [];
|
||||
foreach ($row as $i => $value) {
|
||||
if (isset($columns[$i], $columnSchemas[$columns[$i]])) {
|
||||
$value = $columnSchemas[$columns[$i]]->dbTypecast($value);
|
||||
}
|
||||
if (is_string($value)) {
|
||||
$value = $schema->quoteValue($value);
|
||||
} elseif (is_float($value)) {
|
||||
// ensure type cast always has . as decimal separator in all locales
|
||||
$value = StringHelper::floatToString($value);
|
||||
} elseif ($value === true) {
|
||||
$value = 'TRUE';
|
||||
} elseif ($value === false) {
|
||||
$value = 'FALSE';
|
||||
} elseif ($value === null) {
|
||||
$value = 'NULL';
|
||||
} elseif ($value instanceof ExpressionInterface) {
|
||||
$value = $this->buildExpression($value, $params);
|
||||
}
|
||||
$vs[] = $value;
|
||||
}
|
||||
$values[] = '(' . implode(', ', $vs) . ')';
|
||||
}
|
||||
if (empty($values)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
foreach ($columns as $i => $name) {
|
||||
$columns[$i] = $schema->quoteColumnName($name);
|
||||
}
|
||||
|
||||
return 'INSERT INTO ' . $schema->quoteTableName($table)
|
||||
. ' (' . implode(', ', $columns) . ') VALUES ' . implode(', ', $values);
|
||||
}
|
||||
}
|
||||
724
vendor/yiisoft/yii2/db/pgsql/Schema.php
vendored
Normal file
724
vendor/yiisoft/yii2/db/pgsql/Schema.php
vendored
Normal file
@@ -0,0 +1,724 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\pgsql;
|
||||
|
||||
use yii\base\NotSupportedException;
|
||||
use yii\db\CheckConstraint;
|
||||
use yii\db\Constraint;
|
||||
use yii\db\ConstraintFinderInterface;
|
||||
use yii\db\ConstraintFinderTrait;
|
||||
use yii\db\Expression;
|
||||
use yii\db\ForeignKeyConstraint;
|
||||
use yii\db\IndexConstraint;
|
||||
use yii\db\TableSchema;
|
||||
use yii\db\ViewFinderTrait;
|
||||
use yii\helpers\ArrayHelper;
|
||||
|
||||
/**
|
||||
* Schema is the class for retrieving metadata from a PostgreSQL database
|
||||
* (version 9.x and above).
|
||||
*
|
||||
* @author Gevik Babakhani <gevikb@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class Schema extends \yii\db\Schema implements ConstraintFinderInterface
|
||||
{
|
||||
use ViewFinderTrait;
|
||||
use ConstraintFinderTrait;
|
||||
|
||||
const TYPE_JSONB = 'jsonb';
|
||||
|
||||
/**
|
||||
* @var string the default schema used for the current session.
|
||||
*/
|
||||
public $defaultSchema = 'public';
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $columnSchemaClass = 'yii\db\pgsql\ColumnSchema';
|
||||
/**
|
||||
* @var array mapping from physical column types (keys) to abstract
|
||||
* column types (values)
|
||||
* @see http://www.postgresql.org/docs/current/static/datatype.html#DATATYPE-TABLE
|
||||
*/
|
||||
public $typeMap = [
|
||||
'bit' => self::TYPE_INTEGER,
|
||||
'bit varying' => self::TYPE_INTEGER,
|
||||
'varbit' => self::TYPE_INTEGER,
|
||||
|
||||
'bool' => self::TYPE_BOOLEAN,
|
||||
'boolean' => self::TYPE_BOOLEAN,
|
||||
|
||||
'box' => self::TYPE_STRING,
|
||||
'circle' => self::TYPE_STRING,
|
||||
'point' => self::TYPE_STRING,
|
||||
'line' => self::TYPE_STRING,
|
||||
'lseg' => self::TYPE_STRING,
|
||||
'polygon' => self::TYPE_STRING,
|
||||
'path' => self::TYPE_STRING,
|
||||
|
||||
'character' => self::TYPE_CHAR,
|
||||
'char' => self::TYPE_CHAR,
|
||||
'bpchar' => self::TYPE_CHAR,
|
||||
'character varying' => self::TYPE_STRING,
|
||||
'varchar' => self::TYPE_STRING,
|
||||
'text' => self::TYPE_TEXT,
|
||||
|
||||
'bytea' => self::TYPE_BINARY,
|
||||
|
||||
'cidr' => self::TYPE_STRING,
|
||||
'inet' => self::TYPE_STRING,
|
||||
'macaddr' => self::TYPE_STRING,
|
||||
|
||||
'real' => self::TYPE_FLOAT,
|
||||
'float4' => self::TYPE_FLOAT,
|
||||
'double precision' => self::TYPE_DOUBLE,
|
||||
'float8' => self::TYPE_DOUBLE,
|
||||
'decimal' => self::TYPE_DECIMAL,
|
||||
'numeric' => self::TYPE_DECIMAL,
|
||||
|
||||
'money' => self::TYPE_MONEY,
|
||||
|
||||
'smallint' => self::TYPE_SMALLINT,
|
||||
'int2' => self::TYPE_SMALLINT,
|
||||
'int4' => self::TYPE_INTEGER,
|
||||
'int' => self::TYPE_INTEGER,
|
||||
'integer' => self::TYPE_INTEGER,
|
||||
'bigint' => self::TYPE_BIGINT,
|
||||
'int8' => self::TYPE_BIGINT,
|
||||
'oid' => self::TYPE_BIGINT, // should not be used. it's pg internal!
|
||||
|
||||
'smallserial' => self::TYPE_SMALLINT,
|
||||
'serial2' => self::TYPE_SMALLINT,
|
||||
'serial4' => self::TYPE_INTEGER,
|
||||
'serial' => self::TYPE_INTEGER,
|
||||
'bigserial' => self::TYPE_BIGINT,
|
||||
'serial8' => self::TYPE_BIGINT,
|
||||
'pg_lsn' => self::TYPE_BIGINT,
|
||||
|
||||
'date' => self::TYPE_DATE,
|
||||
'interval' => self::TYPE_STRING,
|
||||
'time without time zone' => self::TYPE_TIME,
|
||||
'time' => self::TYPE_TIME,
|
||||
'time with time zone' => self::TYPE_TIME,
|
||||
'timetz' => self::TYPE_TIME,
|
||||
'timestamp without time zone' => self::TYPE_TIMESTAMP,
|
||||
'timestamp' => self::TYPE_TIMESTAMP,
|
||||
'timestamp with time zone' => self::TYPE_TIMESTAMP,
|
||||
'timestamptz' => self::TYPE_TIMESTAMP,
|
||||
'abstime' => self::TYPE_TIMESTAMP,
|
||||
|
||||
'tsquery' => self::TYPE_STRING,
|
||||
'tsvector' => self::TYPE_STRING,
|
||||
'txid_snapshot' => self::TYPE_STRING,
|
||||
|
||||
'unknown' => self::TYPE_STRING,
|
||||
|
||||
'uuid' => self::TYPE_STRING,
|
||||
'json' => self::TYPE_JSON,
|
||||
'jsonb' => self::TYPE_JSON,
|
||||
'xml' => self::TYPE_STRING,
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $tableQuoteCharacter = '"';
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function resolveTableName($name)
|
||||
{
|
||||
$resolvedName = new TableSchema();
|
||||
$parts = explode('.', str_replace('"', '', $name));
|
||||
if (isset($parts[1])) {
|
||||
$resolvedName->schemaName = $parts[0];
|
||||
$resolvedName->name = $parts[1];
|
||||
} else {
|
||||
$resolvedName->schemaName = $this->defaultSchema;
|
||||
$resolvedName->name = $name;
|
||||
}
|
||||
$resolvedName->fullName = ($resolvedName->schemaName !== $this->defaultSchema ? $resolvedName->schemaName . '.' : '') . $resolvedName->name;
|
||||
return $resolvedName;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function findSchemaNames()
|
||||
{
|
||||
static $sql = <<<'SQL'
|
||||
SELECT "ns"."nspname"
|
||||
FROM "pg_namespace" AS "ns"
|
||||
WHERE "ns"."nspname" != 'information_schema' AND "ns"."nspname" NOT LIKE 'pg_%'
|
||||
ORDER BY "ns"."nspname" ASC
|
||||
SQL;
|
||||
|
||||
return $this->db->createCommand($sql)->queryColumn();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function findTableNames($schema = '')
|
||||
{
|
||||
if ($schema === '') {
|
||||
$schema = $this->defaultSchema;
|
||||
}
|
||||
$sql = <<<'SQL'
|
||||
SELECT c.relname AS table_name
|
||||
FROM pg_class c
|
||||
INNER JOIN pg_namespace ns ON ns.oid = c.relnamespace
|
||||
WHERE ns.nspname = :schemaName AND c.relkind IN ('r','v','m','f')
|
||||
ORDER BY c.relname
|
||||
SQL;
|
||||
return $this->db->createCommand($sql, [':schemaName' => $schema])->queryColumn();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function loadTableSchema($name)
|
||||
{
|
||||
$table = new TableSchema();
|
||||
$this->resolveTableNames($table, $name);
|
||||
if ($this->findColumns($table)) {
|
||||
$this->findConstraints($table);
|
||||
return $table;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function loadTablePrimaryKey($tableName)
|
||||
{
|
||||
return $this->loadTableConstraints($tableName, 'primaryKey');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function loadTableForeignKeys($tableName)
|
||||
{
|
||||
return $this->loadTableConstraints($tableName, 'foreignKeys');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function loadTableIndexes($tableName)
|
||||
{
|
||||
static $sql = <<<'SQL'
|
||||
SELECT
|
||||
"ic"."relname" AS "name",
|
||||
"ia"."attname" AS "column_name",
|
||||
"i"."indisunique" AS "index_is_unique",
|
||||
"i"."indisprimary" AS "index_is_primary"
|
||||
FROM "pg_class" AS "tc"
|
||||
INNER JOIN "pg_namespace" AS "tcns"
|
||||
ON "tcns"."oid" = "tc"."relnamespace"
|
||||
INNER JOIN "pg_index" AS "i"
|
||||
ON "i"."indrelid" = "tc"."oid"
|
||||
INNER JOIN "pg_class" AS "ic"
|
||||
ON "ic"."oid" = "i"."indexrelid"
|
||||
INNER JOIN "pg_attribute" AS "ia"
|
||||
ON "ia"."attrelid" = "i"."indrelid" AND "ia"."attnum" = ANY ("i"."indkey")
|
||||
WHERE "tcns"."nspname" = :schemaName AND "tc"."relname" = :tableName
|
||||
ORDER BY "ia"."attnum" ASC
|
||||
SQL;
|
||||
|
||||
$resolvedName = $this->resolveTableName($tableName);
|
||||
$indexes = $this->db->createCommand($sql, [
|
||||
':schemaName' => $resolvedName->schemaName,
|
||||
':tableName' => $resolvedName->name,
|
||||
])->queryAll();
|
||||
$indexes = $this->normalizePdoRowKeyCase($indexes, true);
|
||||
$indexes = ArrayHelper::index($indexes, null, 'name');
|
||||
$result = [];
|
||||
foreach ($indexes as $name => $index) {
|
||||
$result[] = new IndexConstraint([
|
||||
'isPrimary' => (bool) $index[0]['index_is_primary'],
|
||||
'isUnique' => (bool) $index[0]['index_is_unique'],
|
||||
'name' => $name,
|
||||
'columnNames' => ArrayHelper::getColumn($index, 'column_name'),
|
||||
]);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function loadTableUniques($tableName)
|
||||
{
|
||||
return $this->loadTableConstraints($tableName, 'uniques');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function loadTableChecks($tableName)
|
||||
{
|
||||
return $this->loadTableConstraints($tableName, 'checks');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @throws NotSupportedException if this method is called.
|
||||
*/
|
||||
protected function loadTableDefaultValues($tableName)
|
||||
{
|
||||
throw new NotSupportedException('PostgreSQL does not support default value constraints.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a query builder for the PostgreSQL database.
|
||||
* @return QueryBuilder query builder instance
|
||||
*/
|
||||
public function createQueryBuilder()
|
||||
{
|
||||
return new QueryBuilder($this->db);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the table name and schema name (if any).
|
||||
* @param TableSchema $table the table metadata object
|
||||
* @param string $name the table name
|
||||
*/
|
||||
protected function resolveTableNames($table, $name)
|
||||
{
|
||||
$parts = explode('.', str_replace('"', '', $name));
|
||||
|
||||
if (isset($parts[1])) {
|
||||
$table->schemaName = $parts[0];
|
||||
$table->name = $parts[1];
|
||||
} else {
|
||||
$table->schemaName = $this->defaultSchema;
|
||||
$table->name = $parts[0];
|
||||
}
|
||||
|
||||
$table->fullName = $table->schemaName !== $this->defaultSchema ? $table->schemaName . '.' . $table->name : $table->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc]
|
||||
*/
|
||||
protected function findViewNames($schema = '')
|
||||
{
|
||||
if ($schema === '') {
|
||||
$schema = $this->defaultSchema;
|
||||
}
|
||||
$sql = <<<'SQL'
|
||||
SELECT c.relname AS table_name
|
||||
FROM pg_class c
|
||||
INNER JOIN pg_namespace ns ON ns.oid = c.relnamespace
|
||||
WHERE ns.nspname = :schemaName AND (c.relkind = 'v' OR c.relkind = 'm')
|
||||
ORDER BY c.relname
|
||||
SQL;
|
||||
return $this->db->createCommand($sql, [':schemaName' => $schema])->queryColumn();
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects the foreign key column details for the given table.
|
||||
* @param TableSchema $table the table metadata
|
||||
*/
|
||||
protected function findConstraints($table)
|
||||
{
|
||||
$tableName = $this->quoteValue($table->name);
|
||||
$tableSchema = $this->quoteValue($table->schemaName);
|
||||
|
||||
//We need to extract the constraints de hard way since:
|
||||
//http://www.postgresql.org/message-id/26677.1086673982@sss.pgh.pa.us
|
||||
|
||||
$sql = <<<SQL
|
||||
select
|
||||
ct.conname as constraint_name,
|
||||
a.attname as column_name,
|
||||
fc.relname as foreign_table_name,
|
||||
fns.nspname as foreign_table_schema,
|
||||
fa.attname as foreign_column_name
|
||||
from
|
||||
(SELECT ct.conname, ct.conrelid, ct.confrelid, ct.conkey, ct.contype, ct.confkey, generate_subscripts(ct.conkey, 1) AS s
|
||||
FROM pg_constraint ct
|
||||
) AS ct
|
||||
inner join pg_class c on c.oid=ct.conrelid
|
||||
inner join pg_namespace ns on c.relnamespace=ns.oid
|
||||
inner join pg_attribute a on a.attrelid=ct.conrelid and a.attnum = ct.conkey[ct.s]
|
||||
left join pg_class fc on fc.oid=ct.confrelid
|
||||
left join pg_namespace fns on fc.relnamespace=fns.oid
|
||||
left join pg_attribute fa on fa.attrelid=ct.confrelid and fa.attnum = ct.confkey[ct.s]
|
||||
where
|
||||
ct.contype='f'
|
||||
and c.relname={$tableName}
|
||||
and ns.nspname={$tableSchema}
|
||||
order by
|
||||
fns.nspname, fc.relname, a.attnum
|
||||
SQL;
|
||||
|
||||
$constraints = [];
|
||||
foreach ($this->db->createCommand($sql)->queryAll() as $constraint) {
|
||||
if ($this->db->slavePdo->getAttribute(\PDO::ATTR_CASE) === \PDO::CASE_UPPER) {
|
||||
$constraint = array_change_key_case($constraint, CASE_LOWER);
|
||||
}
|
||||
if ($constraint['foreign_table_schema'] !== $this->defaultSchema) {
|
||||
$foreignTable = $constraint['foreign_table_schema'] . '.' . $constraint['foreign_table_name'];
|
||||
} else {
|
||||
$foreignTable = $constraint['foreign_table_name'];
|
||||
}
|
||||
$name = $constraint['constraint_name'];
|
||||
if (!isset($constraints[$name])) {
|
||||
$constraints[$name] = [
|
||||
'tableName' => $foreignTable,
|
||||
'columns' => [],
|
||||
];
|
||||
}
|
||||
$constraints[$name]['columns'][$constraint['column_name']] = $constraint['foreign_column_name'];
|
||||
}
|
||||
foreach ($constraints as $name => $constraint) {
|
||||
$table->foreignKeys[$name] = array_merge([$constraint['tableName']], $constraint['columns']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets information about given table unique indexes.
|
||||
* @param TableSchema $table the table metadata
|
||||
* @return array with index and column names
|
||||
*/
|
||||
protected function getUniqueIndexInformation($table)
|
||||
{
|
||||
$sql = <<<'SQL'
|
||||
SELECT
|
||||
i.relname as indexname,
|
||||
pg_get_indexdef(idx.indexrelid, k + 1, TRUE) AS columnname
|
||||
FROM (
|
||||
SELECT *, generate_subscripts(indkey, 1) AS k
|
||||
FROM pg_index
|
||||
) idx
|
||||
INNER JOIN pg_class i ON i.oid = idx.indexrelid
|
||||
INNER JOIN pg_class c ON c.oid = idx.indrelid
|
||||
INNER JOIN pg_namespace ns ON c.relnamespace = ns.oid
|
||||
WHERE idx.indisprimary = FALSE AND idx.indisunique = TRUE
|
||||
AND c.relname = :tableName AND ns.nspname = :schemaName
|
||||
ORDER BY i.relname, k
|
||||
SQL;
|
||||
|
||||
return $this->db->createCommand($sql, [
|
||||
':schemaName' => $table->schemaName,
|
||||
':tableName' => $table->name,
|
||||
])->queryAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all unique indexes for the given table.
|
||||
*
|
||||
* Each array element is of the following structure:
|
||||
*
|
||||
* ```php
|
||||
* [
|
||||
* 'IndexName1' => ['col1' [, ...]],
|
||||
* 'IndexName2' => ['col2' [, ...]],
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* @param TableSchema $table the table metadata
|
||||
* @return array all unique indexes for the given table.
|
||||
*/
|
||||
public function findUniqueIndexes($table)
|
||||
{
|
||||
$uniqueIndexes = [];
|
||||
|
||||
foreach ($this->getUniqueIndexInformation($table) as $row) {
|
||||
if ($this->db->slavePdo->getAttribute(\PDO::ATTR_CASE) === \PDO::CASE_UPPER) {
|
||||
$row = array_change_key_case($row, CASE_LOWER);
|
||||
}
|
||||
$column = $row['columnname'];
|
||||
if (!empty($column) && $column[0] === '"') {
|
||||
// postgres will quote names that are not lowercase-only
|
||||
// https://github.com/yiisoft/yii2/issues/10613
|
||||
$column = substr($column, 1, -1);
|
||||
}
|
||||
$uniqueIndexes[$row['indexname']][] = $column;
|
||||
}
|
||||
|
||||
return $uniqueIndexes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects the metadata of table columns.
|
||||
* @param TableSchema $table the table metadata
|
||||
* @return bool whether the table exists in the database
|
||||
*/
|
||||
protected function findColumns($table)
|
||||
{
|
||||
$tableName = $this->db->quoteValue($table->name);
|
||||
$schemaName = $this->db->quoteValue($table->schemaName);
|
||||
$sql = <<<SQL
|
||||
SELECT
|
||||
d.nspname AS table_schema,
|
||||
c.relname AS table_name,
|
||||
a.attname AS column_name,
|
||||
COALESCE(td.typname, tb.typname, t.typname) AS data_type,
|
||||
COALESCE(td.typtype, tb.typtype, t.typtype) AS type_type,
|
||||
a.attlen AS character_maximum_length,
|
||||
pg_catalog.col_description(c.oid, a.attnum) AS column_comment,
|
||||
a.atttypmod AS modifier,
|
||||
a.attnotnull = false AS is_nullable,
|
||||
CAST(pg_get_expr(ad.adbin, ad.adrelid) AS varchar) AS column_default,
|
||||
coalesce(pg_get_expr(ad.adbin, ad.adrelid) ~ 'nextval',false) AS is_autoinc,
|
||||
CASE WHEN COALESCE(td.typtype, tb.typtype, t.typtype) = 'e'::char
|
||||
THEN array_to_string((SELECT array_agg(enumlabel) FROM pg_enum WHERE enumtypid = COALESCE(td.oid, tb.oid, a.atttypid))::varchar[], ',')
|
||||
ELSE NULL
|
||||
END AS enum_values,
|
||||
CASE atttypid
|
||||
WHEN 21 /*int2*/ THEN 16
|
||||
WHEN 23 /*int4*/ THEN 32
|
||||
WHEN 20 /*int8*/ THEN 64
|
||||
WHEN 1700 /*numeric*/ THEN
|
||||
CASE WHEN atttypmod = -1
|
||||
THEN null
|
||||
ELSE ((atttypmod - 4) >> 16) & 65535
|
||||
END
|
||||
WHEN 700 /*float4*/ THEN 24 /*FLT_MANT_DIG*/
|
||||
WHEN 701 /*float8*/ THEN 53 /*DBL_MANT_DIG*/
|
||||
ELSE null
|
||||
END AS numeric_precision,
|
||||
CASE
|
||||
WHEN atttypid IN (21, 23, 20) THEN 0
|
||||
WHEN atttypid IN (1700) THEN
|
||||
CASE
|
||||
WHEN atttypmod = -1 THEN null
|
||||
ELSE (atttypmod - 4) & 65535
|
||||
END
|
||||
ELSE null
|
||||
END AS numeric_scale,
|
||||
CAST(
|
||||
information_schema._pg_char_max_length(information_schema._pg_truetypid(a, t), information_schema._pg_truetypmod(a, t))
|
||||
AS numeric
|
||||
) AS size,
|
||||
a.attnum = any (ct.conkey) as is_pkey,
|
||||
COALESCE(NULLIF(a.attndims, 0), NULLIF(t.typndims, 0), (t.typcategory='A')::int) AS dimension
|
||||
FROM
|
||||
pg_class c
|
||||
LEFT JOIN pg_attribute a ON a.attrelid = c.oid
|
||||
LEFT JOIN pg_attrdef ad ON a.attrelid = ad.adrelid AND a.attnum = ad.adnum
|
||||
LEFT JOIN pg_type t ON a.atttypid = t.oid
|
||||
LEFT JOIN pg_type tb ON (a.attndims > 0 OR t.typcategory='A') AND t.typelem > 0 AND t.typelem = tb.oid OR t.typbasetype > 0 AND t.typbasetype = tb.oid
|
||||
LEFT JOIN pg_type td ON t.typndims > 0 AND t.typbasetype > 0 AND tb.typelem = td.oid
|
||||
LEFT JOIN pg_namespace d ON d.oid = c.relnamespace
|
||||
LEFT JOIN pg_constraint ct ON ct.conrelid = c.oid AND ct.contype = 'p'
|
||||
WHERE
|
||||
a.attnum > 0 AND t.typname != ''
|
||||
AND c.relname = {$tableName}
|
||||
AND d.nspname = {$schemaName}
|
||||
ORDER BY
|
||||
a.attnum;
|
||||
SQL;
|
||||
$columns = $this->db->createCommand($sql)->queryAll();
|
||||
if (empty($columns)) {
|
||||
return false;
|
||||
}
|
||||
foreach ($columns as $column) {
|
||||
if ($this->db->slavePdo->getAttribute(\PDO::ATTR_CASE) === \PDO::CASE_UPPER) {
|
||||
$column = array_change_key_case($column, CASE_LOWER);
|
||||
}
|
||||
$column = $this->loadColumnSchema($column);
|
||||
$table->columns[$column->name] = $column;
|
||||
if ($column->isPrimaryKey) {
|
||||
$table->primaryKey[] = $column->name;
|
||||
if ($table->sequenceName === null && preg_match("/nextval\\('\"?\\w+\"?\.?\"?\\w+\"?'(::regclass)?\\)/", $column->defaultValue) === 1) {
|
||||
$table->sequenceName = preg_replace(['/nextval/', '/::/', '/regclass/', '/\'\)/', '/\(\'/'], '', $column->defaultValue);
|
||||
}
|
||||
$column->defaultValue = null;
|
||||
} elseif ($column->defaultValue) {
|
||||
if ($column->type === 'timestamp' && $column->defaultValue === 'now()') {
|
||||
$column->defaultValue = new Expression($column->defaultValue);
|
||||
} elseif ($column->type === 'boolean') {
|
||||
$column->defaultValue = ($column->defaultValue === 'true');
|
||||
} elseif (strncasecmp($column->dbType, 'bit', 3) === 0 || strncasecmp($column->dbType, 'varbit', 6) === 0) {
|
||||
$column->defaultValue = bindec(trim($column->defaultValue, 'B\''));
|
||||
} elseif (preg_match("/^'(.*?)'::/", $column->defaultValue, $matches)) {
|
||||
$column->defaultValue = $column->phpTypecast($matches[1]);
|
||||
} elseif (preg_match('/^(\()?(.*?)(?(1)\))(?:::.+)?$/', $column->defaultValue, $matches)) {
|
||||
if ($matches[2] === 'NULL') {
|
||||
$column->defaultValue = null;
|
||||
} else {
|
||||
$column->defaultValue = $column->phpTypecast($matches[2]);
|
||||
}
|
||||
} else {
|
||||
$column->defaultValue = $column->phpTypecast($column->defaultValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the column information into a [[ColumnSchema]] object.
|
||||
* @param array $info column information
|
||||
* @return ColumnSchema the column schema object
|
||||
*/
|
||||
protected function loadColumnSchema($info)
|
||||
{
|
||||
/** @var ColumnSchema $column */
|
||||
$column = $this->createColumnSchema();
|
||||
$column->allowNull = $info['is_nullable'];
|
||||
$column->autoIncrement = $info['is_autoinc'];
|
||||
$column->comment = $info['column_comment'];
|
||||
$column->dbType = $info['data_type'];
|
||||
$column->defaultValue = $info['column_default'];
|
||||
$column->enumValues = ($info['enum_values'] !== null) ? explode(',', str_replace(["''"], ["'"], $info['enum_values'])) : null;
|
||||
$column->unsigned = false; // has no meaning in PG
|
||||
$column->isPrimaryKey = $info['is_pkey'];
|
||||
$column->name = $info['column_name'];
|
||||
$column->precision = $info['numeric_precision'];
|
||||
$column->scale = $info['numeric_scale'];
|
||||
$column->size = $info['size'] === null ? null : (int) $info['size'];
|
||||
$column->dimension = (int)$info['dimension'];
|
||||
if (isset($this->typeMap[$column->dbType])) {
|
||||
$column->type = $this->typeMap[$column->dbType];
|
||||
} else {
|
||||
$column->type = self::TYPE_STRING;
|
||||
}
|
||||
$column->phpType = $this->getColumnPhpType($column);
|
||||
|
||||
return $column;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function insert($table, $columns)
|
||||
{
|
||||
$params = [];
|
||||
$sql = $this->db->getQueryBuilder()->insert($table, $columns, $params);
|
||||
$returnColumns = $this->getTableSchema($table)->primaryKey;
|
||||
if (!empty($returnColumns)) {
|
||||
$returning = [];
|
||||
foreach ((array) $returnColumns as $name) {
|
||||
$returning[] = $this->quoteColumnName($name);
|
||||
}
|
||||
$sql .= ' RETURNING ' . implode(', ', $returning);
|
||||
}
|
||||
|
||||
$command = $this->db->createCommand($sql, $params);
|
||||
$command->prepare(false);
|
||||
$result = $command->queryOne();
|
||||
|
||||
return !$command->pdoStatement->rowCount() ? false : $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads multiple types of constraints and returns the specified ones.
|
||||
* @param string $tableName table name.
|
||||
* @param string $returnType return type:
|
||||
* - primaryKey
|
||||
* - foreignKeys
|
||||
* - uniques
|
||||
* - checks
|
||||
* @return mixed constraints.
|
||||
*/
|
||||
private function loadTableConstraints($tableName, $returnType)
|
||||
{
|
||||
static $sql = <<<'SQL'
|
||||
SELECT
|
||||
"c"."conname" AS "name",
|
||||
"a"."attname" AS "column_name",
|
||||
"c"."contype" AS "type",
|
||||
"ftcns"."nspname" AS "foreign_table_schema",
|
||||
"ftc"."relname" AS "foreign_table_name",
|
||||
"fa"."attname" AS "foreign_column_name",
|
||||
"c"."confupdtype" AS "on_update",
|
||||
"c"."confdeltype" AS "on_delete",
|
||||
"c"."consrc" AS "check_expr"
|
||||
FROM "pg_class" AS "tc"
|
||||
INNER JOIN "pg_namespace" AS "tcns"
|
||||
ON "tcns"."oid" = "tc"."relnamespace"
|
||||
INNER JOIN "pg_constraint" AS "c"
|
||||
ON "c"."conrelid" = "tc"."oid"
|
||||
INNER JOIN "pg_attribute" AS "a"
|
||||
ON "a"."attrelid" = "c"."conrelid" AND "a"."attnum" = ANY ("c"."conkey")
|
||||
LEFT JOIN "pg_class" AS "ftc"
|
||||
ON "ftc"."oid" = "c"."confrelid"
|
||||
LEFT JOIN "pg_namespace" AS "ftcns"
|
||||
ON "ftcns"."oid" = "ftc"."relnamespace"
|
||||
LEFT JOIN "pg_attribute" "fa"
|
||||
ON "fa"."attrelid" = "c"."confrelid" AND "fa"."attnum" = ANY ("c"."confkey")
|
||||
WHERE "tcns"."nspname" = :schemaName AND "tc"."relname" = :tableName
|
||||
ORDER BY "a"."attnum" ASC, "fa"."attnum" ASC
|
||||
SQL;
|
||||
static $actionTypes = [
|
||||
'a' => 'NO ACTION',
|
||||
'r' => 'RESTRICT',
|
||||
'c' => 'CASCADE',
|
||||
'n' => 'SET NULL',
|
||||
'd' => 'SET DEFAULT',
|
||||
];
|
||||
|
||||
$resolvedName = $this->resolveTableName($tableName);
|
||||
$constraints = $this->db->createCommand($sql, [
|
||||
':schemaName' => $resolvedName->schemaName,
|
||||
':tableName' => $resolvedName->name,
|
||||
])->queryAll();
|
||||
$constraints = $this->normalizePdoRowKeyCase($constraints, true);
|
||||
$constraints = ArrayHelper::index($constraints, null, ['type', 'name']);
|
||||
$result = [
|
||||
'primaryKey' => null,
|
||||
'foreignKeys' => [],
|
||||
'uniques' => [],
|
||||
'checks' => [],
|
||||
];
|
||||
foreach ($constraints as $type => $names) {
|
||||
foreach ($names as $name => $constraint) {
|
||||
switch ($type) {
|
||||
case 'p':
|
||||
$result['primaryKey'] = new Constraint([
|
||||
'name' => $name,
|
||||
'columnNames' => ArrayHelper::getColumn($constraint, 'column_name'),
|
||||
]);
|
||||
break;
|
||||
case 'f':
|
||||
$result['foreignKeys'][] = new ForeignKeyConstraint([
|
||||
'name' => $name,
|
||||
'columnNames' => array_keys(array_count_values(ArrayHelper::getColumn($constraint, 'column_name'))),
|
||||
'foreignSchemaName' => $constraint[0]['foreign_table_schema'],
|
||||
'foreignTableName' => $constraint[0]['foreign_table_name'],
|
||||
'foreignColumnNames' => array_keys(array_count_values(ArrayHelper::getColumn($constraint, 'foreign_column_name'))),
|
||||
'onDelete' => isset($actionTypes[$constraint[0]['on_delete']]) ? $actionTypes[$constraint[0]['on_delete']] : null,
|
||||
'onUpdate' => isset($actionTypes[$constraint[0]['on_update']]) ? $actionTypes[$constraint[0]['on_update']] : null,
|
||||
]);
|
||||
break;
|
||||
case 'u':
|
||||
$result['uniques'][] = new Constraint([
|
||||
'name' => $name,
|
||||
'columnNames' => ArrayHelper::getColumn($constraint, 'column_name'),
|
||||
]);
|
||||
break;
|
||||
case 'c':
|
||||
$result['checks'][] = new CheckConstraint([
|
||||
'name' => $name,
|
||||
'columnNames' => ArrayHelper::getColumn($constraint, 'column_name'),
|
||||
'expression' => $constraint[0]['check_expr'],
|
||||
]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($result as $type => $data) {
|
||||
$this->setTableMetadata($tableName, $type, $data);
|
||||
}
|
||||
|
||||
return $result[$returnType];
|
||||
}
|
||||
}
|
||||
46
vendor/yiisoft/yii2/db/sqlite/ColumnSchemaBuilder.php
vendored
Normal file
46
vendor/yiisoft/yii2/db/sqlite/ColumnSchemaBuilder.php
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\sqlite;
|
||||
|
||||
use yii\db\ColumnSchemaBuilder as AbstractColumnSchemaBuilder;
|
||||
|
||||
/**
|
||||
* ColumnSchemaBuilder is the schema builder for Sqlite databases.
|
||||
*
|
||||
* @author Chris Harris <chris@buckshotsoftware.com>
|
||||
* @since 2.0.8
|
||||
*/
|
||||
class ColumnSchemaBuilder extends AbstractColumnSchemaBuilder
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function buildUnsignedString()
|
||||
{
|
||||
return $this->isUnsigned ? ' UNSIGNED' : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
switch ($this->getTypeCategory()) {
|
||||
case self::CATEGORY_PK:
|
||||
$format = '{type}{check}{append}';
|
||||
break;
|
||||
case self::CATEGORY_NUMERIC:
|
||||
$format = '{type}{length}{unsigned}{notnull}{unique}{check}{default}{append}';
|
||||
break;
|
||||
default:
|
||||
$format = '{type}{length}{notnull}{unique}{check}{default}{append}';
|
||||
}
|
||||
|
||||
return $this->buildCompleteString($format);
|
||||
}
|
||||
}
|
||||
116
vendor/yiisoft/yii2/db/sqlite/Command.php
vendored
Normal file
116
vendor/yiisoft/yii2/db/sqlite/Command.php
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\sqlite;
|
||||
|
||||
use yii\db\SqlToken;
|
||||
use yii\helpers\StringHelper;
|
||||
|
||||
/**
|
||||
* Command represents an SQLite's SQL statement to be executed against a database.
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @author Sergey Makinen <sergey@makinen.ru>
|
||||
* @since 2.0.14
|
||||
*/
|
||||
class Command extends \yii\db\Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
$sql = $this->getSql();
|
||||
$params = $this->params;
|
||||
$statements = $this->splitStatements($sql, $params);
|
||||
if ($statements === false) {
|
||||
return parent::execute();
|
||||
}
|
||||
|
||||
$result = null;
|
||||
foreach ($statements as $statement) {
|
||||
list($statementSql, $statementParams) = $statement;
|
||||
$this->setSql($statementSql)->bindValues($statementParams);
|
||||
$result = parent::execute();
|
||||
}
|
||||
$this->setSql($sql)->bindValues($params);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function queryInternal($method, $fetchMode = null)
|
||||
{
|
||||
$sql = $this->getSql();
|
||||
$params = $this->params;
|
||||
$statements = $this->splitStatements($sql, $params);
|
||||
if ($statements === false) {
|
||||
return parent::queryInternal($method, $fetchMode);
|
||||
}
|
||||
|
||||
list($lastStatementSql, $lastStatementParams) = array_pop($statements);
|
||||
foreach ($statements as $statement) {
|
||||
list($statementSql, $statementParams) = $statement;
|
||||
$this->setSql($statementSql)->bindValues($statementParams);
|
||||
parent::execute();
|
||||
}
|
||||
$this->setSql($lastStatementSql)->bindValues($lastStatementParams);
|
||||
$result = parent::queryInternal($method, $fetchMode);
|
||||
$this->setSql($sql)->bindValues($params);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits the specified SQL code into individual SQL statements and returns them
|
||||
* or `false` if there's a single statement.
|
||||
* @param string $sql
|
||||
* @param array $params
|
||||
* @return string[]|false
|
||||
*/
|
||||
private function splitStatements($sql, $params)
|
||||
{
|
||||
$semicolonIndex = strpos($sql, ';');
|
||||
if ($semicolonIndex === false || $semicolonIndex === StringHelper::byteLength($sql) - 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$tokenizer = new SqlTokenizer($sql);
|
||||
$codeToken = $tokenizer->tokenize();
|
||||
if (count($codeToken->getChildren()) === 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$statements = [];
|
||||
foreach ($codeToken->getChildren() as $statement) {
|
||||
$statements[] = [$statement->getSql(), $this->extractUsedParams($statement, $params)];
|
||||
}
|
||||
return $statements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns named bindings used in the specified statement token.
|
||||
* @param SqlToken $statement
|
||||
* @param array $params
|
||||
* @return array
|
||||
*/
|
||||
private function extractUsedParams(SqlToken $statement, $params)
|
||||
{
|
||||
preg_match_all('/(?P<placeholder>[:][a-zA-Z0-9_]+)/', $statement->getSql(), $matches, PREG_SET_ORDER);
|
||||
$result = [];
|
||||
foreach ($matches as $match) {
|
||||
$phName = ltrim($match['placeholder'], ':');
|
||||
if (isset($params[$phName])) {
|
||||
$result[$phName] = $params[$phName];
|
||||
} elseif (isset($params[':' . $phName])) {
|
||||
$result[':' . $phName] = $params[':' . $phName];
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
544
vendor/yiisoft/yii2/db/sqlite/QueryBuilder.php
vendored
Normal file
544
vendor/yiisoft/yii2/db/sqlite/QueryBuilder.php
vendored
Normal file
@@ -0,0 +1,544 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\sqlite;
|
||||
|
||||
use yii\base\InvalidArgumentException;
|
||||
use yii\base\NotSupportedException;
|
||||
use yii\db\Connection;
|
||||
use yii\db\Constraint;
|
||||
use yii\db\Expression;
|
||||
use yii\db\ExpressionInterface;
|
||||
use yii\db\Query;
|
||||
use yii\helpers\StringHelper;
|
||||
|
||||
/**
|
||||
* QueryBuilder is the query builder for SQLite databases.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class QueryBuilder extends \yii\db\QueryBuilder
|
||||
{
|
||||
/**
|
||||
* @var array mapping from abstract column types (keys) to physical column types (values).
|
||||
*/
|
||||
public $typeMap = [
|
||||
Schema::TYPE_PK => 'integer PRIMARY KEY AUTOINCREMENT NOT NULL',
|
||||
Schema::TYPE_UPK => 'integer UNSIGNED PRIMARY KEY AUTOINCREMENT NOT NULL',
|
||||
Schema::TYPE_BIGPK => 'integer PRIMARY KEY AUTOINCREMENT NOT NULL',
|
||||
Schema::TYPE_UBIGPK => 'integer UNSIGNED PRIMARY KEY AUTOINCREMENT NOT NULL',
|
||||
Schema::TYPE_CHAR => 'char(1)',
|
||||
Schema::TYPE_STRING => 'varchar(255)',
|
||||
Schema::TYPE_TEXT => 'text',
|
||||
Schema::TYPE_TINYINT => 'tinyint',
|
||||
Schema::TYPE_SMALLINT => 'smallint',
|
||||
Schema::TYPE_INTEGER => 'integer',
|
||||
Schema::TYPE_BIGINT => 'bigint',
|
||||
Schema::TYPE_FLOAT => 'float',
|
||||
Schema::TYPE_DOUBLE => 'double',
|
||||
Schema::TYPE_DECIMAL => 'decimal(10,0)',
|
||||
Schema::TYPE_DATETIME => 'datetime',
|
||||
Schema::TYPE_TIMESTAMP => 'timestamp',
|
||||
Schema::TYPE_TIME => 'time',
|
||||
Schema::TYPE_DATE => 'date',
|
||||
Schema::TYPE_BINARY => 'blob',
|
||||
Schema::TYPE_BOOLEAN => 'boolean',
|
||||
Schema::TYPE_MONEY => 'decimal(19,4)',
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function defaultExpressionBuilders()
|
||||
{
|
||||
return array_merge(parent::defaultExpressionBuilders(), [
|
||||
'yii\db\conditions\LikeCondition' => 'yii\db\sqlite\conditions\LikeConditionBuilder',
|
||||
'yii\db\conditions\InCondition' => 'yii\db\sqlite\conditions\InConditionBuilder',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @see https://stackoverflow.com/questions/15277373/sqlite-upsert-update-or-insert/15277374#15277374
|
||||
*/
|
||||
public function upsert($table, $insertColumns, $updateColumns, &$params)
|
||||
{
|
||||
/** @var Constraint[] $constraints */
|
||||
list($uniqueNames, $insertNames, $updateNames) = $this->prepareUpsertColumns($table, $insertColumns, $updateColumns, $constraints);
|
||||
if (empty($uniqueNames)) {
|
||||
return $this->insert($table, $insertColumns, $params);
|
||||
}
|
||||
|
||||
list(, $placeholders, $values, $params) = $this->prepareInsertValues($table, $insertColumns, $params);
|
||||
$insertSql = 'INSERT OR IGNORE INTO ' . $this->db->quoteTableName($table)
|
||||
. (!empty($insertNames) ? ' (' . implode(', ', $insertNames) . ')' : '')
|
||||
. (!empty($placeholders) ? ' VALUES (' . implode(', ', $placeholders) . ')' : $values);
|
||||
if ($updateColumns === false) {
|
||||
return $insertSql;
|
||||
}
|
||||
|
||||
$updateCondition = ['or'];
|
||||
$quotedTableName = $this->db->quoteTableName($table);
|
||||
foreach ($constraints as $constraint) {
|
||||
$constraintCondition = ['and'];
|
||||
foreach ($constraint->columnNames as $name) {
|
||||
$quotedName = $this->db->quoteColumnName($name);
|
||||
$constraintCondition[] = "$quotedTableName.$quotedName=(SELECT $quotedName FROM `EXCLUDED`)";
|
||||
}
|
||||
$updateCondition[] = $constraintCondition;
|
||||
}
|
||||
if ($updateColumns === true) {
|
||||
$updateColumns = [];
|
||||
foreach ($updateNames as $name) {
|
||||
$quotedName = $this->db->quoteColumnName($name);
|
||||
if (strrpos($quotedName, '.') === false) {
|
||||
$quotedName = "(SELECT $quotedName FROM `EXCLUDED`)";
|
||||
}
|
||||
$updateColumns[$name] = new Expression($quotedName);
|
||||
}
|
||||
}
|
||||
$updateSql = 'WITH "EXCLUDED" (' . implode(', ', $insertNames)
|
||||
. ') AS (' . (!empty($placeholders) ? 'VALUES (' . implode(', ', $placeholders) . ')' : ltrim($values, ' ')) . ') '
|
||||
. $this->update($table, $updateColumns, $updateCondition, $params);
|
||||
return "$updateSql; $insertSql;";
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a batch INSERT SQL statement.
|
||||
*
|
||||
* For example,
|
||||
*
|
||||
* ```php
|
||||
* $connection->createCommand()->batchInsert('user', ['name', 'age'], [
|
||||
* ['Tom', 30],
|
||||
* ['Jane', 20],
|
||||
* ['Linda', 25],
|
||||
* ])->execute();
|
||||
* ```
|
||||
*
|
||||
* Note that the values in each row must match the corresponding column names.
|
||||
*
|
||||
* @param string $table the table that new rows will be inserted into.
|
||||
* @param array $columns the column names
|
||||
* @param array|\Generator $rows the rows to be batch inserted into the table
|
||||
* @return string the batch INSERT SQL statement
|
||||
*/
|
||||
public function batchInsert($table, $columns, $rows, &$params = [])
|
||||
{
|
||||
if (empty($rows)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// SQLite supports batch insert natively since 3.7.11
|
||||
// http://www.sqlite.org/releaselog/3_7_11.html
|
||||
$this->db->open(); // ensure pdo is not null
|
||||
if (version_compare($this->db->getServerVersion(), '3.7.11', '>=')) {
|
||||
return parent::batchInsert($table, $columns, $rows, $params);
|
||||
}
|
||||
|
||||
$schema = $this->db->getSchema();
|
||||
if (($tableSchema = $schema->getTableSchema($table)) !== null) {
|
||||
$columnSchemas = $tableSchema->columns;
|
||||
} else {
|
||||
$columnSchemas = [];
|
||||
}
|
||||
|
||||
$values = [];
|
||||
foreach ($rows as $row) {
|
||||
$vs = [];
|
||||
foreach ($row as $i => $value) {
|
||||
if (isset($columnSchemas[$columns[$i]])) {
|
||||
$value = $columnSchemas[$columns[$i]]->dbTypecast($value);
|
||||
}
|
||||
if (is_string($value)) {
|
||||
$value = $schema->quoteValue($value);
|
||||
} elseif (is_float($value)) {
|
||||
// ensure type cast always has . as decimal separator in all locales
|
||||
$value = StringHelper::floatToString($value);
|
||||
} elseif ($value === false) {
|
||||
$value = 0;
|
||||
} elseif ($value === null) {
|
||||
$value = 'NULL';
|
||||
} elseif ($value instanceof ExpressionInterface) {
|
||||
$value = $this->buildExpression($value, $params);
|
||||
}
|
||||
$vs[] = $value;
|
||||
}
|
||||
$values[] = implode(', ', $vs);
|
||||
}
|
||||
if (empty($values)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
foreach ($columns as $i => $name) {
|
||||
$columns[$i] = $schema->quoteColumnName($name);
|
||||
}
|
||||
|
||||
return 'INSERT INTO ' . $schema->quoteTableName($table)
|
||||
. ' (' . implode(', ', $columns) . ') SELECT ' . implode(' UNION SELECT ', $values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a SQL statement for resetting the sequence value of a table's primary key.
|
||||
* The sequence will be reset such that the primary key of the next new row inserted
|
||||
* will have the specified value or 1.
|
||||
* @param string $tableName the name of the table whose primary key sequence will be reset
|
||||
* @param mixed $value the value for the primary key of the next new row inserted. If this is not set,
|
||||
* the next new row's primary key will have a value 1.
|
||||
* @return string the SQL statement for resetting sequence
|
||||
* @throws InvalidArgumentException if the table does not exist or there is no sequence associated with the table.
|
||||
*/
|
||||
public function resetSequence($tableName, $value = null)
|
||||
{
|
||||
$db = $this->db;
|
||||
$table = $db->getTableSchema($tableName);
|
||||
if ($table !== null && $table->sequenceName !== null) {
|
||||
$tableName = $db->quoteTableName($tableName);
|
||||
if ($value === null) {
|
||||
$key = $this->db->quoteColumnName(reset($table->primaryKey));
|
||||
$value = $this->db->useMaster(function (Connection $db) use ($key, $tableName) {
|
||||
return $db->createCommand("SELECT MAX($key) FROM $tableName")->queryScalar();
|
||||
});
|
||||
} else {
|
||||
$value = (int) $value - 1;
|
||||
}
|
||||
|
||||
return "UPDATE sqlite_sequence SET seq='$value' WHERE name='{$table->name}'";
|
||||
} elseif ($table === null) {
|
||||
throw new InvalidArgumentException("Table not found: $tableName");
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException("There is not sequence associated with table '$tableName'.'");
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables integrity check.
|
||||
* @param bool $check whether to turn on or off the integrity check.
|
||||
* @param string $schema the schema of the tables. Meaningless for SQLite.
|
||||
* @param string $table the table name. Meaningless for SQLite.
|
||||
* @return string the SQL statement for checking integrity
|
||||
* @throws NotSupportedException this is not supported by SQLite
|
||||
*/
|
||||
public function checkIntegrity($check = true, $schema = '', $table = '')
|
||||
{
|
||||
return 'PRAGMA foreign_keys=' . (int) $check;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a SQL statement for truncating a DB table.
|
||||
* @param string $table the table to be truncated. The name will be properly quoted by the method.
|
||||
* @return string the SQL statement for truncating a DB table.
|
||||
*/
|
||||
public function truncateTable($table)
|
||||
{
|
||||
return 'DELETE FROM ' . $this->db->quoteTableName($table);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a SQL statement for dropping an index.
|
||||
* @param string $name the name of the index to be dropped. The name will be properly quoted by the method.
|
||||
* @param string $table the table whose index is to be dropped. The name will be properly quoted by the method.
|
||||
* @return string the SQL statement for dropping an index.
|
||||
*/
|
||||
public function dropIndex($name, $table)
|
||||
{
|
||||
return 'DROP INDEX ' . $this->db->quoteTableName($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a SQL statement for dropping a DB column.
|
||||
* @param string $table the table whose column is to be dropped. The name will be properly quoted by the method.
|
||||
* @param string $column the name of the column to be dropped. The name will be properly quoted by the method.
|
||||
* @return string the SQL statement for dropping a DB column.
|
||||
* @throws NotSupportedException this is not supported by SQLite
|
||||
*/
|
||||
public function dropColumn($table, $column)
|
||||
{
|
||||
throw new NotSupportedException(__METHOD__ . ' is not supported by SQLite.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a SQL statement for renaming a column.
|
||||
* @param string $table the table whose column is to be renamed. The name will be properly quoted by the method.
|
||||
* @param string $oldName the old name of the column. The name will be properly quoted by the method.
|
||||
* @param string $newName the new name of the column. The name will be properly quoted by the method.
|
||||
* @return string the SQL statement for renaming a DB column.
|
||||
* @throws NotSupportedException this is not supported by SQLite
|
||||
*/
|
||||
public function renameColumn($table, $oldName, $newName)
|
||||
{
|
||||
throw new NotSupportedException(__METHOD__ . ' is not supported by SQLite.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a SQL statement for adding a foreign key constraint to an existing table.
|
||||
* The method will properly quote the table and column names.
|
||||
* @param string $name the name of the foreign key constraint.
|
||||
* @param string $table the table that the foreign key constraint will be added to.
|
||||
* @param string|array $columns the name of the column to that the constraint will be added on.
|
||||
* If there are multiple columns, separate them with commas or use an array to represent them.
|
||||
* @param string $refTable the table that the foreign key references to.
|
||||
* @param string|array $refColumns the name of the column that the foreign key references to.
|
||||
* If there are multiple columns, separate them with commas or use an array to represent them.
|
||||
* @param string $delete the ON DELETE option. Most DBMS support these options: RESTRICT, CASCADE, NO ACTION, SET DEFAULT, SET NULL
|
||||
* @param string $update the ON UPDATE option. Most DBMS support these options: RESTRICT, CASCADE, NO ACTION, SET DEFAULT, SET NULL
|
||||
* @return string the SQL statement for adding a foreign key constraint to an existing table.
|
||||
* @throws NotSupportedException this is not supported by SQLite
|
||||
*/
|
||||
public function addForeignKey($name, $table, $columns, $refTable, $refColumns, $delete = null, $update = null)
|
||||
{
|
||||
throw new NotSupportedException(__METHOD__ . ' is not supported by SQLite.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a SQL statement for dropping a foreign key constraint.
|
||||
* @param string $name the name of the foreign key constraint to be dropped. The name will be properly quoted by the method.
|
||||
* @param string $table the table whose foreign is to be dropped. The name will be properly quoted by the method.
|
||||
* @return string the SQL statement for dropping a foreign key constraint.
|
||||
* @throws NotSupportedException this is not supported by SQLite
|
||||
*/
|
||||
public function dropForeignKey($name, $table)
|
||||
{
|
||||
throw new NotSupportedException(__METHOD__ . ' is not supported by SQLite.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a SQL statement for renaming a DB table.
|
||||
*
|
||||
* @param string $table the table to be renamed. The name will be properly quoted by the method.
|
||||
* @param string $newName the new table name. The name will be properly quoted by the method.
|
||||
* @return string the SQL statement for renaming a DB table.
|
||||
*/
|
||||
public function renameTable($table, $newName)
|
||||
{
|
||||
return 'ALTER TABLE ' . $this->db->quoteTableName($table) . ' RENAME TO ' . $this->db->quoteTableName($newName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a SQL statement for changing the definition of a column.
|
||||
* @param string $table the table whose column is to be changed. The table name will be properly quoted by the method.
|
||||
* @param string $column the name of the column to be changed. The name will be properly quoted by the method.
|
||||
* @param string $type the new column type. The [[getColumnType()]] method will be invoked to convert abstract
|
||||
* column type (if any) into the physical one. Anything that is not recognized as abstract type will be kept
|
||||
* in the generated SQL. For example, 'string' will be turned into 'varchar(255)', while 'string not null'
|
||||
* will become 'varchar(255) not null'.
|
||||
* @return string the SQL statement for changing the definition of a column.
|
||||
* @throws NotSupportedException this is not supported by SQLite
|
||||
*/
|
||||
public function alterColumn($table, $column, $type)
|
||||
{
|
||||
throw new NotSupportedException(__METHOD__ . ' is not supported by SQLite.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a SQL statement for adding a primary key constraint to an existing table.
|
||||
* @param string $name the name of the primary key constraint.
|
||||
* @param string $table the table that the primary key constraint will be added to.
|
||||
* @param string|array $columns comma separated string or array of columns that the primary key will consist of.
|
||||
* @return string the SQL statement for adding a primary key constraint to an existing table.
|
||||
* @throws NotSupportedException this is not supported by SQLite
|
||||
*/
|
||||
public function addPrimaryKey($name, $table, $columns)
|
||||
{
|
||||
throw new NotSupportedException(__METHOD__ . ' is not supported by SQLite.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a SQL statement for removing a primary key constraint to an existing table.
|
||||
* @param string $name the name of the primary key constraint to be removed.
|
||||
* @param string $table the table that the primary key constraint will be removed from.
|
||||
* @return string the SQL statement for removing a primary key constraint from an existing table.
|
||||
* @throws NotSupportedException this is not supported by SQLite
|
||||
*/
|
||||
public function dropPrimaryKey($name, $table)
|
||||
{
|
||||
throw new NotSupportedException(__METHOD__ . ' is not supported by SQLite.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @throws NotSupportedException this is not supported by SQLite.
|
||||
*/
|
||||
public function addUnique($name, $table, $columns)
|
||||
{
|
||||
throw new NotSupportedException(__METHOD__ . ' is not supported by SQLite.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @throws NotSupportedException this is not supported by SQLite.
|
||||
*/
|
||||
public function dropUnique($name, $table)
|
||||
{
|
||||
throw new NotSupportedException(__METHOD__ . ' is not supported by SQLite.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @throws NotSupportedException this is not supported by SQLite.
|
||||
*/
|
||||
public function addCheck($name, $table, $expression)
|
||||
{
|
||||
throw new NotSupportedException(__METHOD__ . ' is not supported by SQLite.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @throws NotSupportedException this is not supported by SQLite.
|
||||
*/
|
||||
public function dropCheck($name, $table)
|
||||
{
|
||||
throw new NotSupportedException(__METHOD__ . ' is not supported by SQLite.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @throws NotSupportedException this is not supported by SQLite.
|
||||
*/
|
||||
public function addDefaultValue($name, $table, $column, $value)
|
||||
{
|
||||
throw new NotSupportedException(__METHOD__ . ' is not supported by SQLite.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @throws NotSupportedException this is not supported by SQLite.
|
||||
*/
|
||||
public function dropDefaultValue($name, $table)
|
||||
{
|
||||
throw new NotSupportedException(__METHOD__ . ' is not supported by SQLite.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @throws NotSupportedException
|
||||
* @since 2.0.8
|
||||
*/
|
||||
public function addCommentOnColumn($table, $column, $comment)
|
||||
{
|
||||
throw new NotSupportedException(__METHOD__ . ' is not supported by SQLite.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @throws NotSupportedException
|
||||
* @since 2.0.8
|
||||
*/
|
||||
public function addCommentOnTable($table, $comment)
|
||||
{
|
||||
throw new NotSupportedException(__METHOD__ . ' is not supported by SQLite.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @throws NotSupportedException
|
||||
* @since 2.0.8
|
||||
*/
|
||||
public function dropCommentFromColumn($table, $column)
|
||||
{
|
||||
throw new NotSupportedException(__METHOD__ . ' is not supported by SQLite.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @throws NotSupportedException
|
||||
* @since 2.0.8
|
||||
*/
|
||||
public function dropCommentFromTable($table)
|
||||
{
|
||||
throw new NotSupportedException(__METHOD__ . ' is not supported by SQLite.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildLimit($limit, $offset)
|
||||
{
|
||||
$sql = '';
|
||||
if ($this->hasLimit($limit)) {
|
||||
$sql = 'LIMIT ' . $limit;
|
||||
if ($this->hasOffset($offset)) {
|
||||
$sql .= ' OFFSET ' . $offset;
|
||||
}
|
||||
} elseif ($this->hasOffset($offset)) {
|
||||
// limit is not optional in SQLite
|
||||
// http://www.sqlite.org/syntaxdiagrams.html#select-stmt
|
||||
$sql = "LIMIT 9223372036854775807 OFFSET $offset"; // 2^63-1
|
||||
}
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function build($query, $params = [])
|
||||
{
|
||||
$query = $query->prepare($this);
|
||||
|
||||
$params = empty($params) ? $query->params : array_merge($params, $query->params);
|
||||
|
||||
$clauses = [
|
||||
$this->buildSelect($query->select, $params, $query->distinct, $query->selectOption),
|
||||
$this->buildFrom($query->from, $params),
|
||||
$this->buildJoin($query->join, $params),
|
||||
$this->buildWhere($query->where, $params),
|
||||
$this->buildGroupBy($query->groupBy),
|
||||
$this->buildHaving($query->having, $params),
|
||||
];
|
||||
|
||||
$sql = implode($this->separator, array_filter($clauses));
|
||||
$sql = $this->buildOrderByAndLimit($sql, $query->orderBy, $query->limit, $query->offset);
|
||||
|
||||
if (!empty($query->orderBy)) {
|
||||
foreach ($query->orderBy as $expression) {
|
||||
if ($expression instanceof ExpressionInterface) {
|
||||
$this->buildExpression($expression, $params);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!empty($query->groupBy)) {
|
||||
foreach ($query->groupBy as $expression) {
|
||||
if ($expression instanceof ExpressionInterface) {
|
||||
$this->buildExpression($expression, $params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$union = $this->buildUnion($query->union, $params);
|
||||
if ($union !== '') {
|
||||
$sql = "$sql{$this->separator}$union";
|
||||
}
|
||||
|
||||
return [$sql, $params];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildUnion($unions, &$params)
|
||||
{
|
||||
if (empty($unions)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$result = '';
|
||||
|
||||
foreach ($unions as $i => $union) {
|
||||
$query = $union['query'];
|
||||
if ($query instanceof Query) {
|
||||
list($unions[$i]['query'], $params) = $this->build($query, $params);
|
||||
}
|
||||
|
||||
$result .= ' UNION ' . ($union['all'] ? 'ALL ' : '') . ' ' . $unions[$i]['query'];
|
||||
}
|
||||
|
||||
return trim($result);
|
||||
}
|
||||
}
|
||||
456
vendor/yiisoft/yii2/db/sqlite/Schema.php
vendored
Normal file
456
vendor/yiisoft/yii2/db/sqlite/Schema.php
vendored
Normal file
@@ -0,0 +1,456 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\sqlite;
|
||||
|
||||
use yii\base\NotSupportedException;
|
||||
use yii\db\CheckConstraint;
|
||||
use yii\db\ColumnSchema;
|
||||
use yii\db\Constraint;
|
||||
use yii\db\ConstraintFinderInterface;
|
||||
use yii\db\ConstraintFinderTrait;
|
||||
use yii\db\Expression;
|
||||
use yii\db\ForeignKeyConstraint;
|
||||
use yii\db\IndexConstraint;
|
||||
use yii\db\SqlToken;
|
||||
use yii\db\TableSchema;
|
||||
use yii\db\Transaction;
|
||||
use yii\helpers\ArrayHelper;
|
||||
|
||||
/**
|
||||
* Schema is the class for retrieving metadata from a SQLite (2/3) database.
|
||||
*
|
||||
* @property string $transactionIsolationLevel The transaction isolation level to use for this transaction.
|
||||
* This can be either [[Transaction::READ_UNCOMMITTED]] or [[Transaction::SERIALIZABLE]].
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class Schema extends \yii\db\Schema implements ConstraintFinderInterface
|
||||
{
|
||||
use ConstraintFinderTrait;
|
||||
|
||||
/**
|
||||
* @var array mapping from physical column types (keys) to abstract column types (values)
|
||||
*/
|
||||
public $typeMap = [
|
||||
'tinyint' => self::TYPE_TINYINT,
|
||||
'bit' => self::TYPE_SMALLINT,
|
||||
'boolean' => self::TYPE_BOOLEAN,
|
||||
'bool' => self::TYPE_BOOLEAN,
|
||||
'smallint' => self::TYPE_SMALLINT,
|
||||
'mediumint' => self::TYPE_INTEGER,
|
||||
'int' => self::TYPE_INTEGER,
|
||||
'integer' => self::TYPE_INTEGER,
|
||||
'bigint' => self::TYPE_BIGINT,
|
||||
'float' => self::TYPE_FLOAT,
|
||||
'double' => self::TYPE_DOUBLE,
|
||||
'real' => self::TYPE_FLOAT,
|
||||
'decimal' => self::TYPE_DECIMAL,
|
||||
'numeric' => self::TYPE_DECIMAL,
|
||||
'tinytext' => self::TYPE_TEXT,
|
||||
'mediumtext' => self::TYPE_TEXT,
|
||||
'longtext' => self::TYPE_TEXT,
|
||||
'text' => self::TYPE_TEXT,
|
||||
'varchar' => self::TYPE_STRING,
|
||||
'string' => self::TYPE_STRING,
|
||||
'char' => self::TYPE_CHAR,
|
||||
'blob' => self::TYPE_BINARY,
|
||||
'datetime' => self::TYPE_DATETIME,
|
||||
'year' => self::TYPE_DATE,
|
||||
'date' => self::TYPE_DATE,
|
||||
'time' => self::TYPE_TIME,
|
||||
'timestamp' => self::TYPE_TIMESTAMP,
|
||||
'enum' => self::TYPE_STRING,
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $tableQuoteCharacter = '`';
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $columnQuoteCharacter = '`';
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function findTableNames($schema = '')
|
||||
{
|
||||
$sql = "SELECT DISTINCT tbl_name FROM sqlite_master WHERE tbl_name<>'sqlite_sequence' ORDER BY tbl_name";
|
||||
return $this->db->createCommand($sql)->queryColumn();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function loadTableSchema($name)
|
||||
{
|
||||
$table = new TableSchema();
|
||||
$table->name = $name;
|
||||
$table->fullName = $name;
|
||||
|
||||
if ($this->findColumns($table)) {
|
||||
$this->findConstraints($table);
|
||||
return $table;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function loadTablePrimaryKey($tableName)
|
||||
{
|
||||
return $this->loadTableConstraints($tableName, 'primaryKey');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function loadTableForeignKeys($tableName)
|
||||
{
|
||||
$foreignKeys = $this->db->createCommand('PRAGMA FOREIGN_KEY_LIST (' . $this->quoteValue($tableName) . ')')->queryAll();
|
||||
$foreignKeys = $this->normalizePdoRowKeyCase($foreignKeys, true);
|
||||
$foreignKeys = ArrayHelper::index($foreignKeys, null, 'table');
|
||||
ArrayHelper::multisort($foreignKeys, 'seq', SORT_ASC, SORT_NUMERIC);
|
||||
$result = [];
|
||||
foreach ($foreignKeys as $table => $foreignKey) {
|
||||
$result[] = new ForeignKeyConstraint([
|
||||
'columnNames' => ArrayHelper::getColumn($foreignKey, 'from'),
|
||||
'foreignTableName' => $table,
|
||||
'foreignColumnNames' => ArrayHelper::getColumn($foreignKey, 'to'),
|
||||
'onDelete' => isset($foreignKey[0]['on_delete']) ? $foreignKey[0]['on_delete'] : null,
|
||||
'onUpdate' => isset($foreignKey[0]['on_update']) ? $foreignKey[0]['on_update'] : null,
|
||||
]);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function loadTableIndexes($tableName)
|
||||
{
|
||||
return $this->loadTableConstraints($tableName, 'indexes');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function loadTableUniques($tableName)
|
||||
{
|
||||
return $this->loadTableConstraints($tableName, 'uniques');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function loadTableChecks($tableName)
|
||||
{
|
||||
$sql = $this->db->createCommand('SELECT `sql` FROM `sqlite_master` WHERE name = :tableName', [
|
||||
':tableName' => $tableName,
|
||||
])->queryScalar();
|
||||
/** @var $code SqlToken[]|SqlToken[][]|SqlToken[][][] */
|
||||
$code = (new SqlTokenizer($sql))->tokenize();
|
||||
$pattern = (new SqlTokenizer('any CREATE any TABLE any()'))->tokenize();
|
||||
if (!$code[0]->matches($pattern, 0, $firstMatchIndex, $lastMatchIndex)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$createTableToken = $code[0][$lastMatchIndex - 1];
|
||||
$result = [];
|
||||
$offset = 0;
|
||||
while (true) {
|
||||
$pattern = (new SqlTokenizer('any CHECK()'))->tokenize();
|
||||
if (!$createTableToken->matches($pattern, $offset, $firstMatchIndex, $offset)) {
|
||||
break;
|
||||
}
|
||||
|
||||
$checkSql = $createTableToken[$offset - 1]->getSql();
|
||||
$name = null;
|
||||
$pattern = (new SqlTokenizer('CONSTRAINT any'))->tokenize();
|
||||
if (isset($createTableToken[$firstMatchIndex - 2]) && $createTableToken->matches($pattern, $firstMatchIndex - 2)) {
|
||||
$name = $createTableToken[$firstMatchIndex - 1]->content;
|
||||
}
|
||||
$result[] = new CheckConstraint([
|
||||
'name' => $name,
|
||||
'expression' => $checkSql,
|
||||
]);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @throws NotSupportedException if this method is called.
|
||||
*/
|
||||
protected function loadTableDefaultValues($tableName)
|
||||
{
|
||||
throw new NotSupportedException('SQLite does not support default value constraints.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a query builder for the MySQL database.
|
||||
* This method may be overridden by child classes to create a DBMS-specific query builder.
|
||||
* @return QueryBuilder query builder instance
|
||||
*/
|
||||
public function createQueryBuilder()
|
||||
{
|
||||
return new QueryBuilder($this->db);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return ColumnSchemaBuilder column schema builder instance
|
||||
*/
|
||||
public function createColumnSchemaBuilder($type, $length = null)
|
||||
{
|
||||
return new ColumnSchemaBuilder($type, $length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects the table column metadata.
|
||||
* @param TableSchema $table the table metadata
|
||||
* @return bool whether the table exists in the database
|
||||
*/
|
||||
protected function findColumns($table)
|
||||
{
|
||||
$sql = 'PRAGMA table_info(' . $this->quoteSimpleTableName($table->name) . ')';
|
||||
$columns = $this->db->createCommand($sql)->queryAll();
|
||||
if (empty($columns)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($columns as $info) {
|
||||
$column = $this->loadColumnSchema($info);
|
||||
$table->columns[$column->name] = $column;
|
||||
if ($column->isPrimaryKey) {
|
||||
$table->primaryKey[] = $column->name;
|
||||
}
|
||||
}
|
||||
if (count($table->primaryKey) === 1 && !strncasecmp($table->columns[$table->primaryKey[0]]->dbType, 'int', 3)) {
|
||||
$table->sequenceName = '';
|
||||
$table->columns[$table->primaryKey[0]]->autoIncrement = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects the foreign key column details for the given table.
|
||||
* @param TableSchema $table the table metadata
|
||||
*/
|
||||
protected function findConstraints($table)
|
||||
{
|
||||
$sql = 'PRAGMA foreign_key_list(' . $this->quoteSimpleTableName($table->name) . ')';
|
||||
$keys = $this->db->createCommand($sql)->queryAll();
|
||||
foreach ($keys as $key) {
|
||||
$id = (int) $key['id'];
|
||||
if (!isset($table->foreignKeys[$id])) {
|
||||
$table->foreignKeys[$id] = [$key['table'], $key['from'] => $key['to']];
|
||||
} else {
|
||||
// composite FK
|
||||
$table->foreignKeys[$id][$key['from']] = $key['to'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all unique indexes for the given table.
|
||||
*
|
||||
* Each array element is of the following structure:
|
||||
*
|
||||
* ```php
|
||||
* [
|
||||
* 'IndexName1' => ['col1' [, ...]],
|
||||
* 'IndexName2' => ['col2' [, ...]],
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* @param TableSchema $table the table metadata
|
||||
* @return array all unique indexes for the given table.
|
||||
*/
|
||||
public function findUniqueIndexes($table)
|
||||
{
|
||||
$sql = 'PRAGMA index_list(' . $this->quoteSimpleTableName($table->name) . ')';
|
||||
$indexes = $this->db->createCommand($sql)->queryAll();
|
||||
$uniqueIndexes = [];
|
||||
|
||||
foreach ($indexes as $index) {
|
||||
$indexName = $index['name'];
|
||||
$indexInfo = $this->db->createCommand('PRAGMA index_info(' . $this->quoteValue($index['name']) . ')')->queryAll();
|
||||
|
||||
if ($index['unique']) {
|
||||
$uniqueIndexes[$indexName] = [];
|
||||
foreach ($indexInfo as $row) {
|
||||
$uniqueIndexes[$indexName][] = $row['name'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $uniqueIndexes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the column information into a [[ColumnSchema]] object.
|
||||
* @param array $info column information
|
||||
* @return ColumnSchema the column schema object
|
||||
*/
|
||||
protected function loadColumnSchema($info)
|
||||
{
|
||||
$column = $this->createColumnSchema();
|
||||
$column->name = $info['name'];
|
||||
$column->allowNull = !$info['notnull'];
|
||||
$column->isPrimaryKey = $info['pk'] != 0;
|
||||
|
||||
$column->dbType = strtolower($info['type']);
|
||||
$column->unsigned = strpos($column->dbType, 'unsigned') !== false;
|
||||
|
||||
$column->type = self::TYPE_STRING;
|
||||
if (preg_match('/^(\w+)(?:\(([^\)]+)\))?/', $column->dbType, $matches)) {
|
||||
$type = strtolower($matches[1]);
|
||||
if (isset($this->typeMap[$type])) {
|
||||
$column->type = $this->typeMap[$type];
|
||||
}
|
||||
|
||||
if (!empty($matches[2])) {
|
||||
$values = explode(',', $matches[2]);
|
||||
$column->size = $column->precision = (int) $values[0];
|
||||
if (isset($values[1])) {
|
||||
$column->scale = (int) $values[1];
|
||||
}
|
||||
if ($column->size === 1 && ($type === 'tinyint' || $type === 'bit')) {
|
||||
$column->type = 'boolean';
|
||||
} elseif ($type === 'bit') {
|
||||
if ($column->size > 32) {
|
||||
$column->type = 'bigint';
|
||||
} elseif ($column->size === 32) {
|
||||
$column->type = 'integer';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$column->phpType = $this->getColumnPhpType($column);
|
||||
|
||||
if (!$column->isPrimaryKey) {
|
||||
if ($info['dflt_value'] === 'null' || $info['dflt_value'] === '' || $info['dflt_value'] === null) {
|
||||
$column->defaultValue = null;
|
||||
} elseif ($column->type === 'timestamp' && $info['dflt_value'] === 'CURRENT_TIMESTAMP') {
|
||||
$column->defaultValue = new Expression('CURRENT_TIMESTAMP');
|
||||
} else {
|
||||
$value = trim($info['dflt_value'], "'\"");
|
||||
$column->defaultValue = $column->phpTypecast($value);
|
||||
}
|
||||
}
|
||||
|
||||
return $column;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the isolation level of the current transaction.
|
||||
* @param string $level The transaction isolation level to use for this transaction.
|
||||
* This can be either [[Transaction::READ_UNCOMMITTED]] or [[Transaction::SERIALIZABLE]].
|
||||
* @throws NotSupportedException when unsupported isolation levels are used.
|
||||
* SQLite only supports SERIALIZABLE and READ UNCOMMITTED.
|
||||
* @see http://www.sqlite.org/pragma.html#pragma_read_uncommitted
|
||||
*/
|
||||
public function setTransactionIsolationLevel($level)
|
||||
{
|
||||
switch ($level) {
|
||||
case Transaction::SERIALIZABLE:
|
||||
$this->db->createCommand('PRAGMA read_uncommitted = False;')->execute();
|
||||
break;
|
||||
case Transaction::READ_UNCOMMITTED:
|
||||
$this->db->createCommand('PRAGMA read_uncommitted = True;')->execute();
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException(get_class($this) . ' only supports transaction isolation levels READ UNCOMMITTED and SERIALIZABLE.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads multiple types of constraints and returns the specified ones.
|
||||
* @param string $tableName table name.
|
||||
* @param string $returnType return type:
|
||||
* - primaryKey
|
||||
* - indexes
|
||||
* - uniques
|
||||
* @return mixed constraints.
|
||||
*/
|
||||
private function loadTableConstraints($tableName, $returnType)
|
||||
{
|
||||
$indexes = $this->db->createCommand('PRAGMA INDEX_LIST (' . $this->quoteValue($tableName) . ')')->queryAll();
|
||||
$indexes = $this->normalizePdoRowKeyCase($indexes, true);
|
||||
$tableColumns = null;
|
||||
if (!empty($indexes) && !isset($indexes[0]['origin'])) {
|
||||
/*
|
||||
* SQLite may not have an "origin" column in INDEX_LIST
|
||||
* See https://www.sqlite.org/src/info/2743846cdba572f6
|
||||
*/
|
||||
$tableColumns = $this->db->createCommand('PRAGMA TABLE_INFO (' . $this->quoteValue($tableName) . ')')->queryAll();
|
||||
$tableColumns = $this->normalizePdoRowKeyCase($tableColumns, true);
|
||||
$tableColumns = ArrayHelper::index($tableColumns, 'cid');
|
||||
}
|
||||
$result = [
|
||||
'primaryKey' => null,
|
||||
'indexes' => [],
|
||||
'uniques' => [],
|
||||
];
|
||||
foreach ($indexes as $index) {
|
||||
$columns = $this->db->createCommand('PRAGMA INDEX_INFO (' . $this->quoteValue($index['name']) . ')')->queryAll();
|
||||
$columns = $this->normalizePdoRowKeyCase($columns, true);
|
||||
ArrayHelper::multisort($columns, 'seqno', SORT_ASC, SORT_NUMERIC);
|
||||
if ($tableColumns !== null) {
|
||||
// SQLite may not have an "origin" column in INDEX_LIST
|
||||
$index['origin'] = 'c';
|
||||
if (!empty($columns) && $tableColumns[$columns[0]['cid']]['pk'] > 0) {
|
||||
$index['origin'] = 'pk';
|
||||
} elseif ($index['unique'] && $this->isSystemIdentifier($index['name'])) {
|
||||
$index['origin'] = 'u';
|
||||
}
|
||||
}
|
||||
$result['indexes'][] = new IndexConstraint([
|
||||
'isPrimary' => $index['origin'] === 'pk',
|
||||
'isUnique' => (bool) $index['unique'],
|
||||
'name' => $index['name'],
|
||||
'columnNames' => ArrayHelper::getColumn($columns, 'name'),
|
||||
]);
|
||||
if ($index['origin'] === 'u') {
|
||||
$result['uniques'][] = new Constraint([
|
||||
'name' => $index['name'],
|
||||
'columnNames' => ArrayHelper::getColumn($columns, 'name'),
|
||||
]);
|
||||
} elseif ($index['origin'] === 'pk') {
|
||||
$result['primaryKey'] = new Constraint([
|
||||
'columnNames' => ArrayHelper::getColumn($columns, 'name'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
foreach ($result as $type => $data) {
|
||||
$this->setTableMetadata($tableName, $type, $data);
|
||||
}
|
||||
|
||||
return $result[$returnType];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the specified identifier is a SQLite system identifier.
|
||||
* @param string $identifier
|
||||
* @return bool
|
||||
* @see https://www.sqlite.org/src/artifact/74108007d286232f
|
||||
*/
|
||||
private function isSystemIdentifier($identifier)
|
||||
{
|
||||
return strncmp($identifier, 'sqlite_', 7) === 0;
|
||||
}
|
||||
}
|
||||
290
vendor/yiisoft/yii2/db/sqlite/SqlTokenizer.php
vendored
Normal file
290
vendor/yiisoft/yii2/db/sqlite/SqlTokenizer.php
vendored
Normal file
@@ -0,0 +1,290 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db\sqlite;
|
||||
|
||||
/**
|
||||
* SqlTokenizer splits SQLite query into individual SQL tokens.
|
||||
* It's used to obtain a `CHECK` constraint information from a `CREATE TABLE` SQL code.
|
||||
*
|
||||
* @see http://www.sqlite.org/draft/tokenreq.html
|
||||
* @see https://sqlite.org/lang.html
|
||||
* @author Sergey Makinen <sergey@makinen.ru>
|
||||
* @since 2.0.13
|
||||
*/
|
||||
class SqlTokenizer extends \yii\db\SqlTokenizer
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function isWhitespace(&$length)
|
||||
{
|
||||
static $whitespaces = [
|
||||
"\f" => true,
|
||||
"\n" => true,
|
||||
"\r" => true,
|
||||
"\t" => true,
|
||||
' ' => true,
|
||||
];
|
||||
|
||||
$length = 1;
|
||||
return isset($whitespaces[$this->substring($length)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function isComment(&$length)
|
||||
{
|
||||
static $comments = [
|
||||
'--' => true,
|
||||
'/*' => true,
|
||||
];
|
||||
|
||||
$length = 2;
|
||||
if (!isset($comments[$this->substring($length)])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->substring($length) === '--') {
|
||||
$length = $this->indexAfter("\n") - $this->offset;
|
||||
} else {
|
||||
$length = $this->indexAfter('*/') - $this->offset;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function isOperator(&$length, &$content)
|
||||
{
|
||||
static $operators = [
|
||||
'!=',
|
||||
'%',
|
||||
'&',
|
||||
'(',
|
||||
')',
|
||||
'*',
|
||||
'+',
|
||||
',',
|
||||
'-',
|
||||
'.',
|
||||
'/',
|
||||
';',
|
||||
'<',
|
||||
'<<',
|
||||
'<=',
|
||||
'<>',
|
||||
'=',
|
||||
'==',
|
||||
'>',
|
||||
'>=',
|
||||
'>>',
|
||||
'|',
|
||||
'||',
|
||||
'~',
|
||||
];
|
||||
|
||||
return $this->startsWithAnyLongest($operators, true, $length);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function isIdentifier(&$length, &$content)
|
||||
{
|
||||
static $identifierDelimiters = [
|
||||
'"' => '"',
|
||||
'[' => ']',
|
||||
'`' => '`',
|
||||
];
|
||||
|
||||
if (!isset($identifierDelimiters[$this->substring(1)])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$delimiter = $identifierDelimiters[$this->substring(1)];
|
||||
$offset = $this->offset;
|
||||
while (true) {
|
||||
$offset = $this->indexAfter($delimiter, $offset + 1);
|
||||
if ($delimiter === ']' || $this->substring(1, true, $offset) !== $delimiter) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
$length = $offset - $this->offset;
|
||||
$content = $this->substring($length - 2, true, $this->offset + 1);
|
||||
if ($delimiter !== ']') {
|
||||
$content = strtr($content, ["$delimiter$delimiter" => $delimiter]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function isStringLiteral(&$length, &$content)
|
||||
{
|
||||
if ($this->substring(1) !== "'") {
|
||||
return false;
|
||||
}
|
||||
|
||||
$offset = $this->offset;
|
||||
while (true) {
|
||||
$offset = $this->indexAfter("'", $offset + 1);
|
||||
if ($this->substring(1, true, $offset) !== "'") {
|
||||
break;
|
||||
}
|
||||
}
|
||||
$length = $offset - $this->offset;
|
||||
$content = strtr($this->substring($length - 2, true, $this->offset + 1), ["''" => "'"]);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function isKeyword($string, &$content)
|
||||
{
|
||||
static $keywords = [
|
||||
'ABORT' => true,
|
||||
'ACTION' => true,
|
||||
'ADD' => true,
|
||||
'AFTER' => true,
|
||||
'ALL' => true,
|
||||
'ALTER' => true,
|
||||
'ANALYZE' => true,
|
||||
'AND' => true,
|
||||
'AS' => true,
|
||||
'ASC' => true,
|
||||
'ATTACH' => true,
|
||||
'AUTOINCREMENT' => true,
|
||||
'BEFORE' => true,
|
||||
'BEGIN' => true,
|
||||
'BETWEEN' => true,
|
||||
'BY' => true,
|
||||
'CASCADE' => true,
|
||||
'CASE' => true,
|
||||
'CAST' => true,
|
||||
'CHECK' => true,
|
||||
'COLLATE' => true,
|
||||
'COLUMN' => true,
|
||||
'COMMIT' => true,
|
||||
'CONFLICT' => true,
|
||||
'CONSTRAINT' => true,
|
||||
'CREATE' => true,
|
||||
'CROSS' => true,
|
||||
'CURRENT_DATE' => true,
|
||||
'CURRENT_TIME' => true,
|
||||
'CURRENT_TIMESTAMP' => true,
|
||||
'DATABASE' => true,
|
||||
'DEFAULT' => true,
|
||||
'DEFERRABLE' => true,
|
||||
'DEFERRED' => true,
|
||||
'DELETE' => true,
|
||||
'DESC' => true,
|
||||
'DETACH' => true,
|
||||
'DISTINCT' => true,
|
||||
'DROP' => true,
|
||||
'EACH' => true,
|
||||
'ELSE' => true,
|
||||
'END' => true,
|
||||
'ESCAPE' => true,
|
||||
'EXCEPT' => true,
|
||||
'EXCLUSIVE' => true,
|
||||
'EXISTS' => true,
|
||||
'EXPLAIN' => true,
|
||||
'FAIL' => true,
|
||||
'FOR' => true,
|
||||
'FOREIGN' => true,
|
||||
'FROM' => true,
|
||||
'FULL' => true,
|
||||
'GLOB' => true,
|
||||
'GROUP' => true,
|
||||
'HAVING' => true,
|
||||
'IF' => true,
|
||||
'IGNORE' => true,
|
||||
'IMMEDIATE' => true,
|
||||
'IN' => true,
|
||||
'INDEX' => true,
|
||||
'INDEXED' => true,
|
||||
'INITIALLY' => true,
|
||||
'INNER' => true,
|
||||
'INSERT' => true,
|
||||
'INSTEAD' => true,
|
||||
'INTERSECT' => true,
|
||||
'INTO' => true,
|
||||
'IS' => true,
|
||||
'ISNULL' => true,
|
||||
'JOIN' => true,
|
||||
'KEY' => true,
|
||||
'LEFT' => true,
|
||||
'LIKE' => true,
|
||||
'LIMIT' => true,
|
||||
'MATCH' => true,
|
||||
'NATURAL' => true,
|
||||
'NO' => true,
|
||||
'NOT' => true,
|
||||
'NOTNULL' => true,
|
||||
'NULL' => true,
|
||||
'OF' => true,
|
||||
'OFFSET' => true,
|
||||
'ON' => true,
|
||||
'OR' => true,
|
||||
'ORDER' => true,
|
||||
'OUTER' => true,
|
||||
'PLAN' => true,
|
||||
'PRAGMA' => true,
|
||||
'PRIMARY' => true,
|
||||
'QUERY' => true,
|
||||
'RAISE' => true,
|
||||
'RECURSIVE' => true,
|
||||
'REFERENCES' => true,
|
||||
'REGEXP' => true,
|
||||
'REINDEX' => true,
|
||||
'RELEASE' => true,
|
||||
'RENAME' => true,
|
||||
'REPLACE' => true,
|
||||
'RESTRICT' => true,
|
||||
'RIGHT' => true,
|
||||
'ROLLBACK' => true,
|
||||
'ROW' => true,
|
||||
'SAVEPOINT' => true,
|
||||
'SELECT' => true,
|
||||
'SET' => true,
|
||||
'TABLE' => true,
|
||||
'TEMP' => true,
|
||||
'TEMPORARY' => true,
|
||||
'THEN' => true,
|
||||
'TO' => true,
|
||||
'TRANSACTION' => true,
|
||||
'TRIGGER' => true,
|
||||
'UNION' => true,
|
||||
'UNIQUE' => true,
|
||||
'UPDATE' => true,
|
||||
'USING' => true,
|
||||
'VACUUM' => true,
|
||||
'VALUES' => true,
|
||||
'VIEW' => true,
|
||||
'VIRTUAL' => true,
|
||||
'WHEN' => true,
|
||||
'WHERE' => true,
|
||||
'WITH' => true,
|
||||
'WITHOUT' => true,
|
||||
];
|
||||
|
||||
$string = mb_strtoupper($string, 'UTF-8');
|
||||
if (!isset($keywords[$string])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$content = $string;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user