update Medoo 2.2

Lightweight PHP Database Framework 2.2
This commit is contained in:
gtbu 2025-04-06 18:01:44 +02:00
parent 692b424b64
commit 628067d1e1
19 changed files with 3545 additions and 2306 deletions

View file

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2021 Angel Lai
Copyright (c) 2025 Angel Lai
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View file

@ -6,10 +6,10 @@ declare(strict_types=1);
*
* The Lightweight PHP Database Framework to Accelerate Development.
*
* @version 2.1.12
* @author Angel Lai
* @version 2.2.0
* @package Medoo
* @copyright Copyright 2024 Medoo Project, Angel Lai.
* @author Angel Lai
* @copyright Angel Lai
* @license https://opensource.org/licenses/MIT
* @link https://medoo.in
*/
@ -43,10 +43,18 @@ class Raw
}
/**
* @method array select(string $table, array $columns)
* @method mixed select(string $table, string $column)
* @method array select(string $table, array $columns, array $where)
* @method mixed select(string $table, string $column, array $where)
* @method array select(string $table, array $join, array $columns)
* @method mixed select(string $table, array $join, string $column)
* @method null select(string $table, array $columns, callable $callback)
* @method null select(string $table, string $column, callable $callback)
* @method null select(string $table, array $columns, array $where, callable $callback)
* @method null select(string $table, string $column, array $where, callable $callback)
* @method null select(string $table, array $join, array $columns, array $where, callable $callback)
* @method null select(string $table, array $join, string $column, array $where, callable $callback)
* @method mixed get(string $table, array|string $columns, array $where)
* @method bool has(string $table, array $where)
* @method mixed rand(string $table, array|string $column, array $where)
@ -63,28 +71,28 @@ class Raw
class Medoo
{
/**
* The PDO object.
* The PDO database connection instance.
*
* @var \PDO
*/
public $pdo;
/**
* The type of database.
* The database type.
*
* @var string
*/
public $type;
/**
* Table prefix.
* The table prefix.
*
* @var string
*/
protected $prefix;
/**
* The PDO statement object.
* Current PDO statement instance.
*
* @var \PDOStatement
*/
@ -98,88 +106,125 @@ class Medoo
protected $dsn;
/**
* The array of logs.
* Logged queries.
*
* @var array
*/
protected $logs = [];
/**
* Determine should log the query or not.
* Whether query logging is enabled.
*
* @var bool
*/
protected $logging = false;
/**
* Determine is in test mode.
* Whether the database is in test mode.
*
* @var bool
*/
protected $testMode = false;
/**
* The last query string was generated in test mode.
* The last generated query string in test mode.
*
* @var string
*/
public $queryString;
/**
* Determine is in debug mode.
* Whether debug mode is enabled.
*
* @var bool
*/
protected $debugMode = false;
/**
* Determine should save debug logging.
* Whether debug logging is enabled.
*
* @var bool
*/
protected $debugLogging = false;
/**
* The array of logs for debugging.
* Logged debug queries.
*
* @var array
*/
protected $debugLogs = [];
/**
* The unique global id.
* The unique global identifier.
*
* @var integer
* @var int
*/
protected $guid = 0;
/**
* The returned id for the insert.
* The last inserted record ID.
*
* @var string
*/
public $returnId = '';
/**
* Error Message.
* The last error message.
*
* @var string|null
*/
public $error = null;
/**
* The array of error information.
* The last error details.
*
* @var array|null
*/
public $errorInfo = null;
/**
* Connect the database.
* The connector used for table aliases.
*
* @var string
*/
protected $tableAliasConnector = ' AS ';
/**
* The pattern used for quoting identifiers.
*
* @var string
*/
protected $quotePattern = '"$1"';
/**
* Regular expression pattern for valid table names.
*
* @var string
*/
protected const TABLE_PATTERN = "[\p{L}_][\p{L}\p{N}@$#\-_]*";
/**
* Regular expression pattern for valid column names.
*
* @var string
*/
protected const COLUMN_PATTERN = "[\p{L}_][\p{L}\p{N}@$#\-_\.]*";
/**
* Regular expression pattern for valid alias names.
*
* @var string
*/
protected const ALIAS_PATTERN = "[\p{L}_][\p{L}\p{N}@$#\-_]*";
/**
* Establish a database connection.
*
* Example usage:
*
* ```
* $database = new Medoo([
* // required
* // Required
* 'type' => 'mysql',
* 'database' => 'name',
* 'host' => 'localhost',
@ -195,7 +240,7 @@ class Medoo
*
* @param array $options Connection options
* @return Medoo
* @throws PDOException
* @throws PDOException If the connection fails
* @link https://medoo.in/api/new
* @codeCoverageIgnore
*/
@ -211,7 +256,11 @@ class Medoo
return;
}
$options['type'] = $options['type'] ?? $options['database_type'];
$options['type'] = $options['type'] ?? $options['database_type'] ?? null;
if (!$options['type']) {
throw new InvalidArgumentException('Database type is required.');
}
if (!isset($options['pdo'])) {
$options['database'] = $options['database'] ?? $options['database_name'];
@ -221,19 +270,12 @@ class Medoo
}
}
if (isset($options['type'])) {
$this->type = strtolower($options['type']);
if ($this->type === 'mariadb') {
$this->type = 'mysql';
}
}
$this->setupType($options['type']);
if (isset($options['logging']) && is_bool($options['logging'])) {
$this->logging = $options['logging'];
}
$option = $options['option'] ?? [];
$commands = [];
switch ($this->type) {
@ -275,10 +317,7 @@ class Medoo
throw new InvalidArgumentException('Invalid DSN option supplied.');
}
} else {
if (
isset($options['port']) &&
is_int($options['port'] * 1)
) {
if (isset($options['port']) && is_numeric($options['port'])) {
$port = $options['port'];
}
@ -451,7 +490,7 @@ class Medoo
$dsn,
$options['username'] ?? null,
$options['password'] ?? null,
$option
$options['option'] ?? []
);
if (isset($options['error'])) {
@ -479,6 +518,31 @@ class Medoo
}
}
/**
* Setup the database type.
*
* @param string The database type string.
* @return void
*/
public function setupType(string $type)
{
$databaseType = strtolower($type);
if ($databaseType === 'mariadb') {
$databaseType = 'mysql';
}
if ($databaseType === 'oracle') {
$this->tableAliasConnector = ' ';
} elseif ($databaseType === 'mysql') {
$this->quotePattern = '`$1`';
} elseif ($databaseType === 'mssql') {
$this->quotePattern = '[$1]';
}
$this->type = $databaseType;
}
/**
* Generate a new map key for the placeholder.
*
@ -512,7 +576,7 @@ class Medoo
* @codeCoverageIgnore
* @return \PDOStatement|null
*/
public function exec(string $statement, array $map = [], callable $callback = null): ?PDOStatement
public function exec(string $statement, array $map = [], ?callable $callback = null): ?PDOStatement
{
$this->statement = null;
$this->errorInfo = null;
@ -591,14 +655,9 @@ class Medoo
*/
protected function generate(string $statement, array $map): string
{
$identifier = [
'mysql' => '`$1`',
'mssql' => '[$1]'
];
$statement = preg_replace(
'/(?!\'[^\s]+\s?)"([\p{L}_][\p{L}\p{N}@$#\-_]*)"(?!\s?[^\s]+\')/u',
$identifier[$this->type] ?? '"$1"',
'/(?!\'[^\s]+\s?)"(' . $this::COLUMN_PATTERN . ')"(?!\s?[^\s]+\')/u',
$this->quotePattern,
$statement
);
@ -661,7 +720,7 @@ class Medoo
}
$query = preg_replace_callback(
'/(([`\'])[\<]*?)?((FROM|TABLE|INTO|UPDATE|JOIN|TABLE IF EXISTS)\s*)?\<(([\p{L}_][\p{L}\p{N}@$#\-_]*)(\.[\p{L}_][\p{L}\p{N}@$#\-_]*)?)\>([^,]*?\2)?/',
'/(([`\'])[\<]*?)?((FROM|TABLE|TABLES LIKE|INTO|UPDATE|JOIN|TABLE IF EXISTS)\s*)?\<((' . $this::TABLE_PATTERN . ')(\.' . $this::COLUMN_PATTERN . ')?)\>([^,]*?\2)?/',
function ($matches) {
if (!empty($matches[2]) && isset($matches[8])) {
return $matches[0];
@ -688,9 +747,9 @@ class Medoo
}
/**
* Quote a string for use in a query.
* Escape and quote a string for use in an SQL query.
*
* @param string $string
* @param string $string The string to be quoted.
* @return string
*/
public function quote(string $string): string
@ -703,14 +762,15 @@ class Medoo
}
/**
* Quote table name for use in a query.
* Quote a table name for use in an SQL query.
*
* @param string $table
* @param string $table The table name to be quoted.
* @return string
* @throws InvalidArgumentException If the table name is invalid.
*/
public function tableQuote(string $table): string
{
if (preg_match('/^[\p{L}_][\p{L}\p{N}@$#\-_]*$/u', $table)) {
if (preg_match("/^" . $this::TABLE_PATTERN . "$/u", $table)) {
return '"' . $this->prefix . $table . '"';
}
@ -718,14 +778,15 @@ class Medoo
}
/**
* Quote column name for use in a query.
* Quote a column name for use in an SQL query.
*
* @param string $column
* @param string $column The column name to be quoted.
* @return string
* @throws InvalidArgumentException If the column name is invalid.
*/
public function columnQuote(string $column): string
{
if (preg_match('/^[\p{L}_][\p{L}\p{N}@$#\-_]*(\.?[\p{L}_][\p{L}\p{N}@$#\-_]*)?$/u', $column)) {
if (preg_match("/^" . $this::TABLE_PATTERN . "(\.?" . $this::ALIAS_PATTERN . ")?$/u", $column)) {
return strpos($column, '.') !== false ?
'"' . $this->prefix . str_replace('.', '"."', $column) . '"' :
'"' . $column . '"';
@ -794,14 +855,14 @@ class Medoo
} elseif ($isArrayValue) {
$stack[] = $this->columnPush($value, $map, false, $isJoin);
} elseif (!$isIntKey && $raw = $this->buildRaw($value, $map)) {
preg_match('/(?<column>[\p{L}_][\p{L}\p{N}@$#\-_\.]*)(\s*\[(?<type>(String|Bool|Int|Number))\])?/u', $key, $match);
preg_match("/(?<column>" . $this::COLUMN_PATTERN . ")(\s*\[(?<type>(String|Bool|Int|Number))\])?/u", $key, $match);
$stack[] = "{$raw} AS {$this->columnQuote($match['column'])}";
} elseif ($isIntKey && is_string($value)) {
if ($isJoin && strpos($value, '*') !== false) {
throw new InvalidArgumentException('Cannot use table.* to select all columns while joining table.');
}
preg_match('/(?<column>[\p{L}_][\p{L}\p{N}@$#\-_\.]*)(?:\s*\((?<alias>[\p{L}_][\p{L}\p{N}@$#\-_]*)\))?(?:\s*\[(?<type>(?:String|Bool|Int|Number|Object|JSON))\])?/u', $value, $match);
preg_match("/(?<column>" . $this::COLUMN_PATTERN . ")(?:\s*\((?<alias>" . $this::ALIAS_PATTERN . ")\))?(?:\s*\[(?<type>(?:String|Bool|Int|Number|Object|JSON))\])?/u", $value, $match);
$columnString = '';
@ -858,20 +919,20 @@ class Medoo
$isIndex = is_int($key);
preg_match(
'/([\p{L}_][\p{L}\p{N}@$#\-_\.]*)(\[(?<operator>.*)\])?([\p{L}_][\p{L}\p{N}@$#\-_\.]*)?/u',
"/(?<column>" . $this::COLUMN_PATTERN . ")(\[(?<operator>.*)\])?(?<comparison>" . $this::COLUMN_PATTERN . ")?/u",
$isIndex ? $value : $key,
$match
);
$column = $this->columnQuote($match[1]);
$column = $this->columnQuote($match['column']);
$operator = $match['operator'] ?? null;
if ($isIndex && isset($match[4]) && in_array($operator, ['>', '>=', '<', '<=', '=', '!='])) {
$stack[] = "{$column} {$operator} " . $this->columnQuote($match[4]);
if ($isIndex && isset($match['comparison']) && in_array($operator, ['>', '>=', '<', '<=', '=', '!='])) {
$stack[] = "{$column} {$operator} " . $this->columnQuote($match['comparison']);
continue;
}
if ($operator && $operator != '=') {
if ($operator && $operator !== '=') {
if (in_array($operator, ['>', '>=', '<', '<='])) {
$condition = "{$column} {$operator} ";
@ -1182,12 +1243,12 @@ class Medoo
$where = null,
$columnFn = null
): string {
preg_match('/(?<table>[\p{L}_][\p{L}\p{N}@$#\-_]*)\s*\((?<alias>[\p{L}_][\p{L}\p{N}@$#\-_]*)\)/u', $table, $tableMatch);
preg_match("/(?<table>" . $this::TABLE_PATTERN . ")\s*\((?<alias>" . $this::ALIAS_PATTERN . ")\)/u", $table, $tableMatch);
if (isset($tableMatch['table'], $tableMatch['alias'])) {
$table = $this->tableQuote($tableMatch['table']);
$tableAlias = $this->tableQuote($tableMatch['alias']);
$tableQuery = "{$table} AS {$tableAlias}";
$tableQuery = "{$table}{$this->tableAliasConnector}{$tableAlias}";
} else {
$table = $this->tableQuote($table);
$tableQuery = $table;
@ -1283,7 +1344,7 @@ class Medoo
];
foreach ($join as $subtable => $relation) {
preg_match('/(\[(?<join>\<\>?|\>\<?)\])?(?<table>[\p{L}_][\p{L}\p{N}@$#\-_]*)\s?(\((?<alias>[\p{L}_][\p{L}\p{N}@$#\-_]*)\))?/u', $subtable, $match);
preg_match("/(\[(?<join>\<\>?|\>\<?)\])?(?<table>" . $this::TABLE_PATTERN . ")\s?(\((?<alias>" . $this::ALIAS_PATTERN . ")\))?/u", $subtable, $match);
if ($match['join'] === '' || $match['table'] === '') {
continue;
@ -1325,7 +1386,7 @@ class Medoo
$tableName = $this->tableQuote($match['table']);
if (isset($match['alias'])) {
$tableName .= ' AS ' . $this->tableQuote($match['alias']);
$tableName .= $this->tableAliasConnector . $this->tableQuote($match['alias']);
}
$tableJoin[] = $type[$match['join']] . " JOIN {$tableName} {$relation}";
@ -1350,7 +1411,7 @@ class Medoo
foreach ($columns as $key => $value) {
if (is_int($key)) {
preg_match('/([\p{L}_][\p{L}\p{N}@$#\-_]*\.)?(?<column>[\p{L}_][\p{L}\p{N}@$#\-_]*)(?:\s*\((?<alias>[\p{L}_][\p{L}\p{N}@$#\-_]*)\))?(?:\s*\[(?<type>(?:String|Bool|Int|Number|Object|JSON))\])?/u', $value, $keyMatch);
preg_match("/(" . $this::TABLE_PATTERN . "\.)?(?<column>" . $this::COLUMN_PATTERN . ")(?:\s*\((?<alias>" . $this::ALIAS_PATTERN . ")\))?(?:\s*\[(?<type>(?:String|Bool|Int|Number|Object|JSON))\])?/u", $value, $keyMatch);
$columnKey = !empty($keyMatch['alias']) ?
$keyMatch['alias'] :
@ -1360,7 +1421,7 @@ class Medoo
[$columnKey, $keyMatch['type']] :
[$columnKey];
} elseif ($this->isRaw($value)) {
preg_match('/([\p{L}_][\p{L}\p{N}@$#\-_]*\.)?(?<column>[\p{L}_][\p{L}\p{N}@$#\-_]*)(\s*\[(?<type>(String|Bool|Int|Number))\])?/u', $key, $keyMatch);
preg_match("/(" . $this::TABLE_PATTERN . "\.)?(?<column>" . $this::COLUMN_PATTERN . ")(\s*\[(?<type>(String|Bool|Int|Number))\])?/u", $key, $keyMatch);
$columnKey = $keyMatch['column'];
$stack[$key] = isset($keyMatch['type']) ?
@ -1396,14 +1457,14 @@ class Medoo
array $columnMap,
array &$stack,
bool $root,
array &$result = null
?array &$result = null
): void {
if ($root) {
$columnsKey = array_keys($columns);
if (count($columnsKey) === 1 && is_array($columns[$columnsKey[0]])) {
$indexKey = array_keys($columns)[0];
$dataKey = preg_replace("/^[\p{L}_][\p{L}\p{N}@$#\-_]*\./u", '', $indexKey);
$dataKey = preg_replace("/^" . $this::COLUMN_PATTERN . "\./u", '', $indexKey);
$currentStack = [];
foreach ($data as $item) {
@ -1537,7 +1598,7 @@ class Medoo
foreach ($columns as $name => $definition) {
if (is_int($name)) {
$stack[] = preg_replace('/\<([\p{L}_][\p{L}\p{N}@$#\-_]*)\>/u', '"$1"', $definition);
$stack[] = preg_replace("/\<(" . $this::COLUMN_PATTERN . ")\>/u", '"$1"', $definition);
} elseif (is_array($definition)) {
$stack[] = $this->columnQuote($name) . ' ' . implode(' ', $definition);
} elseif (is_string($definition)) {
@ -1668,7 +1729,7 @@ class Medoo
* @param string $primaryKey
* @return \PDOStatement|null
*/
public function insert(string $table, array $values, string $primaryKey = null): ?PDOStatement
public function insert(string $table, array $values, ?string $primaryKey = null): ?PDOStatement
{
$stack = [];
$columns = [];
@ -1792,7 +1853,7 @@ class Medoo
continue;
}
preg_match('/(?<column>[\p{L}_][\p{L}\p{N}@$#\-_]*)(\[(?<operator>\+|\-|\*|\/)\])?/u', $key, $match);
preg_match("/" . $this::COLUMN_PATTERN . "(\[(?<operator>\+|\-|\*|\/)\])?/u", $key, $match);
if (isset($match['operator'])) {
if (is_numeric($value)) {
@ -2131,7 +2192,7 @@ class Medoo
* @codeCoverageIgnore
* @return string|null
*/
public function id(string $name = null): ?string
public function id(?string $name = null): ?string
{
$type = $this->type;

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,117 @@
<?php
namespace Medoo\Tests;
/**
* @coversDefaultClass \Medoo\Medoo
*/
class AggregateTest extends MedooTestCase
{
/**
* @covers ::count()
* @covers ::aggregate()
* @covers ::selectContext()
* @dataProvider typesProvider
*/
public function testCount($type)
{
$this->setType($type);
$this->database->count("account", [
"gender" => "female"
]);
$this->assertQuery(
<<<EOD
SELECT COUNT(*)
FROM "account"
WHERE "gender" = 'female'
EOD,
$this->database->queryString
);
}
/**
* @covers ::max()
* @covers ::aggregate()
* @covers ::selectContext()
* @dataProvider typesProvider
*/
public function testMax($type)
{
$this->setType($type);
$this->database->max("account", "age");
$this->assertQuery(
<<<EOD
SELECT MAX("age")
FROM "account"
EOD,
$this->database->queryString
);
}
/**
* @covers ::min()
* @covers ::aggregate()
* @covers ::selectContext()
* @dataProvider typesProvider
*/
public function testMin($type)
{
$this->setType($type);
$this->database->min("account", "age");
$this->assertQuery(
<<<EOD
SELECT MIN("age")
FROM "account"
EOD,
$this->database->queryString
);
}
/**
* @covers ::avg()
* @covers ::aggregate()
* @covers ::selectContext()
* @dataProvider typesProvider
*/
public function testAvg($type)
{
$this->setType($type);
$this->database->avg("account", "age");
$this->assertQuery(
<<<EOD
SELECT AVG("age")
FROM "account"
EOD,
$this->database->queryString
);
}
/**
* @covers ::sum()
* @covers ::aggregate()
* @covers ::selectContext()
* @dataProvider typesProvider
*/
public function testSum($type)
{
$this->setType($type);
$this->database->sum("account", "money");
$this->assertQuery(
<<<EOD
SELECT SUM("money")
FROM "account"
EOD,
$this->database->queryString
);
}
}

View file

@ -0,0 +1,142 @@
<?php
namespace Medoo\Tests;
/**
* @coversDefaultClass \Medoo\Medoo
*/
class CreateTest extends MedooTestCase
{
/**
* @covers ::create()
* @dataProvider typesProvider
*/
public function testCreate($type)
{
$this->setType($type);
$this->database->create("account", [
"id" => [
"INT",
"NOT NULL",
"AUTO_INCREMENT"
],
"email" => [
"VARCHAR(70)",
"NOT NULL",
"UNIQUE"
],
"PRIMARY KEY (<id>)"
], [
"AUTO_INCREMENT" => 200
]);
$this->assertQuery(
[
'default' => <<<EOD
CREATE TABLE IF NOT EXISTS "account"
("id" INT NOT NULL AUTO_INCREMENT,
"email" VARCHAR(70) NOT NULL UNIQUE,
PRIMARY KEY ("id"))
AUTO_INCREMENT = 200
EOD,
'mssql' => <<<EOD
CREATE TABLE [account]
([id] INT NOT NULL AUTO_INCREMENT,
[email] VARCHAR(70) NOT NULL UNIQUE,
PRIMARY KEY ([id]))
AUTO_INCREMENT = 200
EOD,
'oracle' => <<<EOD
CREATE TABLE "account"
("id" INT NOT NULL AUTO_INCREMENT,
"email" VARCHAR(70) NOT NULL UNIQUE,
PRIMARY KEY ("id"))
AUTO_INCREMENT = 200
EOD
],
$this->database->queryString
);
}
/**
* @covers ::create()
* @dataProvider typesProvider
*/
public function testCreateWithStringDefinition($type)
{
$this->setType($type);
$this->database->create("account", [
"id" => "INT NOT NULL AUTO_INCREMENT",
"email" => "VARCHAR(70) NOT NULL UNIQUE"
]);
$this->assertQuery(
[
'default' => <<<EOD
CREATE TABLE IF NOT EXISTS "account"
("id" INT NOT NULL AUTO_INCREMENT,
"email" VARCHAR(70) NOT NULL UNIQUE)
EOD,
'mssql' => <<<EOD
CREATE TABLE [account]
([id] INT NOT NULL AUTO_INCREMENT,
[email] VARCHAR(70) NOT NULL UNIQUE)
EOD,
'oracle' => <<<EOD
CREATE TABLE "account"
("id" INT NOT NULL AUTO_INCREMENT,
"email" VARCHAR(70) NOT NULL UNIQUE)
EOD
],
$this->database->queryString
);
}
/**
* @covers ::create()
* @dataProvider typesProvider
*/
public function testCreateWithSingleOption($type)
{
$this->setType($type);
$this->database->create("account", [
"id" => [
"INT",
"NOT NULL",
"AUTO_INCREMENT"
],
"email" => [
"VARCHAR(70)",
"NOT NULL",
"UNIQUE"
]
], "TABLESPACE tablespace_name");
$this->assertQuery(
[
'default' => <<<EOD
CREATE TABLE IF NOT EXISTS "account"
("id" INT NOT NULL AUTO_INCREMENT,
"email" VARCHAR(70) NOT NULL UNIQUE)
TABLESPACE tablespace_name
EOD,
'mssql' => <<<EOD
CREATE TABLE [account]
([id] INT NOT NULL AUTO_INCREMENT,
[email] VARCHAR(70) NOT NULL UNIQUE)
TABLESPACE tablespace_name
EOD,
'oracle' => <<<EOD
CREATE TABLE "account"
("id" INT NOT NULL AUTO_INCREMENT,
"email" VARCHAR(70) NOT NULL UNIQUE)
TABLESPACE tablespace_name
EOD
],
$this->database->queryString
);
}
}

View file

@ -0,0 +1,59 @@
<?php
namespace Medoo\Tests;
use Medoo\Medoo;
/**
* @coversDefaultClass \Medoo\Medoo
*/
class DeleteTest extends MedooTestCase
{
/**
* @covers ::delete()
* @dataProvider typesProvider
*/
public function testDelete($type)
{
$this->setType($type);
$this->database->delete("account", [
"AND" => [
"type" => "business",
"age[<]" => 18
]
]);
$this->assertQuery(
<<<EOD
DELETE FROM "account"
WHERE ("type" = 'business' AND "age" < 18)
EOD,
$this->database->queryString
);
}
/**
* @covers ::delete()
* @dataProvider typesProvider
*/
public function testDeleteRaw($type)
{
$this->setType($type);
$whereClause = Medoo::raw("WHERE (<type> = :type AND <age> < :age)", [
':type' => 'business',
':age' => 18,
]);
$this->database->delete("account", $whereClause);
$this->assertQuery(
<<<EOD
DELETE FROM "account"
WHERE ("type" = 'business' AND "age" < 18)
EOD,
$this->database->queryString
);
}
}

View file

@ -0,0 +1,51 @@
<?php
namespace Medoo\Tests;
use Medoo\Medoo;
/**
* @coversDefaultClass \Medoo\Medoo
*/
class DropTest extends MedooTestCase
{
/**
* @covers ::drop()
* @dataProvider typesProvider
*/
public function testDrop($type)
{
$this->setType($type);
$this->database->drop("account");
$this->assertQuery(
<<<EOD
DROP TABLE IF EXISTS "account"
EOD,
$this->database->queryString
);
}
/**
* @covers ::drop()
*/
public function testDropWithPrefix()
{
$database = new Medoo([
'testMode' => true,
'prefix' => 'PREFIX_'
]);
$database->type = "sqlite";
$database->drop("account");
$this->assertQuery(
<<<EOD
DROP TABLE IF EXISTS "PREFIX_account"
EOD,
$database->queryString
);
}
}

163
include/thirdparty/db/tests/GetTest.php vendored Normal file
View file

@ -0,0 +1,163 @@
<?php
namespace Medoo\Tests;
/**
* @coversDefaultClass \Medoo\Medoo
*/
class GetTest extends MedooTestCase
{
/**
* @covers ::get()
* @dataProvider typesProvider
*/
public function testGet($type)
{
$this->setType($type);
$this->database->get("account", "email", [
"user_id" => 1234
]);
$this->assertQuery([
'default' => <<<EOD
SELECT "email"
FROM "account"
WHERE "user_id" = 1234
LIMIT 1
EOD,
'mssql' => <<<EOD
SELECT [email]
FROM [account]
WHERE [user_id] = 1234
ORDER BY (SELECT 0)
OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY
EOD,
'oracle' => <<<EOD
SELECT "email"
FROM "account"
WHERE "user_id" = 1234
OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY
EOD,
], $this->database->queryString);
}
/**
* @covers ::get()
* @dataProvider typesProvider
*/
public function testGetWithColumns($type)
{
$this->setType($type);
$this->database->get("account", [
"email",
"location"
], [
"user_id" => 1234
]);
$this->assertQuery([
'default' => <<<EOD
SELECT "email","location"
FROM "account"
WHERE "user_id" = 1234
LIMIT 1
EOD,
'mssql' => <<<EOD
SELECT [email],[location]
FROM [account]
WHERE [user_id] = 1234
ORDER BY (SELECT 0)
OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY
EOD,
'oracle' => <<<EOD
SELECT "email","location"
FROM "account"
WHERE "user_id" = 1234
OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY
EOD,
], $this->database->queryString);
}
/**
* @covers ::get()
* @dataProvider typesProvider
*/
public function testGetWithJoin($type)
{
$this->setType($type);
$this->database->get("post", [
"[>]account" => "user_id"
], [
"post.content",
"account.user_name"
]);
$this->assertQuery([
'default' => <<<EOD
SELECT "post"."content","account"."user_name"
FROM "post"
LEFT JOIN "account" USING ("user_id")
LIMIT 1
EOD,
'mssql' => <<<EOD
SELECT [post].[content],[account].[user_name]
FROM [post]
LEFT JOIN [account] USING ([user_id])
ORDER BY (SELECT 0)
OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY
EOD,
'oracle' => <<<EOD
SELECT "post"."content","account"."user_name"
FROM "post"
LEFT JOIN "account" USING ("user_id")
OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY
EOD,
], $this->database->queryString);
}
/**
* @covers ::get()
* @dataProvider typesProvider
*/
public function testGetWithJoinAndWhere($type)
{
$this->setType($type);
$this->database->get("post", [
"[>]account" => "user_id"
], [
"post.content",
"account.user_name"
], [
'account.age[>]' => 18
]);
$this->assertQuery([
'default' => <<<EOD
SELECT "post"."content","account"."user_name"
FROM "post"
LEFT JOIN "account" USING ("user_id")
WHERE "account"."age" > 18
LIMIT 1
EOD,
'mssql' => <<<EOD
SELECT [post].[content],[account].[user_name]
FROM [post]
LEFT JOIN [account] USING ([user_id])
WHERE [account].[age] > 18
ORDER BY (SELECT 0)
OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY
EOD,
'oracle' => <<<EOD
SELECT "post"."content","account"."user_name"
FROM "post"
LEFT JOIN "account" USING ("user_id")
WHERE "account"."age" > 18
OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY
EOD,
], $this->database->queryString);
}
}

32
include/thirdparty/db/tests/HasTest.php vendored Normal file
View file

@ -0,0 +1,32 @@
<?php
namespace Medoo\Tests;
/**
* @coversDefaultClass \Medoo\Medoo
*/
class HasTest extends MedooTestCase
{
/**
* @covers ::has()
* @covers ::selectContext()
* @dataProvider typesProvider
*/
public function testHas($type)
{
$this->setType($type);
$this->database->has("account", [
"user_name" => "foo"
]);
$this->assertQuery([
'default' => <<<EOD
SELECT EXISTS(SELECT 1 FROM "account" WHERE "user_name" = 'foo')
EOD,
'mssql' => <<<EOD
SELECT TOP 1 1 FROM [account] WHERE [user_name] = 'foo'
EOD
], $this->database->queryString);
}
}

View file

@ -0,0 +1,242 @@
<?php
namespace Medoo\Tests;
use Medoo\Medoo;
/**
* @coversDefaultClass \Medoo\Medoo
*/
class InsertTest extends MedooTestCase
{
/**
* @covers ::insert()
* @covers ::typeMap()
* @dataProvider typesProvider
*/
public function testInsert($type)
{
$this->setType($type);
$this->database->insert("account", [
"user_name" => "foo",
"email" => "foo@bar.com"
]);
$this->assertQuery(
<<<EOD
INSERT INTO "account" ("user_name", "email")
VALUES ('foo', 'foo@bar.com')
EOD,
$this->database->queryString
);
}
/**
* @covers ::insert()
* @covers ::typeMap()
* @dataProvider typesProvider
*/
public function testInsertWithArray($type)
{
$this->setType($type);
$this->database->insert("account", [
"user_name" => "foo",
"lang" => ["en", "fr"]
]);
$this->assertQuery([
'default' => <<<EOD
INSERT INTO "account" ("user_name", "lang")
VALUES ('foo', 'a:2:{i:0;s:2:"en";i:1;s:2:"fr";}')
EOD,
'mysql' => <<<EOD
INSERT INTO "account" ("user_name", "lang")
VALUES ('foo', 'a:2:{i:0;s:2:\"en\";i:1;s:2:\"fr\";}')
EOD
], $this->database->queryString);
}
/**
* @covers ::insert()
* @covers ::typeMap()
* @dataProvider typesProvider
*/
public function testInsertWithJSON($type)
{
$this->setType($type);
$this->database->insert("account", [
"user_name" => "foo",
"lang [JSON]" => ["en", "fr"]
]);
$this->assertQuery([
'default' => <<<EOD
INSERT INTO "account" ("user_name", "lang")
VALUES ('foo', '["en","fr"]')
EOD,
'mysql' => <<<EOD
INSERT INTO `account` (`user_name`, `lang`)
VALUES ('foo', '[\"en\",\"fr\"]')
EOD
], $this->database->queryString);
}
/**
* @covers ::insert()
* @dataProvider typesProvider
*/
public function testInsertWithRaw($type)
{
$this->setType($type);
$this->database->insert("account", [
"user_name" => Medoo::raw("UUID()")
]);
$this->assertQuery(
<<<EOD
INSERT INTO "account" ("user_name")
VALUES (UUID())
EOD,
$this->database->queryString
);
}
/**
* @covers ::insert()
* @covers ::typeMap()
* @dataProvider typesProvider
*/
public function testInsertWithNull($type)
{
$this->setType($type);
$this->database->insert("account", [
"location" => null
]);
$this->assertQuery(
<<<EOD
INSERT INTO "account" ("location")
VALUES (NULL)
EOD,
$this->database->queryString
);
}
/**
* @covers ::insert()
* @covers ::typeMap()
* @dataProvider typesProvider
*/
public function testInsertWithObject($type)
{
$this->setType($type);
$objectData = new Foo();
$this->database->insert("account", [
"object" => $objectData
]);
$this->assertQuery(
<<<EOD
INSERT INTO "account" ("object")
VALUES (:MeD0_mK)
EOD,
$this->database->queryString
);
}
/**
* @covers ::insert()
* @dataProvider typesProvider
*/
public function testMultiInsert($type)
{
$this->setType($type);
$this->database->insert("account", [
[
"user_name" => "foo",
"email" => "foo@bar.com"
],
[
"user_name" => "bar",
"email" => "bar@foo.com"
]
]);
$this->assertQuery(
<<<EOD
INSERT INTO "account" ("user_name", "email")
VALUES ('foo', 'foo@bar.com'), ('bar', 'bar@foo.com')
EOD,
$this->database->queryString
);
}
public function testOracleWithPrimaryKeyInsert()
{
$this->setType("oracle");
$this->database->insert("ACCOUNT", [
"NAME" => "foo",
"EMAIL" => "foo@bar.com"
], "ID");
$this->assertQuery(
<<<EOD
INSERT INTO "ACCOUNT" ("NAME", "EMAIL")
VALUES ('foo', 'foo@bar.com')
RETURNING "ID" INTO :RETURNID
EOD,
$this->database->queryString
);
}
public function testOracleWithLOBsInsert()
{
$this->setType("oracle");
$fp = fopen('README.md', 'r');
$this->database->insert("ACCOUNT", [
"NAME" => "foo",
"DATA" => $fp
]);
$this->assertQuery(
<<<EOD
INSERT INTO "ACCOUNT" ("NAME", "DATA")
VALUES ('foo', EMPTY_BLOB())
RETURNING "DATA" INTO :MeD1_mK
EOD,
$this->database->queryString
);
}
public function testOracleWithLOBsAndIdInsert()
{
$this->setType("oracle");
$fp = fopen('README.md', 'r');
$this->database->insert("ACCOUNT", [
"NAME" => "foo",
"DATA" => $fp
], "ID");
$this->assertQuery(
<<<EOD
INSERT INTO "ACCOUNT" ("NAME", "DATA")
VALUES ('foo', EMPTY_BLOB())
RETURNING "DATA", "ID" INTO :MeD1_mK, :RETURNID
EOD,
$this->database->queryString
);
}
}

View file

@ -0,0 +1,82 @@
<?php
namespace Medoo\Tests;
use Medoo\Medoo;
use PHPUnit\Framework\TestCase;
class MedooTestCase extends TestCase
{
protected $database;
public $tableAliasConnector = ' AS ';
public $quotePattern = '"$1"';
public function setUp(): void
{
$this->database = new Medoo([
'testMode' => true
]);
}
public function typesProvider(): array
{
return [
'MySQL' => ['mysql'],
'MSSQL' => ['mssql'],
'SQLite' => ['sqlite'],
'PostgreSQL' => ['pgsql'],
'Oracle' => ['oracle']
];
}
public function setType($type): void
{
$this->database->setupType($type);
if ($type === 'oracle') {
$this->tableAliasConnector = ' ';
} elseif ($type === 'mysql') {
$this->quotePattern = '`$1`';
} elseif ($type === 'mssql') {
$this->quotePattern = '[$1]';
}
}
public function expectedQuery($expected): string
{
$result = preg_replace(
'/(?!\'[^\s]+\s?)"([\p{L}_][\p{L}\p{N}@$#\-_]*)"(?!\s?[^\s]+\')/u',
$this->quotePattern,
str_replace("\n", " ", $expected)
);
return str_replace(
' @AS ',
$this->tableAliasConnector,
$result
);
}
public function assertQuery($expected, $query): void
{
if (is_array($expected)) {
$this->assertEquals(
$this->expectedQuery($expected[$this->database->type] ?? $expected['default']),
$query
);
} else {
$this->assertEquals($this->expectedQuery($expected), $query);
}
}
}
class Foo
{
public $bar = "cat";
public function __wakeup()
{
$this->bar = "dog";
}
}

View file

@ -0,0 +1,152 @@
<?php
namespace Medoo\Tests;
use Medoo\Medoo;
/**
* @coversDefaultClass \Medoo\Medoo
*/
class QueryTest extends MedooTestCase
{
/**
* @covers ::query()
* @covers ::isRaw()
* @covers ::buildRaw()
* @dataProvider typesProvider
*/
public function testQuery($type)
{
$this->setType($type);
$this->database->query("SELECT <account.email>,<account.nickname> FROM <account> WHERE <id> != 100");
$this->assertQuery(
<<<EOD
SELECT "account"."email","account"."nickname"
FROM "account"
WHERE "id" != 100
EOD,
$this->database->queryString
);
}
/**
* @covers ::query()
* @covers ::isRaw()
* @covers ::buildRaw()
*/
public function testQueryWithPrefix()
{
$database = new Medoo([
'testMode' => true,
'prefix' => 'PREFIX_'
]);
$database->query("SELECT <account.name> FROM <account>");
$this->assertQuery(
<<<EOD
SELECT "PREFIX_account"."name"
FROM "PREFIX_account"
EOD,
$database->queryString
);
}
/**
* @covers ::query()
* @covers ::isRaw()
* @covers ::buildRaw()
*/
public function testQueryTableWithPrefix()
{
$database = new Medoo([
'testMode' => true,
'prefix' => 'PREFIX_'
]);
$database->query("DROP TABLE IF EXISTS <account>");
$this->assertQuery(
<<<EOD
DROP TABLE IF EXISTS "PREFIX_account"
EOD,
$database->queryString
);
}
/**
* @covers ::query()
* @covers ::isRaw()
* @covers ::buildRaw()
*/
public function testQueryShowTableWithPrefix()
{
$database = new Medoo([
'testMode' => true,
'prefix' => 'PREFIX_'
]);
$database->query("SHOW TABLES LIKE <account>");
$this->assertQuery(
<<<EOD
SHOW TABLES LIKE "PREFIX_account"
EOD,
$database->queryString
);
}
/**
* @covers ::query()
* @covers ::isRaw()
* @covers ::buildRaw()
* @dataProvider typesProvider
*/
public function testPreparedStatementQuery($type)
{
$this->setType($type);
$this->database->query(
"SELECT * FROM <account> WHERE <user_name> = :user_name AND <age> = :age",
[
":user_name" => "John Smite",
":age" => 20
]
);
$this->assertQuery(
<<<EOD
SELECT *
FROM "account"
WHERE "user_name" = 'John Smite' AND "age" = 20
EOD,
$this->database->queryString
);
}
/**
* @covers ::query()
* @covers ::isRaw()
* @covers ::buildRaw()
*/
public function testQueryEscape()
{
$database = new Medoo([
'testMode' => true,
'prefix' => 'PREFIX_'
]);
$database->query("SELECT * FROM <account> WHERE <name> = '<John>'");
$this->assertQuery(
<<<EOD
SELECT *
FROM "PREFIX_account"
WHERE "name" = '<John>'
EOD,
$database->queryString
);
}
}

View file

@ -0,0 +1,123 @@
<?php
namespace Medoo\Tests;
use Medoo\Medoo;
use InvalidArgumentException;
/**
* @coversDefaultClass \Medoo\Medoo
*/
class QuoteTest extends MedooTestCase
{
/**
* @covers ::quote()
* @dataProvider typesProvider
*/
public function testQuote($type)
{
$this->setType($type);
$quotedString = $this->database->quote("Co'mpl''ex \"st'\"ring");
$expected = [
'mysql' => <<<EOD
'Co\'mpl\'\'ex \"st\'\"ring'
EOD,
'mssql' => <<<EOD
'Co''mpl''''ex "st''"ring'
EOD,
'sqlite' => <<<EOD
'Co''mpl''''ex "st''"ring'
EOD,
'pgsql' => <<<EOD
'Co''mpl''''ex "st''"ring'
EOD,
'oracle' => <<<EOD
'Co''mpl''''ex "st''"ring'
EOD
];
$this->assertEquals($expected[$type], $quotedString);
}
/**
* @covers ::columnQuote()
*/
public function testColumnQuote()
{
$this->assertEquals('"ColumnName"', $this->database->columnQuote("ColumnName"));
$this->assertEquals('"Column"."name"', $this->database->columnQuote("Column.name"));
$this->assertEquals('"Column"."Name"', $this->database->columnQuote("Column.Name"));
$this->assertEquals('"ネーム"', $this->database->columnQuote("ネーム"));
}
public function columnNamesProvider(): array
{
return [
["9ColumnName"],
["@ColumnName"],
[".ColumnName"],
["ColumnName."],
["ColumnName (alias)"]
];
}
/**
* @covers ::columnQuote()
* @dataProvider columnNamesProvider
*/
public function testIncorrectColumnQuote($column)
{
$this->expectException(InvalidArgumentException::class);
$this->database->columnQuote($column);
}
/**
* @covers ::tableQuote()
*/
public function testTableQuote()
{
$this->assertEquals('"TableName"', $this->database->tableQuote("TableName"));
$this->assertEquals('"_table"', $this->database->tableQuote("_table"));
$this->assertEquals('"アカウント"', $this->database->tableQuote("アカウント"));
}
/**
* @covers ::tableQuote()
*/
public function testPrefixTableQuote()
{
$database = new Medoo([
'testMode' => true,
'prefix' => 'PREFIX_'
]);
$this->assertEquals('"PREFIX_TableName"', $database->tableQuote("TableName"));
}
public function tableNamesProvider(): array
{
return [
["9TableName"],
["@TableName"],
[".TableName"],
["TableName."],
["Table.name"]
];
}
/**
* @covers ::tableQuote()
* @dataProvider tableNamesProvider
*/
public function testIncorrectTableQuote($table)
{
$this->expectException(InvalidArgumentException::class);
$this->database->tableQuote($table);
}
}

153
include/thirdparty/db/tests/RandTest.php vendored Normal file
View file

@ -0,0 +1,153 @@
<?php
namespace Medoo\Tests;
/**
* @coversDefaultClass \Medoo\Medoo
*/
class RandTest extends MedooTestCase
{
/**
* @covers ::rand()
* @dataProvider typesProvider
*/
public function testRand($type)
{
$this->setType($type);
$this->database->rand("account", [
"user_name"
]);
$this->assertQuery([
'default' => <<<EOD
SELECT "user_name"
FROM "account"
ORDER BY RANDOM()
EOD,
'mysql' => <<<EOD
SELECT `user_name`
FROM `account`
ORDER BY RAND()
EOD,
'mssql' => <<<EOD
SELECT [user_name]
FROM [account]
ORDER BY NEWID()
EOD
], $this->database->queryString);
}
/**
* @covers ::rand()
* @dataProvider typesProvider
*/
public function testWhereRand($type)
{
$this->setType($type);
$this->database->rand("account", [
"user_name"
], [
"location" => "Tokyo"
]);
$this->assertQuery([
'default' => <<<EOD
SELECT "user_name"
FROM "account"
WHERE "location" = 'Tokyo'
ORDER BY RANDOM()
EOD,
'mysql' => <<<EOD
SELECT `user_name`
FROM `account`
WHERE `location` = 'Tokyo'
ORDER BY RAND()
EOD,
'mssql' => <<<EOD
SELECT [user_name]
FROM [account]
WHERE [location] = 'Tokyo'
ORDER BY NEWID()
EOD
], $this->database->queryString);
}
/**
* @covers ::rand()
* @dataProvider typesProvider
*/
public function testWhereWithJoinRand($type)
{
$this->setType($type);
$this->database->rand("account", [
"[>]album" => "user_id"
], [
"account.user_name"
], [
"album.location" => "Tokyo"
]);
$this->assertQuery([
'default' => <<<EOD
SELECT "account"."user_name"
FROM "account"
LEFT JOIN "album" USING ("user_id")
WHERE "album"."location" = 'Tokyo'
ORDER BY RANDOM()
EOD,
'mysql' => <<<EOD
SELECT `account`.`user_name`
FROM `account`
LEFT JOIN `album` USING (`user_id`)
WHERE `album`.`location` = 'Tokyo'
ORDER BY RAND()
EOD,
'mssql' => <<<EOD
SELECT [account].[user_name]
FROM [account]
LEFT JOIN [album] USING ([user_id])
WHERE [album].[location] = 'Tokyo'
ORDER BY NEWID()
EOD
], $this->database->queryString);
}
/**
* @covers ::rand()
* @dataProvider typesProvider
*/
public function testWithJoinRand($type)
{
$this->setType($type);
$this->database->rand("account", [
"[>]album" => "user_id"
], [
"account.user_name"
]);
$this->assertQuery([
'default' => <<<EOD
SELECT "account"."user_name"
FROM "account"
LEFT JOIN "album" USING ("user_id")
ORDER BY RANDOM()
EOD,
'mysql' => <<<EOD
SELECT `account`.`user_name`
FROM `account`
LEFT JOIN `album` USING (`user_id`)
ORDER BY RAND()
EOD,
'mssql' => <<<EOD
SELECT [account].[user_name]
FROM [account]
LEFT JOIN [album] USING ([user_id])
ORDER BY NEWID()
EOD
], $this->database->queryString);
}
}

57
include/thirdparty/db/tests/RawTest.php vendored Normal file
View file

@ -0,0 +1,57 @@
<?php
namespace Medoo\Tests;
use Medoo\Medoo;
/**
* @coversDefaultClass \Medoo\Medoo
*/
class RawTest extends MedooTestCase
{
/**
* @covers ::raw()
* @covers ::isRaw()
* @covers ::buildRaw()
* @dataProvider typesProvider
*/
public function testRawWithPlaceholder($type)
{
$this->setType($type);
$this->database->select('account', [
'score' => Medoo::raw('SUM(<age> + <experience>)')
]);
$this->assertQuery(
<<<EOD
SELECT SUM("age" + "experience") AS "score"
FROM "account"
EOD,
$this->database->queryString
);
}
/**
* @covers ::raw()
* @covers ::isRaw()
* @covers ::buildRaw()
* @dataProvider typesProvider
*/
public function testRawWithSamePlaceholderName($type)
{
$this->setType($type);
$this->database->select('account', [
'system' => Medoo::raw("COUNT(<system> = 'window' OR <system> = 'mac')")
]);
$this->assertQuery(
<<<EOD
SELECT COUNT("system" = 'window' OR "system" = 'mac') AS "system"
FROM "account"
EOD,
$this->database->queryString
);
}
}

View file

@ -0,0 +1,55 @@
<?php
namespace Medoo\Tests;
use InvalidArgumentException;
/**
* @coversDefaultClass \Medoo\Medoo
*/
class ReplaceTest extends MedooTestCase
{
/**
* @covers ::replace()
* @dataProvider typesProvider
*/
public function testReplace($type)
{
$this->setType($type);
$this->database->replace("account", [
"type" => [
"user" => "new_user",
"business" => "new_business"
],
"column" => [
"old_value" => "new_value"
]
], [
"user_id[>]" => 1000
]);
$this->assertQuery(
<<<EOD
UPDATE "account"
SET "type" = REPLACE("type", 'user', 'new_user'),
"type" = REPLACE("type", 'business', 'new_business'),
"column" = REPLACE("column", 'old_value', 'new_value')
WHERE "user_id" > 1000
EOD,
$this->database->queryString
);
}
/**
* @covers ::replace()
*/
public function testReplaceEmptyColumns()
{
$this->expectException(InvalidArgumentException::class);
$this->database->replace("account", [], [
"user_id[>]" => 1000
]);
}
}

View file

@ -0,0 +1,715 @@
<?php
namespace Medoo\Tests;
use Medoo\Medoo;
use InvalidArgumentException;
/**
* @coversDefaultClass \Medoo\Medoo
*/
class SelectTest extends MedooTestCase
{
/**
* @covers ::select()
* @covers ::selectContext()
* @covers ::isJoin()
* @covers ::columnMap()
* @covers ::columnPush()
* @dataProvider typesProvider
*/
public function testSelectAll($type)
{
$this->setType($type);
$this->database->select("account", "*");
$this->assertQuery(
<<<EOD
SELECT * FROM "account"
EOD,
$this->database->queryString
);
}
/**
* @covers ::select()
* @covers ::selectContext()
* @dataProvider typesProvider
*/
public function testSelectTableWithAlias($type)
{
$this->setType($type);
$this->database->select("account (user)", "name");
$this->assertQuery(
<<<EOD
SELECT "name"
FROM "account" @AS "user"
EOD,
$this->database->queryString
);
}
/**
* @covers ::columnMap()
* @covers ::columnPush()
* @dataProvider typesProvider
*/
public function testSelectSingleColumn($type)
{
$this->setType($type);
$this->database->select("account", "name");
$this->assertQuery(
<<<EOD
SELECT "name"
FROM "account"
EOD,
$this->database->queryString
);
}
/**
* @covers ::columnMap()
* @covers ::columnPush()
* @dataProvider typesProvider
*/
public function testSelectColumns($type)
{
$this->setType($type);
$this->database->select("account", ["name", "id"]);
$this->assertQuery(
<<<EOD
SELECT "name","id"
FROM "account"
EOD,
$this->database->queryString
);
}
/**
* @covers ::columnMap()
* @covers ::columnPush()
* @dataProvider typesProvider
*/
public function testSelectColumnsWithAlias($type)
{
$this->setType($type);
$this->database->select("account", ["name(nickname)", "id"]);
$this->assertQuery(
<<<EOD
SELECT "name" AS "nickname","id"
FROM "account"
EOD,
$this->database->queryString
);
}
/**
* @covers ::columnMap()
* @covers ::columnPush()
* @dataProvider typesProvider
*/
public function testSelectColumnsWithType($type)
{
$this->setType($type);
$this->database->select("account", ["name[String]", "data [JSON]"]);
$this->assertQuery(
<<<EOD
SELECT "name","data"
FROM "account"
EOD,
$this->database->queryString
);
}
/**
* @covers ::columnMap()
* @covers ::columnPush()
* @dataProvider typesProvider
*/
public function testSelectColumnsWithAliasAndType($type)
{
$this->setType($type);
$this->database->select("account", ["name (nickname) [String]", "data [JSON]"]);
$this->assertQuery(
<<<EOD
SELECT "name" AS "nickname","data"
FROM "account"
EOD,
$this->database->queryString
);
}
/**
* @covers ::columnMap()
* @covers ::columnPush()
* @dataProvider typesProvider
*/
public function testSelectColumnsWithRaw($type)
{
$this->setType($type);
$this->database->select("account", [
"id [String]" => Medoo::raw("UUID()")
]);
$this->assertQuery(
<<<EOD
SELECT UUID() AS "id"
FROM "account"
EOD,
$this->database->queryString
);
}
/**
* @covers ::select()
* @covers ::selectContext()
* @covers ::isJoin()
* @dataProvider typesProvider
*/
public function testSelectWithWhere($type)
{
$this->setType($type);
$this->database->select("account", [
"name",
"id"
], [
"ORDER" => "age"
]);
$this->assertQuery(
<<<EOD
SELECT "name","id"
FROM "account"
ORDER BY "age"
EOD,
$this->database->queryString
);
}
/**
* @covers ::select()
* @covers ::selectContext()
* @covers ::isJoin()
* @covers ::buildJoin()
* @dataProvider typesProvider
*/
public function testSelectWithLeftJoin($type)
{
$this->setType($type);
$this->database->select("account", [
"[>]post" => "user_id"
], [
"account.name",
"post.title"
]);
$this->assertQuery(
<<<EOD
SELECT "account"."name","post"."title"
FROM "account"
LEFT JOIN "post"
USING ("user_id")
EOD,
$this->database->queryString
);
}
/**
* @covers ::isJoin()
* @covers ::buildJoin()
* @dataProvider typesProvider
*/
public function testSelectWithRightJoin($type)
{
$this->setType($type);
$this->database->select("account", [
"[<]post" => "user_id"
], [
"account.name",
"post.title"
]);
$this->assertQuery(
<<<EOD
SELECT "account"."name","post"."title"
FROM "account"
RIGHT JOIN "post"
USING ("user_id")
EOD,
$this->database->queryString
);
}
/**
* @covers ::isJoin()
* @covers ::buildJoin()
* @dataProvider typesProvider
*/
public function testSelectWithFullJoin($type)
{
$this->setType($type);
$this->database->select("account", [
"[<>]post" => "user_id"
], [
"account.name",
"post.title"
]);
$this->assertQuery(
<<<EOD
SELECT "account"."name","post"."title"
FROM "account"
FULL JOIN "post"
USING ("user_id")
EOD,
$this->database->queryString
);
}
/**
* @covers ::isJoin()
* @covers ::buildJoin()
* @dataProvider typesProvider
*/
public function testSelectWithInnerJoin($type)
{
$this->setType($type);
$this->database->select("account", [
"[><]post" => "user_id"
], [
"account.name",
"post.title"
]);
$this->assertQuery(
<<<EOD
SELECT "account"."name","post"."title"
FROM "account"
INNER JOIN "post"
USING ("user_id")
EOD,
$this->database->queryString
);
}
/**
* @covers ::isJoin()
* @covers ::buildJoin()
* @dataProvider typesProvider
*/
public function testSelectWithSameKeysJoin($type)
{
$this->setType($type);
$this->database->select("account", [
"[>]photo" => ["user_id", "avatar_id"],
], [
"account.name",
"photo.link"
]);
$this->assertQuery(
<<<EOD
SELECT "account"."name","photo"."link"
FROM "account"
LEFT JOIN "photo"
USING ("user_id", "avatar_id")
EOD,
$this->database->queryString
);
}
/**
* @covers ::isJoin()
* @covers ::buildJoin()
* @dataProvider typesProvider
*/
public function testSelectWithKeyJoin($type)
{
$this->setType($type);
$this->database->select("account", [
"[>]post" => ["user_id" => "author_id"],
], [
"account.name",
"post.title"
]);
$this->assertQuery(
<<<EOD
SELECT "account"."name","post"."title"
FROM "account"
LEFT JOIN "post"
ON "account"."user_id" = "post"."author_id"
EOD,
$this->database->queryString
);
}
/**
* @covers ::isJoin()
* @covers ::buildJoin()
* @dataProvider typesProvider
*/
public function testSelectWithAliasJoin($type)
{
$this->setType($type);
$this->database->select("account", [
"[>]post (main_post)" => ["user_id" => "author_id"],
], [
"account.name",
"main_post.title"
]);
$this->assertQuery(
<<<EOD
SELECT "account"."name","main_post"."title"
FROM "account"
LEFT JOIN "post" @AS "main_post"
ON "account"."user_id" = "main_post"."author_id"
EOD,
$this->database->queryString
);
}
/**
* @covers ::isJoin()
* @covers ::buildJoin()
* @dataProvider typesProvider
*/
public function testSelectWithReferJoin($type)
{
$this->setType($type);
$this->database->select("account", [
"[>]post" => ["user_id" => "author_id"],
"[>]album" => ["post.author_id" => "user_id"],
], [
"account.name",
"post.title",
"album.link"
]);
$this->assertQuery(
<<<EOD
SELECT "account"."name","post"."title","album"."link"
FROM "account"
LEFT JOIN "post"
ON "account"."user_id" = "post"."author_id"
LEFT JOIN "album"
ON "post"."author_id" = "album"."user_id"
EOD,
$this->database->queryString
);
}
/**
* @covers ::isJoin()
* @covers ::buildJoin()
* @dataProvider typesProvider
*/
public function testSelectWithMultipleConditionJoin($type)
{
$this->setType($type);
$this->database->select("account", [
"[>]album" => ["author_id" => "user_id"],
"[>]post" => [
"user_id" => "author_id",
"album.user_id" => "owner_id"
]
], [
"account.name",
"post.title",
"album.link"
]);
$this->assertQuery(
<<<EOD
SELECT "account"."name","post"."title","album"."link"
FROM "account"
LEFT JOIN "album"
ON "account"."author_id" = "album"."user_id"
LEFT JOIN "post"
ON "account"."user_id" = "post"."author_id"
AND "album"."user_id" = "post"."owner_id"
EOD,
$this->database->queryString
);
}
/**
* @covers ::isJoin()
* @covers ::buildJoin()
* @dataProvider typesProvider
*/
public function testSelectWithAdditionalConditionJoin($type)
{
$this->setType($type);
$this->database->select("account", [
"[>]post" => [
"user_id" => "author_id",
"AND" => [
"post.id[>]" => 10
]
]
], [
"account.name",
"post.title"
]);
$this->assertQuery(
<<<EOD
SELECT "account"."name","post"."title"
FROM "account"
LEFT JOIN "post"
ON "account"."user_id" = "post"."author_id"
AND "post"."id" > 10
EOD,
$this->database->queryString
);
}
/**
* @covers ::isJoin()
* @covers ::buildJoin()
* @dataProvider typesProvider
*/
public function testSelectRawJoin($type)
{
$this->setType($type);
$this->database->select("account", [
"[>]post" => Medoo::raw("ON <account.user_id> = <post.author_id>")
], [
"account.name",
"post.title"
]);
$this->assertQuery(
<<<EOD
SELECT "account"."name","post"."title"
FROM "account"
LEFT JOIN "post"
ON "account"."user_id" = "post"."author_id"
EOD,
$this->database->queryString
);
}
/**
* @covers ::columnMap()
* @covers ::columnPush()
* @dataProvider typesProvider
*/
public function testSelectAllWithJoin($type)
{
$this->setType($type);
$this->expectException(InvalidArgumentException::class);
$this->database->select("account", [
"[>]post" => "user_id"
], [
"account.*"
]);
}
/**
* @covers ::columnMap()
* @covers ::columnPush()
* @dataProvider typesProvider
*/
public function testSelectWithDataMapping($type)
{
$this->setType($type);
$this->database->select("post", [
"[>]account" => ["user_id"]
], [
"post.content",
"userData" => [
"account.user_id",
"account.email",
"meta" => [
"account.location",
"account.gender"
]
]
]);
$this->assertQuery(
<<<EOD
SELECT "post"."content","account"."user_id","account"."email","account"."location","account"."gender"
FROM "post"
LEFT JOIN "account"
USING ("user_id")
EOD,
$this->database->queryString
);
}
/**
* @covers ::columnMap()
* @covers ::columnPush()
* @dataProvider typesProvider
*/
public function testSelectWithIndexMapping($type)
{
$this->setType($type);
$this->database->select("account", [
"user_id" => [
"name (nickname)",
"location"
]
]);
$this->assertQuery(
<<<EOD
SELECT "user_id","name" AS "nickname","location"
FROM "account"
EOD,
$this->database->queryString
);
}
/**
* @covers ::columnMap()
* @covers ::columnPush()
* @dataProvider typesProvider
*/
public function testSelectWithDistinct($type)
{
$this->setType($type);
$this->database->select("account", [
"@location",
"nickname"
]);
$this->assertQuery(
<<<EOD
SELECT DISTINCT "location","nickname"
FROM "account"
EOD,
$this->database->queryString
);
}
/**
* @covers ::columnMap()
* @covers ::columnPush()
* @dataProvider typesProvider
*/
public function testSelectWithDistinctDiffOrder($type)
{
$this->setType($type);
$this->database->select("account", [
"location",
"@nickname"
]);
$this->assertQuery(
<<<EOD
SELECT DISTINCT "nickname","location"
FROM "account"
EOD,
$this->database->queryString
);
}
/**
* @covers ::columnMap()
* @covers ::columnPush()
* @dataProvider typesProvider
*/
public function testSelectWithUnicodeCharacter($type)
{
$this->setType($type);
$this->database->select("considérer", [
"name (名前)",
"положение (ロケーション)"
]);
$this->assertQuery(
<<<EOD
SELECT "name" AS "名前","положение" AS "ロケーション"
FROM "considérer"
EOD,
$this->database->queryString
);
}
/**
* @covers ::columnMap()
* @covers ::columnPush()
* @dataProvider typesProvider
*/
public function testSelectWithHyphenCharacter($type)
{
$this->setType($type);
$this->database->select("account", [
"nick-name"
]);
$this->assertQuery(
<<<EOD
SELECT "nick-name"
FROM "account"
EOD,
$this->database->queryString
);
}
/**
* @covers ::columnMap()
* @covers ::columnPush()
* @dataProvider typesProvider
*/
public function testSelectWithSingleCharacter($type)
{
$this->setType($type);
$this->database->select("a", [
"[>]e" => ["f"]
], [
"b (c)"
]);
$this->assertQuery(
<<<EOD
SELECT "b" AS "c"
FROM "a"
LEFT JOIN "e" USING ("f")
EOD,
$this->database->queryString
);
}
}

View file

@ -0,0 +1,91 @@
<?php
namespace Medoo\Tests;
use Medoo\Medoo;
/**
* @coversDefaultClass \Medoo\Medoo
*/
class UpdateTest extends MedooTestCase
{
/**
* @covers \Medoo\Medoo::update()
* @dataProvider typesProvider
*/
public function testUpdate($type)
{
$this->setType($type);
$objectData = new Foo();
$this->database->update("account", [
"type" => "user",
"money" => 23.2,
"age[+]" => 1,
"level[-]" => 5,
"score[*]" => 2,
"lang" => ["en", "fr"],
"lang [JSON]" => ["en", "fr"],
"is_locked" => true,
"uuid" => Medoo::raw("UUID()"),
"object" => $objectData
], [
"user_id[<]" => 1000
]);
$this->assertQuery([
'default' => <<<EOD
UPDATE "account"
SET "type" = 'user',
"money" = '23.2',
"age" = "age" + 1,
"level" = "level" - 5,
"score" = "score" * 2,
"lang" = 'a:2:{i:0;s:2:"en";i:1;s:2:"fr";}',
"lang" = '["en","fr"]',
"is_locked" = 1,
"uuid" = UUID(),
"object" = :MeD5_mK
WHERE "user_id" < 1000
EOD,
'mysql' => <<<EOD
UPDATE "account"
SET "type" = 'user',
"money" = '23.2',
"age" = "age" + 1,
"level" = "level" - 5,
"score" = "score" * 2,
"lang" = 'a:2:{i:0;s:2:\"en\";i:1;s:2:\"fr\";}',
"lang" = '[\"en\",\"fr\"]',
"is_locked" = 1,
"uuid" = UUID(),
"object" = :MeD5_mK
WHERE "user_id" < 1000
EOD,
], $this->database->queryString);
}
public function testOracleLOBsUpdate()
{
$this->setType("oracle");
$fp = fopen('README.md', 'r');
$this->database->update("ACCOUNT", [
"DATA" => $fp
], [
"ID" => 1
]);
$this->assertQuery(
<<<EOD
UPDATE "ACCOUNT"
SET "DATA" = EMPTY_BLOB()
WHERE "ID" = 1
RETURNING "DATA" INTO :MeD0_mK
EOD,
$this->database->queryString
);
}
}

1177
include/thirdparty/db/tests/WhereTest.php vendored Normal file

File diff suppressed because it is too large Load diff