Feature: #104493 - Add castText() expression support to ExpressionBuilder
See forge#104493
Description
The TYPO3
\TYPO3\
provides a new method to cast expression results to text like datatypes. This
is done to large
VARCHAR/ types using the
CAST/ or similar
methods based on the used database engine.
Note
This should not be mixed with
TEXT,
CHAR or
VARCHAR
data types for column (fields) definition used to describe the structure
of a table.
ExpressionBuilder::castText()
Creates a
CAST expression.
/**
* Creates a cast for the `$expression` result to a text datatype depending on the database management system.
*
* Note that for MySQL/MariaDB the corresponding CHAR/VARCHAR types are used with a length of `16383` reflecting
* 65554 bytes with `utf8mb4` and working with default `max_packet_size=16KB`. For SQLite and PostgreSQL the text
* type conversion is used.
*
* Main purpose of this expression is to use it in a expression chain to convert non-text values to text in chain
* with other expressions, for example to {@see self::concat()} multiple values or to ensure the type, within
* `UNION/UNION ALL` query parts for example in recursive `Common Table Expressions` parts.
*
* This is a replacement for {@see QueryBuilder::castFieldToTextType()} with minor adjustments like enforcing and
* limiting the size to a fixed variant to be more usable in sensible areas like `Common Table Expressions`.
*
* Alternatively the {@see self::castVarchar()} can be used which allows for dynamic length setting per expression
* call.
*
* **Example:**
* ```
* $queryBuilder->expr()->castText(
* '(' . '1 * 10' . ')',
* 'virtual_field'
* );
* ```
*
* **Result with MySQL:**
* ```
* CAST((1 * 10) AS CHAR(16383) AS `virtual_field`
* ```
*
* @throws \RuntimeException when used with a unsupported platform.
*/
public function castText(CompositeExpression|\Stringable|string $expression, string $asIdentifier = ''): string
{
$platform = $this->connection->getDatabasePlatform();
if ($platform instanceof DoctrinePostgreSQLPlatform) {
return $this->as(sprintf('((%s)::%s)', $expression, 'text'), $asIdentifier);
}
if ($platform instanceof DoctrineSQLitePlatform) {
return $this->as(sprintf('(CAST((%s) AS %s))', $expression, 'TEXT'), $asIdentifier);
}
if ($platform instanceof DoctrineMariaDBPlatform) {
// 16383 is the maximum for a VARCHAR field with `utf8mb4`
return $this->as(sprintf('(CAST((%s) AS %s(%s)))', $expression, 'VARCHAR', '16383'), $asIdentifier);
}
if ($platform instanceof DoctrineMySQLPlatform) {
// 16383 is the maximum for a VARCHAR field with `utf8mb4`
return $this->as(sprintf('(CAST((%s) AS %s(%s)))', $expression, 'CHAR', '16383'), $asIdentifier);
}
throw new \RuntimeException(
sprintf(
'%s is not implemented for the used database platform "%s", yet!',
__METHOD__,
get_class($this->connection->getDatabasePlatform())
),
1722105672
);
}
Impact
Extension authors can use the new expression method to build more advanced queries without the requirement to deal with the correct implementation for all supported database vendors - at least to some grade.