diff --git a/Civi/Api4/Generic/DAOGetAction.php b/Civi/Api4/Generic/DAOGetAction.php
index f374047486b853dea3bf37222471b2999a097f0a..aedc6362ca4ac5e99607970a09cd0b9785a7c67f 100644
--- a/Civi/Api4/Generic/DAOGetAction.php
+++ b/Civi/Api4/Generic/DAOGetAction.php
@@ -123,6 +123,22 @@ class DAOGetAction extends AbstractGetAction {
     }
   }
 
+  /**
+   * @param string $fieldName
+   * @param string $op
+   * @param mixed $value
+   * @param bool $isExpression
+   * @return $this
+   * @throws \API_Exception
+   */
+  public function addWhere(string $fieldName, string $op, $value = NULL, bool $isExpression = FALSE) {
+    if (!in_array($op, CoreUtil::getOperators())) {
+      throw new \API_Exception('Unsupported operator');
+    }
+    $this->where[] = [$fieldName, $op, $value, $isExpression];
+    return $this;
+  }
+
   /**
    * @return array
    */
diff --git a/Civi/Api4/Query/Api4SelectQuery.php b/Civi/Api4/Query/Api4SelectQuery.php
index 119abe49a90fd809f62c4568caf8ba717c6c6884..3f44491bde38eb5edfea37840e0b9eab6a1e4a52 100644
--- a/Civi/Api4/Query/Api4SelectQuery.php
+++ b/Civi/Api4/Query/Api4SelectQuery.php
@@ -431,7 +431,7 @@ class Api4SelectQuery {
 
   /**
    * Validate and transform a leaf clause array to SQL.
-   * @param array $clause [$fieldName, $operator, $criteria]
+   * @param array $clause [$fieldName, $operator, $criteria, $isExpression]
    * @param string $type
    *   WHERE|HAVING|ON
    * @param int $depth
@@ -443,12 +443,13 @@ class Api4SelectQuery {
     $field = NULL;
     // Pad array for unary operators
     [$expr, $operator, $value] = array_pad($clause, 3, NULL);
+    $isExpression = $clause[3] ?? FALSE;
     if (!in_array($operator, CoreUtil::getOperators(), TRUE)) {
       throw new \API_Exception('Illegal operator');
     }
 
     // For WHERE clause, expr must be the name of a field.
-    if ($type === 'WHERE') {
+    if ($type === 'WHERE' && !$isExpression) {
       $field = $this->getField($expr, TRUE);
       FormattingUtil::formatInputValue($value, $expr, $field, $operator);
       $fieldAlias = $this->getExpression($expr)->render($this->apiFieldSpec);
@@ -491,7 +492,7 @@ class Api4SelectQuery {
       }
       $fieldAlias = '`' . $fieldAlias . '`';
     }
-    elseif ($type === 'ON') {
+    elseif ($type === 'ON' || ($type === 'WHERE' && $isExpression)) {
       $expr = $this->getExpression($expr);
       $fieldName = count($expr->getFields()) === 1 ? $expr->getFields()[0] : NULL;
       $fieldAlias = $expr->render($this->apiFieldSpec);
diff --git a/Civi/Api4/Query/SqlFunctionBINARY.php b/Civi/Api4/Query/SqlFunctionBINARY.php
new file mode 100644
index 0000000000000000000000000000000000000000..e2500d83dbbf1bb6f4dcd28d04403f26192bfb44
--- /dev/null
+++ b/Civi/Api4/Query/SqlFunctionBINARY.php
@@ -0,0 +1,37 @@
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved.                        |
+ |                                                                    |
+ | This work is published under the GNU AGPLv3 license with some      |
+ | permitted exceptions and without any warranty. For full license    |
+ | and copyright information, see https://civicrm.org/licensing       |
+ +--------------------------------------------------------------------+
+ */
+
+namespace Civi\Api4\Query;
+
+/**
+ * Sql function
+ */
+class SqlFunctionBINARY extends SqlFunction {
+
+  protected static $category = self::CATEGORY_STRING;
+
+  protected static function params(): array {
+    return [
+      [
+        'optional' => FALSE,
+        'must_be' => ['SqlField', 'SqlString'],
+      ],
+    ];
+  }
+
+  /**
+   * @return string
+   */
+  public static function getTitle(): string {
+    return ts('Binary');
+  }
+
+}
diff --git a/tests/phpunit/api/v4/Action/ContactGetTest.php b/tests/phpunit/api/v4/Action/ContactGetTest.php
index 2e5075ea03258b8fe1761c44ce011c3cf079c88c..b47a69480d58a11ab71af2ac85607b110cdb8fdb 100644
--- a/tests/phpunit/api/v4/Action/ContactGetTest.php
+++ b/tests/phpunit/api/v4/Action/ContactGetTest.php
@@ -261,6 +261,26 @@ class ContactGetTest extends \api\v4\UnitTestCase {
     $this->assertEquals(['Student'], $result['Contact_RelationshipCache_Contact_01.contact_sub_type:label']);
   }
 
+  public function testGetWithWhereExpression() {
+    $last_name = uniqid(__FUNCTION__);
+
+    $alice = Contact::create()
+      ->setValues(['first_name' => 'Alice', 'last_name' => $last_name])
+      ->execute()->first();
+
+    $result = Contact::get(FALSE)
+      ->addWhere('last_name', '=', $last_name)
+      ->addWhere('LOWER(first_name)', '=', "BINARY('ALICE')", TRUE)
+      ->execute()->indexBy('id');
+    $this->assertCount(0, $result);
+
+    $result = Contact::get(FALSE)
+      ->addWhere('last_name', '=', $last_name)
+      ->addWhere('LOWER(first_name)', '=', "BINARY('alice')", TRUE)
+      ->execute()->indexBy('id');
+    $this->assertArrayHasKey($alice['id'], (array) $result);
+  }
+
   /**
    * @throws \API_Exception
    */
diff --git a/tests/phpunit/api/v4/Action/FkJoinTest.php b/tests/phpunit/api/v4/Action/FkJoinTest.php
index d7d95d5f8b229cdf1705636271b165d108b0dc07..960b638ad4c7c1b62fe37829f50ff4f2de03a76c 100644
--- a/tests/phpunit/api/v4/Action/FkJoinTest.php
+++ b/tests/phpunit/api/v4/Action/FkJoinTest.php
@@ -426,4 +426,19 @@ class FkJoinTest extends UnitTestCase {
     $this->assertStringContainsString("Deprecated join alias 'contact' used in APIv4 get. Should be changed to 'contact_id'", $message);
   }
 
+  public function testJoinWithExpression() {
+    Phone::create(FALSE)
+      ->setValues(['contact_id' => $this->getReference('test_contact_1')['id'], 'phone' => '654321'])
+      ->execute();
+    $contacts = Contact::get(FALSE)
+      ->addSelect('id', 'phone.phone')
+      ->addJoin('Phone', 'INNER', ['LOWER(phone.phone)', '=', "CONCAT('6', '5', '4', '3', '2', '1')"])
+      ->addWhere('id', 'IN', [$this->getReference('test_contact_1')['id'], $this->getReference('test_contact_2')['id']])
+      ->addOrderBy('phone.id')
+      ->execute();
+    $this->assertCount(1, $contacts);
+    $this->assertEquals($this->getReference('test_contact_1')['id'], $contacts[0]['id']);
+    $this->assertEquals('654321', $contacts[0]['phone.phone']);
+  }
+
 }
diff --git a/tests/phpunit/api/v4/Action/SqlFunctionTest.php b/tests/phpunit/api/v4/Action/SqlFunctionTest.php
index 5cf7373770d89da00fb1ae6544bd90ecdf3186a5..ebac8d5d30cb7a81772039e6552763c335d2ea8b 100644
--- a/tests/phpunit/api/v4/Action/SqlFunctionTest.php
+++ b/tests/phpunit/api/v4/Action/SqlFunctionTest.php
@@ -131,6 +131,20 @@ class SqlFunctionTest extends UnitTestCase {
     $this->assertEquals(100, $result[0]['MIN:total_amount']);
     $this->assertEquals(2, $result[0]['count']);
     $this->assertEquals(1, $result[1]['count']);
+
+    $result = Contribution::get(FALSE)
+      ->addGroupBy('contact_id')
+      ->addGroupBy('receive_date')
+      ->addSelect('contact_id')
+      ->addSelect('receive_date')
+      ->addSelect('SUM(total_amount)')
+      ->addOrderBy('receive_date')
+      ->addWhere('contact_id', '=', $cid)
+      ->addHaving('SUM(total_amount)', '>', 300)
+      ->execute();
+    $this->assertCount(1, $result);
+    $this->assertStringStartsWith('2020-04-04', $result[0]['receive_date']);
+    $this->assertEquals(400, $result[0]['SUM:total_amount']);
   }
 
   public function testComparisonFunctions() {