From 1a995285e9f7a21eb4d8aed9f6c5d62725b2e995 Mon Sep 17 00:00:00 2001 From: Travis Tegen Date: Tue, 26 Apr 2022 16:09:38 -0400 Subject: [PATCH] update for Laravel 9 --- .editorconfig | 15 + .travis.yml | 3 +- composer.json | 19 +- readme.md | 18 +- .../OracleDB/Connectors/OracleConnector.php | 8 +- src/Jfelder/OracleDB/OCI_PDO/OCI.php | 105 +-- src/Jfelder/OracleDB/OCI_PDO/OCIStatement.php | 243 +++--- .../OracleDB/Query/Grammars/OracleGrammar.php | 4 +- src/Jfelder/OracleDB/Query/OracleBuilder.php | 2 +- .../Query/Processors/OracleProcessor.php | 6 +- .../Schema/Grammars/OracleGrammar.php | 8 +- src/config/.gitkeep | 0 tests/.gitkeep | 0 tests/OracleDBConnectorTest.php | 46 +- tests/OracleDBOCIProcessorTest.php | 9 +- tests/OracleDBOCIStatementTest.php | 269 +++--- tests/OracleDBOCITest.php | 57 +- tests/OracleDBPDOProcessorTest.php | 5 +- tests/OracleDBQueryBuilderTest.php | 789 ++++++++++++++++-- tests/OracleDBSchemaGrammarTest.php | 44 +- tests/mocks/OCIFunctions.php | 19 +- tests/mocks/OCIMocks.php | 19 +- tests/mocks/PDOMocks.php | 2 +- 23 files changed, 1185 insertions(+), 505 deletions(-) create mode 100644 .editorconfig delete mode 100644 src/config/.gitkeep delete mode 100644 tests/.gitkeep diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..6537ca4 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 4 +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false + +[*.{yml,yaml}] +indent_size = 2 diff --git a/.travis.yml b/.travis.yml index bbb7d1c..f6ca083 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,7 @@ language: php php: - - 7.3 - - 7.4 + - 8.1 env: global: diff --git a/composer.json b/composer.json index f817a7c..7f2e312 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "jfelder/oracledb", "description": "Oracle DB driver for Laravel", - "keywords": ["oracle", "laravel", "laravel 8", "pdo_oci", "oci8"], + "keywords": ["oracle", "laravel", "laravel 9", "pdo_oci", "oci8"], "license": "MIT", "authors": [ { @@ -10,20 +10,25 @@ } ], "require": { - "php": "^7.3.0", - "illuminate/support": "^8.0", - "illuminate/database": "^8.24.0", - "illuminate/pagination": "^8.0" + "php": "^8.1.0", + "illuminate/support": "^9.0", + "illuminate/database": "^9.0", + "illuminate/pagination": "^9.0" }, "require-dev": { - "mockery/mockery": "^1.4.2", - "phpunit/phpunit": "^9.3.3" + "mockery/mockery": "^1.4.4", + "phpunit/phpunit": "^9.5.8" }, "autoload": { "psr-4": { "Jfelder\\": "src/Jfelder" } }, + "autoload-dev": { + "psr-4": { + "Jfelder\\OracleDB\\Tests\\": "tests/" + } + }, "extra": { "laravel": { "providers": [ diff --git a/readme.md b/readme.md index 3e175fe..efa1007 100644 --- a/readme.md +++ b/readme.md @@ -1,13 +1,13 @@ ## Laravel Oracle Database Package -### OracleDB (updated for Laravel 8.x) +### OracleDB (updated for Laravel 9) [![Latest Stable Version](https://poser.pugx.org/jfelder/oracledb/v/stable.png)](https://packagist.org/packages/jfelder/oracledb) [![Total Downloads](https://poser.pugx.org/jfelder/oracledb/downloads.png)](https://packagist.org/packages/jfelder/oracledb) [![Build Status](https://travis-ci.org/jfelder/Laravel-OracleDB.png)](https://travis-ci.org/jfelder/Laravel-OracleDB) OracleDB is an Oracle Database Driver package for [Laravel Framework](https://laravel.com) - thanks [@taylorotwell](https://github.com/taylorotwell). OracleDB is an extension of [Illuminate/Database](https://github.com/illuminate/database) that uses either the [PDO_OCI](https://www.php.net/manual/en/ref.pdo-oci.php) extension or the [OCI8 Functions](https://www.php.net/manual/en/ref.oci8.php) wrapped into the PDO namespace. -_NOTE: This package has not been tested in PHP 8._ +> **Note:** This package is designed to run in PHP 8.1, and has not been tested in PHP 8.0 **Please report any bugs you may find.** @@ -55,7 +55,10 @@ connection into the "Default Database Connection Name" section in `config/databa Once you have configured the OracleDB database connection(s), you may run queries using the `DB` facade as normal. -_NOTE: OCI8 is the default driver. If you want to use the PDO_OCI driver, change the `driver` value to `'pdo'` in the `config/oracledb.php` file for whichever connections you wish to have utilize PDO_OCI. Setting the driver to `'pdo'` will make OracleDB use the [PDO_OCI](https://www.php.net/manual/en/ref.pdo-oci.php) extension. Given any other `driver` value, OracleDB will use the [OCI8 Functions](https://www.php.net/manual/en/ref.oci8.php)._ +> **Note:** The default driver, `'oci8'`, makes OracleDB use the +[OCI8 Functions](https://www.php.net/manual/en/ref.oci8.php) under the hood. If you want to use +[PDO_OCI](https://www.php.net/manual/en/ref.pdo-oci.php) instead, change the `driver` value to `'pdo'` in the +`config/oracledb.php` file. ```php $results = DB::select('select * from users where id = ?', [1]); @@ -74,15 +77,15 @@ in config/oracledb.php file. #### Inserting Records Into A Table With An Auto-Incrementing ID ```php - $id = DB::connection('oracle')->table('users')->insertGetId( - ['email' => 'john@example.com', 'votes' => 0], 'userid' - ); +$id = DB::connection('oracle')->table('users')->insertGetId( + ['email' => 'john@example.com', 'votes' => 0], 'userid' +); ``` > **Note:** When using the insertGetId method, you can specify the auto-incrementing column name as the second parameter in insertGetId function. It will default to "id" if not specified. -See [Laravel Database Basic Docs](https://laravel.com/docs/8.x/database) for more information. +See [Laravel Database Basic Docs](https://laravel.com/docs/9.x/database) for more information. ### Unimplemented Features @@ -98,6 +101,7 @@ features not already listed. - deleting with a join `DB::from('users')->join('contacts', 'users.id', '=', 'contacts.id')->where('users.email', '=', 'foo')->delete();` - deleting with a limit `DB::from('users')->where('email', '=', 'foo')->orderBy('id')->take(1)->delete();` - json operations `DB::from('users')->where('items->sku', '=', 'foo-bar')->get();` +- whereFulltext `DB::table('users')->whereFulltext('description', 'Hello World');` #### Schema Builder diff --git a/src/Jfelder/OracleDB/Connectors/OracleConnector.php b/src/Jfelder/OracleDB/Connectors/OracleConnector.php index 4b185eb..539e66d 100644 --- a/src/Jfelder/OracleDB/Connectors/OracleConnector.php +++ b/src/Jfelder/OracleDB/Connectors/OracleConnector.php @@ -2,6 +2,7 @@ namespace Jfelder\OracleDB\Connectors; +use InvalidArgumentException; use Illuminate\Database\Connectors\Connector as Connector; use Illuminate\Database\Connectors\ConnectorInterface as ConnectorInterface; use Jfelder\OracleDB\OCI_PDO\OCI as OCI; @@ -26,14 +27,17 @@ class OracleConnector extends Connector implements ConnectorInterface * @param array $config * @param array $options * @return PDO + * @throws InvalidArgumentException */ public function createConnection($dsn, array $config, array $options) { - if ($config['driver'] == 'pdo') { + if ($config['driver'] === 'pdo') { return parent::createConnection($dsn, $config, $options); - } else { + } elseif ($config['driver'] === 'oci8') { return new OCI($dsn, $config['username'], $config['password'], $options, $config['charset']); } + + throw new InvalidArgumentException('Unsupported driver ['.$config['driver'].'].'); } /** diff --git a/src/Jfelder/OracleDB/OCI_PDO/OCI.php b/src/Jfelder/OracleDB/OCI_PDO/OCI.php index 76e2619..4ac1f97 100644 --- a/src/Jfelder/OracleDB/OCI_PDO/OCI.php +++ b/src/Jfelder/OracleDB/OCI_PDO/OCI.php @@ -2,7 +2,9 @@ namespace Jfelder\OracleDB\OCI_PDO; -class OCI extends \PDO +use PDO; + +class OCI extends PDO { /** * @var string @@ -35,10 +37,11 @@ class OCI extends \PDO /** * @var array Database connection attributes */ - protected $attributes = [\PDO::ATTR_AUTOCOMMIT => 1, - \PDO::ATTR_ERRMODE => 0, - \PDO::ATTR_CASE => 0, - \PDO::ATTR_ORACLE_NULLS => 0, + protected $attributes = [ + PDO::ATTR_AUTOCOMMIT => 1, + PDO::ATTR_ERRMODE => 0, + PDO::ATTR_CASE => 0, + PDO::ATTR_ORACLE_NULLS => 0, ]; /** @@ -90,13 +93,13 @@ public function __construct($dsn, $username, $password, $driver_options = [], $c $this->attributes = $driver_options + $this->attributes; $this->charset = $charset; - if ($this->getAttribute(\PDO::ATTR_PERSISTENT)) { + if ($this->getAttribute(PDO::ATTR_PERSISTENT)) { $this->conn = oci_pconnect($username, $password, $dsn, $charset); } else { $this->conn = oci_connect($username, $password, $dsn, $charset); } - //Check if connection was successful + // Check if connection was successful if (! $this->conn) { throw new OCIException($this->setErrorInfo('08006')); } @@ -107,7 +110,7 @@ public function __construct($dsn, $username, $password, $driver_options = [], $c */ public function __destruct() { - if (strtolower(get_resource_type($this->conn)) == 'oci8') { + if (strtolower(get_resource_type($this->conn)) === 'oci8') { oci_close($this->conn); } } @@ -119,7 +122,7 @@ public function __destruct() * * @return bool Returns TRUE on success */ - public function beginTransaction() + public function beginTransaction(): bool { if ($this->inTransaction()) { throw new OCIException($this->setErrorInfo('25000', '9999', 'Already in a transaction')); @@ -137,12 +140,12 @@ public function beginTransaction() * * @return bool Returns TRUE on success or FALSE on failure */ - public function commit() + public function commit(): bool { if ($this->inTransaction()) { $r = oci_commit($this->conn); if (! $r) { - throw new OCIException('08007'); + throw new OCIException($this->setErrorInfo('08007')); } $this->transaction = ! $this->flipExecuteMode(); @@ -155,15 +158,11 @@ public function commit() /** * Fetch the SQLSTATE associated with the last operation on the database handle. * - * @return mixed Returns SQLSTATE if available or null + * @return mixed Returns SQLSTATE if available, otherwise null */ - public function errorCode() + public function errorCode(): ?string { - if (! empty($this->error[0])) { - return $this->error[0]; - } - - return; + return empty($this->error[0]) ? null : $this->error[0]; } /** @@ -171,7 +170,7 @@ public function errorCode() * * @return array Array of error information about the last operation performed */ - public function errorInfo() + public function errorInfo(): array { return $this->error; } @@ -183,17 +182,13 @@ public function errorInfo() * * @return int Returns the number of rows that were modified or deleted by the statement */ - public function exec($statement) + public function exec(string $statement): int|false { $this->prepare($statement); $result = $this->stmt->execute(); - if (! $result) { - return false; - } - - return $this->stmt->rowCount(); + return $result ? $this->stmt->rowCount() : false; } /** @@ -203,13 +198,9 @@ public function exec($statement) * * @return mixed The value of the requested PDO attribute or null if it does not exist. */ - public function getAttribute($attribute) + public function getAttribute(int $attribute): mixed { - if (isset($this->attributes[$attribute])) { - return $this->attributes[$attribute]; - } - - return; + return $this->attributes[$attribute] ?? null; } /** @@ -217,7 +208,7 @@ public function getAttribute($attribute) * * @return bool Returns TRUE if a transaction is currently active, and FALSE if not. */ - public function inTransaction() + public function inTransaction(): bool { return $this->transaction; } @@ -227,7 +218,7 @@ public function inTransaction() * * @throws OCIException This feature is not supported */ - public function lastInsertId($name = null) + public function lastInsertId(?string $name = null): string|false { throw new OCIException($this->setErrorInfo('IM001', '0000', 'Driver does not support this function')); } @@ -235,27 +226,27 @@ public function lastInsertId($name = null) /** * Prepares a statement for execution and returns a Jfelder\OracleDB\OCI_PDO\OCIStatement object. * - * @param string $statement Valid SQL statement for the target database server. - * @param array $driver_options Attribute values for the OCIStatement object + * @param string $query Valid SQL statement for the target database server. + * @param array $options Attribute values for the OCIStatement object * * @return mixed Returns a OCIStatement on success, false otherwise */ - public function prepare($statement, $driver_options = []) + public function prepare(string $query, array $options = []): OCIStatement|false { - $tokens = explode('?', $statement); + $tokens = explode('?', $query); $count = count($tokens) - 1; if ($count) { - $statement = ''; + $query = ''; for ($i = 0; $i < $count; $i++) { - $statement .= trim($tokens[$i])." :{$i} "; + $query .= trim($tokens[$i])." :{$i} "; } - $statement .= trim($tokens[$i]); + $query .= trim($tokens[$i]); } - $this->queryString = $statement; + $this->queryString = $query; $stmt = oci_parse($this->conn, $this->queryString); - $this->stmt = new OCIStatement($stmt, $this, $this->queryString, $driver_options); + $this->stmt = new OCIStatement($stmt, $this, $this->queryString, $options); return $this->stmt; } @@ -264,38 +255,34 @@ public function prepare($statement, $driver_options = []) * Executes an SQL statement, returning a result set as a Jfelder\OracleDB\OCI_PDO\OCIStatement object * on success or false on failure. * - * @param string $statement Valid SQL statement for the target database server. - * @param int $mode The fetch mode must be one of the PDO::FETCH_* constants. - * @param mixed $type Column number, class name or object depending on PDO::FETCH_* constant used - * @param array $ctorargs Constructor arguments + * @param string $query Valid SQL statement for the target database server. + * @param int $fetchMode The fetch mode must be one of the PDO::FETCH_* constants. + * @param mixed ...$fetchModeArgs Has no effect; was only included to extend parent. * * @return mixed Returns a OCIStatement on success, false otherwise */ - public function query($statement, $mode = null, $type = null, $ctorargs = []) + public function query(string $query, ?int $fetchMode = null, mixed ...$fetchModeArgs): OCIStatement|false { - $this->prepare($statement); - if ($mode) { - $this->stmt->setFetchMode($mode, $type, $ctorargs); + $this->prepare($query); + + if ($fetchMode) { + $this->stmt->setFetchMode($fetchMode, $fetchModeArgs); } $result = $this->stmt->execute(); - if (! $result) { - return false; - } - - return $this->stmt; + return $result ? $this->stmt : false; } /** * Quotes a string for use in a query. * * @param string $string The string to be quoted. - * @param int $parameter_type Provides a data type hint for drivers that have alternate quoting styles. + * @param int $type Provides a data type hint for drivers that have alternate quoting styles. * * @return string Returns false */ - public function quote($string, $parameter_type = \PDO::PARAM_STR) + public function quote(string $string, int $type = PDO::PARAM_STR): string|false { return false; } @@ -307,7 +294,7 @@ public function quote($string, $parameter_type = \PDO::PARAM_STR) * * @return bool Returns TRUE on success or FALSE on failure. */ - public function rollBack() + public function rollBack(): bool { if ($this->inTransaction()) { $r = oci_rollback($this->conn); @@ -330,7 +317,7 @@ public function rollBack() * * @return true */ - public function setAttribute($attribute, $value) + public function setAttribute(int $attribute, mixed $value): bool { $this->attributes[$attribute] = $value; diff --git a/src/Jfelder/OracleDB/OCI_PDO/OCIStatement.php b/src/Jfelder/OracleDB/OCI_PDO/OCIStatement.php index dafdc82..9330449 100644 --- a/src/Jfelder/OracleDB/OCI_PDO/OCIStatement.php +++ b/src/Jfelder/OracleDB/OCI_PDO/OCIStatement.php @@ -2,7 +2,10 @@ namespace Jfelder\OracleDB\OCI_PDO; -class OCIStatement extends \PDOStatement +use PDO; +use PDOStatement; + +class OCIStatement extends PDOStatement { /** * @var oci8 statement Statement handle @@ -33,13 +36,13 @@ class OCIStatement extends \PDOStatement * @var array PDO => OCI data types conversion var */ protected $datatypes = [ - \PDO::PARAM_BOOL => \SQLT_INT, + PDO::PARAM_BOOL => \SQLT_INT, // there is no SQLT_NULL, but oracle will insert a null value if it receives an empty string - \PDO::PARAM_NULL => \SQLT_CHR, - \PDO::PARAM_INT => \SQLT_INT, - \PDO::PARAM_STR => \SQLT_CHR, - \PDO::PARAM_INPUT_OUTPUT => \SQLT_CHR, - \PDO::PARAM_LOB => \SQLT_BLOB, + PDO::PARAM_NULL => \SQLT_CHR, + PDO::PARAM_INT => \SQLT_INT, + PDO::PARAM_STR => \SQLT_CHR, + PDO::PARAM_INPUT_OUTPUT => \SQLT_CHR, + PDO::PARAM_LOB => \SQLT_BLOB, ]; /** @@ -70,11 +73,11 @@ class OCIStatement extends \PDOStatement * * @throws OCIException if $stmt is not a vaild oci8 statement resource */ - public function __construct($stmt, \Jfelder\OracleDB\OCI_PDO\OCI $oci, $sql = '', $options = []) + public function __construct($stmt, OCI $oci, $sql = '', $options = []) { $resource_type = strtolower(get_resource_type($stmt)); - if ($resource_type != 'oci8 statement') { + if ($resource_type !== 'oci8 statement') { throw new OCIException($this->setErrorInfo('0A000', '9999', "Invalid resource received: {$resource_type}")); } @@ -100,30 +103,30 @@ public function __destruct() * Bind a column to a PHP variable. * * @param mixed $column Number of the column (1-indexed) in the result set - * @param mixed $param Name of the PHP variable to which the column will be bound. + * @param mixed $var Name of the PHP variable to which the column will be bound. * @param int $type Data type of the parameter, specified by the PDO::PARAM_* constants. - * @param int $maxlen A hint for pre-allocation. - * @param mixed $driverdata Optional parameter(s) for the driver. + * @param int $maxLength A hint for pre-allocation. + * @param mixed $driverOptions Optional parameter(s) for the driver. * * @throws \InvalidArgumentException If an unknown data type is passed in * * @return bool Returns TRUE on success or FALSE on failure. */ - public function bindColumn($column, &$param, $data_type = null, $maxlen = null, $driverdata = null) + public function bindColumn(string|int $column, mixed &$var, int $type = null, int $maxLength = 0, mixed $driverOptions = null): bool { if (! is_numeric($column) || $column < 1) { throw new \InvalidArgumentException("Invalid column specified: {$column}"); } - if (! isset($this->datatypes[$data_type])) { - throw new \InvalidArgumentException("Unknown data type in oci_bind_by_name: {$data_type}"); + if (! isset($this->datatypes[$type])) { + throw new \InvalidArgumentException("Unknown data type in oci_bind_by_name: {$type}"); } $this->bindings[$column] = [ - 'var' => &$param, - 'data_type' => $data_type, - 'max_length' => $maxlen, - 'driverdata' => $driverdata, + 'var' => &$var, + 'data_type' => $type, + 'max_length' => $maxLength, + 'driverdata' => $driverOptions, ]; return true; @@ -132,35 +135,34 @@ public function bindColumn($column, &$param, $data_type = null, $maxlen = null, /** * Binds a parameter to the specified variable name. * - * @param mixed $parameter Parameter identifier - * @param mixed $variable Name of the PHP variable to bind to the SQL statement parameter - * @param int $data_type Explicit data type for the parameter using the PDO::PARAM_* constants - * @param int $length Length of the data type - * @param mixed $driver_options + * @param mixed $param Parameter identifier + * @param mixed $var Name of the PHP variable to bind to the SQL statement parameter + * @param int $type Explicit data type for the parameter using the PDO::PARAM_* constants + * @param int $maxLength Length of the data type + * @param mixed $driverOptions * * @throws \InvalidArgumentException If an unknown data type is passed in * * @return bool Returns TRUE on success or FALSE on failure */ - public function bindParam($parameter, &$variable, $data_type = \PDO::PARAM_STR, $length = -1, $driver_options = null) + public function bindParam(string|int $param, mixed &$var, int $type = PDO::PARAM_STR, int $maxLength = -1, mixed $driverOptions = null): bool { - if (is_numeric($parameter)) { - $parameter = ":{$parameter}"; + if (is_numeric($param)) { + $param = ":{$param}"; } - $this->addParameter($parameter, $variable, $data_type, $length, $driver_options); + $this->addParameter($param, $var, $type, $maxLength, $driverOptions); - if (! isset($this->datatypes[$data_type])) { - if ($data_type === (\PDO::PARAM_INT | \PDO::PARAM_INPUT_OUTPUT)) { - $data_type = \PDO::PARAM_STR; - $length = $length > 40 ? $length : 40; + if (! isset($this->datatypes[$type])) { + if ($type === (PDO::PARAM_INT | PDO::PARAM_INPUT_OUTPUT)) { + $type = PDO::PARAM_STR; + $maxLength = $maxLength > 40 ? $maxLength : 40; } else { - throw new \InvalidArgumentException("Unknown data type in oci_bind_by_name: {$data_type}"); + throw new \InvalidArgumentException("Unknown data type in oci_bind_by_name: {$type}"); } } - //Bind the parameter - $result = oci_bind_by_name($this->stmt, $parameter, $variable, $length, $this->datatypes[$data_type]); + $result = oci_bind_by_name($this->stmt, $param, $var, $maxLength, $this->datatypes[$type]); return $result; } @@ -168,40 +170,39 @@ public function bindParam($parameter, &$variable, $data_type = \PDO::PARAM_STR, /** * Binds a value to a parameter. * - * @param mixed $parameter Parameter identifier. + * @param mixed $param Parameter identifier. * @param mixed $value The value to bind to the parameter - * @param int $data_type Explicit data type for the parameter using the PDO::PARAM_* constants + * @param int $type Explicit data type for the parameter using the PDO::PARAM_* constants * * @throws \InvalidArgumentException If an unknown data type is passed in * * @return bool Returns TRUE on success or FALSE on failure. */ - public function bindValue($parameter, $value, $data_type = \PDO::PARAM_STR) + public function bindValue(string|int $param, mixed $value, int $type = PDO::PARAM_STR): bool { - if (is_numeric($parameter)) { - $parameter = ":{$parameter}"; + if (is_numeric($param)) { + $param = ":{$param}"; } - $this->addParameter($parameter, $value, $data_type); + $this->addParameter($param, $value, $type); - if (! isset($this->datatypes[$data_type])) { - throw new \InvalidArgumentException("Unknown data type in oci_bind_by_name: {$data_type}"); + if (! isset($this->datatypes[$type])) { + throw new \InvalidArgumentException("Unknown data type in oci_bind_by_name: {$type}"); } - //Bind the parameter - $result = oci_bind_by_name($this->stmt, $parameter, $value, -1, $this->datatypes[$data_type]); + $result = oci_bind_by_name($this->stmt, $param, $value, -1, $this->datatypes[$type]); return $result; } /** * Closes the cursor, enabling the statement to be executed again. - * + * * Todo implement this method instead of always returning true * * @return bool Returns TRUE on success or FALSE on failure. */ - public function closeCursor() + public function closeCursor(): bool { return true; } @@ -212,19 +213,19 @@ public function closeCursor() * @return int Returns the number of columns in the result set represented by the PDOStatement object. * If there is no result set, returns 0. */ - public function columnCount() + public function columnCount(): int { return oci_num_fields($this->stmt); } /** - * Dump an SQL prepared command. + * Dump an SQL prepared command directly to the normal output. * - * @return string print_r of the sql and parameters array + * @return bool Returns true. */ - public function debugDumpParams() + public function debugDumpParams(): ?bool { - return print_r(['sql' => $this->sql, 'params' => $this->parameters], true); + return print_r(['sql' => $this->sql, 'params' => $this->parameters]); } /** @@ -232,13 +233,9 @@ public function debugDumpParams() * * @return mixed Returns an SQLSTATE or NULL if no operation has been run */ - public function errorCode() + public function errorCode(): ?string { - if (! empty($this->error[0])) { - return $this->error[0]; - } - - return; + return empty($this->error[0]) ? null : $this->error[0]; } /** @@ -246,7 +243,7 @@ public function errorCode() * * @return array array of error information about the last operation performed */ - public function errorInfo() + public function errorInfo(): array { return $this->error; } @@ -254,16 +251,16 @@ public function errorInfo() /** * Executes a prepared statement. * - * @param array $input_parameters An array of values with as many elements as there are bound parameters in the + * @param array $params An array of values with as many elements as there are bound parameters in the * SQL statement being executed * * @return bool Returns TRUE on success or FALSE on failure. */ - public function execute($input_parameters = null) + public function execute(?array $params = null): bool { - if (is_array($input_parameters)) { - foreach ($input_parameters as $k => $v) { - $this->bindParam($k, $input_parameters[$k]); + if (is_array($params)) { + foreach ($params as $k => $v) { + $this->bindParam($k, $params[$k]); } } @@ -281,30 +278,29 @@ public function execute($input_parameters = null) /** * Fetches the next row from a result set. * - * @param int $fetch_style Controls how the next row will be returned to the caller. This value must be one of + * @param int $mode Controls how the next row will be returned to the caller. This value must be one of * the PDO::FETCH_* constants - * @param int $cursor_orientation For a Statement object representing a scrollable cursor, this value determines - * which row will be returned to the caller. - * @param int $cursor_offset Specifies the absolute number of the row in the result set that shall be fetched + * @param int $cursorOrientation Has no effect; was only included to extend parent. + * @param int $cursorOffset Has no effect; was only included to extend parent. * * @return mixed The return value of this function on success depends on the fetch type. * In all cases, FALSE is returned on failure. */ - public function fetch($fetch_style = \PDO::FETCH_CLASS, $cursor_orientation = \PDO::FETCH_ORI_NEXT, $cursor_offset = 0) + public function fetch(int $mode = PDO::FETCH_CLASS, int $cursorOrientation = PDO::FETCH_ORI_NEXT, int $cursorOffset = 0): mixed { // set global fetch_style - $this->setAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE, $fetch_style); + $this->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, $mode); // init return value $rs = false; // determine what oci_fetch_* to run - switch ($fetch_style) { - case \PDO::FETCH_CLASS: - case \PDO::FETCH_ASSOC: + switch ($mode) { + case PDO::FETCH_CLASS: + case PDO::FETCH_ASSOC: $rs = oci_fetch_assoc($this->stmt); break; - case \PDO::FETCH_NUM: + case PDO::FETCH_NUM: $rs = oci_fetch_row($this->stmt); break; default: @@ -324,112 +320,81 @@ public function fetch($fetch_style = \PDO::FETCH_CLASS, $cursor_orientation = \P /** * Returns an array containing all of the result set rows. * - * @param int|null $fetch_style Controls how the next row will be returned to the caller. This value must be one + * @param int|null $mode Controls how the next row will be returned to the caller. This value must be one * of the PDO::FETCH_* constants - * @param mixed $fetch_argument This argument have a different meaning depending on the value of the - * fetch_style parameter - * @param array $ctor_args Arguments of custom class constructor when the fetch_style parameter is - * PDO::FETCH_CLASS. + * @param mixed ...$args Has no effect; was only included to extend parent. * * @return array */ - public function fetchAll($fetch_style = \PDO::FETCH_CLASS, $fetch_argument = null, $ctor_args = []) + public function fetchAll(int $mode = PDO::FETCH_CLASS, mixed ...$args): array { - if ($fetch_style != \PDO::FETCH_CLASS && $fetch_style != \PDO::FETCH_ASSOC) { + if ($mode != PDO::FETCH_CLASS && $mode != PDO::FETCH_ASSOC) { throw new \InvalidArgumentException( - "Invalid fetch style requested: {$fetch_style}. Only PDO::FETCH_CLASS and PDO::FETCH_ASSOC suported." + "Invalid fetch style requested: {$mode}. Only PDO::FETCH_CLASS and PDO::FETCH_ASSOC suported." ); } - $this->setAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE, $fetch_style); + $this->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, $mode); - $rs = oci_fetch_all($this->stmt, $temprs, 0, -1, \OCI_FETCHSTATEMENT_BY_ROW + \OCI_ASSOC); + oci_fetch_all($this->stmt, $results, 0, -1, \OCI_FETCHSTATEMENT_BY_ROW + \OCI_ASSOC); - if (oci_error($this->stmt) === false) { - // convert to type requested from PDO options - foreach ($temprs as $k => $v) { - $temprs[$k] = $this->processFetchOptions($v); - } - $rs = $temprs; - } - - if($rs === false) { - $this->setErrorInfo('07000'); + foreach ($results as $k => $v) { + $results[$k] = $this->processFetchOptions($v); } - return $rs; + return $results; } /** * Returns a single column from the next row of a result set. * - * @param int $column_number 0-indexed number of the column you wish to retrieve from the row. + * @param int $column 0-indexed number of the column you wish to retrieve from the row. * If no value is supplied, fetchColumn fetches the first column. * * @return mixed single column in the next row of a result set */ - public function fetchColumn($column_number = 0) + public function fetchColumn(int $column = 0): mixed { - if (! is_int($column_number)) { - throw new OCIException($this->setErrorInfo( - '0A000', - '9999', - "Invalid Column type specfied: {$column_number}. Expecting an int." - )); - } - - $rs = $this->fetch(\PDO::FETCH_NUM); + $rs = $this->fetch(PDO::FETCH_NUM); - return isset($rs[$column_number]) ? $rs[$column_number] : false; + return isset($rs[$column]) ? $rs[$column] : false; } /** * Fetches the next row and returns it as an object. * - * @param string $class_name Name of the created class - * @param array $ctor_args Elements of this array are passed to the constructor + * @param string $class Name of the created class + * @param array $constructorArgs Elements of this array are passed to the constructor * * @return bool Returns an instance of the required class with property names that correspond to the column names * or FALSE on failure. */ - public function fetchObject($class_name = 'stdClass', $ctor_args = null) + public function fetchObject(?string $class = 'stdClass', array $constructorArgs = []): object|false { - $this->setFetchMode(\PDO::FETCH_CLASS, $class_name, $ctor_args); + $this->setFetchMode(PDO::FETCH_CLASS, $class, $constructorArgs); - return $this->fetch(\PDO::FETCH_CLASS); + return $this->fetch(PDO::FETCH_CLASS); } /** * Retrieve a statement attribute. * - * @param int $attribute The attribute number + * @param int $name The attribute number * @return mixed Returns the value of the attribute on success or null on failure */ - public function getAttribute($attribute) + public function getAttribute(int $name): mixed { - if (isset($this->attributes[$attribute])) { - return $this->attributes[$attribute]; - } - - return; + return $this->attributes[$name] ?? null; } /** * Returns metadata for a column in a result set. * - * @param int #column The 0-indexed column in the result set. + * @param int $column The 0-indexed column in the result set. * @return array Returns an associative array representing the metadata for a single column */ - public function getColumnMeta($column) + public function getColumnMeta(int $column): array|false { - if (! is_int($column)) { - throw new OCIException($this->setErrorInfo( - '0A000', - '9999', - "Invalid Column type specfied: {$column}. Expecting an int." - )); - } - $column++; return [ @@ -446,7 +411,7 @@ public function getColumnMeta($column) * * @return bool Returns TRUE on success or FALSE on failure */ - public function nextRowset() + public function nextRowset(): bool { return true; } @@ -456,7 +421,7 @@ public function nextRowset() * * @return int Returns the number of rows affected as an integer, or FALSE on errors. */ - public function rowCount() + public function rowCount(): int { return oci_num_rows($this->stmt); } @@ -468,7 +433,7 @@ public function rowCount() * @param mixed $value Value of named attribute * @return bool Returns TRUE */ - public function setAttribute($attribute, $value) + public function setAttribute(int $attribute, mixed $value): bool { $this->attributes[$attribute] = $value; @@ -479,11 +444,10 @@ public function setAttribute($attribute, $value) * Set the default fetch mode for this statement. * * @param int $mode The fetch mode must be one of the PDO::FETCH_* constants. - * @param mixed $type Column number, class name or object depending on PDO::FETCH_* constant used - * @param array $ctorargs Constructor arguments + * @param mixed ...$args Has no effect; was only included to extend parent. * @return bool Returns TRUE on success or FALSE on failure */ - public function setFetchMode($mode, $type = null, $ctorargs = []) + public function setFetchMode(int $mode, mixed ...$args) { return true; } @@ -496,11 +460,12 @@ public function setFetchMode($mode, $type = null, $ctorargs = []) */ /** - * Stores query parameters for debugDumpParams putput. + * Stores query parameters for debugDumpParams output. */ - private function addParameter($parameter, $variable, $data_type = \PDO::PARAM_STR, $length = -1, $driver_options = null) + private function addParameter($parameter, $variable, $data_type = PDO::PARAM_STR, $length = -1, $driver_options = null) { $param_count = count($this->parameters); + $this->parameters[$param_count] = [ 'paramno' => $param_count, 'name' => $parameter, @@ -547,11 +512,11 @@ private function processBindings($rs) private function processFetchOptions($rec) { if ($rec !== false) { - if ($this->conn->getAttribute(\PDO::ATTR_CASE) == \PDO::CASE_LOWER) { + if ($this->conn->getAttribute(PDO::ATTR_CASE) == PDO::CASE_LOWER) { $rec = array_change_key_case($rec, \CASE_LOWER); } - $rec = ($this->getAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE) != \PDO::FETCH_CLASS) ? $rec : (object) $rec; + $rec = ($this->getAttribute(PDO::ATTR_DEFAULT_FETCH_MODE) != PDO::FETCH_CLASS) ? $rec : (object) $rec; } return $rec; diff --git a/src/Jfelder/OracleDB/Query/Grammars/OracleGrammar.php b/src/Jfelder/OracleDB/Query/Grammars/OracleGrammar.php index 8239259..8266893 100644 --- a/src/Jfelder/OracleDB/Query/Grammars/OracleGrammar.php +++ b/src/Jfelder/OracleDB/Query/Grammars/OracleGrammar.php @@ -39,7 +39,7 @@ protected function dateBasedWhere($type, Builder $query, $where) */ public function compileSelect(Builder $query) { - if ($query->unions && $query->aggregate) { + if (($query->unions || $query->havings) && $query->aggregate) { return $this->compileUnionAggregate($query); } @@ -177,7 +177,7 @@ public function compileDelete(Builder $query) } return parent::compileDelete($query); - } + } /** * Compile a delete statement with joins or limit into SQL. diff --git a/src/Jfelder/OracleDB/Query/OracleBuilder.php b/src/Jfelder/OracleDB/Query/OracleBuilder.php index 886c02f..9ef39e2 100644 --- a/src/Jfelder/OracleDB/Query/OracleBuilder.php +++ b/src/Jfelder/OracleDB/Query/OracleBuilder.php @@ -24,5 +24,5 @@ public function crossJoinSub($query, $as) $this->joins[] = $this->newJoinClause($this, 'cross', new Expression($expression)); return $this; - } + } } diff --git a/src/Jfelder/OracleDB/Query/Processors/OracleProcessor.php b/src/Jfelder/OracleDB/Query/Processors/OracleProcessor.php index 836441b..02bf6e0 100644 --- a/src/Jfelder/OracleDB/Query/Processors/OracleProcessor.php +++ b/src/Jfelder/OracleDB/Query/Processors/OracleProcessor.php @@ -22,7 +22,7 @@ public function processInsertGetId(Builder $query, $sql, $values, $sequence = nu $counter = 0; $last_insert_id = 0; - //Get PDO object + // Get PDO object $pdo = $query->getConnection()->getPdo(); // get PDO statment object @@ -39,7 +39,7 @@ public function processInsertGetId(Builder $query, $sql, $values, $sequence = nu $stmt->bindValue($counter++, $v, $this->bindType($v)); } - // bind output param for the returning cluase + // bind output param for the returning clause $stmt->bindParam($counter, $last_insert_id, \PDO::PARAM_INT | \PDO::PARAM_INPUT_OUTPUT, 8); // execute statement @@ -67,7 +67,7 @@ public function processColumnListing($results) /* * Determine parameter type passed in - * + * * @param mixed $param * @return \PDO::PARAM_* type */ diff --git a/src/Jfelder/OracleDB/Schema/Grammars/OracleGrammar.php b/src/Jfelder/OracleDB/Schema/Grammars/OracleGrammar.php index 83a2206..a9493bc 100644 --- a/src/Jfelder/OracleDB/Schema/Grammars/OracleGrammar.php +++ b/src/Jfelder/OracleDB/Schema/Grammars/OracleGrammar.php @@ -102,7 +102,7 @@ protected function addForeignKeys(Blueprint $blueprint) if (! is_null($foreign->onUpdate)) { $sql .= " on update {$foreign->onUpdate}"; - } + } } return $sql; @@ -207,7 +207,7 @@ public function compileForeign(Blueprint $blueprint, Fluent $command) if (! is_null($command->onUpdate)) { $sql .= " on update {$command->onUpdate}"; - } + } return $sql; } @@ -494,7 +494,7 @@ protected function typeDouble(Fluent $column) if (is_null($column->total) || is_null($column->places)) { throw new RuntimeException('This database engine requires specifying both precision and scale for a "double" column.'); } - + return "number({$column->total}, {$column->places})"; } @@ -532,7 +532,7 @@ protected function typeEnum(Fluent $column) 'varchar2(255) check(%s in (%s))', $column->name, $this->quoteString($column->allowed) - ); + ); } /** diff --git a/src/config/.gitkeep b/src/config/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/tests/.gitkeep b/tests/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/tests/OracleDBConnectorTest.php b/tests/OracleDBConnectorTest.php index f8cc3f2..ec08391 100644 --- a/tests/OracleDBConnectorTest.php +++ b/tests/OracleDBConnectorTest.php @@ -1,6 +1,7 @@ setDefaultOptions([0 => 'foo', 1 => 'bar']); - $this->assertEquals([0 => 'baz', 1 => 'bar', 2 => 'boom'], $connector->getOptions(['options' => [0 => 'baz', 2 => 'boom']])); - } - /** - * @dataProvider OracleConnectProvider + * @dataProvider oracleConnectProvider */ public function testOracleConnectCallsCreateConnectionWithProperArguments($dsn, $config) { @@ -33,17 +27,33 @@ public function testOracleConnectCallsCreateConnectionWithProperArguments($dsn, $this->assertSame($result, $connection); } - public function OracleConnectProvider() + public function oracleConnectProvider() { return [ - ['oci:dbname=(DESCRIPTION =(ADDRESS = (PROTOCOL = TCP)(HOST = localhost)(PORT = 1234))(CONNECT_DATA =(SID = ORCL)))', - ['driver' => 'pdo', 'host' => 'localhost', 'port' => '1234', 'database' => 'ORCL', 'tns' => ''], ], - ['oci:dbname=(DESCRIPTION =(ADDRESS = (PROTOCOL = TCP)(HOST = localhost)(PORT = 4321))(CONNECT_DATA =(SID = ORCL)))', - ['driver' => 'pdo', 'tns' => '(DESCRIPTION =(ADDRESS = (PROTOCOL = TCP)(HOST = localhost)(PORT = 4321))(CONNECT_DATA =(SID = ORCL)))'], ], - ['(DESCRIPTION =(ADDRESS = (PROTOCOL = TCP)(HOST = localhost)(PORT = 6789))(CONNECT_DATA =(SID = ORCL)))', - ['driver' => 'oci8', 'tns' => '(DESCRIPTION =(ADDRESS = (PROTOCOL = TCP)(HOST = localhost)(PORT = 6789))(CONNECT_DATA =(SID = ORCL)))'], ], - ['(DESCRIPTION =(ADDRESS = (PROTOCOL = TCP)(HOST = localhost)(PORT = 9876))(CONNECT_DATA =(SID = ORCL)))', - ['driver' => 'oci8', 'host' => 'localhost', 'port' => '9876', 'database' => 'ORCL', 'tns' => ''], ], + [ + 'oci:dbname=(DESCRIPTION =(ADDRESS = (PROTOCOL = TCP)(HOST = localhost)(PORT = 1234))(CONNECT_DATA =(SID = ORCL)))', + ['driver' => 'pdo', 'host' => 'localhost', 'port' => '1234', 'database' => 'ORCL', 'tns' => ''] + ], + [ + 'oci:dbname=(DESCRIPTION =(ADDRESS = (PROTOCOL = TCP)(HOST = localhost)(PORT = 4321))(CONNECT_DATA =(SID = ORCL)))', + ['driver' => 'pdo', 'tns' => '(DESCRIPTION =(ADDRESS = (PROTOCOL = TCP)(HOST = localhost)(PORT = 4321))(CONNECT_DATA =(SID = ORCL)))'] + ], + [ + '(DESCRIPTION =(ADDRESS = (PROTOCOL = TCP)(HOST = localhost)(PORT = 6789))(CONNECT_DATA =(SID = ORCL)))', + ['driver' => 'oci8', 'tns' => '(DESCRIPTION =(ADDRESS = (PROTOCOL = TCP)(HOST = localhost)(PORT = 6789))(CONNECT_DATA =(SID = ORCL)))'] + ], + [ + '(DESCRIPTION =(ADDRESS = (PROTOCOL = TCP)(HOST = localhost)(PORT = 9876))(CONNECT_DATA =(SID = ORCL)))', + ['driver' => 'oci8', 'host' => 'localhost', 'port' => '9876', 'database' => 'ORCL', 'tns' => ''], + ], ]; } + + public function testOracleConnectWithInvalidDriver() + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Unsupported driver [garbage].'); + + (new OracleConnector)->createConnection('', ['driver' => 'garbage'], []); + } } diff --git a/tests/OracleDBOCIProcessorTest.php b/tests/OracleDBOCIProcessorTest.php index da8d834..ef0ef86 100644 --- a/tests/OracleDBOCIProcessorTest.php +++ b/tests/OracleDBOCIProcessorTest.php @@ -1,7 +1,12 @@ shouldReceive('getConnection')->once()->andReturn($connection); - $processor = new Jfelder\OracleDB\Query\Processors\OracleProcessor; + $processor = new OracleProcessor; $result = $processor->processInsertGetId($builder, 'sql', [1, 'foo', true, null], 'id'); $this->assertSame(0, $result); @@ -45,7 +50,7 @@ public function testInsertGetIdProcessing() public function testProcessColumnListing() { - $processor = new Jfelder\OracleDB\Query\Processors\OracleProcessor(); + $processor = new OracleProcessor; $listing = [['column_name' => 'id'], ['column_name' => 'name'], ['column_name' => 'email']]; $expected = ['id', 'name', 'email']; $this->assertEquals($expected, $processor->processColumnListing($listing)); diff --git a/tests/OracleDBOCIStatementTest.php b/tests/OracleDBOCIStatementTest.php index e783090..cf2dbf5 100644 --- a/tests/OracleDBOCIStatementTest.php +++ b/tests/OracleDBOCIStatementTest.php @@ -1,5 +1,12 @@ oci = m::mock(new \TestOCIStub('', null, null, [\PDO::ATTR_CASE => \PDO::CASE_LOWER])); - $this->stmt = m::mock(new \TestOCIStatementStub('oci8 statement', $this->oci, '', ['fake' => 'attribute'])); + $this->oci = m::mock(new TestOCIStub('', null, null, [PDO::ATTR_CASE => PDO::CASE_LOWER])); + $this->stmt = m::mock(new TestOCIStatementStub('oci8 statement', $this->oci, '', [4321 => 'attributeValue'])); - //fake result sets for all the fetch calls + // fake result sets for all the fetch calls $this->resultUpperArray = ['FNAME' => 'Test', 'LNAME' => 'Testerson', 'EMAIL' => 'tester@testing.com']; $this->resultUpperObject = (object) $this->resultUpperArray; $this->resultLowerArray = array_change_key_case($this->resultUpperArray, \CASE_LOWER); @@ -57,11 +65,11 @@ public function tearDown(): void public function testConstructor() { - $oci = new \TestOCIStub(); + $oci = new TestOCIStub(); $ocistmt = new OCIStatement('oci8 statement', $oci); // use reflection to test values of protected properties - $reflection = new \ReflectionClass($ocistmt); + $reflection = new ReflectionClass($ocistmt); // stmt property $property = $reflection->getProperty('stmt'); @@ -84,53 +92,54 @@ public function testConstructorWithoutValidStatementPassignIn() global $OCIStatementStatus; $OCIStatementStatus = false; $this->expectException(OCIException::class); - $ocistmt = new OCIStatement('oci8 statement', new \TestOCIStub()); + $ocistmt = new OCIStatement('oci8 statement', new TestOCIStub()); } public function testDestructor() { global $OCIStatementStatus; - $ocistmt = new OCIStatement('oci8 statement', new \TestOCIStub()); + $ocistmt = new OCIStatement('oci8 statement', new TestOCIStub()); unset($ocistmt); $this->assertFalse($OCIStatementStatus); } public function testBindColumnWithColumnName() { - $stmt = new \TestOCIStatementStub('oci8 statement', $this->oci, 'sql', []); + $stmt = new TestOCIStatementStub('oci8 statement', $this->oci, 'sql', []); $holder = ''; $this->expectException(InvalidArgumentException::class); - $stmt->bindColumn('holder', $holder, \PDO::PARAM_STR); + $stmt->bindColumn('holder', $holder, PDO::PARAM_STR); } public function testBindColumnWithColumnNumberLessThanOne() { - $stmt = new \TestOCIStatementStub('oci8 statement', $this->oci, 'sql', []); + $stmt = new TestOCIStatementStub('oci8 statement', $this->oci, 'sql', []); $holder = ''; $this->expectException(InvalidArgumentException::class); - $stmt->bindColumn(0, $holder, \PDO::PARAM_STR); + $stmt->bindColumn(0, $holder, PDO::PARAM_STR); } public function testBindColumnWithInvalidDataType() { - $stmt = new \TestOCIStatementStub('oci8 statement', $this->oci, 'sql', []); + $stmt = new TestOCIStatementStub('oci8 statement', $this->oci, 'sql', []); $holder = ''; + $nonExistantDataType = 12345; $this->expectException(InvalidArgumentException::class); - $stmt->bindColumn(1, $holder, 'hello'); + $stmt->bindColumn(1, $holder, $nonExistantDataType); } public function testBindColumnSuccess() { - $stmt = new \TestOCIStatementStub('oci8 statement', $this->oci, 'sql', []); + $stmt = new TestOCIStatementStub('oci8 statement', $this->oci, 'sql', []); $holder = ''; - $this->assertTrue($stmt->bindColumn(1, $holder, \PDO::PARAM_STR, 40)); + $this->assertTrue($stmt->bindColumn(1, $holder, PDO::PARAM_STR, 40)); - $reflection = new \ReflectionClass($stmt); + $reflection = new ReflectionClass($stmt); // bindings property $property = $reflection->getProperty('bindings'); $property->setAccessible(true); - $this->assertEquals([1 => ['var' => $holder, 'data_type' => \PDO::PARAM_STR, 'max_length' => 40, 'driverdata' => null]], $property->getValue($stmt)); + $this->assertEquals([1 => ['var' => $holder, 'data_type' => PDO::PARAM_STR, 'max_length' => 40, 'driverdata' => null]], $property->getValue($stmt)); } public function testBindParamWithValidDataType() @@ -139,7 +148,7 @@ public function testBindParamWithValidDataType() $OCIBindChangeStatus = true; $variable = ''; - $stmt = new \TestOCIStatementStub(true, new \TestOCIStub(), '', []); + $stmt = new TestOCIStatementStub(true, new TestOCIStub(), '', []); $this->assertTrue($stmt->bindParam('param', $variable)); $this->assertEquals('oci_bind_by_name', $variable); } @@ -147,10 +156,11 @@ public function testBindParamWithValidDataType() public function testBindParamWithInvalidDataType() { $variable = ''; + $nonExistantDataType = 12345; $this->expectException(InvalidArgumentException::class); - $stmt = new \TestOCIStatementStub(true, new \TestOCIStub(), '', []); - $stmt->bindParam('param', $variable, 'hello'); + $stmt = new TestOCIStatementStub(true, new TestOCIStub(), '', []); + $stmt->bindParam('param', $variable, $nonExistantDataType); } public function testBindParamWithReturnDataType() @@ -159,8 +169,8 @@ public function testBindParamWithReturnDataType() $OCIBindChangeStatus = true; $variable = ''; - $stmt = new \TestOCIStatementStub(true, new \TestOCIStub(), '', []); - $this->assertTrue($stmt->bindParam('param', $variable, \PDO::PARAM_INPUT_OUTPUT)); + $stmt = new TestOCIStatementStub(true, new TestOCIStub(), '', []); + $this->assertTrue($stmt->bindParam('param', $variable, PDO::PARAM_INPUT_OUTPUT)); $this->assertEquals('oci_bind_by_name', $variable); } @@ -172,9 +182,9 @@ public function testBindValueWithValidDataType() public function testBindValueWithNullDataType() { global $OCIBindByNameTypeReceived; - $this->assertTrue($this->stmt->bindValue('param', null, \PDO::PARAM_NULL)); + $this->assertTrue($this->stmt->bindValue('param', null, PDO::PARAM_NULL)); $this->assertSame(\SQLT_CHR, $OCIBindByNameTypeReceived); - } + } public function testBindValueWithInvalidDataType() { @@ -193,42 +203,61 @@ public function testColumnCount() $this->assertEquals(1, $this->stmt->columnCount()); } - public function testDebugDumpParams() + public function testDebugDumpParamsWhenNothingHasBeenSet() + { + $expectedOutput = print_r(['sql' => '', 'params' => []], true); + + $this->expectOutputString($expectedOutput); + + $this->assertTrue($this->stmt->debugDumpParams()); + } + + public function testDebugDumpParamsWhenThingsHaveBeenSet() { global $OCIBindChangeStatus; $OCIBindChangeStatus = false; - $this->assertEquals(print_r(['sql' => '', 'params' => []], true), $this->stmt->debugDumpParams()); - $stmt = new \TestOCIStatementStub(true, true, 'select * from table where id = :0 and name = :1', []); + $sql = 'select * from table where id = :0 and name = :1'; $var = 'Hello'; + $expectedOutput = print_r( + [ + 'sql' => $sql, + 'params' => [ + [ + 'paramno' => 0, + 'name' => ':0', + 'value' => $var, + 'is_param' => 1, + 'param_type' => PDO::PARAM_INPUT_OUTPUT + ], + [ + 'paramno' => 1, + 'name' => ':1', + 'value' => 'hi', + 'is_param' => 1, + 'param_type' => PDO::PARAM_STR, + ], + ] + ], + true + ); - $stmt->bindParam(0, $var, \PDO::PARAM_INPUT_OUTPUT); + $stmt = new TestOCIStatementStub(true, true, $sql, []); + $stmt->bindParam(0, $var, PDO::PARAM_INPUT_OUTPUT); $stmt->bindValue(1, 'hi'); - $this->assertEquals(print_r(['sql' => 'select * from table where id = :0 and name = :1', - 'params' => [ - ['paramno' => 0, - 'name' => ':0', - 'value' => $var, - 'is_param' => 1, - 'param_type' => \PDO::PARAM_INPUT_OUTPUT, - ], - ['paramno' => 1, - 'name' => ':1', - 'value' => 'hi', - 'is_param' => 1, - 'param_type' => \PDO::PARAM_STR, - ], - ], ], true), $stmt->debugDumpParams() - ); + + $this->expectOutputString($expectedOutput); + + $this->assertTrue($stmt->debugDumpParams()); } public function testErrorCode() { - $ocistmt = new \TestOCIStatementStub(true, '', '', []); + $ocistmt = new TestOCIStatementStub(true, '', '', []); $this->assertNull($ocistmt->errorCode()); // use reflection to test values of protected properties - $reflection = new \ReflectionClass($ocistmt); + $reflection = new ReflectionClass($ocistmt); // setErrorInfo $method = $reflection->getMethod('setErrorInfo'); @@ -240,11 +269,11 @@ public function testErrorCode() public function testErrorInfo() { - $ocistmt = new \TestOCIStatementStub(true, '', '', []); + $ocistmt = new TestOCIStatementStub(true, '', '', []); $this->assertEquals([0 => '', 1 => null, 2 => null], $ocistmt->errorInfo()); // use reflection to test values of protected properties - $reflection = new \ReflectionClass($ocistmt); + $reflection = new ReflectionClass($ocistmt); // setErrorInfo $method = $reflection->getMethod('setErrorInfo'); @@ -282,21 +311,21 @@ public function testExecuteFailesWithoutParameters() public function testFetchWithBindColumn() { - $this->oci->setAttribute(\PDO::ATTR_CASE, \PDO::CASE_LOWER); - $stmt = new \TestOCIStatementStub('oci8 statement', $this->oci, 'sql', []); + $this->oci->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER); + $stmt = new TestOCIStatementStub('oci8 statement', $this->oci, 'sql', []); $holder = 'dad'; - $this->assertTrue($stmt->bindColumn(1, $holder, \PDO::PARAM_STR, 40)); + $this->assertTrue($stmt->bindColumn(1, $holder, PDO::PARAM_STR, 40)); - $reflection = new \ReflectionClass($stmt); + $reflection = new ReflectionClass($stmt); // bindings property $property = $reflection->getProperty('bindings'); $property->setAccessible(true); - $this->assertEquals([1 => ['var' => $holder, 'data_type' => \PDO::PARAM_STR, 'max_length' => 40, 'driverdata' => null]], $property->getValue($stmt)); + $this->assertEquals([1 => ['var' => $holder, 'data_type' => PDO::PARAM_STR, 'max_length' => 40, 'driverdata' => null]], $property->getValue($stmt)); - $obj = $stmt->fetch(\PDO::FETCH_CLASS); + $obj = $stmt->fetch(PDO::FETCH_CLASS); - $this->assertEquals([1 => ['var' => $holder, 'data_type' => \PDO::PARAM_STR, 'max_length' => 40, 'driverdata' => null]], $property->getValue($stmt)); + $this->assertEquals([1 => ['var' => $holder, 'data_type' => PDO::PARAM_STR, 'max_length' => 40, 'driverdata' => null]], $property->getValue($stmt)); $this->assertEquals($obj->fname, $holder); } @@ -304,36 +333,36 @@ public function testFetchWithBindColumn() public function testFetchSuccessReturnArray() { // return lower case - $this->oci->setAttribute(\PDO::ATTR_CASE, \PDO::CASE_LOWER); - $this->assertEquals($this->resultLowerArray, $this->stmt->fetch(\PDO::FETCH_ASSOC)); - $this->assertEquals($this->resultBothLowerArray, $this->stmt->fetch(\PDO::FETCH_BOTH)); + $this->oci->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER); + $this->assertEquals($this->resultLowerArray, $this->stmt->fetch(PDO::FETCH_ASSOC)); + $this->assertEquals($this->resultBothLowerArray, $this->stmt->fetch(PDO::FETCH_BOTH)); // return upper cased keyed object - $this->oci->setAttribute(\PDO::ATTR_CASE, \PDO::CASE_UPPER); - $this->assertEquals($this->resultUpperArray, $this->stmt->fetch(\PDO::FETCH_ASSOC)); - $this->assertEquals($this->resultBothUpperArray, $this->stmt->fetch(\PDO::FETCH_BOTH)); + $this->oci->setAttribute(PDO::ATTR_CASE, PDO::CASE_UPPER); + $this->assertEquals($this->resultUpperArray, $this->stmt->fetch(PDO::FETCH_ASSOC)); + $this->assertEquals($this->resultBothUpperArray, $this->stmt->fetch(PDO::FETCH_BOTH)); // return natural keyed object, in oracle that is upper case - $this->oci->setAttribute(\PDO::ATTR_CASE, \PDO::CASE_NATURAL); - $this->assertEquals($this->resultUpperArray, $this->stmt->fetch(\PDO::FETCH_ASSOC)); - $this->assertEquals($this->resultBothUpperArray, $this->stmt->fetch(\PDO::FETCH_BOTH)); + $this->oci->setAttribute(PDO::ATTR_CASE, PDO::CASE_NATURAL); + $this->assertEquals($this->resultUpperArray, $this->stmt->fetch(PDO::FETCH_ASSOC)); + $this->assertEquals($this->resultBothUpperArray, $this->stmt->fetch(PDO::FETCH_BOTH)); - $this->assertEquals($this->resultNumArray, $this->stmt->fetch(\PDO::FETCH_NUM)); + $this->assertEquals($this->resultNumArray, $this->stmt->fetch(PDO::FETCH_NUM)); } public function testFetchSuccessReturnObject() { // return lower cased keyed object - $this->oci->setAttribute(\PDO::ATTR_CASE, \PDO::CASE_LOWER); - $this->assertEquals($this->resultLowerObject, $this->stmt->fetch(\PDO::FETCH_CLASS)); + $this->oci->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER); + $this->assertEquals($this->resultLowerObject, $this->stmt->fetch(PDO::FETCH_CLASS)); // return upper cased keyed object - $this->oci->setAttribute(\PDO::ATTR_CASE, \PDO::CASE_UPPER); - $this->assertEquals($this->resultUpperObject, $this->stmt->fetch(\PDO::FETCH_CLASS)); + $this->oci->setAttribute(PDO::ATTR_CASE, PDO::CASE_UPPER); + $this->assertEquals($this->resultUpperObject, $this->stmt->fetch(PDO::FETCH_CLASS)); // return natural keyed object, in oracle that is upper case - $this->oci->setAttribute(\PDO::ATTR_CASE, \PDO::CASE_NATURAL); - $this->assertEquals($this->resultUpperObject, $this->stmt->fetch(\PDO::FETCH_CLASS)); + $this->oci->setAttribute(PDO::ATTR_CASE, PDO::CASE_NATURAL); + $this->assertEquals($this->resultUpperObject, $this->stmt->fetch(PDO::FETCH_CLASS)); } public function testFetchFail() @@ -344,87 +373,102 @@ public function testFetchFail() $this->assertEquals('07000', $this->stmt->errorCode()); } - public function testFetchAllSuccessReturnArray() + public function testFetchAllWithNoArg() + { + // return lower cased keyed object + $this->oci->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER); + $this->assertEquals($this->resultAllLowerObject, $this->stmt->fetchAll()); + + // return upper cased keyed object + $this->oci->setAttribute(PDO::ATTR_CASE, PDO::CASE_UPPER); + $this->assertEquals($this->resultAllUpperObject, $this->stmt->fetchAll()); + + // return natural keyed object, in oracle that is upper case + $this->oci->setAttribute(PDO::ATTR_CASE, PDO::CASE_NATURAL); + $this->assertEquals($this->resultAllUpperObject, $this->stmt->fetchAll()); + } + + public function testFetchAllReturnArray() { // return lower case - $this->oci->setAttribute(\PDO::ATTR_CASE, \PDO::CASE_LOWER); - $this->assertEquals($this->resultAllLowerArray, $this->stmt->fetchAll(\PDO::FETCH_ASSOC)); + $this->oci->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER); + $this->assertEquals($this->resultAllLowerArray, $this->stmt->fetchAll(PDO::FETCH_ASSOC)); // return upper cased keyed object - $this->oci->setAttribute(\PDO::ATTR_CASE, \PDO::CASE_UPPER); - $this->assertEquals($this->resultAllUpperArray, $this->stmt->fetchAll(\PDO::FETCH_ASSOC)); + $this->oci->setAttribute(PDO::ATTR_CASE, PDO::CASE_UPPER); + $this->assertEquals($this->resultAllUpperArray, $this->stmt->fetchAll(PDO::FETCH_ASSOC)); // return natural keyed object, in oracle that is upper case - $this->oci->setAttribute(\PDO::ATTR_CASE, \PDO::CASE_NATURAL); - $this->assertEquals($this->resultAllUpperArray, $this->stmt->fetchAll(\PDO::FETCH_ASSOC)); + $this->oci->setAttribute(PDO::ATTR_CASE, PDO::CASE_NATURAL); + $this->assertEquals($this->resultAllUpperArray, $this->stmt->fetchAll(PDO::FETCH_ASSOC)); } - public function testFetchAllSuccessReturnObject() + public function testFetchAllReturnObject() { // return lower cased keyed object - $this->oci->setAttribute(\PDO::ATTR_CASE, \PDO::CASE_LOWER); - $this->assertEquals($this->resultAllLowerObject, $this->stmt->fetchAll(\PDO::FETCH_CLASS)); + $this->oci->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER); + $this->assertEquals($this->resultAllLowerObject, $this->stmt->fetchAll(PDO::FETCH_CLASS)); // return upper cased keyed object - $this->oci->setAttribute(\PDO::ATTR_CASE, \PDO::CASE_UPPER); - $this->assertEquals($this->resultAllUpperObject, $this->stmt->fetchAll(\PDO::FETCH_CLASS)); + $this->oci->setAttribute(PDO::ATTR_CASE, PDO::CASE_UPPER); + $this->assertEquals($this->resultAllUpperObject, $this->stmt->fetchAll(PDO::FETCH_CLASS)); // return natural keyed object, in oracle that is upper case - $this->oci->setAttribute(\PDO::ATTR_CASE, \PDO::CASE_NATURAL); - $this->assertEquals($this->resultAllUpperObject, $this->stmt->fetchAll(\PDO::FETCH_CLASS)); + $this->oci->setAttribute(PDO::ATTR_CASE, PDO::CASE_NATURAL); + $this->assertEquals($this->resultAllUpperObject, $this->stmt->fetchAll(PDO::FETCH_CLASS)); } - public function testFetchAllFail() + public function testFetchAllWhenEmptyResultSet() { - global $OCIFetchStatus; - $OCIFetchStatus = false; - $this->assertFalse($this->stmt->fetchAll()); - $this->assertEquals('07000', $this->stmt->errorCode()); + global $OCIFetchAllReturnEmpty; + $OCIFetchAllReturnEmpty = true; + $this->assertSame([], $this->stmt->fetchAll()); } public function testFetchAllFailWithInvalidFetchStyle() { + $invalidMode = PDO::FETCH_BOTH; $this->expectException(InvalidArgumentException::class); - $this->stmt->fetchAll(\PDO::FETCH_BOTH); + $this->expectExceptionMessage('Invalid fetch style requested: '.$invalidMode.'. Only PDO::FETCH_CLASS and PDO::FETCH_ASSOC suported.'); + $this->stmt->fetchAll($invalidMode); } - public function testFetchColumnWithColumnNumber() + public function testFetchColumnWithNoArg() { - $this->assertEquals($this->resultNumArray[1], $this->stmt->fetchColumn(1)); + $this->assertEquals($this->resultNumArray[0], $this->stmt->fetchColumn()); } - public function testFetchColumnWithColumnName() + public function testFetchColumnWithColumnNumber() { - $this->expectException(OCIException::class); - $this->stmt->fetchColumn('ColumnName'); + $this->assertEquals($this->resultNumArray[1], $this->stmt->fetchColumn(1)); } public function testFetchObject() { // return lower cased keyed object - $this->oci->setAttribute(\PDO::ATTR_CASE, \PDO::CASE_LOWER); + $this->oci->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER); $this->assertEquals($this->resultLowerObject, $this->stmt->fetchObject()); // return upper cased keyed object - $this->oci->setAttribute(\PDO::ATTR_CASE, \PDO::CASE_UPPER); + $this->oci->setAttribute(PDO::ATTR_CASE, PDO::CASE_UPPER); $this->assertEquals($this->resultUpperObject, $this->stmt->fetchObject()); // return natural keyed object, in oracle that is upper case - $this->oci->setAttribute(\PDO::ATTR_CASE, \PDO::CASE_NATURAL); + $this->oci->setAttribute(PDO::ATTR_CASE, PDO::CASE_NATURAL); $this->assertEquals($this->resultUpperObject, $this->stmt->fetchObject()); } public function testGetAttributeForValidAttribute() { - $this->assertEquals('attribute', $this->stmt->getAttribute('fake')); + $this->assertEquals('attributeValue', $this->stmt->getAttribute(4321)); } public function testGetAttributeForInvalidAttribute() { - $this->assertEquals(null, $this->stmt->getAttribute('invalid')); + $this->assertEquals(null, $this->stmt->getAttribute(12345)); } - public function testGetColumnMetaWithColumnNumber() + public function testGetColumnMeta() { $expected = ['native_type' => 1, 'driver:decl_type' => 1, 'name' => 1, 'len' => 1, 'precision' => 1, ]; @@ -433,12 +477,6 @@ public function testGetColumnMetaWithColumnNumber() $this->assertEquals($expected, $result); } - public function testGetColumnMetaWithColumnName() - { - $this->expectException(OCIException::class); - $this->stmt->getColumnMeta('ColumnName'); - } - public function testNextRowset() { $this->assertTrue($this->stmt->nextRowset()); @@ -451,17 +489,20 @@ public function testRowCount() public function testSetAttribute() { - $this->assertTrue($this->stmt->setAttribute('testing', 'setAttribute')); - $this->assertEquals('setAttribute', $this->stmt->getAttribute('testing')); + $attr = PDO::ATTR_DEFAULT_FETCH_MODE; + $value = PDO::FETCH_CLASS; + + $this->assertTrue($this->stmt->setAttribute($attr, $value)); + $this->assertEquals($value, $this->stmt->getAttribute($attr)); } public function testSetFetchMode() { - $this->assertTrue($this->stmt->setFetchMode(\PDO::FETCH_CLASS)); + $this->assertTrue($this->stmt->setFetchMode(PDO::FETCH_CLASS)); } public function testGetOCIResource() { $this->assertEquals('oci8 statement', $this->stmt->getOCIResource()); - } -} \ No newline at end of file + } +} diff --git a/tests/OracleDBOCITest.php b/tests/OracleDBOCITest.php index f534100..3f59dde 100644 --- a/tests/OracleDBOCITest.php +++ b/tests/OracleDBOCITest.php @@ -1,5 +1,9 @@ oci = m::mock(new \TestOCIStub('', null, null, [\PDO::ATTR_CASE => \PDO::CASE_LOWER])); + $this->oci = m::mock(new TestOCIStub('', null, null, [PDO::ATTR_CASE => PDO::CASE_LOWER])); } } @@ -37,16 +41,16 @@ public function tearDown(): void public function testConstructorSuccessWithPersistentConnection() { - $oci = new OCI('dsn', null, null, [\PDO::ATTR_PERSISTENT => 1]); + $oci = new OCI('dsn', null, null, [PDO::ATTR_PERSISTENT => 1]); $this->assertInstanceOf(OCI::class, $oci); - $this->assertEquals(1, $oci->getAttribute(\PDO::ATTR_PERSISTENT)); + $this->assertEquals(1, $oci->getAttribute(PDO::ATTR_PERSISTENT)); } public function testConstructorSuccessWithoutPersistentConnection() { - $oci = new OCI('dsn', null, null, [\PDO::ATTR_PERSISTENT => 0]); + $oci = new OCI('dsn', null, null, [PDO::ATTR_PERSISTENT => 0]); $this->assertInstanceOf(OCI::class, $oci); - $this->assertEquals(0, $oci->getAttribute(\PDO::ATTR_PERSISTENT)); + $this->assertEquals(0, $oci->getAttribute(PDO::ATTR_PERSISTENT)); } public function testConstructorFailWithPersistentConnection() @@ -54,7 +58,7 @@ public function testConstructorFailWithPersistentConnection() global $OCITransactionStatus; $OCITransactionStatus = false; $this->expectException(OCIException::class); - $oci = new OCI('dsn', null, null, [\PDO::ATTR_PERSISTENT => 1]); + $oci = new OCI('dsn', null, null, [PDO::ATTR_PERSISTENT => 1]); } public function testConstructorFailWithoutPersistentConnection() @@ -62,7 +66,7 @@ public function testConstructorFailWithoutPersistentConnection() global $OCITransactionStatus; $OCITransactionStatus = false; $this->expectException(OCIException::class); - $oci = new OCI('dsn', null, null, [\PDO::ATTR_PERSISTENT => 0]); + $oci = new OCI('dsn', null, null, [PDO::ATTR_PERSISTENT => 0]); } public function testDestructor() @@ -111,7 +115,7 @@ public function testCommitNotInTransaction() public function testErrorCode() { - $oci = new \TestOCIStub(); + $oci = new TestOCIStub(); $this->assertNull($oci->errorCode()); // use reflection to test values of protected properties @@ -127,7 +131,7 @@ public function testErrorCode() public function testErrorInfo() { - $oci = new \TestOCIStub(); + $oci = new TestOCIStub(); $this->assertEquals([0 => '', 1 => null, 2 => null], $oci->errorInfo()); // use reflection to test values of protected properties @@ -144,7 +148,7 @@ public function testErrorInfo() public function testExec() { $sql = 'select * from table'; - $oci = new \TestOCIStub(); + $oci = new TestOCIStub(); $stmt = $oci->exec($sql); $this->assertEquals(1, $stmt); @@ -175,19 +179,20 @@ public function testExecFails() global $OCIExecuteStatus; $OCIExecuteStatus = false; $sql = 'select * from table'; - $oci = new \TestOCIStub(); + $oci = new TestOCIStub(); $stmt = $oci->exec($sql); $this->assertFalse($stmt); } public function testGetAttributeForValidAttribute() { - $this->assertEquals(1, $this->oci->getAttribute(\PDO::ATTR_AUTOCOMMIT)); + $this->assertEquals(1, $this->oci->getAttribute(PDO::ATTR_AUTOCOMMIT)); } public function testGetAttributeForInvalidAttribute() { - $this->assertEquals(null, $this->oci->getAttribute('doesnotexist')); + $nonExistantAttr = 12345; + $this->assertEquals(null, $this->oci->getAttribute($nonExistantAttr)); } public function testInTransactionWhileNotInTransaction() @@ -216,7 +221,7 @@ public function testLastInsertIDWithoutName() public function testPrepareWithNonParameterQuery() { $sql = 'select * from table'; - $oci = new \TestOCIStub(); + $oci = new TestOCIStub(); $stmt = $oci->prepare($sql); $this->assertInstanceOf(OCIStatement::class, $stmt); @@ -242,7 +247,7 @@ public function testPrepareWithNonParameterQuery() public function testPrepareWithParameterQuery() { $sql = 'select * from table where id = ? and date = ?'; - $oci = new \TestOCIStub(); + $oci = new TestOCIStub(); $stmt = $oci->prepare($sql); $this->assertInstanceOf(OCIStatement::class, $stmt); @@ -270,7 +275,7 @@ public function testPrepareFail() global $OCIStatementStatus; $OCIStatementStatus = false; $sql = 'select * from table where id = ? and date = ?'; - $oci = new \TestOCIStub(); + $oci = new TestOCIStub(); $this->expectException(OCIException::class); $stmt = $oci->prepare($sql); } @@ -278,7 +283,7 @@ public function testPrepareFail() public function testQuery() { $sql = 'select * from table'; - $oci = new \TestOCIStub(); + $oci = new TestOCIStub(); $stmt = $oci->query($sql); $this->assertInstanceOf(OCIStatement::class, $stmt); @@ -304,8 +309,8 @@ public function testQuery() public function testQueryWithModeParams() { $sql = 'select * from table'; - $oci = new \TestOCIStub(); - $stmt = $oci->query($sql, \PDO::FETCH_CLASS, 'stdClass', []); + $oci = new TestOCIStub(); + $stmt = $oci->query($sql, PDO::FETCH_CLASS, 'stdClass', []); $this->assertInstanceOf(OCIStatement::class, $stmt); // use reflection to test values of protected properties @@ -332,7 +337,7 @@ public function testQueryFail() global $OCIExecuteStatus; $OCIExecuteStatus = false; $sql = 'select * from table'; - $oci = new \TestOCIStub(); + $oci = new TestOCIStub(); $stmt = $oci->query($sql); $this->assertFalse($stmt); } @@ -340,7 +345,7 @@ public function testQueryFail() public function testQuote() { $this->assertFalse($this->oci->quote('String')); - $this->assertFalse($this->oci->quote('String', \PDO::PARAM_STR)); + $this->assertFalse($this->oci->quote('String', PDO::PARAM_STR)); } public function testRollBackInTransactionPasses() @@ -365,10 +370,12 @@ public function testRollBackNotInTransaction() public function testSetAttribute() { - $this->oci->setAttribute('attribute', 'value'); - $this->assertEquals('value', $this->oci->getAttribute('attribute')); - $this->oci->setAttribute('attribute', 4); - $this->assertEquals(4, $this->oci->getAttribute('attribute')); + $attr = 12345; + + $this->oci->setAttribute($attr, 'value'); + $this->assertEquals('value', $this->oci->getAttribute($attr)); + $this->oci->setAttribute($attr, 4); + $this->assertEquals(4, $this->oci->getAttribute($attr)); } public function testFlipExecuteMode() diff --git a/tests/OracleDBPDOProcessorTest.php b/tests/OracleDBPDOProcessorTest.php index 0a55374..2bd5d9e 100644 --- a/tests/OracleDBPDOProcessorTest.php +++ b/tests/OracleDBPDOProcessorTest.php @@ -1,5 +1,8 @@ getMockBuilder('ProcessorTestPDOStub')->getMock(); + $pdo = $this->getMockBuilder(ProcessorTestPDOStub::class)->getMock(); $pdo->expects($this->once())->method('lastInsertId')->with($this->equalTo('id'))->willReturn('1'); $connection = m::mock(Connection::class); diff --git a/tests/OracleDBQueryBuilderTest.php b/tests/OracleDBQueryBuilderTest.php index b3dde00..5cba562 100644 --- a/tests/OracleDBQueryBuilderTest.php +++ b/tests/OracleDBQueryBuilderTest.php @@ -1,6 +1,14 @@ getOracleBuilder(); $builder->select('w x.y.z as foo.bar')->from('baz'); $this->assertSame('select "w x"."y"."z" as "foo.bar" from "baz"', $builder->toSql()); - } + } public function testAddingSelects() { @@ -167,7 +178,7 @@ public function testWhenCallback() $builder = $this->getOracleBuilder(); $builder->select('*')->from('users')->when(false, $callback)->where('email', 'foo'); $this->assertSame('select * from "users" where "email" = ?', $builder->toSql()); - } + } public function testWhenCallbackWithReturn() { @@ -184,12 +195,12 @@ public function testWhenCallbackWithReturn() $builder = $this->getOracleBuilder(); $builder->select('*')->from('users')->when(false, $callback)->where('email', 'foo'); $this->assertSame('select * from "users" where "email" = ?', $builder->toSql()); - } + } public function testWhenCallbackWithDefault() { $callback = function ($query, $condition) { - $this->assertEquals('truthy', $condition); + $this->assertSame('truthy', $condition); $query->where('id', '=', 1); }; @@ -208,8 +219,8 @@ public function testWhenCallbackWithDefault() $builder = $this->getOracleBuilder(); $builder->select('*')->from('users')->when(0, $callback, $default)->where('email', 'foo'); $this->assertSame('select * from "users" where "id" = ? and "email" = ?', $builder->toSql()); - $this->assertEquals([0 => 2, 1 => 'foo'], $builder->getBindings()); - } + $this->assertEquals([0 => 2, 1 => 'foo'], $builder->getBindings()); + } public function testUnlessCallback() { @@ -254,7 +265,7 @@ public function testUnlessCallbackWithDefault() }; $default = function ($query, $condition) { - $this->assertEquals('truthy', $condition); + $this->assertSame('truthy', $condition); $query->where('id', '=', 2); }; @@ -268,7 +279,7 @@ public function testUnlessCallbackWithDefault() $builder->select('*')->from('users')->unless('truthy', $callback, $default)->where('email', 'foo'); $this->assertSame('select * from "users" where "id" = ? and "email" = ?', $builder->toSql()); $this->assertEquals([0 => 2, 1 => 'foo'], $builder->getBindings()); - } + } public function testTapCallback() { @@ -279,16 +290,24 @@ public function testTapCallback() $builder = $this->getOracleBuilder(); $builder->select('*')->from('users')->tap($callback)->where('email', 'foo'); $this->assertSame('select * from "users" where "id" = ? and "email" = ?', $builder->toSql()); - } + } public function testBasicWheres() { $builder = $this->getOracleBuilder(); $builder->select('*')->from('users')->where('id', '=', 1); - $this->assertEquals('select * from "users" where "id" = ?', $builder->toSql()); + $this->assertSame('select * from "users" where "id" = ?', $builder->toSql()); $this->assertEquals([0 => 1], $builder->getBindings()); } + public function testBasicWhereNot() + { + $builder = $this->getOracleBuilder(); + $builder->select('*')->from('users')->whereNot('name', 'foo')->whereNot('name', '<>', 'bar'); + $this->assertSame('select * from "users" where not "name" = ? and not "name" <> ?', $builder->toSql()); + $this->assertEquals(['foo', 'bar'], $builder->getBindings()); + } + public function testWheresWithArrayValue() { $builder = $this->getOracleBuilder(); @@ -314,8 +333,8 @@ public function testWheresWithArrayValue() $builder = $this->getOracleBuilder(); $builder->select('*')->from('users')->where('id', '=', [[12, 30]]); $this->assertSame('select * from "users" where "id" = ?', $builder->toSql()); - $this->assertEquals([0 => 12], $builder->getBindings()); - } + $this->assertEquals([0 => 12], $builder->getBindings()); + } public function testDateBasedWheresAcceptsTwoArguments() { @@ -384,7 +403,7 @@ public function testWhereDateOracle() $builder = $this->getOracleBuilder(); $builder->select('*')->from('users')->whereDate('created_at', '=', new Raw("TO_CHAR(sysdate - 1, 'YYYY-MM-DD')")); $this->assertSame('select * from "users" where TO_CHAR("created_at", \'YYYY-MM-DD\') = TO_CHAR(sysdate - 1, \'YYYY-MM-DD\')', $builder->toSql()); - } + } public function testWhereDayOracle() { @@ -400,7 +419,7 @@ public function testOrWhereDayOracle() $builder->select('*')->from('users')->whereDay('created_at', '=', 1)->orWhereDay('created_at', '=', 2); $this->assertSame('select * from "users" where TO_CHAR("created_at", \'DD\') = ? or TO_CHAR("created_at", \'DD\') = ?', $builder->toSql()); $this->assertEquals([0 => 1, 1 => 2], $builder->getBindings()); - } + } public function testWhereMonthOracle() { @@ -432,7 +451,7 @@ public function testOrWhereYearOracle() $builder->select('*')->from('users')->whereYear('created_at', '=', 2014)->orWhereYear('created_at', '=', 2015); $this->assertSame('select * from "users" where TO_CHAR("created_at", \'YYYY\') = ? or TO_CHAR("created_at", \'YYYY\') = ?', $builder->toSql()); $this->assertEquals([0 => 2014, 1 => 2015], $builder->getBindings()); - } + } public function testWhereTimeOracle() { @@ -467,7 +486,7 @@ public function testWhereLikeOracle() $this->assertSame('select * from "users" where "id" not like ?', $builder->toSql()); $this->assertEquals([0 => '1'], $builder->getBindings()); } - + public function testWhereBetweens() { $builder = $this->getOracleBuilder(); @@ -493,7 +512,18 @@ public function testWhereBetweens() $builder = $this->getOracleBuilder(); $builder->select('*')->from('users')->whereBetween('id', [new Raw(1), new Raw(2)]); $this->assertSame('select * from "users" where "id" between 1 and 2', $builder->toSql()); - $this->assertEquals([], $builder->getBindings()); + $this->assertEquals([], $builder->getBindings()); + + $builder = $this->getOracleBuilder(); + $period = Carbon::now()->toPeriod(Carbon::now()->addDay()); + $builder->select('*')->from('users')->whereBetween('created_at', $period); + $this->assertSame('select * from "users" where "created_at" between ? and ?', $builder->toSql()); + $this->assertEquals($period->toArray(), $builder->getBindings()); + + $builder = $this->getOracleBuilder(); + $builder->select('*')->from('users')->whereBetween('id', collect([1, 2])); + $this->assertSame('select * from "users" where "id" between ? and ?', $builder->toSql()); + $this->assertEquals([0 => 1, 1 => 2], $builder->getBindings()); } public function testWhereBetweenColumns() @@ -522,6 +552,14 @@ public function testBasicOrWheres() $this->assertEquals([0 => 1, 1 => 'foo'], $builder->getBindings()); } + public function testBasicOrWhereNot() + { + $builder = $this->getOracleBuilder(); + $builder->select('*')->from('users')->orWhereNot('name', 'foo')->orWhereNot('name', '<>', 'bar'); + $this->assertSame('select * from "users" where not "name" = ? or not "name" <> ?', $builder->toSql()); + $this->assertEquals(['foo', 'bar'], $builder->getBindings()); + } + public function testRawWheres() { $builder = $this->getOracleBuilder(); @@ -632,7 +670,7 @@ public function testOrWhereIntegerNotInRaw() $builder->select('*')->from('users')->where('id', '=', 1)->orWhereIntegerNotInRaw('id', ['1a', 2]); $this->assertSame('select * from "users" where "id" = ? or "id" not in (1, 2)', $builder->toSql()); $this->assertEquals([0 => 1], $builder->getBindings()); - } + } public function testEmptyWhereIntegerInRaw() { @@ -648,7 +686,7 @@ public function testEmptyWhereIntegerNotInRaw() $builder->select('*')->from('users')->whereIntegerNotInRaw('id', []); $this->assertSame('select * from "users" where 1 = 1', $builder->toSql()); $this->assertEquals([], $builder->getBindings()); - } + } public function testBasicWhereColumn() { @@ -731,7 +769,7 @@ public function testUnionWithJoin() })); $this->assertSame('(select * from "users") union (select * from "dogs" inner join "breeds" on "dogs"."breed_id" = "breeds"."id" and "breeds"."is_native" = ?)', $builder->toSql()); $this->assertEquals([0 => 1], $builder->getBindings()); - } + } public function testOracleUnionOrderBys() { @@ -764,6 +802,25 @@ public function testUnionAggregate() $builder->from('posts')->union($this->getOracleBuilder()->from('videos'))->count(); } + /** + * @doesNotPerformAssertions + */ + public function testHavingAggregate() + { + $expected = 'select count(*) as aggregate from (select (select "count(*)" from "videos" where "posts"."id" = "videos"."post_id") as "videos_count" from "posts" having "videos_count" > ?) as "temp_table"'; + $builder = $this->getOracleBuilder(); + $builder->getConnection()->shouldReceive('getDatabaseName'); + $builder->getConnection()->shouldReceive('select')->once()->with($expected, [0 => 1], true)->andReturn([['aggregate' => 1]]); + $builder->getProcessor()->shouldReceive('processSelect')->once()->andReturnUsing(function ($builder, $results) { + return $results; + }); + + $builder->from('posts')->selectSub(function ($query) { + $query->from('videos')->select('count(*)')->whereColumn('posts.id', '=', 'videos.post_id'); + }, 'videos_count')->having('videos_count', '>', 1); + $builder->count(); + } + public function testSubSelectWhereIns() { $builder = $this->getOracleBuilder(); @@ -802,7 +859,7 @@ public function testJsonWhereNull() $builder = $this->getOracleBuilder(); $builder->select('*')->from('users')->whereNull('items->id'); $builder->toSql(); - } + } public function testJsonWhereNotNull() { @@ -825,7 +882,7 @@ public function testArrayWhereNulls() $builder->select('*')->from('users')->where('id', '=', 1)->orWhereNull(['id', 'expires_at']); $this->assertSame('select * from "users" where "id" = ? or "id" is null or "expires_at" is null', $builder->toSql()); $this->assertEquals([0 => 1], $builder->getBindings()); - } + } public function testBasicWhereNotNulls() { @@ -937,7 +994,7 @@ public function testReorder() $this->assertEquals([true], $builder->getBindings()); $builder->reorder(); $this->assertEquals([], $builder->getBindings()); - } + } public function testOrderBySubQueries() { @@ -961,7 +1018,7 @@ public function testOrderBySubQueries() ->orderBy($this->getOracleBuilder()->selectRaw('field(category, ?, ?)', ['news', 'opinion'])); $this->assertSame('(select * from "posts" where "public" = ?) union all (select * from "videos" where "public" = ?) order by (select field(category, ?, ?)) asc', $builder->toSql()); $this->assertEquals([1, 1, 'news', 'opinion'], $builder->getBindings()); - } + } public function testOrderByInvalidDirectionParam() { @@ -969,7 +1026,7 @@ public function testOrderByInvalidDirectionParam() $builder = $this->getOracleBuilder(); $builder->select('*')->from('users')->orderBy('age', 'asec'); - } + } public function testHavings() { @@ -1000,6 +1057,25 @@ public function testHavings() $this->assertSame('select "category", count(*) as "total" from "item" where "department" = ? group by "category" having "total" > ?', $builder->toSql()); } + public function testNestedHavings() + { + $builder = $this->getOracleBuilder(); + $builder->select('*')->from('users')->having('email', '=', 'foo')->orHaving(function ($q) { + $q->having('name', '=', 'bar')->having('age', '=', 25); + }); + $this->assertSame('select * from "users" having "email" = ? or ("name" = ? and "age" = ?)', $builder->toSql()); + $this->assertEquals([0 => 'foo', 1 => 'bar', 2 => 25], $builder->getBindings()); + } + + public function testNestedHavingBindings() + { + $builder = $this->getOracleBuilder(); + $builder->having('email', '=', 'foo')->having(function ($q) { + $q->selectRaw('?', ['ignore'])->having('name', '=', 'bar'); + }); + $this->assertEquals([0 => 'foo', 1 => 'bar'], $builder->getBindings()); + } + public function testHavingBetweens() { $builder = $this->getOracleBuilder(); @@ -1011,7 +1087,77 @@ public function testHavingBetweens() $builder->select('*')->from('users')->havingBetween('id', [[1, 2], [3, 4]]); $this->assertSame('select * from "users" having "id" between ? and ?', $builder->toSql()); $this->assertEquals([0 => 1, 1 => 2], $builder->getBindings()); - } + } + + public function testHavingNull() + { + $builder = $this->getOracleBuilder(); + $builder->select('*')->from('users')->havingNull('email'); + $this->assertSame('select * from "users" having "email" is null', $builder->toSql()); + + $builder = $this->getOracleBuilder(); + $builder->select('*')->from('users') + ->havingNull('email') + ->havingNull('phone'); + $this->assertSame('select * from "users" having "email" is null and "phone" is null', $builder->toSql()); + + $builder = $this->getOracleBuilder(); + $builder->select('*')->from('users') + ->orHavingNull('email') + ->orHavingNull('phone'); + $this->assertSame('select * from "users" having "email" is null or "phone" is null', $builder->toSql()); + + $builder = $this->getOracleBuilder(); + $builder->select('*')->from('users')->groupBy('email')->havingNull('email'); + $this->assertSame('select * from "users" group by "email" having "email" is null', $builder->toSql()); + + $builder = $this->getOracleBuilder(); + $builder->select('email as foo_email')->from('users')->havingNull('foo_email'); + $this->assertSame('select "email" as "foo_email" from "users" having "foo_email" is null', $builder->toSql()); + + $builder = $this->getOracleBuilder(); + $builder->select(['category', new Raw('count(*) as "total"')])->from('item')->where('department', '=', 'popular')->groupBy('category')->havingNull('total'); + $this->assertSame('select "category", count(*) as "total" from "item" where "department" = ? group by "category" having "total" is null', $builder->toSql()); + + $builder = $this->getOracleBuilder(); + $builder->select(['category', new Raw('count(*) as "total"')])->from('item')->where('department', '=', 'popular')->groupBy('category')->havingNull('total'); + $this->assertSame('select "category", count(*) as "total" from "item" where "department" = ? group by "category" having "total" is null', $builder->toSql()); + } + + public function testHavingNotNull() + { + $builder = $this->getOracleBuilder(); + $builder->select('*')->from('users')->havingNotNull('email'); + $this->assertSame('select * from "users" having "email" is not null', $builder->toSql()); + + $builder = $this->getOracleBuilder(); + $builder->select('*')->from('users') + ->havingNotNull('email') + ->havingNotNull('phone'); + $this->assertSame('select * from "users" having "email" is not null and "phone" is not null', $builder->toSql()); + + $builder = $this->getOracleBuilder(); + $builder->select('*')->from('users') + ->orHavingNotNull('email') + ->orHavingNotNull('phone'); + $this->assertSame('select * from "users" having "email" is not null or "phone" is not null', $builder->toSql()); + + $builder = $this->getOracleBuilder(); + $builder->select('*')->from('users')->groupBy('email')->havingNotNull('email'); + $this->assertSame('select * from "users" group by "email" having "email" is not null', $builder->toSql()); + + $builder = $this->getOracleBuilder(); + $builder->select('email as foo_email')->from('users')->havingNotNull('foo_email'); + $this->assertSame('select "email" as "foo_email" from "users" having "foo_email" is not null', $builder->toSql()); + + $builder = $this->getOracleBuilder(); + $builder->select(['category', new Raw('count(*) as "total"')])->from('item')->where('department', '=', 'popular')->groupBy('category')->havingNotNull('total'); + $this->assertSame('select "category", count(*) as "total" from "item" where "department" = ? group by "category" having "total" is not null', $builder->toSql()); + + $builder = $this->getOracleBuilder(); + $builder->select(['category', new Raw('count(*) as "total"')])->from('item')->where('department', '=', 'popular')->groupBy('category')->havingNotNull('total'); + $this->assertSame('select "category", count(*) as "total" from "item" where "department" = ? group by "category" having "total" is not null', $builder->toSql()); + } public function testHavingShortcut() { @@ -1052,7 +1198,7 @@ public function testRawHavings() $builder = $this->getOracleBuilder(); $builder->select('*')->from('users')->havingBetween('last_login_date', ['2018-11-16', '2018-12-16'])->orHavingRaw('user_foo < user_bar'); - $this->assertSame('select * from "users" having "last_login_date" between ? and ? or user_foo < user_bar', $builder->toSql()); + $this->assertSame('select * from "users" having "last_login_date" between ? and ? or user_foo < user_bar', $builder->toSql()); } public function testLimitsAndOffsets() @@ -1065,6 +1211,14 @@ public function testLimitsAndOffsets() $builder->select('*')->from('users')->offset(5)->limit(10); $this->assertEquals('select t2.* from ( select rownum AS "rn", t1.* from (select * from "users") t1 ) t2 where t2."rn" between 6 and 15', $builder->toSql()); + $builder = $this->getOracleBuilder(); + $builder->select('*')->from('users')->limit(null); + $this->assertSame('select * from "users"', $builder->toSql()); + + $builder = $this->getOracleBuilder(); + $builder->select('*')->from('users')->limit(0); + $this->assertSame('select * from (select * from "users") where rownum < 1', $builder->toSql()); + $builder = $this->getOracleBuilder(); $builder->select('*')->from('users')->skip(5)->take(10); $this->assertEquals('select t2.* from ( select rownum AS "rn", t1.* from (select * from "users") t1 ) t2 where t2."rn" between 6 and 15', $builder->toSql()); @@ -1084,6 +1238,14 @@ public function testLimitsAndOffsets() $builder = $this->getOracleBuilder(); $builder->select('*')->from('users')->skip(-5)->take(-10); $this->assertSame('select * from (select * from "users") where rownum >= 1', $builder->toSql()); + + $builder = $this->getOracleBuilder(); + $builder->select('*')->from('users')->skip(null)->take(null); + $this->assertSame('select * from (select * from "users") where rownum >= 1', $builder->toSql()); + + $builder = $this->getOracleBuilder(); + $builder->select('*')->from('users')->skip(5)->take(null); + $this->assertSame('select * from (select * from "users") where rownum >= 6', $builder->toSql()); } public function testForPage() @@ -1111,7 +1273,7 @@ public function testForPage() $builder = $this->getOracleBuilder(); $builder->select('*')->from('users')->forPage(-2, 0); $this->assertSame('select * from (select * from "users") where rownum < 1', $builder->toSql()); - } + } public function testGetCountForPaginationWithBindings() { @@ -1139,7 +1301,7 @@ public function testGetCountForPaginationWithColumnAliases() $count = $builder->getCountForPagination($columns); $this->assertEquals(1, $count); - } + } public function testGetCountForPaginationWithUnion() { @@ -1153,7 +1315,7 @@ public function testGetCountForPaginationWithUnion() $count = $builder->getCountForPagination(); $this->assertEquals(1, $count); - } + } public function testWhereShortcut() { @@ -1179,7 +1341,7 @@ public function testWhereWithArrayConditions() $builder->select('*')->from('users')->where([['foo', 1], ['bar', '<', 2]]); $this->assertSame('select * from "users" where ("foo" = ? and "bar" < ?)', $builder->toSql()); $this->assertEquals([0 => 1, 1 => 2], $builder->getBindings()); - } + } public function testNestedWheres() { @@ -1200,6 +1362,30 @@ public function testNestedWhereBindings() $this->assertEquals([0 => 'foo', 1 => 'bar'], $builder->getBindings()); } + public function testWhereNot() + { + $builder = $this->getOracleBuilder(); + $builder->select('*')->from('users')->whereNot(function ($q) { + $q->where('email', '=', 'foo'); + }); + $this->assertSame('select * from "users" where not ("email" = ?)', $builder->toSql()); + $this->assertEquals([0 => 'foo'], $builder->getBindings()); + + $builder = $this->getOracleBuilder(); + $builder->select('*')->from('users')->where('name', '=', 'bar')->whereNot(function ($q) { + $q->where('email', '=', 'foo'); + }); + $this->assertSame('select * from "users" where "name" = ? and not ("email" = ?)', $builder->toSql()); + $this->assertEquals([0 => 'bar', 1 => 'foo'], $builder->getBindings()); + + $builder = $this->getOracleBuilder(); + $builder->select('*')->from('users')->where('name', '=', 'bar')->orWhereNot(function ($q) { + $q->where('email', '=', 'foo'); + }); + $this->assertSame('select * from "users" where "name" = ? or not ("email" = ?)', $builder->toSql()); + $this->assertEquals([0 => 'bar', 1 => 'foo'], $builder->getBindings()); + } + public function testFullSubSelects() { $builder = $this->getOracleBuilder(); @@ -1270,7 +1456,7 @@ public function testCrossJoinSubs() $builder = $this->getOracleBuilder(); $builder->selectRaw('(sale / "overall".sales) * 100 AS percent_of_total')->from('sales')->crossJoinSub($this->getOracleBuilder()->selectRaw('SUM(sale) AS sales')->from('sales'), 'overall'); $this->assertSame('select (sale / "overall".sales) * 100 AS percent_of_total from "sales" cross join (select SUM(sale) AS sales from "sales") "overall"', $builder->toSql()); - } + } public function testComplexJoin() { @@ -1400,7 +1586,7 @@ public function testJoinsWithNestedConditions() }); $this->assertSame('select * from "users" left join "contacts" on "users"."id" = "contacts"."id" and "contacts"."is_active" = ? or (("contacts"."country" = ? or "contacts"."type" = "users"."type") and ("contacts"."country" = ? or "contacts"."is_partner" is null))', $builder->toSql()); $this->assertEquals([1, 'UK', 'US'], $builder->getBindings()); - } + } public function testJoinsWithAdvancedConditions() { @@ -1440,7 +1626,7 @@ public function testJoinsWithSubqueryCondition() }); $this->assertSame('select * from "users" left join "contacts" on "users"."id" = "contacts"."id" and exists (select 1 from "contact_types" where contact_types.id = contacts.contact_type_id and "category_id" = ? and "deleted_at" is null)', $builder->toSql()); $this->assertEquals(['1'], $builder->getBindings()); - } + } public function testJoinsWithAdvancedSubqueryCondition() { @@ -1459,7 +1645,7 @@ public function testJoinsWithAdvancedSubqueryCondition() }); $this->assertSame('select * from "users" left join "contacts" on "users"."id" = "contacts"."id" and exists (select 1 from "contact_types" where contact_types.id = contacts.contact_type_id and "category_id" = ? and "deleted_at" is null and "level_id" in (select "id" from "levels" where "is_active" = ?))', $builder->toSql()); $this->assertEquals(['1', true], $builder->getBindings()); - } + } public function testJoinsWithNestedJoins() { @@ -1468,7 +1654,7 @@ public function testJoinsWithNestedJoins() $j->on('users.id', 'contacts.id')->join('contact_types', 'contacts.contact_type_id', '=', 'contact_types.id'); }); $this->assertSame('select "users"."id", "contacts"."id", "contact_types"."id" from "users" left join ("contacts" inner join "contact_types" on "contacts"."contact_type_id" = "contact_types"."id") on "users"."id" = "contacts"."id"', $builder->toSql()); - } + } public function testJoinsWithMultipleNestedJoins() { @@ -1487,7 +1673,7 @@ public function testJoinsWithMultipleNestedJoins() }); $this->assertSame('select "users"."id", "contacts"."id", "contact_types"."id", "countrys"."id", "planets"."id" from "users" left join ("contacts" inner join "contact_types" on "contacts"."contact_type_id" = "contact_types"."id" left join ("countrys" inner join "planets" on "countrys"."planet_id" = "planet"."id" and "planet"."is_settled" = ? and "planet"."population" >= ?) on "contacts"."country" = "countrys"."country") on "users"."id" = "contacts"."id"', $builder->toSql()); $this->assertEquals(['1', 10000], $builder->getBindings()); - } + } public function testJoinsWithNestedJoinWithAdvancedSubqueryCondition() { @@ -1541,7 +1727,7 @@ public function testJoinSub() $this->expectException(InvalidArgumentException::class); $builder = $this->getOracleBuilder(); $builder->from('users')->joinSub(['foo'], 'sub', 'users.id', '=', 'sub.id'); - } + } public function testJoinSubWithPrefix() { @@ -1663,7 +1849,7 @@ public function testAggregateFunctions() $builder = $this->getOracleBuilder(); $builder->getConnection()->shouldReceive('select')->once()->with('select t2."rn" as "exists" from ( select rownum AS "rn", t1.* from (select * from "users") t1 ) t2 where t2."rn" between 1 and 1', [], true)->andReturn([['exists' => 0]]); $results = $builder->from('users')->doesntExist(); - $this->assertTrue($results); + $this->assertTrue($results); $builder = $this->getOracleBuilder(); $builder->getConnection()->shouldReceive('select')->once()->with('select max("id") as aggregate from "users"', [], true)->andReturn([['aggregate' => 1]]); @@ -1695,10 +1881,10 @@ public function testExistsOr() $builder = $this->getOracleBuilder(); $builder->getConnection()->shouldReceive('select')->andReturn([['exists' => 0]]); $results = $builder->from('users')->doesntExistOr(function () { - throw new RuntimeException(); + throw new RuntimeException; }); $this->assertTrue($results); - } + } public function testDoesntExistsOr() { @@ -1711,10 +1897,10 @@ public function testDoesntExistsOr() $builder = $this->getOracleBuilder(); $builder->getConnection()->shouldReceive('select')->andReturn([['exists' => 1]]); $results = $builder->from('users')->existsOr(function () { - throw new RuntimeException(); + throw new RuntimeException; }); $this->assertTrue($results); - } + } public function testAggregateResetFollowedByGet() { @@ -1808,14 +1994,14 @@ function (Builder $query) { ); $this->assertEquals(1, $result); - } + } public function testInsertUsingInvalidSubquery() { $this->expectException(InvalidArgumentException::class); $builder = $this->getOracleBuilder(); $builder->from('table1')->insertUsing(['foo'], ['bar']); - } + } public function testInsertOrIgnoreMethod() { @@ -1823,7 +2009,7 @@ public function testInsertOrIgnoreMethod() $this->expectExceptionMessage('does not support'); $builder = $this->getOracleBuilder(); $builder->from('users')->insertOrIgnore(['email' => 'foo']); - } + } public function testMultipleInsertMethod() { @@ -1845,7 +2031,7 @@ public function testInsertGetIdMethodRemovesExpressions() { $builder = $this->getOracleBuilder(); $builder->getProcessor()->shouldReceive('processInsertGetId')->once()->with($builder, 'insert into "users" ("email", "bar") values (?, bar) returning "id" into ?', ['foo'], 'id')->andReturn(1); - $result = $builder->from('users')->insertGetId(['email' => 'foo', 'bar' => new Illuminate\Database\Query\Expression('bar')], 'id'); + $result = $builder->from('users')->insertGetId(['email' => 'foo', 'bar' => new Raw('bar')], 'id'); $this->assertEquals(1, $result); } @@ -1855,7 +2041,7 @@ public function testInsertGetIdWithEmptyValues() $this->expectExceptionMessage('does not support'); $builder = $this->getOracleBuilder(); $builder->from('users')->insertGetId([]); - } + } public function testInsertMethodRespectsRawBindings() { @@ -1892,7 +2078,7 @@ public function testUpsertMethod() $this->expectExceptionMessage('does not support'); $builder = $this->getOracleBuilder(); $builder->from('users')->upsert([['email' => 'foo', 'name' => 'bar'], ['name' => 'bar2', 'email' => 'foo2']], 'email'); - } + } public function testUpsertMethodWithUpdateColumns() { @@ -1900,7 +2086,7 @@ public function testUpsertMethodWithUpdateColumns() $this->expectExceptionMessage('does not support'); $builder = $this->getOracleBuilder(); $builder->from('users')->upsert([['email' => 'foo', 'name' => 'bar'], ['name' => 'bar2', 'email' => 'foo2']], 'email', ['name']); - } + } public function testUpdateMethodWithJoins() { @@ -1967,7 +2153,7 @@ public function testUpdateOrInsertMethodWorksWithEmptyUpdateValues() $this->assertTrue($builder->updateOrInsert(['email' => 'foo'])); $builder->shouldNotHaveReceived('update'); - } + } public function testDeleteMethod() { @@ -1998,7 +2184,7 @@ public function testDeleteWithJoinMethod() $this->expectExceptionMessage('does not support'); $builder = $this->getOracleBuilder(); $builder->from('users')->join('contacts', 'users.id', '=', 'contacts.id')->where('users.email', '=', 'foo')->orderBy('users.id')->limit(1)->delete(); - } + } /** * @doesNotPerformAssertions @@ -2010,13 +2196,136 @@ public function testTruncateMethod() $builder->from('users')->truncate(); } + public function testPreserveAddsClosureToArray() + { + $builder = $this->getOracleBuilder(); + $builder->beforeQuery(function () { + }); + $this->assertCount(1, $builder->beforeQueryCallbacks); + $this->assertInstanceOf(Closure::class, $builder->beforeQueryCallbacks[0]); + } + + public function testApplyPreserveCleansArray() + { + $builder = $this->getOracleBuilder(); + $builder->beforeQuery(function () { + }); + $this->assertCount(1, $builder->beforeQueryCallbacks); + $builder->applyBeforeQueryCallbacks(); + $this->assertCount(0, $builder->beforeQueryCallbacks); + } + + public function testPreservedAreAppliedByToSql() + { + $builder = $this->getOracleBuilder(); + $builder->beforeQuery(function ($builder) { + $builder->where('foo', 'bar'); + }); + $this->assertSame('select * where "foo" = ?', $builder->toSql()); + $this->assertEquals(['bar'], $builder->getBindings()); + } + + /** + * @doesNotPerformAssertions + */ + public function testPreservedAreAppliedByInsert() + { + $builder = $this->getOracleBuilder(); + $builder->getConnection()->shouldReceive('insert')->once()->with('insert into "users" ("email") values (?)', ['foo']); + $builder->beforeQuery(function ($builder) { + $builder->from('users'); + }); + $builder->insert(['email' => 'foo']); + } + + /** + * @doesNotPerformAssertions + */ + public function testPreservedAreAppliedByInsertGetId() + { + $this->called = false; + $builder = $this->getOracleBuilder(); + $builder->getProcessor()->shouldReceive('processInsertGetId')->once()->with($builder, 'insert into "users" ("email") values (?) returning "id" into ?', ['foo'], 'id'); + $builder->beforeQuery(function ($builder) { + $builder->from('users'); + }); + $builder->insertGetId(['email' => 'foo'], 'id'); + } + + /** + * @doesNotPerformAssertions + */ + public function testPreservedAreAppliedByInsertUsing() + { + $builder = $this->getOracleBuilder(); + $builder->getConnection()->shouldReceive('affectingStatement')->once()->with('insert into "users" () select *', []); + $builder->beforeQuery(function ($builder) { + $builder->from('users'); + }); + $builder->insertUsing([], $this->getOracleBuilder()); + } + + // NOTE: testPreservedAreAppliedByUpsert omitted since ->upsert is on the "not implemented" list + + /** + * @doesNotPerformAssertions + */ + public function testPreservedAreAppliedByUpdate() + { + $builder = $this->getOracleBuilder(); + $builder->getConnection()->shouldReceive('update')->once()->with('update "users" set "email" = ? where "id" = ?', ['foo', 1]); + $builder->from('users')->beforeQuery(function ($builder) { + $builder->where('id', 1); + }); + $builder->update(['email' => 'foo']); + } + + /** + * @doesNotPerformAssertions + */ + public function testPreservedAreAppliedByDelete() + { + $builder = $this->getOracleBuilder(); + $builder->getConnection()->shouldReceive('delete')->once()->with('delete from "users"', []); + $builder->beforeQuery(function ($builder) { + $builder->from('users'); + }); + $builder->delete(); + } + + /** + * @doesNotPerformAssertions + */ + public function testPreservedAreAppliedByTruncate() + { + $builder = $this->getOracleBuilder(); + $builder->getConnection()->shouldReceive('statement')->once()->with('truncate table "users"', []); + $builder->beforeQuery(function ($builder) { + $builder->from('users'); + }); + $builder->truncate(); + } + + /** + * @doesNotPerformAssertions + */ + public function testPreservedAreAppliedByExists() + { + $builder = $this->getOracleBuilder(); + $builder->getConnection()->shouldReceive('select')->once()->with('select t2."rn" as "exists" from ( select rownum AS "rn", t1.* from (select * from "users") t1 ) t2 where t2."rn" between 1 and 1', [], true); + $builder->beforeQuery(function ($builder) { + $builder->from('users'); + }); + $builder->exists(); + } + public function testUpdateWrappingJson() { $this->expectException(RuntimeException::class); $this->expectExceptionMessage('does not support'); $builder = $this->getOracleBuilder(); $builder->from('users')->where('active', '=', 1)->update(['name->first_name' => 'John', 'name->last_name' => 'Doe']); - } + } public function testUpdateWrappingNestedJson() { @@ -2037,7 +2346,7 @@ public function testUpdateWrappingJsonArray() 'group_id' => new Raw('45'), 'created_at' => new DateTime('2019-08-06'), ]); - } + } public function testUpdateWrappingNestedJsonArray() { @@ -2052,7 +2361,7 @@ public function testUpdateWrappingNestedJsonArray() 'options->sharing->twitter' => 'username', 'created_at' => new DateTime('2019-08-06'), ]); - } + } public function testUpdateWithJsonPreparesBindingsCorrectly() { @@ -2060,8 +2369,8 @@ public function testUpdateWithJsonPreparesBindingsCorrectly() $this->expectExceptionMessage('does not support'); $builder = $this->getOracleBuilder(); $builder->from('users')->where('id', '=', 0)->update(['options->enable' => false, 'updated_at' => '2015-05-26 22:02:06']); - } - + } + public function testWrappingJsonWithString() { $this->expectException(RuntimeException::class); @@ -2100,7 +2409,7 @@ public function testWrappingJsonWithBooleanAndIntegerThatLooksLikeOne() $this->expectExceptionMessage('does not support'); $builder = $this->getOracleBuilder(); $builder->select('*')->from('users')->where('items->available', '=', true)->where('items->active', '=', false)->where('items->number_available', '=', 0)->toSql(); - } + } public function testJsonPathEscaping() { @@ -2118,6 +2427,17 @@ public function testWrappingJson() $builder->select('items->price')->from('users')->where('users.items->price', '=', 1)->orderBy('items->price')->toSql(); } + public function testBitwiseOperators() + { + $builder = $this->getOracleBuilder(); + $builder->select('*')->from('users')->where('bar', '&', 1); + $this->assertSame('select * from "users" where "bar" & ?', $builder->toSql()); + + $builder = $this->getOracleBuilder(); + $builder->select('*')->from('users')->having('bar', '&', 1); + $this->assertSame('select * from "users" having "bar" & ?', $builder->toSql()); + } + public function testMergeWheresCanMergeWheresAndBindings() { $builder = $this->getOracleBuilder(); @@ -2324,21 +2644,21 @@ public function testSubSelectResetBindings() $this->assertSame('select * from "one"', $builder->toSql()); $this->assertEquals([], $builder->getBindings()); - } + } public function testUppercaseLeadingBooleansAreRemoved() { $builder = $this->getOracleBuilder(); $builder->select('*')->from('users')->where('name', '=', 'Taylor', 'AND'); $this->assertSame('select * from "users" where "name" = ?', $builder->toSql()); - } + } public function testLowercaseLeadingBooleansAreRemoved() { $builder = $this->getOracleBuilder(); $builder->select('*')->from('users')->where('name', '=', 'Taylor', 'and'); $this->assertSame('select * from "users" where "name" = ?', $builder->toSql()); - } + } /** * @doesNotPerformAssertions @@ -2364,7 +2684,7 @@ public function testChunkWithLastChunkComplete() $builder->chunk(2, function ($results) use ($callbackAssertor) { $callbackAssertor->doSomething($results); }); - } + } /** * @doesNotPerformAssertions @@ -2416,7 +2736,7 @@ public function testChunkCanBeStoppedByReturningFalse() /** * @doesNotPerformAssertions - */ + */ public function testChunkWithCountZero() { $builder = $this->getMockQueryBuilder(); @@ -2432,11 +2752,11 @@ public function testChunkWithCountZero() $builder->chunk(0, function ($results) use ($callbackAssertor) { $callbackAssertor->doSomething($results); }); - } + } /** * @doesNotPerformAssertions - */ + */ public function testChunkPaginatesUsingIdWithLastChunkComplete() { $builder = $this->getMockQueryBuilder(); @@ -2462,7 +2782,7 @@ public function testChunkPaginatesUsingIdWithLastChunkComplete() /** * @doesNotPerformAssertions - */ + */ public function testChunkPaginatesUsingIdWithLastChunkPartial() { $builder = $this->getMockQueryBuilder(); @@ -2485,7 +2805,7 @@ public function testChunkPaginatesUsingIdWithLastChunkPartial() /** * @doesNotPerformAssertions - */ + */ public function testChunkPaginatesUsingIdWithCountZero() { $builder = $this->getMockQueryBuilder(); @@ -2501,7 +2821,7 @@ public function testChunkPaginatesUsingIdWithCountZero() $builder->chunkById(0, function ($results) use ($callbackAssertor) { $callbackAssertor->doSomething($results); }, 'someIdField'); - } + } /** * @doesNotPerformAssertions @@ -2524,7 +2844,7 @@ public function testChunkPaginatesUsingIdWithAlias() $builder->chunkById(2, function ($results) use ($callbackAssertor) { $callbackAssertor->doSomething($results); }, 'table.id', 'table_id'); - } + } public function testPaginate() { @@ -2611,7 +2931,7 @@ public function testPaginateWhenNoResults() 'path' => $path, 'pageName' => $pageName, ]), $result); - } + } public function testPaginateWithSpecificColumns() { @@ -2638,7 +2958,312 @@ public function testPaginateWithSpecificColumns() 'path' => $path, 'pageName' => $pageName, ]), $result); - } + } + + public function testCursorPaginate() + { + $perPage = 16; + $columns = ['test']; + $cursorName = 'cursor-name'; + $cursor = new Cursor(['test' => 'bar']); + $builder = $this->getMockQueryBuilder(); + $builder->from('foobar')->orderBy('test'); + $builder->shouldReceive('newQuery')->andReturnUsing(function () use ($builder) { + return new Builder($builder->connection, $builder->grammar, $builder->processor); + }); + + $path = 'http://foo.bar?cursor='.$cursor->encode(); + + $results = collect([['test' => 'foo'], ['test' => 'bar']]); + + $builder->shouldReceive('get')->once()->andReturnUsing(function () use ($builder, $results) { + $this->assertEquals( + 'select * from "foobar" where ("test" > ?) order by "test" asc limit 17', + $builder->toSql()); + $this->assertEquals(['bar'], $builder->bindings['where']); + + return $results; + }); + + Paginator::currentPathResolver(function () use ($path) { + return $path; + }); + + $result = $builder->cursorPaginate($perPage, $columns, $cursorName, $cursor); + + $this->assertEquals(new CursorPaginator($results, $perPage, $cursor, [ + 'path' => $path, + 'cursorName' => $cursorName, + 'parameters' => ['test'], + ]), $result); + } + + public function testCursorPaginateMultipleOrderColumns() + { + $perPage = 16; + $columns = ['test', 'another']; + $cursorName = 'cursor-name'; + $cursor = new Cursor(['test' => 'bar', 'another' => 'foo']); + $builder = $this->getMockQueryBuilder(); + $builder->from('foobar')->orderBy('test')->orderBy('another'); + $builder->shouldReceive('newQuery')->andReturnUsing(function () use ($builder) { + return new Builder($builder->connection, $builder->grammar, $builder->processor); + }); + + $path = 'http://foo.bar?cursor='.$cursor->encode(); + + $results = collect([['test' => 'foo', 'another' => 1], ['test' => 'bar', 'another' => 2]]); + + $builder->shouldReceive('get')->once()->andReturnUsing(function () use ($builder, $results) { + $this->assertEquals( + 'select * from "foobar" where ("test" > ? or ("test" = ? and ("another" > ?))) order by "test" asc, "another" asc limit 17', + $builder->toSql() + ); + $this->assertEquals(['bar', 'bar', 'foo'], $builder->bindings['where']); + + return $results; + }); + + Paginator::currentPathResolver(function () use ($path) { + return $path; + }); + + $result = $builder->cursorPaginate($perPage, $columns, $cursorName, $cursor); + + $this->assertEquals(new CursorPaginator($results, $perPage, $cursor, [ + 'path' => $path, + 'cursorName' => $cursorName, + 'parameters' => ['test', 'another'], + ]), $result); + } + + public function testCursorPaginateWithDefaultArguments() + { + $perPage = 15; + $cursorName = 'cursor'; + $cursor = new Cursor(['test' => 'bar']); + $builder = $this->getMockQueryBuilder(); + $builder->from('foobar')->orderBy('test'); + $builder->shouldReceive('newQuery')->andReturnUsing(function () use ($builder) { + return new Builder($builder->connection, $builder->grammar, $builder->processor); + }); + + $path = 'http://foo.bar?cursor='.$cursor->encode(); + + $results = collect([['test' => 'foo'], ['test' => 'bar']]); + + $builder->shouldReceive('get')->once()->andReturnUsing(function () use ($builder, $results) { + $this->assertEquals( + 'select * from "foobar" where ("test" > ?) order by "test" asc limit 16', + $builder->toSql()); + $this->assertEquals(['bar'], $builder->bindings['where']); + + return $results; + }); + + CursorPaginator::currentCursorResolver(function () use ($cursor) { + return $cursor; + }); + + Paginator::currentPathResolver(function () use ($path) { + return $path; + }); + + $result = $builder->cursorPaginate(); + + $this->assertEquals(new CursorPaginator($results, $perPage, $cursor, [ + 'path' => $path, + 'cursorName' => $cursorName, + 'parameters' => ['test'], + ]), $result); + } + + public function testCursorPaginateWhenNoResults() + { + $perPage = 15; + $cursorName = 'cursor'; + $builder = $this->getMockQueryBuilder()->orderBy('test'); + $path = 'http://foo.bar?cursor=3'; + + $results = []; + + $builder->shouldReceive('get')->once()->andReturn($results); + + CursorPaginator::currentCursorResolver(function () { + return null; + }); + + Paginator::currentPathResolver(function () use ($path) { + return $path; + }); + + $result = $builder->cursorPaginate(); + + $this->assertEquals(new CursorPaginator($results, $perPage, null, [ + 'path' => $path, + 'cursorName' => $cursorName, + 'parameters' => ['test'], + ]), $result); + } + + public function testCursorPaginateWithSpecificColumns() + { + $perPage = 16; + $columns = ['id', 'name']; + $cursorName = 'cursor-name'; + $cursor = new Cursor(['id' => 2]); + $builder = $this->getMockQueryBuilder(); + $builder->from('foobar')->orderBy('id'); + $builder->shouldReceive('newQuery')->andReturnUsing(function () use ($builder) { + return new Builder($builder->connection, $builder->grammar, $builder->processor); + }); + + $path = 'http://foo.bar?cursor=3'; + + $results = collect([['id' => 3, 'name' => 'Taylor'], ['id' => 5, 'name' => 'Mohamed']]); + + $builder->shouldReceive('get')->once()->andReturnUsing(function () use ($builder, $results) { + $this->assertEquals( + 'select * from "foobar" where ("id" > ?) order by "id" asc limit 17', + $builder->toSql()); + $this->assertEquals([2], $builder->bindings['where']); + + return $results; + }); + + Paginator::currentPathResolver(function () use ($path) { + return $path; + }); + + $result = $builder->cursorPaginate($perPage, $columns, $cursorName, $cursor); + + $this->assertEquals(new CursorPaginator($results, $perPage, $cursor, [ + 'path' => $path, + 'cursorName' => $cursorName, + 'parameters' => ['id'], + ]), $result); + } + + public function testCursorPaginateWithMixedOrders() + { + $perPage = 16; + $columns = ['foo', 'bar', 'baz']; + $cursorName = 'cursor-name'; + $cursor = new Cursor(['foo' => 1, 'bar' => 2, 'baz' => 3]); + $builder = $this->getMockQueryBuilder(); + $builder->from('foobar')->orderBy('foo')->orderByDesc('bar')->orderBy('baz'); + $builder->shouldReceive('newQuery')->andReturnUsing(function () use ($builder) { + return new Builder($builder->connection, $builder->grammar, $builder->processor); + }); + + $path = 'http://foo.bar?cursor='.$cursor->encode(); + + $results = collect([['foo' => 1, 'bar' => 2, 'baz' => 4], ['foo' => 1, 'bar' => 1, 'baz' => 1]]); + + $builder->shouldReceive('get')->once()->andReturnUsing(function () use ($builder, $results) { + $this->assertEquals( + 'select * from "foobar" where ("foo" > ? or ("foo" = ? and ("bar" < ? or ("bar" = ? and ("baz" > ?))))) order by "foo" asc, "bar" desc, "baz" asc limit 17', + $builder->toSql() + ); + $this->assertEquals([1, 1, 2, 2, 3], $builder->bindings['where']); + + return $results; + }); + + Paginator::currentPathResolver(function () use ($path) { + return $path; + }); + + $result = $builder->cursorPaginate($perPage, $columns, $cursorName, $cursor); + + $this->assertEquals(new CursorPaginator($results, $perPage, $cursor, [ + 'path' => $path, + 'cursorName' => $cursorName, + 'parameters' => ['foo', 'bar', 'baz'], + ]), $result); + } + + public function testCursorPaginateWithDynamicColumnInSelectRaw() + { + $perPage = 15; + $cursorName = 'cursor'; + $cursor = new Cursor(['test' => 'bar']); + $builder = $this->getMockQueryBuilder(); + $builder->from('foobar')->select('*')->selectRaw('(CONCAT(firstname, \' \', lastname)) as test')->orderBy('test'); + $builder->shouldReceive('newQuery')->andReturnUsing(function () use ($builder) { + return new Builder($builder->connection, $builder->grammar, $builder->processor); + }); + + $path = 'http://foo.bar?cursor='.$cursor->encode(); + + $results = collect([['test' => 'foo'], ['test' => 'bar']]); + + $builder->shouldReceive('get')->once()->andReturnUsing(function () use ($builder, $results) { + $this->assertEquals( + 'select *, (CONCAT(firstname, \' \', lastname)) as test from "foobar" where ((CONCAT(firstname, \' \', lastname)) > ?) order by "test" asc limit 16', + $builder->toSql()); + $this->assertEquals(['bar'], $builder->bindings['where']); + + return $results; + }); + + CursorPaginator::currentCursorResolver(function () use ($cursor) { + return $cursor; + }); + + Paginator::currentPathResolver(function () use ($path) { + return $path; + }); + + $result = $builder->cursorPaginate(); + + $this->assertEquals(new CursorPaginator($results, $perPage, $cursor, [ + 'path' => $path, + 'cursorName' => $cursorName, + 'parameters' => ['test'], + ]), $result); + } + + public function testCursorPaginateWithDynamicColumnInSelectSub() + { + $perPage = 15; + $cursorName = 'cursor'; + $cursor = new Cursor(['test' => 'bar']); + $builder = $this->getMockQueryBuilder(); + $builder->from('foobar')->select('*')->selectSub('CONCAT(firstname, \' \', lastname)', 'test')->orderBy('test'); + $builder->shouldReceive('newQuery')->andReturnUsing(function () use ($builder) { + return new Builder($builder->connection, $builder->grammar, $builder->processor); + }); + + $path = 'http://foo.bar?cursor='.$cursor->encode(); + + $results = collect([['test' => 'foo'], ['test' => 'bar']]); + + $builder->shouldReceive('get')->once()->andReturnUsing(function () use ($builder, $results) { + $this->assertEquals( + 'select *, (CONCAT(firstname, \' \', lastname)) as "test" from "foobar" where ((CONCAT(firstname, \' \', lastname)) > ?) order by "test" asc limit 16', + $builder->toSql()); + $this->assertEquals(['bar'], $builder->bindings['where']); + + return $results; + }); + + CursorPaginator::currentCursorResolver(function () use ($cursor) { + return $cursor; + }); + + Paginator::currentPathResolver(function () use ($path) { + return $path; + }); + + $result = $builder->cursorPaginate(); + + $this->assertEquals(new CursorPaginator($results, $perPage, $cursor, [ + 'path' => $path, + 'cursorName' => $cursorName, + 'parameters' => ['test'], + ]), $result); + } public function testWhereRowValues() { @@ -2663,7 +3288,7 @@ public function testWhereRowValuesArityMismatch() $builder = $this->getOracleBuilder(); $builder->select('*')->from('orders')->whereRowValues(['last_update'], '<', [1, 2]); - } + } public function testWhereJsonContains() { @@ -2671,7 +3296,7 @@ public function testWhereJsonContains() $this->expectExceptionMessage('does not support'); $builder = $this->getOracleBuilder(); $builder->select('*')->from('users')->whereJsonContains('options', ['en'])->toSql(); - } + } public function testWhereJsonDoesntContain() { @@ -2679,7 +3304,7 @@ public function testWhereJsonDoesntContain() $this->expectExceptionMessage('does not support'); $builder = $this->getOracleBuilder(); $builder->select('*')->from('users')->whereJsonDoesntContain('options->languages', ['en'])->toSql(); - } + } public function testWhereJsonLength() { @@ -2687,7 +3312,7 @@ public function testWhereJsonLength() $this->expectExceptionMessage('does not support'); $builder = $this->getOracleBuilder(); $builder->select('*')->from('users')->whereJsonLength('options', 0)->toSql(); - } + } public function testBasicSelectNotUsingQuotes() { @@ -2848,7 +3473,7 @@ public function testCloneWithoutBindings() $this->assertSame('select * from "users" order by "email" asc', $clone->toSql()); $this->assertEquals([], $clone->getBindings()); - } + } protected function getConnection() { @@ -2856,14 +3481,14 @@ protected function getConnection() $connection->shouldReceive('getDatabaseName')->andReturn('database'); return $connection; - } + } protected function getOracleBuilder($quote = true) { global $ConfigReturnValue; $ConfigReturnValue = $quote; - $grammar = new Jfelder\OracleDB\Query\Grammars\OracleGrammar; + $grammar = new OracleGrammar; $processor = m::mock(OracleProcessor::class); return new OracleQueryBuilder($this->getConnection(), $grammar, $processor); @@ -2871,14 +3496,14 @@ protected function getOracleBuilder($quote = true) protected function getOracleBuilderWithProcessor() { - $grammar = new Jfelder\OracleDB\Query\Grammars\OracleGrammar; - $processor = new Jfelder\OracleDB\Query\Processors\OracleProcessor; + $grammar = new OracleGrammar; + $processor = new OracleProcessor; return new OracleQueryBuilder($this->getConnection(), $grammar, $processor); } /** - * @return m\MockInterface + * @return \Mockery\MockInterface|\Illuminate\Database\Query\Builder */ protected function getMockQueryBuilder() { @@ -2887,5 +3512,5 @@ protected function getMockQueryBuilder() new Grammar, m::mock(Processor::class), ])->makePartial(); - } + } } diff --git a/tests/OracleDBSchemaGrammarTest.php b/tests/OracleDBSchemaGrammarTest.php index cc12e74..a2d3815 100644 --- a/tests/OracleDBSchemaGrammarTest.php +++ b/tests/OracleDBSchemaGrammarTest.php @@ -1,6 +1,10 @@ expectExceptionMessage('This database driver does not support creating databases.'); $grammar->compileCreateDatabase('foo', m::mock(OracleConnection::class)); - } + } public function testDropDatabaseIfExists() { @@ -35,7 +39,7 @@ public function testDropDatabaseIfExists() $this->expectExceptionMessage('This database driver does not support dropping databases.'); $grammar->compileDropDatabaseIfExists('foo'); - } + } public function testBasicCreateTable() { @@ -54,15 +58,15 @@ public function testBasicCreateTable() $blueprint = new Blueprint('users'); $blueprint->increments('id'); - $blueprint->string('email'); + $blueprint->string('email'); $conn = $this->getConnection(); - $conn->shouldNotReceive('getConfig'); + $conn->shouldNotReceive('getConfig'); $statements = $blueprint->toSql($conn, $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('alter table users add ( id number(10,0) not null, email varchar2(255) not null, constraint users_id_primary primary key ( id ) )', $statements[0]); + $this->assertSame('alter table users add ( id number(10,0) not null, email varchar2(255) not null, constraint users_id_primary primary key ( id ) )', $statements[0]); } public function testBasicCreateTableWithPrimary() @@ -182,7 +186,7 @@ public function testAutoIncrementStartingValue() $this->assertCount(1, $statements); $this->assertSame('create table users ( id number(10,0) not null, email varchar2(255) not null, constraint users_id_primary primary key ( id ) )', $statements[0]); - } + } public function testBasicAlterTable() { @@ -285,7 +289,7 @@ public function testDropColumn() $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('alter table users drop ( foo, bar )', $statements[0]); + $this->assertSame('alter table users drop ( foo, bar )', $statements[0]); } public function testDropPrimary() @@ -346,7 +350,7 @@ public function testDropTimestampsTz() $this->assertCount(1, $statements); $this->assertSame('alter table users drop ( created_at, updated_at )', $statements[0]); - } + } public function testDropMorphs() { @@ -421,7 +425,7 @@ public function testAddingRawIndex() $this->assertCount(1, $statements); $this->assertSame('create index raw_index on users ( (function(column)) )', $statements[0]); - } + } public function testAddingForeignKey() { @@ -437,14 +441,14 @@ public function testAddingForeignKey() $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('alter table users add constraint users_foo_id_foreign foreign key ( foo_id ) references orders ( id ) on delete cascade', $statements[0]); + $this->assertSame('alter table users add constraint users_foo_id_foreign foreign key ( foo_id ) references orders ( id ) on delete cascade', $statements[0]); $blueprint = new Blueprint('users'); $blueprint->foreign('foo_id')->references('id')->on('orders')->cascadeOnUpdate(); $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('alter table users add constraint users_foo_id_foreign foreign key ( foo_id ) references orders ( id ) on update cascade', $statements[0]); + $this->assertSame('alter table users add constraint users_foo_id_foreign foreign key ( foo_id ) references orders ( id ) on update cascade', $statements[0]); } public function testAddingForeignKeyWithCascadeDelete() @@ -492,7 +496,7 @@ public function testAddingID() $this->assertCount(1, $statements); $this->assertSame('alter table users add ( foo number(19,0) not null, constraint users_foo_primary primary key ( foo ) )', $statements[0]); - } + } public function testAddingForeignID() { @@ -513,7 +517,7 @@ public function testAddingForeignID() 'alter table users add constraint users_team_id_foreign foreign key ( team_id ) references teams ( id )', 'alter table users add constraint users_team_column_id_foreign foreign key ( team_column_id ) references teams ( id )', ], $statements); - } + } public function testAddingBigIncrementingID() { @@ -549,7 +553,7 @@ public function testAddingString() $this->assertEquals('alter table users add ( foo varchar2(100) default \'bar\' null )', $statements[0]); $blueprint = new Blueprint('users'); - $blueprint->string('foo', 100)->nullable()->default(new Illuminate\Database\Query\Expression('CURRENT TIMESTAMP')); + $blueprint->string('foo', 100)->nullable()->default(new Raw('CURRENT TIMESTAMP')); $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertEquals(1, count($statements)); @@ -630,7 +634,7 @@ public function testAddingIncrementsWithStartingValues() $this->assertCount(1, $statements); $this->assertSame('alter table users add ( id number(19,0) not null, constraint users_id_primary primary key ( id ) )', $statements[0]); - } + } public function testAddingMediumInteger() { @@ -700,7 +704,7 @@ public function testAddingDoubleWithoutThirdParameter() $blueprint = new Blueprint('users'); $blueprint->double('foo', 15); $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - } + } public function testAddingDecimal() { @@ -775,7 +779,7 @@ public function testAddingTimeStamp() public function testAddingTimestampWithDefault() { $blueprint = new Blueprint('users'); - $blueprint->timestamp('created_at')->default(new Expression('CURRENT_TIMESTAMP')); + $blueprint->timestamp('created_at')->default(new Raw('CURRENT_TIMESTAMP')); $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); $this->assertSame("alter table users add ( created_at timestamp default CURRENT_TIMESTAMP not null )", $statements[0]); @@ -852,7 +856,7 @@ public function testGrammarsAreMacroable() $c = $this->getGrammar()::compileReplace(); $this->assertTrue($c); - } + } protected function getConnection() { @@ -864,6 +868,6 @@ public function getGrammar($quote = false) global $ConfigReturnValue; $ConfigReturnValue = $quote; - return new Jfelder\OracleDB\Schema\Grammars\OracleGrammar; + return new OracleGrammar; } } diff --git a/tests/mocks/OCIFunctions.php b/tests/mocks/OCIFunctions.php index 3953082..e042e62 100644 --- a/tests/mocks/OCIFunctions.php +++ b/tests/mocks/OCIFunctions.php @@ -6,12 +6,13 @@ $OCIConnectionStatus = true; $OCIExecuteStatus = true; $OCIFetchStatus = true; + $OCIFetchAllReturnEmpty = false; $OCIBindChangeStatus = false; $OCIBindByNameTypeReceived = null; } namespace Jfelder\OracleDB\OCI_PDO { - // OCI specific + // For testing OCI.php if (! function_exists("Jfelder\OracleDB\OCI_PDO\oci_error")) { function oci_error($a = '') { @@ -22,7 +23,6 @@ function oci_error($a = '') : ['code' => 0,'message' => '', 'sqltext' => '']; } } - if (! function_exists("Jfelder\OracleDB\OCI_PDO\oci_connect")) { function oci_connect($a = '') { @@ -71,7 +71,7 @@ function oci_parse($a = '', $b = '') } } - // OCI Statement specific + // For testing OCIStatement.php if (! function_exists("Jfelder\OracleDB\OCI_PDO\get_resource_type")) { function get_resource_type($a = '') { @@ -81,7 +81,7 @@ function get_resource_type($a = '') } } if (! function_exists("Jfelder\OracleDB\OCI_PDO\oci_bind_by_name")) { - function oci_bind_by_name($a = '', $b = '', &$c, $d = '', $e = '') + function oci_bind_by_name($a = '', $b = '', &$c = '', $d = '', $e = '') { global $OCIStatementStatus, $OCIBindChangeStatus, $OCIBindByNameTypeReceived; @@ -140,12 +140,15 @@ function oci_fetch_array($a = '') } } if (! function_exists("Jfelder\OracleDB\OCI_PDO\oci_fetch_all")) { - function oci_fetch_all($a = '', &$b) + function oci_fetch_all($a = '', &$b = '') { - global $OCIFetchStatus; - $b = [['FNAME' => 'Test', 'LNAME' => 'Testerson', 'EMAIL' => 'tester@testing.com']]; + global $OCIFetchAllReturnEmpty; + + $b = $OCIFetchAllReturnEmpty + ? [] + : [['FNAME' => 'Test', 'LNAME' => 'Testerson', 'EMAIL' => 'tester@testing.com']]; - return $OCIFetchStatus; + return count($b); } } if (! function_exists("Jfelder\OracleDB\OCI_PDO\oci_field_type")) { diff --git a/tests/mocks/OCIMocks.php b/tests/mocks/OCIMocks.php index c3c540e..ee06fbc 100644 --- a/tests/mocks/OCIMocks.php +++ b/tests/mocks/OCIMocks.php @@ -1,7 +1,10 @@