diff --git a/src/SystemVariableAssigner.php b/src/SystemVariableAssigner.php index a928ac3..2e631c7 100644 --- a/src/SystemVariableAssigner.php +++ b/src/SystemVariableAssigner.php @@ -3,6 +3,7 @@ namespace Mpyw\LaravelMySqlSystemVariableManager; use Closure; +use Mpyw\LaravelMySqlSystemVariableManager\Value as BindingValue; use Mpyw\LaravelPdoEmulationControl\EmulationController; use Mpyw\Unclosure\Value; use PDO; @@ -86,7 +87,8 @@ protected static function withStatementFor(PDO $pdo, string $query, array $value { $statement = $pdo->prepare($query); foreach (array_values($values) as $i => $value) { - $statement->bindValue($i + 1, $value, Grammar::paramTypeFor($value)); + $value = BindingValue::wrap($value); + $statement->bindValue($i + 1, $value->getValue(), $value->getParamType()); } $statement->execute(); diff --git a/src/SystemVariableGrammar.php b/src/SystemVariableGrammar.php index b51ecb9..16f5b5e 100644 --- a/src/SystemVariableGrammar.php +++ b/src/SystemVariableGrammar.php @@ -2,8 +2,6 @@ namespace Mpyw\LaravelMySqlSystemVariableManager; -use PDO; - class SystemVariableGrammar { /** @@ -23,42 +21,11 @@ public static function assignmentExpressions(array $values): array { $expressions = []; foreach ($values as $name => $value) { - $expressions[] = static::escapeIdentifier($name) . '=' . static::placeholderFor($value); + $expressions[] = static::escapeIdentifier($name) . '=' . Value::wrap($value)->getPlaceholder(); } return $expressions; } - /** - * @param mixed $value - * @return int - */ - public static function paramTypeFor($value): int - { - switch (gettype($value)) { - case 'integer': - return PDO::PARAM_INT; - case 'boolean': - return PDO::PARAM_BOOL; - case 'NULL': - default: - return PDO::PARAM_STR; - } - } - - /** - * @param mixed $value - * @return string - */ - public static function placeholderFor($value): string - { - switch (gettype($value)) { - case 'double': - return 'cast(? as decimal(65, 30))'; - default: - return '?'; - } - } - /** * @param string $identifier * @return string diff --git a/src/Value.php b/src/Value.php new file mode 100644 index 0000000..b6feca8 --- /dev/null +++ b/src/Value.php @@ -0,0 +1,172 @@ +value = $value; + $this->type = $type; + } + + /** + * Return original value. + * + * @return bool|float|int|string + */ + public function getValue() + { + return $this->value; + } + + /** + * Return type. + * + * @return string + */ + public function getType(): string + { + return $this->type; + } + + /** + * Return PDO::PARAM_* type. + * + * @return int + */ + public function getParamType(): int + { + switch ($this->type) { + case static::TYPE_INT: + return PDO::PARAM_INT; + case static::TYPE_BOOL: + return PDO::PARAM_BOOL; + case static::TYPE_FLOAT: + case static::TYPE_STR: + default: + return PDO::PARAM_STR; + } + } + + /** + * Return a placeholder format. + * + * @return string + */ + public function getPlaceholder(): string + { + switch ($this->type) { + case static::TYPE_FLOAT: + return 'cast(? as decimal(65, 30))'; + case static::TYPE_INT: + case static::TYPE_BOOL: + case static::TYPE_STR: + default: + return '?'; + } + } +} diff --git a/src/ValueInterface.php b/src/ValueInterface.php new file mode 100644 index 0000000..6d880e3 --- /dev/null +++ b/src/ValueInterface.php @@ -0,0 +1,39 @@ + ['foreign_key_checks', true, '1', false, '0'], 'assigning string (native)' => ['tx_isolation', false, 'REPEATABLE-READ', 'read-committed', 'READ-COMMITTED'], 'assigning string (emulated)' => ['tx_isolation', true, 'REPEATABLE-READ', 'read-committed', 'READ-COMMITTED'], + 'assigning wrapped float (native)' => ['long_query_time', false, 10.0, Value::float(15.0), 15.0], + 'assigning wrapped float (emulated)' => ['long_query_time', true, '10.000000', Value::float(15.0), '15.000000'], + 'assigning wrapped integer (native)' => ['long_query_time', false, 10.0, Value::int(15), 15.0], + 'assigning wrapped integer (emulated)' => ['long_query_time', true, '10.000000', Value::int(15), '15.000000'], + 'assigning wrapped boolean (native)' => ['foreign_key_checks', false, 1, Value::bool(false), 0], + 'assigning wrapped boolean (emulated)' => ['foreign_key_checks', true, '1', Value::bool(false), '0'], + 'assigning wrapped string (native)' => ['tx_isolation', false, 'REPEATABLE-READ', Value::str('read-committed'), 'READ-COMMITTED'], + 'assigning wrapped string (emulated)' => ['tx_isolation', true, 'REPEATABLE-READ', Value::str('read-committed'), 'READ-COMMITTED'], ]; } public function testAssigningNullThrowsExceptionOnNative(): void { - $this->expectException(PDOException::class); - $this->expectExceptionMessage("SQLSTATE[42000]: Syntax error or access violation: 1231 Variable 'foreign_key_checks' can't be set to the value of 'NULL'"); + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('The value must be a scalar or Mpyw\LaravelMySqlSystemVariableManager\ValueInterface instance.'); $this->onNativeConnection(function (MySqlConnection $db) { $db->setSystemVariable('foreign_key_checks', null); @@ -55,8 +64,8 @@ public function testAssigningNullThrowsExceptionOnNative(): void public function testAssigningNullThrowsExceptionOnEmulation(): void { - $this->expectException(PDOException::class); - $this->expectExceptionMessage("SQLSTATE[42000]: Syntax error or access violation: 1231 Variable 'foreign_key_checks' can't be set to the value of 'NULL'"); + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('The value must be a scalar or Mpyw\LaravelMySqlSystemVariableManager\ValueInterface instance.'); $this->onEmulatedConnection(function (MySqlConnection $db) { $db->setSystemVariable('foreign_key_checks', null); @@ -64,15 +73,24 @@ public function testAssigningNullThrowsExceptionOnEmulation(): void }); } - public function testAssigningNullDoesNotThrowOnUnresolvedConnection(): void + public function testAssigningNullThrowsOnUnresolvedNativeConnection(): void { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('The value must be a scalar or Mpyw\LaravelMySqlSystemVariableManager\ValueInterface instance.'); + $this->onNativeConnection(function (MySqlConnection $db) { $db->setSystemVariable('foreign_key_checks', null); }); + } + + public function testAssigningNullThrowsOnUnresolvedEmulatedConnection(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('The value must be a scalar or Mpyw\LaravelMySqlSystemVariableManager\ValueInterface instance.'); + $this->onEmulatedConnection(function (MySqlConnection $db) { $db->setSystemVariable('foreign_key_checks', null); }); - $this->assertTrue(true); } public function testAssignmentPriorityOnLazilyResolvedConnection(): void