Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

APIv4 - Prevent field alias conflicts. #17109

Merged
merged 1 commit into from
Apr 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CRM/Contact/BAO/GroupContactCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -721,12 +721,12 @@ protected static function getApiSQL(array $savedSearch, string $addSelect, strin
list($idField) = explode(' AS ', $apiParams['select'][0]);
$apiParams['select'] = [
$addSelect,
$idField . ' AS smart_group_contact_id',
$idField,
];
$api = \Civi\API\Request::create($savedSearch['api_entity'], 'get', $apiParams);
$query = new \Civi\Api4\Query\Api4SelectQuery($api);
$query->forceSelectId = FALSE;
$query->getQuery()->having('smart_group_contact_id ' . $excludeClause);
$query->getQuery()->having("$idField $excludeClause");
return $query->getSql();
}

Expand Down
3 changes: 3 additions & 0 deletions Civi/Api4/Query/Api4SelectQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,9 @@ protected function buildSelectClause() {
}
if ($valid) {
$alias = $expr->getAlias();
if ($alias != $expr->getExpr() && isset($this->apiFieldSpec[$alias])) {
throw new \API_Exception('Cannot use existing field name as alias');
}
$this->selectAliases[$alias] = $expr->getExpr();
$this->query->select($expr->render($this->apiFieldSpec) . " AS `$alias`");
}
Expand Down
3 changes: 3 additions & 0 deletions Civi/Api4/Query/SqlField.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
class SqlField extends SqlExpression {

protected function initialize() {
if ($this->alias && $this->alias !== $this->expr) {
throw new \API_Exception("Aliasing field names is not allowed, only expressions can have an alias.");
}
$this->fields[] = $this->expr;
}

Expand Down
32 changes: 28 additions & 4 deletions tests/phpunit/api/v4/Action/SqlExpressionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,15 @@ class SqlExpressionTest extends UnitTestCase {
public function testSelectNull() {
Contact::create()->addValue('first_name', 'bob')->setCheckPermissions(FALSE)->execute();
$result = Contact::get()
->addSelect('NULL AS nothing', 'NULL', 'NULL AS b*d char', 'first_name AS firsty')
->addSelect('NULL AS nothing', 'NULL', 'NULL AS b*d char', 'first_name')
->addWhere('first_name', '=', 'bob')
->setLimit(1)
->execute()
->first();
$this->assertNull($result['nothing']);
$this->assertNull($result['NULL']);
$this->assertNull($result['b_d_char']);
$this->assertEquals('bob', $result['firsty']);
$this->assertEquals('bob', $result['first_name']);
$this->assertArrayNotHasKey('b*d char', $result);
}

Expand All @@ -60,16 +60,40 @@ public function testSelectNumbers() {
public function testSelectStrings() {
Contact::create()->addValue('first_name', 'bob')->setCheckPermissions(FALSE)->execute();
$result = Contact::get()
->addSelect('first_name AS bob')
->addSelect('first_name')
->addSelect('"hello world" AS hi')
->addSelect('"can\'t \"quote\"" AS quot')
->addWhere('first_name', '=', 'bob')
->setLimit(1)
->execute()
->first();
$this->assertEquals('bob', $result['bob']);
$this->assertEquals('bob', $result['first_name']);
$this->assertEquals('hello world', $result['hi']);
$this->assertEquals('can\'t "quote"', $result['quot']);
}

public function testSelectAlias() {
try {
Contact::get()
->addSelect('first_name AS bob')
->execute();
}
catch (\API_Exception $e) {
$msg = $e->getMessage();
}
$this->assertContains('alias', $msg);
try {
Contact::get()
->addSelect('55 AS sort_name')
->execute();
}
catch (\API_Exception $e) {
$msg = $e->getMessage();
}
$this->assertContains('existing field name', $msg);
Contact::get()
->addSelect('55 AS ok_alias')
->execute();
}

}