Revert "APIv4 - Add SQL expression handling and aggregate functions"
authorColeman Watts <coleman@civicrm.org>
Sun, 19 Apr 2020 23:56:20 +0000 (19:56 -0400)
committerColeman Watts <coleman@civicrm.org>
Sun, 19 Apr 2020 23:56:20 +0000 (19:56 -0400)
This reverts commit 3176b04cb62b0e8f94454e367736f50454f89de8.

19 files changed:
Civi/Api4/Generic/AbstractGetAction.php
Civi/Api4/Generic/DAOGetFieldsAction.php
Civi/Api4/Query/Api4SelectQuery.php
Civi/Api4/Query/SqlExpression.php [deleted file]
Civi/Api4/Query/SqlField.php [deleted file]
Civi/Api4/Query/SqlFunction.php [deleted file]
Civi/Api4/Query/SqlFunctionAVG.php [deleted file]
Civi/Api4/Query/SqlFunctionCOUNT.php [deleted file]
Civi/Api4/Query/SqlFunctionMAX.php [deleted file]
Civi/Api4/Query/SqlFunctionMIN.php [deleted file]
Civi/Api4/Query/SqlFunctionSUM.php [deleted file]
Civi/Api4/Query/SqlNull.php [deleted file]
Civi/Api4/Query/SqlNumber.php [deleted file]
Civi/Api4/Query/SqlString.php [deleted file]
Civi/Api4/Query/SqlWild.php [deleted file]
tests/phpunit/api/v4/Action/SqlExpressionTest.php [deleted file]
tests/phpunit/api/v4/Action/SqlFunctionTest.php [deleted file]
tests/phpunit/api/v4/Query/SqlExpressionParserTest.php [deleted file]
tests/phpunit/api/v4/UnitTestCase.php

index dc020f4af146dd4a8bc88d768c0a33bbebbb319a..d82e7071b1e6f0328d65c8afae9a774d14e242da 100644 (file)
@@ -81,7 +81,7 @@ abstract class AbstractGetAction extends AbstractQueryAction {
    */
   protected function expandSelectClauseWildcards() {
     $wildFields = array_filter($this->select, function($item) {
-      return strpos($item, '*') !== FALSE && strpos($item, '.') === FALSE && strpos($item, '(') === FALSE && strpos($item, ' ') === FALSE;
+      return strpos($item, '*') !== FALSE && strpos($item, '.') === FALSE;
     });
     foreach ($wildFields as $item) {
       $pos = array_search($item, array_values($this->select));
index 74afc1715fc9d1446475b30b17c79ff804f927ce..7d7ac1849d0a0158340d92774b231b76b4ba32c8 100644 (file)
@@ -63,10 +63,6 @@ class DAOGetFieldsAction extends BasicGetFieldsAction {
       'name' => 'help_post',
       'data_type' => 'String',
     ];
-    $fields[] = [
-      'name' => 'column_name',
-      'data_type' => 'String',
-    ];
     $fields[] = [
       'name' => 'custom_field_id',
       'data_type' => 'Integer',
index 09e95f147ec9260647010b77099147f498843675..38c46ad887917c028710e5fbf26b75093a26fcc5 100644 (file)
@@ -47,11 +47,6 @@ class Api4SelectQuery extends SelectQuery {
    */
   protected $joinedTables = [];
 
-  /**
-   * @var array
-   */
-  protected $selectAliases = [];
-
   /**
    * If set to an array, this will start collecting debug info.
    *
@@ -129,10 +124,12 @@ class Api4SelectQuery extends SelectQuery {
         break;
       }
       $results[$id] = [];
-      foreach ($this->selectAliases as $alias) {
+      foreach ($this->select as $alias) {
         $returnName = $alias;
-        $alias = str_replace('.', '_', $alias);
-        $results[$id][$returnName] = property_exists($query, $alias) ? $query->$alias : NULL;
+        if ($this->isOneToOneField($alias)) {
+          $alias = str_replace('.', '_', $alias);
+          $results[$id][$returnName] = property_exists($query, $alias) ? $query->$alias : NULL;
+        }
       }
     }
     $event = new PostSelectQueryEvent($results, $this);
@@ -158,7 +155,7 @@ class Api4SelectQuery extends SelectQuery {
 
       // Expand wildcards in joins (the api wrapper already expanded non-joined wildcards)
       $wildFields = array_filter($this->select, function($item) {
-        return strpos($item, '*') !== FALSE && strpos($item, '.') !== FALSE && strpos($item, '(') === FALSE && strpos($item, ' ') === FALSE;
+        return strpos($item, '*') !== FALSE && strpos($item, '.') !== FALSE;
       });
       foreach ($wildFields as $item) {
         $pos = array_search($item, array_values($this->select));
@@ -168,26 +165,20 @@ class Api4SelectQuery extends SelectQuery {
       }
       $this->select = array_unique($this->select);
     }
-    foreach ($this->select as $item) {
-      $expr = SqlExpression::convert($item, TRUE);
-      $valid = TRUE;
-      foreach ($expr->getFields() as $fieldName) {
-        $field = $this->getField($fieldName);
-        // Remove expressions with unknown fields without raising an error
-        if (!$field) {
-          $this->select = array_diff($this->select, [$item]);
-          if (is_array($this->debugOutput)) {
-            $this->debugOutput['undefined_fields'][] = $fieldName;
-          }
-          $valid = FALSE;
-        }
-        elseif ($field['is_many']) {
-          $valid = FALSE;
+    foreach ($this->select as $fieldName) {
+      $field = $this->getField($fieldName);
+      // Remove unknown fields without raising an error
+      if (!$field) {
+        $this->select = array_diff($this->select, [$fieldName]);
+        if (is_array($this->debugOutput)) {
+          $this->debugOutput['undefined_fields'][] = $fieldName;
         }
       }
-      if ($valid) {
-        $alias = $this->selectAliases[] = $expr->getAlias();
-        $this->query->select($expr->render($this->apiFieldSpec) . " AS `$alias`");
+      elseif ($field['is_many']) {
+        continue;
+      }
+      elseif ($field) {
+        $this->query->select($field['sql_name'] . " AS `$fieldName`");
       }
     }
   }
@@ -206,15 +197,11 @@ class Api4SelectQuery extends SelectQuery {
    * @inheritDoc
    */
   protected function buildOrderBy() {
-    foreach ($this->orderBy as $item => $dir) {
+    foreach ($this->orderBy as $fieldName => $dir) {
       if ($dir !== 'ASC' && $dir !== 'DESC') {
-        throw new \API_Exception("Invalid sort direction. Cannot order by $item $dir");
-      }
-      $expr = SqlExpression::convert($item);
-      foreach ($expr->getFields() as $fieldName) {
-        $this->getField($fieldName, TRUE);
+        throw new \API_Exception("Invalid sort direction. Cannot order by $fieldName $dir");
       }
-      $this->query->orderBy($expr->render($this->apiFieldSpec) . " $dir");
+      $this->query->orderBy($this->getField($fieldName, TRUE)['sql_name'] . " $dir");
     }
   }
 
@@ -228,15 +215,16 @@ class Api4SelectQuery extends SelectQuery {
   }
 
   /**
-   * Adds GROUP BY clause to query
+   *
    */
   protected function buildGroupBy() {
-    foreach ($this->groupBy as $item) {
-      $expr = SqlExpression::convert($item);
-      foreach ($expr->getFields() as $fieldName) {
-        $this->getField($fieldName, TRUE);
+    foreach ($this->groupBy as $field) {
+      if ($this->isOneToOneField($field) && $this->getField($field)) {
+        $this->query->groupBy($field['sql_name']);
+      }
+      else {
+        throw new \API_Exception("Invalid field. Cannot group by $field");
       }
-      $this->query->groupBy($expr->render($this->apiFieldSpec));
     }
   }
 
@@ -312,7 +300,6 @@ class Api4SelectQuery extends SelectQuery {
    *
    * @param string $fieldName
    * @param bool $strict
-   *   In strict mode, this will throw an exception if the field doesn't exist
    *
    * @return string|null
    * @throws \API_Exception
@@ -323,10 +310,13 @@ class Api4SelectQuery extends SelectQuery {
       $this->joinFK($fieldName);
     }
     $field = $this->apiFieldSpec[$fieldName] ?? NULL;
-    if ($strict && !$field) {
+    if ($field) {
+      return $field;
+    }
+    elseif ($strict) {
       throw new \API_Exception("Invalid field '$fieldName'");
     }
-    return $field;
+    return NULL;
   }
 
   /**
diff --git a/Civi/Api4/Query/SqlExpression.php b/Civi/Api4/Query/SqlExpression.php
deleted file mode 100644 (file)
index 198ce1c..0000000
+++ /dev/null
@@ -1,144 +0,0 @@
-<?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;
-
-/**
- * Base class for SqlColumn, SqlString, SqlBool, and SqlFunction classes.
- *
- * These are used to validate and format sql expressions in Api4 select queries.
- *
- * @package Civi\Api4\Query
- */
-abstract class SqlExpression {
-
-  /**
-   * @var array
-   */
-  protected $fields = [];
-
-  /**
-   * @var string|null
-   */
-  protected $alias;
-
-  /**
-   * The argument string.
-   * @var string
-   */
-  protected $arg = '';
-
-  /**
-   * SqlFunction constructor.
-   * @param string $arg
-   * @param string|null $alias
-   */
-  public function __construct(string $arg, $alias = NULL) {
-    $this->arg = $arg;
-    $this->alias = $alias;
-    $this->initialize();
-  }
-
-  abstract protected function initialize();
-
-  /**
-   * Converts a string to a SqlExpression object.
-   *
-   * E.g. the expression "SUM(foo)" would return a SqlFunctionSUM object.
-   *
-   * @param string $expression
-   * @param bool $parseAlias
-   * @param array $mustBe
-   * @param array $cantBe
-   * @return SqlExpression
-   * @throws \API_Exception
-   */
-  public static function convert(string $expression, $parseAlias = FALSE, $mustBe = [], $cantBe = ['SqlWild']) {
-    $as = $parseAlias ? strrpos($expression, ' AS ') : FALSE;
-    $expr = $as ? substr($expression, 0, $as) : $expression;
-    $alias = $as ? \CRM_Utils_String::munge(substr($expression, $as + 4)) : NULL;
-    $bracketPos = strpos($expr, '(');
-    $firstChar = substr($expr, 0, 1);
-    $lastChar = substr($expr, -1);
-    // Function
-    if ($bracketPos && $lastChar === ')') {
-      $fnName = substr($expr, 0, $bracketPos);
-      if ($fnName !== strtoupper($fnName)) {
-        throw new \API_Exception('Sql function must be uppercase.');
-      }
-      $className = 'SqlFunction' . $fnName;
-      $expr = substr($expr, $bracketPos + 1, -1);
-    }
-    // String expression
-    elseif ($firstChar === $lastChar && in_array($firstChar, ['"', "'"], TRUE)) {
-      $className = 'SqlString';
-    }
-    elseif ($expr === 'NULL') {
-      $className = 'SqlNull';
-    }
-    elseif ($expr === '*') {
-      $className = 'SqlWild';
-    }
-    elseif (is_numeric($expr)) {
-      $className = 'SqlNumber';
-    }
-    // If none of the above, assume it's a field name
-    else {
-      $className = 'SqlField';
-    }
-    $className = __NAMESPACE__ . '\\' . $className;
-    if (!class_exists($className)) {
-      throw new \API_Exception('Unable to parse sql expression: ' . $expression);
-    }
-    $sqlExpression = new $className($expr, $alias);
-    foreach ($cantBe as $cant) {
-      if (is_a($sqlExpression, __NAMESPACE__ . '\\' . $cant)) {
-        throw new \API_Exception('Illegal sql expression.');
-      }
-    }
-    if ($mustBe) {
-      foreach ($mustBe as $must) {
-        if (is_a($sqlExpression, __NAMESPACE__ . '\\' . $must)) {
-          return $sqlExpression;
-        }
-      }
-      throw new \API_Exception('Illegal sql expression.');
-    }
-    return $sqlExpression;
-  }
-
-  /**
-   * Returns the field names of all sql columns that are arguments to this expression.
-   *
-   * @return array
-   */
-  public function getFields(): array {
-    return $this->fields;
-  }
-
-  /**
-   * Renders expression to a sql string, replacing field names with column names.
-   *
-   * @param array $fieldList
-   * @return string
-   */
-  abstract public function render(array $fieldList): string;
-
-  /**
-   * Returns the alias to use for SELECT AS.
-   *
-   * @return string
-   */
-  public function getAlias(): string {
-    return $this->alias ?? $this->fields[0] ?? \CRM_Utils_String::munge($this->arg);
-  }
-
-}
diff --git a/Civi/Api4/Query/SqlField.php b/Civi/Api4/Query/SqlField.php
deleted file mode 100644 (file)
index 488e3b0..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-<?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 column expression
- */
-class SqlField extends SqlExpression {
-
-  protected function initialize() {
-    $this->fields[] = $this->arg;
-  }
-
-  public function render(array $fieldList): string {
-    if (empty($fieldList[$this->arg])) {
-      throw new \API_Exception("Invalid field '{$this->arg}'");
-    }
-    return $fieldList[$this->arg]['sql_name'];
-  }
-
-}
diff --git a/Civi/Api4/Query/SqlFunction.php b/Civi/Api4/Query/SqlFunction.php
deleted file mode 100644 (file)
index df93b66..0000000
+++ /dev/null
@@ -1,190 +0,0 @@
-<?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;
-
-/**
- * Base class for all Sql functions.
- *
- * @package Civi\Api4\Query
- */
-abstract class SqlFunction extends SqlExpression {
-
-  protected static $params = [];
-
-  protected $args = [];
-
-  /**
-   * Parse the argument string into an array of function arguments
-   */
-  protected function initialize() {
-    $arg = $this->arg;
-    foreach ($this->getParams() as $param) {
-      $prefix = $this->captureKeyword($param['prefix'], $arg);
-      if ($param['expr'] && isset($prefix) || in_array('', $param['prefix']) || !$param['optional']) {
-        $this->captureExpressions($arg, $param['expr'], $param['must_be'], $param['cant_be']);
-        $this->captureKeyword($param['suffix'], $arg);
-      }
-    }
-  }
-
-  /**
-   * Shift a keyword off the beginning of the argument string and into the argument array.
-   *
-   * @param array $keywords
-   *   Whitelist of keywords
-   * @param string $arg
-   * @return mixed|null
-   */
-  private function captureKeyword($keywords, &$arg) {
-    foreach (array_filter($keywords) as $key) {
-      if (strpos($arg, $key . ' ') === 0) {
-        $this->args[] = $key;
-        $arg = ltrim(substr($arg, strlen($key)));
-        return $key;
-      }
-    }
-    return NULL;
-  }
-
-  /**
-   * Shifts 0 or more expressions off the argument string and into the argument array
-   *
-   * @param string $arg
-   * @param int $limit
-   * @param array $mustBe
-   * @param array $cantBe
-   * @throws \API_Exception
-   */
-  private function captureExpressions(&$arg, $limit, $mustBe, $cantBe) {
-    $captured = 0;
-    $arg = ltrim($arg);
-    while ($arg) {
-      $item = $this->captureExpression($arg);
-      $arg = ltrim(substr($arg, strlen($item)));
-      $expr = SqlExpression::convert($item, FALSE, $mustBe, $cantBe);
-      $this->fields = array_merge($this->fields, $expr->getFields());
-      if ($captured) {
-        $this->args[] = ',';
-      }
-      $this->args[] = $expr;
-      $captured++;
-      // Keep going if we have a comma indicating another expression follows
-      if ($captured < $limit && substr($arg, 0, 1) === ',') {
-        $arg = ltrim(substr($arg, 1));
-      }
-      else {
-        return;
-      }
-    }
-  }
-
-  /**
-   * Scans the beginning of a string for an expression; stops when it hits delimiter
-   *
-   * @param $arg
-   * @return string
-   */
-  private function captureExpression($arg) {
-    $chars = str_split($arg);
-    $isEscaped = $quote = NULL;
-    $item = '';
-    $quotes = ['"', "'"];
-    $brackets = [
-      ')' => '(',
-    ];
-    $enclosures = array_fill_keys($brackets, 0);
-    foreach ($chars as $index => $char) {
-      if (!$isEscaped && in_array($char, $quotes, TRUE)) {
-        // Open quotes - we'll ignore everything inside
-        if (!$quote) {
-          $quote = $char;
-        }
-        // Close quotes
-        elseif ($char === $quote) {
-          $quote = NULL;
-        }
-      }
-      if (!$quote) {
-        // Delineates end of expression
-        if (($char == ',' || $char == ' ') && !array_filter($enclosures)) {
-          return $item;
-        }
-        // Open brackets - we'll ignore delineators inside
-        if (isset($enclosures[$char])) {
-          $enclosures[$char]++;
-        }
-        // Close brackets
-        if (isset($brackets[$char]) && $enclosures[$brackets[$char]]) {
-          $enclosures[$brackets[$char]]--;
-        }
-      }
-      $item .= $char;
-      // We are escaping the next char if this is a backslash not preceded by an odd number of backslashes
-      $isEscaped = $char === '\\' && ((strlen($item) - strlen(rtrim($item, '\\'))) % 2);
-    }
-    return $item;
-  }
-
-  public function render(array $fieldList): string {
-    $output = $this->getName() . '(';
-    foreach ($this->args as $index => $arg) {
-      if ($index && $arg !== ',') {
-        $output .= ' ';
-      }
-      if (is_object($arg)) {
-        $output .= $arg->render($fieldList);
-      }
-      else {
-        $output .= $arg;
-      }
-    }
-    return $output . ')';
-  }
-
-  /**
-   * @inheritDoc
-   */
-  public function getAlias(): string {
-    return $this->alias ?? $this->getName() . ':' . implode('_', $this->fields);
-  }
-
-  /**
-   * Get the name of this sql function.
-   * @return string
-   */
-  public function getName(): string {
-    $className = get_class($this);
-    $pos = strrpos($className, 'SqlFunction');
-    return substr($className, $pos + 11);
-  }
-
-  /**
-   * Get the param metadata for this sql function.
-   * @return array
-   */
-  public static function getParams(): array {
-    $params = [];
-    foreach (static::$params as $param) {
-      // Merge in defaults to ensure each param has these properties
-      $params[] = $param + [
-        'prefix' => [],
-        'expr' => 1,
-        'suffix' => [],
-        'optional' => FALSE,
-        'must_be' => [],
-        'cant_be' => ['SqlWild'],
-      ];
-    }
-    return $params;
-  }
-
-}
diff --git a/Civi/Api4/Query/SqlFunctionAVG.php b/Civi/Api4/Query/SqlFunctionAVG.php
deleted file mode 100644 (file)
index 9a06413..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-<?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 SqlFunctionAVG extends SqlFunction {
-
-  protected static $params = [
-    [
-      'prefix' => ['', 'DISTINCT', 'ALL'],
-      'expr' => 1,
-      'must_be' => ['SqlField'],
-    ],
-  ];
-
-}
diff --git a/Civi/Api4/Query/SqlFunctionCOUNT.php b/Civi/Api4/Query/SqlFunctionCOUNT.php
deleted file mode 100644 (file)
index d444675..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-<?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 SqlFunctionCOUNT extends SqlFunction {
-
-  protected static $params = [
-    [
-      'prefix' => ['', 'DISTINCT', 'ALL'],
-      'expr' => 1,
-      'must_be' => ['SqlField', 'SqlWild'],
-      'cant_be' => [],
-    ],
-  ];
-
-}
diff --git a/Civi/Api4/Query/SqlFunctionMAX.php b/Civi/Api4/Query/SqlFunctionMAX.php
deleted file mode 100644 (file)
index f80ebee..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-<?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 SqlFunctionMAX extends SqlFunction {
-
-  protected static $params = [
-    [
-      'prefix' => ['', 'DISTINCT', 'ALL'],
-      'expr' => 1,
-      'must_be' => ['SqlField'],
-    ],
-  ];
-
-}
diff --git a/Civi/Api4/Query/SqlFunctionMIN.php b/Civi/Api4/Query/SqlFunctionMIN.php
deleted file mode 100644 (file)
index 993a5b1..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-<?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 SqlFunctionMIN extends SqlFunction {
-
-  protected static $params = [
-    [
-      'prefix' => ['', 'DISTINCT', 'ALL'],
-      'expr' => 1,
-      'must_be' => ['SqlField'],
-    ],
-  ];
-
-}
diff --git a/Civi/Api4/Query/SqlFunctionSUM.php b/Civi/Api4/Query/SqlFunctionSUM.php
deleted file mode 100644 (file)
index 36f4ebb..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-<?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 SqlFunctionSUM extends SqlFunction {
-
-  protected static $params = [
-    [
-      'prefix' => ['', 'DISTINCT', 'ALL'],
-      'expr' => 1,
-      'must_be' => ['SqlField'],
-    ],
-  ];
-
-}
diff --git a/Civi/Api4/Query/SqlNull.php b/Civi/Api4/Query/SqlNull.php
deleted file mode 100644 (file)
index 046d04c..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-<?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;
-
-/**
- * NULL sql expression
- */
-class SqlNull extends SqlExpression {
-
-  protected function initialize() {
-  }
-
-  public function render(array $fieldList): string {
-    return 'NULL';
-  }
-
-}
diff --git a/Civi/Api4/Query/SqlNumber.php b/Civi/Api4/Query/SqlNumber.php
deleted file mode 100644 (file)
index e8ee255..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-<?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;
-
-/**
- * Numeric sql expression
- */
-class SqlNumber extends SqlExpression {
-
-  protected function initialize() {
-    \CRM_Utils_Type::validate($this->arg, 'Float');
-  }
-
-  public function render(array $fieldList): string {
-    return $this->arg;
-  }
-
-}
diff --git a/Civi/Api4/Query/SqlString.php b/Civi/Api4/Query/SqlString.php
deleted file mode 100644 (file)
index 12c85ab..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-<?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;
-
-/**
- * String sql expression
- */
-class SqlString extends SqlExpression {
-
-  protected function initialize() {
-    // Remove surrounding quotes
-    $str = substr($this->arg, 1, -1);
-    // Unescape the outer quote character inside the string to prevent double-escaping in render()
-    $quot = substr($this->arg, 0, 1);
-    $backslash = chr(0) . 'backslash' . chr(0);
-    $this->arg = str_replace(['\\\\', "\\$quot", $backslash], [$backslash, $quot, '\\\\'], $str);
-  }
-
-  public function render(array $fieldList): string {
-    return '"' . \CRM_Core_DAO::escapeString($this->arg) . '"';
-  }
-
-}
diff --git a/Civi/Api4/Query/SqlWild.php b/Civi/Api4/Query/SqlWild.php
deleted file mode 100644 (file)
index 7799042..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-<?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;
-
-/**
- * Wild * sql expression
- */
-class SqlWild extends SqlExpression {
-
-  protected function initialize() {
-  }
-
-  public function render(array $fieldList): string {
-    return '*';
-  }
-
-}
diff --git a/tests/phpunit/api/v4/Action/SqlExpressionTest.php b/tests/phpunit/api/v4/Action/SqlExpressionTest.php
deleted file mode 100644 (file)
index 3145c9d..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-<?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       |
- +--------------------------------------------------------------------+
- */
-
-/**
- *
- * @package CRM
- * @copyright CiviCRM LLC https://civicrm.org/licensing
- *
- */
-
-
-namespace api\v4\Action;
-
-use api\v4\UnitTestCase;
-use Civi\Api4\Contact;
-
-/**
- * @group headless
- */
-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')
-      ->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->assertArrayNotHasKey('b*d char', $result);
-  }
-
-  public function testSelectNumbers() {
-    Contact::create()->addValue('first_name', 'bob')->setCheckPermissions(FALSE)->execute();
-    $result = Contact::get()
-      ->addSelect('first_name', 123, 45.678, '-55 AS neg')
-      ->addWhere('first_name', '=', 'bob')
-      ->setLimit(1)
-      ->execute()
-      ->first();
-    $this->assertEquals('bob', $result['first_name']);
-    $this->assertEquals('123', $result['123']);
-    $this->assertEquals('-55', $result['neg']);
-    $this->assertEquals('45.678', $result['45_678']);
-  }
-
-  public function testSelectStrings() {
-    Contact::create()->addValue('first_name', 'bob')->setCheckPermissions(FALSE)->execute();
-    $result = Contact::get()
-      ->addSelect('first_name AS bob')
-      ->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('hello world', $result['hi']);
-    $this->assertEquals('can\'t "quote"', $result['quot']);
-  }
-
-}
diff --git a/tests/phpunit/api/v4/Action/SqlFunctionTest.php b/tests/phpunit/api/v4/Action/SqlFunctionTest.php
deleted file mode 100644 (file)
index 9b33c59..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-<?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       |
- +--------------------------------------------------------------------+
- */
-
-/**
- *
- * @package CRM
- * @copyright CiviCRM LLC https://civicrm.org/licensing
- *
- */
-
-
-namespace api\v4\Action;
-
-use api\v4\UnitTestCase;
-use Civi\Api4\Contact;
-use Civi\Api4\Contribution;
-
-/**
- * @group headless
- */
-class SqlFunctionTest extends UnitTestCase {
-
-  public function testGroupAggregates() {
-    $cid = Contact::create()->setCheckPermissions(FALSE)->addValue('first_name', 'bill')->execute()->first()['id'];
-    Contribution::save()
-      ->setCheckPermissions(FALSE)
-      ->setDefaults(['contact_id' => $cid, 'financial_type_id' => 1])
-      ->setRecords([
-        ['total_amount' => 100, 'receive_date' => '2020-01-01'],
-        ['total_amount' => 200, 'receive_date' => '2020-01-01'],
-        ['total_amount' => 300, 'receive_date' => '2020-01-01'],
-        ['total_amount' => 400, 'receive_date' => '2020-01-01'],
-      ])
-      ->execute();
-    $agg = Contribution::get()
-      ->setCheckPermissions(FALSE)
-      ->addGroupBy('contact_id')
-      ->addWhere('contact_id', '=', $cid)
-      ->addSelect('AVG(total_amount) AS average')
-      ->addSelect('SUM(total_amount)')
-      ->addSelect('MAX(total_amount)')
-      ->addSelect('MIN(total_amount)')
-      ->addSelect('COUNT(*) AS count')
-      ->execute()
-      ->first();
-    $this->assertEquals(250, $agg['average']);
-    $this->assertEquals(1000, $agg['SUM:total_amount']);
-    $this->assertEquals(400, $agg['MAX:total_amount']);
-    $this->assertEquals(100, $agg['MIN:total_amount']);
-    $this->assertEquals(4, $agg['count']);
-  }
-
-}
diff --git a/tests/phpunit/api/v4/Query/SqlExpressionParserTest.php b/tests/phpunit/api/v4/Query/SqlExpressionParserTest.php
deleted file mode 100644 (file)
index cf43dbd..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-<?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       |
- +--------------------------------------------------------------------+
- */
-
-/**
- *
- * @package CRM
- * @copyright CiviCRM LLC https://civicrm.org/licensing
- * $Id$
- *
- */
-
-
-namespace api\v4\Query;
-
-use api\v4\UnitTestCase;
-use Civi\Api4\Query\SqlExpression;
-
-/**
- * @group headless
- */
-class SqlExpressionParserTest extends UnitTestCase {
-
-  public function aggregateFunctions() {
-    return [
-      ['AVG'],
-      ['COUNT'],
-      ['MAX'],
-      ['MIN'],
-      ['SUM'],
-    ];
-  }
-
-  /**
-   * @param string|\Civi\Api4\Query\SqlFunction $fnName
-   * @dataProvider aggregateFunctions
-   */
-  public function testAggregateFuncitons($fnName) {
-    $className = 'Civi\Api4\Query\SqlFunction' . $fnName;
-    $params = $className::getParams();
-    $this->assertNotEmpty($params[0]['prefix']);
-    $this->assertEmpty($params[0]['suffix']);
-
-    $sqlFn = new $className('total');
-    $this->assertEquals($fnName, $sqlFn->getName());
-    $this->assertEquals(['total'], $sqlFn->getFields());
-    $this->assertCount(1, $this->getArgs($sqlFn));
-
-    $sqlFn = SqlExpression::convert($fnName . '(DISTINCT stuff)');
-    $this->assertEquals($fnName, $sqlFn->getName());
-    $this->assertEquals("Civi\Api4\Query\SqlFunction$fnName", get_class($sqlFn));
-    $this->assertEquals($params, $sqlFn->getParams());
-    $this->assertEquals(['stuff'], $sqlFn->getFields());
-    $this->assertCount(2, $this->getArgs($sqlFn));
-
-    try {
-      $sqlFn = SqlExpression::convert($fnName . '(*)');
-      if ($fnName === 'COUNT') {
-        $this->assertTrue(is_a($this->getArgs($sqlFn)[0], 'Civi\Api4\Query\SqlWild'));
-      }
-      else {
-        $this->fail('SqlWild should only be allowed in COUNT.');
-      }
-    }
-    catch (\API_Exception $e) {
-      $this->assertContains('Illegal', $e->getMessage());
-    }
-  }
-
-  /**
-   * @param \Civi\Api4\Query\SqlFunction $fn
-   * @return array
-   * @throws \ReflectionException
-   */
-  private function getArgs($fn) {
-    $ref = new \ReflectionClass($fn);
-    $args = $ref->getProperty('args');
-    $args->setAccessible(TRUE);
-    return $args->getValue($fn);
-  }
-
-}
index ebe220b5849254a94e245b966d848c35e8987863..977e737a6061eb4431158c1fcf76a1f121a6803a 100644 (file)
@@ -25,8 +25,6 @@ use api\v4\Traits\TestDataLoaderTrait;
 use Civi\Test\HeadlessInterface;
 use Civi\Test\TransactionalInterface;
 
-require_once 'api/Exception.php';
-
 /**
  * @group headless
  */