12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301 |
- <?php
- namespace yii\db;
- use Yii;
- use yii\base\Component;
- use yii\base\NotSupportedException;
- class Command extends Component
- {
-
- public $db;
-
- public $pdoStatement;
-
- public $fetchMode = \PDO::FETCH_ASSOC;
-
- public $params = [];
-
- public $queryCacheDuration;
-
- public $queryCacheDependency;
-
- private $_pendingParams = [];
-
- private $_sql;
-
- private $_refreshTableName;
-
- private $_isolationLevel = false;
-
- private $_retryHandler;
-
- public function cache($duration = null, $dependency = null)
- {
- $this->queryCacheDuration = $duration === null ? $this->db->queryCacheDuration : $duration;
- $this->queryCacheDependency = $dependency;
- return $this;
- }
-
- public function noCache()
- {
- $this->queryCacheDuration = -1;
- return $this;
- }
-
- public function getSql()
- {
- return $this->_sql;
- }
-
- public function setSql($sql)
- {
- if ($sql !== $this->_sql) {
- $this->cancel();
- $this->reset();
- $this->_sql = $this->db->quoteSql($sql);
- }
- return $this;
- }
-
- public function setRawSql($sql)
- {
- if ($sql !== $this->_sql) {
- $this->cancel();
- $this->reset();
- $this->_sql = $sql;
- }
- return $this;
- }
-
- public function getRawSql()
- {
- if (empty($this->params)) {
- return $this->_sql;
- }
- $params = [];
- foreach ($this->params as $name => $value) {
- if (is_string($name) && strncmp(':', $name, 1)) {
- $name = ':' . $name;
- }
- if (is_string($value)) {
- $params[$name] = $this->db->quoteValue($value);
- } elseif (is_bool($value)) {
- $params[$name] = ($value ? 'TRUE' : 'FALSE');
- } elseif ($value === null) {
- $params[$name] = 'NULL';
- } elseif ((!is_object($value) && !is_resource($value)) || $value instanceof Expression) {
- $params[$name] = $value;
- }
- }
- if (!isset($params[1])) {
- return strtr($this->_sql, $params);
- }
- $sql = '';
- foreach (explode('?', $this->_sql) as $i => $part) {
- $sql .= (isset($params[$i]) ? $params[$i] : '') . $part;
- }
- return $sql;
- }
-
- public function prepare($forRead = null)
- {
- if ($this->pdoStatement) {
- $this->bindPendingParams();
- return;
- }
- $sql = $this->getSql();
- if ($this->db->getTransaction()) {
-
- $forRead = false;
- }
- if ($forRead || $forRead === null && $this->db->getSchema()->isReadQuery($sql)) {
- $pdo = $this->db->getSlavePdo();
- } else {
- $pdo = $this->db->getMasterPdo();
- }
- try {
- $this->pdoStatement = $pdo->prepare($sql);
- $this->bindPendingParams();
- } catch (\Exception $e) {
- $message = $e->getMessage() . "\nFailed to prepare SQL: $sql";
- $errorInfo = $e instanceof \PDOException ? $e->errorInfo : null;
- throw new Exception($message, $errorInfo, (int) $e->getCode(), $e);
- }
- }
-
- public function cancel()
- {
- $this->pdoStatement = null;
- }
-
- public function bindParam($name, &$value, $dataType = null, $length = null, $driverOptions = null)
- {
- $this->prepare();
- if ($dataType === null) {
- $dataType = $this->db->getSchema()->getPdoType($value);
- }
- if ($length === null) {
- $this->pdoStatement->bindParam($name, $value, $dataType);
- } elseif ($driverOptions === null) {
- $this->pdoStatement->bindParam($name, $value, $dataType, $length);
- } else {
- $this->pdoStatement->bindParam($name, $value, $dataType, $length, $driverOptions);
- }
- $this->params[$name] = &$value;
- return $this;
- }
-
- protected function bindPendingParams()
- {
- foreach ($this->_pendingParams as $name => $value) {
- $this->pdoStatement->bindValue($name, $value[0], $value[1]);
- }
- $this->_pendingParams = [];
- }
-
- public function bindValue($name, $value, $dataType = null)
- {
- if ($dataType === null) {
- $dataType = $this->db->getSchema()->getPdoType($value);
- }
- $this->_pendingParams[$name] = [$value, $dataType];
- $this->params[$name] = $value;
- return $this;
- }
-
- public function bindValues($values)
- {
- if (empty($values)) {
- return $this;
- }
- $schema = $this->db->getSchema();
- foreach ($values as $name => $value) {
- if (is_array($value)) {
- $this->_pendingParams[$name] = $value;
- $this->params[$name] = $value[0];
- } elseif ($value instanceof PdoValue) {
- $this->_pendingParams[$name] = [$value->getValue(), $value->getType()];
- $this->params[$name] = $value->getValue();
- } else {
- $type = $schema->getPdoType($value);
- $this->_pendingParams[$name] = [$value, $type];
- $this->params[$name] = $value;
- }
- }
- return $this;
- }
-
- public function query()
- {
- return $this->queryInternal('');
- }
-
- public function queryAll($fetchMode = null)
- {
- return $this->queryInternal('fetchAll', $fetchMode);
- }
-
- public function queryOne($fetchMode = null)
- {
- return $this->queryInternal('fetch', $fetchMode);
- }
-
- public function queryScalar()
- {
- $result = $this->queryInternal('fetchColumn', 0);
- if (is_resource($result) && get_resource_type($result) === 'stream') {
- return stream_get_contents($result);
- }
- return $result;
- }
-
- public function queryColumn()
- {
- return $this->queryInternal('fetchAll', \PDO::FETCH_COLUMN);
- }
-
- public function insert($table, $columns)
- {
- $params = [];
- $sql = $this->db->getQueryBuilder()->insert($table, $columns, $params);
- return $this->setSql($sql)->bindValues($params);
- }
-
- public function batchInsert($table, $columns, $rows)
- {
- $table = $this->db->quoteSql($table);
- $columns = array_map(function ($column) {
- return $this->db->quoteSql($column);
- }, $columns);
- $params = [];
- $sql = $this->db->getQueryBuilder()->batchInsert($table, $columns, $rows, $params);
- $this->setRawSql($sql);
- $this->bindValues($params);
- return $this;
- }
-
- public function upsert($table, $insertColumns, $updateColumns = true, $params = [])
- {
- $sql = $this->db->getQueryBuilder()->upsert($table, $insertColumns, $updateColumns, $params);
- return $this->setSql($sql)->bindValues($params);
- }
-
- public function update($table, $columns, $condition = '', $params = [])
- {
- $sql = $this->db->getQueryBuilder()->update($table, $columns, $condition, $params);
- return $this->setSql($sql)->bindValues($params);
- }
-
- public function delete($table, $condition = '', $params = [])
- {
- $sql = $this->db->getQueryBuilder()->delete($table, $condition, $params);
- return $this->setSql($sql)->bindValues($params);
- }
-
- public function createTable($table, $columns, $options = null)
- {
- $sql = $this->db->getQueryBuilder()->createTable($table, $columns, $options);
- return $this->setSql($sql)->requireTableSchemaRefresh($table);
- }
-
- public function renameTable($table, $newName)
- {
- $sql = $this->db->getQueryBuilder()->renameTable($table, $newName);
- return $this->setSql($sql)->requireTableSchemaRefresh($table);
- }
-
- public function dropTable($table)
- {
- $sql = $this->db->getQueryBuilder()->dropTable($table);
- return $this->setSql($sql)->requireTableSchemaRefresh($table);
- }
-
- public function truncateTable($table)
- {
- $sql = $this->db->getQueryBuilder()->truncateTable($table);
- return $this->setSql($sql);
- }
-
- public function addColumn($table, $column, $type)
- {
- $sql = $this->db->getQueryBuilder()->addColumn($table, $column, $type);
- return $this->setSql($sql)->requireTableSchemaRefresh($table);
- }
-
- public function dropColumn($table, $column)
- {
- $sql = $this->db->getQueryBuilder()->dropColumn($table, $column);
- return $this->setSql($sql)->requireTableSchemaRefresh($table);
- }
-
- public function renameColumn($table, $oldName, $newName)
- {
- $sql = $this->db->getQueryBuilder()->renameColumn($table, $oldName, $newName);
- return $this->setSql($sql)->requireTableSchemaRefresh($table);
- }
-
- public function alterColumn($table, $column, $type)
- {
- $sql = $this->db->getQueryBuilder()->alterColumn($table, $column, $type);
- return $this->setSql($sql)->requireTableSchemaRefresh($table);
- }
-
- public function addPrimaryKey($name, $table, $columns)
- {
- $sql = $this->db->getQueryBuilder()->addPrimaryKey($name, $table, $columns);
- return $this->setSql($sql)->requireTableSchemaRefresh($table);
- }
-
- public function dropPrimaryKey($name, $table)
- {
- $sql = $this->db->getQueryBuilder()->dropPrimaryKey($name, $table);
- return $this->setSql($sql)->requireTableSchemaRefresh($table);
- }
-
- public function addForeignKey($name, $table, $columns, $refTable, $refColumns, $delete = null, $update = null)
- {
- $sql = $this->db->getQueryBuilder()->addForeignKey($name, $table, $columns, $refTable, $refColumns, $delete, $update);
- return $this->setSql($sql)->requireTableSchemaRefresh($table);
- }
-
- public function dropForeignKey($name, $table)
- {
- $sql = $this->db->getQueryBuilder()->dropForeignKey($name, $table);
- return $this->setSql($sql)->requireTableSchemaRefresh($table);
- }
-
- public function createIndex($name, $table, $columns, $unique = false)
- {
- $sql = $this->db->getQueryBuilder()->createIndex($name, $table, $columns, $unique);
- return $this->setSql($sql)->requireTableSchemaRefresh($table);
- }
-
- public function dropIndex($name, $table)
- {
- $sql = $this->db->getQueryBuilder()->dropIndex($name, $table);
- return $this->setSql($sql)->requireTableSchemaRefresh($table);
- }
-
- public function addUnique($name, $table, $columns)
- {
- $sql = $this->db->getQueryBuilder()->addUnique($name, $table, $columns);
- return $this->setSql($sql)->requireTableSchemaRefresh($table);
- }
-
- public function dropUnique($name, $table)
- {
- $sql = $this->db->getQueryBuilder()->dropUnique($name, $table);
- return $this->setSql($sql)->requireTableSchemaRefresh($table);
- }
-
- public function addCheck($name, $table, $expression)
- {
- $sql = $this->db->getQueryBuilder()->addCheck($name, $table, $expression);
- return $this->setSql($sql)->requireTableSchemaRefresh($table);
- }
-
- public function dropCheck($name, $table)
- {
- $sql = $this->db->getQueryBuilder()->dropCheck($name, $table);
- return $this->setSql($sql)->requireTableSchemaRefresh($table);
- }
-
- public function addDefaultValue($name, $table, $column, $value)
- {
- $sql = $this->db->getQueryBuilder()->addDefaultValue($name, $table, $column, $value);
- return $this->setSql($sql)->requireTableSchemaRefresh($table);
- }
-
- public function dropDefaultValue($name, $table)
- {
- $sql = $this->db->getQueryBuilder()->dropDefaultValue($name, $table);
- return $this->setSql($sql)->requireTableSchemaRefresh($table);
- }
-
- public function resetSequence($table, $value = null)
- {
- $sql = $this->db->getQueryBuilder()->resetSequence($table, $value);
- return $this->setSql($sql);
- }
-
- public function executeResetSequence($table, $value = null)
- {
- return $this->db->getQueryBuilder()->executeResetSequence($table, $value);
- }
-
- public function checkIntegrity($check = true, $schema = '', $table = '')
- {
- $sql = $this->db->getQueryBuilder()->checkIntegrity($check, $schema, $table);
- return $this->setSql($sql);
- }
-
- public function addCommentOnColumn($table, $column, $comment)
- {
- $sql = $this->db->getQueryBuilder()->addCommentOnColumn($table, $column, $comment);
- return $this->setSql($sql)->requireTableSchemaRefresh($table);
- }
-
- public function addCommentOnTable($table, $comment)
- {
- $sql = $this->db->getQueryBuilder()->addCommentOnTable($table, $comment);
- return $this->setSql($sql);
- }
-
- public function dropCommentFromColumn($table, $column)
- {
- $sql = $this->db->getQueryBuilder()->dropCommentFromColumn($table, $column);
- return $this->setSql($sql)->requireTableSchemaRefresh($table);
- }
-
- public function dropCommentFromTable($table)
- {
- $sql = $this->db->getQueryBuilder()->dropCommentFromTable($table);
- return $this->setSql($sql);
- }
-
- public function createView($viewName, $subquery)
- {
- $sql = $this->db->getQueryBuilder()->createView($viewName, $subquery);
- return $this->setSql($sql)->requireTableSchemaRefresh($viewName);
- }
-
- public function dropView($viewName)
- {
- $sql = $this->db->getQueryBuilder()->dropView($viewName);
- return $this->setSql($sql)->requireTableSchemaRefresh($viewName);
- }
-
- public function execute()
- {
- $sql = $this->getSql();
- list($profile, $rawSql) = $this->logQuery(__METHOD__);
- if ($sql == '') {
- return 0;
- }
- $this->prepare(false);
- try {
- $profile and Yii::beginProfile($rawSql, __METHOD__);
- $this->internalExecute($rawSql);
- $n = $this->pdoStatement->rowCount();
- $profile and Yii::endProfile($rawSql, __METHOD__);
- $this->refreshTableSchema();
- return $n;
- } catch (Exception $e) {
- $profile and Yii::endProfile($rawSql, __METHOD__);
- throw $e;
- }
- }
-
- protected function logQuery($category)
- {
- if ($this->db->enableLogging) {
- $rawSql = $this->getRawSql();
- Yii::info($rawSql, $category);
- }
- if (!$this->db->enableProfiling) {
- return [false, isset($rawSql) ? $rawSql : null];
- }
- return [true, isset($rawSql) ? $rawSql : $this->getRawSql()];
- }
-
- protected function queryInternal($method, $fetchMode = null)
- {
- list($profile, $rawSql) = $this->logQuery('yii\db\Command::query');
- if ($method !== '') {
- $info = $this->db->getQueryCacheInfo($this->queryCacheDuration, $this->queryCacheDependency);
- if (is_array($info)) {
-
- $cache = $info[0];
- $cacheKey = [
- __CLASS__,
- $method,
- $fetchMode,
- $this->db->dsn,
- $this->db->username,
- $rawSql ?: $rawSql = $this->getRawSql(),
- ];
- $result = $cache->get($cacheKey);
- if (is_array($result) && isset($result[0])) {
- Yii::debug('Query result served from cache', 'yii\db\Command::query');
- return $result[0];
- }
- }
- }
- $this->prepare(true);
- try {
- $profile and Yii::beginProfile($rawSql, 'yii\db\Command::query');
- $this->internalExecute($rawSql);
- if ($method === '') {
- $result = new DataReader($this);
- } else {
- if ($fetchMode === null) {
- $fetchMode = $this->fetchMode;
- }
- $result = call_user_func_array([$this->pdoStatement, $method], (array) $fetchMode);
- $this->pdoStatement->closeCursor();
- }
- $profile and Yii::endProfile($rawSql, 'yii\db\Command::query');
- } catch (Exception $e) {
- $profile and Yii::endProfile($rawSql, 'yii\db\Command::query');
- throw $e;
- }
- if (isset($cache, $cacheKey, $info)) {
- $cache->set($cacheKey, [$result], $info[1], $info[2]);
- Yii::debug('Saved query result in cache', 'yii\db\Command::query');
- }
- return $result;
- }
-
- protected function requireTableSchemaRefresh($name)
- {
- $this->_refreshTableName = $name;
- return $this;
- }
-
- protected function refreshTableSchema()
- {
- if ($this->_refreshTableName !== null) {
- $this->db->getSchema()->refreshTableSchema($this->_refreshTableName);
- }
- }
-
- protected function requireTransaction($isolationLevel = null)
- {
- $this->_isolationLevel = $isolationLevel;
- return $this;
- }
-
- protected function setRetryHandler(callable $handler)
- {
- $this->_retryHandler = $handler;
- return $this;
- }
-
- protected function internalExecute($rawSql)
- {
- $attempt = 0;
- while (true) {
- try {
- if (
- ++$attempt === 1
- && $this->_isolationLevel !== false
- && $this->db->getTransaction() === null
- ) {
- $this->db->transaction(function () use ($rawSql) {
- $this->internalExecute($rawSql);
- }, $this->_isolationLevel);
- } else {
- $this->pdoStatement->execute();
- }
- break;
- } catch (\Exception $e) {
- $rawSql = $rawSql ?: $this->getRawSql();
- $e = $this->db->getSchema()->convertException($e, $rawSql);
- if ($this->_retryHandler === null || !call_user_func($this->_retryHandler, $e, $attempt)) {
- throw $e;
- }
- }
- }
- }
-
- protected function reset()
- {
- $this->_sql = null;
- $this->_pendingParams = [];
- $this->params = [];
- $this->_refreshTableName = null;
- $this->_isolationLevel = false;
- $this->_retryHandler = null;
- }
- }
|