diff --git a/README.md b/README.md
index 5dc8b5e..ff32c46 100644
--- a/README.md
+++ b/README.md
@@ -78,9 +78,12 @@ $newTable = $toSchema->createTable('new_table');
// add columns
$newTable->addColumn('id', 'integer', ['unsigned' => true]);
-$newTable->addColumn('payload', 'string');
+$newTable->addColumn('payload', 'string', ['notnull' => false]);
+// *option 'notnull' in false mode allows you to insert NULL into the column;
+// in this case, the column will be represented in the ClickHouse as Nullable(String)
$newTable->addColumn('hash', 'string', ['length' => 32, 'fixed' => true]);
-// *option 'fixed' sets the fixed length of a string column as specified; if specified, the type of the column is FixedString
+// *option 'fixed' sets the fixed length of a string column as specified;
+// if specified, the type of the column is FixedString
//set primary key
$newTable->setPrimaryKey(['id']);
diff --git a/src/ClickHouseConnection.php b/src/ClickHouseConnection.php
index dbd54cc..15cd109 100644
--- a/src/ClickHouseConnection.php
+++ b/src/ClickHouseConnection.php
@@ -21,6 +21,8 @@
use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
use Doctrine\DBAL\ParameterType;
use Doctrine\DBAL\Platforms\AbstractPlatform;
+use function array_merge;
+use function func_get_args;
/**
* ClickHouse implementation for the Connection interface.
@@ -36,7 +38,7 @@ class ClickHouseConnection implements Connection, PingableConnection, ServerInfo
/**
* Connection constructor
*
- * @param array $params Array with connection params.
+ * @param mixed[] $params
*/
public function __construct(
array $params,
@@ -70,7 +72,7 @@ public function prepare($prepareString)
*/
public function query()
{
- $args = \func_get_args();
+ $args = func_get_args();
$stmt = $this->prepare($args[0]);
$stmt->execute();
@@ -164,7 +166,7 @@ public function getServerVersion()
try {
return $this->smi2CHClient->getServerVersion();
} catch (TransportException $exception) {
- return null;
+ return '';
}
}
@@ -173,6 +175,6 @@ public function getServerVersion()
*/
public function requiresQueryForServerVersion()
{
- return true;
+ return true;
}
}
diff --git a/src/ClickHousePlatform.php b/src/ClickHousePlatform.php
index e7a0d81..ea3c98b 100644
--- a/src/ClickHousePlatform.php
+++ b/src/ClickHousePlatform.php
@@ -14,19 +14,6 @@
namespace FOD\DBALClickHouse;
-use Doctrine\DBAL\Types\{
- BlobType,
- DecimalType,
- FloatType,
- StringType,
- TextType,
- Type,
- DateType,
- IntegerType,
- SmallIntType,
- BigIntType,
- DateTimeType
-};
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\ParameterType;
use Doctrine\DBAL\Platforms\AbstractPlatform;
@@ -34,6 +21,37 @@
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
use Doctrine\DBAL\Schema\Index;
use Doctrine\DBAL\Schema\TableDiff;
+use Doctrine\DBAL\Types\BigIntType;
+use Doctrine\DBAL\Types\BlobType;
+use Doctrine\DBAL\Types\DateTimeType;
+use Doctrine\DBAL\Types\DateType;
+use Doctrine\DBAL\Types\DecimalType;
+use Doctrine\DBAL\Types\FloatType;
+use Doctrine\DBAL\Types\IntegerType;
+use Doctrine\DBAL\Types\SmallIntType;
+use Doctrine\DBAL\Types\StringType;
+use Doctrine\DBAL\Types\TextType;
+use Doctrine\DBAL\Types\Type;
+use FOD\DBALClickHouse\Types\BitNumericalClickHouseType;
+use FOD\DBALClickHouse\Types\DatableClickHouseType;
+use FOD\DBALClickHouse\Types\NumericalClickHouseType;
+use FOD\DBALClickHouse\Types\StringClickHouseType;
+use FOD\DBALClickHouse\Types\UnsignedNumericalClickHouseType;
+use function addslashes;
+use function array_filter;
+use function array_key_exists;
+use function array_keys;
+use function array_merge;
+use function array_unique;
+use function array_values;
+use function count;
+use function func_get_args;
+use function get_class;
+use function implode;
+use function in_array;
+use function sprintf;
+use function stripos;
+use function trim;
/**
* Provides the behavior, features and SQL dialect of the ClickHouse database platform.
@@ -50,7 +68,11 @@ class ClickHousePlatform extends AbstractPlatform
*/
public function getBooleanTypeDeclarationSQL(array $columnDef) : string
{
- return 'UInt8';
+ return $this->prepareDeclarationSQL(
+ UnsignedNumericalClickHouseType::UNSIGNED_CHAR .
+ NumericalClickHouseType::TYPE_INT . BitNumericalClickHouseType::EIGHT_BIT,
+ $columnDef
+ );
}
/**
@@ -58,7 +80,11 @@ public function getBooleanTypeDeclarationSQL(array $columnDef) : string
*/
public function getIntegerTypeDeclarationSQL(array $columnDef) : string
{
- return $this->_getCommonIntegerTypeDeclarationSQL($columnDef) . 'Int32';
+ return $this->prepareDeclarationSQL(
+ $this->_getCommonIntegerTypeDeclarationSQL($columnDef) .
+ NumericalClickHouseType::TYPE_INT . BitNumericalClickHouseType::THIRTY_TWO_BIT,
+ $columnDef
+ );
}
/**
@@ -66,7 +92,7 @@ public function getIntegerTypeDeclarationSQL(array $columnDef) : string
*/
public function getBigIntTypeDeclarationSQL(array $columnDef) : string
{
- return 'String';
+ return $this->prepareDeclarationSQL(StringClickHouseType::TYPE_STRING, $columnDef);
}
/**
@@ -74,7 +100,11 @@ public function getBigIntTypeDeclarationSQL(array $columnDef) : string
*/
public function getSmallIntTypeDeclarationSQL(array $columnDef) : string
{
- return $this->_getCommonIntegerTypeDeclarationSQL($columnDef) . 'Int16';
+ return $this->prepareDeclarationSQL(
+ $this->_getCommonIntegerTypeDeclarationSQL($columnDef) .
+ NumericalClickHouseType::TYPE_INT . BitNumericalClickHouseType::SIXTEEN_BIT,
+ $columnDef
+ );
}
/**
@@ -86,7 +116,7 @@ protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef) : strin
throw new \Exception('Clickhouse do not support AUTO_INCREMENT fields');
}
- return empty($columnDef['unsigned']) ? '' : 'U';
+ return empty($columnDef['unsigned']) ? '' : UnsignedNumericalClickHouseType::UNSIGNED_CHAR;
}
/**
@@ -128,6 +158,37 @@ protected function initializeDoctrineTypeMappings() : void
'enum8' => 'string',
'enum16' => 'string',
+
+ 'nullable(int8)' => 'smallint',
+ 'nullable(int16)' => 'integer',
+ 'nullable(int32)' => 'integer',
+ 'nullable(int64)' => 'bigint',
+ 'nullable(uint8)' => 'smallint',
+ 'nullable(uint16)' => 'integer',
+ 'nullable(uint32)' => 'integer',
+ 'nullable(uint64)' => 'bigint',
+ 'nullable(float32)' => 'decimal',
+ 'nullable(float64)' => 'float',
+
+ 'nullable(string)' => 'string',
+ 'nullable(fixedstring)' => 'string',
+ 'nullable(date)' => 'date',
+ 'nullable(datetime)' => 'datetime',
+
+ 'array(nullable(int8))' => 'array',
+ 'array(nullable(int16))' => 'array',
+ 'array(nullable(int32))' => 'array',
+ 'array(nullable(int64))' => 'array',
+ 'array(nullable(uint8))' => 'array',
+ 'array(nullable(uint16))' => 'array',
+ 'array(nullable(uint32))' => 'array',
+ 'array(nullable(uint64))' => 'array',
+ 'array(nullable(float32))' => 'array',
+ 'array(nullable(float64))' => 'array',
+
+ 'array(nullable(string))' => 'array',
+ 'array(nullable(date))' => 'array',
+ 'array(nullable(datetime))' => 'array',
];
}
@@ -137,8 +198,33 @@ protected function initializeDoctrineTypeMappings() : void
protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) : string
{
return $fixed
- ? 'FixedString(' . $length . ')'
- : 'String';
+ ? (StringClickHouseType::TYPE_FIXED_STRING . '(' . $length . ')')
+ : StringClickHouseType::TYPE_STRING;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getVarcharTypeDeclarationSQL(array $field)
+ {
+ if (! isset($field['length'])) {
+ $field['length'] = $this->getVarcharDefaultLength();
+ }
+
+ $fixed = $field['fixed'] ?? false;
+
+ $maxLength = $fixed
+ ? $this->getCharMaxLength()
+ : $this->getVarcharMaxLength();
+
+ if ($field['length'] > $maxLength) {
+ return $this->getClobTypeDeclarationSQL($field);
+ }
+
+ return $this->prepareDeclarationSQL(
+ $this->getVarcharTypeDeclarationSQLSnippet($field['length'], $fixed),
+ $field
+ );
}
/**
@@ -146,7 +232,7 @@ protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) : string
*/
protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) : string
{
- return 'String';
+ return StringClickHouseType::TYPE_STRING;
}
/**
@@ -154,7 +240,7 @@ protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) : string
*/
public function getClobTypeDeclarationSQL(array $field) : string
{
- return 'String';
+ return $this->prepareDeclarationSQL(StringClickHouseType::TYPE_STRING, $field);
}
/**
@@ -162,7 +248,7 @@ public function getClobTypeDeclarationSQL(array $field) : string
*/
public function getBlobTypeDeclarationSQL(array $field) : string
{
- return 'String';
+ return $this->prepareDeclarationSQL(StringClickHouseType::TYPE_STRING, $field);
}
/**
@@ -323,7 +409,7 @@ public function getSubstringExpression($value, $from, $length = null) : string
*/
public function getConcatExpression() : string
{
- return 'concat(' . implode(', ', \func_get_args()) . ')';
+ return 'concat(' . implode(', ', func_get_args()) . ')';
}
/**
@@ -331,7 +417,7 @@ public function getConcatExpression() : string
*/
public function getIsNullExpression($expression)
{
- throw DBALException::notSupported(__METHOD__);
+ return 'isNull(' . $expression . ')';
}
/**
@@ -339,7 +425,7 @@ public function getIsNullExpression($expression)
*/
public function getIsNotNullExpression($expression)
{
- throw DBALException::notSupported(__METHOD__);
+ return 'isNotNull(' . $expression . ')';
}
/**
@@ -561,7 +647,7 @@ protected function _getCreateTableSQL($tableName, array $columns, array $options
/**
* MergeTree* specific section
*/
- if (\in_array(
+ if (in_array(
$engine,
[
'MergeTree',
@@ -639,7 +725,7 @@ protected function _getCreateTableSQL($tableName, array $columns, array $options
throw new \Exception(
'In table `' . $tableName . '` you have set field `' .
$options['eventDateColumn'] .
- '` (' . \get_class($columns[$options['eventDateColumn']]['type']) . ')
+ '` (' . get_class($columns[$options['eventDateColumn']]['type']) . ')
as `eventDateColumn`, but it is not instance of DateType'
);
}
@@ -650,7 +736,8 @@ protected function _getCreateTableSQL($tableName, array $columns, array $options
$eventDateColumnName = $options['eventDateColumn'];
}
$dateColumnParams['name'] = $eventDateColumnName;
- $columns = [$eventDateColumnName => $dateColumnParams] + $columns; // insert into very beginning
+ // insert into very beginning
+ $columns = [$eventDateColumnName => $dateColumnParams] + $columns;
/**
* Primary key section
@@ -682,8 +769,9 @@ protected function _getCreateTableSQL($tableName, array $columns, array $options
! $columns[$options['versionColumn']]['type'] instanceof DateTimeType
) {
throw new \Exception(
- 'For ReplacingMergeTree tables `versionColumn` must be any of UInt* family, or Date, or DateTime types. ' .
- \get_class($columns[$options['versionColumn']]['type']) . ' given.'
+ 'For ReplacingMergeTree tables `versionColumn` must be any of UInt* family,
+ or Date, or DateTime types. ' .
+ get_class($columns[$options['versionColumn']]['type']) . ' given.'
);
}
@@ -693,10 +781,13 @@ protected function _getCreateTableSQL($tableName, array $columns, array $options
$engineOptions .= ')';
}
- $columnListSql = $this->getColumnDeclarationListSQL($columns);
- $query = 'CREATE TABLE ' . $tableName . ' (' . $columnListSql . ') ENGINE = ' . $engine . $engineOptions;
-
- $sql[] = $query;
+ $sql[] = sprintf(
+ 'CREATE TABLE %s (%s) ENGINE = %s%s',
+ $tableName,
+ $this->getColumnDeclarationListSQL($columns),
+ $engine,
+ $engineOptions
+ );
return $sql;
}
@@ -804,6 +895,18 @@ protected function _getAlterTableIndexForeignKeySQL(TableDiff $diff)
throw DBALException::notSupported(__METHOD__);
}
+ /**
+ * @param mixed[] $columnDef
+ */
+ protected function prepareDeclarationSQL(string $declarationSQL, array $columnDef) : string
+ {
+ if (array_key_exists('notnull', $columnDef) && $columnDef['notnull'] === false) {
+ return 'Nullable(' . $declarationSQL . ')';
+ }
+
+ return $declarationSQL;
+ }
+
/**
* {@inheritDoc}
*/
@@ -825,7 +928,7 @@ public function getColumnDeclarationSQL($name, array $field) : string
*/
public function getDecimalTypeDeclarationSQL(array $columnDef) : string
{
- return 'String';
+ return $this->prepareDeclarationSQL(StringClickHouseType::TYPE_STRING, $columnDef);
}
/**
@@ -930,7 +1033,10 @@ public function getListDatabasesSQL() : string
*/
public function getListTableColumnsSQL($table, $database = null) : string
{
- return 'DESCRIBE TABLE ' . ($database ? $this->quoteSingleIdentifier($database) . '.' : '') . $this->quoteSingleIdentifier($table);
+ return sprintf(
+ 'DESCRIBE TABLE %s',
+ ($database ? $this->quoteSingleIdentifier($database) . '.' : '') . $this->quoteSingleIdentifier($table)
+ );
}
/**
@@ -978,7 +1084,7 @@ public function getCreateDatabaseSQL($database) : string
*/
public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) : string
{
- return 'DateTime';
+ return $this->prepareDeclarationSQL(DatableClickHouseType::TYPE_DATE_TIME, $fieldDeclaration);
}
/**
@@ -986,12 +1092,15 @@ public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) : string
*/
public function getDateTimeTzTypeDeclarationSQL(array $fieldDeclaration) : string
{
- return 'DateTime';
+ return $this->prepareDeclarationSQL(DatableClickHouseType::TYPE_DATE_TIME, $fieldDeclaration);
}
+ /**
+ * {@inheritDoc}
+ */
public function getTimeTypeDeclarationSQL(array $fieldDeclaration) : string
{
- return 'String';
+ return $this->prepareDeclarationSQL(StringClickHouseType::TYPE_STRING, $fieldDeclaration);
}
/**
@@ -999,7 +1108,7 @@ public function getTimeTypeDeclarationSQL(array $fieldDeclaration) : string
*/
public function getDateTypeDeclarationSQL(array $fieldDeclaration) : string
{
- return 'Date';
+ return $this->prepareDeclarationSQL(DatableClickHouseType::TYPE_DATE, $fieldDeclaration);
}
/**
@@ -1007,7 +1116,10 @@ public function getDateTypeDeclarationSQL(array $fieldDeclaration) : string
*/
public function getFloatDeclarationSQL(array $fieldDeclaration) : string
{
- return 'Float64';
+ return $this->prepareDeclarationSQL(
+ NumericalClickHouseType::TYPE_FLOAT . BitNumericalClickHouseType::SIXTY_FOUR_BIT,
+ $fieldDeclaration
+ );
}
/**
@@ -1141,23 +1253,30 @@ public function getDefaultValueDeclarationSQL($field) : string
if (! isset($field['default'])) {
return '';
}
-
- $default = " DEFAULT '" . $field['default'] . "'";
- if ($fieldType = (string) ($field['type'] ?? null)) {
- if (\in_array($fieldType, [
+ $defaultValue = $this->quoteStringLiteral($field['default']);
+ $fieldType = $field['type'] ?: null;
+ if ($fieldType !== null) {
+ if ($fieldType === DatableClickHouseType::TYPE_DATE ||
+ $fieldType instanceof DateType ||
+ in_array($fieldType, [
'Integer',
'SmallInt',
'Float',
- ]) || ($fieldType === 'BigInt' && Type::getType('BigInt')->getBindingType() === ParameterType::INTEGER)) {
- $default = ' DEFAULT ' . $field['default'];
- } elseif ($fieldType === 'DateTime' && $field['default'] === $this->getCurrentTimestampSQL()) {
- $default = ' DEFAULT ' . $this->getCurrentTimestampSQL();
- } elseif ($fieldType === 'Date') { // TODO check if string matches constant date like 'dddd-yy-mm' and quote it
- $default = ' DEFAULT ' . $field['default'];
+ ]) ||
+ (
+ $fieldType === 'BigInt'
+ && Type::getType('BigInt')->getBindingType() === ParameterType::INTEGER
+ )
+ ) {
+ $defaultValue = $field['default'];
+ } elseif ($fieldType === DatableClickHouseType::TYPE_DATE_TIME &&
+ $field['default'] === $this->getCurrentTimestampSQL()
+ ) {
+ $defaultValue = $this->getCurrentTimestampSQL();
}
}
- return $default;
+ return sprintf(' DEFAULT %s', $defaultValue);
}
/**
diff --git a/src/ClickHouseSchemaManager.php b/src/ClickHouseSchemaManager.php
index f3ba5d0..69cc2b4 100644
--- a/src/ClickHouseSchemaManager.php
+++ b/src/ClickHouseSchemaManager.php
@@ -18,6 +18,12 @@
use Doctrine\DBAL\Schema\Column;
use Doctrine\DBAL\Schema\View;
use Doctrine\DBAL\Types\Type;
+use const CASE_LOWER;
+use function array_change_key_case;
+use function preg_replace;
+use function stripos;
+use function strtolower;
+use function trim;
/**
* Schema manager for the ClickHouse DBMS.
diff --git a/src/ClickHouseStatement.php b/src/ClickHouseStatement.php
index 516ed60..d9532be 100644
--- a/src/ClickHouseStatement.php
+++ b/src/ClickHouseStatement.php
@@ -19,6 +19,25 @@
use Doctrine\DBAL\FetchMode;
use Doctrine\DBAL\ParameterType;
use Doctrine\DBAL\Platforms\AbstractPlatform;
+use function array_key_exists;
+use function array_keys;
+use function array_map;
+use function array_replace;
+use function array_shift;
+use function array_values;
+use function array_walk;
+use function count;
+use function current;
+use function explode;
+use function implode;
+use function in_array;
+use function is_array;
+use function is_bool;
+use function is_float;
+use function is_int;
+use function preg_replace;
+use function stripos;
+use function trim;
/**
* ClickHouse Statement
@@ -34,18 +53,18 @@ class ClickHouseStatement implements \IteratorAggregate, Statement
/** @var AbstractPlatform */
protected $platform;
- /** @var array|null */
- protected $rows;
+ /** @var mixed[] */
+ protected $rows = [];
/**
* Query parameters for prepared statement (key => value)
- * @var array
+ * @var mixed[]
*/
protected $values = [];
/**
* Query parameters' types for prepared statement (key => value)
- * @var array
+ * @var mixed[]
*/
protected $types = [];
@@ -68,7 +87,7 @@ public function __construct(Client $client, string $statement, AbstractPlatform
public function getIterator() : \ArrayIterator
{
if (! $this->iterator) {
- $this->iterator = new \ArrayIterator($this->rows ?: []);
+ $this->iterator = new \ArrayIterator($this->rows);
}
return $this->iterator;
@@ -79,7 +98,7 @@ public function getIterator() : \ArrayIterator
*/
public function closeCursor()
{
- $this->rows = null;
+ $this->rows = [];
$this->iterator = null;
return true;
@@ -108,7 +127,7 @@ public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null)
protected function assumeFetchMode(?int $fetchMode = null) : int
{
$mode = $fetchMode ?: $this->fetchMode;
- if (! \in_array($mode, [
+ if (! in_array($mode, [
FetchMode::ASSOCIATIVE,
FetchMode::NUMERIC,
FetchMode::STANDARD_OBJECT,
@@ -147,7 +166,9 @@ public function fetch($fetchMode = null, $cursorOrientation = \PDO::FETCH_ORI_NE
if ($this->assumeFetchMode($fetchMode) === \PDO::FETCH_KEY_PAIR) {
if (count($data) < 2) {
- throw new \Exception('To fetch in \PDO::FETCH_KEY_PAIR mode, result set must contain at least 2 columns');
+ throw new \Exception(
+ 'To fetch in \PDO::FETCH_KEY_PAIR mode, result set must contain at least 2 columns'
+ );
}
return [array_shift($data) => array_shift($data)];
@@ -190,7 +211,9 @@ function ($row) {
return array_map(
function ($row) {
if (count($row) < 2) {
- throw new \Exception('To fetch in \PDO::FETCH_KEY_PAIR mode, result set must contain at least 2 columns');
+ throw new \Exception(
+ 'To fetch in \PDO::FETCH_KEY_PAIR mode, result set must contain at least 2 columns'
+ );
}
return [array_shift($row) => array_shift($row)];
@@ -207,7 +230,8 @@ function ($row) {
*/
public function fetchColumn($columnIndex = 0)
{
- if ($elem = $this->fetch(FetchMode::NUMERIC)) {
+ $elem = $this->fetch(FetchMode::NUMERIC);
+ if (is_array($elem)) {
return $elem[$columnIndex] ?? $elem[0];
}
@@ -248,7 +272,7 @@ public function errorInfo() : void
public function execute($params = null) : bool
{
$hasZeroIndex = false;
- if (\is_array($params)) {
+ if (is_array($params)) {
$this->values = array_replace($this->values, $params);//TODO array keys must be all strings or all integers?
$hasZeroIndex = array_key_exists(0, $params);
}
@@ -268,7 +292,7 @@ public function execute($params = null) : bool
} else {
foreach (array_keys($this->values) as $key) {
$sql = preg_replace(
- '/(' . (\is_int($key) ? '\?' : ':' . $key) . ')/i',
+ '/(' . (is_int($key) ? '\?' : ':' . $key) . ')/i',
$this->getTypedParam($key),
$sql,
1
@@ -317,40 +341,44 @@ protected function processViaSMI2(string $sql) : void
*/
protected function getTypedParam($key) : string
{
+ if ($this->values[$key] === null) {
+ return 'NULL';
+ }
+
$type = $this->types[$key] ?? null;
// if param type was not setted - trying to get db-type by php-var-type
if ($type === null) {
- if (\is_bool($this->values[$key])) {
+ if (is_bool($this->values[$key])) {
$type = ParameterType::BOOLEAN;
- } elseif (\is_int($this->values[$key]) || \is_float($this->values[$key])) {
+ } elseif (is_int($this->values[$key]) || is_float($this->values[$key])) {
$type = ParameterType::INTEGER;
- } elseif (\is_array($this->values[$key])) {
+ } elseif (is_array($this->values[$key])) {
/*
* ClickHouse Arrays
*/
$values = $this->values[$key];
- if (\is_int(current($values)) || \is_float(current($values))) {
+ if (is_int(current($values)) || is_float(current($values))) {
array_map(
function ($value) : void {
- if (! \is_int($value) && ! \is_float($value)) {
- throw new ClickHouseException('Array values must all be int/float or string, mixes not allowed');
+ if (! is_int($value) && ! is_float($value)) {
+ throw new ClickHouseException(
+ 'Array values must all be int/float or string, mixes not allowed'
+ );
}
},
$values
);
} else {
- $values = array_map([$this->platform, 'quoteStringLiteral'], $values);
+ $values = array_map(function ($value) {
+ return $value === null ? 'NULL' : $this->platform->quoteStringLiteral($value);
+ }, $values);
}
return '[' . implode(', ', $values) . ']';
}
}
- if ($type === ParameterType::NULL) {
- throw new ClickHouseException('NULLs are not supported by ClickHouse');
- }
-
if ($type === ParameterType::INTEGER) {
return (string) $this->values[$key];
}
diff --git a/src/Connection.php b/src/Connection.php
index 35bfdaa..80f8e72 100644
--- a/src/Connection.php
+++ b/src/Connection.php
@@ -15,6 +15,9 @@
namespace FOD\DBALClickHouse;
use Doctrine\DBAL\DBALException;
+use function strtoupper;
+use function substr;
+use function trim;
/**
* ClickHouse Connection
diff --git a/src/Types/AbstractArrayFloatType.php b/src/Types/AbstractArrayFloatType.php
deleted file mode 100644
index 88f2970..0000000
--- a/src/Types/AbstractArrayFloatType.php
+++ /dev/null
@@ -1,39 +0,0 @@
-)
- *
- * (c) FriendsOfDoctrine .
- *
- * For the full copyright and license inflormation, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace FOD\DBALClickHouse\Types;
-
-use Doctrine\DBAL\Platforms\AbstractPlatform;
-
-/**
- * Array(Float*) Types basic class
- */
-abstract class AbstractArrayFloatType extends AbstractArrayNumType
-{
- /**
- * {@inheritDoc}
- */
- public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) : string
- {
- return 'Array(Float' . $this->getBitness() . ')';
- }
-
- /**
- * {@inheritDoc}
- */
- public function getName() : string
- {
- return 'array(float' . $this->getBitness() . ')';
- }
-}
diff --git a/src/Types/AbstractArrayIntType.php b/src/Types/AbstractArrayIntType.php
deleted file mode 100644
index 06d3b55..0000000
--- a/src/Types/AbstractArrayIntType.php
+++ /dev/null
@@ -1,41 +0,0 @@
-)
- *
- * (c) FriendsOfDoctrine .
- *
- * For the full copyright and license inflormation, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace FOD\DBALClickHouse\Types;
-
-use Doctrine\DBAL\Platforms\AbstractPlatform;
-
-/**
- * Array(Int*) Types basic class
- */
-abstract class AbstractArrayIntType extends AbstractArrayNumType
-{
- public const UNSIGNED = false;
-
- /**
- * {@inheritDoc}
- */
- public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) : string
- {
- return 'Array(' . (static::UNSIGNED ? 'U' : '') . 'Int' . $this->getBitness() . ')';
- }
-
- /**
- * {@inheritDoc}
- */
- public function getName() : string
- {
- return 'array(' . (static::UNSIGNED ? 'u' : '') . 'int' . $this->getBitness() . ')';
- }
-}
diff --git a/src/Types/ArrayDateTimeType.php b/src/Types/ArrayDateTimeType.php
index d7daffa..76cf715 100644
--- a/src/Types/ArrayDateTimeType.php
+++ b/src/Types/ArrayDateTimeType.php
@@ -16,26 +16,18 @@
use Doctrine\DBAL\ParameterType;
use Doctrine\DBAL\Platforms\AbstractPlatform;
+use function array_filter;
+use function array_map;
+use function implode;
/**
* Array(DateTime) Type class
*/
-class ArrayDateTimeType extends AbstractArrayType
+class ArrayDateTimeType extends ArrayType implements DatableClickHouseType
{
- /**
- * {@inheritDoc}
- */
- public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) : string
- {
- return 'Array(DateTime)';
- }
-
- /**
- * {@inheritDoc}
- */
- public function getName() : string
+ public function getBaseClickHouseType() : string
{
- return 'array(datetime)';
+ return DatableClickHouseType::TYPE_DATE_TIME;
}
/**
@@ -43,9 +35,12 @@ public function getName() : string
*/
public function convertToPHPValue($value, AbstractPlatform $platform)
{
- return array_map(function ($stringDatetime) use ($platform) {
- return \DateTime::createFromFormat($platform->getDateTimeFormatString(), $stringDatetime);
- }, (array) $value);
+ return array_map(
+ function ($stringDatetime) use ($platform) {
+ return \DateTime::createFromFormat($platform->getDateTimeFormatString(), $stringDatetime);
+ },
+ (array) $value
+ );
}
/**
@@ -55,11 +50,17 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform)
{
return '[' . implode(
', ',
- array_map(function (\DateTime $datetime) use ($platform) {
+ array_map(
+ function (\DateTime $datetime) use ($platform) {
return "'" . $datetime->format($platform->getDateTimeFormatString()) . "'";
- }, array_filter((array) $value, function ($datetime) {
- return $datetime instanceof \DateTime;
- }))
+ },
+ array_filter(
+ (array) $value,
+ function ($datetime) {
+ return $datetime instanceof \DateTime;
+ }
+ )
+ )
) . ']';
}
diff --git a/src/Types/ArrayDateType.php b/src/Types/ArrayDateType.php
index e546cbd..f96829d 100644
--- a/src/Types/ArrayDateType.php
+++ b/src/Types/ArrayDateType.php
@@ -16,26 +16,18 @@
use Doctrine\DBAL\ParameterType;
use Doctrine\DBAL\Platforms\AbstractPlatform;
+use function array_filter;
+use function array_map;
+use function implode;
/**
* Array(Date) Type class
*/
-class ArrayDateType extends AbstractArrayType
+class ArrayDateType extends ArrayType implements DatableClickHouseType
{
- /**
- * {@inheritDoc}
- */
- public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) : string
- {
- return 'Array(Date)';
- }
-
- /**
- * {@inheritDoc}
- */
- public function getName() : string
+ public function getBaseClickHouseType() : string
{
- return 'array(date)';
+ return DatableClickHouseType::TYPE_DATE;
}
/**
@@ -43,9 +35,12 @@ public function getName() : string
*/
public function convertToPHPValue($value, AbstractPlatform $platform)
{
- return array_map(function ($stringDatetime) use ($platform) {
- return \DateTime::createFromFormat($platform->getDateFormatString(), $stringDatetime);
- }, (array) $value);
+ return array_map(
+ function ($stringDatetime) use ($platform) {
+ return \DateTime::createFromFormat($platform->getDateFormatString(), $stringDatetime);
+ },
+ (array) $value
+ );
}
/**
@@ -55,11 +50,17 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform)
{
return '[' . implode(
', ',
- array_map(function (\DateTime $datetime) use ($platform) {
+ array_map(
+ function (\DateTime $datetime) use ($platform) {
return "'" . $datetime->format($platform->getDateFormatString()) . "'";
- }, array_filter((array) $value, function ($datetime) {
- return $datetime instanceof \DateTime;
- }))
+ },
+ array_filter(
+ (array) $value,
+ function ($datetime) {
+ return $datetime instanceof \DateTime;
+ }
+ )
+ )
) . ']';
}
diff --git a/src/Types/ArrayFloat32Type.php b/src/Types/ArrayFloat32Type.php
index c5e4467..fcc0b27 100644
--- a/src/Types/ArrayFloat32Type.php
+++ b/src/Types/ArrayFloat32Type.php
@@ -17,13 +17,15 @@
/**
* Array(Float32) Type
*/
-class ArrayFloat32Type extends AbstractArrayFloatType
+class ArrayFloat32Type extends ArrayType implements BitNumericalClickHouseType
{
- public const BITNESS = 32;
+ public function getBits() : int
+ {
+ return BitNumericalClickHouseType::THIRTY_TWO_BIT;
+ }
- /** {@inheritdoc} */
- protected function getBitness() : int
+ public function getBaseClickHouseType() : string
{
- return self::BITNESS;
+ return NumericalClickHouseType::TYPE_FLOAT;
}
}
diff --git a/src/Types/ArrayFloat64Type.php b/src/Types/ArrayFloat64Type.php
index f51ee29..01c8790 100644
--- a/src/Types/ArrayFloat64Type.php
+++ b/src/Types/ArrayFloat64Type.php
@@ -17,13 +17,15 @@
/**
* Array(Float64) Type
*/
-class ArrayFloat64Type extends AbstractArrayFloatType
+class ArrayFloat64Type extends ArrayType implements BitNumericalClickHouseType
{
- public const BITNESS = 64;
+ public function getBits() : int
+ {
+ return BitNumericalClickHouseType::SIXTY_FOUR_BIT;
+ }
- /** {@inheritdoc} */
- protected function getBitness() : int
+ public function getBaseClickHouseType() : string
{
- return self::BITNESS;
+ return NumericalClickHouseType::TYPE_FLOAT;
}
}
diff --git a/src/Types/ArrayInt16Type.php b/src/Types/ArrayInt16Type.php
index 034563a..0c78d76 100644
--- a/src/Types/ArrayInt16Type.php
+++ b/src/Types/ArrayInt16Type.php
@@ -17,13 +17,15 @@
/**
* Array(Int16) Type
*/
-class ArrayInt16Type extends AbstractArrayIntType
+class ArrayInt16Type extends ArrayType implements BitNumericalClickHouseType
{
- public const BITNESS = 16;
+ public function getBits() : int
+ {
+ return BitNumericalClickHouseType::SIXTEEN_BIT;
+ }
- /** {@inheritdoc} */
- protected function getBitness() : int
+ public function getBaseClickHouseType() : string
{
- return self::BITNESS;
+ return NumericalClickHouseType::TYPE_INT;
}
}
diff --git a/src/Types/ArrayInt32Type.php b/src/Types/ArrayInt32Type.php
index 81a74a6..201278e 100644
--- a/src/Types/ArrayInt32Type.php
+++ b/src/Types/ArrayInt32Type.php
@@ -17,13 +17,15 @@
/**
* Array(Int32) Type
*/
-class ArrayInt32Type extends AbstractArrayIntType
+class ArrayInt32Type extends ArrayType implements BitNumericalClickHouseType
{
- public const BITNESS = 32;
+ public function getBits() : int
+ {
+ return BitNumericalClickHouseType::THIRTY_TWO_BIT;
+ }
- /** {@inheritdoc} */
- protected function getBitness() : int
+ public function getBaseClickHouseType() : string
{
- return self::BITNESS;
+ return NumericalClickHouseType::TYPE_INT;
}
}
diff --git a/src/Types/ArrayInt64Type.php b/src/Types/ArrayInt64Type.php
index 863cb4d..2e1860d 100644
--- a/src/Types/ArrayInt64Type.php
+++ b/src/Types/ArrayInt64Type.php
@@ -17,13 +17,15 @@
/**
* Array(Int64) Type
*/
-class ArrayInt64Type extends AbstractArrayIntType
+class ArrayInt64Type extends ArrayType implements BitNumericalClickHouseType
{
- public const BITNESS = 64;
+ public function getBits() : int
+ {
+ return BitNumericalClickHouseType::SIXTY_FOUR_BIT;
+ }
- /** {@inheritdoc} */
- protected function getBitness() : int
+ public function getBaseClickHouseType() : string
{
- return self::BITNESS;
+ return NumericalClickHouseType::TYPE_INT;
}
}
diff --git a/src/Types/ArrayInt8Type.php b/src/Types/ArrayInt8Type.php
index 8099a9d..9f18e6f 100644
--- a/src/Types/ArrayInt8Type.php
+++ b/src/Types/ArrayInt8Type.php
@@ -17,13 +17,15 @@
/**
* Array(Int8) Type
*/
-class ArrayInt8Type extends AbstractArrayIntType
+class ArrayInt8Type extends ArrayType implements BitNumericalClickHouseType
{
- public const BITNESS = 8;
+ public function getBits() : int
+ {
+ return BitNumericalClickHouseType::EIGHT_BIT;
+ }
- /** {@inheritdoc} */
- protected function getBitness() : int
+ public function getBaseClickHouseType() : string
{
- return self::BITNESS;
+ return NumericalClickHouseType::TYPE_INT;
}
}
diff --git a/src/Types/ArrayStringType.php b/src/Types/ArrayStringType.php
index 64cb5c6..8b39e5f 100644
--- a/src/Types/ArrayStringType.php
+++ b/src/Types/ArrayStringType.php
@@ -16,26 +16,17 @@
use Doctrine\DBAL\ParameterType;
use Doctrine\DBAL\Platforms\AbstractPlatform;
+use function array_map;
+use function implode;
/**
* Array(String) Type class
*/
-class ArrayStringType extends AbstractArrayType
+class ArrayStringType extends ArrayType implements StringClickHouseType
{
- /**
- * {@inheritDoc}
- */
- public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) : string
- {
- return 'Array(String)';
- }
-
- /**
- * {@inheritDoc}
- */
- public function getName() : string
+ public function getBaseClickHouseType() : string
{
- return 'array(string)';
+ return StringClickHouseType::TYPE_STRING;
}
/**
diff --git a/src/Types/AbstractArrayType.php b/src/Types/ArrayType.php
similarity index 64%
rename from src/Types/AbstractArrayType.php
rename to src/Types/ArrayType.php
index dd19594..2c185fc 100644
--- a/src/Types/AbstractArrayType.php
+++ b/src/Types/ArrayType.php
@@ -17,13 +17,16 @@
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\Type;
+use function array_key_exists;
+use function sprintf;
+use function strtolower;
/**
* Array(*) Types basic class
*/
-abstract class AbstractArrayType extends Type
+abstract class ArrayType extends Type implements ClickHouseType
{
- public const ARRAY_TYPES = [
+ protected const ARRAY_TYPES = [
'array(int8)' => ArrayInt8Type::class,
'array(int16)' => ArrayInt16Type::class,
'array(int32)' => ArrayInt32Type::class,
@@ -65,4 +68,36 @@ public function getMappedDatabaseTypes(AbstractPlatform $platform) : array
{
return [$this->getName()];
}
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) : string
+ {
+ return $this->getDeclaration($fieldDeclaration);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getName() : string
+ {
+ return strtolower($this->getDeclaration());
+ }
+
+ /**
+ * @param mixed[] $fieldDeclaration
+ */
+ protected function getDeclaration(array $fieldDeclaration = []) : string
+ {
+ return sprintf(
+ array_key_exists(
+ 'notnull',
+ $fieldDeclaration
+ ) && $fieldDeclaration['notnull'] === false ? 'Array(Nullable(%s%s%s))' : 'Array(%s%s%s)',
+ $this instanceof UnsignedNumericalClickHouseType ? 'U' : '',
+ $this->getBaseClickHouseType(),
+ $this instanceof BitNumericalClickHouseType ? $this->getBits() : ''
+ );
+ }
}
diff --git a/src/Types/ArrayUInt16Type.php b/src/Types/ArrayUInt16Type.php
index 98c2847..3fb8d4c 100644
--- a/src/Types/ArrayUInt16Type.php
+++ b/src/Types/ArrayUInt16Type.php
@@ -17,7 +17,15 @@
/**
* Array(UInt16) Type
*/
-class ArrayUInt16Type extends ArrayInt16Type
+class ArrayUInt16Type extends ArrayType implements BitNumericalClickHouseType, UnsignedNumericalClickHouseType
{
- public const UNSIGNED = true;
+ public function getBits() : int
+ {
+ return BitNumericalClickHouseType::SIXTEEN_BIT;
+ }
+
+ public function getBaseClickHouseType() : string
+ {
+ return NumericalClickHouseType::TYPE_INT;
+ }
}
diff --git a/src/Types/ArrayUInt32Type.php b/src/Types/ArrayUInt32Type.php
index 39bec16..ef6794c 100644
--- a/src/Types/ArrayUInt32Type.php
+++ b/src/Types/ArrayUInt32Type.php
@@ -17,7 +17,15 @@
/**
* Array(UInt32) Type
*/
-class ArrayUInt32Type extends ArrayInt32Type
+class ArrayUInt32Type extends ArrayType implements BitNumericalClickHouseType, UnsignedNumericalClickHouseType
{
- public const UNSIGNED = true;
+ public function getBits() : int
+ {
+ return BitNumericalClickHouseType::THIRTY_TWO_BIT;
+ }
+
+ public function getBaseClickHouseType() : string
+ {
+ return NumericalClickHouseType::TYPE_INT;
+ }
}
diff --git a/src/Types/ArrayUInt64Type.php b/src/Types/ArrayUInt64Type.php
index 5c42123..a21c280 100644
--- a/src/Types/ArrayUInt64Type.php
+++ b/src/Types/ArrayUInt64Type.php
@@ -17,7 +17,15 @@
/**
* Array(UInt64) Type
*/
-class ArrayUInt64Type extends ArrayInt64Type
+class ArrayUInt64Type extends ArrayType implements BitNumericalClickHouseType, UnsignedNumericalClickHouseType
{
- public const UNSIGNED = true;
+ public function getBits() : int
+ {
+ return BitNumericalClickHouseType::SIXTY_FOUR_BIT;
+ }
+
+ public function getBaseClickHouseType() : string
+ {
+ return NumericalClickHouseType::TYPE_INT;
+ }
}
diff --git a/src/Types/ArrayUInt8Type.php b/src/Types/ArrayUInt8Type.php
index 832e886..626eafa 100644
--- a/src/Types/ArrayUInt8Type.php
+++ b/src/Types/ArrayUInt8Type.php
@@ -17,7 +17,15 @@
/**
* Array(UInt8) Type
*/
-class ArrayUInt8Type extends ArrayInt8Type
+class ArrayUInt8Type extends ArrayType implements BitNumericalClickHouseType, UnsignedNumericalClickHouseType
{
- public const UNSIGNED = true;
+ public function getBits() : int
+ {
+ return BitNumericalClickHouseType::EIGHT_BIT;
+ }
+
+ public function getBaseClickHouseType() : string
+ {
+ return NumericalClickHouseType::TYPE_INT;
+ }
}
diff --git a/src/Types/AbstractArrayNumType.php b/src/Types/BitNumericalClickHouseType.php
similarity index 61%
rename from src/Types/AbstractArrayNumType.php
rename to src/Types/BitNumericalClickHouseType.php
index 0d412f2..ab843b8 100644
--- a/src/Types/AbstractArrayNumType.php
+++ b/src/Types/BitNumericalClickHouseType.php
@@ -14,13 +14,12 @@
namespace FOD\DBALClickHouse\Types;
-/**
- * Array(Numeric) Types basic class
- */
-abstract class AbstractArrayNumType extends AbstractArrayType
+interface BitNumericalClickHouseType extends NumericalClickHouseType
{
- /**
- * @return int Bitness of integers or floats in Array (Array(Int{bitness}) or Array(Float{bitness}))
- */
- abstract protected function getBitness() : int;
+ public const EIGHT_BIT = 8;
+ public const SIXTEEN_BIT = 16;
+ public const THIRTY_TWO_BIT = 32;
+ public const SIXTY_FOUR_BIT = 64;
+
+ public function getBits() : int;
}
diff --git a/src/Types/ClickHouseType.php b/src/Types/ClickHouseType.php
new file mode 100644
index 0000000..9f12543
--- /dev/null
+++ b/src/Types/ClickHouseType.php
@@ -0,0 +1,20 @@
+)
+ *
+ * (c) FriendsOfDoctrine .
+ *
+ * For the full copyright and license inflormation, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace FOD\DBALClickHouse\Types;
+
+interface ClickHouseType
+{
+ public function getBaseClickHouseType() : string;
+}
diff --git a/src/Types/DatableClickHouseType.php b/src/Types/DatableClickHouseType.php
new file mode 100644
index 0000000..e2e7e5a
--- /dev/null
+++ b/src/Types/DatableClickHouseType.php
@@ -0,0 +1,21 @@
+)
+ *
+ * (c) FriendsOfDoctrine .
+ *
+ * For the full copyright and license inflormation, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace FOD\DBALClickHouse\Types;
+
+interface DatableClickHouseType extends ClickHouseType
+{
+ public const TYPE_DATE = 'Date';
+ public const TYPE_DATE_TIME = 'DateTime';
+}
diff --git a/src/Types/NumericalClickHouseType.php b/src/Types/NumericalClickHouseType.php
new file mode 100644
index 0000000..ebee8ed
--- /dev/null
+++ b/src/Types/NumericalClickHouseType.php
@@ -0,0 +1,21 @@
+)
+ *
+ * (c) FriendsOfDoctrine .
+ *
+ * For the full copyright and license inflormation, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace FOD\DBALClickHouse\Types;
+
+interface NumericalClickHouseType extends ClickHouseType
+{
+ public const TYPE_INT = 'Int';
+ public const TYPE_FLOAT = 'Float';
+}
diff --git a/src/Types/StringClickHouseType.php b/src/Types/StringClickHouseType.php
new file mode 100644
index 0000000..524d7e9
--- /dev/null
+++ b/src/Types/StringClickHouseType.php
@@ -0,0 +1,21 @@
+)
+ *
+ * (c) FriendsOfDoctrine .
+ *
+ * For the full copyright and license inflormation, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace FOD\DBALClickHouse\Types;
+
+interface StringClickHouseType extends ClickHouseType
+{
+ public const TYPE_STRING = 'String';
+ public const TYPE_FIXED_STRING = 'FixedString';
+}
diff --git a/src/Types/UnsignedNumericalClickHouseType.php b/src/Types/UnsignedNumericalClickHouseType.php
new file mode 100644
index 0000000..f17132e
--- /dev/null
+++ b/src/Types/UnsignedNumericalClickHouseType.php
@@ -0,0 +1,20 @@
+)
+ *
+ * (c) FriendsOfDoctrine .
+ *
+ * For the full copyright and license inflormation, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace FOD\DBALClickHouse\Types;
+
+interface UnsignedNumericalClickHouseType extends NumericalClickHouseType
+{
+ public const UNSIGNED_CHAR = 'U';
+}
diff --git a/tests/ArraysTest.php b/tests/ArraysTest.php
index fb0225e..dfc412d 100644
--- a/tests/ArraysTest.php
+++ b/tests/ArraysTest.php
@@ -12,7 +12,7 @@
namespace FOD\DBALClickHouse\Tests;
use FOD\DBALClickHouse\Connection;
-use FOD\DBALClickHouse\Types\AbstractArrayType;
+use FOD\DBALClickHouse\Types\ArrayType;
use PHPUnit\Framework\TestCase;
/**
@@ -28,7 +28,7 @@ class ArraysTest extends TestCase
public function setUp()
{
$this->connection = CreateConnectionTest::createConnection();
- AbstractArrayType::registerArrayTypes($this->connection->getDatabasePlatform());
+ ArrayType::registerArrayTypes($this->connection->getDatabasePlatform());
}
public function tearDown()
diff --git a/tests/ConnectionTest.php b/tests/ConnectionTest.php
index 63bd06d..5603b10 100644
--- a/tests/ConnectionTest.php
+++ b/tests/ConnectionTest.php
@@ -150,7 +150,7 @@ public function testGetServerVersion()
{
$conn = $this->connection->getWrappedConnection();
if ($conn instanceof ServerInfoAwareConnection) {
- $this->assertRegExp('/(^[0-9]+.[0-9]+.[0-9]+.[0-9]$)/mi', $conn->getServerVersion());
+ $this->assertRegExp('/(^[0-9]+.[0-9]+.[0-9]+(.[0-9]$|$))/mi', $conn->getServerVersion());
} else {
$this->fail(sprintf('`%s` does not implement the `%s` interface', \get_class($conn),
ServerInfoAwareConnection::class));
diff --git a/tests/CreateSchemaTest.php b/tests/CreateSchemaTest.php
index 59dbaa5..3b3ad5e 100644
--- a/tests/CreateSchemaTest.php
+++ b/tests/CreateSchemaTest.php
@@ -15,6 +15,7 @@
use Doctrine\DBAL\Types\Type;
use FOD\DBALClickHouse\ClickHouseSchemaManager;
use FOD\DBALClickHouse\Connection;
+use FOD\DBALClickHouse\Types\ArrayType;
use PHPUnit\Framework\TestCase;
/**
@@ -238,4 +239,75 @@ public function testEventDateProviderColumnBadOption()
}
$this->connection->exec('DROP TABLE test_table');
}
+
+ public function testNullableColumns()
+ {
+ $fromSchema = $this->connection->getSchemaManager()->createSchema();
+ ArrayType::registerArrayTypes($this->connection->getDatabasePlatform());
+
+ $toSchema = clone $fromSchema;
+
+ $newTable = $toSchema->createTable('test_table_nullable');
+
+ $newTable->addColumn('id', 'integer', ['unsigned' => true, 'notnull' => false]);
+ $newTable->addColumn('payload', 'string', ['notnull' => false]);
+ $newTable->addColumn('price', 'float', ['notnull' => false]);
+ $newTable->addColumn('transactions', 'array(datetime)', ['notnull' => false]);
+ $newTable->addColumn('status', 'boolean', ['notnull' => false]);
+ $newTable->setPrimaryKey(['id']);
+ $newTable->addOption('engine', 'Memory');
+
+ $migrationSQLs = $fromSchema->getMigrateToSql($toSchema, $this->connection->getDatabasePlatform());
+ $generatedSQL = implode(';', $migrationSQLs);
+ $this->assertEquals("CREATE TABLE test_table_nullable (id UInt32, payload Nullable(String), price Nullable(Float64), transactions Array(Nullable(DateTime)), status Nullable(UInt8)) ENGINE = Memory",
+ $generatedSQL);
+ foreach ($migrationSQLs as $sql) {
+ $this->connection->exec($sql);
+ }
+ $this->connection->insert('test_table_nullable',
+ [
+ 'id' => 1,
+ 'payload' => 's1',
+ 'price' => 1.5,
+ 'transactions' => [date('Y-m-d H:i:s'), null],
+ 'status' => null
+ ]);
+ $this->connection->insert('test_table_nullable',
+ [
+ 'id' => 2,
+ 'payload' => 's2',
+ 'price' => 120,
+ 'transactions' => [null, null],
+ 'status' => false
+ ]);
+ $this->connection->insert('test_table_nullable',
+ [
+ 'id' => 3,
+ 'payload' => null,
+ 'price' => 1000,
+ 'transactions' => [date('Y-m-d H:i:s')],
+ 'status' => true
+ ]);
+ $this->connection->insert('test_table_nullable',
+ [
+ 'id' => 4,
+ 'payload' => 's4',
+ 'price' => null,
+ 'transactions' => [date('Y-m-d H:i:s'), date('Y-m-d H:i:s')],
+ 'status' => null
+ ]);
+ $this->connection->insert('test_table_nullable',
+ [
+ 'id' => 5,
+ 'payload' => 's5',
+ 'price' => 100,
+ 'transactions' => [date('Y-m-d H:i:s')],
+ 'status' => true
+ ]);
+
+ $this->assertEquals(2,
+ (int)$this->connection->fetchColumn("SELECT count() from test_table_nullable WHERE {$this->connection->getDatabasePlatform()->getIsNullExpression('status')}"));
+
+ $this->connection->exec('DROP TABLE test_table_nullable');
+ }
}