Added new (clean) yii boilerplate
This commit is contained in:
1610
framework/db/CDbCommand.php
Normal file
1610
framework/db/CDbCommand.php
Normal file
File diff suppressed because it is too large
Load Diff
823
framework/db/CDbConnection.php
Normal file
823
framework/db/CDbConnection.php
Normal file
@@ -0,0 +1,823 @@
|
||||
<?php
|
||||
/**
|
||||
* CDbConnection class file
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
/**
|
||||
* CDbConnection represents a connection to a database.
|
||||
*
|
||||
* CDbConnection works together with {@link CDbCommand}, {@link CDbDataReader}
|
||||
* and {@link CDbTransaction} to provide data access to various DBMS
|
||||
* in a common set of APIs. They are a thin wrapper of the {@link http://www.php.net/manual/en/ref.pdo.php PDO}
|
||||
* PHP extension.
|
||||
*
|
||||
* To establish a connection, set {@link setActive active} to true after
|
||||
* specifying {@link connectionString}, {@link username} and {@link password}.
|
||||
*
|
||||
* The following example shows how to create a CDbConnection instance and establish
|
||||
* the actual connection:
|
||||
* <pre>
|
||||
* $connection=new CDbConnection($dsn,$username,$password);
|
||||
* $connection->active=true;
|
||||
* </pre>
|
||||
*
|
||||
* After the DB connection is established, one can execute an SQL statement like the following:
|
||||
* <pre>
|
||||
* $command=$connection->createCommand($sqlStatement);
|
||||
* $command->execute(); // a non-query SQL statement execution
|
||||
* // or execute an SQL query and fetch the result set
|
||||
* $reader=$command->query();
|
||||
*
|
||||
* // each $row is an array representing a row of data
|
||||
* foreach($reader as $row) ...
|
||||
* </pre>
|
||||
*
|
||||
* One can do prepared SQL execution and bind parameters to the prepared SQL:
|
||||
* <pre>
|
||||
* $command=$connection->createCommand($sqlStatement);
|
||||
* $command->bindParam($name1,$value1);
|
||||
* $command->bindParam($name2,$value2);
|
||||
* $command->execute();
|
||||
* </pre>
|
||||
*
|
||||
* To use transaction, do like the following:
|
||||
* <pre>
|
||||
* $transaction=$connection->beginTransaction();
|
||||
* try
|
||||
* {
|
||||
* $connection->createCommand($sql1)->execute();
|
||||
* $connection->createCommand($sql2)->execute();
|
||||
* //.... other SQL executions
|
||||
* $transaction->commit();
|
||||
* }
|
||||
* catch(Exception $e)
|
||||
* {
|
||||
* $transaction->rollback();
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* CDbConnection also provides a set of methods to support setting and querying
|
||||
* of certain DBMS attributes, such as {@link getNullConversion nullConversion}.
|
||||
*
|
||||
* Since CDbConnection implements the interface IApplicationComponent, it can
|
||||
* be used as an application component and be configured in application configuration,
|
||||
* like the following,
|
||||
* <pre>
|
||||
* array(
|
||||
* 'components'=>array(
|
||||
* 'db'=>array(
|
||||
* 'class'=>'CDbConnection',
|
||||
* 'connectionString'=>'sqlite:path/to/dbfile',
|
||||
* ),
|
||||
* ),
|
||||
* )
|
||||
* </pre>
|
||||
*
|
||||
* @property boolean $active Whether the DB connection is established.
|
||||
* @property PDO $pdoInstance The PDO instance, null if the connection is not established yet.
|
||||
* @property CDbTransaction $currentTransaction The currently active transaction. Null if no active transaction.
|
||||
* @property CDbSchema $schema The database schema for the current connection.
|
||||
* @property CDbCommandBuilder $commandBuilder The command builder.
|
||||
* @property string $lastInsertID The row ID of the last row inserted, or the last value retrieved from the sequence object.
|
||||
* @property mixed $columnCase The case of the column names.
|
||||
* @property mixed $nullConversion How the null and empty strings are converted.
|
||||
* @property boolean $autoCommit Whether creating or updating a DB record will be automatically committed.
|
||||
* @property boolean $persistent Whether the connection is persistent or not.
|
||||
* @property string $driverName Name of the DB driver.
|
||||
* @property string $clientVersion The version information of the DB driver.
|
||||
* @property string $connectionStatus The status of the connection.
|
||||
* @property boolean $prefetch Whether the connection performs data prefetching.
|
||||
* @property string $serverInfo The information of DBMS server.
|
||||
* @property string $serverVersion The version information of DBMS server.
|
||||
* @property integer $timeout Timeout settings for the connection.
|
||||
* @property array $attributes Attributes (name=>value) that are previously explicitly set for the DB connection.
|
||||
* @property array $stats The first element indicates the number of SQL statements executed,
|
||||
* and the second element the total time spent in SQL execution.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @package system.db
|
||||
* @since 1.0
|
||||
*/
|
||||
class CDbConnection extends CApplicationComponent
|
||||
{
|
||||
/**
|
||||
* @var string The Data Source Name, or DSN, contains the information required to connect to the database.
|
||||
* @see http://www.php.net/manual/en/function.PDO-construct.php
|
||||
*
|
||||
* Note that if you're using GBK or BIG5 then it's highly recommended to
|
||||
* update to PHP 5.3.6+ and to specify charset via DSN like
|
||||
* 'mysql:dbname=mydatabase;host=127.0.0.1;charset=GBK;'.
|
||||
*/
|
||||
public $connectionString;
|
||||
/**
|
||||
* @var string the username for establishing DB connection. Defaults to empty string.
|
||||
*/
|
||||
public $username='';
|
||||
/**
|
||||
* @var string the password for establishing DB connection. Defaults to empty string.
|
||||
*/
|
||||
public $password='';
|
||||
/**
|
||||
* @var integer number of seconds that table metadata can remain valid in cache.
|
||||
* Use 0 or negative value to indicate not caching schema.
|
||||
* If greater than 0 and the primary cache is enabled, the table metadata will be cached.
|
||||
* @see schemaCachingExclude
|
||||
*/
|
||||
public $schemaCachingDuration=0;
|
||||
/**
|
||||
* @var array list of tables whose metadata should NOT be cached. Defaults to empty array.
|
||||
* @see schemaCachingDuration
|
||||
*/
|
||||
public $schemaCachingExclude=array();
|
||||
/**
|
||||
* @var string the ID of the cache application component that is used to cache the table metadata.
|
||||
* Defaults to 'cache' which refers to the primary cache application component.
|
||||
* Set this property to false if you want to disable caching table metadata.
|
||||
*/
|
||||
public $schemaCacheID='cache';
|
||||
/**
|
||||
* @var integer number of seconds that query results can remain valid in cache.
|
||||
* Use 0 or negative value to indicate not caching query results (the default behavior).
|
||||
*
|
||||
* In order to enable query caching, this property must be a positive
|
||||
* integer and {@link queryCacheID} must point to a valid cache component ID.
|
||||
*
|
||||
* The method {@link cache()} is provided as a convenient way of setting this property
|
||||
* and {@link queryCachingDependency} on the fly.
|
||||
*
|
||||
* @see cache
|
||||
* @see queryCachingDependency
|
||||
* @see queryCacheID
|
||||
* @since 1.1.7
|
||||
*/
|
||||
public $queryCachingDuration=0;
|
||||
/**
|
||||
* @var CCacheDependency|ICacheDependency the dependency that will be used when saving query results into cache.
|
||||
* @see queryCachingDuration
|
||||
* @since 1.1.7
|
||||
*/
|
||||
public $queryCachingDependency;
|
||||
/**
|
||||
* @var integer the number of SQL statements that need to be cached next.
|
||||
* If this is 0, then even if query caching is enabled, no query will be cached.
|
||||
* Note that each time after executing a SQL statement (whether executed on DB server or fetched from
|
||||
* query cache), this property will be reduced by 1 until 0.
|
||||
* @since 1.1.7
|
||||
*/
|
||||
public $queryCachingCount=0;
|
||||
/**
|
||||
* @var string the ID of the cache application component that is used for query caching.
|
||||
* Defaults to 'cache' which refers to the primary cache application component.
|
||||
* Set this property to false if you want to disable query caching.
|
||||
* @since 1.1.7
|
||||
*/
|
||||
public $queryCacheID='cache';
|
||||
/**
|
||||
* @var boolean whether the database connection should be automatically established
|
||||
* the component is being initialized. Defaults to true. Note, this property is only
|
||||
* effective when the CDbConnection object is used as an application component.
|
||||
*/
|
||||
public $autoConnect=true;
|
||||
/**
|
||||
* @var string the charset used for database connection. The property is only used
|
||||
* for MySQL and PostgreSQL databases. Defaults to null, meaning using default charset
|
||||
* as specified by the database.
|
||||
*
|
||||
* Note that if you're using GBK or BIG5 then it's highly recommended to
|
||||
* update to PHP 5.3.6+ and to specify charset via DSN like
|
||||
* 'mysql:dbname=mydatabase;host=127.0.0.1;charset=GBK;'.
|
||||
*/
|
||||
public $charset;
|
||||
/**
|
||||
* @var boolean whether to turn on prepare emulation. Defaults to false, meaning PDO
|
||||
* will use the native prepare support if available. For some databases (such as MySQL),
|
||||
* this may need to be set true so that PDO can emulate the prepare support to bypass
|
||||
* the buggy native prepare support. Note, this property is only effective for PHP 5.1.3 or above.
|
||||
* The default value is null, which will not change the ATTR_EMULATE_PREPARES value of PDO.
|
||||
*/
|
||||
public $emulatePrepare;
|
||||
/**
|
||||
* @var boolean whether to log the values that are bound to a prepare SQL statement.
|
||||
* Defaults to false. During development, you may consider setting this property to true
|
||||
* so that parameter values bound to SQL statements are logged for debugging purpose.
|
||||
* You should be aware that logging parameter values could be expensive and have significant
|
||||
* impact on the performance of your application.
|
||||
*/
|
||||
public $enableParamLogging=false;
|
||||
/**
|
||||
* @var boolean whether to enable profiling the SQL statements being executed.
|
||||
* Defaults to false. This should be mainly enabled and used during development
|
||||
* to find out the bottleneck of SQL executions.
|
||||
*/
|
||||
public $enableProfiling=false;
|
||||
/**
|
||||
* @var string the default prefix for table names. Defaults to null, meaning no table prefix.
|
||||
* By setting this property, any token like '{{tableName}}' in {@link CDbCommand::text} will
|
||||
* be replaced by 'prefixTableName', where 'prefix' refers to this property value.
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public $tablePrefix;
|
||||
/**
|
||||
* @var array list of SQL statements that should be executed right after the DB connection is established.
|
||||
* @since 1.1.1
|
||||
*/
|
||||
public $initSQLs;
|
||||
/**
|
||||
* @var array mapping between PDO driver and schema class name.
|
||||
* A schema class can be specified using path alias.
|
||||
* @since 1.1.6
|
||||
*/
|
||||
public $driverMap=array(
|
||||
'pgsql'=>'CPgsqlSchema', // PostgreSQL
|
||||
'mysqli'=>'CMysqlSchema', // MySQL
|
||||
'mysql'=>'CMysqlSchema', // MySQL
|
||||
'sqlite'=>'CSqliteSchema', // sqlite 3
|
||||
'sqlite2'=>'CSqliteSchema', // sqlite 2
|
||||
'mssql'=>'CMssqlSchema', // Mssql driver on windows hosts
|
||||
'dblib'=>'CMssqlSchema', // dblib drivers on linux (and maybe others os) hosts
|
||||
'sqlsrv'=>'CMssqlSchema', // Mssql
|
||||
'oci'=>'COciSchema', // Oracle driver
|
||||
);
|
||||
|
||||
/**
|
||||
* @var string Custom PDO wrapper class.
|
||||
* @since 1.1.8
|
||||
*/
|
||||
public $pdoClass = 'PDO';
|
||||
|
||||
private $_attributes=array();
|
||||
private $_active=false;
|
||||
private $_pdo;
|
||||
private $_transaction;
|
||||
private $_schema;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* Note, the DB connection is not established when this connection
|
||||
* instance is created. Set {@link setActive active} property to true
|
||||
* to establish the connection.
|
||||
* @param string $dsn The Data Source Name, or DSN, contains the information required to connect to the database.
|
||||
* @param string $username The user name for the DSN string.
|
||||
* @param string $password The password for the DSN string.
|
||||
* @see http://www.php.net/manual/en/function.PDO-construct.php
|
||||
*/
|
||||
public function __construct($dsn='',$username='',$password='')
|
||||
{
|
||||
$this->connectionString=$dsn;
|
||||
$this->username=$username;
|
||||
$this->password=$password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the connection when serializing.
|
||||
* @return array
|
||||
*/
|
||||
public function __sleep()
|
||||
{
|
||||
$this->close();
|
||||
return array_keys(get_object_vars($this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of available PDO drivers.
|
||||
* @return array list of available PDO drivers
|
||||
* @see http://www.php.net/manual/en/function.PDO-getAvailableDrivers.php
|
||||
*/
|
||||
public static function getAvailableDrivers()
|
||||
{
|
||||
return PDO::getAvailableDrivers();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the component.
|
||||
* This method is required by {@link IApplicationComponent} and is invoked by application
|
||||
* when the CDbConnection is used as an application component.
|
||||
* If you override this method, make sure to call the parent implementation
|
||||
* so that the component can be marked as initialized.
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
parent::init();
|
||||
if($this->autoConnect)
|
||||
$this->setActive(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the DB connection is established.
|
||||
* @return boolean whether the DB connection is established
|
||||
*/
|
||||
public function getActive()
|
||||
{
|
||||
return $this->_active;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open or close the DB connection.
|
||||
* @param boolean $value whether to open or close DB connection
|
||||
* @throws CException if connection fails
|
||||
*/
|
||||
public function setActive($value)
|
||||
{
|
||||
if($value!=$this->_active)
|
||||
{
|
||||
if($value)
|
||||
$this->open();
|
||||
else
|
||||
$this->close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the parameters about query caching.
|
||||
* This method can be used to enable or disable query caching.
|
||||
* By setting the $duration parameter to be 0, the query caching will be disabled.
|
||||
* Otherwise, query results of the new SQL statements executed next will be saved in cache
|
||||
* and remain valid for the specified duration.
|
||||
* If the same query is executed again, the result may be fetched from cache directly
|
||||
* without actually executing the SQL statement.
|
||||
* @param integer $duration the number of seconds that query results may remain valid in cache.
|
||||
* If this is 0, the caching will be disabled.
|
||||
* @param CCacheDependency|ICacheDependency $dependency the dependency that will be used when saving
|
||||
* the query results into cache.
|
||||
* @param integer $queryCount number of SQL queries that need to be cached after calling this method. Defaults to 1,
|
||||
* meaning that the next SQL query will be cached.
|
||||
* @return CDbConnection the connection instance itself.
|
||||
* @since 1.1.7
|
||||
*/
|
||||
public function cache($duration, $dependency=null, $queryCount=1)
|
||||
{
|
||||
$this->queryCachingDuration=$duration;
|
||||
$this->queryCachingDependency=$dependency;
|
||||
$this->queryCachingCount=$queryCount;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens DB connection if it is currently not
|
||||
* @throws CException if connection fails
|
||||
*/
|
||||
protected function open()
|
||||
{
|
||||
if($this->_pdo===null)
|
||||
{
|
||||
if(empty($this->connectionString))
|
||||
throw new CDbException('CDbConnection.connectionString cannot be empty.');
|
||||
try
|
||||
{
|
||||
Yii::trace('Opening DB connection','system.db.CDbConnection');
|
||||
$this->_pdo=$this->createPdoInstance();
|
||||
$this->initConnection($this->_pdo);
|
||||
$this->_active=true;
|
||||
}
|
||||
catch(PDOException $e)
|
||||
{
|
||||
if(YII_DEBUG)
|
||||
{
|
||||
throw new CDbException('CDbConnection failed to open the DB connection: '.
|
||||
$e->getMessage(),(int)$e->getCode(),$e->errorInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
Yii::log($e->getMessage(),CLogger::LEVEL_ERROR,'exception.CDbException');
|
||||
throw new CDbException('CDbConnection failed to open the DB connection.',(int)$e->getCode(),$e->errorInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the currently active DB connection.
|
||||
* It does nothing if the connection is already closed.
|
||||
*/
|
||||
protected function close()
|
||||
{
|
||||
Yii::trace('Closing DB connection','system.db.CDbConnection');
|
||||
$this->_pdo=null;
|
||||
$this->_active=false;
|
||||
$this->_schema=null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the PDO instance.
|
||||
* When some functionalities are missing in the pdo driver, we may use
|
||||
* an adapter class to provide them.
|
||||
* @throws CDbException when failed to open DB connection
|
||||
* @return PDO the pdo instance
|
||||
*/
|
||||
protected function createPdoInstance()
|
||||
{
|
||||
$pdoClass=$this->pdoClass;
|
||||
if(($pos=strpos($this->connectionString,':'))!==false)
|
||||
{
|
||||
$driver=strtolower(substr($this->connectionString,0,$pos));
|
||||
if($driver==='mssql' || $driver==='dblib')
|
||||
$pdoClass='CMssqlPdoAdapter';
|
||||
elseif($driver==='sqlsrv')
|
||||
$pdoClass='CMssqlSqlsrvPdoAdapter';
|
||||
}
|
||||
|
||||
if(!class_exists($pdoClass))
|
||||
throw new CDbException(Yii::t('yii','CDbConnection is unable to find PDO class "{className}". Make sure PDO is installed correctly.',
|
||||
array('{className}'=>$pdoClass)));
|
||||
|
||||
@$instance=new $pdoClass($this->connectionString,$this->username,$this->password,$this->_attributes);
|
||||
|
||||
if(!$instance)
|
||||
throw new CDbException(Yii::t('yii','CDbConnection failed to open the DB connection.'));
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the open db connection.
|
||||
* This method is invoked right after the db connection is established.
|
||||
* The default implementation is to set the charset for MySQL and PostgreSQL database connections.
|
||||
* @param PDO $pdo the PDO instance
|
||||
*/
|
||||
protected function initConnection($pdo)
|
||||
{
|
||||
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
if($this->emulatePrepare!==null && constant('PDO::ATTR_EMULATE_PREPARES'))
|
||||
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES,$this->emulatePrepare);
|
||||
if($this->charset!==null)
|
||||
{
|
||||
$driver=strtolower($pdo->getAttribute(PDO::ATTR_DRIVER_NAME));
|
||||
if(in_array($driver,array('pgsql','mysql','mysqli')))
|
||||
$pdo->exec('SET NAMES '.$pdo->quote($this->charset));
|
||||
}
|
||||
if($this->initSQLs!==null)
|
||||
{
|
||||
foreach($this->initSQLs as $sql)
|
||||
$pdo->exec($sql);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the PDO instance.
|
||||
* @return PDO the PDO instance, null if the connection is not established yet
|
||||
*/
|
||||
public function getPdoInstance()
|
||||
{
|
||||
return $this->_pdo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a command for execution.
|
||||
* @param mixed $query the DB query to be executed. This can be either a string representing a SQL statement,
|
||||
* or an array representing different fragments of a SQL statement. Please refer to {@link CDbCommand::__construct}
|
||||
* for more details about how to pass an array as the query. If this parameter is not given,
|
||||
* you will have to call query builder methods of {@link CDbCommand} to build the DB query.
|
||||
* @return CDbCommand the DB command
|
||||
*/
|
||||
public function createCommand($query=null)
|
||||
{
|
||||
$this->setActive(true);
|
||||
return new CDbCommand($this,$query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the currently active transaction.
|
||||
* @return CDbTransaction the currently active transaction. Null if no active transaction.
|
||||
*/
|
||||
public function getCurrentTransaction()
|
||||
{
|
||||
if($this->_transaction!==null)
|
||||
{
|
||||
if($this->_transaction->getActive())
|
||||
return $this->_transaction;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a transaction.
|
||||
* @return CDbTransaction the transaction initiated
|
||||
*/
|
||||
public function beginTransaction()
|
||||
{
|
||||
Yii::trace('Starting transaction','system.db.CDbConnection');
|
||||
$this->setActive(true);
|
||||
$this->_pdo->beginTransaction();
|
||||
return $this->_transaction=new CDbTransaction($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the database schema for the current connection
|
||||
* @throws CDbException if CDbConnection does not support reading schema for specified database driver
|
||||
* @return CDbSchema the database schema for the current connection
|
||||
*/
|
||||
public function getSchema()
|
||||
{
|
||||
if($this->_schema!==null)
|
||||
return $this->_schema;
|
||||
else
|
||||
{
|
||||
$driver=$this->getDriverName();
|
||||
if(isset($this->driverMap[$driver]))
|
||||
return $this->_schema=Yii::createComponent($this->driverMap[$driver], $this);
|
||||
else
|
||||
throw new CDbException(Yii::t('yii','CDbConnection does not support reading schema for {driver} database.',
|
||||
array('{driver}'=>$driver)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the SQL command builder for the current DB connection.
|
||||
* @return CDbCommandBuilder the command builder
|
||||
*/
|
||||
public function getCommandBuilder()
|
||||
{
|
||||
return $this->getSchema()->getCommandBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @see http://www.php.net/manual/en/function.PDO-lastInsertId.php
|
||||
*/
|
||||
public function getLastInsertID($sequenceName='')
|
||||
{
|
||||
$this->setActive(true);
|
||||
return $this->_pdo->lastInsertId($sequenceName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Quotes a string value for use in a query.
|
||||
* @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_int($str) || is_float($str))
|
||||
return $str;
|
||||
|
||||
$this->setActive(true);
|
||||
if(($value=$this->_pdo->quote($str))!==false)
|
||||
return $value;
|
||||
else // 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.
|
||||
* @param string $name table name
|
||||
* @return string the properly quoted table name
|
||||
*/
|
||||
public function quoteTableName($name)
|
||||
{
|
||||
return $this->getSchema()->quoteTableName($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Quotes a column name for use in a query.
|
||||
* If the column name contains prefix, the prefix will also be properly quoted.
|
||||
* @param string $name column name
|
||||
* @return string the properly quoted column name
|
||||
*/
|
||||
public function quoteColumnName($name)
|
||||
{
|
||||
return $this->getSchema()->quoteColumnName($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the PDO type for the specified PHP type.
|
||||
* @param string $type The PHP type (obtained by gettype() call).
|
||||
* @return integer the corresponding PDO type
|
||||
*/
|
||||
public function getPdoType($type)
|
||||
{
|
||||
static $map=array
|
||||
(
|
||||
'boolean'=>PDO::PARAM_BOOL,
|
||||
'integer'=>PDO::PARAM_INT,
|
||||
'string'=>PDO::PARAM_STR,
|
||||
'resource'=>PDO::PARAM_LOB,
|
||||
'NULL'=>PDO::PARAM_NULL,
|
||||
);
|
||||
return isset($map[$type]) ? $map[$type] : PDO::PARAM_STR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the case of the column names
|
||||
* @return mixed the case of the column names
|
||||
* @see http://www.php.net/manual/en/pdo.setattribute.php
|
||||
*/
|
||||
public function getColumnCase()
|
||||
{
|
||||
return $this->getAttribute(PDO::ATTR_CASE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the case of the column names.
|
||||
* @param mixed $value the case of the column names
|
||||
* @see http://www.php.net/manual/en/pdo.setattribute.php
|
||||
*/
|
||||
public function setColumnCase($value)
|
||||
{
|
||||
$this->setAttribute(PDO::ATTR_CASE,$value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns how the null and empty strings are converted.
|
||||
* @return mixed how the null and empty strings are converted
|
||||
* @see http://www.php.net/manual/en/pdo.setattribute.php
|
||||
*/
|
||||
public function getNullConversion()
|
||||
{
|
||||
return $this->getAttribute(PDO::ATTR_ORACLE_NULLS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets how the null and empty strings are converted.
|
||||
* @param mixed $value how the null and empty strings are converted
|
||||
* @see http://www.php.net/manual/en/pdo.setattribute.php
|
||||
*/
|
||||
public function setNullConversion($value)
|
||||
{
|
||||
$this->setAttribute(PDO::ATTR_ORACLE_NULLS,$value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether creating or updating a DB record will be automatically committed.
|
||||
* Some DBMS (such as sqlite) may not support this feature.
|
||||
* @return boolean whether creating or updating a DB record will be automatically committed.
|
||||
*/
|
||||
public function getAutoCommit()
|
||||
{
|
||||
return $this->getAttribute(PDO::ATTR_AUTOCOMMIT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether creating or updating a DB record will be automatically committed.
|
||||
* Some DBMS (such as sqlite) may not support this feature.
|
||||
* @param boolean $value whether creating or updating a DB record will be automatically committed.
|
||||
*/
|
||||
public function setAutoCommit($value)
|
||||
{
|
||||
$this->setAttribute(PDO::ATTR_AUTOCOMMIT,$value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the connection is persistent or not.
|
||||
* Some DBMS (such as sqlite) may not support this feature.
|
||||
* @return boolean whether the connection is persistent or not
|
||||
*/
|
||||
public function getPersistent()
|
||||
{
|
||||
return $this->getAttribute(PDO::ATTR_PERSISTENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the connection is persistent or not.
|
||||
* Some DBMS (such as sqlite) may not support this feature.
|
||||
* @param boolean $value whether the connection is persistent or not
|
||||
*/
|
||||
public function setPersistent($value)
|
||||
{
|
||||
return $this->setAttribute(PDO::ATTR_PERSISTENT,$value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the DB driver
|
||||
* @return string name of the DB driver
|
||||
*/
|
||||
public function getDriverName()
|
||||
{
|
||||
if(($pos=strpos($this->connectionString, ':'))!==false)
|
||||
return strtolower(substr($this->connectionString, 0, $pos));
|
||||
// return $this->getAttribute(PDO::ATTR_DRIVER_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the version information of the DB driver.
|
||||
* @return string the version information of the DB driver
|
||||
*/
|
||||
public function getClientVersion()
|
||||
{
|
||||
return $this->getAttribute(PDO::ATTR_CLIENT_VERSION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the status of the connection.
|
||||
* Some DBMS (such as sqlite) may not support this feature.
|
||||
* @return string the status of the connection
|
||||
*/
|
||||
public function getConnectionStatus()
|
||||
{
|
||||
return $this->getAttribute(PDO::ATTR_CONNECTION_STATUS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the connection performs data prefetching.
|
||||
* @return boolean whether the connection performs data prefetching
|
||||
*/
|
||||
public function getPrefetch()
|
||||
{
|
||||
return $this->getAttribute(PDO::ATTR_PREFETCH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the information of DBMS server.
|
||||
* @return string the information of DBMS server
|
||||
*/
|
||||
public function getServerInfo()
|
||||
{
|
||||
return $this->getAttribute(PDO::ATTR_SERVER_INFO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the version information of DBMS server.
|
||||
* @return string the version information of DBMS server
|
||||
*/
|
||||
public function getServerVersion()
|
||||
{
|
||||
return $this->getAttribute(PDO::ATTR_SERVER_VERSION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the timeout settings for the connection.
|
||||
* @return integer timeout settings for the connection
|
||||
*/
|
||||
public function getTimeout()
|
||||
{
|
||||
return $this->getAttribute(PDO::ATTR_TIMEOUT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains a specific DB connection attribute information.
|
||||
* @param integer $name the attribute to be queried
|
||||
* @return mixed the corresponding attribute information
|
||||
* @see http://www.php.net/manual/en/function.PDO-getAttribute.php
|
||||
*/
|
||||
public function getAttribute($name)
|
||||
{
|
||||
$this->setActive(true);
|
||||
return $this->_pdo->getAttribute($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an attribute on the database connection.
|
||||
* @param integer $name the attribute to be set
|
||||
* @param mixed $value the attribute value
|
||||
* @see http://www.php.net/manual/en/function.PDO-setAttribute.php
|
||||
*/
|
||||
public function setAttribute($name,$value)
|
||||
{
|
||||
if($this->_pdo instanceof PDO)
|
||||
$this->_pdo->setAttribute($name,$value);
|
||||
else
|
||||
$this->_attributes[$name]=$value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the attributes that are previously explicitly set for the DB connection.
|
||||
* @return array attributes (name=>value) that are previously explicitly set for the DB connection.
|
||||
* @see setAttributes
|
||||
* @since 1.1.7
|
||||
*/
|
||||
public function getAttributes()
|
||||
{
|
||||
return $this->_attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a set of attributes on the database connection.
|
||||
* @param array $values attributes (name=>value) to be set.
|
||||
* @see setAttribute
|
||||
* @since 1.1.7
|
||||
*/
|
||||
public function setAttributes($values)
|
||||
{
|
||||
foreach($values as $name=>$value)
|
||||
$this->_attributes[$name]=$value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the statistical results of SQL executions.
|
||||
* The results returned include the number of SQL statements executed and
|
||||
* the total time spent.
|
||||
* In order to use this method, {@link enableProfiling} has to be set true.
|
||||
* @return array the first element indicates the number of SQL statements executed,
|
||||
* and the second element the total time spent in SQL execution.
|
||||
*/
|
||||
public function getStats()
|
||||
{
|
||||
$logger=Yii::getLogger();
|
||||
$timings=$logger->getProfilingResults(null,'system.db.CDbCommand.query');
|
||||
$count=count($timings);
|
||||
$time=array_sum($timings);
|
||||
$timings=$logger->getProfilingResults(null,'system.db.CDbCommand.execute');
|
||||
$count+=count($timings);
|
||||
$time+=array_sum($timings);
|
||||
return array($count,$time);
|
||||
}
|
||||
}
|
||||
245
framework/db/CDbDataReader.php
Normal file
245
framework/db/CDbDataReader.php
Normal file
@@ -0,0 +1,245 @@
|
||||
<?php
|
||||
/**
|
||||
* CDbDataReader class file
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
/**
|
||||
* CDbDataReader represents a forward-only stream of rows from a query result set.
|
||||
*
|
||||
* To read the current row of data, call {@link read}. The method {@link readAll}
|
||||
* returns all the rows in a single array.
|
||||
*
|
||||
* One can also retrieve the rows of data in CDbDataReader by using foreach:
|
||||
* <pre>
|
||||
* foreach($reader as $row)
|
||||
* // $row represents a row of data
|
||||
* </pre>
|
||||
* Since CDbDataReader is a forward-only stream, you can only traverse it once.
|
||||
*
|
||||
* It is possible to use a specific mode of data fetching by setting
|
||||
* {@link setFetchMode FetchMode}. See {@link http://www.php.net/manual/en/function.PDOStatement-setFetchMode.php}
|
||||
* for more details.
|
||||
*
|
||||
* @property boolean $isClosed Whether the reader is closed or not.
|
||||
* @property integer $rowCount Number of rows contained in the result.
|
||||
* @property integer $columnCount The number of columns in the result set.
|
||||
* @property mixed $fetchMode Fetch mode.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @package system.db
|
||||
* @since 1.0
|
||||
*/
|
||||
class CDbDataReader extends CComponent implements Iterator, Countable
|
||||
{
|
||||
private $_statement;
|
||||
private $_closed=false;
|
||||
private $_row;
|
||||
private $_index=-1;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param CDbCommand $command the command generating the query result
|
||||
*/
|
||||
public function __construct(CDbCommand $command)
|
||||
{
|
||||
$this->_statement=$command->getPdoStatement();
|
||||
$this->_statement->setFetchMode(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 mixed $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 integer $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 mixed $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(array($this->_statement,'setFetchMode'),$params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Advances the reader to the next row in a result set.
|
||||
* @return array|false 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 integer $columnIndex zero-based column index
|
||||
* @return mixed|false the column of the current row, false if no more row 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|false 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 boolean 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 boolean 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 integer 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 integer 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 integer 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 CException if this method is invoked twice
|
||||
*/
|
||||
public function rewind()
|
||||
{
|
||||
if($this->_index<0)
|
||||
{
|
||||
$this->_row=$this->_statement->fetch();
|
||||
$this->_index=0;
|
||||
}
|
||||
else
|
||||
throw new CDbException(Yii::t('yii','CDbDataReader cannot rewind. It is a forward-only reader.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of the current row.
|
||||
* This method is required by the interface Iterator.
|
||||
* @return integer 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 boolean whether there is a row of data at current position.
|
||||
*/
|
||||
public function valid()
|
||||
{
|
||||
return $this->_row!==false;
|
||||
}
|
||||
}
|
||||
38
framework/db/CDbException.php
Normal file
38
framework/db/CDbException.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
/**
|
||||
* CDbException class file.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
/**
|
||||
* CDbException represents an exception that is caused by some DB-related operations.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @package system.db
|
||||
* @since 1.0
|
||||
*/
|
||||
class CDbException extends CException
|
||||
{
|
||||
/**
|
||||
* @var mixed the error info provided by a PDO exception. This is the same as returned
|
||||
* by {@link http://www.php.net/manual/en/pdo.errorinfo.php PDO::errorInfo}.
|
||||
* @since 1.1.4
|
||||
*/
|
||||
public $errorInfo;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param string $message PDO error message
|
||||
* @param integer $code PDO error code
|
||||
* @param mixed $errorInfo PDO error info
|
||||
*/
|
||||
public function __construct($message,$code=0,$errorInfo=null)
|
||||
{
|
||||
$this->errorInfo=$errorInfo;
|
||||
parent::__construct($message,$code);
|
||||
}
|
||||
}
|
||||
435
framework/db/CDbMigration.php
Normal file
435
framework/db/CDbMigration.php
Normal file
@@ -0,0 +1,435 @@
|
||||
<?php
|
||||
/**
|
||||
* CDbMigration class file.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
/**
|
||||
* CDbMigration is the base class for representing a database migration.
|
||||
*
|
||||
* CDbMigration is designed to be used together with the "yiic migrate" command.
|
||||
*
|
||||
* Each child class of CDbMigration represents an individual database migration which
|
||||
* is identified by the child class name.
|
||||
*
|
||||
* Within each migration, the {@link up} method contains the logic for "upgrading"
|
||||
* the database used in an application; while the {@link down} method contains "downgrading"
|
||||
* logic. The "yiic migrate" command manages all available migrations in an application.
|
||||
*
|
||||
* By overriding {@link safeUp} or {@link safeDown} methods instead of {@link up} and {@link down}
|
||||
* the migration logic will be wrapped with a DB transaction.
|
||||
*
|
||||
* CDbMigration provides a set of convenient methods for manipulating database data and schema.
|
||||
* For example, the {@link insert} method can be used to easily insert a row of data into
|
||||
* a database table; the {@link createTable} method can be used to create a database table.
|
||||
* Compared with the same methods in {@link CDbCommand}, these methods will display extra
|
||||
* information showing the method parameters and execution time, which may be useful when
|
||||
* applying migrations.
|
||||
*
|
||||
* @property CDbConnection $dbConnection The currently active database connection.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @package system.db
|
||||
* @since 1.1.6
|
||||
*/
|
||||
abstract class CDbMigration extends CComponent
|
||||
{
|
||||
private $_db;
|
||||
|
||||
/**
|
||||
* This method contains the logic to be executed when applying this migration.
|
||||
* Child classes may implement this method to provide actual migration logic.
|
||||
* @return boolean Returning false means, the migration will not be applied.
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
$transaction=$this->getDbConnection()->beginTransaction();
|
||||
try
|
||||
{
|
||||
if($this->safeUp()===false)
|
||||
{
|
||||
$transaction->rollback();
|
||||
return false;
|
||||
}
|
||||
$transaction->commit();
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
echo "Exception: ".$e->getMessage().' ('.$e->getFile().':'.$e->getLine().")\n";
|
||||
echo $e->getTraceAsString()."\n";
|
||||
$transaction->rollback();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method contains the logic to be executed when removing this migration.
|
||||
* Child classes may override this method if the corresponding migrations can be removed.
|
||||
* @return boolean Returning false means, the migration will not be applied.
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
$transaction=$this->getDbConnection()->beginTransaction();
|
||||
try
|
||||
{
|
||||
if($this->safeDown()===false)
|
||||
{
|
||||
$transaction->rollback();
|
||||
return false;
|
||||
}
|
||||
$transaction->commit();
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
echo "Exception: ".$e->getMessage().' ('.$e->getFile().':'.$e->getLine().")\n";
|
||||
echo $e->getTraceAsString()."\n";
|
||||
$transaction->rollback();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method contains the logic to be executed when applying this migration.
|
||||
* This method differs from {@link up} in that the DB logic implemented here will
|
||||
* be enclosed within a DB transaction.
|
||||
* Child classes may implement this method instead of {@link up} if the DB logic
|
||||
* needs to be within a transaction.
|
||||
* @return boolean Returning false means, the migration will not be applied and
|
||||
* the transaction will be rolled back.
|
||||
* @since 1.1.7
|
||||
*/
|
||||
public function safeUp()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* This method contains the logic to be executed when removing this migration.
|
||||
* This method differs from {@link down} in that the DB logic implemented here will
|
||||
* be enclosed within a DB transaction.
|
||||
* Child classes may implement this method instead of {@link up} if the DB logic
|
||||
* needs to be within a transaction.
|
||||
* @return boolean Returning false means, the migration will not be applied and
|
||||
* the transaction will be rolled back.
|
||||
* @since 1.1.7
|
||||
*/
|
||||
public function safeDown()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the currently active database connection.
|
||||
* By default, the 'db' application component will be returned and activated.
|
||||
* You can call {@link setDbConnection} to switch to a different database connection.
|
||||
* Methods such as {@link insert}, {@link createTable} will use this database connection
|
||||
* to perform DB queries.
|
||||
* @throws CException if "db" application component is not configured
|
||||
* @return CDbConnection the currently active database connection
|
||||
*/
|
||||
public function getDbConnection()
|
||||
{
|
||||
if($this->_db===null)
|
||||
{
|
||||
$this->_db=Yii::app()->getComponent('db');
|
||||
if(!$this->_db instanceof CDbConnection)
|
||||
throw new CException(Yii::t('yii', 'The "db" application component must be configured to be a CDbConnection object.'));
|
||||
}
|
||||
return $this->_db;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the currently active database connection.
|
||||
* The database connection will be used by the methods such as {@link insert}, {@link createTable}.
|
||||
* @param CDbConnection $db the database connection component
|
||||
*/
|
||||
public function setDbConnection($db)
|
||||
{
|
||||
$this->_db=$db;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a SQL statement.
|
||||
* This method executes the specified SQL statement using {@link dbConnection}.
|
||||
* @param string $sql the SQL statement to be executed
|
||||
* @param array $params input parameters (name=>value) for the SQL execution. See {@link CDbCommand::execute} for more details.
|
||||
* @since 1.1.7
|
||||
*/
|
||||
public function execute($sql, $params=array())
|
||||
{
|
||||
echo " > execute SQL: $sql ...";
|
||||
$time=microtime(true);
|
||||
$this->getDbConnection()->createCommand($sql)->execute($params);
|
||||
echo " done (time: ".sprintf('%.3f', microtime(true)-$time)."s)\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
echo " > insert into $table ...";
|
||||
$time=microtime(true);
|
||||
$this->getDbConnection()->createCommand()->insert($table, $columns);
|
||||
echo " done (time: ".sprintf('%.3f', microtime(true)-$time)."s)\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 mixed $conditions the conditions that will be put in the WHERE part. Please
|
||||
* refer to {@link CDbCommand::where} on how to specify conditions.
|
||||
* @param array $params the parameters to be bound to the query.
|
||||
*/
|
||||
public function update($table, $columns, $conditions='', $params=array())
|
||||
{
|
||||
echo " > update $table ...";
|
||||
$time=microtime(true);
|
||||
$this->getDbConnection()->createCommand()->update($table, $columns, $conditions, $params);
|
||||
echo " done (time: ".sprintf('%.3f', microtime(true)-$time)."s)\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and executes a DELETE SQL statement.
|
||||
* @param string $table the table where the data will be deleted from.
|
||||
* @param mixed $conditions the conditions that will be put in the WHERE part. Please
|
||||
* refer to {@link CDbCommand::where} on how to specify conditions.
|
||||
* @param array $params the parameters to be bound to the query.
|
||||
*/
|
||||
public function delete($table, $conditions='', $params=array())
|
||||
{
|
||||
echo " > delete from $table ...";
|
||||
$time=microtime(true);
|
||||
$this->getDbConnection()->createCommand()->delete($table, $conditions, $params);
|
||||
echo " done (time: ".sprintf('%.3f', microtime(true)-$time)."s)\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {@link 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
|
||||
* inserted 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)
|
||||
{
|
||||
echo " > create table $table ...";
|
||||
$time=microtime(true);
|
||||
$this->getDbConnection()->createCommand()->createTable($table, $columns, $options);
|
||||
echo " done (time: ".sprintf('%.3f', microtime(true)-$time)."s)\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
echo " > rename table $table to $newName ...";
|
||||
$time=microtime(true);
|
||||
$this->getDbConnection()->createCommand()->renameTable($table, $newName);
|
||||
echo " done (time: ".sprintf('%.3f', microtime(true)-$time)."s)\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
echo " > drop table $table ...";
|
||||
$time=microtime(true);
|
||||
$this->getDbConnection()->createCommand()->dropTable($table);
|
||||
echo " done (time: ".sprintf('%.3f', microtime(true)-$time)."s)\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
echo " > truncate table $table ...";
|
||||
$time=microtime(true);
|
||||
$this->getDbConnection()->createCommand()->truncateTable($table);
|
||||
echo " done (time: ".sprintf('%.3f', microtime(true)-$time)."s)\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {@link 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)
|
||||
{
|
||||
echo " > add column $column $type to table $table ...";
|
||||
$time=microtime(true);
|
||||
$this->getDbConnection()->createCommand()->addColumn($table, $column, $type);
|
||||
echo " done (time: ".sprintf('%.3f', microtime(true)-$time)."s)\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
echo " > drop column $column from table $table ...";
|
||||
$time=microtime(true);
|
||||
$this->getDbConnection()->createCommand()->dropColumn($table, $column);
|
||||
echo " done (time: ".sprintf('%.3f', microtime(true)-$time)."s)\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
echo " > rename column $name in table $table to $newName ...";
|
||||
$time=microtime(true);
|
||||
$this->getDbConnection()->createCommand()->renameColumn($table, $name, $newName);
|
||||
echo " done (time: ".sprintf('%.3f', microtime(true)-$time)."s)\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {@link 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)
|
||||
{
|
||||
echo " > alter column $column in table $table to $type ...";
|
||||
$time=microtime(true);
|
||||
$this->getDbConnection()->createCommand()->alterColumn($table, $column, $type);
|
||||
echo " done (time: ".sprintf('%.3f', microtime(true)-$time)."s)\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 $columns the name of the column to that the constraint will be added on. If there are multiple columns, separate them with commas.
|
||||
* @param string $refTable the table that the foreign key references to.
|
||||
* @param string $refColumns the name of the column that the foreign key references to. If there are multiple columns, separate them with commas.
|
||||
* @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)
|
||||
{
|
||||
echo " > add foreign key $name: $table ($columns) references $refTable ($refColumns) ...";
|
||||
$time=microtime(true);
|
||||
$this->getDbConnection()->createCommand()->addForeignKey($name, $table, $columns, $refTable, $refColumns, $delete, $update);
|
||||
echo " done (time: ".sprintf('%.3f', microtime(true)-$time)."s)\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
echo " > drop foreign key $name from table $table ...";
|
||||
$time=microtime(true);
|
||||
$this->getDbConnection()->createCommand()->dropForeignKey($name, $table);
|
||||
echo " done (time: ".sprintf('%.3f', microtime(true)-$time)."s)\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 $column the column(s) that should be included in the index. If there are multiple columns, please separate them
|
||||
* by commas. The column names will be properly quoted by the method.
|
||||
* @param boolean $unique whether to add UNIQUE constraint on the created index.
|
||||
*/
|
||||
public function createIndex($name, $table, $column, $unique=false)
|
||||
{
|
||||
echo " > create".($unique ? ' unique':'')." index $name on $table ($column) ...";
|
||||
$time=microtime(true);
|
||||
$this->getDbConnection()->createCommand()->createIndex($name, $table, $column, $unique);
|
||||
echo " done (time: ".sprintf('%.3f', microtime(true)-$time)."s)\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
echo " > drop index $name ...";
|
||||
$time=microtime(true);
|
||||
$this->getDbConnection()->createCommand()->dropIndex($name, $table);
|
||||
echo " done (time: ".sprintf('%.3f', microtime(true)-$time)."s)\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshed schema cache for a table
|
||||
* @param string $table name of the table to refresh
|
||||
* @since 1.1.9
|
||||
*/
|
||||
public function refreshTableSchema($table)
|
||||
{
|
||||
echo " > refresh table $table schema cache ...";
|
||||
$time=microtime(true);
|
||||
$this->getDbConnection()->getSchema()->getTable($table,true);
|
||||
echo " done (time: ".sprintf('%.3f', microtime(true)-$time)."s)\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and executes a SQL statement for creating a primary key, supports composite primary keys.
|
||||
* @param string $name name of the primary key constraint to add
|
||||
* @param string $table name of the table to add primary key to
|
||||
* @param string $columns name of the column to utilise as primary key. If there are multiple columns, separate them with commas.
|
||||
* @since 1.1.13
|
||||
*/
|
||||
public function addPrimaryKey($name,$table,$columns)
|
||||
{
|
||||
echo " > alter table $table add constraint $name primary key ($columns) ...";
|
||||
$time=microtime(true);
|
||||
$this->getDbConnection()->createCommand()->addPrimaryKey($name,$table,$columns);
|
||||
echo " done (time: ".sprintf('%.3f', microtime(true)-$time)."s)\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and executes a SQL statement for removing a primary key, supports composite primary keys.
|
||||
* @param string $name name of the constraint to remove
|
||||
* @param string $table name of the table to remove primary key from
|
||||
* @since 1.1.13
|
||||
*/
|
||||
public function dropPrimaryKey($name,$table)
|
||||
{
|
||||
echo " > alter table $table drop primary key $name ...";
|
||||
$time=microtime(true);
|
||||
$this->getDbConnection()->createCommand()->dropPrimaryKey($name,$table);
|
||||
echo " done (time: ".sprintf('%.3f', microtime(true)-$time)."s)\n";
|
||||
}
|
||||
}
|
||||
110
framework/db/CDbTransaction.php
Normal file
110
framework/db/CDbTransaction.php
Normal file
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
/**
|
||||
* CDbTransaction class file
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
/**
|
||||
* CDbTransaction represents a DB transaction.
|
||||
*
|
||||
* It is usually created by calling {@link CDbConnection::beginTransaction}.
|
||||
*
|
||||
* The following code is a common scenario of using transactions:
|
||||
* <pre>
|
||||
* $transaction=$connection->beginTransaction();
|
||||
* try
|
||||
* {
|
||||
* $connection->createCommand($sql1)->execute();
|
||||
* $connection->createCommand($sql2)->execute();
|
||||
* //.... other SQL executions
|
||||
* $transaction->commit();
|
||||
* }
|
||||
* catch(Exception $e)
|
||||
* {
|
||||
* $transaction->rollback();
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @property CDbConnection $connection The DB connection for this transaction.
|
||||
* @property boolean $active Whether this transaction is active.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @package system.db
|
||||
* @since 1.0
|
||||
*/
|
||||
class CDbTransaction extends CComponent
|
||||
{
|
||||
private $_connection=null;
|
||||
private $_active;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param CDbConnection $connection the connection associated with this transaction
|
||||
* @see CDbConnection::beginTransaction
|
||||
*/
|
||||
public function __construct(CDbConnection $connection)
|
||||
{
|
||||
$this->_connection=$connection;
|
||||
$this->_active=true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Commits a transaction.
|
||||
* @throws CException if the transaction or the DB connection is not active.
|
||||
*/
|
||||
public function commit()
|
||||
{
|
||||
if($this->_active && $this->_connection->getActive())
|
||||
{
|
||||
Yii::trace('Committing transaction','system.db.CDbTransaction');
|
||||
$this->_connection->getPdoInstance()->commit();
|
||||
$this->_active=false;
|
||||
}
|
||||
else
|
||||
throw new CDbException(Yii::t('yii','CDbTransaction is inactive and cannot perform commit or roll back operations.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Rolls back a transaction.
|
||||
* @throws CException if the transaction or the DB connection is not active.
|
||||
*/
|
||||
public function rollback()
|
||||
{
|
||||
if($this->_active && $this->_connection->getActive())
|
||||
{
|
||||
Yii::trace('Rolling back transaction','system.db.CDbTransaction');
|
||||
$this->_connection->getPdoInstance()->rollBack();
|
||||
$this->_active=false;
|
||||
}
|
||||
else
|
||||
throw new CDbException(Yii::t('yii','CDbTransaction is inactive and cannot perform commit or roll back operations.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CDbConnection the DB connection for this transaction
|
||||
*/
|
||||
public function getConnection()
|
||||
{
|
||||
return $this->_connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean whether this transaction is active
|
||||
*/
|
||||
public function getActive()
|
||||
{
|
||||
return $this->_active;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param boolean $value whether this transaction is active
|
||||
*/
|
||||
protected function setActive($value)
|
||||
{
|
||||
$this->_active=$value;
|
||||
}
|
||||
}
|
||||
1638
framework/db/ar/CActiveFinder.php
Normal file
1638
framework/db/ar/CActiveFinder.php
Normal file
File diff suppressed because it is too large
Load Diff
2436
framework/db/ar/CActiveRecord.php
Normal file
2436
framework/db/ar/CActiveRecord.php
Normal file
File diff suppressed because it is too large
Load Diff
114
framework/db/ar/CActiveRecordBehavior.php
Normal file
114
framework/db/ar/CActiveRecordBehavior.php
Normal file
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
/**
|
||||
* CActiveRecordBehavior class file.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
/**
|
||||
* CActiveRecordBehavior is the base class for behaviors that can be attached to {@link CActiveRecord}.
|
||||
* Compared with {@link CModelBehavior}, CActiveRecordBehavior attaches to more events
|
||||
* that are only defined by {@link CActiveRecord}.
|
||||
*
|
||||
* @property CActiveRecord $owner The owner AR that this behavior is attached to.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @package system.db.ar
|
||||
*/
|
||||
class CActiveRecordBehavior extends CModelBehavior
|
||||
{
|
||||
/**
|
||||
* Declares events and the corresponding event handler methods.
|
||||
* If you override this method, make sure you merge the parent result to the return value.
|
||||
* @return array events (array keys) and the corresponding event handler methods (array values).
|
||||
* @see CBehavior::events
|
||||
*/
|
||||
public function events()
|
||||
{
|
||||
return array_merge(parent::events(), array(
|
||||
'onBeforeSave'=>'beforeSave',
|
||||
'onAfterSave'=>'afterSave',
|
||||
'onBeforeDelete'=>'beforeDelete',
|
||||
'onAfterDelete'=>'afterDelete',
|
||||
'onBeforeFind'=>'beforeFind',
|
||||
'onAfterFind'=>'afterFind',
|
||||
'onBeforeCount'=>'beforeCount',
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Responds to {@link CActiveRecord::onBeforeSave} event.
|
||||
* Override this method and make it public if you want to handle the corresponding
|
||||
* event of the {@link CBehavior::owner owner}.
|
||||
* You may set {@link CModelEvent::isValid} to be false to quit the saving process.
|
||||
* @param CModelEvent $event event parameter
|
||||
*/
|
||||
protected function beforeSave($event)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Responds to {@link CActiveRecord::onAfterSave} event.
|
||||
* Override this method and make it public if you want to handle the corresponding event
|
||||
* of the {@link CBehavior::owner owner}.
|
||||
* @param CModelEvent $event event parameter
|
||||
*/
|
||||
protected function afterSave($event)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Responds to {@link CActiveRecord::onBeforeDelete} event.
|
||||
* Override this method and make it public if you want to handle the corresponding event
|
||||
* of the {@link CBehavior::owner owner}.
|
||||
* You may set {@link CModelEvent::isValid} to be false to quit the deletion process.
|
||||
* @param CEvent $event event parameter
|
||||
*/
|
||||
protected function beforeDelete($event)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Responds to {@link CActiveRecord::onAfterDelete} event.
|
||||
* Override this method and make it public if you want to handle the corresponding event
|
||||
* of the {@link CBehavior::owner owner}.
|
||||
* @param CEvent $event event parameter
|
||||
*/
|
||||
protected function afterDelete($event)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Responds to {@link CActiveRecord::onBeforeFind} event.
|
||||
* Override this method and make it public if you want to handle the corresponding event
|
||||
* of the {@link CBehavior::owner owner}.
|
||||
* @param CEvent $event event parameter
|
||||
*/
|
||||
protected function beforeFind($event)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Responds to {@link CActiveRecord::onAfterFind} event.
|
||||
* Override this method and make it public if you want to handle the corresponding event
|
||||
* of the {@link CBehavior::owner owner}.
|
||||
* @param CEvent $event event parameter
|
||||
*/
|
||||
protected function afterFind($event)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Responds to {@link CActiveRecord::onBeforeCount} event.
|
||||
* Override this method and make it public if you want to handle the corresponding event
|
||||
* of the {@link CBehavior::owner owner}.
|
||||
* @param CEvent $event event parameter
|
||||
* @since 1.1.14
|
||||
*/
|
||||
protected function beforeCount($event)
|
||||
{
|
||||
}
|
||||
}
|
||||
153
framework/db/schema/CDbColumnSchema.php
Normal file
153
framework/db/schema/CDbColumnSchema.php
Normal file
@@ -0,0 +1,153 @@
|
||||
<?php
|
||||
/**
|
||||
* CDbColumnSchema class file.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
/**
|
||||
* CDbColumnSchema class describes the column meta data of a database table.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @package system.db.schema
|
||||
* @since 1.0
|
||||
*/
|
||||
class CDbColumnSchema extends CComponent
|
||||
{
|
||||
/**
|
||||
* @var string name of this column (without quotes).
|
||||
*/
|
||||
public $name;
|
||||
/**
|
||||
* @var string raw name of this column. This is the quoted name that can be used in SQL queries.
|
||||
*/
|
||||
public $rawName;
|
||||
/**
|
||||
* @var boolean whether this column can be null.
|
||||
*/
|
||||
public $allowNull;
|
||||
/**
|
||||
* @var string the DB type of this column.
|
||||
*/
|
||||
public $dbType;
|
||||
/**
|
||||
* @var string the PHP type of this column.
|
||||
*/
|
||||
public $type;
|
||||
/**
|
||||
* @var mixed default value of this column
|
||||
*/
|
||||
public $defaultValue;
|
||||
/**
|
||||
* @var integer size of the column.
|
||||
*/
|
||||
public $size;
|
||||
/**
|
||||
* @var integer precision of the column data, if it is numeric.
|
||||
*/
|
||||
public $precision;
|
||||
/**
|
||||
* @var integer scale of the column data, if it is numeric.
|
||||
*/
|
||||
public $scale;
|
||||
/**
|
||||
* @var boolean whether this column is a primary key
|
||||
*/
|
||||
public $isPrimaryKey;
|
||||
/**
|
||||
* @var boolean whether this column is a foreign key
|
||||
*/
|
||||
public $isForeignKey;
|
||||
/**
|
||||
* @var boolean whether this column is auto-incremental
|
||||
* @since 1.1.7
|
||||
*/
|
||||
public $autoIncrement=false;
|
||||
/**
|
||||
* @var string comment of this column. Default value is empty string which means that no comment
|
||||
* has been set for the column. Null value means that RDBMS does not support column comments
|
||||
* at all (SQLite) or comment retrieval for the active RDBMS is not yet supported by the framework.
|
||||
* @since 1.1.13
|
||||
*/
|
||||
public $comment='';
|
||||
|
||||
/**
|
||||
* Initializes the column with its DB type and default value.
|
||||
* This sets up the column's PHP type, size, precision, scale as well as default value.
|
||||
* @param string $dbType the column's DB type
|
||||
* @param mixed $defaultValue the default value
|
||||
*/
|
||||
public function init($dbType, $defaultValue)
|
||||
{
|
||||
$this->dbType=$dbType;
|
||||
$this->extractType($dbType);
|
||||
$this->extractLimit($dbType);
|
||||
if($defaultValue!==null)
|
||||
$this->extractDefault($defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the PHP type from DB type.
|
||||
* @param string $dbType DB type
|
||||
*/
|
||||
protected function extractType($dbType)
|
||||
{
|
||||
if(stripos($dbType,'int')!==false && stripos($dbType,'unsigned int')===false)
|
||||
$this->type='integer';
|
||||
elseif(stripos($dbType,'bool')!==false)
|
||||
$this->type='boolean';
|
||||
elseif(preg_match('/(real|floa|doub)/i',$dbType))
|
||||
$this->type='double';
|
||||
else
|
||||
$this->type='string';
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts size, precision and scale information from column's DB type.
|
||||
* @param string $dbType the column's DB type
|
||||
*/
|
||||
protected function extractLimit($dbType)
|
||||
{
|
||||
if(strpos($dbType,'(') && preg_match('/\((.*)\)/',$dbType,$matches))
|
||||
{
|
||||
$values=explode(',',$matches[1]);
|
||||
$this->size=$this->precision=(int)$values[0];
|
||||
if(isset($values[1]))
|
||||
$this->scale=(int)$values[1];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the default value for the column.
|
||||
* The value is typecasted to correct PHP type.
|
||||
* @param mixed $defaultValue the default value obtained from metadata
|
||||
*/
|
||||
protected function extractDefault($defaultValue)
|
||||
{
|
||||
$this->defaultValue=$this->typecast($defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the input value to the type that this column is of.
|
||||
* @param mixed $value input value
|
||||
* @return mixed converted value
|
||||
*/
|
||||
public function typecast($value)
|
||||
{
|
||||
if(gettype($value)===$this->type || $value===null || $value instanceof CDbExpression)
|
||||
return $value;
|
||||
if($value==='' && $this->allowNull)
|
||||
return $this->type==='string' ? '' : null;
|
||||
switch($this->type)
|
||||
{
|
||||
case 'string': return (string)$value;
|
||||
case 'integer': return (integer)$value;
|
||||
case 'boolean': return (boolean)$value;
|
||||
case 'double':
|
||||
default: return $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
873
framework/db/schema/CDbCommandBuilder.php
Normal file
873
framework/db/schema/CDbCommandBuilder.php
Normal file
@@ -0,0 +1,873 @@
|
||||
<?php
|
||||
/**
|
||||
* CDbCommandBuilder class file.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
/**
|
||||
* CDbCommandBuilder provides basic methods to create query commands for tables.
|
||||
*
|
||||
* @property CDbConnection $dbConnection Database connection.
|
||||
* @property CDbSchema $schema The schema for this command builder.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @package system.db.schema
|
||||
* @since 1.0
|
||||
*/
|
||||
class CDbCommandBuilder extends CComponent
|
||||
{
|
||||
const PARAM_PREFIX=':yp';
|
||||
|
||||
private $_schema;
|
||||
private $_connection;
|
||||
|
||||
/**
|
||||
* @param CDbSchema $schema the schema for this command builder
|
||||
*/
|
||||
public function __construct($schema)
|
||||
{
|
||||
$this->_schema=$schema;
|
||||
$this->_connection=$schema->getDbConnection();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CDbConnection database connection.
|
||||
*/
|
||||
public function getDbConnection()
|
||||
{
|
||||
return $this->_connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CDbSchema the schema for this command builder.
|
||||
*/
|
||||
public function getSchema()
|
||||
{
|
||||
return $this->_schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last insertion ID for the specified table.
|
||||
* @param mixed $table the table schema ({@link CDbTableSchema}) or the table name (string).
|
||||
* @return mixed last insertion id. Null is returned if no sequence name.
|
||||
*/
|
||||
public function getLastInsertID($table)
|
||||
{
|
||||
$this->ensureTable($table);
|
||||
if($table->sequenceName!==null)
|
||||
return $this->_connection->getLastInsertID($table->sequenceName);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a SELECT command for a single table.
|
||||
* @param mixed $table the table schema ({@link CDbTableSchema}) or the table name (string).
|
||||
* @param CDbCriteria $criteria the query criteria
|
||||
* @param string $alias the alias name of the primary table. Defaults to 't'.
|
||||
* @return CDbCommand query command.
|
||||
*/
|
||||
public function createFindCommand($table,$criteria,$alias='t')
|
||||
{
|
||||
$this->ensureTable($table);
|
||||
$select=is_array($criteria->select) ? implode(', ',$criteria->select) : $criteria->select;
|
||||
if($criteria->alias!='')
|
||||
$alias=$criteria->alias;
|
||||
$alias=$this->_schema->quoteTableName($alias);
|
||||
|
||||
// issue 1432: need to expand * when SQL has JOIN
|
||||
if($select==='*' && !empty($criteria->join))
|
||||
{
|
||||
$prefix=$alias.'.';
|
||||
$select=array();
|
||||
foreach($table->getColumnNames() as $name)
|
||||
$select[]=$prefix.$this->_schema->quoteColumnName($name);
|
||||
$select=implode(', ',$select);
|
||||
}
|
||||
|
||||
$sql=($criteria->distinct ? 'SELECT DISTINCT':'SELECT')." {$select} FROM {$table->rawName} $alias";
|
||||
$sql=$this->applyJoin($sql,$criteria->join);
|
||||
$sql=$this->applyCondition($sql,$criteria->condition);
|
||||
$sql=$this->applyGroup($sql,$criteria->group);
|
||||
$sql=$this->applyHaving($sql,$criteria->having);
|
||||
$sql=$this->applyOrder($sql,$criteria->order);
|
||||
$sql=$this->applyLimit($sql,$criteria->limit,$criteria->offset);
|
||||
$command=$this->_connection->createCommand($sql);
|
||||
$this->bindValues($command,$criteria->params);
|
||||
return $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a COUNT(*) command for a single table.
|
||||
* @param mixed $table the table schema ({@link CDbTableSchema}) or the table name (string).
|
||||
* @param CDbCriteria $criteria the query criteria
|
||||
* @param string $alias the alias name of the primary table. Defaults to 't'.
|
||||
* @return CDbCommand query command.
|
||||
*/
|
||||
public function createCountCommand($table,$criteria,$alias='t')
|
||||
{
|
||||
$this->ensureTable($table);
|
||||
if($criteria->alias!='')
|
||||
$alias=$criteria->alias;
|
||||
$alias=$this->_schema->quoteTableName($alias);
|
||||
|
||||
if(!empty($criteria->group) || !empty($criteria->having))
|
||||
{
|
||||
$select=is_array($criteria->select) ? implode(', ',$criteria->select) : $criteria->select;
|
||||
if($criteria->alias!='')
|
||||
$alias=$criteria->alias;
|
||||
$sql=($criteria->distinct ? 'SELECT DISTINCT':'SELECT')." {$select} FROM {$table->rawName} $alias";
|
||||
$sql=$this->applyJoin($sql,$criteria->join);
|
||||
$sql=$this->applyCondition($sql,$criteria->condition);
|
||||
$sql=$this->applyGroup($sql,$criteria->group);
|
||||
$sql=$this->applyHaving($sql,$criteria->having);
|
||||
$sql="SELECT COUNT(*) FROM ($sql) sq";
|
||||
}
|
||||
else
|
||||
{
|
||||
if(is_string($criteria->select) && stripos($criteria->select,'count')===0)
|
||||
$sql="SELECT ".$criteria->select;
|
||||
elseif($criteria->distinct)
|
||||
{
|
||||
if(is_array($table->primaryKey))
|
||||
{
|
||||
$pk=array();
|
||||
foreach($table->primaryKey as $key)
|
||||
$pk[]=$alias.'.'.$key;
|
||||
$pk=implode(', ',$pk);
|
||||
}
|
||||
else
|
||||
$pk=$alias.'.'.$table->primaryKey;
|
||||
$sql="SELECT COUNT(DISTINCT $pk)";
|
||||
}
|
||||
else
|
||||
$sql="SELECT COUNT(*)";
|
||||
$sql.=" FROM {$table->rawName} $alias";
|
||||
$sql=$this->applyJoin($sql,$criteria->join);
|
||||
$sql=$this->applyCondition($sql,$criteria->condition);
|
||||
}
|
||||
|
||||
// Suppress binding of parameters belonging to the ORDER clause. Issue #1407.
|
||||
if($criteria->order && $criteria->params)
|
||||
{
|
||||
$params1=array();
|
||||
preg_match_all('/(:\w+)/',$sql,$params1);
|
||||
$params2=array();
|
||||
preg_match_all('/(:\w+)/',$this->applyOrder($sql,$criteria->order),$params2);
|
||||
foreach(array_diff($params2[0],$params1[0]) as $param)
|
||||
unset($criteria->params[$param]);
|
||||
}
|
||||
|
||||
// Do the same for SELECT part.
|
||||
if($criteria->select && $criteria->params)
|
||||
{
|
||||
$params1=array();
|
||||
preg_match_all('/(:\w+)/',$sql,$params1);
|
||||
$params2=array();
|
||||
preg_match_all('/(:\w+)/',$sql.' '.(is_array($criteria->select) ? implode(', ',$criteria->select) : $criteria->select),$params2);
|
||||
foreach(array_diff($params2[0],$params1[0]) as $param)
|
||||
unset($criteria->params[$param]);
|
||||
}
|
||||
|
||||
$command=$this->_connection->createCommand($sql);
|
||||
$this->bindValues($command,$criteria->params);
|
||||
return $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a DELETE command.
|
||||
* @param mixed $table the table schema ({@link CDbTableSchema}) or the table name (string).
|
||||
* @param CDbCriteria $criteria the query criteria
|
||||
* @return CDbCommand delete command.
|
||||
*/
|
||||
public function createDeleteCommand($table,$criteria)
|
||||
{
|
||||
$this->ensureTable($table);
|
||||
$sql="DELETE FROM {$table->rawName}";
|
||||
$sql=$this->applyJoin($sql,$criteria->join);
|
||||
$sql=$this->applyCondition($sql,$criteria->condition);
|
||||
$sql=$this->applyGroup($sql,$criteria->group);
|
||||
$sql=$this->applyHaving($sql,$criteria->having);
|
||||
$sql=$this->applyOrder($sql,$criteria->order);
|
||||
$sql=$this->applyLimit($sql,$criteria->limit,$criteria->offset);
|
||||
$command=$this->_connection->createCommand($sql);
|
||||
$this->bindValues($command,$criteria->params);
|
||||
return $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an INSERT command.
|
||||
* @param mixed $table the table schema ({@link CDbTableSchema}) or the table name (string).
|
||||
* @param array $data data to be inserted (column name=>column value). If a key is not a valid column name, the corresponding value will be ignored.
|
||||
* @return CDbCommand insert command
|
||||
*/
|
||||
public function createInsertCommand($table,$data)
|
||||
{
|
||||
$this->ensureTable($table);
|
||||
$fields=array();
|
||||
$values=array();
|
||||
$placeholders=array();
|
||||
$i=0;
|
||||
foreach($data as $name=>$value)
|
||||
{
|
||||
if(($column=$table->getColumn($name))!==null && ($value!==null || $column->allowNull))
|
||||
{
|
||||
$fields[]=$column->rawName;
|
||||
if($value instanceof CDbExpression)
|
||||
{
|
||||
$placeholders[]=$value->expression;
|
||||
foreach($value->params as $n=>$v)
|
||||
$values[$n]=$v;
|
||||
}
|
||||
else
|
||||
{
|
||||
$placeholders[]=self::PARAM_PREFIX.$i;
|
||||
$values[self::PARAM_PREFIX.$i]=$column->typecast($value);
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if($fields===array())
|
||||
{
|
||||
$pks=is_array($table->primaryKey) ? $table->primaryKey : array($table->primaryKey);
|
||||
foreach($pks as $pk)
|
||||
{
|
||||
$fields[]=$table->getColumn($pk)->rawName;
|
||||
$placeholders[]=$this->getIntegerPrimaryKeyDefaultValue();
|
||||
}
|
||||
}
|
||||
$sql="INSERT INTO {$table->rawName} (".implode(', ',$fields).') VALUES ('.implode(', ',$placeholders).')';
|
||||
$command=$this->_connection->createCommand($sql);
|
||||
|
||||
foreach($values as $name=>$value)
|
||||
$command->bindValue($name,$value);
|
||||
|
||||
return $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a multiple INSERT command.
|
||||
* This method could be used to achieve better performance during insertion of the large
|
||||
* amount of data into the database tables.
|
||||
* @param mixed $table the table schema ({@link CDbTableSchema}) or the table name (string).
|
||||
* @param array[] $data list data to be inserted, each value should be an array in format (column name=>column value).
|
||||
* If a key is not a valid column name, the corresponding value will be ignored.
|
||||
* @return CDbCommand multiple insert command
|
||||
* @since 1.1.14
|
||||
*/
|
||||
public function createMultipleInsertCommand($table,array $data)
|
||||
{
|
||||
return $this->composeMultipleInsertCommand($table,$data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a multiple INSERT command.
|
||||
* This method compose the SQL expression via given part templates, providing ability to adjust
|
||||
* command for different SQL syntax.
|
||||
* @param mixed $table the table schema ({@link CDbTableSchema}) or the table name (string).
|
||||
* @param array[] $data list data to be inserted, each value should be an array in format (column name=>column value).
|
||||
* If a key is not a valid column name, the corresponding value will be ignored.
|
||||
* @param array $templates templates for the SQL parts.
|
||||
* @return CDbCommand multiple insert command
|
||||
*/
|
||||
protected function composeMultipleInsertCommand($table,array $data,array $templates=array())
|
||||
{
|
||||
$templates=array_merge(
|
||||
array(
|
||||
'main'=>'INSERT INTO {{tableName}} ({{columnInsertNames}}) VALUES {{rowInsertValues}}',
|
||||
'columnInsertValue'=>'{{value}}',
|
||||
'columnInsertValueGlue'=>', ',
|
||||
'rowInsertValue'=>'({{columnInsertValues}})',
|
||||
'rowInsertValueGlue'=>', ',
|
||||
'columnInsertNameGlue'=>', ',
|
||||
),
|
||||
$templates
|
||||
);
|
||||
$this->ensureTable($table);
|
||||
$tableName=$this->getDbConnection()->quoteTableName($table->name);
|
||||
$params=array();
|
||||
$columnInsertNames=array();
|
||||
$rowInsertValues=array();
|
||||
|
||||
$columns=array();
|
||||
foreach($data as $rowData)
|
||||
{
|
||||
foreach($rowData as $columnName=>$columnValue)
|
||||
{
|
||||
if(!in_array($columnName,$columns,true))
|
||||
if($table->getColumn($columnName)!==null)
|
||||
$columns[]=$columnName;
|
||||
}
|
||||
}
|
||||
foreach($columns as $name)
|
||||
$columnInsertNames[$name]=$this->getDbConnection()->quoteColumnName($name);
|
||||
$columnInsertNamesSqlPart=implode($templates['columnInsertNameGlue'],$columnInsertNames);
|
||||
|
||||
foreach($data as $rowKey=>$rowData)
|
||||
{
|
||||
$columnInsertValues=array();
|
||||
foreach($columns as $columnName)
|
||||
{
|
||||
$column=$table->getColumn($columnName);
|
||||
$columnValue=array_key_exists($columnName,$rowData) ? $rowData[$columnName] : new CDbExpression('NULL');
|
||||
if($columnValue instanceof CDbExpression)
|
||||
{
|
||||
$columnInsertValue=$columnValue->expression;
|
||||
foreach($columnValue->params as $columnValueParamName=>$columnValueParam)
|
||||
$params[$columnValueParamName]=$columnValueParam;
|
||||
}
|
||||
else
|
||||
{
|
||||
$columnInsertValue=':'.$columnName.'_'.$rowKey;
|
||||
$params[':'.$columnName.'_'.$rowKey]=$column->typecast($columnValue);
|
||||
}
|
||||
$columnInsertValues[]=strtr($templates['columnInsertValue'],array(
|
||||
'{{column}}'=>$columnInsertNames[$columnName],
|
||||
'{{value}}'=>$columnInsertValue,
|
||||
));
|
||||
}
|
||||
$rowInsertValues[]=strtr($templates['rowInsertValue'],array(
|
||||
'{{tableName}}'=>$tableName,
|
||||
'{{columnInsertNames}}'=>$columnInsertNamesSqlPart,
|
||||
'{{columnInsertValues}}'=>implode($templates['columnInsertValueGlue'],$columnInsertValues)
|
||||
));
|
||||
}
|
||||
|
||||
$sql=strtr($templates['main'],array(
|
||||
'{{tableName}}'=>$tableName,
|
||||
'{{columnInsertNames}}'=>$columnInsertNamesSqlPart,
|
||||
'{{rowInsertValues}}'=>implode($templates['rowInsertValueGlue'], $rowInsertValues),
|
||||
));
|
||||
$command=$this->getDbConnection()->createCommand($sql);
|
||||
|
||||
foreach($params as $name=>$value)
|
||||
$command->bindValue($name,$value);
|
||||
|
||||
return $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an UPDATE command.
|
||||
* @param mixed $table the table schema ({@link CDbTableSchema}) or the table name (string).
|
||||
* @param array $data list of columns to be updated (name=>value)
|
||||
* @param CDbCriteria $criteria the query criteria
|
||||
* @throws CDbException if no columns are being updated for the given table
|
||||
* @return CDbCommand update command.
|
||||
*/
|
||||
public function createUpdateCommand($table,$data,$criteria)
|
||||
{
|
||||
$this->ensureTable($table);
|
||||
$fields=array();
|
||||
$values=array();
|
||||
$bindByPosition=isset($criteria->params[0]);
|
||||
$i=0;
|
||||
foreach($data as $name=>$value)
|
||||
{
|
||||
if(($column=$table->getColumn($name))!==null)
|
||||
{
|
||||
if($value instanceof CDbExpression)
|
||||
{
|
||||
$fields[]=$column->rawName.'='.$value->expression;
|
||||
foreach($value->params as $n=>$v)
|
||||
$values[$n]=$v;
|
||||
}
|
||||
elseif($bindByPosition)
|
||||
{
|
||||
$fields[]=$column->rawName.'=?';
|
||||
$values[]=$column->typecast($value);
|
||||
}
|
||||
else
|
||||
{
|
||||
$fields[]=$column->rawName.'='.self::PARAM_PREFIX.$i;
|
||||
$values[self::PARAM_PREFIX.$i]=$column->typecast($value);
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if($fields===array())
|
||||
throw new CDbException(Yii::t('yii','No columns are being updated for table "{table}".',
|
||||
array('{table}'=>$table->name)));
|
||||
$sql="UPDATE {$table->rawName} SET ".implode(', ',$fields);
|
||||
$sql=$this->applyJoin($sql,$criteria->join);
|
||||
$sql=$this->applyCondition($sql,$criteria->condition);
|
||||
$sql=$this->applyOrder($sql,$criteria->order);
|
||||
$sql=$this->applyLimit($sql,$criteria->limit,$criteria->offset);
|
||||
|
||||
$command=$this->_connection->createCommand($sql);
|
||||
$this->bindValues($command,array_merge($values,$criteria->params));
|
||||
|
||||
return $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an UPDATE command that increments/decrements certain columns.
|
||||
* @param mixed $table the table schema ({@link CDbTableSchema}) or the table name (string).
|
||||
* @param array $counters counters to be updated (counter increments/decrements indexed by column names.)
|
||||
* @param CDbCriteria $criteria the query criteria
|
||||
* @throws CDbException if no columns are being updated for the given table
|
||||
* @return CDbCommand the created command
|
||||
*/
|
||||
public function createUpdateCounterCommand($table,$counters,$criteria)
|
||||
{
|
||||
$this->ensureTable($table);
|
||||
$fields=array();
|
||||
foreach($counters as $name=>$value)
|
||||
{
|
||||
if(($column=$table->getColumn($name))!==null)
|
||||
{
|
||||
$value=(float)$value;
|
||||
if($value<0)
|
||||
$fields[]="{$column->rawName}={$column->rawName}-".(-$value);
|
||||
else
|
||||
$fields[]="{$column->rawName}={$column->rawName}+".$value;
|
||||
}
|
||||
}
|
||||
if($fields!==array())
|
||||
{
|
||||
$sql="UPDATE {$table->rawName} SET ".implode(', ',$fields);
|
||||
$sql=$this->applyJoin($sql,$criteria->join);
|
||||
$sql=$this->applyCondition($sql,$criteria->condition);
|
||||
$sql=$this->applyOrder($sql,$criteria->order);
|
||||
$sql=$this->applyLimit($sql,$criteria->limit,$criteria->offset);
|
||||
$command=$this->_connection->createCommand($sql);
|
||||
$this->bindValues($command,$criteria->params);
|
||||
return $command;
|
||||
}
|
||||
else
|
||||
throw new CDbException(Yii::t('yii','No counter columns are being updated for table "{table}".',
|
||||
array('{table}'=>$table->name)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a command based on a given SQL statement.
|
||||
* @param string $sql the explicitly specified SQL statement
|
||||
* @param array $params parameters that will be bound to the SQL statement
|
||||
* @return CDbCommand the created command
|
||||
*/
|
||||
public function createSqlCommand($sql,$params=array())
|
||||
{
|
||||
$command=$this->_connection->createCommand($sql);
|
||||
$this->bindValues($command,$params);
|
||||
return $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alters the SQL to apply JOIN clause.
|
||||
* @param string $sql the SQL statement to be altered
|
||||
* @param string $join the JOIN clause (starting with join type, such as INNER JOIN)
|
||||
* @return string the altered SQL statement
|
||||
*/
|
||||
public function applyJoin($sql,$join)
|
||||
{
|
||||
if($join!='')
|
||||
return $sql.' '.$join;
|
||||
else
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alters the SQL to apply WHERE clause.
|
||||
* @param string $sql the SQL statement without WHERE clause
|
||||
* @param string $condition the WHERE clause (without WHERE keyword)
|
||||
* @return string the altered SQL statement
|
||||
*/
|
||||
public function applyCondition($sql,$condition)
|
||||
{
|
||||
if($condition!='')
|
||||
return $sql.' WHERE '.$condition;
|
||||
else
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alters the SQL to apply ORDER BY.
|
||||
* @param string $sql SQL statement without ORDER BY.
|
||||
* @param string $orderBy column ordering
|
||||
* @return string modified SQL applied with ORDER BY.
|
||||
*/
|
||||
public function applyOrder($sql,$orderBy)
|
||||
{
|
||||
if($orderBy!='')
|
||||
return $sql.' ORDER BY '.$orderBy;
|
||||
else
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alters the SQL to apply LIMIT and OFFSET.
|
||||
* Default implementation is applicable for PostgreSQL, MySQL and SQLite.
|
||||
* @param string $sql SQL query string without LIMIT and OFFSET.
|
||||
* @param integer $limit maximum number of rows, -1 to ignore limit.
|
||||
* @param integer $offset row offset, -1 to ignore offset.
|
||||
* @return string SQL with LIMIT and OFFSET
|
||||
*/
|
||||
public function applyLimit($sql,$limit,$offset)
|
||||
{
|
||||
if($limit>=0)
|
||||
$sql.=' LIMIT '.(int)$limit;
|
||||
if($offset>0)
|
||||
$sql.=' OFFSET '.(int)$offset;
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alters the SQL to apply GROUP BY.
|
||||
* @param string $sql SQL query string without GROUP BY.
|
||||
* @param string $group GROUP BY
|
||||
* @return string SQL with GROUP BY.
|
||||
*/
|
||||
public function applyGroup($sql,$group)
|
||||
{
|
||||
if($group!='')
|
||||
return $sql.' GROUP BY '.$group;
|
||||
else
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alters the SQL to apply HAVING.
|
||||
* @param string $sql SQL query string without HAVING
|
||||
* @param string $having HAVING
|
||||
* @return string SQL with HAVING
|
||||
*/
|
||||
public function applyHaving($sql,$having)
|
||||
{
|
||||
if($having!='')
|
||||
return $sql.' HAVING '.$having;
|
||||
else
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds parameter values for an SQL command.
|
||||
* @param CDbCommand $command database command
|
||||
* @param array $values values for binding (integer-indexed array for question mark placeholders, string-indexed array for named placeholders)
|
||||
*/
|
||||
public function bindValues($command, $values)
|
||||
{
|
||||
if(($n=count($values))===0)
|
||||
return;
|
||||
if(isset($values[0])) // question mark placeholders
|
||||
{
|
||||
for($i=0;$i<$n;++$i)
|
||||
$command->bindValue($i+1,$values[$i]);
|
||||
}
|
||||
else // named placeholders
|
||||
{
|
||||
foreach($values as $name=>$value)
|
||||
{
|
||||
if($name[0]!==':')
|
||||
$name=':'.$name;
|
||||
$command->bindValue($name,$value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a query criteria.
|
||||
* @param mixed $condition query condition or criteria.
|
||||
* If a string, it is treated as query condition (the WHERE clause);
|
||||
* If an array, it is treated as the initial values for constructing a {@link CDbCriteria} object;
|
||||
* Otherwise, it should be an instance of {@link CDbCriteria}.
|
||||
* @param array $params parameters to be bound to an SQL statement.
|
||||
* This is only used when the first parameter is a string (query condition).
|
||||
* In other cases, please use {@link CDbCriteria::params} to set parameters.
|
||||
* @return CDbCriteria the created query criteria
|
||||
* @throws CException if the condition is not string, array and CDbCriteria
|
||||
*/
|
||||
public function createCriteria($condition='',$params=array())
|
||||
{
|
||||
if(is_array($condition))
|
||||
$criteria=new CDbCriteria($condition);
|
||||
elseif($condition instanceof CDbCriteria)
|
||||
$criteria=clone $condition;
|
||||
else
|
||||
{
|
||||
$criteria=new CDbCriteria;
|
||||
$criteria->condition=$condition;
|
||||
$criteria->params=$params;
|
||||
}
|
||||
return $criteria;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a query criteria with the specified primary key.
|
||||
* @param mixed $table the table schema ({@link CDbTableSchema}) or the table name (string).
|
||||
* @param mixed $pk primary key value(s). Use array for multiple primary keys. For composite key, each key value must be an array (column name=>column value).
|
||||
* @param mixed $condition query condition or criteria.
|
||||
* If a string, it is treated as query condition;
|
||||
* If an array, it is treated as the initial values for constructing a {@link CDbCriteria};
|
||||
* Otherwise, it should be an instance of {@link CDbCriteria}.
|
||||
* @param array $params parameters to be bound to an SQL statement.
|
||||
* This is only used when the second parameter is a string (query condition).
|
||||
* In other cases, please use {@link CDbCriteria::params} to set parameters.
|
||||
* @param string $prefix column prefix (ended with dot). If null, it will be the table name
|
||||
* @return CDbCriteria the created query criteria
|
||||
*/
|
||||
public function createPkCriteria($table,$pk,$condition='',$params=array(),$prefix=null)
|
||||
{
|
||||
$this->ensureTable($table);
|
||||
$criteria=$this->createCriteria($condition,$params);
|
||||
if($criteria->alias!='')
|
||||
$prefix=$this->_schema->quoteTableName($criteria->alias).'.';
|
||||
if(!is_array($pk)) // single key
|
||||
$pk=array($pk);
|
||||
if(is_array($table->primaryKey) && !isset($pk[0]) && $pk!==array()) // single composite key
|
||||
$pk=array($pk);
|
||||
$condition=$this->createInCondition($table,$table->primaryKey,$pk,$prefix);
|
||||
if($criteria->condition!='')
|
||||
$criteria->condition=$condition.' AND ('.$criteria->condition.')';
|
||||
else
|
||||
$criteria->condition=$condition;
|
||||
|
||||
return $criteria;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the expression for selecting rows of specified primary key values.
|
||||
* @param mixed $table the table schema ({@link CDbTableSchema}) or the table name (string).
|
||||
* @param array $values list of primary key values to be selected within
|
||||
* @param string $prefix column prefix (ended with dot). If null, it will be the table name
|
||||
* @return string the expression for selection
|
||||
*/
|
||||
public function createPkCondition($table,$values,$prefix=null)
|
||||
{
|
||||
$this->ensureTable($table);
|
||||
return $this->createInCondition($table,$table->primaryKey,$values,$prefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a query criteria with the specified column values.
|
||||
* @param mixed $table the table schema ({@link CDbTableSchema}) or the table name (string).
|
||||
* @param array $columns column values that should be matched in the query (name=>value)
|
||||
* @param mixed $condition query condition or criteria.
|
||||
* If a string, it is treated as query condition;
|
||||
* If an array, it is treated as the initial values for constructing a {@link CDbCriteria};
|
||||
* Otherwise, it should be an instance of {@link CDbCriteria}.
|
||||
* @param array $params parameters to be bound to an SQL statement.
|
||||
* This is only used when the third parameter is a string (query condition).
|
||||
* In other cases, please use {@link CDbCriteria::params} to set parameters.
|
||||
* @param string $prefix column prefix (ended with dot). If null, it will be the table name
|
||||
* @throws CDbException if specified column is not found in given table
|
||||
* @return CDbCriteria the created query criteria
|
||||
*/
|
||||
public function createColumnCriteria($table,$columns,$condition='',$params=array(),$prefix=null)
|
||||
{
|
||||
$this->ensureTable($table);
|
||||
$criteria=$this->createCriteria($condition,$params);
|
||||
if($criteria->alias!='')
|
||||
$prefix=$this->_schema->quoteTableName($criteria->alias).'.';
|
||||
$bindByPosition=isset($criteria->params[0]);
|
||||
$conditions=array();
|
||||
$values=array();
|
||||
$i=0;
|
||||
if($prefix===null)
|
||||
$prefix=$table->rawName.'.';
|
||||
foreach($columns as $name=>$value)
|
||||
{
|
||||
if(($column=$table->getColumn($name))!==null)
|
||||
{
|
||||
if(is_array($value))
|
||||
$conditions[]=$this->createInCondition($table,$name,$value,$prefix);
|
||||
elseif($value!==null)
|
||||
{
|
||||
if($bindByPosition)
|
||||
{
|
||||
$conditions[]=$prefix.$column->rawName.'=?';
|
||||
$values[]=$value;
|
||||
}
|
||||
else
|
||||
{
|
||||
$conditions[]=$prefix.$column->rawName.'='.self::PARAM_PREFIX.$i;
|
||||
$values[self::PARAM_PREFIX.$i]=$value;
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
else
|
||||
$conditions[]=$prefix.$column->rawName.' IS NULL';
|
||||
}
|
||||
else
|
||||
throw new CDbException(Yii::t('yii','Table "{table}" does not have a column named "{column}".',
|
||||
array('{table}'=>$table->name,'{column}'=>$name)));
|
||||
}
|
||||
$criteria->params=array_merge($values,$criteria->params);
|
||||
if(isset($conditions[0]))
|
||||
{
|
||||
if($criteria->condition!='')
|
||||
$criteria->condition=implode(' AND ',$conditions).' AND ('.$criteria->condition.')';
|
||||
else
|
||||
$criteria->condition=implode(' AND ',$conditions);
|
||||
}
|
||||
return $criteria;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the expression for searching the specified keywords within a list of columns.
|
||||
* The search expression is generated using the 'LIKE' SQL syntax.
|
||||
* Every word in the keywords must be present and appear in at least one of the columns.
|
||||
* @param mixed $table the table schema ({@link CDbTableSchema}) or the table name (string).
|
||||
* @param array $columns list of column names for potential search condition.
|
||||
* @param mixed $keywords search keywords. This can be either a string with space-separated keywords or an array of keywords.
|
||||
* @param string $prefix optional column prefix (with dot at the end). If null, the table name will be used as the prefix.
|
||||
* @param boolean $caseSensitive whether the search is case-sensitive. Defaults to true.
|
||||
* @throws CDbException if specified column is not found in given table
|
||||
* @return string SQL search condition matching on a set of columns. An empty string is returned
|
||||
* if either the column array or the keywords are empty.
|
||||
*/
|
||||
public function createSearchCondition($table,$columns,$keywords,$prefix=null,$caseSensitive=true)
|
||||
{
|
||||
$this->ensureTable($table);
|
||||
if(!is_array($keywords))
|
||||
$keywords=preg_split('/\s+/u',$keywords,-1,PREG_SPLIT_NO_EMPTY);
|
||||
if(empty($keywords))
|
||||
return '';
|
||||
if($prefix===null)
|
||||
$prefix=$table->rawName.'.';
|
||||
$conditions=array();
|
||||
foreach($columns as $name)
|
||||
{
|
||||
if(($column=$table->getColumn($name))===null)
|
||||
throw new CDbException(Yii::t('yii','Table "{table}" does not have a column named "{column}".',
|
||||
array('{table}'=>$table->name,'{column}'=>$name)));
|
||||
$condition=array();
|
||||
foreach($keywords as $keyword)
|
||||
{
|
||||
$keyword='%'.strtr($keyword,array('%'=>'\%', '_'=>'\_')).'%';
|
||||
if($caseSensitive)
|
||||
$condition[]=$prefix.$column->rawName.' LIKE '.$this->_connection->quoteValue('%'.$keyword.'%');
|
||||
else
|
||||
$condition[]='LOWER('.$prefix.$column->rawName.') LIKE LOWER('.$this->_connection->quoteValue('%'.$keyword.'%').')';
|
||||
}
|
||||
$conditions[]=implode(' AND ',$condition);
|
||||
}
|
||||
return '('.implode(' OR ',$conditions).')';
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the expression for selecting rows of specified primary key values.
|
||||
* @param mixed $table the table schema ({@link CDbTableSchema}) or the table name (string).
|
||||
* @param mixed $columnName the column name(s). It can be either a string indicating a single column
|
||||
* or an array of column names. If the latter, it stands for a composite key.
|
||||
* @param array $values list of key values to be selected within
|
||||
* @param string $prefix column prefix (ended with dot). If null, it will be the table name
|
||||
* @throws CDbException if specified column is not found in given table
|
||||
* @return string the expression for selection
|
||||
*/
|
||||
public function createInCondition($table,$columnName,$values,$prefix=null)
|
||||
{
|
||||
if(($n=count($values))<1)
|
||||
return '0=1';
|
||||
|
||||
$this->ensureTable($table);
|
||||
|
||||
if($prefix===null)
|
||||
$prefix=$table->rawName.'.';
|
||||
|
||||
$db=$this->_connection;
|
||||
|
||||
if(is_array($columnName) && count($columnName)===1)
|
||||
$columnName=reset($columnName);
|
||||
|
||||
if(is_string($columnName)) // simple key
|
||||
{
|
||||
if(!isset($table->columns[$columnName]))
|
||||
throw new CDbException(Yii::t('yii','Table "{table}" does not have a column named "{column}".',
|
||||
array('{table}'=>$table->name, '{column}'=>$columnName)));
|
||||
$column=$table->columns[$columnName];
|
||||
|
||||
$values=array_values($values);
|
||||
foreach($values as &$value)
|
||||
{
|
||||
$value=$column->typecast($value);
|
||||
if(is_string($value))
|
||||
$value=$db->quoteValue($value);
|
||||
}
|
||||
if($n===1)
|
||||
return $prefix.$column->rawName.($values[0]===null?' IS NULL':'='.$values[0]);
|
||||
else
|
||||
return $prefix.$column->rawName.' IN ('.implode(', ',$values).')';
|
||||
}
|
||||
elseif(is_array($columnName)) // composite key: $values=array(array('pk1'=>'v1','pk2'=>'v2'),array(...))
|
||||
{
|
||||
foreach($columnName as $name)
|
||||
{
|
||||
if(!isset($table->columns[$name]))
|
||||
throw new CDbException(Yii::t('yii','Table "{table}" does not have a column named "{column}".',
|
||||
array('{table}'=>$table->name, '{column}'=>$name)));
|
||||
|
||||
for($i=0;$i<$n;++$i)
|
||||
{
|
||||
if(isset($values[$i][$name]))
|
||||
{
|
||||
$value=$table->columns[$name]->typecast($values[$i][$name]);
|
||||
if(is_string($value))
|
||||
$values[$i][$name]=$db->quoteValue($value);
|
||||
else
|
||||
$values[$i][$name]=$value;
|
||||
}
|
||||
else
|
||||
throw new CDbException(Yii::t('yii','The value for the column "{column}" is not supplied when querying the table "{table}".',
|
||||
array('{table}'=>$table->name,'{column}'=>$name)));
|
||||
}
|
||||
}
|
||||
if(count($values)===1)
|
||||
{
|
||||
$entries=array();
|
||||
foreach($values[0] as $name=>$value)
|
||||
$entries[]=$prefix.$table->columns[$name]->rawName.($value===null?' IS NULL':'='.$value);
|
||||
return implode(' AND ',$entries);
|
||||
}
|
||||
|
||||
return $this->createCompositeInCondition($table,$values,$prefix);
|
||||
}
|
||||
else
|
||||
throw new CDbException(Yii::t('yii','Column name must be either a string or an array.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the expression for selecting rows with specified composite key values.
|
||||
* @param CDbTableSchema $table the table schema
|
||||
* @param array $values list of primary key values to be selected within
|
||||
* @param string $prefix column prefix (ended with dot)
|
||||
* @return string the expression for selection
|
||||
*/
|
||||
protected function createCompositeInCondition($table,$values,$prefix)
|
||||
{
|
||||
$keyNames=array();
|
||||
foreach(array_keys($values[0]) as $name)
|
||||
$keyNames[]=$prefix.$table->columns[$name]->rawName;
|
||||
$vs=array();
|
||||
foreach($values as $value)
|
||||
$vs[]='('.implode(', ',$value).')';
|
||||
return '('.implode(', ',$keyNames).') IN ('.implode(', ',$vs).')';
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the parameter is a valid table schema.
|
||||
* If it is a string, the corresponding table schema will be retrieved.
|
||||
* @param mixed $table table schema ({@link CDbTableSchema}) or table name (string).
|
||||
* If this refers to a valid table name, this parameter will be returned with the corresponding table schema.
|
||||
* @throws CDbException if the table name is not valid
|
||||
*/
|
||||
protected function ensureTable(&$table)
|
||||
{
|
||||
if(is_string($table) && ($table=$this->_schema->getTable($tableName=$table))===null)
|
||||
throw new CDbException(Yii::t('yii','Table "{table}" does not exist.',
|
||||
array('{table}'=>$tableName)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns default value of the integer/serial primary key. Default value means that the next
|
||||
* autoincrement/sequence value would be used.
|
||||
* @return string default value of the integer/serial primary key.
|
||||
* @since 1.1.14
|
||||
*/
|
||||
protected function getIntegerPrimaryKeyDefaultValue()
|
||||
{
|
||||
return 'NULL';
|
||||
}
|
||||
}
|
||||
644
framework/db/schema/CDbCriteria.php
Normal file
644
framework/db/schema/CDbCriteria.php
Normal file
@@ -0,0 +1,644 @@
|
||||
<?php
|
||||
/**
|
||||
* CDbCriteria class file.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
/**
|
||||
* CDbCriteria represents a query criteria, such as conditions, ordering by, limit/offset.
|
||||
*
|
||||
* It can be used in AR query methods such as CActiveRecord::find and CActiveRecord::findAll.
|
||||
*
|
||||
* $criteria=new CDbCriteria();
|
||||
* $criteria->compare('status',Post::STATUS_ACTIVE);
|
||||
* $criteria->addInCondition('id',array(1,2,3,4,5,6));
|
||||
*
|
||||
* $posts = Post::model()->findAll($criteria);
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @package system.db.schema
|
||||
* @since 1.0
|
||||
*/
|
||||
class CDbCriteria extends CComponent
|
||||
{
|
||||
const PARAM_PREFIX=':ycp';
|
||||
/**
|
||||
* @var integer the global counter for anonymous binding parameters.
|
||||
* This counter is used for generating the name for the anonymous parameters.
|
||||
*/
|
||||
public static $paramCount=0;
|
||||
/**
|
||||
* @var mixed the columns being selected. This refers to the SELECT clause in an SQL
|
||||
* statement. The property can be either a string (column names separated by commas)
|
||||
* or an array of column names. Defaults to '*', meaning all columns.
|
||||
*/
|
||||
public $select='*';
|
||||
/**
|
||||
* @var boolean whether to select distinct rows of data only. If this is set true,
|
||||
* the SELECT clause would be changed to SELECT DISTINCT.
|
||||
*/
|
||||
public $distinct=false;
|
||||
/**
|
||||
* @var string query condition. This refers to the WHERE clause in an SQL statement.
|
||||
* For example, <code>age>31 AND team=1</code>.
|
||||
*/
|
||||
public $condition='';
|
||||
/**
|
||||
* @var array list of query parameter values indexed by parameter placeholders.
|
||||
* For example, <code>array(':name'=>'Dan', ':age'=>31)</code>.
|
||||
*/
|
||||
public $params=array();
|
||||
/**
|
||||
* @var integer maximum number of records to be returned. If less than 0, it means no limit.
|
||||
*/
|
||||
public $limit=-1;
|
||||
/**
|
||||
* @var integer zero-based offset from where the records are to be returned. If less than 0, it means starting from the beginning.
|
||||
*/
|
||||
public $offset=-1;
|
||||
/**
|
||||
* @var string how to sort the query results. This refers to the ORDER BY clause in an SQL statement.
|
||||
*/
|
||||
public $order='';
|
||||
/**
|
||||
* @var string how to group the query results. This refers to the GROUP BY clause in an SQL statement.
|
||||
* For example, <code>'projectID, teamID'</code>.
|
||||
*/
|
||||
public $group='';
|
||||
/**
|
||||
* @var string how to join with other tables. This refers to the JOIN clause in an SQL statement.
|
||||
* For example, <code>'LEFT JOIN users ON users.id=authorID'</code>.
|
||||
*/
|
||||
public $join='';
|
||||
/**
|
||||
* @var string the condition to be applied with GROUP-BY clause.
|
||||
* For example, <code>'SUM(revenue)<50000'</code>.
|
||||
*/
|
||||
public $having='';
|
||||
/**
|
||||
* @var mixed the relational query criteria. This is used for fetching related objects in eager loading fashion.
|
||||
* This property is effective only when the criteria is passed as a parameter to the following methods of CActiveRecord:
|
||||
* <ul>
|
||||
* <li>{@link CActiveRecord::find()}</li>
|
||||
* <li>{@link CActiveRecord::findAll()}</li>
|
||||
* <li>{@link CActiveRecord::findByPk()}</li>
|
||||
* <li>{@link CActiveRecord::findAllByPk()}</li>
|
||||
* <li>{@link CActiveRecord::findByAttributes()}</li>
|
||||
* <li>{@link CActiveRecord::findAllByAttributes()}</li>
|
||||
* <li>{@link CActiveRecord::count()}</li>
|
||||
* </ul>
|
||||
* The property value will be used as the parameter to the {@link CActiveRecord::with()} method
|
||||
* to perform the eager loading. Please refer to {@link CActiveRecord::with()} on how to specify this parameter.
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public $with;
|
||||
/**
|
||||
* @var string the alias name of the table. If not set, it means the alias is 't'.
|
||||
*/
|
||||
public $alias;
|
||||
/**
|
||||
* @var boolean whether the foreign tables should be joined with the primary table in a single SQL.
|
||||
* This property is only used in relational AR queries for HAS_MANY and MANY_MANY relations.
|
||||
*
|
||||
* When this property is set true, only a single SQL will be executed for a relational AR query,
|
||||
* even if the primary table is limited and the relationship between a foreign table and the primary
|
||||
* table is many-to-one.
|
||||
*
|
||||
* When this property is set false, a SQL statement will be executed for each HAS_MANY relation.
|
||||
*
|
||||
* When this property is not set, if the primary table is limited or paginated,
|
||||
* a SQL statement will be executed for each HAS_MANY relation.
|
||||
* Otherwise, a single SQL statement will be executed for all.
|
||||
*
|
||||
* @since 1.1.4
|
||||
*/
|
||||
public $together;
|
||||
/**
|
||||
* @var string the name of the AR attribute whose value should be used as index of the query result array.
|
||||
* Defaults to null, meaning the result array will be zero-based integers.
|
||||
* @since 1.1.5
|
||||
*/
|
||||
public $index;
|
||||
/**
|
||||
* @var mixed scopes to apply
|
||||
*
|
||||
* This property is effective only when passing criteria to
|
||||
* the one of the following methods:
|
||||
* <ul>
|
||||
* <li>{@link CActiveRecord::find()}</li>
|
||||
* <li>{@link CActiveRecord::findAll()}</li>
|
||||
* <li>{@link CActiveRecord::findByPk()}</li>
|
||||
* <li>{@link CActiveRecord::findAllByPk()}</li>
|
||||
* <li>{@link CActiveRecord::findByAttributes()}</li>
|
||||
* <li>{@link CActiveRecord::findAllByAttributes()}</li>
|
||||
* <li>{@link CActiveRecord::count()}</li>
|
||||
* </ul>
|
||||
*
|
||||
* Can be set to one of the following:
|
||||
* <ul>
|
||||
* <li>One scope: $criteria->scopes='scopeName';</li>
|
||||
* <li>Multiple scopes: $criteria->scopes=array('scopeName1','scopeName2');</li>
|
||||
* <li>Scope with parameters: $criteria->scopes=array('scopeName'=>array($params));</li>
|
||||
* <li>Multiple scopes with parameters: $criteria->scopes=array('scopeName1'=>array($params1),'scopeName2'=>array($params2));</li>
|
||||
* <li>Multiple scopes with the same name: array(array('scopeName'=>array($params1)),array('scopeName'=>array($params2)));</li>
|
||||
* </ul>
|
||||
* @since 1.1.7
|
||||
*/
|
||||
public $scopes;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param array $data criteria initial property values (indexed by property name)
|
||||
*/
|
||||
public function __construct($data=array())
|
||||
{
|
||||
foreach($data as $name=>$value)
|
||||
$this->$name=$value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remaps criteria parameters on unserialize to prevent name collisions.
|
||||
* @since 1.1.9
|
||||
*/
|
||||
public function __wakeup()
|
||||
{
|
||||
$map=array();
|
||||
$params=array();
|
||||
foreach($this->params as $name=>$value)
|
||||
{
|
||||
if(strpos($name,self::PARAM_PREFIX)===0)
|
||||
{
|
||||
$newName=self::PARAM_PREFIX.self::$paramCount++;
|
||||
$map[$name]=$newName;
|
||||
}
|
||||
else
|
||||
{
|
||||
$newName=$name;
|
||||
}
|
||||
$params[$newName]=$value;
|
||||
}
|
||||
if (!empty($map))
|
||||
{
|
||||
$sqlContentFieldNames=array(
|
||||
'select',
|
||||
'condition',
|
||||
'order',
|
||||
'group',
|
||||
'join',
|
||||
'having',
|
||||
);
|
||||
foreach($sqlContentFieldNames as $field)
|
||||
{
|
||||
if(is_array($this->$field))
|
||||
foreach($this->$field as $k=>$v)
|
||||
$this->{$field}[$k]=strtr($v,$map);
|
||||
else
|
||||
$this->$field=strtr($this->$field,$map);
|
||||
}
|
||||
}
|
||||
$this->params=$params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a condition to the existing {@link condition}.
|
||||
* The new condition and the existing condition will be concatenated via the specified operator
|
||||
* which defaults to 'AND'.
|
||||
* The new condition can also be an array. In this case, all elements in the array
|
||||
* will be concatenated together via the operator.
|
||||
* This method handles the case when the existing condition is empty.
|
||||
* After calling this method, the {@link condition} property will be modified.
|
||||
* @param mixed $condition the new condition. It can be either a string or an array of strings.
|
||||
* @param string $operator the operator to join different conditions. Defaults to 'AND'.
|
||||
* @return CDbCriteria the criteria object itself
|
||||
*/
|
||||
public function addCondition($condition,$operator='AND')
|
||||
{
|
||||
if(is_array($condition))
|
||||
{
|
||||
if($condition===array())
|
||||
return $this;
|
||||
$condition='('.implode(') '.$operator.' (',$condition).')';
|
||||
}
|
||||
if($this->condition==='')
|
||||
$this->condition=$condition;
|
||||
else
|
||||
$this->condition='('.$this->condition.') '.$operator.' ('.$condition.')';
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a search condition to the existing {@link condition}.
|
||||
* The search condition and the existing condition will be concatenated via the specified operator
|
||||
* which defaults to 'AND'.
|
||||
* The search condition is generated using the SQL LIKE operator with the given column name and
|
||||
* search keyword.
|
||||
* @param string $column the column name (or a valid SQL expression)
|
||||
* @param string $keyword the search keyword. This interpretation of the keyword is affected by the next parameter.
|
||||
* @param boolean $escape whether the keyword should be escaped if it contains characters % or _.
|
||||
* When this parameter is true (default), the special characters % (matches 0 or more characters)
|
||||
* and _ (matches a single character) will be escaped, and the keyword will be surrounded with a %
|
||||
* character on both ends. When this parameter is false, the keyword will be directly used for
|
||||
* matching without any change.
|
||||
* @param string $operator the operator used to concatenate the new condition with the existing one.
|
||||
* Defaults to 'AND'.
|
||||
* @param string $like the LIKE operator. Defaults to 'LIKE'. You may also set this to be 'NOT LIKE'.
|
||||
* @return CDbCriteria the criteria object itself
|
||||
*/
|
||||
public function addSearchCondition($column,$keyword,$escape=true,$operator='AND',$like='LIKE')
|
||||
{
|
||||
if($keyword=='')
|
||||
return $this;
|
||||
if($escape)
|
||||
$keyword='%'.strtr($keyword,array('%'=>'\%', '_'=>'\_', '\\'=>'\\\\')).'%';
|
||||
$condition=$column." $like ".self::PARAM_PREFIX.self::$paramCount;
|
||||
$this->params[self::PARAM_PREFIX.self::$paramCount++]=$keyword;
|
||||
return $this->addCondition($condition, $operator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends an IN condition to the existing {@link condition}.
|
||||
* The IN condition and the existing condition will be concatenated via the specified operator
|
||||
* which defaults to 'AND'.
|
||||
* The IN condition is generated by using the SQL IN operator which requires the specified
|
||||
* column value to be among the given list of values.
|
||||
* @param string $column the column name (or a valid SQL expression)
|
||||
* @param array $values list of values that the column value should be in
|
||||
* @param string $operator the operator used to concatenate the new condition with the existing one.
|
||||
* Defaults to 'AND'.
|
||||
* @return CDbCriteria the criteria object itself
|
||||
*/
|
||||
public function addInCondition($column,$values,$operator='AND')
|
||||
{
|
||||
if(($n=count($values))<1)
|
||||
$condition='0=1'; // 0=1 is used because in MSSQL value alone can't be used in WHERE
|
||||
elseif($n===1)
|
||||
{
|
||||
$value=reset($values);
|
||||
if($value===null)
|
||||
$condition=$column.' IS NULL';
|
||||
else
|
||||
{
|
||||
$condition=$column.'='.self::PARAM_PREFIX.self::$paramCount;
|
||||
$this->params[self::PARAM_PREFIX.self::$paramCount++]=$value;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$params=array();
|
||||
foreach($values as $value)
|
||||
{
|
||||
$params[]=self::PARAM_PREFIX.self::$paramCount;
|
||||
$this->params[self::PARAM_PREFIX.self::$paramCount++]=$value;
|
||||
}
|
||||
$condition=$column.' IN ('.implode(', ',$params).')';
|
||||
}
|
||||
return $this->addCondition($condition,$operator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends an NOT IN condition to the existing {@link condition}.
|
||||
* The NOT IN condition and the existing condition will be concatenated via the specified operator
|
||||
* which defaults to 'AND'.
|
||||
* The NOT IN condition is generated by using the SQL NOT IN operator which requires the specified
|
||||
* column value to be among the given list of values.
|
||||
* @param string $column the column name (or a valid SQL expression)
|
||||
* @param array $values list of values that the column value should not be in
|
||||
* @param string $operator the operator used to concatenate the new condition with the existing one.
|
||||
* Defaults to 'AND'.
|
||||
* @return CDbCriteria the criteria object itself
|
||||
* @since 1.1.1
|
||||
*/
|
||||
public function addNotInCondition($column,$values,$operator='AND')
|
||||
{
|
||||
if(($n=count($values))<1)
|
||||
return $this;
|
||||
if($n===1)
|
||||
{
|
||||
$value=reset($values);
|
||||
if($value===null)
|
||||
$condition=$column.' IS NOT NULL';
|
||||
else
|
||||
{
|
||||
$condition=$column.'!='.self::PARAM_PREFIX.self::$paramCount;
|
||||
$this->params[self::PARAM_PREFIX.self::$paramCount++]=$value;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$params=array();
|
||||
foreach($values as $value)
|
||||
{
|
||||
$params[]=self::PARAM_PREFIX.self::$paramCount;
|
||||
$this->params[self::PARAM_PREFIX.self::$paramCount++]=$value;
|
||||
}
|
||||
$condition=$column.' NOT IN ('.implode(', ',$params).')';
|
||||
}
|
||||
return $this->addCondition($condition,$operator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a condition for matching the given list of column values.
|
||||
* The generated condition will be concatenated to the existing {@link condition}
|
||||
* via the specified operator which defaults to 'AND'.
|
||||
* The condition is generated by matching each column and the corresponding value.
|
||||
* @param array $columns list of column names and values to be matched (name=>value)
|
||||
* @param string $columnOperator the operator to concatenate multiple column matching condition. Defaults to 'AND'.
|
||||
* @param string $operator the operator used to concatenate the new condition with the existing one.
|
||||
* Defaults to 'AND'.
|
||||
* @return CDbCriteria the criteria object itself
|
||||
*/
|
||||
public function addColumnCondition($columns,$columnOperator='AND',$operator='AND')
|
||||
{
|
||||
$params=array();
|
||||
foreach($columns as $name=>$value)
|
||||
{
|
||||
if($value===null)
|
||||
$params[]=$name.' IS NULL';
|
||||
else
|
||||
{
|
||||
$params[]=$name.'='.self::PARAM_PREFIX.self::$paramCount;
|
||||
$this->params[self::PARAM_PREFIX.self::$paramCount++]=$value;
|
||||
}
|
||||
}
|
||||
return $this->addCondition(implode(" $columnOperator ",$params), $operator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a comparison expression to the {@link condition} property.
|
||||
*
|
||||
* This method is a helper that appends to the {@link condition} property
|
||||
* with a new comparison expression. The comparison is done by comparing a column
|
||||
* with the given value using some comparison operator.
|
||||
*
|
||||
* The comparison operator is intelligently determined based on the first few
|
||||
* characters in the given value. In particular, it recognizes the following operators
|
||||
* if they appear as the leading characters in the given value:
|
||||
* <ul>
|
||||
* <li><code><</code>: the column must be less than the given value.</li>
|
||||
* <li><code>></code>: the column must be greater than the given value.</li>
|
||||
* <li><code><=</code>: the column must be less than or equal to the given value.</li>
|
||||
* <li><code>>=</code>: the column must be greater than or equal to the given value.</li>
|
||||
* <li><code><></code>: the column must not be the same as the given value.
|
||||
* Note that when $partialMatch is true, this would mean the value must not be a substring
|
||||
* of the column.</li>
|
||||
* <li><code>=</code>: the column must be equal to the given value.</li>
|
||||
* <li>none of the above: the column must be equal to the given value. Note that when $partialMatch
|
||||
* is true, this would mean the value must be the same as the given value or be a substring of it.</li>
|
||||
* </ul>
|
||||
*
|
||||
* Note that any surrounding white spaces will be removed from the value before comparison.
|
||||
* When the value is empty, no comparison expression will be added to the search condition.
|
||||
*
|
||||
* @param string $column the name of the column to be searched
|
||||
* @param mixed $value the column value to be compared with. If the value is a string, the aforementioned
|
||||
* intelligent comparison will be conducted. If the value is an array, the comparison is done
|
||||
* by exact match of any of the value in the array. If the string or the array is empty,
|
||||
* the existing search condition will not be modified.
|
||||
* @param boolean $partialMatch whether the value should consider partial text match (using LIKE and NOT LIKE operators).
|
||||
* Defaults to false, meaning exact comparison.
|
||||
* @param string $operator the operator used to concatenate the new condition with the existing one.
|
||||
* Defaults to 'AND'.
|
||||
* @param boolean $escape whether the value should be escaped if $partialMatch is true and
|
||||
* the value contains characters % or _. When this parameter is true (default),
|
||||
* the special characters % (matches 0 or more characters)
|
||||
* and _ (matches a single character) will be escaped, and the value will be surrounded with a %
|
||||
* character on both ends. When this parameter is false, the value will be directly used for
|
||||
* matching without any change.
|
||||
* @return CDbCriteria the criteria object itself
|
||||
* @since 1.1.1
|
||||
*/
|
||||
public function compare($column, $value, $partialMatch=false, $operator='AND', $escape=true)
|
||||
{
|
||||
if(is_array($value))
|
||||
{
|
||||
if($value===array())
|
||||
return $this;
|
||||
return $this->addInCondition($column,$value,$operator);
|
||||
}
|
||||
else
|
||||
$value="$value";
|
||||
|
||||
if(preg_match('/^(?:\s*(<>|<=|>=|<|>|=))?(.*)$/',$value,$matches))
|
||||
{
|
||||
$value=$matches[2];
|
||||
$op=$matches[1];
|
||||
}
|
||||
else
|
||||
$op='';
|
||||
|
||||
if($value==='')
|
||||
return $this;
|
||||
|
||||
if($partialMatch)
|
||||
{
|
||||
if($op==='')
|
||||
return $this->addSearchCondition($column,$value,$escape,$operator);
|
||||
if($op==='<>')
|
||||
return $this->addSearchCondition($column,$value,$escape,$operator,'NOT LIKE');
|
||||
}
|
||||
elseif($op==='')
|
||||
$op='=';
|
||||
|
||||
$this->addCondition($column.$op.self::PARAM_PREFIX.self::$paramCount,$operator);
|
||||
$this->params[self::PARAM_PREFIX.self::$paramCount++]=$value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a between condition to the {@link condition} property.
|
||||
*
|
||||
* The new between condition and the existing condition will be concatenated via
|
||||
* the specified operator which defaults to 'AND'.
|
||||
* If one or both values are empty then the condition is not added to the existing condition.
|
||||
* This method handles the case when the existing condition is empty.
|
||||
* After calling this method, the {@link condition} property will be modified.
|
||||
* @param string $column the name of the column to search between.
|
||||
* @param string $valueStart the beginning value to start the between search.
|
||||
* @param string $valueEnd the ending value to end the between search.
|
||||
* @param string $operator the operator used to concatenate the new condition with the existing one.
|
||||
* Defaults to 'AND'.
|
||||
* @return CDbCriteria the criteria object itself
|
||||
* @since 1.1.2
|
||||
*/
|
||||
public function addBetweenCondition($column,$valueStart,$valueEnd,$operator='AND')
|
||||
{
|
||||
if($valueStart==='' || $valueEnd==='')
|
||||
return $this;
|
||||
|
||||
$paramStart=self::PARAM_PREFIX.self::$paramCount++;
|
||||
$paramEnd=self::PARAM_PREFIX.self::$paramCount++;
|
||||
$this->params[$paramStart]=$valueStart;
|
||||
$this->params[$paramEnd]=$valueEnd;
|
||||
$condition="$column BETWEEN $paramStart AND $paramEnd";
|
||||
|
||||
return $this->addCondition($condition,$operator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges with another criteria.
|
||||
* In general, the merging makes the resulting criteria more restrictive.
|
||||
* For example, if both criterias have conditions, they will be 'AND' together.
|
||||
* Also, the criteria passed as the parameter takes precedence in case
|
||||
* two options cannot be merged (e.g. LIMIT, OFFSET).
|
||||
* @param mixed $criteria the criteria to be merged with. Either an array or CDbCriteria.
|
||||
* @param string|boolean $operator the operator used to concatenate where and having conditions. Defaults to 'AND'.
|
||||
* For backwards compatibility a boolean value can be passed:
|
||||
* - 'false' for 'OR'
|
||||
* - 'true' for 'AND'
|
||||
*/
|
||||
public function mergeWith($criteria,$operator='AND')
|
||||
{
|
||||
if(is_bool($operator))
|
||||
$operator=$operator ? 'AND' : 'OR';
|
||||
if(is_array($criteria))
|
||||
$criteria=new self($criteria);
|
||||
if($this->select!==$criteria->select)
|
||||
{
|
||||
if($this->select==='*')
|
||||
$this->select=$criteria->select;
|
||||
elseif($criteria->select!=='*')
|
||||
{
|
||||
$select1=is_string($this->select)?preg_split('/\s*,\s*/',trim($this->select),-1,PREG_SPLIT_NO_EMPTY):$this->select;
|
||||
$select2=is_string($criteria->select)?preg_split('/\s*,\s*/',trim($criteria->select),-1,PREG_SPLIT_NO_EMPTY):$criteria->select;
|
||||
$this->select=array_merge($select1,array_diff($select2,$select1));
|
||||
}
|
||||
}
|
||||
|
||||
if($this->condition!==$criteria->condition)
|
||||
{
|
||||
if($this->condition==='')
|
||||
$this->condition=$criteria->condition;
|
||||
elseif($criteria->condition!=='')
|
||||
$this->condition="({$this->condition}) $operator ({$criteria->condition})";
|
||||
}
|
||||
|
||||
if($this->params!==$criteria->params)
|
||||
$this->params=array_merge($this->params,$criteria->params);
|
||||
|
||||
if($criteria->limit>0)
|
||||
$this->limit=$criteria->limit;
|
||||
|
||||
if($criteria->offset>=0)
|
||||
$this->offset=$criteria->offset;
|
||||
|
||||
if($criteria->alias!==null)
|
||||
$this->alias=$criteria->alias;
|
||||
|
||||
if($this->order!==$criteria->order)
|
||||
{
|
||||
if($this->order==='')
|
||||
$this->order=$criteria->order;
|
||||
elseif($criteria->order!=='')
|
||||
$this->order=$criteria->order.', '.$this->order;
|
||||
}
|
||||
|
||||
if($this->group!==$criteria->group)
|
||||
{
|
||||
if($this->group==='')
|
||||
$this->group=$criteria->group;
|
||||
elseif($criteria->group!=='')
|
||||
$this->group.=', '.$criteria->group;
|
||||
}
|
||||
|
||||
if($this->join!==$criteria->join)
|
||||
{
|
||||
if($this->join==='')
|
||||
$this->join=$criteria->join;
|
||||
elseif($criteria->join!=='')
|
||||
$this->join.=' '.$criteria->join;
|
||||
}
|
||||
|
||||
if($this->having!==$criteria->having)
|
||||
{
|
||||
if($this->having==='')
|
||||
$this->having=$criteria->having;
|
||||
elseif($criteria->having!=='')
|
||||
$this->having="({$this->having}) $operator ({$criteria->having})";
|
||||
}
|
||||
|
||||
if($criteria->distinct>0)
|
||||
$this->distinct=$criteria->distinct;
|
||||
|
||||
if($criteria->together!==null)
|
||||
$this->together=$criteria->together;
|
||||
|
||||
if($criteria->index!==null)
|
||||
$this->index=$criteria->index;
|
||||
|
||||
if(empty($this->scopes))
|
||||
$this->scopes=$criteria->scopes;
|
||||
elseif(!empty($criteria->scopes))
|
||||
{
|
||||
$scopes1=(array)$this->scopes;
|
||||
$scopes2=(array)$criteria->scopes;
|
||||
foreach($scopes1 as $k=>$v)
|
||||
{
|
||||
if(is_integer($k))
|
||||
$scopes[]=$v;
|
||||
elseif(isset($scopes2[$k]))
|
||||
$scopes[]=array($k=>$v);
|
||||
else
|
||||
$scopes[$k]=$v;
|
||||
}
|
||||
foreach($scopes2 as $k=>$v)
|
||||
{
|
||||
if(is_integer($k))
|
||||
$scopes[]=$v;
|
||||
elseif(isset($scopes1[$k]))
|
||||
$scopes[]=array($k=>$v);
|
||||
else
|
||||
$scopes[$k]=$v;
|
||||
}
|
||||
$this->scopes=$scopes;
|
||||
}
|
||||
|
||||
if(empty($this->with))
|
||||
$this->with=$criteria->with;
|
||||
elseif(!empty($criteria->with))
|
||||
{
|
||||
$this->with=(array)$this->with;
|
||||
foreach((array)$criteria->with as $k=>$v)
|
||||
{
|
||||
if(is_integer($k))
|
||||
$this->with[]=$v;
|
||||
elseif(isset($this->with[$k]))
|
||||
{
|
||||
$excludes=array();
|
||||
foreach(array('joinType','on') as $opt)
|
||||
{
|
||||
if(isset($this->with[$k][$opt]))
|
||||
$excludes[$opt]=$this->with[$k][$opt];
|
||||
if(isset($v[$opt]))
|
||||
$excludes[$opt]= ($opt==='on' && isset($excludes[$opt]) && $v[$opt]!==$excludes[$opt]) ?
|
||||
"($excludes[$opt]) AND $v[$opt]" : $v[$opt];
|
||||
unset($this->with[$k][$opt]);
|
||||
unset($v[$opt]);
|
||||
}
|
||||
$this->with[$k]=new self($this->with[$k]);
|
||||
$this->with[$k]->mergeWith($v,$operator);
|
||||
$this->with[$k]=$this->with[$k]->toArray();
|
||||
if (count($excludes)!==0)
|
||||
$this->with[$k]=CMap::mergeArray($this->with[$k],$excludes);
|
||||
}
|
||||
else
|
||||
$this->with[$k]=$v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array the array representation of the criteria
|
||||
*/
|
||||
public function toArray()
|
||||
{
|
||||
$result=array();
|
||||
foreach(array('select', 'condition', 'params', 'limit', 'offset', 'order', 'group', 'join', 'having', 'distinct', 'scopes', 'with', 'alias', 'index', 'together') as $name)
|
||||
$result[$name]=$this->$name;
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
60
framework/db/schema/CDbExpression.php
Normal file
60
framework/db/schema/CDbExpression.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
/**
|
||||
* CDbExpression class file.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
/**
|
||||
* CDbExpression represents a DB expression that does not need escaping.
|
||||
* CDbExpression is mainly used in {@link CActiveRecord} as attribute values.
|
||||
* When inserting or updating a {@link CActiveRecord}, attribute values of
|
||||
* type CDbExpression will be directly put into the corresponding SQL statement
|
||||
* without escaping. A typical usage is that an attribute is set with 'NOW()'
|
||||
* expression so that saving the record would fill the corresponding column
|
||||
* with the current DB server timestamp.
|
||||
*
|
||||
* Starting from version 1.1.1, one can also specify parameters to be bound
|
||||
* for the expression. For example, if the expression is 'LOWER(:value)', then
|
||||
* one can set {@link params} to be <code>array(':value'=>$value)</code>.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @package system.db.schema
|
||||
*/
|
||||
class CDbExpression extends CComponent
|
||||
{
|
||||
/**
|
||||
* @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 {@link expression}, while the values
|
||||
* are the corresponding parameter values.
|
||||
* @since 1.1.1
|
||||
*/
|
||||
public $params=array();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param string $expression the DB expression
|
||||
* @param array $params parameters
|
||||
*/
|
||||
public function __construct($expression,$params=array())
|
||||
{
|
||||
$this->expression=$expression;
|
||||
$this->params=$params;
|
||||
}
|
||||
|
||||
/**
|
||||
* String magic method
|
||||
* @return string the DB expression
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->expression;
|
||||
}
|
||||
}
|
||||
597
framework/db/schema/CDbSchema.php
Normal file
597
framework/db/schema/CDbSchema.php
Normal file
@@ -0,0 +1,597 @@
|
||||
<?php
|
||||
/**
|
||||
* CDbSchema class file.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
/**
|
||||
* CDbSchema is the base class for retrieving metadata information.
|
||||
*
|
||||
* @property CDbConnection $dbConnection Database connection. The connection is active.
|
||||
* @property array $tables The metadata for all tables in the database.
|
||||
* Each array element is an instance of {@link CDbTableSchema} (or its child class).
|
||||
* The array keys are table names.
|
||||
* @property array $tableNames All table names in the database.
|
||||
* @property CDbCommandBuilder $commandBuilder The SQL command builder for this connection.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @package system.db.schema
|
||||
* @since 1.0
|
||||
*/
|
||||
abstract class CDbSchema extends CComponent
|
||||
{
|
||||
/**
|
||||
* @var array the abstract column types mapped to physical column types.
|
||||
* @since 1.1.6
|
||||
*/
|
||||
public $columnTypes=array();
|
||||
|
||||
private $_tableNames=array();
|
||||
private $_tables=array();
|
||||
private $_connection;
|
||||
private $_builder;
|
||||
private $_cacheExclude=array();
|
||||
|
||||
/**
|
||||
* Loads the metadata for the specified table.
|
||||
* @param string $name table name
|
||||
* @return CDbTableSchema driver dependent table metadata, null if the table does not exist.
|
||||
*/
|
||||
abstract protected function loadTable($name);
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param CDbConnection $conn database connection.
|
||||
*/
|
||||
public function __construct($conn)
|
||||
{
|
||||
$this->_connection=$conn;
|
||||
foreach($conn->schemaCachingExclude as $name)
|
||||
$this->_cacheExclude[$name]=true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CDbConnection database connection. The connection is active.
|
||||
*/
|
||||
public function getDbConnection()
|
||||
{
|
||||
return $this->_connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the metadata for the named table.
|
||||
* @param string $name table name
|
||||
* @param boolean $refresh if we need to refresh schema cache for a table.
|
||||
* Parameter available since 1.1.9
|
||||
* @return CDbTableSchema table metadata. Null if the named table does not exist.
|
||||
*/
|
||||
public function getTable($name,$refresh=false)
|
||||
{
|
||||
if($refresh===false && isset($this->_tables[$name]))
|
||||
return $this->_tables[$name];
|
||||
else
|
||||
{
|
||||
if($this->_connection->tablePrefix!==null && strpos($name,'{{')!==false)
|
||||
$realName=preg_replace('/\{\{(.*?)\}\}/',$this->_connection->tablePrefix.'$1',$name);
|
||||
else
|
||||
$realName=$name;
|
||||
|
||||
// temporarily disable query caching
|
||||
if($this->_connection->queryCachingDuration>0)
|
||||
{
|
||||
$qcDuration=$this->_connection->queryCachingDuration;
|
||||
$this->_connection->queryCachingDuration=0;
|
||||
}
|
||||
|
||||
if(!isset($this->_cacheExclude[$name]) && ($duration=$this->_connection->schemaCachingDuration)>0 && $this->_connection->schemaCacheID!==false && ($cache=Yii::app()->getComponent($this->_connection->schemaCacheID))!==null)
|
||||
{
|
||||
$key='yii:dbschema'.$this->_connection->connectionString.':'.$this->_connection->username.':'.$name;
|
||||
$table=$cache->get($key);
|
||||
if($refresh===true || $table===false)
|
||||
{
|
||||
$table=$this->loadTable($realName);
|
||||
if($table!==null)
|
||||
$cache->set($key,$table,$duration);
|
||||
}
|
||||
$this->_tables[$name]=$table;
|
||||
}
|
||||
else
|
||||
$this->_tables[$name]=$table=$this->loadTable($realName);
|
||||
|
||||
if(isset($qcDuration)) // re-enable query caching
|
||||
$this->_connection->queryCachingDuration=$qcDuration;
|
||||
|
||||
return $table;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @return array the metadata for all tables in the database.
|
||||
* Each array element is an instance of {@link CDbTableSchema} (or its child class).
|
||||
* The array keys are table names.
|
||||
*/
|
||||
public function getTables($schema='')
|
||||
{
|
||||
$tables=array();
|
||||
foreach($this->getTableNames($schema) as $name)
|
||||
{
|
||||
if(($table=$this->getTable($name))!==null)
|
||||
$tables[$name]=$table;
|
||||
}
|
||||
return $tables;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* If not empty, the returned table names will be prefixed with the schema name.
|
||||
* @return array all table names in the database.
|
||||
*/
|
||||
public function getTableNames($schema='')
|
||||
{
|
||||
if(!isset($this->_tableNames[$schema]))
|
||||
$this->_tableNames[$schema]=$this->findTableNames($schema);
|
||||
return $this->_tableNames[$schema];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CDbCommandBuilder the SQL command builder for this connection.
|
||||
*/
|
||||
public function getCommandBuilder()
|
||||
{
|
||||
if($this->_builder!==null)
|
||||
return $this->_builder;
|
||||
else
|
||||
return $this->_builder=$this->createCommandBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the schema.
|
||||
* This method resets the loaded table metadata and command builder
|
||||
* so that they can be recreated to reflect the change of schema.
|
||||
*/
|
||||
public function refresh()
|
||||
{
|
||||
if(($duration=$this->_connection->schemaCachingDuration)>0 && $this->_connection->schemaCacheID!==false && ($cache=Yii::app()->getComponent($this->_connection->schemaCacheID))!==null)
|
||||
{
|
||||
foreach(array_keys($this->_tables) as $name)
|
||||
{
|
||||
if(!isset($this->_cacheExclude[$name]))
|
||||
{
|
||||
$key='yii:dbschema'.$this->_connection->connectionString.':'.$this->_connection->username.':'.$name;
|
||||
$cache->delete($key);
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->_tables=array();
|
||||
$this->_tableNames=array();
|
||||
$this->_builder=null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Quotes a table name for use in a query.
|
||||
* If the table name contains schema prefix, the prefix will also be properly quoted.
|
||||
* @param string $name table name
|
||||
* @return string the properly quoted table name
|
||||
* @see quoteSimpleTableName
|
||||
*/
|
||||
public function quoteTableName($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 simple table name for use in a query.
|
||||
* A simple table name does not schema prefix.
|
||||
* @param string $name table name
|
||||
* @return string the properly quoted table name
|
||||
* @since 1.1.6
|
||||
*/
|
||||
public function quoteSimpleTableName($name)
|
||||
{
|
||||
return "'".$name."'";
|
||||
}
|
||||
|
||||
/**
|
||||
* Quotes a column name for use in a query.
|
||||
* If the column name contains prefix, the prefix will also be properly quoted.
|
||||
* @param string $name column name
|
||||
* @return string the properly quoted column name
|
||||
* @see quoteSimpleColumnName
|
||||
*/
|
||||
public function quoteColumnName($name)
|
||||
{
|
||||
if(($pos=strrpos($name,'.'))!==false)
|
||||
{
|
||||
$prefix=$this->quoteTableName(substr($name,0,$pos)).'.';
|
||||
$name=substr($name,$pos+1);
|
||||
}
|
||||
else
|
||||
$prefix='';
|
||||
return $prefix . ($name==='*' ? $name : $this->quoteSimpleColumnName($name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Quotes a simple column name for use in a query.
|
||||
* A simple column name does not contain prefix.
|
||||
* @param string $name column name
|
||||
* @return string the properly quoted column name
|
||||
* @since 1.1.6
|
||||
*/
|
||||
public function quoteSimpleColumnName($name)
|
||||
{
|
||||
return '"'.$name.'"';
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two table names.
|
||||
* The table names can be either quoted or unquoted. This method
|
||||
* will consider both cases.
|
||||
* @param string $name1 table name 1
|
||||
* @param string $name2 table name 2
|
||||
* @return boolean whether the two table names refer to the same table.
|
||||
*/
|
||||
public function compareTableNames($name1,$name2)
|
||||
{
|
||||
$name1=str_replace(array('"','`',"'"),'',$name1);
|
||||
$name2=str_replace(array('"','`',"'"),'',$name2);
|
||||
if(($pos=strrpos($name1,'.'))!==false)
|
||||
$name1=substr($name1,$pos+1);
|
||||
if(($pos=strrpos($name2,'.'))!==false)
|
||||
$name2=substr($name2,$pos+1);
|
||||
if($this->_connection->tablePrefix!==null)
|
||||
{
|
||||
if(strpos($name1,'{')!==false)
|
||||
$name1=$this->_connection->tablePrefix.str_replace(array('{','}'),'',$name1);
|
||||
if(strpos($name2,'{')!==false)
|
||||
$name2=$this->_connection->tablePrefix.str_replace(array('{','}'),'',$name2);
|
||||
}
|
||||
return $name1===$name2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets 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 max value of a primary key plus one (i.e. sequence trimming).
|
||||
* @param CDbTableSchema $table the table schema whose primary key sequence will be reset
|
||||
* @param integer|null $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 the max value of a primary
|
||||
* key plus one (i.e. sequence trimming).
|
||||
* @since 1.1
|
||||
*/
|
||||
public function resetSequence($table,$value=null)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables integrity check.
|
||||
* @param boolean $check whether to turn on or off the integrity check.
|
||||
* @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema.
|
||||
* @since 1.1
|
||||
*/
|
||||
public function checkIntegrity($check=true,$schema='')
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a command builder for the database.
|
||||
* This method may be overridden by child classes to create a DBMS-specific command builder.
|
||||
* @return CDbCommandBuilder command builder instance
|
||||
*/
|
||||
protected function createCommandBuilder()
|
||||
{
|
||||
return new CDbCommandBuilder($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* If not empty, the returned table names will be prefixed with the schema name.
|
||||
* @throws CDbException if current schema does not support fetching all table names
|
||||
* @return array all table names in the database.
|
||||
*/
|
||||
protected function findTableNames($schema='')
|
||||
{
|
||||
throw new CDbException(Yii::t('yii','{class} does not support fetching all table names.',
|
||||
array('{class}'=>get_class($this))));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an abstract column type into a physical column type.
|
||||
* The conversion is done using the type map specified in {@link columnTypes}.
|
||||
* These abstract column types are supported (using MySQL as example to explain the corresponding
|
||||
* physical types):
|
||||
* <ul>
|
||||
* <li>pk: an auto-incremental primary key type, will be converted into "int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY"</li>
|
||||
* <li>string: string type, will be converted into "varchar(255)"</li>
|
||||
* <li>text: a long string type, will be converted into "text"</li>
|
||||
* <li>integer: integer type, will be converted into "int(11)"</li>
|
||||
* <li>boolean: boolean type, will be converted into "tinyint(1)"</li>
|
||||
* <li>float: float number type, will be converted into "float"</li>
|
||||
* <li>decimal: decimal number type, will be converted into "decimal"</li>
|
||||
* <li>datetime: datetime type, will be converted into "datetime"</li>
|
||||
* <li>timestamp: timestamp type, will be converted into "timestamp"</li>
|
||||
* <li>time: time type, will be converted into "time"</li>
|
||||
* <li>date: date type, will be converted into "date"</li>
|
||||
* <li>binary: binary data type, will be converted into "blob"</li>
|
||||
* </ul>
|
||||
*
|
||||
* If the abstract type contains two or more parts separated by spaces (e.g. "string NOT NULL"), then only
|
||||
* the first part will be converted, and the rest of the parts will be appended to the conversion result.
|
||||
* For example, 'string NOT NULL' is converted to 'varchar(255) NOT NULL'.
|
||||
* @param string $type abstract column type
|
||||
* @return string physical column type.
|
||||
* @since 1.1.6
|
||||
*/
|
||||
public function getColumnType($type)
|
||||
{
|
||||
if(isset($this->columnTypes[$type]))
|
||||
return $this->columnTypes[$type];
|
||||
elseif(($pos=strpos($type,' '))!==false)
|
||||
{
|
||||
$t=substr($type,0,$pos);
|
||||
return (isset($this->columnTypes[$t]) ? $this->columnTypes[$t] : $t).substr($type,$pos);
|
||||
}
|
||||
else
|
||||
return $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds 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 {@link 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
|
||||
* inserted 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.
|
||||
* @return string the SQL statement for creating a new DB table.
|
||||
* @since 1.1.6
|
||||
*/
|
||||
public function createTable($table, $columns, $options=null)
|
||||
{
|
||||
$cols=array();
|
||||
foreach($columns as $name=>$type)
|
||||
{
|
||||
if(is_string($name))
|
||||
$cols[]="\t".$this->quoteColumnName($name).' '.$this->getColumnType($type);
|
||||
else
|
||||
$cols[]="\t".$type;
|
||||
}
|
||||
$sql="CREATE TABLE ".$this->quoteTableName($table)." (\n".implode(",\n",$cols)."\n)";
|
||||
return $options===null ? $sql : $sql.' '.$options;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @since 1.1.6
|
||||
*/
|
||||
public function renameTable($table, $newName)
|
||||
{
|
||||
return 'RENAME TABLE ' . $this->quoteTableName($table) . ' TO ' . $this->quoteTableName($newName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds 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.
|
||||
* @return string the SQL statement for dropping a DB table.
|
||||
* @since 1.1.6
|
||||
*/
|
||||
public function dropTable($table)
|
||||
{
|
||||
return "DROP TABLE ".$this->quoteTableName($table);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @since 1.1.6
|
||||
*/
|
||||
public function truncateTable($table)
|
||||
{
|
||||
return "TRUNCATE TABLE ".$this->quoteTableName($table);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds 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 {@link 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 adding a new column.
|
||||
* @since 1.1.6
|
||||
*/
|
||||
public function addColumn($table, $column, $type)
|
||||
{
|
||||
return 'ALTER TABLE ' . $this->quoteTableName($table)
|
||||
. ' ADD ' . $this->quoteColumnName($column) . ' '
|
||||
. $this->getColumnType($type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @since 1.1.6
|
||||
*/
|
||||
public function dropColumn($table, $column)
|
||||
{
|
||||
return "ALTER TABLE ".$this->quoteTableName($table)
|
||||
." DROP COLUMN ".$this->quoteColumnName($column);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 $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.
|
||||
* @return string the SQL statement for renaming a DB column.
|
||||
* @since 1.1.6
|
||||
*/
|
||||
public function renameColumn($table, $name, $newName)
|
||||
{
|
||||
return "ALTER TABLE ".$this->quoteTableName($table)
|
||||
. " RENAME COLUMN ".$this->quoteColumnName($name)
|
||||
. " TO ".$this->quoteColumnName($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 {@link 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.
|
||||
* @since 1.1.6
|
||||
*/
|
||||
public function alterColumn($table, $column, $type)
|
||||
{
|
||||
return 'ALTER TABLE ' . $this->quoteTableName($table) . ' CHANGE '
|
||||
. $this->quoteColumnName($column) . ' '
|
||||
. $this->quoteColumnName($column) . ' '
|
||||
. $this->getColumnType($type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 $columns the name of the column to that the constraint will be added on. If there are multiple columns, separate them with commas.
|
||||
* @param string $refTable the table that the foreign key references to.
|
||||
* @param string $refColumns the name of the column that the foreign key references to. If there are multiple columns, separate them with commas.
|
||||
* @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.
|
||||
* @since 1.1.6
|
||||
*/
|
||||
public function addForeignKey($name, $table, $columns, $refTable, $refColumns, $delete=null, $update=null)
|
||||
{
|
||||
$columns=preg_split('/\s*,\s*/',$columns,-1,PREG_SPLIT_NO_EMPTY);
|
||||
foreach($columns as $i=>$col)
|
||||
$columns[$i]=$this->quoteColumnName($col);
|
||||
$refColumns=preg_split('/\s*,\s*/',$refColumns,-1,PREG_SPLIT_NO_EMPTY);
|
||||
foreach($refColumns as $i=>$col)
|
||||
$refColumns[$i]=$this->quoteColumnName($col);
|
||||
$sql='ALTER TABLE '.$this->quoteTableName($table)
|
||||
.' ADD CONSTRAINT '.$this->quoteColumnName($name)
|
||||
.' FOREIGN KEY ('.implode(', ', $columns).')'
|
||||
.' REFERENCES '.$this->quoteTableName($refTable)
|
||||
.' ('.implode(', ', $refColumns).')';
|
||||
if($delete!==null)
|
||||
$sql.=' ON DELETE '.$delete;
|
||||
if($update!==null)
|
||||
$sql.=' ON UPDATE '.$update;
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @since 1.1.6
|
||||
*/
|
||||
public function dropForeignKey($name, $table)
|
||||
{
|
||||
return 'ALTER TABLE '.$this->quoteTableName($table)
|
||||
.' DROP CONSTRAINT '.$this->quoteColumnName($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 $column the column(s) that should be included in the index. If there are multiple columns, please separate them
|
||||
* by commas. Each column name will be properly quoted by the method, unless a parenthesis is found in the name.
|
||||
* @param boolean $unique whether to add UNIQUE constraint on the created index.
|
||||
* @return string the SQL statement for creating a new index.
|
||||
* @since 1.1.6
|
||||
*/
|
||||
public function createIndex($name, $table, $column, $unique=false)
|
||||
{
|
||||
$cols=array();
|
||||
$columns=preg_split('/\s*,\s*/',$column,-1,PREG_SPLIT_NO_EMPTY);
|
||||
foreach($columns as $col)
|
||||
{
|
||||
if(strpos($col,'(')!==false)
|
||||
$cols[]=$col;
|
||||
else
|
||||
$cols[]=$this->quoteColumnName($col);
|
||||
}
|
||||
return ($unique ? 'CREATE UNIQUE INDEX ' : 'CREATE INDEX ')
|
||||
. $this->quoteTableName($name).' ON '
|
||||
. $this->quoteTableName($table).' ('.implode(', ',$cols).')';
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @since 1.1.6
|
||||
*/
|
||||
public function dropIndex($name, $table)
|
||||
{
|
||||
return 'DROP INDEX '.$this->quoteTableName($name).' ON '.$this->quoteTableName($table);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Array value can be passed since 1.1.14.
|
||||
* @return string the SQL statement for adding a primary key constraint to an existing table.
|
||||
* @since 1.1.13
|
||||
*/
|
||||
public function addPrimaryKey($name,$table,$columns)
|
||||
{
|
||||
if(is_string($columns))
|
||||
$columns=preg_split('/\s*,\s*/',$columns,-1,PREG_SPLIT_NO_EMPTY);
|
||||
foreach($columns as $i=>$col)
|
||||
$columns[$i]=$this->quoteColumnName($col);
|
||||
return 'ALTER TABLE ' . $this->quoteTableName($table) . ' ADD CONSTRAINT '
|
||||
. $this->quoteColumnName($name) . ' PRIMARY KEY ('
|
||||
. implode(', ', $columns). ' )';
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @since 1.1.13
|
||||
*/
|
||||
public function dropPrimaryKey($name,$table)
|
||||
{
|
||||
return 'ALTER TABLE ' . $this->quoteTableName($table) . ' DROP CONSTRAINT '
|
||||
. $this->quoteColumnName($name);
|
||||
}
|
||||
}
|
||||
77
framework/db/schema/CDbTableSchema.php
Normal file
77
framework/db/schema/CDbTableSchema.php
Normal file
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
/**
|
||||
* CDbTableSchema class file.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
/**
|
||||
* CDbTableSchema is the base class for representing the metadata of a database table.
|
||||
*
|
||||
* It may be extended by different DBMS driver to provide DBMS-specific table metadata.
|
||||
*
|
||||
* CDbTableSchema provides the following information about a table:
|
||||
* <ul>
|
||||
* <li>{@link name}</li>
|
||||
* <li>{@link rawName}</li>
|
||||
* <li>{@link columns}</li>
|
||||
* <li>{@link primaryKey}</li>
|
||||
* <li>{@link foreignKeys}</li>
|
||||
* <li>{@link sequenceName}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @property array $columnNames List of column names.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @package system.db.schema
|
||||
* @since 1.0
|
||||
*/
|
||||
class CDbTableSchema extends CComponent
|
||||
{
|
||||
/**
|
||||
* @var string name of this table.
|
||||
*/
|
||||
public $name;
|
||||
/**
|
||||
* @var string raw name of this table. This is the quoted version of table name with optional schema name. It can be directly used in SQLs.
|
||||
*/
|
||||
public $rawName;
|
||||
/**
|
||||
* @var string|array primary key name of this table. If composite key, an array of key names is returned.
|
||||
*/
|
||||
public $primaryKey;
|
||||
/**
|
||||
* @var string sequence name for the primary key. Null if no sequence.
|
||||
*/
|
||||
public $sequenceName;
|
||||
/**
|
||||
* @var array foreign keys of this table. The array is indexed by column name. Each value is an array of foreign table name and foreign column name.
|
||||
*/
|
||||
public $foreignKeys=array();
|
||||
/**
|
||||
* @var array column metadata of this table. Each array element is a CDbColumnSchema object, indexed by column names.
|
||||
*/
|
||||
public $columns=array();
|
||||
|
||||
/**
|
||||
* 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 CDbColumnSchema 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array list of column names
|
||||
*/
|
||||
public function getColumnNames()
|
||||
{
|
||||
return array_keys($this->columns);
|
||||
}
|
||||
}
|
||||
88
framework/db/schema/mssql/CMssqlColumnSchema.php
Normal file
88
framework/db/schema/mssql/CMssqlColumnSchema.php
Normal file
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
/**
|
||||
* CMssqlColumnSchema class file.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @author Christophe Boulain <Christophe.Boulain@gmail.com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
/**
|
||||
* CMssqlColumnSchema class describes the column meta data of a MSSQL table.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @author Christophe Boulain <Christophe.Boulain@gmail.com>
|
||||
* @package system.db.schema.mssql
|
||||
*/
|
||||
class CMssqlColumnSchema extends CDbColumnSchema
|
||||
{
|
||||
|
||||
/**
|
||||
* Initializes the column with its DB type and default value.
|
||||
* This sets up the column's PHP type, size, precision, scale as well as default value.
|
||||
* @param string $dbType the column's DB type
|
||||
* @param mixed $defaultValue the default value
|
||||
*/
|
||||
public function init($dbType, $defaultValue)
|
||||
{
|
||||
if ($defaultValue=='(NULL)')
|
||||
{
|
||||
$defaultValue=null;
|
||||
}
|
||||
parent::init($dbType, $defaultValue);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Extracts the PHP type from DB type.
|
||||
* @param string $dbType DB type
|
||||
*/
|
||||
protected function extractType($dbType)
|
||||
{
|
||||
if(strpos($dbType,'float')!==false || strpos($dbType,'real')!==false)
|
||||
$this->type='double';
|
||||
elseif(strpos($dbType,'bigint')===false && (strpos($dbType,'int')!==false || strpos($dbType,'smallint')!==false || strpos($dbType,'tinyint')))
|
||||
$this->type='integer';
|
||||
elseif(strpos($dbType,'bit')!==false)
|
||||
$this->type='boolean';
|
||||
else
|
||||
$this->type='string';
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the default value for the column.
|
||||
* The value is typecasted to correct PHP type.
|
||||
* @param mixed $defaultValue the default value obtained from metadata
|
||||
*/
|
||||
protected function extractDefault($defaultValue)
|
||||
{
|
||||
if($this->dbType==='timestamp' )
|
||||
$this->defaultValue=null;
|
||||
else
|
||||
parent::extractDefault(str_replace(array('(',')',"'"), '', $defaultValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts size, precision and scale information from column's DB type.
|
||||
* We do nothing here, since sizes and precisions have been computed before.
|
||||
* @param string $dbType the column's DB type
|
||||
*/
|
||||
protected function extractLimit($dbType)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the input value to the type that this column is of.
|
||||
* @param mixed $value input value
|
||||
* @return mixed converted value
|
||||
*/
|
||||
public function typecast($value)
|
||||
{
|
||||
if($this->type==='boolean')
|
||||
return $value ? 1 : 0;
|
||||
else
|
||||
return parent::typecast($value);
|
||||
}
|
||||
}
|
||||
338
framework/db/schema/mssql/CMssqlCommandBuilder.php
Normal file
338
framework/db/schema/mssql/CMssqlCommandBuilder.php
Normal file
@@ -0,0 +1,338 @@
|
||||
<?php
|
||||
/**
|
||||
* CMsCommandBuilder class file.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @author Christophe Boulain <Christophe.Boulain@gmail.com>
|
||||
* @author Wei Zhuo <weizhuo[at]gmail[dot]com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
/**
|
||||
* CMssqlCommandBuilder provides basic methods to create query commands for tables for Mssql Servers.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @author Christophe Boulain <Christophe.Boulain@gmail.com>
|
||||
* @author Wei Zhuo <weizhuo[at]gmail[dot]com>
|
||||
* @package system.db.schema.mssql
|
||||
*/
|
||||
class CMssqlCommandBuilder extends CDbCommandBuilder
|
||||
{
|
||||
/**
|
||||
* Creates a COUNT(*) command for a single table.
|
||||
* Override parent implementation to remove the order clause of criteria if it exists
|
||||
* @param CDbTableSchema $table the table metadata
|
||||
* @param CDbCriteria $criteria the query criteria
|
||||
* @param string $alias the alias name of the primary table. Defaults to 't'.
|
||||
* @return CDbCommand query command.
|
||||
*/
|
||||
public function createCountCommand($table,$criteria,$alias='t')
|
||||
{
|
||||
$criteria->order='';
|
||||
return parent::createCountCommand($table, $criteria,$alias);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a SELECT command for a single table.
|
||||
* Override parent implementation to check if an orderby clause if specified when querying with an offset
|
||||
* @param CDbTableSchema $table the table metadata
|
||||
* @param CDbCriteria $criteria the query criteria
|
||||
* @param string $alias the alias name of the primary table. Defaults to 't'.
|
||||
* @return CDbCommand query command.
|
||||
*/
|
||||
public function createFindCommand($table,$criteria,$alias='t')
|
||||
{
|
||||
$criteria=$this->checkCriteria($table,$criteria);
|
||||
return parent::createFindCommand($table,$criteria,$alias);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an UPDATE command.
|
||||
* Override parent implementation because mssql don't want to update an identity column
|
||||
* @param CDbTableSchema $table the table metadata
|
||||
* @param array $data list of columns to be updated (name=>value)
|
||||
* @param CDbCriteria $criteria the query criteria
|
||||
* @throws CDbException if no columns are being updated
|
||||
* @return CDbCommand update command.
|
||||
*/
|
||||
public function createUpdateCommand($table,$data,$criteria)
|
||||
{
|
||||
$criteria=$this->checkCriteria($table,$criteria);
|
||||
$fields=array();
|
||||
$values=array();
|
||||
$bindByPosition=isset($criteria->params[0]);
|
||||
$i=0;
|
||||
foreach($data as $name=>$value)
|
||||
{
|
||||
if(($column=$table->getColumn($name))!==null)
|
||||
{
|
||||
if ($table->sequenceName !== null && $column->isPrimaryKey === true) continue;
|
||||
if ($column->dbType === 'timestamp') continue;
|
||||
if($value instanceof CDbExpression)
|
||||
{
|
||||
$fields[]=$column->rawName.'='.$value->expression;
|
||||
foreach($value->params as $n=>$v)
|
||||
$values[$n]=$v;
|
||||
}
|
||||
elseif($bindByPosition)
|
||||
{
|
||||
$fields[]=$column->rawName.'=?';
|
||||
$values[]=$column->typecast($value);
|
||||
}
|
||||
else
|
||||
{
|
||||
$fields[]=$column->rawName.'='.self::PARAM_PREFIX.$i;
|
||||
$values[self::PARAM_PREFIX.$i]=$column->typecast($value);
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if($fields===array())
|
||||
throw new CDbException(Yii::t('yii','No columns are being updated for table "{table}".',
|
||||
array('{table}'=>$table->name)));
|
||||
$sql="UPDATE {$table->rawName} SET ".implode(', ',$fields);
|
||||
$sql=$this->applyJoin($sql,$criteria->join);
|
||||
$sql=$this->applyCondition($sql,$criteria->condition);
|
||||
$sql=$this->applyOrder($sql,$criteria->order);
|
||||
$sql=$this->applyLimit($sql,$criteria->limit,$criteria->offset);
|
||||
|
||||
$command=$this->getDbConnection()->createCommand($sql);
|
||||
$this->bindValues($command,array_merge($values,$criteria->params));
|
||||
|
||||
return $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a DELETE command.
|
||||
* Override parent implementation to check if an orderby clause if specified when querying with an offset
|
||||
* @param CDbTableSchema $table the table metadata
|
||||
* @param CDbCriteria $criteria the query criteria
|
||||
* @return CDbCommand delete command.
|
||||
*/
|
||||
public function createDeleteCommand($table,$criteria)
|
||||
{
|
||||
$criteria=$this->checkCriteria($table, $criteria);
|
||||
return parent::createDeleteCommand($table, $criteria);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an UPDATE command that increments/decrements certain columns.
|
||||
* Override parent implementation to check if an orderby clause if specified when querying with an offset
|
||||
* @param CDbTableSchema $table the table metadata
|
||||
* @param CDbCriteria $counters the query criteria
|
||||
* @param array $criteria counters to be updated (counter increments/decrements indexed by column names.)
|
||||
* @return CDbCommand the created command
|
||||
* @throws CException if no counter is specified
|
||||
*/
|
||||
public function createUpdateCounterCommand($table,$counters,$criteria)
|
||||
{
|
||||
$criteria=$this->checkCriteria($table, $criteria);
|
||||
return parent::createUpdateCounterCommand($table, $counters, $criteria);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a port from Prado Framework.
|
||||
*
|
||||
* Overrides parent implementation. Alters the sql to apply $limit and $offset.
|
||||
* The idea for limit with offset is done by modifying the sql on the fly
|
||||
* with numerous assumptions on the structure of the sql string.
|
||||
* The modification is done with reference to the notes from
|
||||
* http://troels.arvin.dk/db/rdbms/#select-limit-offset
|
||||
*
|
||||
* <code>
|
||||
* SELECT * FROM (
|
||||
* SELECT TOP n * FROM (
|
||||
* SELECT TOP z columns -- (z=n+skip)
|
||||
* FROM tablename
|
||||
* ORDER BY key ASC
|
||||
* ) AS FOO ORDER BY key DESC -- ('FOO' may be anything)
|
||||
* ) AS BAR ORDER BY key ASC -- ('BAR' may be anything)
|
||||
* </code>
|
||||
*
|
||||
* <b>Regular expressions are used to alter the SQL query. The resulting SQL query
|
||||
* may be malformed for complex queries.</b> The following restrictions apply
|
||||
*
|
||||
* <ul>
|
||||
* <li>
|
||||
* In particular, <b>commas</b> should <b>NOT</b>
|
||||
* be used as part of the ordering expression or identifier. Commas must only be
|
||||
* used for separating the ordering clauses.
|
||||
* </li>
|
||||
* <li>
|
||||
* In the ORDER BY clause, the column name should NOT be be qualified
|
||||
* with a table name or view name. Alias the column names or use column index.
|
||||
* </li>
|
||||
* <li>
|
||||
* No clauses should follow the ORDER BY clause, e.g. no COMPUTE or FOR clauses.
|
||||
* </li>
|
||||
* </ul>
|
||||
*
|
||||
* @param string $sql SQL query string.
|
||||
* @param integer $limit maximum number of rows, -1 to ignore limit.
|
||||
* @param integer $offset row offset, -1 to ignore offset.
|
||||
* @return string SQL with limit and offset.
|
||||
*
|
||||
* @author Wei Zhuo <weizhuo[at]gmail[dot]com>
|
||||
*/
|
||||
public function applyLimit($sql, $limit, $offset)
|
||||
{
|
||||
$limit = $limit!==null ? (int)$limit : -1;
|
||||
$offset = $offset!==null ? (int)$offset : -1;
|
||||
if ($limit > 0 && $offset <= 0) //just limit
|
||||
$sql = preg_replace('/^([\s(])*SELECT( DISTINCT)?(?!\s*TOP\s*\()/i',"\\1SELECT\\2 TOP $limit", $sql);
|
||||
elseif($limit > 0 && $offset > 0)
|
||||
$sql = $this->rewriteLimitOffsetSql($sql, $limit,$offset);
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewrite sql to apply $limit > and $offset > 0 for MSSQL database.
|
||||
* See http://troels.arvin.dk/db/rdbms/#select-limit-offset
|
||||
* @param string $sql sql query
|
||||
* @param integer $limit $limit > 0
|
||||
* @param integer $offset $offset > 0
|
||||
* @return string modified sql query applied with limit and offset.
|
||||
*
|
||||
* @author Wei Zhuo <weizhuo[at]gmail[dot]com>
|
||||
*/
|
||||
protected function rewriteLimitOffsetSql($sql, $limit, $offset)
|
||||
{
|
||||
$fetch = $limit+$offset;
|
||||
$sql = preg_replace('/^([\s(])*SELECT( DISTINCT)?(?!\s*TOP\s*\()/i',"\\1SELECT\\2 TOP $fetch", $sql);
|
||||
$ordering = $this->findOrdering($sql);
|
||||
$orginalOrdering = $this->joinOrdering($ordering, '[__outer__]');
|
||||
$reverseOrdering = $this->joinOrdering($this->reverseDirection($ordering), '[__inner__]');
|
||||
$sql = "SELECT * FROM (SELECT TOP {$limit} * FROM ($sql) as [__inner__] {$reverseOrdering}) as [__outer__] {$orginalOrdering}";
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Base on simplified syntax http://msdn2.microsoft.com/en-us/library/aa259187(SQL.80).aspx
|
||||
*
|
||||
* @param string $sql $sql
|
||||
* @return array ordering expression as key and ordering direction as value
|
||||
*
|
||||
* @author Wei Zhuo <weizhuo[at]gmail[dot]com>
|
||||
*/
|
||||
protected function findOrdering($sql)
|
||||
{
|
||||
if(!preg_match('/ORDER BY/i', $sql))
|
||||
return array();
|
||||
$matches=array();
|
||||
$ordering=array();
|
||||
preg_match_all('/(ORDER BY)[\s"\[](.*)(ASC|DESC)?(?:[\s"\[]|$|COMPUTE|FOR)/i', $sql, $matches);
|
||||
if(count($matches)>1 && count($matches[2]) > 0)
|
||||
{
|
||||
$parts = explode(',', $matches[2][0]);
|
||||
foreach($parts as $part)
|
||||
{
|
||||
$subs=array();
|
||||
if(preg_match_all('/(.*)[\s"\]](ASC|DESC)$/i', trim($part), $subs))
|
||||
{
|
||||
if(count($subs) > 1 && count($subs[2]) > 0)
|
||||
{
|
||||
$name='';
|
||||
foreach(explode('.', $subs[1][0]) as $p)
|
||||
{
|
||||
if($name!=='')
|
||||
$name.='.';
|
||||
$name.='[' . trim($p, '[]') . ']';
|
||||
}
|
||||
$ordering[$name] = $subs[2][0];
|
||||
}
|
||||
//else what?
|
||||
}
|
||||
else
|
||||
$ordering[trim($part)] = 'ASC';
|
||||
}
|
||||
}
|
||||
|
||||
// replacing column names with their alias names
|
||||
foreach($ordering as $name => $direction)
|
||||
{
|
||||
$matches = array();
|
||||
$pattern = '/\s+'.str_replace(array('[',']'), array('\[','\]'), $name).'\s+AS\s+(\[[^\]]+\])/i';
|
||||
preg_match($pattern, $sql, $matches);
|
||||
if(isset($matches[1]))
|
||||
{
|
||||
$ordering[$matches[1]] = $ordering[$name];
|
||||
unset($ordering[$name]);
|
||||
}
|
||||
}
|
||||
|
||||
return $ordering;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $orders ordering obtained from findOrdering()
|
||||
* @param string $newPrefix new table prefix to the ordering columns
|
||||
* @return string concat the orderings
|
||||
*
|
||||
* @author Wei Zhuo <weizhuo[at]gmail[dot]com>
|
||||
*/
|
||||
protected function joinOrdering($orders, $newPrefix)
|
||||
{
|
||||
if(count($orders)>0)
|
||||
{
|
||||
$str=array();
|
||||
foreach($orders as $column => $direction)
|
||||
$str[] = $column.' '.$direction;
|
||||
$orderBy = 'ORDER BY '.implode(', ', $str);
|
||||
return preg_replace('/\s+\[[^\]]+\]\.(\[[^\]]+\])/i', ' '.$newPrefix.'.\1', $orderBy);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $orders original ordering
|
||||
* @return array ordering with reversed direction.
|
||||
*
|
||||
* @author Wei Zhuo <weizhuo[at]gmail[dot]com>
|
||||
*/
|
||||
protected function reverseDirection($orders)
|
||||
{
|
||||
foreach($orders as $column => $direction)
|
||||
$orders[$column] = strtolower(trim($direction))==='desc' ? 'ASC' : 'DESC';
|
||||
return $orders;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks if the criteria has an order by clause when using offset/limit.
|
||||
* Override parent implementation to check if an orderby clause if specified when querying with an offset
|
||||
* If not, order it by pk.
|
||||
* @param CMssqlTableSchema $table table schema
|
||||
* @param CDbCriteria $criteria criteria
|
||||
* @return CDbCriteria the modified criteria
|
||||
*/
|
||||
protected function checkCriteria($table, $criteria)
|
||||
{
|
||||
if ($criteria->offset > 0 && $criteria->order==='')
|
||||
{
|
||||
$criteria->order=is_array($table->primaryKey)?implode(',',$table->primaryKey):$table->primaryKey;
|
||||
}
|
||||
return $criteria;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the expression for selecting rows with specified composite key values.
|
||||
* @param CDbTableSchema $table the table schema
|
||||
* @param array $values list of primary key values to be selected within
|
||||
* @param string $prefix column prefix (ended with dot)
|
||||
* @return string the expression for selection
|
||||
*/
|
||||
protected function createCompositeInCondition($table,$values,$prefix)
|
||||
{
|
||||
$vs=array();
|
||||
foreach($values as $value)
|
||||
{
|
||||
$c=array();
|
||||
foreach($value as $k=>$v)
|
||||
$c[]=$prefix.$table->columns[$k]->rawName.'='.$v;
|
||||
$vs[]='('.implode(' AND ',$c).')';
|
||||
}
|
||||
return '('.implode(' OR ',$vs).')';
|
||||
}
|
||||
}
|
||||
72
framework/db/schema/mssql/CMssqlPdoAdapter.php
Normal file
72
framework/db/schema/mssql/CMssqlPdoAdapter.php
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
/**
|
||||
* CMssqlPdo class file
|
||||
*
|
||||
* @author Christophe Boulain <Christophe.Boulain@gmail.com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
/**
|
||||
* This is an extension of default PDO class for mssql driver only
|
||||
* It provides some missing functionalities of pdo driver
|
||||
* @author Christophe Boulain <Christophe.Boulain@gmail.com>
|
||||
* @package system.db.schema.mssql
|
||||
*/
|
||||
class CMssqlPdoAdapter extends PDO
|
||||
{
|
||||
/**
|
||||
* Get the last inserted id value
|
||||
* MSSQL doesn't support sequence, so, argument is ignored
|
||||
*
|
||||
* @param string|null sequence name. Defaults to null
|
||||
* @return integer last inserted id
|
||||
*/
|
||||
public function lastInsertId ($sequence=NULL)
|
||||
{
|
||||
return $this->query('SELECT CAST(COALESCE(SCOPE_IDENTITY(), @@IDENTITY) AS bigint)')->fetchColumn();
|
||||
}
|
||||
|
||||
/**
|
||||
* Begin a transaction
|
||||
*
|
||||
* Is is necessary to override pdo's method, as mssql pdo drivers
|
||||
* does not support transaction
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function beginTransaction ()
|
||||
{
|
||||
$this->exec('BEGIN TRANSACTION');
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Commit a transaction
|
||||
*
|
||||
* Is is necessary to override pdo's method, as mssql pdo drivers
|
||||
* does not support transaction
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function commit ()
|
||||
{
|
||||
$this->exec('COMMIT TRANSACTION');
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rollback a transaction
|
||||
*
|
||||
* Is is necessary to override pdo's method, ac mssql pdo drivers
|
||||
* does not support transaction
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function rollBack ()
|
||||
{
|
||||
$this->exec('ROLLBACK TRANSACTION');
|
||||
return true;
|
||||
}
|
||||
}
|
||||
439
framework/db/schema/mssql/CMssqlSchema.php
Normal file
439
framework/db/schema/mssql/CMssqlSchema.php
Normal file
@@ -0,0 +1,439 @@
|
||||
<?php
|
||||
/**
|
||||
* CMssqlSchema class file.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @author Christophe Boulain <Christophe.Boulain@gmail.com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
/**
|
||||
* CMssqlSchema is the class for retrieving metadata information from a MS SQL Server database.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @author Christophe Boulain <Christophe.Boulain@gmail.com>
|
||||
* @package system.db.schema.mssql
|
||||
*/
|
||||
class CMssqlSchema extends CDbSchema
|
||||
{
|
||||
const DEFAULT_SCHEMA='dbo';
|
||||
|
||||
/**
|
||||
* @var array the abstract column types mapped to physical column types.
|
||||
* @since 1.1.6
|
||||
*/
|
||||
public $columnTypes=array(
|
||||
'pk' => 'int IDENTITY PRIMARY KEY',
|
||||
'string' => 'varchar(255)',
|
||||
'text' => 'text',
|
||||
'integer' => 'int',
|
||||
'float' => 'float',
|
||||
'decimal' => 'decimal',
|
||||
'datetime' => 'datetime',
|
||||
'timestamp' => 'timestamp',
|
||||
'time' => 'time',
|
||||
'date' => 'date',
|
||||
'binary' => 'binary',
|
||||
'boolean' => 'bit',
|
||||
);
|
||||
|
||||
/**
|
||||
* Quotes a table name for use in a query.
|
||||
* A simple table name does not schema prefix.
|
||||
* @param string $name table name
|
||||
* @return string the properly quoted table name
|
||||
* @since 1.1.6
|
||||
*/
|
||||
public function quoteSimpleTableName($name)
|
||||
{
|
||||
return '['.$name.']';
|
||||
}
|
||||
|
||||
/**
|
||||
* Quotes a column name for use in a query.
|
||||
* A simple column name does not contain prefix.
|
||||
* @param string $name column name
|
||||
* @return string the properly quoted column name
|
||||
* @since 1.1.6
|
||||
*/
|
||||
public function quoteSimpleColumnName($name)
|
||||
{
|
||||
return '['.$name.']';
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two table names.
|
||||
* The table names can be either quoted or unquoted. This method
|
||||
* will consider both cases.
|
||||
* @param string $name1 table name 1
|
||||
* @param string $name2 table name 2
|
||||
* @return boolean whether the two table names refer to the same table.
|
||||
*/
|
||||
public function compareTableNames($name1,$name2)
|
||||
{
|
||||
$name1=str_replace(array('[',']'),'',$name1);
|
||||
$name2=str_replace(array('[',']'),'',$name2);
|
||||
return parent::compareTableNames(strtolower($name1),strtolower($name2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets 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 max value of a primary key plus one (i.e. sequence trimming).
|
||||
* @param CDbTableSchema $table the table schema whose primary key sequence will be reset
|
||||
* @param integer|null $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 the max value of a primary
|
||||
* key plus one (i.e. sequence trimming).
|
||||
* @since 1.1.6
|
||||
*/
|
||||
public function resetSequence($table,$value=null)
|
||||
{
|
||||
if($table->sequenceName===null)
|
||||
return;
|
||||
if($value!==null)
|
||||
$value=(int)($value)-1;
|
||||
else
|
||||
$value=(int)$this->getDbConnection()
|
||||
->createCommand("SELECT MAX([{$table->primaryKey}]) FROM {$table->rawName}")
|
||||
->queryScalar();
|
||||
$name=strtr($table->rawName,array('['=>'',']'=>''));
|
||||
$this->getDbConnection()
|
||||
->createCommand("DBCC CHECKIDENT ('$name',RESEED,$value)")
|
||||
->execute();
|
||||
}
|
||||
|
||||
private $_normalTables=array(); // non-view tables
|
||||
/**
|
||||
* Enables or disables integrity check.
|
||||
* @param boolean $check whether to turn on or off the integrity check.
|
||||
* @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema.
|
||||
* @since 1.1.6
|
||||
*/
|
||||
public function checkIntegrity($check=true,$schema='')
|
||||
{
|
||||
$enable=$check ? 'CHECK' : 'NOCHECK';
|
||||
if(!isset($this->_normalTables[$schema]))
|
||||
$this->_normalTables[$schema]=$this->findTableNames($schema,false);
|
||||
$db=$this->getDbConnection();
|
||||
foreach($this->_normalTables[$schema] as $tableName)
|
||||
{
|
||||
$tableName=$this->quoteTableName($tableName);
|
||||
$db->createCommand("ALTER TABLE $tableName $enable CONSTRAINT ALL")->execute();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the metadata for the specified table.
|
||||
* @param string $name table name
|
||||
* @return CMssqlTableSchema driver dependent table metadata. Null if the table does not exist.
|
||||
*/
|
||||
protected function loadTable($name)
|
||||
{
|
||||
$table=new CMssqlTableSchema;
|
||||
$this->resolveTableNames($table,$name);
|
||||
//if (!in_array($table->name, $this->tableNames)) return null;
|
||||
$table->primaryKey=$this->findPrimaryKey($table);
|
||||
$table->foreignKeys=$this->findForeignKeys($table);
|
||||
if($this->findColumns($table))
|
||||
{
|
||||
return $table;
|
||||
}
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates various kinds of table names.
|
||||
* @param CMssqlTableSchema $table the table instance
|
||||
* @param string $name the unquoted table name
|
||||
*/
|
||||
protected function resolveTableNames($table,$name)
|
||||
{
|
||||
$parts=explode('.',str_replace(array('[',']'),'',$name));
|
||||
if(($c=count($parts))==3)
|
||||
{
|
||||
// Catalog name, schema name and table name provided
|
||||
$table->catalogName=$parts[0];
|
||||
$table->schemaName=$parts[1];
|
||||
$table->name=$parts[2];
|
||||
$table->rawName=$this->quoteTableName($table->catalogName).'.'.$this->quoteTableName($table->schemaName).'.'.$this->quoteTableName($table->name);
|
||||
}
|
||||
elseif ($c==2)
|
||||
{
|
||||
// Only schema name and table name provided
|
||||
$table->name=$parts[1];
|
||||
$table->schemaName=$parts[0];
|
||||
$table->rawName=$this->quoteTableName($table->schemaName).'.'.$this->quoteTableName($table->name);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Only the name given, we need to get at least the schema name
|
||||
//if (empty($this->_schemaNames)) $this->findTableNames();
|
||||
$table->name=$parts[0];
|
||||
$table->schemaName=self::DEFAULT_SCHEMA;
|
||||
$table->rawName=$this->quoteTableName($table->schemaName).'.'.$this->quoteTableName($table->name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the primary key column(s) details for the given table.
|
||||
* @param CMssqlTableSchema $table table
|
||||
* @return mixed primary keys (null if no pk, string if only 1 column pk, or array if composite pk)
|
||||
*/
|
||||
protected function findPrimaryKey($table)
|
||||
{
|
||||
$kcu='INFORMATION_SCHEMA.KEY_COLUMN_USAGE';
|
||||
$tc='INFORMATION_SCHEMA.TABLE_CONSTRAINTS';
|
||||
if (isset($table->catalogName))
|
||||
{
|
||||
$kcu=$table->catalogName.'.'.$kcu;
|
||||
$tc=$table->catalogName.'.'.$tc;
|
||||
}
|
||||
|
||||
$sql = <<<EOD
|
||||
SELECT k.column_name field_name
|
||||
FROM {$this->quoteTableName($kcu)} k
|
||||
LEFT JOIN {$this->quoteTableName($tc)} c
|
||||
ON k.table_name = c.table_name
|
||||
AND k.constraint_name = c.constraint_name
|
||||
WHERE c.constraint_type ='PRIMARY KEY'
|
||||
AND k.table_name = :table
|
||||
AND k.table_schema = :schema
|
||||
EOD;
|
||||
$command = $this->getDbConnection()->createCommand($sql);
|
||||
$command->bindValue(':table', $table->name);
|
||||
$command->bindValue(':schema', $table->schemaName);
|
||||
$primary=$command->queryColumn();
|
||||
switch (count($primary))
|
||||
{
|
||||
case 0: // No primary key on table
|
||||
$primary=null;
|
||||
break;
|
||||
case 1: // Only 1 primary key
|
||||
$primary=$primary[0];
|
||||
break;
|
||||
}
|
||||
return $primary;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets foreign relationship constraint keys and table name
|
||||
* @param CMssqlTableSchema $table table
|
||||
* @return array foreign relationship table name and keys.
|
||||
*/
|
||||
protected function findForeignKeys($table)
|
||||
{
|
||||
$rc='INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS';
|
||||
$kcu='INFORMATION_SCHEMA.KEY_COLUMN_USAGE';
|
||||
if (isset($table->catalogName))
|
||||
{
|
||||
$kcu=$table->catalogName.'.'.$kcu;
|
||||
$rc=$table->catalogName.'.'.$rc;
|
||||
}
|
||||
|
||||
//From http://msdn2.microsoft.com/en-us/library/aa175805(SQL.80).aspx
|
||||
$sql = <<<EOD
|
||||
SELECT
|
||||
KCU1.CONSTRAINT_NAME AS 'FK_CONSTRAINT_NAME'
|
||||
, KCU1.TABLE_NAME AS 'FK_TABLE_NAME'
|
||||
, KCU1.COLUMN_NAME AS 'FK_COLUMN_NAME'
|
||||
, KCU1.ORDINAL_POSITION AS 'FK_ORDINAL_POSITION'
|
||||
, KCU2.CONSTRAINT_NAME AS 'UQ_CONSTRAINT_NAME'
|
||||
, KCU2.TABLE_NAME AS 'UQ_TABLE_NAME'
|
||||
, KCU2.COLUMN_NAME AS 'UQ_COLUMN_NAME'
|
||||
, KCU2.ORDINAL_POSITION AS 'UQ_ORDINAL_POSITION'
|
||||
FROM {$this->quoteTableName($rc)} RC
|
||||
JOIN {$this->quoteTableName($kcu)} KCU1
|
||||
ON KCU1.CONSTRAINT_CATALOG = RC.CONSTRAINT_CATALOG
|
||||
AND KCU1.CONSTRAINT_SCHEMA = RC.CONSTRAINT_SCHEMA
|
||||
AND KCU1.CONSTRAINT_NAME = RC.CONSTRAINT_NAME
|
||||
JOIN {$this->quoteTableName($kcu)} KCU2
|
||||
ON KCU2.CONSTRAINT_CATALOG =
|
||||
RC.UNIQUE_CONSTRAINT_CATALOG
|
||||
AND KCU2.CONSTRAINT_SCHEMA =
|
||||
RC.UNIQUE_CONSTRAINT_SCHEMA
|
||||
AND KCU2.CONSTRAINT_NAME =
|
||||
RC.UNIQUE_CONSTRAINT_NAME
|
||||
AND KCU2.ORDINAL_POSITION = KCU1.ORDINAL_POSITION
|
||||
WHERE KCU1.TABLE_NAME = :table
|
||||
EOD;
|
||||
$command = $this->getDbConnection()->createCommand($sql);
|
||||
$command->bindValue(':table', $table->name);
|
||||
$fkeys=array();
|
||||
foreach($command->queryAll() as $info)
|
||||
{
|
||||
$fkeys[$info['FK_COLUMN_NAME']]=array($info['UQ_TABLE_NAME'],$info['UQ_COLUMN_NAME'],);
|
||||
|
||||
}
|
||||
return $fkeys;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Collects the table column metadata.
|
||||
* @param CMssqlTableSchema $table the table metadata
|
||||
* @return boolean whether the table exists in the database
|
||||
*/
|
||||
protected function findColumns($table)
|
||||
{
|
||||
$columnsTable="INFORMATION_SCHEMA.COLUMNS";
|
||||
$where=array();
|
||||
$where[]="t1.TABLE_NAME='".$table->name."'";
|
||||
if (isset($table->catalogName))
|
||||
{
|
||||
$where[]="t1.TABLE_CATALOG='".$table->catalogName."'";
|
||||
$columnsTable = $table->catalogName.'.'.$columnsTable;
|
||||
}
|
||||
if (isset($table->schemaName))
|
||||
$where[]="t1.TABLE_SCHEMA='".$table->schemaName."'";
|
||||
|
||||
$sql="SELECT t1.*, columnproperty(object_id(t1.table_schema+'.'+t1.table_name), t1.column_name, 'IsIdentity') AS IsIdentity, ".
|
||||
"CONVERT(VARCHAR, t2.value) AS Comment FROM ".$this->quoteTableName($columnsTable)." AS t1 ".
|
||||
"LEFT OUTER JOIN sys.extended_properties AS t2 ON t1.ORDINAL_POSITION = t2.minor_id AND ".
|
||||
"object_name(t2.major_id) = t1.TABLE_NAME AND t2.class=1 AND t2.class_desc='OBJECT_OR_COLUMN' AND t2.name='MS_Description' ".
|
||||
"WHERE ".join(' AND ',$where);
|
||||
try
|
||||
{
|
||||
$columns=$this->getDbConnection()->createCommand($sql)->queryAll();
|
||||
if(empty($columns))
|
||||
return false;
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach($columns as $column)
|
||||
{
|
||||
$c=$this->createColumn($column);
|
||||
if (is_array($table->primaryKey))
|
||||
$c->isPrimaryKey=in_array($c->name, $table->primaryKey);
|
||||
else
|
||||
$c->isPrimaryKey=strcasecmp($c->name,$table->primaryKey)===0;
|
||||
|
||||
$c->isForeignKey=isset($table->foreignKeys[$c->name]);
|
||||
$table->columns[$c->name]=$c;
|
||||
if ($c->autoIncrement && $table->sequenceName===null)
|
||||
$table->sequenceName=$table->name;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a table column.
|
||||
* @param array $column column metadata
|
||||
* @return CDbColumnSchema normalized column metadata
|
||||
*/
|
||||
protected function createColumn($column)
|
||||
{
|
||||
$c=new CMssqlColumnSchema;
|
||||
$c->name=$column['COLUMN_NAME'];
|
||||
$c->rawName=$this->quoteColumnName($c->name);
|
||||
$c->allowNull=$column['IS_NULLABLE']=='YES';
|
||||
if ($column['NUMERIC_PRECISION_RADIX']!==null)
|
||||
{
|
||||
// We have a numeric datatype
|
||||
$c->size=$c->precision=$column['NUMERIC_PRECISION']!==null?(int)$column['NUMERIC_PRECISION']:null;
|
||||
$c->scale=$column['NUMERIC_SCALE']!==null?(int)$column['NUMERIC_SCALE']:null;
|
||||
}
|
||||
elseif ($column['DATA_TYPE']=='image' || $column['DATA_TYPE']=='text')
|
||||
$c->size=$c->precision=null;
|
||||
else
|
||||
$c->size=$c->precision=($column['CHARACTER_MAXIMUM_LENGTH']!== null)?(int)$column['CHARACTER_MAXIMUM_LENGTH']:null;
|
||||
$c->autoIncrement=$column['IsIdentity']==1;
|
||||
$c->comment=$column['Comment']===null ? '' : $column['Comment'];
|
||||
|
||||
$c->init($column['DATA_TYPE'],$column['COLUMN_DEFAULT']);
|
||||
return $c;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* If not empty, the returned table names will be prefixed with the schema name.
|
||||
* @param boolean $includeViews whether to include views in the result. Defaults to true.
|
||||
* @return array all table names in the database.
|
||||
*/
|
||||
protected function findTableNames($schema='',$includeViews=true)
|
||||
{
|
||||
if($schema==='')
|
||||
$schema=self::DEFAULT_SCHEMA;
|
||||
if($includeViews)
|
||||
$condition="TABLE_TYPE in ('BASE TABLE','VIEW')";
|
||||
else
|
||||
$condition="TABLE_TYPE='BASE TABLE'";
|
||||
$sql=<<<EOD
|
||||
SELECT TABLE_NAME FROM [INFORMATION_SCHEMA].[TABLES]
|
||||
WHERE TABLE_SCHEMA=:schema AND $condition
|
||||
EOD;
|
||||
$command=$this->getDbConnection()->createCommand($sql);
|
||||
$command->bindParam(":schema", $schema);
|
||||
$rows=$command->queryAll();
|
||||
$names=array();
|
||||
foreach ($rows as $row)
|
||||
{
|
||||
if ($schema == self::DEFAULT_SCHEMA)
|
||||
$names[]=$row['TABLE_NAME'];
|
||||
else
|
||||
$names[]=$schema.'.'.$row['TABLE_NAME'];
|
||||
}
|
||||
|
||||
return $names;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a command builder for the database.
|
||||
* This method overrides parent implementation in order to create a MSSQL specific command builder
|
||||
* @return CDbCommandBuilder command builder instance
|
||||
*/
|
||||
protected function createCommandBuilder()
|
||||
{
|
||||
return new CMssqlCommandBuilder($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @since 1.1.6
|
||||
*/
|
||||
public function renameTable($table, $newName)
|
||||
{
|
||||
return "sp_rename '$table', '$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 $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.
|
||||
* @return string the SQL statement for renaming a DB column.
|
||||
* @since 1.1.6
|
||||
*/
|
||||
public function renameColumn($table, $name, $newName)
|
||||
{
|
||||
return "sp_rename '$table.$name', '$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 {@link 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.
|
||||
* @since 1.1.6
|
||||
*/
|
||||
public function alterColumn($table, $column, $type)
|
||||
{
|
||||
$type=$this->getColumnType($type);
|
||||
$sql='ALTER TABLE ' . $this->quoteTableName($table) . ' ALTER COLUMN '
|
||||
. $this->quoteColumnName($column) . ' '
|
||||
. $this->getColumnType($type);
|
||||
return $sql;
|
||||
}
|
||||
}
|
||||
36
framework/db/schema/mssql/CMssqlSqlsrvPdoAdapter.php
Normal file
36
framework/db/schema/mssql/CMssqlSqlsrvPdoAdapter.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/**
|
||||
* CMssqlSqlsrvPdoAdapter class file.
|
||||
*
|
||||
* @author Timur Ruziev <resurtm@gmail.com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
/**
|
||||
* This is an extension of default PDO class for MSSQL SQLSRV driver only.
|
||||
* It provides workaround of the improperly implemented functionalities of PDO SQLSRV driver.
|
||||
*
|
||||
* @author Timur Ruziev <resurtm@gmail.com>
|
||||
* @package system.db.schema.mssql
|
||||
* @since 1.1.13
|
||||
*/
|
||||
class CMssqlSqlsrvPdoAdapter extends PDO
|
||||
{
|
||||
/**
|
||||
* Returns last inserted ID value.
|
||||
* SQLSRV driver supports PDO::lastInsertId() with one peculiarity: when $sequence's value is null or empty
|
||||
* string it returns empty string. But when parameter is not specified at all it's working as expected
|
||||
* and returns actual last inserted ID (like other PDO drivers).
|
||||
*
|
||||
* @param string|null $sequence the sequence name. Defaults to null.
|
||||
* @return integer last inserted ID value.
|
||||
*/
|
||||
public function lastInsertId($sequence=null)
|
||||
{
|
||||
if(!$sequence)
|
||||
return parent::lastInsertId();
|
||||
return parent::lastInsertId($sequence);
|
||||
}
|
||||
}
|
||||
31
framework/db/schema/mssql/CMssqlTableSchema.php
Normal file
31
framework/db/schema/mssql/CMssqlTableSchema.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
/**
|
||||
* CMssqlTableSchema class file.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @author Christophe Boulain <Christophe.Boulain@gmail.com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
/**
|
||||
* CMssqlTableSchema represents the metadata for a MSSQL table.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @author Christophe Boulain <Christophe.Boulain@gmail.com>
|
||||
* @package system.db.schema.mssql
|
||||
*/
|
||||
class CMssqlTableSchema extends CDbTableSchema
|
||||
{
|
||||
/**
|
||||
* @var string name of the catalog (database) that this table belongs to.
|
||||
* Defaults to null, meaning no schema (or the current database).
|
||||
*/
|
||||
public $catalogName;
|
||||
/**
|
||||
* @var string name of the schema that this table belongs to.
|
||||
* Defaults to null, meaning no schema (or the current database owner).
|
||||
*/
|
||||
public $schemaName;
|
||||
}
|
||||
74
framework/db/schema/mysql/CMysqlColumnSchema.php
Normal file
74
framework/db/schema/mysql/CMysqlColumnSchema.php
Normal file
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
/**
|
||||
* CMysqlColumnSchema class file.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
/**
|
||||
* CMysqlColumnSchema class describes the column meta data of a MySQL table.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @package system.db.schema.mysql
|
||||
* @since 1.0
|
||||
*/
|
||||
class CMysqlColumnSchema extends CDbColumnSchema
|
||||
{
|
||||
/**
|
||||
* Extracts the PHP type from DB type.
|
||||
* @param string $dbType DB type
|
||||
*/
|
||||
protected function extractType($dbType)
|
||||
{
|
||||
if(strncmp($dbType,'enum',4)===0)
|
||||
$this->type='string';
|
||||
elseif(strpos($dbType,'float')!==false || strpos($dbType,'double')!==false)
|
||||
$this->type='double';
|
||||
elseif(strpos($dbType,'bool')!==false)
|
||||
$this->type='boolean';
|
||||
elseif(strpos($dbType,'int')===0 && strpos($dbType,'unsigned')===false || preg_match('/(bit|tinyint|smallint|mediumint)/',$dbType))
|
||||
$this->type='integer';
|
||||
else
|
||||
$this->type='string';
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the default value for the column.
|
||||
* The value is typecasted to correct PHP type.
|
||||
* @param mixed $defaultValue the default value obtained from metadata
|
||||
*/
|
||||
protected function extractDefault($defaultValue)
|
||||
{
|
||||
if(strncmp($this->dbType,'bit',3)===0)
|
||||
$this->defaultValue=bindec(trim($defaultValue,'b\''));
|
||||
elseif($this->dbType==='timestamp' && $defaultValue==='CURRENT_TIMESTAMP')
|
||||
$this->defaultValue=null;
|
||||
else
|
||||
parent::extractDefault($defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts size, precision and scale information from column's DB type.
|
||||
* @param string $dbType the column's DB type
|
||||
*/
|
||||
protected function extractLimit($dbType)
|
||||
{
|
||||
if (strncmp($dbType, 'enum', 4)===0 && preg_match('/\(([\'"])(.*)\\1\)/',$dbType,$matches))
|
||||
{
|
||||
// explode by (single or double) quote and comma (ENUM values may contain commas)
|
||||
$values = explode($matches[1].','.$matches[1], $matches[2]);
|
||||
$size = 0;
|
||||
foreach($values as $value)
|
||||
{
|
||||
if(($n=strlen($value)) > $size)
|
||||
$size=$n;
|
||||
}
|
||||
$this->size = $this->precision = $size;
|
||||
}
|
||||
else
|
||||
parent::extractLimit($dbType);
|
||||
}
|
||||
}
|
||||
43
framework/db/schema/mysql/CMysqlCommandBuilder.php
Normal file
43
framework/db/schema/mysql/CMysqlCommandBuilder.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
/**
|
||||
* CMysqlCommandBuilder class file.
|
||||
*
|
||||
* @author Carsten Brandt <mail@cebe.cc>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
/**
|
||||
* CMysqlCommandBuilder provides basic methods to create query commands for tables.
|
||||
*
|
||||
* @author Carsten Brandt <mail@cebe.cc>
|
||||
* @package system.db.schema.mysql
|
||||
* @since 1.1.13
|
||||
*/
|
||||
class CMysqlCommandBuilder extends CDbCommandBuilder
|
||||
{
|
||||
/**
|
||||
* Alters the SQL to apply JOIN clause.
|
||||
* This method handles the mysql specific syntax where JOIN has to come before SET in UPDATE statement
|
||||
* and for DELETE where JOIN has to be after FROM part.
|
||||
* @param string $sql the SQL statement to be altered
|
||||
* @param string $join the JOIN clause (starting with join type, such as INNER JOIN)
|
||||
* @return string the altered SQL statement
|
||||
*/
|
||||
public function applyJoin($sql,$join)
|
||||
{
|
||||
if($join=='')
|
||||
return $sql;
|
||||
|
||||
if(strpos($sql,'UPDATE')===0 && ($pos=strpos($sql,'SET'))!==false)
|
||||
return substr($sql,0,$pos).$join.' '.substr($sql,$pos);
|
||||
elseif(strpos($sql,'DELETE FROM ')===0)
|
||||
{
|
||||
$tableName=substr($sql,12);
|
||||
return "DELETE {$tableName} FROM {$tableName} ".$join;
|
||||
}
|
||||
else
|
||||
return $sql.' '.$join;
|
||||
}
|
||||
}
|
||||
361
framework/db/schema/mysql/CMysqlSchema.php
Normal file
361
framework/db/schema/mysql/CMysqlSchema.php
Normal file
@@ -0,0 +1,361 @@
|
||||
<?php
|
||||
/**
|
||||
* CMysqlSchema class file.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
/**
|
||||
* CMysqlSchema is the class for retrieving metadata information from a MySQL database (version 4.1.x and 5.x).
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @package system.db.schema.mysql
|
||||
* @since 1.0
|
||||
*/
|
||||
class CMysqlSchema extends CDbSchema
|
||||
{
|
||||
/**
|
||||
* @var array the abstract column types mapped to physical column types.
|
||||
* @since 1.1.6
|
||||
*/
|
||||
public $columnTypes=array(
|
||||
'pk' => 'int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY',
|
||||
'string' => 'varchar(255)',
|
||||
'text' => 'text',
|
||||
'integer' => 'int(11)',
|
||||
'float' => 'float',
|
||||
'decimal' => 'decimal',
|
||||
'datetime' => 'datetime',
|
||||
'timestamp' => 'timestamp',
|
||||
'time' => 'time',
|
||||
'date' => 'date',
|
||||
'binary' => 'blob',
|
||||
'boolean' => 'tinyint(1)',
|
||||
'money' => 'decimal(19,4)',
|
||||
);
|
||||
|
||||
/**
|
||||
* Quotes a table name for use in a query.
|
||||
* A simple table name does not schema prefix.
|
||||
* @param string $name table name
|
||||
* @return string the properly quoted table name
|
||||
* @since 1.1.6
|
||||
*/
|
||||
public function quoteSimpleTableName($name)
|
||||
{
|
||||
return '`'.$name.'`';
|
||||
}
|
||||
|
||||
/**
|
||||
* Quotes a column name for use in a query.
|
||||
* A simple column name does not contain prefix.
|
||||
* @param string $name column name
|
||||
* @return string the properly quoted column name
|
||||
* @since 1.1.6
|
||||
*/
|
||||
public function quoteSimpleColumnName($name)
|
||||
{
|
||||
return '`'.$name.'`';
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two table names.
|
||||
* The table names can be either quoted or unquoted. This method
|
||||
* will consider both cases.
|
||||
* @param string $name1 table name 1
|
||||
* @param string $name2 table name 2
|
||||
* @return boolean whether the two table names refer to the same table.
|
||||
*/
|
||||
public function compareTableNames($name1,$name2)
|
||||
{
|
||||
return parent::compareTableNames(strtolower($name1),strtolower($name2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets 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 max value of a primary key plus one (i.e. sequence trimming).
|
||||
* @param CDbTableSchema $table the table schema whose primary key sequence will be reset
|
||||
* @param integer|null $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 the max value of a primary
|
||||
* key plus one (i.e. sequence trimming).
|
||||
* @since 1.1
|
||||
*/
|
||||
public function resetSequence($table,$value=null)
|
||||
{
|
||||
if($table->sequenceName===null)
|
||||
return;
|
||||
if($value!==null)
|
||||
$value=(int)$value;
|
||||
else
|
||||
{
|
||||
$value=(int)$this->getDbConnection()
|
||||
->createCommand("SELECT MAX(`{$table->primaryKey}`) FROM {$table->rawName}")
|
||||
->queryScalar();
|
||||
$value++;
|
||||
}
|
||||
$this->getDbConnection()
|
||||
->createCommand("ALTER TABLE {$table->rawName} AUTO_INCREMENT=$value")
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables integrity check.
|
||||
* @param boolean $check whether to turn on or off the integrity check.
|
||||
* @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema.
|
||||
* @since 1.1
|
||||
*/
|
||||
public function checkIntegrity($check=true,$schema='')
|
||||
{
|
||||
$this->getDbConnection()->createCommand('SET FOREIGN_KEY_CHECKS='.($check?1:0))->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the metadata for the specified table.
|
||||
* @param string $name table name
|
||||
* @return CMysqlTableSchema driver dependent table metadata. Null if the table does not exist.
|
||||
*/
|
||||
protected function loadTable($name)
|
||||
{
|
||||
$table=new CMysqlTableSchema;
|
||||
$this->resolveTableNames($table,$name);
|
||||
|
||||
if($this->findColumns($table))
|
||||
{
|
||||
$this->findConstraints($table);
|
||||
return $table;
|
||||
}
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates various kinds of table names.
|
||||
* @param CMysqlTableSchema $table the table instance
|
||||
* @param string $name the unquoted table name
|
||||
*/
|
||||
protected function resolveTableNames($table,$name)
|
||||
{
|
||||
$parts=explode('.',str_replace(array('`','"'),'',$name));
|
||||
if(isset($parts[1]))
|
||||
{
|
||||
$table->schemaName=$parts[0];
|
||||
$table->name=$parts[1];
|
||||
$table->rawName=$this->quoteTableName($table->schemaName).'.'.$this->quoteTableName($table->name);
|
||||
}
|
||||
else
|
||||
{
|
||||
$table->name=$parts[0];
|
||||
$table->rawName=$this->quoteTableName($table->name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects the table column metadata.
|
||||
* @param CMysqlTableSchema $table the table metadata
|
||||
* @return boolean whether the table exists in the database
|
||||
*/
|
||||
protected function findColumns($table)
|
||||
{
|
||||
$sql='SHOW FULL COLUMNS FROM '.$table->rawName;
|
||||
try
|
||||
{
|
||||
$columns=$this->getDbConnection()->createCommand($sql)->queryAll();
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
foreach($columns as $column)
|
||||
{
|
||||
$c=$this->createColumn($column);
|
||||
$table->columns[$c->name]=$c;
|
||||
if($c->isPrimaryKey)
|
||||
{
|
||||
if($table->primaryKey===null)
|
||||
$table->primaryKey=$c->name;
|
||||
elseif(is_string($table->primaryKey))
|
||||
$table->primaryKey=array($table->primaryKey,$c->name);
|
||||
else
|
||||
$table->primaryKey[]=$c->name;
|
||||
if($c->autoIncrement)
|
||||
$table->sequenceName='';
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a table column.
|
||||
* @param array $column column metadata
|
||||
* @return CDbColumnSchema normalized column metadata
|
||||
*/
|
||||
protected function createColumn($column)
|
||||
{
|
||||
$c=new CMysqlColumnSchema;
|
||||
$c->name=$column['Field'];
|
||||
$c->rawName=$this->quoteColumnName($c->name);
|
||||
$c->allowNull=$column['Null']==='YES';
|
||||
$c->isPrimaryKey=strpos($column['Key'],'PRI')!==false;
|
||||
$c->isForeignKey=false;
|
||||
$c->init($column['Type'],$column['Default']);
|
||||
$c->autoIncrement=strpos(strtolower($column['Extra']),'auto_increment')!==false;
|
||||
if(isset($column['Comment']))
|
||||
$c->comment=$column['Comment'];
|
||||
|
||||
return $c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return float server version.
|
||||
*/
|
||||
protected function getServerVersion()
|
||||
{
|
||||
$version=$this->getDbConnection()->getAttribute(PDO::ATTR_SERVER_VERSION);
|
||||
$digits=array();
|
||||
preg_match('/(\d+)\.(\d+)\.(\d+)/', $version, $digits);
|
||||
return floatval($digits[1].'.'.$digits[2].$digits[3]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects the foreign key column details for the given table.
|
||||
* @param CMysqlTableSchema $table the table metadata
|
||||
*/
|
||||
protected function findConstraints($table)
|
||||
{
|
||||
$row=$this->getDbConnection()->createCommand('SHOW CREATE TABLE '.$table->rawName)->queryRow();
|
||||
$matches=array();
|
||||
$regexp='/FOREIGN KEY\s+\(([^\)]+)\)\s+REFERENCES\s+([^\(^\s]+)\s*\(([^\)]+)\)/mi';
|
||||
foreach($row as $sql)
|
||||
{
|
||||
if(preg_match_all($regexp,$sql,$matches,PREG_SET_ORDER))
|
||||
break;
|
||||
}
|
||||
foreach($matches as $match)
|
||||
{
|
||||
$keys=array_map('trim',explode(',',str_replace(array('`','"'),'',$match[1])));
|
||||
$fks=array_map('trim',explode(',',str_replace(array('`','"'),'',$match[3])));
|
||||
foreach($keys as $k=>$name)
|
||||
{
|
||||
$table->foreignKeys[$name]=array(str_replace(array('`','"'),'',$match[2]),$fks[$k]);
|
||||
if(isset($table->columns[$name]))
|
||||
$table->columns[$name]->isForeignKey=true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* If not empty, the returned table names will be prefixed with the schema name.
|
||||
* @return array all table names in the database.
|
||||
*/
|
||||
protected function findTableNames($schema='')
|
||||
{
|
||||
if($schema==='')
|
||||
return $this->getDbConnection()->createCommand('SHOW TABLES')->queryColumn();
|
||||
$names=$this->getDbConnection()->createCommand('SHOW TABLES FROM '.$this->quoteTableName($schema))->queryColumn();
|
||||
foreach($names as &$name)
|
||||
$name=$schema.'.'.$name;
|
||||
return $names;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a command builder for the database.
|
||||
* This method overrides parent implementation in order to create a MySQL specific command builder
|
||||
* @return CDbCommandBuilder command builder instance
|
||||
* @since 1.1.13
|
||||
*/
|
||||
protected function createCommandBuilder()
|
||||
{
|
||||
return new CMysqlCommandBuilder($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 $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.
|
||||
* @throws CDbException if specified column is not found in given table
|
||||
* @return string the SQL statement for renaming a DB column.
|
||||
* @since 1.1.6
|
||||
*/
|
||||
public function renameColumn($table, $name, $newName)
|
||||
{
|
||||
$db=$this->getDbConnection();
|
||||
$row=$db->createCommand('SHOW CREATE TABLE '.$db->quoteTableName($table))->queryRow();
|
||||
if($row===false)
|
||||
throw new CDbException(Yii::t('yii','Unable to find "{column}" in table "{table}".',array('{column}'=>$name,'{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===$name)
|
||||
{
|
||||
return "ALTER TABLE ".$db->quoteTableName($table)
|
||||
. " CHANGE ".$db->quoteColumnName($name)
|
||||
. ' '.$db->quoteColumnName($newName).' '.$matches[2][$i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// try to give back a SQL anyway
|
||||
return "ALTER TABLE ".$db->quoteTableName($table)
|
||||
. " CHANGE ".$db->quoteColumnName($name).' '.$newName;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @since 1.1.6
|
||||
*/
|
||||
public function dropForeignKey($name, $table)
|
||||
{
|
||||
return 'ALTER TABLE '.$this->quoteTableName($table)
|
||||
.' DROP FOREIGN KEY '.$this->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.
|
||||
* @since 1.1.13
|
||||
*/
|
||||
public function dropPrimaryKey($name,$table)
|
||||
{
|
||||
return 'ALTER TABLE ' . $this->quoteTableName($table) . ' DROP PRIMARY KEY';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a SQL statement for adding a primary key constraint to a table.
|
||||
* @param string $name not used in the MySQL syntax, the primary key is always called PRIMARY and is reserved.
|
||||
* @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.
|
||||
* @since 1.1.14
|
||||
*/
|
||||
public function addPrimaryKey($name,$table,$columns)
|
||||
{
|
||||
if(is_string($columns))
|
||||
$columns=preg_split('/\s*,\s*/',$columns,-1,PREG_SPLIT_NO_EMPTY);
|
||||
foreach($columns as $i=>$col)
|
||||
$columns[$i]=$this->quoteColumnName($col);
|
||||
return 'ALTER TABLE ' . $this->quoteTableName($table) . ' ADD PRIMARY KEY ('
|
||||
. implode(', ', $columns). ' )';
|
||||
}
|
||||
}
|
||||
25
framework/db/schema/mysql/CMysqlTableSchema.php
Normal file
25
framework/db/schema/mysql/CMysqlTableSchema.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
/**
|
||||
* CMysqlTableSchema class file.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
/**
|
||||
* CMysqlTableSchema represents the metadata for a MySQL table.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @package system.db.schema.mysql
|
||||
* @since 1.0
|
||||
*/
|
||||
class CMysqlTableSchema extends CDbTableSchema
|
||||
{
|
||||
/**
|
||||
* @var string name of the schema (database) that this table belongs to.
|
||||
* Defaults to null, meaning no schema (or the current database).
|
||||
*/
|
||||
public $schemaName;
|
||||
}
|
||||
65
framework/db/schema/oci/COciColumnSchema.php
Normal file
65
framework/db/schema/oci/COciColumnSchema.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
/**
|
||||
* COciColumnSchema class file.
|
||||
*
|
||||
* @author Ricardo Grana <rickgrana@yahoo.com.br>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
/**
|
||||
* COciColumnSchema class describes the column meta data of an Oracle table.
|
||||
*
|
||||
* @author Ricardo Grana <rickgrana@yahoo.com.br>
|
||||
* @package system.db.schema.oci
|
||||
*/
|
||||
class COciColumnSchema extends CDbColumnSchema
|
||||
{
|
||||
/**
|
||||
* Extracts the PHP type from DB type.
|
||||
* @param string $dbType DB type
|
||||
* @return string
|
||||
*/
|
||||
protected function extractOraType($dbType){
|
||||
if(strpos($dbType,'FLOAT')!==false) return 'double';
|
||||
|
||||
if (strpos($dbType,'NUMBER')!==false || strpos($dbType,'INTEGER')!==false)
|
||||
{
|
||||
if(strpos($dbType,'(') && preg_match('/\((.*)\)/',$dbType,$matches))
|
||||
{
|
||||
$values=explode(',',$matches[1]);
|
||||
if(isset($values[1]) and (((int)$values[1]) > 0))
|
||||
return 'double';
|
||||
else
|
||||
return 'integer';
|
||||
}
|
||||
else
|
||||
return 'double';
|
||||
}
|
||||
else
|
||||
return 'string';
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the PHP type from DB type.
|
||||
* @param string $dbType DB type
|
||||
*/
|
||||
protected function extractType($dbType)
|
||||
{
|
||||
$this->type=$this->extractOraType($dbType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the default value for the column.
|
||||
* The value is typecasted to correct PHP type.
|
||||
* @param mixed $defaultValue the default value obtained from metadata
|
||||
*/
|
||||
protected function extractDefault($defaultValue)
|
||||
{
|
||||
if(stripos($defaultValue,'timestamp')!==false)
|
||||
$this->defaultValue=null;
|
||||
else
|
||||
parent::extractDefault($defaultValue);
|
||||
}
|
||||
}
|
||||
147
framework/db/schema/oci/COciCommandBuilder.php
Normal file
147
framework/db/schema/oci/COciCommandBuilder.php
Normal file
@@ -0,0 +1,147 @@
|
||||
<?php
|
||||
/**
|
||||
* COciCommandBuilder class file.
|
||||
*
|
||||
* @author Ricardo Grana <rickgrana@yahoo.com.br>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
/**
|
||||
* COciCommandBuilder provides basic methods to create query commands for tables.
|
||||
*
|
||||
* @author Ricardo Grana <rickgrana@yahoo.com.br>
|
||||
* @package system.db.schema.oci
|
||||
*/
|
||||
class COciCommandBuilder extends CDbCommandBuilder
|
||||
{
|
||||
/**
|
||||
* @var integer the last insertion ID
|
||||
*/
|
||||
public $returnID;
|
||||
|
||||
/**
|
||||
* Returns the last insertion ID for the specified table.
|
||||
* @param mixed $table the table schema ({@link CDbTableSchema}) or the table name (string).
|
||||
* @return mixed last insertion id. Null is returned if no sequence name.
|
||||
*/
|
||||
public function getLastInsertID($table)
|
||||
{
|
||||
return $this->returnID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alters the SQL to apply LIMIT and OFFSET.
|
||||
* Default implementation is applicable for PostgreSQL, MySQL and SQLite.
|
||||
* @param string $sql SQL query string without LIMIT and OFFSET.
|
||||
* @param integer $limit maximum number of rows, -1 to ignore limit.
|
||||
* @param integer $offset row offset, -1 to ignore offset.
|
||||
* @return string SQL with LIMIT and OFFSET
|
||||
*/
|
||||
public function applyLimit($sql,$limit,$offset)
|
||||
{
|
||||
if (($limit < 0) and ($offset < 0)) return $sql;
|
||||
|
||||
$filters = array();
|
||||
if($offset>0){
|
||||
$filters[] = 'rowNumId > '.(int)$offset;
|
||||
}
|
||||
|
||||
if($limit>=0){
|
||||
$filters[]= 'rownum <= '.(int)$limit;
|
||||
}
|
||||
|
||||
if (count($filters) > 0){
|
||||
$filter = implode(' and ', $filters);
|
||||
$filter= " WHERE ".$filter;
|
||||
}else{
|
||||
$filter = '';
|
||||
}
|
||||
|
||||
|
||||
$sql = <<<EOD
|
||||
WITH USER_SQL AS ({$sql}),
|
||||
PAGINATION AS (SELECT USER_SQL.*, rownum as rowNumId FROM USER_SQL)
|
||||
SELECT *
|
||||
FROM PAGINATION
|
||||
{$filter}
|
||||
EOD;
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an INSERT command.
|
||||
* @param mixed $table the table schema ({@link CDbTableSchema}) or the table name (string).
|
||||
* @param array $data data to be inserted (column name=>column value). If a key is not a valid column name, the corresponding value will be ignored.
|
||||
* @return CDbCommand insert command
|
||||
*/
|
||||
public function createInsertCommand($table,$data)
|
||||
{
|
||||
$this->ensureTable($table);
|
||||
$fields=array();
|
||||
$values=array();
|
||||
$placeholders=array();
|
||||
$i=0;
|
||||
foreach($data as $name=>$value)
|
||||
{
|
||||
if(($column=$table->getColumn($name))!==null && ($value!==null || $column->allowNull))
|
||||
{
|
||||
$fields[]=$column->rawName;
|
||||
if($value instanceof CDbExpression)
|
||||
{
|
||||
$placeholders[]=$value->expression;
|
||||
foreach($value->params as $n=>$v)
|
||||
$values[$n]=$v;
|
||||
}
|
||||
else
|
||||
{
|
||||
$placeholders[]=self::PARAM_PREFIX.$i;
|
||||
$values[self::PARAM_PREFIX.$i]=$column->typecast($value);
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$sql="INSERT INTO {$table->rawName} (".implode(', ',$fields).') VALUES ('.implode(', ',$placeholders).')';
|
||||
|
||||
if(is_string($table->primaryKey) && ($column=$table->getColumn($table->primaryKey))!==null && $column->type!=='string')
|
||||
{
|
||||
$sql.=' RETURNING '.$column->rawName.' INTO :RETURN_ID';
|
||||
$command=$this->getDbConnection()->createCommand($sql);
|
||||
$command->bindParam(':RETURN_ID', $this->returnID, PDO::PARAM_INT, 12);
|
||||
$table->sequenceName='RETURN_ID';
|
||||
}
|
||||
else
|
||||
$command=$this->getDbConnection()->createCommand($sql);
|
||||
|
||||
foreach($values as $name=>$value)
|
||||
$command->bindValue($name,$value);
|
||||
|
||||
return $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a multiple INSERT command.
|
||||
* This method could be used to achieve better performance during insertion of the large
|
||||
* amount of data into the database tables.
|
||||
* @param mixed $table the table schema ({@link CDbTableSchema}) or the table name (string).
|
||||
* @param array[] $data list data to be inserted, each value should be an array in format (column name=>column value).
|
||||
* If a key is not a valid column name, the corresponding value will be ignored.
|
||||
* @return CDbCommand multiple insert command
|
||||
* @since 1.1.14
|
||||
*/
|
||||
public function createMultipleInsertCommand($table,array $data)
|
||||
{
|
||||
$templates=array(
|
||||
'main'=>'INSERT ALL {{rowInsertValues}} SELECT * FROM dual',
|
||||
'columnInsertValue'=>'{{value}}',
|
||||
'columnInsertValueGlue'=>', ',
|
||||
'rowInsertValue'=>'INTO {{tableName}} ({{columnInsertNames}}) VALUES ({{columnInsertValues}})',
|
||||
'rowInsertValueGlue'=>' ',
|
||||
'columnInsertNameGlue'=>', ',
|
||||
);
|
||||
return $this->composeMultipleInsertCommand($table,$data,$templates);
|
||||
}
|
||||
}
|
||||
411
framework/db/schema/oci/COciSchema.php
Normal file
411
framework/db/schema/oci/COciSchema.php
Normal file
@@ -0,0 +1,411 @@
|
||||
<?php
|
||||
/**
|
||||
* COciSchema class file.
|
||||
*
|
||||
* @author Ricardo Grana <rickgrana@yahoo.com.br>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
/**
|
||||
* COciSchema is the class for retrieving metadata information from an Oracle database.
|
||||
*
|
||||
* @property string $defaultSchema Default schema.
|
||||
*
|
||||
* @author Ricardo Grana <rickgrana@yahoo.com.br>
|
||||
* @package system.db.schema.oci
|
||||
*/
|
||||
class COciSchema extends CDbSchema
|
||||
{
|
||||
private $_defaultSchema = '';
|
||||
|
||||
/**
|
||||
* @var array the abstract column types mapped to physical column types.
|
||||
* @since 1.1.6
|
||||
*/
|
||||
public $columnTypes=array(
|
||||
'pk' => 'NUMBER(10) NOT NULL PRIMARY KEY',
|
||||
'string' => 'VARCHAR2(255)',
|
||||
'text' => 'CLOB',
|
||||
'integer' => 'NUMBER(10)',
|
||||
'float' => 'NUMBER',
|
||||
'decimal' => 'NUMBER',
|
||||
'datetime' => 'TIMESTAMP',
|
||||
'timestamp' => 'TIMESTAMP',
|
||||
'time' => 'TIMESTAMP',
|
||||
'date' => 'DATE',
|
||||
'binary' => 'BLOB',
|
||||
'boolean' => 'NUMBER(1)',
|
||||
'money' => 'NUMBER(19,4)',
|
||||
);
|
||||
|
||||
/**
|
||||
* Quotes a table name for use in a query.
|
||||
* A simple table name does not schema prefix.
|
||||
* @param string $name table name
|
||||
* @return string the properly quoted table name
|
||||
* @since 1.1.6
|
||||
*/
|
||||
public function quoteSimpleTableName($name)
|
||||
{
|
||||
return '"'.$name.'"';
|
||||
}
|
||||
|
||||
/**
|
||||
* Quotes a column name for use in a query.
|
||||
* A simple column name does not contain prefix.
|
||||
* @param string $name column name
|
||||
* @return string the properly quoted column name
|
||||
* @since 1.1.6
|
||||
*/
|
||||
public function quoteSimpleColumnName($name)
|
||||
{
|
||||
return '"'.$name.'"';
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a command builder for the database.
|
||||
* This method may be overridden by child classes to create a DBMS-specific command builder.
|
||||
* @return CDbCommandBuilder command builder instance
|
||||
*/
|
||||
protected function createCommandBuilder()
|
||||
{
|
||||
return new COciCommandBuilder($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $schema default schema.
|
||||
*/
|
||||
public function setDefaultSchema($schema)
|
||||
{
|
||||
$this->_defaultSchema=$schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string default schema.
|
||||
*/
|
||||
public function getDefaultSchema()
|
||||
{
|
||||
if (!strlen($this->_defaultSchema))
|
||||
{
|
||||
$this->setDefaultSchema(strtoupper($this->getDbConnection()->username));
|
||||
}
|
||||
|
||||
return $this->_defaultSchema;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $table table name with optional schema name prefix, uses default schema name prefix is not provided.
|
||||
* @return array tuple as ($schemaName,$tableName)
|
||||
*/
|
||||
protected function getSchemaTableName($table)
|
||||
{
|
||||
$table = strtoupper($table);
|
||||
if(count($parts= explode('.', str_replace('"','',$table))) > 1)
|
||||
return array($parts[0], $parts[1]);
|
||||
else
|
||||
return array($this->getDefaultSchema(),$parts[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the metadata for the specified table.
|
||||
* @param string $name table name
|
||||
* @return CDbTableSchema driver dependent table metadata.
|
||||
*/
|
||||
protected function loadTable($name)
|
||||
{
|
||||
$table=new COciTableSchema;
|
||||
$this->resolveTableNames($table,$name);
|
||||
|
||||
if(!$this->findColumns($table))
|
||||
return null;
|
||||
$this->findConstraints($table);
|
||||
|
||||
return $table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates various kinds of table names.
|
||||
* @param COciTableSchema $table the table instance
|
||||
* @param string $name the unquoted table name
|
||||
*/
|
||||
protected function resolveTableNames($table,$name)
|
||||
{
|
||||
$parts=explode('.',str_replace('"','',$name));
|
||||
if(isset($parts[1]))
|
||||
{
|
||||
$schemaName=$parts[0];
|
||||
$tableName=$parts[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
$schemaName=$this->getDefaultSchema();
|
||||
$tableName=$parts[0];
|
||||
}
|
||||
|
||||
$table->name=$tableName;
|
||||
$table->schemaName=$schemaName;
|
||||
if($schemaName===$this->getDefaultSchema())
|
||||
$table->rawName=$this->quoteTableName($tableName);
|
||||
else
|
||||
$table->rawName=$this->quoteTableName($schemaName).'.'.$this->quoteTableName($tableName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects the table column metadata.
|
||||
* @param COciTableSchema $table the table metadata
|
||||
* @return boolean whether the table exists in the database
|
||||
*/
|
||||
protected function findColumns($table)
|
||||
{
|
||||
$schemaName=$table->schemaName;
|
||||
$tableName=$table->name;
|
||||
|
||||
$sql=<<<EOD
|
||||
SELECT a.column_name, a.data_type ||
|
||||
case
|
||||
when data_precision is not null
|
||||
then '(' || a.data_precision ||
|
||||
case when a.data_scale > 0 then ',' || a.data_scale else '' end
|
||||
|| ')'
|
||||
when data_type = 'DATE' then ''
|
||||
when data_type = 'NUMBER' then ''
|
||||
else '(' || to_char(a.data_length) || ')'
|
||||
end as data_type,
|
||||
a.nullable, a.data_default,
|
||||
( SELECT D.constraint_type
|
||||
FROM ALL_CONS_COLUMNS C
|
||||
inner join ALL_constraints D on D.OWNER = C.OWNER and D.constraint_name = C.constraint_name
|
||||
WHERE C.OWNER = B.OWNER
|
||||
and C.table_name = B.object_name
|
||||
and C.column_name = A.column_name
|
||||
and D.constraint_type = 'P') as Key,
|
||||
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 user_col_comments com ON (A.table_name = com.table_name AND A.column_name = com.column_name)
|
||||
WHERE
|
||||
a.owner = '{$schemaName}'
|
||||
and (b.object_type = 'TABLE' or b.object_type = 'VIEW')
|
||||
and b.object_name = '{$tableName}'
|
||||
ORDER by a.column_id
|
||||
EOD;
|
||||
|
||||
$command=$this->getDbConnection()->createCommand($sql);
|
||||
|
||||
if(($columns=$command->queryAll())===array()){
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach($columns as $column)
|
||||
{
|
||||
$c=$this->createColumn($column);
|
||||
|
||||
$table->columns[$c->name]=$c;
|
||||
if($c->isPrimaryKey)
|
||||
{
|
||||
if($table->primaryKey===null)
|
||||
$table->primaryKey=$c->name;
|
||||
elseif(is_string($table->primaryKey))
|
||||
$table->primaryKey=array($table->primaryKey,$c->name);
|
||||
else
|
||||
$table->primaryKey[]=$c->name;
|
||||
$table->sequenceName='';
|
||||
$c->autoIncrement=true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a table column.
|
||||
* @param array $column column metadata
|
||||
* @return CDbColumnSchema normalized column metadata
|
||||
*/
|
||||
protected function createColumn($column)
|
||||
{
|
||||
$c=new COciColumnSchema;
|
||||
$c->name=$column['COLUMN_NAME'];
|
||||
$c->rawName=$this->quoteColumnName($c->name);
|
||||
$c->allowNull=$column['NULLABLE']==='Y';
|
||||
$c->isPrimaryKey=strpos($column['KEY'],'P')!==false;
|
||||
$c->isForeignKey=false;
|
||||
$c->init($column['DATA_TYPE'],$column['DATA_DEFAULT']);
|
||||
$c->comment=$column['COLUMN_COMMENT']===null ? '' : $column['COLUMN_COMMENT'];
|
||||
|
||||
return $c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects the primary and foreign key column details for the given table.
|
||||
* @param COciTableSchema $table the table metadata
|
||||
*/
|
||||
protected function findConstraints($table)
|
||||
{
|
||||
$sql=<<<EOD
|
||||
SELECT D.constraint_type as 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 = '{$table->schemaName}'
|
||||
and C.table_name = '{$table->name}'
|
||||
and D.constraint_type <> 'P'
|
||||
order by d.constraint_name, c.position
|
||||
EOD;
|
||||
$command=$this->getDbConnection()->createCommand($sql);
|
||||
foreach($command->queryAll() as $row)
|
||||
{
|
||||
if($row['CONSTRAINT_TYPE']==='R') // foreign key
|
||||
{
|
||||
$name = $row["COLUMN_NAME"];
|
||||
$table->foreignKeys[$name]=array($row["TABLE_REF"], $row["COLUMN_REF"]);
|
||||
if(isset($table->columns[$name]))
|
||||
$table->columns[$name]->isForeignKey=true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* If not empty, the returned table names will be prefixed with the schema name.
|
||||
* @return array all table names in the database.
|
||||
*/
|
||||
protected function findTableNames($schema='')
|
||||
{
|
||||
if($schema==='')
|
||||
{
|
||||
$sql=<<<EOD
|
||||
SELECT table_name, '{$schema}' as table_schema FROM user_tables
|
||||
EOD;
|
||||
$command=$this->getDbConnection()->createCommand($sql);
|
||||
}
|
||||
else
|
||||
{
|
||||
$sql=<<<EOD
|
||||
SELECT object_name as table_name, owner as table_schema FROM all_objects
|
||||
WHERE object_type = 'TABLE' AND owner=:schema
|
||||
EOD;
|
||||
$command=$this->getDbConnection()->createCommand($sql);
|
||||
$command->bindParam(':schema',$schema);
|
||||
}
|
||||
|
||||
$rows=$command->queryAll();
|
||||
$names=array();
|
||||
foreach($rows as $row)
|
||||
{
|
||||
if($schema===$this->getDefaultSchema() || $schema==='')
|
||||
$names[]=$row['TABLE_NAME'];
|
||||
else
|
||||
$names[]=$row['TABLE_SCHEMA'].'.'.$row['TABLE_NAME'];
|
||||
}
|
||||
return $names;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @since 1.1.6
|
||||
*/
|
||||
public function renameTable($table, $newName)
|
||||
{
|
||||
return 'ALTER TABLE ' . $this->quoteTableName($table) . ' RENAME TO ' . $this->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 {@link 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.
|
||||
* @since 1.1.6
|
||||
*/
|
||||
public function alterColumn($table, $column, $type)
|
||||
{
|
||||
$type=$this->getColumnType($type);
|
||||
$sql='ALTER TABLE ' . $this->quoteTableName($table) . ' MODIFY '
|
||||
. $this->quoteColumnName($column) . ' '
|
||||
. $this->getColumnType($type);
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @since 1.1.6
|
||||
*/
|
||||
public function dropIndex($name, $table)
|
||||
{
|
||||
return 'DROP INDEX '.$this->quoteTableName($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets 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 max value of a primary key plus one (i.e. sequence trimming).
|
||||
*
|
||||
* Note, behavior of this method has changed since 1.1.14 release. Please refer to the following
|
||||
* issue for more details: {@link https://github.com/yiisoft/yii/issues/2241}
|
||||
*
|
||||
* @param CDbTableSchema $table the table schema whose primary key sequence will be reset
|
||||
* @param integer|null $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 the max value of a primary
|
||||
* key plus one (i.e. sequence trimming).
|
||||
* @since 1.1.13
|
||||
*/
|
||||
public function resetSequence($table,$value=null)
|
||||
{
|
||||
if($table->sequenceName===null)
|
||||
return;
|
||||
|
||||
if($value!==null)
|
||||
$value=(int)$value;
|
||||
else
|
||||
{
|
||||
$value=(int)$this->getDbConnection()
|
||||
->createCommand("SELECT MAX(\"{$table->primaryKey}\") FROM {$table->rawName}")
|
||||
->queryScalar();
|
||||
$value++;
|
||||
}
|
||||
$this->getDbConnection()
|
||||
->createCommand("DROP SEQUENCE \"{$table->name}_SEQ\"")
|
||||
->execute();
|
||||
$this->getDbConnection()
|
||||
->createCommand("CREATE SEQUENCE \"{$table->name}_SEQ\" START WITH {$value} INCREMENT BY 1 NOMAXVALUE NOCACHE")
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables integrity check.
|
||||
* @param boolean $check whether to turn on or off the integrity check.
|
||||
* @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema.
|
||||
* @since 1.1.14
|
||||
*/
|
||||
public function checkIntegrity($check=true,$schema='')
|
||||
{
|
||||
if($schema==='')
|
||||
$schema=$this->getDefaultSchema();
|
||||
$mode=$check ? 'ENABLE' : 'DISABLE';
|
||||
foreach($this->getTableNames($schema) as $table)
|
||||
{
|
||||
$constraints=$this->getDbConnection()
|
||||
->createCommand("SELECT CONSTRAINT_NAME FROM USER_CONSTRAINTS WHERE TABLE_NAME=:t AND OWNER=:o")
|
||||
->queryColumn(array(':t'=>$table,':o'=>$schema));
|
||||
foreach($constraints as $constraint)
|
||||
$this->getDbConnection()
|
||||
->createCommand("ALTER TABLE \"{$schema}\".\"{$table}\" {$mode} CONSTRAINT \"{$constraint}\"")
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
24
framework/db/schema/oci/COciTableSchema.php
Normal file
24
framework/db/schema/oci/COciTableSchema.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
/**
|
||||
* COciTableSchema class file.
|
||||
*
|
||||
* @author Ricardo Grana <rickgrana@yahoo.com.br>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
/**
|
||||
* COciTableSchema represents the metadata for an Oracle table.
|
||||
*
|
||||
* @author Ricardo Grana <rickgrana@yahoo.com.br>
|
||||
* @package system.db.schema.oci
|
||||
*/
|
||||
class COciTableSchema extends CDbTableSchema
|
||||
{
|
||||
/**
|
||||
* @var string name of the schema (database) that this table belongs to.
|
||||
* Defaults to null, meaning no schema (or the current database).
|
||||
*/
|
||||
public $schemaName;
|
||||
}
|
||||
57
framework/db/schema/pgsql/CPgsqlColumnSchema.php
Normal file
57
framework/db/schema/pgsql/CPgsqlColumnSchema.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
/**
|
||||
* CPgsqlColumnSchema class file.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
/**
|
||||
* CPgsqlColumnSchema class describes the column meta data of a PostgreSQL table.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @package system.db.schema.pgsql
|
||||
* @since 1.0
|
||||
*/
|
||||
class CPgsqlColumnSchema extends CDbColumnSchema
|
||||
{
|
||||
/**
|
||||
* Extracts the PHP type from DB type.
|
||||
* @param string $dbType DB type
|
||||
*/
|
||||
protected function extractType($dbType)
|
||||
{
|
||||
if(strpos($dbType,'[')!==false || strpos($dbType,'char')!==false || strpos($dbType,'text')!==false)
|
||||
$this->type='string';
|
||||
elseif(strpos($dbType,'bool')!==false)
|
||||
$this->type='boolean';
|
||||
elseif(preg_match('/(real|float|double)/',$dbType))
|
||||
$this->type='double';
|
||||
elseif(preg_match('/(integer|oid|serial|smallint)/',$dbType))
|
||||
$this->type='integer';
|
||||
else
|
||||
$this->type='string';
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the default value for the column.
|
||||
* The value is typecasted to correct PHP type.
|
||||
* @param mixed $defaultValue the default value obtained from metadata
|
||||
*/
|
||||
protected function extractDefault($defaultValue)
|
||||
{
|
||||
if($defaultValue==='true')
|
||||
$this->defaultValue=true;
|
||||
elseif($defaultValue==='false')
|
||||
$this->defaultValue=false;
|
||||
elseif(strpos($defaultValue,'nextval')===0)
|
||||
$this->defaultValue=null;
|
||||
elseif(preg_match('/^\'(.*)\'::/',$defaultValue,$matches))
|
||||
$this->defaultValue=$this->typecast(str_replace("''","'",$matches[1]));
|
||||
elseif(preg_match('/^-?\d+(\.\d*)?$/',$defaultValue,$matches))
|
||||
$this->defaultValue=$this->typecast($defaultValue);
|
||||
// else is null
|
||||
}
|
||||
}
|
||||
30
framework/db/schema/pgsql/CPgsqlCommandBuilder.php
Normal file
30
framework/db/schema/pgsql/CPgsqlCommandBuilder.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
/**
|
||||
* CPgsqlCommandBuilder class file.
|
||||
*
|
||||
* @author Timur Ruziev <resurtm@gmail.com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
/**
|
||||
* CPgsqlCommandBuilder provides basic methods to create query commands for tables.
|
||||
*
|
||||
* @author Timur Ruziev <resurtm@gmail.com>
|
||||
* @package system.db.schema.pgsql
|
||||
* @since 1.1.14
|
||||
*/
|
||||
class CPgsqlCommandBuilder extends CDbCommandBuilder
|
||||
{
|
||||
/**
|
||||
* Returns default value of the integer/serial primary key. Default value means that the next
|
||||
* autoincrement/sequence value would be used.
|
||||
* @return string default value of the integer/serial primary key.
|
||||
* @since 1.1.14
|
||||
*/
|
||||
protected function getIntegerPrimaryKeyDefaultValue()
|
||||
{
|
||||
return 'DEFAULT';
|
||||
}
|
||||
}
|
||||
437
framework/db/schema/pgsql/CPgsqlSchema.php
Normal file
437
framework/db/schema/pgsql/CPgsqlSchema.php
Normal file
@@ -0,0 +1,437 @@
|
||||
<?php
|
||||
/**
|
||||
* CPgsqlSchema class file.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
/**
|
||||
* CPgsqlSchema is the class for retrieving metadata information from a PostgreSQL database.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @package system.db.schema.pgsql
|
||||
* @since 1.0
|
||||
*/
|
||||
class CPgsqlSchema extends CDbSchema
|
||||
{
|
||||
const DEFAULT_SCHEMA='public';
|
||||
|
||||
/**
|
||||
* @var array the abstract column types mapped to physical column types.
|
||||
* @since 1.1.6
|
||||
*/
|
||||
public $columnTypes=array(
|
||||
'pk' => 'serial NOT NULL PRIMARY KEY',
|
||||
'string' => 'character varying (255)',
|
||||
'text' => 'text',
|
||||
'integer' => 'integer',
|
||||
'float' => 'double precision',
|
||||
'decimal' => 'numeric',
|
||||
'datetime' => 'timestamp',
|
||||
'timestamp' => 'timestamp',
|
||||
'time' => 'time',
|
||||
'date' => 'date',
|
||||
'binary' => 'bytea',
|
||||
'boolean' => 'boolean',
|
||||
'money' => 'decimal(19,4)',
|
||||
);
|
||||
|
||||
private $_sequences=array();
|
||||
|
||||
/**
|
||||
* Quotes a table name for use in a query.
|
||||
* A simple table name does not schema prefix.
|
||||
* @param string $name table name
|
||||
* @return string the properly quoted table name
|
||||
* @since 1.1.6
|
||||
*/
|
||||
public function quoteSimpleTableName($name)
|
||||
{
|
||||
return '"'.$name.'"';
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets 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 max value of a primary key plus one (i.e. sequence trimming).
|
||||
* @param CDbTableSchema $table the table schema whose primary key sequence will be reset
|
||||
* @param integer|null $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 the max value of a primary
|
||||
* key plus one (i.e. sequence trimming).
|
||||
* @since 1.1
|
||||
*/
|
||||
public function resetSequence($table,$value=null)
|
||||
{
|
||||
if($table->sequenceName===null)
|
||||
return;
|
||||
$sequence='"'.$table->sequenceName.'"';
|
||||
if(strpos($sequence,'.')!==false)
|
||||
$sequence=str_replace('.','"."',$sequence);
|
||||
if($value!==null)
|
||||
$value=(int)$value;
|
||||
else
|
||||
$value="(SELECT COALESCE(MAX(\"{$table->primaryKey}\"),0) FROM {$table->rawName})+1";
|
||||
$this->getDbConnection()
|
||||
->createCommand("SELECT SETVAL('$sequence',$value,false)")
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables integrity check.
|
||||
* @param boolean $check whether to turn on or off the integrity check.
|
||||
* @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema.
|
||||
* @since 1.1
|
||||
*/
|
||||
public function checkIntegrity($check=true,$schema='')
|
||||
{
|
||||
$enable=$check ? 'ENABLE' : 'DISABLE';
|
||||
$tableNames=$this->getTableNames($schema);
|
||||
$db=$this->getDbConnection();
|
||||
foreach($tableNames as $tableName)
|
||||
{
|
||||
$tableName='"'.$tableName.'"';
|
||||
if(strpos($tableName,'.')!==false)
|
||||
$tableName=str_replace('.','"."',$tableName);
|
||||
$db->createCommand("ALTER TABLE $tableName $enable TRIGGER ALL")->execute();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the metadata for the specified table.
|
||||
* @param string $name table name
|
||||
* @return CDbTableSchema driver dependent table metadata.
|
||||
*/
|
||||
protected function loadTable($name)
|
||||
{
|
||||
$table=new CPgsqlTableSchema;
|
||||
$this->resolveTableNames($table,$name);
|
||||
if(!$this->findColumns($table))
|
||||
return null;
|
||||
$this->findConstraints($table);
|
||||
|
||||
if(is_string($table->primaryKey) && isset($this->_sequences[$table->rawName.'.'.$table->primaryKey]))
|
||||
$table->sequenceName=$this->_sequences[$table->rawName.'.'.$table->primaryKey];
|
||||
elseif(is_array($table->primaryKey))
|
||||
{
|
||||
foreach($table->primaryKey as $pk)
|
||||
{
|
||||
if(isset($this->_sequences[$table->rawName.'.'.$pk]))
|
||||
{
|
||||
$table->sequenceName=$this->_sequences[$table->rawName.'.'.$pk];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates various kinds of table names.
|
||||
* @param CPgsqlTableSchema $table the table instance
|
||||
* @param string $name the unquoted table name
|
||||
*/
|
||||
protected function resolveTableNames($table,$name)
|
||||
{
|
||||
$parts=explode('.',str_replace('"','',$name));
|
||||
if(isset($parts[1]))
|
||||
{
|
||||
$schemaName=$parts[0];
|
||||
$tableName=$parts[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
$schemaName=self::DEFAULT_SCHEMA;
|
||||
$tableName=$parts[0];
|
||||
}
|
||||
|
||||
$table->name=$tableName;
|
||||
$table->schemaName=$schemaName;
|
||||
if($schemaName===self::DEFAULT_SCHEMA)
|
||||
$table->rawName=$this->quoteTableName($tableName);
|
||||
else
|
||||
$table->rawName=$this->quoteTableName($schemaName).'.'.$this->quoteTableName($tableName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects the table column metadata.
|
||||
* @param CPgsqlTableSchema $table the table metadata
|
||||
* @return boolean whether the table exists in the database
|
||||
*/
|
||||
protected function findColumns($table)
|
||||
{
|
||||
$sql=<<<EOD
|
||||
SELECT a.attname, LOWER(format_type(a.atttypid, a.atttypmod)) AS type, d.adsrc, a.attnotnull, a.atthasdef,
|
||||
pg_catalog.col_description(a.attrelid, a.attnum) AS comment
|
||||
FROM pg_attribute a LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
|
||||
WHERE a.attnum > 0 AND NOT a.attisdropped
|
||||
AND a.attrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname=:table
|
||||
AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname = :schema))
|
||||
ORDER BY a.attnum
|
||||
EOD;
|
||||
$command=$this->getDbConnection()->createCommand($sql);
|
||||
$command->bindValue(':table',$table->name);
|
||||
$command->bindValue(':schema',$table->schemaName);
|
||||
|
||||
if(($columns=$command->queryAll())===array())
|
||||
return false;
|
||||
|
||||
foreach($columns as $column)
|
||||
{
|
||||
$c=$this->createColumn($column);
|
||||
$table->columns[$c->name]=$c;
|
||||
|
||||
if(stripos($column['adsrc'],'nextval')===0 && preg_match('/nextval\([^\']*\'([^\']+)\'[^\)]*\)/i',$column['adsrc'],$matches))
|
||||
{
|
||||
if(strpos($matches[1],'.')!==false || $table->schemaName===self::DEFAULT_SCHEMA)
|
||||
$this->_sequences[$table->rawName.'.'.$c->name]=$matches[1];
|
||||
else
|
||||
$this->_sequences[$table->rawName.'.'.$c->name]=$table->schemaName.'.'.$matches[1];
|
||||
$c->autoIncrement=true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a table column.
|
||||
* @param array $column column metadata
|
||||
* @return CDbColumnSchema normalized column metadata
|
||||
*/
|
||||
protected function createColumn($column)
|
||||
{
|
||||
$c=new CPgsqlColumnSchema;
|
||||
$c->name=$column['attname'];
|
||||
$c->rawName=$this->quoteColumnName($c->name);
|
||||
$c->allowNull=!$column['attnotnull'];
|
||||
$c->isPrimaryKey=false;
|
||||
$c->isForeignKey=false;
|
||||
$c->comment=$column['comment']===null ? '' : $column['comment'];
|
||||
|
||||
$c->init($column['type'],$column['atthasdef'] ? $column['adsrc'] : null);
|
||||
|
||||
return $c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects the primary and foreign key column details for the given table.
|
||||
* @param CPgsqlTableSchema $table the table metadata
|
||||
*/
|
||||
protected function findConstraints($table)
|
||||
{
|
||||
$sql=<<<EOD
|
||||
SELECT conname, consrc, contype, indkey FROM (
|
||||
SELECT
|
||||
conname,
|
||||
CASE WHEN contype='f' THEN
|
||||
pg_catalog.pg_get_constraintdef(oid)
|
||||
ELSE
|
||||
'CHECK (' || consrc || ')'
|
||||
END AS consrc,
|
||||
contype,
|
||||
conrelid AS relid,
|
||||
NULL AS indkey
|
||||
FROM
|
||||
pg_catalog.pg_constraint
|
||||
WHERE
|
||||
contype IN ('f', 'c')
|
||||
UNION ALL
|
||||
SELECT
|
||||
pc.relname,
|
||||
NULL,
|
||||
CASE WHEN indisprimary THEN
|
||||
'p'
|
||||
ELSE
|
||||
'u'
|
||||
END,
|
||||
pi.indrelid,
|
||||
indkey
|
||||
FROM
|
||||
pg_catalog.pg_class pc,
|
||||
pg_catalog.pg_index pi
|
||||
WHERE
|
||||
pc.oid=pi.indexrelid
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM pg_catalog.pg_depend d JOIN pg_catalog.pg_constraint c
|
||||
ON (d.refclassid = c.tableoid AND d.refobjid = c.oid)
|
||||
WHERE d.classid = pc.tableoid AND d.objid = pc.oid AND d.deptype = 'i' AND c.contype IN ('u', 'p')
|
||||
)
|
||||
) AS sub
|
||||
WHERE relid = (SELECT oid FROM pg_catalog.pg_class WHERE relname=:table
|
||||
AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace
|
||||
WHERE nspname=:schema))
|
||||
EOD;
|
||||
$command=$this->getDbConnection()->createCommand($sql);
|
||||
$command->bindValue(':table',$table->name);
|
||||
$command->bindValue(':schema',$table->schemaName);
|
||||
foreach($command->queryAll() as $row)
|
||||
{
|
||||
if($row['contype']==='p') // primary key
|
||||
$this->findPrimaryKey($table,$row['indkey']);
|
||||
elseif($row['contype']==='f') // foreign key
|
||||
$this->findForeignKey($table,$row['consrc']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects primary key information.
|
||||
* @param CPgsqlTableSchema $table the table metadata
|
||||
* @param string $indices pgsql primary key index list
|
||||
*/
|
||||
protected function findPrimaryKey($table,$indices)
|
||||
{
|
||||
$indices=implode(', ',preg_split('/\s+/',$indices));
|
||||
$sql=<<<EOD
|
||||
SELECT attnum, attname FROM pg_catalog.pg_attribute WHERE
|
||||
attrelid=(
|
||||
SELECT oid FROM pg_catalog.pg_class WHERE relname=:table AND relnamespace=(
|
||||
SELECT oid FROM pg_catalog.pg_namespace WHERE nspname=:schema
|
||||
)
|
||||
)
|
||||
AND attnum IN ({$indices})
|
||||
EOD;
|
||||
$command=$this->getDbConnection()->createCommand($sql);
|
||||
$command->bindValue(':table',$table->name);
|
||||
$command->bindValue(':schema',$table->schemaName);
|
||||
foreach($command->queryAll() as $row)
|
||||
{
|
||||
$name=$row['attname'];
|
||||
if(isset($table->columns[$name]))
|
||||
{
|
||||
$table->columns[$name]->isPrimaryKey=true;
|
||||
if($table->primaryKey===null)
|
||||
$table->primaryKey=$name;
|
||||
elseif(is_string($table->primaryKey))
|
||||
$table->primaryKey=array($table->primaryKey,$name);
|
||||
else
|
||||
$table->primaryKey[]=$name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects foreign key information.
|
||||
* @param CPgsqlTableSchema $table the table metadata
|
||||
* @param string $src pgsql foreign key definition
|
||||
*/
|
||||
protected function findForeignKey($table,$src)
|
||||
{
|
||||
$matches=array();
|
||||
$brackets='\(([^\)]+)\)';
|
||||
$pattern="/FOREIGN\s+KEY\s+{$brackets}\s+REFERENCES\s+([^\(]+){$brackets}/i";
|
||||
if(preg_match($pattern,str_replace('"','',$src),$matches))
|
||||
{
|
||||
$keys=preg_split('/,\s+/', $matches[1]);
|
||||
$tableName=$matches[2];
|
||||
$fkeys=preg_split('/,\s+/', $matches[3]);
|
||||
foreach($keys as $i=>$key)
|
||||
{
|
||||
$table->foreignKeys[$key]=array($tableName,$fkeys[$i]);
|
||||
if(isset($table->columns[$key]))
|
||||
$table->columns[$key]->isForeignKey=true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* If not empty, the returned table names will be prefixed with the schema name.
|
||||
* @return array all table names in the database.
|
||||
*/
|
||||
protected function findTableNames($schema='')
|
||||
{
|
||||
if($schema==='')
|
||||
$schema=self::DEFAULT_SCHEMA;
|
||||
$sql=<<<EOD
|
||||
SELECT table_name, table_schema FROM information_schema.tables
|
||||
WHERE table_schema=:schema AND table_type='BASE TABLE'
|
||||
EOD;
|
||||
$command=$this->getDbConnection()->createCommand($sql);
|
||||
$command->bindParam(':schema',$schema);
|
||||
$rows=$command->queryAll();
|
||||
$names=array();
|
||||
foreach($rows as $row)
|
||||
{
|
||||
if($schema===self::DEFAULT_SCHEMA)
|
||||
$names[]=$row['table_name'];
|
||||
else
|
||||
$names[]=$row['table_schema'].'.'.$row['table_name'];
|
||||
}
|
||||
return $names;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @since 1.1.6
|
||||
*/
|
||||
public function renameTable($table, $newName)
|
||||
{
|
||||
return 'ALTER TABLE ' . $this->quoteTableName($table) . ' RENAME TO ' . $this->quoteTableName($newName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds 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 {@link 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 adding a new column.
|
||||
* @since 1.1.6
|
||||
*/
|
||||
public function addColumn($table, $column, $type)
|
||||
{
|
||||
$type=$this->getColumnType($type);
|
||||
$sql='ALTER TABLE ' . $this->quoteTableName($table)
|
||||
. ' ADD COLUMN ' . $this->quoteColumnName($column) . ' '
|
||||
. $this->getColumnType($type);
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {@link 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.
|
||||
* @since 1.1.6
|
||||
*/
|
||||
public function alterColumn($table, $column, $type)
|
||||
{
|
||||
$type=$this->getColumnType($type);
|
||||
$sql='ALTER TABLE ' . $this->quoteTableName($table) . ' ALTER COLUMN '
|
||||
. $this->quoteColumnName($column) . ' TYPE ' . $this->getColumnType($type);
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @since 1.1.6
|
||||
*/
|
||||
public function dropIndex($name, $table)
|
||||
{
|
||||
return 'DROP INDEX '.$this->quoteTableName($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a command builder for the database.
|
||||
* This method may be overridden by child classes to create a DBMS-specific command builder.
|
||||
* @return CPgsqlCommandBuilder command builder instance.
|
||||
*/
|
||||
protected function createCommandBuilder()
|
||||
{
|
||||
return new CPgsqlCommandBuilder($this);
|
||||
}
|
||||
}
|
||||
24
framework/db/schema/pgsql/CPgsqlTableSchema.php
Normal file
24
framework/db/schema/pgsql/CPgsqlTableSchema.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
/**
|
||||
* CPgsqlTable class file.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
/**
|
||||
* CPgsqlTable represents the metadata for a PostgreSQL table.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @package system.db.schema.pgsql
|
||||
* @since 1.0
|
||||
*/
|
||||
class CPgsqlTableSchema extends CDbTableSchema
|
||||
{
|
||||
/**
|
||||
* @var string name of the schema that this table belongs to.
|
||||
*/
|
||||
public $schemaName;
|
||||
}
|
||||
35
framework/db/schema/sqlite/CSqliteColumnSchema.php
Normal file
35
framework/db/schema/sqlite/CSqliteColumnSchema.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
/**
|
||||
* CSqliteColumnSchema class file.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
/**
|
||||
* CSqliteColumnSchema class describes the column meta data of a SQLite table.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @package system.db.schema.sqlite
|
||||
* @since 1.0
|
||||
*/
|
||||
class CSqliteColumnSchema extends CDbColumnSchema
|
||||
{
|
||||
/**
|
||||
* Extracts the default value for the column.
|
||||
* The value is typecasted to correct PHP type.
|
||||
* @param mixed $defaultValue the default value obtained from metadata
|
||||
*/
|
||||
protected function extractDefault($defaultValue)
|
||||
{
|
||||
if($this->dbType==='timestamp' && $defaultValue==='CURRENT_TIMESTAMP')
|
||||
$this->defaultValue=null;
|
||||
else
|
||||
$this->defaultValue=$this->typecast(strcasecmp($defaultValue,'null') ? $defaultValue : null);
|
||||
|
||||
if($this->type==='string' && $this->defaultValue!==null) // PHP 5.2.6 adds single quotes while 5.2.0 doesn't
|
||||
$this->defaultValue=trim($this->defaultValue,"'\"");
|
||||
}
|
||||
}
|
||||
63
framework/db/schema/sqlite/CSqliteCommandBuilder.php
Normal file
63
framework/db/schema/sqlite/CSqliteCommandBuilder.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
/**
|
||||
* CSqliteCommandBuilder class file.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
/**
|
||||
* CSqliteCommandBuilder provides basic methods to create query commands for SQLite tables.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @package system.db.schema.sqlite
|
||||
* @since 1.0
|
||||
*/
|
||||
class CSqliteCommandBuilder extends CDbCommandBuilder
|
||||
{
|
||||
/**
|
||||
* Generates the expression for selecting rows with specified composite key values.
|
||||
* This method is overridden because SQLite does not support the default
|
||||
* IN expression with composite columns.
|
||||
* @param CDbTableSchema $table the table schema
|
||||
* @param array $values list of primary key values to be selected within
|
||||
* @param string $prefix column prefix (ended with dot)
|
||||
* @return string the expression for selection
|
||||
*/
|
||||
protected function createCompositeInCondition($table,$values,$prefix)
|
||||
{
|
||||
$keyNames=array();
|
||||
foreach(array_keys($values[0]) as $name)
|
||||
$keyNames[]=$prefix.$table->columns[$name]->rawName;
|
||||
$vs=array();
|
||||
foreach($values as $value)
|
||||
$vs[]=implode("||','||",$value);
|
||||
return implode("||','||",$keyNames).' IN ('.implode(', ',$vs).')';
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a multiple INSERT command.
|
||||
* This method could be used to achieve better performance during insertion of the large
|
||||
* amount of data into the database tables.
|
||||
* Note that SQLite does not keep original order of the inserted rows.
|
||||
* @param mixed $table the table schema ({@link CDbTableSchema}) or the table name (string).
|
||||
* @param array[] $data list data to be inserted, each value should be an array in format (column name=>column value).
|
||||
* If a key is not a valid column name, the corresponding value will be ignored.
|
||||
* @return CDbCommand multiple insert command
|
||||
* @since 1.1.14
|
||||
*/
|
||||
public function createMultipleInsertCommand($table,array $data)
|
||||
{
|
||||
$templates=array(
|
||||
'main'=>'INSERT INTO {{tableName}} ({{columnInsertNames}}) {{rowInsertValues}}',
|
||||
'columnInsertValue'=>'{{value}} AS {{column}}',
|
||||
'columnInsertValueGlue'=>', ',
|
||||
'rowInsertValue'=>'SELECT {{columnInsertValues}}',
|
||||
'rowInsertValueGlue'=>' UNION ',
|
||||
'columnInsertNameGlue'=>', ',
|
||||
);
|
||||
return $this->composeMultipleInsertCommand($table,$data,$templates);
|
||||
}
|
||||
}
|
||||
333
framework/db/schema/sqlite/CSqliteSchema.php
Normal file
333
framework/db/schema/sqlite/CSqliteSchema.php
Normal file
@@ -0,0 +1,333 @@
|
||||
<?php
|
||||
/**
|
||||
* CSqliteSchema class file.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
/**
|
||||
* CSqliteSchema is the class for retrieving metadata information from a SQLite (2/3) database.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @package system.db.schema.sqlite
|
||||
* @since 1.0
|
||||
*/
|
||||
class CSqliteSchema extends CDbSchema
|
||||
{
|
||||
/**
|
||||
* @var array the abstract column types mapped to physical column types.
|
||||
* @since 1.1.6
|
||||
*/
|
||||
public $columnTypes=array(
|
||||
'pk' => 'integer PRIMARY KEY AUTOINCREMENT NOT NULL',
|
||||
'string' => 'varchar(255)',
|
||||
'text' => 'text',
|
||||
'integer' => 'integer',
|
||||
'float' => 'float',
|
||||
'decimal' => 'decimal',
|
||||
'datetime' => 'datetime',
|
||||
'timestamp' => 'timestamp',
|
||||
'time' => 'time',
|
||||
'date' => 'date',
|
||||
'binary' => 'blob',
|
||||
'boolean' => 'tinyint(1)',
|
||||
'money' => 'decimal(19,4)',
|
||||
);
|
||||
|
||||
/**
|
||||
* Resets 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 max value of a primary key plus one (i.e. sequence trimming).
|
||||
* @param CDbTableSchema $table the table schema whose primary key sequence will be reset
|
||||
* @param integer|null $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 the max value of a primary
|
||||
* key plus one (i.e. sequence trimming).
|
||||
* @since 1.1
|
||||
*/
|
||||
public function resetSequence($table,$value=null)
|
||||
{
|
||||
if($table->sequenceName===null)
|
||||
return;
|
||||
if($value!==null)
|
||||
$value=(int)($value)-1;
|
||||
else
|
||||
$value=(int)$this->getDbConnection()
|
||||
->createCommand("SELECT MAX(`{$table->primaryKey}`) FROM {$table->rawName}")
|
||||
->queryScalar();
|
||||
try
|
||||
{
|
||||
// it's possible that 'sqlite_sequence' does not exist
|
||||
$this->getDbConnection()
|
||||
->createCommand("UPDATE sqlite_sequence SET seq='$value' WHERE name='{$table->name}'")
|
||||
->execute();
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables integrity check. Note that this method used to do nothing before 1.1.14. Since 1.1.14
|
||||
* it changes integrity check state as expected.
|
||||
* @param boolean $check whether to turn on or off the integrity check.
|
||||
* @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema.
|
||||
* @since 1.1
|
||||
*/
|
||||
public function checkIntegrity($check=true,$schema='')
|
||||
{
|
||||
$this->getDbConnection()->createCommand('PRAGMA foreign_keys='.(int)$check)->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all table names in the database.
|
||||
* @param string $schema the schema of the tables. This is not used for sqlite database.
|
||||
* @return array all table names in the database.
|
||||
*/
|
||||
protected function findTableNames($schema='')
|
||||
{
|
||||
$sql="SELECT DISTINCT tbl_name FROM sqlite_master WHERE tbl_name<>'sqlite_sequence'";
|
||||
return $this->getDbConnection()->createCommand($sql)->queryColumn();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a command builder for the database.
|
||||
* @return CSqliteCommandBuilder command builder instance
|
||||
*/
|
||||
protected function createCommandBuilder()
|
||||
{
|
||||
return new CSqliteCommandBuilder($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the metadata for the specified table.
|
||||
* @param string $name table name
|
||||
* @return CDbTableSchema driver dependent table metadata. Null if the table does not exist.
|
||||
*/
|
||||
protected function loadTable($name)
|
||||
{
|
||||
$table=new CDbTableSchema;
|
||||
$table->name=$name;
|
||||
$table->rawName=$this->quoteTableName($name);
|
||||
|
||||
if($this->findColumns($table))
|
||||
{
|
||||
$this->findConstraints($table);
|
||||
return $table;
|
||||
}
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects the table column metadata.
|
||||
* @param CDbTableSchema $table the table metadata
|
||||
* @return boolean whether the table exists in the database
|
||||
*/
|
||||
protected function findColumns($table)
|
||||
{
|
||||
$sql="PRAGMA table_info({$table->rawName})";
|
||||
$columns=$this->getDbConnection()->createCommand($sql)->queryAll();
|
||||
if(empty($columns))
|
||||
return false;
|
||||
|
||||
foreach($columns as $column)
|
||||
{
|
||||
$c=$this->createColumn($column);
|
||||
$table->columns[$c->name]=$c;
|
||||
if($c->isPrimaryKey)
|
||||
{
|
||||
if($table->primaryKey===null)
|
||||
$table->primaryKey=$c->name;
|
||||
elseif(is_string($table->primaryKey))
|
||||
$table->primaryKey=array($table->primaryKey,$c->name);
|
||||
else
|
||||
$table->primaryKey[]=$c->name;
|
||||
}
|
||||
}
|
||||
if(is_string($table->primaryKey) && !strncasecmp($table->columns[$table->primaryKey]->dbType,'int',3))
|
||||
{
|
||||
$table->sequenceName='';
|
||||
$table->columns[$table->primaryKey]->autoIncrement=true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects the foreign key column details for the given table.
|
||||
* @param CDbTableSchema $table the table metadata
|
||||
*/
|
||||
protected function findConstraints($table)
|
||||
{
|
||||
$foreignKeys=array();
|
||||
$sql="PRAGMA foreign_key_list({$table->rawName})";
|
||||
$keys=$this->getDbConnection()->createCommand($sql)->queryAll();
|
||||
foreach($keys as $key)
|
||||
{
|
||||
$column=$table->columns[$key['from']];
|
||||
$column->isForeignKey=true;
|
||||
$foreignKeys[$key['from']]=array($key['table'],$key['to']);
|
||||
}
|
||||
$table->foreignKeys=$foreignKeys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a table column.
|
||||
* @param array $column column metadata
|
||||
* @return CDbColumnSchema normalized column metadata
|
||||
*/
|
||||
protected function createColumn($column)
|
||||
{
|
||||
$c=new CSqliteColumnSchema;
|
||||
$c->name=$column['name'];
|
||||
$c->rawName=$this->quoteColumnName($c->name);
|
||||
$c->allowNull=!$column['notnull'];
|
||||
$c->isPrimaryKey=$column['pk']!=0;
|
||||
$c->isForeignKey=false;
|
||||
$c->comment=null; // SQLite does not support column comments at all
|
||||
|
||||
$c->init(strtolower($column['type']),$column['dflt_value']);
|
||||
return $c;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @since 1.1.13
|
||||
*/
|
||||
public function renameTable($table, $newName)
|
||||
{
|
||||
return 'ALTER TABLE ' . $this->quoteTableName($table) . ' RENAME TO ' . $this->quoteTableName($newName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @since 1.1.6
|
||||
*/
|
||||
public function truncateTable($table)
|
||||
{
|
||||
return "DELETE FROM ".$this->quoteTableName($table);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a SQL statement for dropping a DB column.
|
||||
* Because SQLite does not support dropping a DB column, calling this method will throw an exception.
|
||||
* @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.
|
||||
* @since 1.1.6
|
||||
*/
|
||||
public function dropColumn($table, $column)
|
||||
{
|
||||
throw new CDbException(Yii::t('yii', 'Dropping DB column is not supported by SQLite.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a SQL statement for renaming a column.
|
||||
* Because SQLite does not support renaming a DB column, calling this method will throw an exception.
|
||||
* @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.
|
||||
* @return string the SQL statement for renaming a DB column.
|
||||
* @since 1.1.6
|
||||
*/
|
||||
public function renameColumn($table, $name, $newName)
|
||||
{
|
||||
throw new CDbException(Yii::t('yii', 'Renaming a DB column is not supported by SQLite.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a SQL statement for adding a foreign key constraint to an existing table.
|
||||
* Because SQLite does not support adding foreign key to an existing table, calling this method will throw an exception.
|
||||
* @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 $columns the name of the column to that the constraint will be added on. If there are multiple columns, separate them with commas.
|
||||
* @param string $refTable the table that the foreign key references to.
|
||||
* @param string $refColumns the name of the column that the foreign key references to. If there are multiple columns, separate them with commas.
|
||||
* @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.
|
||||
* @since 1.1.6
|
||||
*/
|
||||
public function addForeignKey($name, $table, $columns, $refTable, $refColumns, $delete=null, $update=null)
|
||||
{
|
||||
throw new CDbException(Yii::t('yii', 'Adding a foreign key constraint to an existing table is not supported by SQLite.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a SQL statement for dropping a foreign key constraint.
|
||||
* Because SQLite does not support dropping a foreign key constraint, calling this method will throw an exception.
|
||||
* @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.
|
||||
* @since 1.1.6
|
||||
*/
|
||||
public function dropForeignKey($name, $table)
|
||||
{
|
||||
throw new CDbException(Yii::t('yii', 'Dropping a foreign key constraint is not supported by SQLite.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a SQL statement for changing the definition of a column.
|
||||
* Because SQLite does not support altering a DB column, calling this method will throw an exception.
|
||||
* @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 {@link 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.
|
||||
* @since 1.1.6
|
||||
*/
|
||||
public function alterColumn($table, $column, $type)
|
||||
{
|
||||
throw new CDbException(Yii::t('yii', 'Altering a DB column is not supported by SQLite.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @since 1.1.6
|
||||
*/
|
||||
public function dropIndex($name, $table)
|
||||
{
|
||||
return 'DROP INDEX '.$this->quoteTableName($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a SQL statement for adding a primary key constraint to an existing table.
|
||||
* Because SQLite does not support adding a primary key on an existing table this method will throw an exception.
|
||||
* @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.
|
||||
* @since 1.1.13
|
||||
*/
|
||||
public function addPrimaryKey($name,$table,$columns)
|
||||
{
|
||||
throw new CDbException(Yii::t('yii', 'Adding a primary key after table has been created is not supported by SQLite.'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Builds a SQL statement for removing a primary key constraint to an existing table.
|
||||
* Because SQLite does not support dropping a primary key from an existing table this method will throw an exception
|
||||
* @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.
|
||||
* @since 1.1.13
|
||||
*/
|
||||
public function dropPrimaryKey($name,$table)
|
||||
{
|
||||
throw new CDbException(Yii::t('yii', 'Removing a primary key after table has been created is not supported by SQLite.'));
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user