mirror of
				https://github.com/yiisoft/yii2.git
				synced 2025-11-04 14:46:19 +08:00 
			
		
		
		
	fixes #6792, use a simpler composite in condition implementation on dbs other than pgsql
This commit is contained in:
		@ -1134,27 +1134,26 @@ class QueryBuilder extends \yii\base\Object
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    protected function buildCompositeInCondition($operator, $columns, $values, &$params)
 | 
					    protected function buildCompositeInCondition($operator, $columns, $values, &$params)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					        $quotedColumns = [];
 | 
				
			||||||
 | 
					        foreach ($columns as $i => $column) {
 | 
				
			||||||
 | 
					            $quotedColumns[$i] = strpos($column, '(') === false ? $this->db->quoteColumnName($column) : $column;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        $vss = [];
 | 
					        $vss = [];
 | 
				
			||||||
        foreach ($values as $value) {
 | 
					        foreach ($values as $value) {
 | 
				
			||||||
            $vs = [];
 | 
					            $vs = [];
 | 
				
			||||||
            foreach ($columns as $column) {
 | 
					            foreach ($columns as $i => $column) {
 | 
				
			||||||
                if (isset($value[$column])) {
 | 
					                if (isset($value[$column])) {
 | 
				
			||||||
                    $phName = self::PARAM_PREFIX . count($params);
 | 
					                    $phName = self::PARAM_PREFIX . count($params);
 | 
				
			||||||
                    $params[$phName] = $value[$column];
 | 
					                    $params[$phName] = $value[$column];
 | 
				
			||||||
                    $vs[] = $phName;
 | 
					                    $vs[] = $quotedColumns[$i] . ($operator === 'IN' ? ' = ' : ' != ') . $phName;
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    $vs[] = 'NULL';
 | 
					                    $vs[] = $quotedColumns[$i] . ($operator === 'IN' ? ' IS' : ' IS NOT') . ' NULL';
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            $vss[] = '(' . implode(', ', $vs) . ')';
 | 
					            $vss[] = '(' . implode($operator === 'IN' ? ' AND ' : ' OR ', $vs) . ')';
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        foreach ($columns as $i => $column) {
 | 
					 | 
				
			||||||
            if (strpos($column, '(') === false) {
 | 
					 | 
				
			||||||
                $columns[$i] = $this->db->quoteColumnName($column);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return '(' . implode(', ', $columns) . ") $operator (" . implode(', ', $vss) . ')';
 | 
					        return '(' . implode($operator === 'IN' ? ' OR ' : ' AND ', $vss) . ')';
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
 | 
				
			|||||||
@ -204,4 +204,38 @@ class QueryBuilder extends \yii\db\QueryBuilder
 | 
				
			|||||||
        return 'INSERT INTO ' . $schema->quoteTableName($table)
 | 
					        return 'INSERT INTO ' . $schema->quoteTableName($table)
 | 
				
			||||||
        . ' (' . implode(', ', $columns) . ') VALUES ' . implode(', ', $values);
 | 
					        . ' (' . implode(', ', $columns) . ') VALUES ' . implode(', ', $values);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Builds SQL for IN condition
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param string $operator
 | 
				
			||||||
 | 
					     * @param array $columns
 | 
				
			||||||
 | 
					     * @param array $values
 | 
				
			||||||
 | 
					     * @param array $params
 | 
				
			||||||
 | 
					     * @return string SQL
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected function buildCompositeInCondition($operator, $columns, $values, &$params)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $vss = [];
 | 
				
			||||||
 | 
					        foreach ($values as $value) {
 | 
				
			||||||
 | 
					            $vs = [];
 | 
				
			||||||
 | 
					            foreach ($columns as $column) {
 | 
				
			||||||
 | 
					                if (isset($value[$column])) {
 | 
				
			||||||
 | 
					                    $phName = self::PARAM_PREFIX . count($params);
 | 
				
			||||||
 | 
					                    $params[$phName] = $value[$column];
 | 
				
			||||||
 | 
					                    $vs[] = $phName;
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    $vs[] = 'NULL';
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            $vss[] = '(' . implode(', ', $vs) . ')';
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        foreach ($columns as $i => $column) {
 | 
				
			||||||
 | 
					            if (strpos($column, '(') === false) {
 | 
				
			||||||
 | 
					                $columns[$i] = $this->db->quoteColumnName($column);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return '(' . implode(', ', $columns) . ") $operator (" . implode(', ', $vss) . ')';
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -201,12 +201,6 @@ class QueryBuilderTest extends DatabaseTestCase
 | 
				
			|||||||
            [ ['in', 'id', (new Query())->select('id')->from('users')->where(['active' => 1])], '(`id`) IN (SELECT `id` FROM `users` WHERE `active`=:qp0)', [':qp0' => 1] ],
 | 
					            [ ['in', 'id', (new Query())->select('id')->from('users')->where(['active' => 1])], '(`id`) IN (SELECT `id` FROM `users` WHERE `active`=:qp0)', [':qp0' => 1] ],
 | 
				
			||||||
            [ ['not in', 'id', (new Query())->select('id')->from('users')->where(['active' => 1])], '(`id`) NOT IN (SELECT `id` FROM `users` WHERE `active`=:qp0)', [':qp0' => 1] ],
 | 
					            [ ['not in', 'id', (new Query())->select('id')->from('users')->where(['active' => 1])], '(`id`) NOT IN (SELECT `id` FROM `users` WHERE `active`=:qp0)', [':qp0' => 1] ],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // composite in
 | 
					 | 
				
			||||||
            [ ['in', ['id', 'name'], [['id' => 1, 'name' => 'foo'], ['id' => 2, 'name' => 'bar']]], '(`id`, `name`) IN ((:qp0, :qp1), (:qp2, :qp3))', [':qp0' => 1, ':qp1' => 'foo', ':qp2' => 2, ':qp3' => 'bar'] ],
 | 
					 | 
				
			||||||
            [ ['not in', ['id', 'name'], [['id' => 1, 'name' => 'foo'], ['id' => 2, 'name' => 'bar']]], '(`id`, `name`) NOT IN ((:qp0, :qp1), (:qp2, :qp3))', [':qp0' => 1, ':qp1' => 'foo', ':qp2' => 2, ':qp3' => 'bar'] ],
 | 
					 | 
				
			||||||
            [ ['in', ['id', 'name'], (new Query())->select(['id', 'name'])->from('users')->where(['active' => 1])], '(`id`, `name`) IN (SELECT `id`, `name` FROM `users` WHERE `active`=:qp0)', [':qp0' => 1] ],
 | 
					 | 
				
			||||||
            [ ['not in', ['id', 'name'], (new Query())->select(['id', 'name'])->from('users')->where(['active' => 1])], '(`id`, `name`) NOT IN (SELECT `id`, `name` FROM `users` WHERE `active`=:qp0)', [':qp0' => 1] ],
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // exists
 | 
					            // exists
 | 
				
			||||||
            [ ['exists', (new Query())->select('id')->from('users')->where(['active' => 1])], 'EXISTS (SELECT `id` FROM `users` WHERE `active`=:qp0)', [':qp0' => 1] ],
 | 
					            [ ['exists', (new Query())->select('id')->from('users')->where(['active' => 1])], 'EXISTS (SELECT `id` FROM `users` WHERE `active`=:qp0)', [':qp0' => 1] ],
 | 
				
			||||||
            [ ['not exists', (new Query())->select('id')->from('users')->where(['active' => 1])], 'NOT EXISTS (SELECT `id` FROM `users` WHERE `active`=:qp0)', [':qp0' => 1] ],
 | 
					            [ ['not exists', (new Query())->select('id')->from('users')->where(['active' => 1])], 'NOT EXISTS (SELECT `id` FROM `users` WHERE `active`=:qp0)', [':qp0' => 1] ],
 | 
				
			||||||
@ -228,6 +222,23 @@ class QueryBuilderTest extends DatabaseTestCase
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        ];
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        switch ($this->driverName) {
 | 
				
			||||||
 | 
					            default:
 | 
				
			||||||
 | 
					                $conditions = array_merge($conditions, [
 | 
				
			||||||
 | 
					                    [ ['in', ['id', 'name'], [['id' => 1, 'name' => 'foo'], ['id' => 2, 'name' => 'bar']]], '((`id` = :qp0 AND `name` = :qp1) OR (`id` = :qp2 AND `name` = :qp3))', [':qp0' => 1, ':qp1' => 'foo', ':qp2' => 2, ':qp3' => 'bar'] ],
 | 
				
			||||||
 | 
					                    [ ['not in', ['id', 'name'], [['id' => 1, 'name' => 'foo'], ['id' => 2, 'name' => 'bar']]], '((`id` != :qp0 OR `name` != :qp1) AND (`id` != :qp2 OR `name` != :qp3))', [':qp0' => 1, ':qp1' => 'foo', ':qp2' => 2, ':qp3' => 'bar'] ],
 | 
				
			||||||
 | 
					                ]);
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case 'pgsql':
 | 
				
			||||||
 | 
					                $conditions = array_merge($conditions, [
 | 
				
			||||||
 | 
					                    [ ['in', ['id', 'name'], [['id' => 1, 'name' => 'foo'], ['id' => 2, 'name' => 'bar']]], '(`id`, `name`) IN ((:qp0, :qp1), (:qp2, :qp3))', [':qp0' => 1, ':qp1' => 'foo', ':qp2' => 2, ':qp3' => 'bar'] ],
 | 
				
			||||||
 | 
					                    [ ['not in', ['id', 'name'], [['id' => 1, 'name' => 'foo'], ['id' => 2, 'name' => 'bar']]], '(`id`, `name`) NOT IN ((:qp0, :qp1), (:qp2, :qp3))', [':qp0' => 1, ':qp1' => 'foo', ':qp2' => 2, ':qp3' => 'bar'] ],
 | 
				
			||||||
 | 
					                    [ ['in', ['id', 'name'], (new Query())->select(['id', 'name'])->from('users')->where(['active' => 1])], '(`id`, `name`) IN (SELECT `id`, `name` FROM `users` WHERE `active`=:qp0)', [':qp0' => 1] ],
 | 
				
			||||||
 | 
					                    [ ['not in', ['id', 'name'], (new Query())->select(['id', 'name'])->from('users')->where(['active' => 1])], '(`id`, `name`) NOT IN (SELECT `id`, `name` FROM `users` WHERE `active`=:qp0)', [':qp0' => 1] ],
 | 
				
			||||||
 | 
					                ]);
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // adjust dbms specific escaping
 | 
					        // adjust dbms specific escaping
 | 
				
			||||||
        foreach($conditions as $i => $condition) {
 | 
					        foreach($conditions as $i => $condition) {
 | 
				
			||||||
            $conditions[$i][1] = $this->replaceQuotes($condition[1]);
 | 
					            $conditions[$i][1] = $this->replaceQuotes($condition[1]);
 | 
				
			||||||
@ -444,4 +455,17 @@ class QueryBuilderTest extends DatabaseTestCase
 | 
				
			|||||||
        $this->assertEquals($expected, $sql);
 | 
					        $this->assertEquals($expected, $sql);
 | 
				
			||||||
        $this->assertEmpty($params);
 | 
					        $this->assertEmpty($params);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function testCompositeInCondition()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $condition = [
 | 
				
			||||||
 | 
					            'in',
 | 
				
			||||||
 | 
					            ['id', 'name'],
 | 
				
			||||||
 | 
					            [
 | 
				
			||||||
 | 
					                ['id' => 1, 'name' => 'foo'],
 | 
				
			||||||
 | 
					                ['id' => 2, 'name' => 'bar'],
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					        (new Query())->from('customer')->where($condition)->all($this->getConnection());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user