}
/* Transaction stays in the ERROR state until rolled back */
if ( $this->mTrxLevel ) {
- $this->rollback( __METHOD__ );
+ // Throw away the transaction state, then raise the error as normal.
+ // Note that if this connection is managed by LBFactory, it's already expected
+ // that the other transactions LBFactory manages will be rolled back.
+ $this->rollback( __METHOD__, self::FLUSHING_INTERNAL );
}
parent::reportQueryError( $error, $errno, $sql, $fname, false );
}
public function selectSQLText(
$table, $vars, $conds = '', $fname = __METHOD__, $options = [], $join_conds = []
) {
+ if ( is_string( $options ) ) {
+ $options = [ $options ];
+ }
+
// Change the FOR UPDATE option as necessary based on the join conditions. Then pass
// to the parent function to get the actual SQL text.
// In Postgres when using FOR UPDATE, only the main table and tables that are inner joined
$forUpdateKey = array_search( 'FOR UPDATE', $options, true );
if ( $forUpdateKey !== false && $join_conds ) {
unset( $options[$forUpdateKey] );
+ $options['FOR UPDATE'] = [];
+
+ // All tables not in $join_conds are good
+ foreach ( $table as $alias => $name ) {
+ if ( is_numeric( $alias ) ) {
+ $alias = $name;
+ }
+ if ( !isset( $join_conds[$alias] ) ) {
+ $options['FOR UPDATE'][] = $alias;
+ }
+ }
foreach ( $join_conds as $table_cond => $join_cond ) {
if ( 0 === preg_match( '/^(?:LEFT|RIGHT|FULL)(?: OUTER)? JOIN$/i', $join_cond[0] ) ) {
$options['FOR UPDATE'][] = $table_cond;
}
}
+
+ // Quote alias names so $this->tableName() won't mangle them
+ $options['FOR UPDATE'] = array_map( function ( $name ) use ( $table ) {
+ return isset( $table[$name] ) ? $this->addIdentifierQuotes( $name ) : $name;
+ }, $options['FOR UPDATE'] );
}
if ( isset( $options['ORDER BY'] ) && $options['ORDER BY'] == 'NULL' ) {
* @param string $fname
* @param array $insertOptions
* @param array $selectOptions
+ * @param array $selectJoinConds
* @return bool
*/
public function nativeInsertSelect(
$destTable, $srcTable, $varMap, $conds, $fname = __METHOD__,
- $insertOptions = [], $selectOptions = []
+ $insertOptions = [], $selectOptions = [], $selectJoinConds = []
) {
- $destTable = $this->tableName( $destTable );
-
if ( !is_array( $insertOptions ) ) {
$insertOptions = [ $insertOptions ];
}
$savepoint->savepoint();
}
- if ( !is_array( $selectOptions ) ) {
- $selectOptions = [ $selectOptions ];
- }
- list( $startOpts, $useIndex, $tailOpts, $ignoreIndex ) =
- $this->makeSelectOptions( $selectOptions );
- if ( is_array( $srcTable ) ) {
- $srcTable = implode( ',', array_map( [ $this, 'tableName' ], $srcTable ) );
- } else {
- $srcTable = $this->tableName( $srcTable );
- }
-
- $sql = "INSERT INTO $destTable (" . implode( ',', array_keys( $varMap ) ) . ')' .
- " SELECT $startOpts " . implode( ',', $varMap ) .
- " FROM $srcTable $useIndex $ignoreIndex ";
-
- if ( $conds != '*' ) {
- $sql .= ' WHERE ' . $this->makeList( $conds, LIST_AND );
- }
-
- $sql .= " $tailOpts";
+ $res = parent::nativeInsertSelect( $destTable, $srcTable, $varMap, $conds, $fname,
+ $insertOptions, $selectOptions, $selectJoinConds );
- $res = (bool)$this->query( $sql, $fname, $savepoint );
if ( $savepoint ) {
$bar = pg_result_error( $this->mLastResult );
if ( $bar != false ) {
if ( $schema === false ) {
$schema = $this->getCoreSchema();
}
+ $table = $this->realTableName( $table, 'raw' );
$etable = $this->addQuotes( $table );
$eschema = $this->addQuotes( $schema );
$sql = "SELECT 1 FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n "
return false;
}
+ public function serverIsReadOnly() {
+ $res = $this->query( "SHOW default_transaction_read_only", __METHOD__ );
+ $row = $this->fetchObject( $res );
+
+ return $row ? ( strtolower( $row->default_transaction_read_only ) === 'on' ) : false;
+ }
+
/**
* @param string $lockName
* @return string Integer