feat: commit vendor folder directly to bypass air-gapped build restrictions
This commit is contained in:
Vendored
+22
@@ -0,0 +1,22 @@
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2013-2021 Arjay Angeles <aqangeles@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
'Software'), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
Vendored
+181
@@ -0,0 +1,181 @@
|
||||
# Oracle DB driver for Laravel 4|5|6|7|8 via OCI8
|
||||
|
||||
[](https://github.com/yajra/laravel-oci8/actions)
|
||||
[](https://packagist.org/packages/yajra/laravel-oci8)
|
||||
[](https://packagist.org/packages/yajra/laravel-oci8)
|
||||
[](https://packagist.org/packages/yajra/laravel-oci8)
|
||||
|
||||
## Laravel-OCI8
|
||||
|
||||
Laravel-OCI8 is an Oracle Database Driver package for [Laravel](http://laravel.com/). Laravel-OCI8 is an extension of [Illuminate/Database](https://github.com/illuminate/database) that uses [OCI8](http://php.net/oci8) extension to communicate with Oracle. Thanks to @taylorotwell.
|
||||
|
||||
## Documentations
|
||||
|
||||
- You will find user friendly and updated documentation here: [Laravel-OCI8 Docs](https://yajrabox.com/docs/laravel-oci8)
|
||||
- All about oracle and php:[The Underground PHP and Oracle Manual](http://www.oracle.com/technetwork/database/database-technologies/php/201212-ug-php-oracle-1884760.pdf)
|
||||
|
||||
## Laravel Version Compatibility
|
||||
|
||||
Laravel | Package
|
||||
:---------|:----------
|
||||
5.1.x | 5.1.x
|
||||
5.2.x | 5.2.x
|
||||
5.3.x | 5.3.x
|
||||
5.4.x | 5.4.x
|
||||
5.5.x | 5.5.x
|
||||
5.6.x | 5.6.x
|
||||
5.7.x | 5.7.x
|
||||
5.8.x | 5.8.x
|
||||
6.x.x | 6.x.x
|
||||
7.x.x | 7.x.x
|
||||
8.x.x | 8.x.x
|
||||
|
||||
## Quick Installation
|
||||
|
||||
```bash
|
||||
composer require yajra/laravel-oci8:^8
|
||||
```
|
||||
|
||||
## Service Provider (Optional on Laravel 5.5+)
|
||||
|
||||
Once Composer has installed or updated your packages you need to register Laravel-OCI8. Open up `config/app.php` and find the providers key and add:
|
||||
|
||||
```php
|
||||
Yajra\Oci8\Oci8ServiceProvider::class,
|
||||
```
|
||||
|
||||
## Configuration (OPTIONAL)
|
||||
|
||||
Finally you can optionally publish a configuration file by running the following Artisan command.
|
||||
If config file is not publish, the package will automatically use what is declared on your `.env` file database configuration.
|
||||
|
||||
```bash
|
||||
php artisan vendor:publish --tag=oracle
|
||||
```
|
||||
|
||||
This will copy the configuration file to `config/oracle.php`.
|
||||
|
||||
> Note: For [Laravel Lumen configuration](http://lumen.laravel.com/docs/configuration#configuration-files), make sure you have a `config/database.php` file on your project and append the configuration below:
|
||||
|
||||
```php
|
||||
'oracle' => [
|
||||
'driver' => 'oracle',
|
||||
'tns' => env('DB_TNS', ''),
|
||||
'host' => env('DB_HOST', ''),
|
||||
'port' => env('DB_PORT', '1521'),
|
||||
'database' => env('DB_DATABASE', ''),
|
||||
'service_name' => env('DB_SERVICENAME', ''),
|
||||
'username' => env('DB_USERNAME', ''),
|
||||
'password' => env('DB_PASSWORD', ''),
|
||||
'charset' => env('DB_CHARSET', 'AL32UTF8'),
|
||||
'prefix' => env('DB_PREFIX', ''),
|
||||
'prefix_schema' => env('DB_SCHEMA_PREFIX', ''),
|
||||
'edition' => env('DB_EDITION', 'ora$base'),
|
||||
'server_version' => env('DB_SERVER_VERSION', '11g'),
|
||||
'load_balance' => env('DB_LOAD_BALANCE', 'yes'),
|
||||
'dynamic' => [],
|
||||
],
|
||||
```
|
||||
|
||||
> Then, you can set connection data in your `.env` files:
|
||||
|
||||
```ini
|
||||
DB_CONNECTION=oracle
|
||||
DB_HOST=oracle.host
|
||||
DB_PORT=1521
|
||||
DB_SERVICENAME=orcl
|
||||
DB_DATABASE=xe
|
||||
DB_USERNAME=hr
|
||||
DB_PASSWORD=hr
|
||||
```
|
||||
|
||||
> If you want to connect to a cluster containing multiple hosts, you can either set `tns` manually or set host as a comma-separated array and configure other fields as you wish:
|
||||
|
||||
```ini
|
||||
DB_CONNECTION=oracle
|
||||
DB_HOST=oracle1.host, oracle2.host
|
||||
DB_PORT=1521
|
||||
DB_SERVICENAME=orcl
|
||||
DB_LOAD_BALANCE=no
|
||||
DB_DATABASE=xe
|
||||
DB_USERNAME=hr
|
||||
DB_PASSWORD=hr
|
||||
```
|
||||
|
||||
> If you need to connect with the service name instead of tns, you can use the configuration below:
|
||||
|
||||
```php
|
||||
'oracle' => [
|
||||
'driver' => 'oracle',
|
||||
'host' => 'oracle.host',
|
||||
'port' => '1521',
|
||||
'database' => 'xe',
|
||||
'service_name' => 'sid_alias',
|
||||
'username' => 'hr',
|
||||
'password' => 'hr',
|
||||
'charset' => '',
|
||||
'prefix' => '',
|
||||
]
|
||||
```
|
||||
|
||||
In some cases you may wish to set the connection parameters dynamically in your app. For instance, you may access more than one database, or your users may already have their own accounts on the Oracle database:
|
||||
|
||||
```php
|
||||
'oracle' => [
|
||||
'driver' => 'oracle',
|
||||
'host' => 'oracle.host',
|
||||
'port' => '1521',
|
||||
'service_name' => 'sid_alias',
|
||||
'prefix' => 'schemaowner',
|
||||
'dynamic' => [App\Models\Oracle\Config::class, 'dynamicConfig'],
|
||||
]
|
||||
```
|
||||
|
||||
The callback function in your app must be static and accept a reference to the `$config[]` array (which will already be populated with values set in the config file):
|
||||
|
||||
```php
|
||||
namespace App\Models\Oracle;
|
||||
|
||||
class Config {
|
||||
|
||||
public static function dynamicConfig(&$config) {
|
||||
|
||||
if (Illuminate\Support\Facades\Auth::check()) {
|
||||
$config['username'] = App\Oracle\Config::getOraUser();
|
||||
$config['password'] = App\Oracle\Config::getOraPass();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Then run your laravel installation...
|
||||
|
||||
## [Laravel 5.2++] Oracle User Provider
|
||||
|
||||
When using oracle, we may encounter a problem on authentication because oracle queries are case sensitive by default.
|
||||
By using this oracle user provider, we will now be able to avoid user issues when logging in and doing a forgot password failure because of case sensitive search.
|
||||
|
||||
To use, just update `auth.php` config and set the driver to `oracle`
|
||||
|
||||
```php
|
||||
'providers' => [
|
||||
'users' => [
|
||||
'driver' => 'oracle',
|
||||
'model' => App\User::class,
|
||||
],
|
||||
]
|
||||
```
|
||||
|
||||
## Credits
|
||||
|
||||
- [Arjay Angeles][link-author]
|
||||
- [Jimmy Felder](https://github.com/jfelder/Laravel-OracleDB)
|
||||
- [All Contributors][link-contributors]
|
||||
|
||||
## License
|
||||
|
||||
The MIT License (MIT). Please see [License File](LICENSE.md) for more information.
|
||||
|
||||
[link-author]: https://github.com/yajra
|
||||
[link-contributors]: ../../contributors
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"name": "yajra/laravel-oci8",
|
||||
"description": "Oracle DB driver for Laravel 4|5|6|7 via OCI8",
|
||||
"keywords" : ["laravel","oracle","oci8","pdo_oci"],
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Arjay Angeles",
|
||||
"email": "aqangeles@gmail.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.3|^8.0",
|
||||
"ext-oci8": ">=2.0.0",
|
||||
"ext-pdo": "*",
|
||||
"illuminate/database": "^8",
|
||||
"illuminate/pagination": "^8",
|
||||
"illuminate/support": "^8",
|
||||
"illuminate/validation": "^8",
|
||||
"yajra/laravel-pdo-via-oci8": "^2.0|^3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"orchestra/testbench": "^6.5",
|
||||
"mockery/mockery": "^1.4.2",
|
||||
"phpunit/phpunit": "^8.4|^9.0"
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/helper.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Yajra\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Yajra\\Oci8\\Tests\\": "tests/"
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "8.x-dev"
|
||||
},
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Yajra\\Oci8\\Oci8ServiceProvider",
|
||||
"Yajra\\Oci8\\Oci8ValidationServiceProvider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"docker": "docker run -d -p 49160:22 -p 49161:1521 deepdiver/docker-oracle-xe-11g"
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# install deps
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get -y install -qq build-essential unzip wget libaio1
|
||||
|
||||
# install oci8 libs & extension
|
||||
sudo mkdir -p /opt/oracle
|
||||
|
||||
wget https://github.com/bumpx/oracle-instantclient/raw/master/instantclient-basic-linux.x64-12.1.0.2.0.zip
|
||||
wget https://github.com/bumpx/oracle-instantclient/raw/master/instantclient-sdk-linux.x64-12.1.0.2.0.zip
|
||||
|
||||
sudo unzip -o ./instantclient-basic-linux.x64-12.1.0.2.0.zip -d /opt/oracle
|
||||
sudo unzip -o ./instantclient-sdk-linux.x64-12.1.0.2.0.zip -d /opt/oracle
|
||||
|
||||
sudo ln -s /opt/oracle/instantclient/sqlplus /usr/bin/sqlplus
|
||||
sudo ln -s /opt/oracle/instantclient_12_1 /opt/oracle/instantclient
|
||||
sudo ln -s /opt/oracle/instantclient/libclntsh.so.12.1 /opt/oracle/instantclient/libclntsh.so
|
||||
sudo ln -s /opt/oracle/instantclient/libocci.so.12.1 /opt/oracle/instantclient/libocci.so
|
||||
|
||||
sudo sh -c "echo 'instantclient,/opt/oracle/instantclient' | pecl install oci8-2.2.0"
|
||||
|
||||
# setup ld library path
|
||||
sudo sh -c "echo '/opt/oracle/instantclient' >> /etc/ld.so.conf"
|
||||
sudo ldconfig
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# install deps
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get -y install -qq build-essential unzip wget libaio1
|
||||
|
||||
# install oci8 libs & extension
|
||||
sudo mkdir -p /opt/oracle
|
||||
|
||||
wget https://download.oracle.com/otn_software/linux/instantclient/199000/instantclient-basic-linux.x64-19.9.0.0.0dbru.zip
|
||||
wget https://download.oracle.com/otn_software/linux/instantclient/199000/instantclient-sdk-linux.x64-19.9.0.0.0dbru.zip
|
||||
|
||||
sudo unzip -o ./instantclient-basic-linux.x64-19.9.0.0.0dbru.zip -d /opt/oracle
|
||||
sudo unzip -o ./instantclient-sdk-linux.x64-19.9.0.0.0dbru.zip -d /opt/oracle
|
||||
|
||||
sudo ln -s /opt/oracle/instantclient/sqlplus /usr/bin/sqlplus
|
||||
sudo ln -s /opt/oracle/instantclient_19_9 /opt/oracle/instantclient
|
||||
|
||||
sudo sh -c "echo 'instantclient,/opt/oracle/instantclient' | pecl install oci8-3.0.1"
|
||||
|
||||
# setup ld library path
|
||||
sudo sh -c "echo '/opt/oracle/instantclient' >> /etc/ld.so.conf"
|
||||
sudo ldconfig
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# install deps
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get -y install -qq build-essential unzip wget libaio1
|
||||
|
||||
# install oci8 libs & extension
|
||||
sudo mkdir -p /opt/oracle
|
||||
|
||||
wget https://github.com/bumpx/oracle-instantclient/raw/master/instantclient-basic-linux.x64-12.1.0.2.0.zip
|
||||
wget https://github.com/bumpx/oracle-instantclient/raw/master/instantclient-sdk-linux.x64-12.1.0.2.0.zip
|
||||
|
||||
sudo unzip -o ./instantclient-basic-linux.x64-12.1.0.2.0.zip -d /opt/oracle
|
||||
sudo unzip -o ./instantclient-sdk-linux.x64-12.1.0.2.0.zip -d /opt/oracle
|
||||
|
||||
sudo ln -s /opt/oracle/instantclient/sqlplus /usr/bin/sqlplus
|
||||
sudo ln -s /opt/oracle/instantclient_12_1 /opt/oracle/instantclient
|
||||
sudo ln -s /opt/oracle/instantclient/libclntsh.so.12.1 /opt/oracle/instantclient/libclntsh.so
|
||||
sudo ln -s /opt/oracle/instantclient/libocci.so.12.1 /opt/oracle/instantclient/libocci.so
|
||||
|
||||
sudo sh -c "echo 'instantclient,/opt/oracle/instantclient' | pecl install oci8"
|
||||
|
||||
# setup ld library path
|
||||
sudo sh -c "echo '/opt/oracle/instantclient' >> /etc/ld.so.conf"
|
||||
sudo ldconfig
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace Yajra\Oci8\Auth;
|
||||
|
||||
use Illuminate\Auth\EloquentUserProvider;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class OracleUserProvider extends EloquentUserProvider
|
||||
{
|
||||
/**
|
||||
* Retrieve a user by the given credentials.
|
||||
*
|
||||
* @param array $credentials
|
||||
* @return \Illuminate\Contracts\Auth\Authenticatable|null
|
||||
*/
|
||||
public function retrieveByCredentials(array $credentials)
|
||||
{
|
||||
if (empty($credentials)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// First we will add each credential element to the query as a where clause.
|
||||
// Then we can execute the query and, if we found a user, return it in a
|
||||
// Eloquent User "model" that will be utilized by the Guard instances.
|
||||
$query = $this->createModel()->newQuery();
|
||||
|
||||
foreach ($credentials as $key => $value) {
|
||||
if (! Str::contains($key, 'password')) {
|
||||
$query->whereRaw("upper({$key}) = upper(?)", [$value]);
|
||||
}
|
||||
}
|
||||
|
||||
return $query->first();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
<?php
|
||||
|
||||
namespace Yajra\Oci8\Connectors;
|
||||
|
||||
use Illuminate\Database\Connectors\Connector;
|
||||
use Illuminate\Database\Connectors\ConnectorInterface;
|
||||
use Illuminate\Support\Arr;
|
||||
use PDO;
|
||||
use Yajra\Pdo\Oci8;
|
||||
|
||||
class OracleConnector extends Connector implements ConnectorInterface
|
||||
{
|
||||
/**
|
||||
* The default PDO connection options.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $options = [
|
||||
PDO::ATTR_CASE => PDO::CASE_LOWER,
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL,
|
||||
];
|
||||
|
||||
/**
|
||||
* Establish a database connection.
|
||||
*
|
||||
* @param array $config
|
||||
* @return PDO
|
||||
*/
|
||||
public function connect(array $config)
|
||||
{
|
||||
$tns = ! empty($config['tns']) ? $config['tns'] : $this->getDsn($config);
|
||||
|
||||
$options = $this->getOptions($config);
|
||||
|
||||
if (defined('OCI_CRED_EXT') && Arr::get($options, 'session_mode') === OCI_CRED_EXT) {
|
||||
// External connections can only be used with user / and an empty password
|
||||
$config['username'] = '/';
|
||||
$config['password'] = null;
|
||||
}
|
||||
|
||||
$connection = $this->createConnection($tns, $config, $options);
|
||||
|
||||
return $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a DSN string from a configuration.
|
||||
*
|
||||
* @param array $config
|
||||
* @return string
|
||||
*/
|
||||
protected function getDsn(array $config)
|
||||
{
|
||||
if (! empty($config['tns'])) {
|
||||
return $config['tns'];
|
||||
}
|
||||
|
||||
// parse configuration
|
||||
$config = $this->parseConfig($config);
|
||||
|
||||
// check multiple connections/host, comma delimiter
|
||||
$config = $this->checkMultipleHostDsn($config);
|
||||
|
||||
// return generated tns
|
||||
return $config['tns'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse configurations.
|
||||
*
|
||||
* @param array $config
|
||||
* @return array
|
||||
*/
|
||||
protected function parseConfig(array $config)
|
||||
{
|
||||
$config = $this->setHost($config);
|
||||
$config = $this->setPort($config);
|
||||
$config = $this->setProtocol($config);
|
||||
$config = $this->setServiceId($config);
|
||||
$config = $this->setTNS($config);
|
||||
$config = $this->setCharset($config);
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set host from config.
|
||||
*
|
||||
* @param array $config
|
||||
* @return array
|
||||
*/
|
||||
protected function setHost(array $config)
|
||||
{
|
||||
$config['host'] = isset($config['host']) ? $config['host'] : $config['hostname'];
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set port from config.
|
||||
*
|
||||
* @param array $config
|
||||
* @return array
|
||||
*/
|
||||
private function setPort(array $config)
|
||||
{
|
||||
$config['port'] = isset($config['port']) ? $config['port'] : '1521';
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set protocol from config.
|
||||
*
|
||||
* @param array $config
|
||||
* @return array
|
||||
*/
|
||||
private function setProtocol(array $config)
|
||||
{
|
||||
$config['protocol'] = isset($config['protocol']) ? $config['protocol'] : 'TCP';
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set service id from config.
|
||||
*
|
||||
* @param array $config
|
||||
* @return array
|
||||
*/
|
||||
protected function setServiceId(array $config)
|
||||
{
|
||||
$config['service'] = empty($config['service_name'])
|
||||
? $service_param = 'SID = '.$config['database']
|
||||
: $service_param = 'SERVICE_NAME = '.$config['service_name'];
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set tns from config.
|
||||
*
|
||||
* @param array $config
|
||||
* @return array
|
||||
*/
|
||||
protected function setTNS(array $config)
|
||||
{
|
||||
$config['tns'] = "(DESCRIPTION = (ADDRESS = (PROTOCOL = {$config['protocol']})(HOST = {$config['host']})(PORT = {$config['port']})) (CONNECT_DATA =({$config['service']})))";
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set charset from config.
|
||||
*
|
||||
* @param array $config
|
||||
* @return array
|
||||
*/
|
||||
protected function setCharset(array $config)
|
||||
{
|
||||
if (! isset($config['charset'])) {
|
||||
$config['charset'] = 'AL32UTF8';
|
||||
}
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set DSN host from config.
|
||||
*
|
||||
* @param array $config
|
||||
* @return array
|
||||
*/
|
||||
protected function checkMultipleHostDsn(array $config)
|
||||
{
|
||||
$host = is_array($config['host']) ? $config['host'] : explode(',', $config['host']);
|
||||
|
||||
$count = count($host);
|
||||
if ($count > 1) {
|
||||
$address = '';
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$address .= '(ADDRESS = (PROTOCOL = '.$config['protocol'].')(HOST = '.trim($host[$i]).')(PORT = '.$config['port'].'))';
|
||||
}
|
||||
|
||||
// backwards compatibility for users dont have this field in their php config
|
||||
$loadBalance = $config['load_balance'] ?? 'yes';
|
||||
|
||||
// create a tns with multiple address connection
|
||||
$config['tns'] = "(DESCRIPTION = {$address} (LOAD_BALANCE = {$loadBalance}) (FAILOVER = on) (CONNECT_DATA = (SERVER = DEDICATED) ({$config['service']})))";
|
||||
}
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new PDO connection.
|
||||
*
|
||||
* @param string $tns
|
||||
* @param array $config
|
||||
* @param array $options
|
||||
* @return PDO
|
||||
*/
|
||||
public function createConnection($tns, array $config, array $options)
|
||||
{
|
||||
// add fallback in case driver is not set, will use pdo instead
|
||||
if (! in_array($config['driver'], ['oci8', 'pdo-via-oci8', 'oracle'])) {
|
||||
return parent::createConnection($tns, $config, $options);
|
||||
}
|
||||
|
||||
$config = $this->setCharset($config);
|
||||
$options['charset'] = $config['charset'];
|
||||
|
||||
return new Oci8($tns, $config['username'], $config['password'], $options);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,305 @@
|
||||
<?php
|
||||
|
||||
namespace Yajra\Oci8\Eloquent;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Query\Builder as IlluminateQueryBuilder;
|
||||
use Yajra\Oci8\Oci8Connection;
|
||||
use Yajra\Oci8\Query\Grammars\OracleGrammar;
|
||||
use Yajra\Oci8\Query\OracleBuilder as QueryBuilder;
|
||||
|
||||
class OracleEloquent extends Model
|
||||
{
|
||||
/**
|
||||
* List of binary (blob) columns.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $binaries = [];
|
||||
|
||||
/**
|
||||
* List of binary fields for storage.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $binaryFields = [];
|
||||
|
||||
/**
|
||||
* Sequence name variable.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $sequence = null;
|
||||
|
||||
/**
|
||||
* Get next value of the model sequence.
|
||||
*
|
||||
* @param null|string $sequence
|
||||
* @return int
|
||||
*/
|
||||
public static function nextValue($sequence = null)
|
||||
{
|
||||
$instance = new static;
|
||||
|
||||
$sequence = $sequence ?? $instance->getSequenceName();
|
||||
|
||||
return $instance->getConnection()
|
||||
->getSequence()
|
||||
->nextValue($sequence);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get model's sequence name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSequenceName()
|
||||
{
|
||||
if ($this->sequence) {
|
||||
return $this->sequence;
|
||||
}
|
||||
|
||||
return $this->getTable().'_'.$this->getKeyName().'_seq';
|
||||
}
|
||||
|
||||
/**
|
||||
* Set sequence name.
|
||||
*
|
||||
* @param string $name
|
||||
* @return $this
|
||||
*/
|
||||
public function setSequenceName($name)
|
||||
{
|
||||
$this->sequence = $name;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the model in the database.
|
||||
*
|
||||
* @param array $attributes
|
||||
* @param array $options
|
||||
* @return bool|int
|
||||
*/
|
||||
public function update(array $attributes = [], array $options = [])
|
||||
{
|
||||
if (! $this->exists) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If dirty attributes contains binary field
|
||||
// extract binary fields to new array
|
||||
if ($this->extractBinaries($attributes)) {
|
||||
return $this->newQuery()->updateLob($attributes, $this->binaryFields, $this->getKeyName());
|
||||
}
|
||||
|
||||
return $this->fill($attributes)->save($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract binary fields from given attributes.
|
||||
*
|
||||
* @param array $attributes
|
||||
* @return array
|
||||
*/
|
||||
protected function extractBinaries(&$attributes)
|
||||
{
|
||||
// If attributes contains binary field
|
||||
// extract binary fields to new array
|
||||
$binaries = [];
|
||||
if ($this->checkBinary($attributes) && $this->getConnection() instanceof Oci8Connection) {
|
||||
foreach ($attributes as $key => $value) {
|
||||
if (in_array($key, $this->binaries)) {
|
||||
$binaries[$key] = $value;
|
||||
unset($attributes[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->binaryFields = $binaries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if attributes contains binary field.
|
||||
*
|
||||
* @param array $attributes
|
||||
* @return bool
|
||||
*/
|
||||
protected function checkBinary(array $attributes)
|
||||
{
|
||||
foreach ($attributes as $key => $value) {
|
||||
// if attribute is in binary field list
|
||||
if (in_array($key, $this->binaries)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the table qualified key name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getQualifiedKeyName()
|
||||
{
|
||||
$pos = strpos($this->getTable(), '@');
|
||||
|
||||
if ($pos === false) {
|
||||
return $this->getTable().'.'.$this->getKeyName();
|
||||
}
|
||||
|
||||
$table = substr($this->getTable(), 0, $pos);
|
||||
$dbLink = substr($this->getTable(), $pos);
|
||||
|
||||
return $table.'.'.$this->getKeyName().$dbLink;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a new query builder instance for the connection.
|
||||
*
|
||||
* @return \Illuminate\Database\Query\Builder|\Yajra\Oci8\Query\OracleBuilder
|
||||
*/
|
||||
protected function newBaseQueryBuilder()
|
||||
{
|
||||
$conn = $this->getConnection();
|
||||
$grammar = $conn->getQueryGrammar();
|
||||
|
||||
if ($grammar instanceof OracleGrammar) {
|
||||
return new QueryBuilder($conn, $grammar, $conn->getPostProcessor());
|
||||
}
|
||||
|
||||
return new IlluminateQueryBuilder($conn, $grammar, $conn->getPostProcessor());
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a model update operation.
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Builder $query
|
||||
* @return bool
|
||||
*/
|
||||
protected function performUpdate(Builder $query)
|
||||
{
|
||||
// If the updating event returns false, we will cancel the update operation so
|
||||
// developers can hook Validation systems into their models and cancel this
|
||||
// operation if the model does not pass validation. Otherwise, we update.
|
||||
if ($this->fireModelEvent('updating') === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// First we need to create a fresh query instance and touch the creation and
|
||||
// update timestamp on the model which are maintained by us for developer
|
||||
// convenience. Then we will just continue saving the model instances.
|
||||
if ($this->usesTimestamps()) {
|
||||
$this->updateTimestamps();
|
||||
}
|
||||
|
||||
// Once we have run the update operation, we will fire the "updated" event for
|
||||
// this model instance. This will allow developers to hook into these after
|
||||
// models are updated, giving them a chance to do any special processing.
|
||||
$dirty = $this->getDirty();
|
||||
|
||||
if (count($dirty) > 0) {
|
||||
// If dirty attributes contains binary field
|
||||
// extract binary fields to new array
|
||||
$this->updateBinary($query, $dirty);
|
||||
|
||||
$this->fireModelEvent('updated', false);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update model with binary (blob) fields.
|
||||
*
|
||||
* @param Builder $query
|
||||
* @param array $dirty
|
||||
*/
|
||||
protected function updateBinary(Builder $query, $dirty)
|
||||
{
|
||||
$builder = $this->setKeysForSaveQuery($query);
|
||||
|
||||
if ($this->extractBinaries($dirty)) {
|
||||
$builder->updateLob($dirty, $this->binaryFields, $this->getKeyName());
|
||||
} else {
|
||||
$builder->update($dirty);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a model insert operation.
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Builder $query
|
||||
* @return bool
|
||||
*/
|
||||
protected function performInsert(Builder $query)
|
||||
{
|
||||
if ($this->fireModelEvent('creating') === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// First we'll need to create a fresh query instance and touch the creation and
|
||||
// update timestamps on this model, which are maintained by us for developer
|
||||
// convenience. After, we will just continue saving these model instances.
|
||||
if ($this->usesTimestamps()) {
|
||||
$this->updateTimestamps();
|
||||
}
|
||||
|
||||
// If the model has an incrementing key, we can use the "insertGetId" method on
|
||||
// the query builder, which will give us back the final inserted ID for this
|
||||
// table from the database. Not all tables have to be incrementing though.
|
||||
$attributes = $this->attributes;
|
||||
|
||||
if ($this->getIncrementing()) {
|
||||
$this->insertAndSetId($query, $attributes);
|
||||
}
|
||||
|
||||
// If the table is not incrementing we'll simply insert this attributes as they
|
||||
// are, as this attributes arrays must contain an "id" column already placed
|
||||
// there by the developer as the manually determined key for these models.
|
||||
else {
|
||||
if (empty($attributes)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If attributes contains binary field
|
||||
// extract binary fields to new array
|
||||
if ($this->extractBinaries($attributes)) {
|
||||
$query->getQuery()->insertLob($attributes, $this->binaryFields, $this->getKeyName());
|
||||
} else {
|
||||
$query->insert($attributes);
|
||||
}
|
||||
}
|
||||
|
||||
// We will go ahead and set the exists property to true, so that it is set when
|
||||
// the created event is fired, just in case the developer tries to update it
|
||||
// during the event. This will allow them to do so and run an update here.
|
||||
$this->exists = true;
|
||||
|
||||
$this->wasRecentlyCreated = true;
|
||||
|
||||
$this->fireModelEvent('created', false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert the given attributes and set the ID on the model.
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Builder $query
|
||||
* @param array $attributes
|
||||
* @return int|void
|
||||
*/
|
||||
protected function insertAndSetId(Builder $query, $attributes)
|
||||
{
|
||||
$id = ($binaries = $this->extractBinaries($attributes)) ?
|
||||
$query->getQuery()->insertLob($attributes, $binaries, $keyName = $this->getKeyName()) :
|
||||
$query->insertGetId($attributes, $keyName = $this->getKeyName());
|
||||
|
||||
$this->setAttribute($keyName, $id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,498 @@
|
||||
<?php
|
||||
|
||||
namespace Yajra\Oci8;
|
||||
|
||||
use Doctrine\DBAL\Driver\OCI8\Driver as DoctrineDriver;
|
||||
use Doctrine\DBAL\Version;
|
||||
use Exception;
|
||||
use Illuminate\Database\Connection;
|
||||
use Illuminate\Database\Grammar;
|
||||
use Illuminate\Support\Str;
|
||||
use PDO;
|
||||
use PDOStatement;
|
||||
use Throwable;
|
||||
use Yajra\Oci8\PDO\Oci8Driver;
|
||||
use Yajra\Oci8\Query\Grammars\OracleGrammar as QueryGrammar;
|
||||
use Yajra\Oci8\Query\OracleBuilder as QueryBuilder;
|
||||
use Yajra\Oci8\Query\Processors\OracleProcessor as Processor;
|
||||
use Yajra\Oci8\Schema\Grammars\OracleGrammar as SchemaGrammar;
|
||||
use Yajra\Oci8\Schema\OracleBuilder as SchemaBuilder;
|
||||
use Yajra\Oci8\Schema\Sequence;
|
||||
use Yajra\Oci8\Schema\Trigger;
|
||||
use Yajra\Pdo\Oci8\Statement;
|
||||
|
||||
class Oci8Connection extends Connection
|
||||
{
|
||||
const RECONNECT_ERRORS = 'reconnect_errors';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $schema;
|
||||
|
||||
/**
|
||||
* @var \Yajra\Oci8\Schema\Sequence
|
||||
*/
|
||||
protected $sequence;
|
||||
|
||||
/**
|
||||
* @var \Yajra\Oci8\Schema\Trigger
|
||||
*/
|
||||
protected $trigger;
|
||||
|
||||
/**
|
||||
* @param PDO|\Closure $pdo
|
||||
* @param string $database
|
||||
* @param string $tablePrefix
|
||||
* @param array $config
|
||||
*/
|
||||
public function __construct($pdo, $database = '', $tablePrefix = '', array $config = [])
|
||||
{
|
||||
parent::__construct($pdo, $database, $tablePrefix, $config);
|
||||
$this->sequence = new Sequence($this);
|
||||
$this->trigger = new Trigger($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current schema.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSchema()
|
||||
{
|
||||
return $this->schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set current schema.
|
||||
*
|
||||
* @param string $schema
|
||||
* @return $this
|
||||
*/
|
||||
public function setSchema($schema)
|
||||
{
|
||||
$this->schema = $schema;
|
||||
$sessionVars = [
|
||||
'CURRENT_SCHEMA' => $schema,
|
||||
];
|
||||
|
||||
return $this->setSessionVars($sessionVars);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update oracle session variables.
|
||||
*
|
||||
* @param array $sessionVars
|
||||
* @return $this
|
||||
*/
|
||||
public function setSessionVars(array $sessionVars)
|
||||
{
|
||||
$vars = [];
|
||||
foreach ($sessionVars as $option => $value) {
|
||||
if (strtoupper($option) == 'CURRENT_SCHEMA' || strtoupper($option) == 'EDITION') {
|
||||
$vars[] = "$option = $value";
|
||||
} else {
|
||||
$vars[] = "$option = '$value'";
|
||||
}
|
||||
}
|
||||
|
||||
if ($vars) {
|
||||
$sql = 'ALTER SESSION SET '.implode(' ', $vars);
|
||||
$this->statement($sql);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get sequence class.
|
||||
*
|
||||
* @return \Yajra\Oci8\Schema\Sequence
|
||||
*/
|
||||
public function getSequence()
|
||||
{
|
||||
return $this->sequence;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set sequence class.
|
||||
*
|
||||
* @param \Yajra\Oci8\Schema\Sequence $sequence
|
||||
* @return \Yajra\Oci8\Schema\Sequence
|
||||
*/
|
||||
public function setSequence(Sequence $sequence)
|
||||
{
|
||||
return $this->sequence = $sequence;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get oracle trigger class.
|
||||
*
|
||||
* @return \Yajra\Oci8\Schema\Trigger
|
||||
*/
|
||||
public function getTrigger()
|
||||
{
|
||||
return $this->trigger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set oracle trigger class.
|
||||
*
|
||||
* @param \Yajra\Oci8\Schema\Trigger $trigger
|
||||
* @return \Yajra\Oci8\Schema\Trigger
|
||||
*/
|
||||
public function setTrigger(Trigger $trigger)
|
||||
{
|
||||
return $this->trigger = $trigger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a schema builder instance for the connection.
|
||||
*
|
||||
* @return \Yajra\Oci8\Schema\OracleBuilder
|
||||
*/
|
||||
public function getSchemaBuilder()
|
||||
{
|
||||
if (is_null($this->schemaGrammar)) {
|
||||
$this->useDefaultSchemaGrammar();
|
||||
}
|
||||
|
||||
return new SchemaBuilder($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a new query builder instance.
|
||||
*
|
||||
* @return \Illuminate\Database\Query\Builder
|
||||
*/
|
||||
public function query()
|
||||
{
|
||||
return new QueryBuilder(
|
||||
$this, $this->getQueryGrammar(), $this->getPostProcessor()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set oracle session date format.
|
||||
*
|
||||
* @param string $format
|
||||
* @return $this
|
||||
*/
|
||||
public function setDateFormat($format = 'YYYY-MM-DD HH24:MI:SS')
|
||||
{
|
||||
$sessionVars = [
|
||||
'NLS_DATE_FORMAT' => $format,
|
||||
'NLS_TIMESTAMP_FORMAT' => $format,
|
||||
];
|
||||
|
||||
return $this->setSessionVars($sessionVars);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get doctrine driver.
|
||||
*
|
||||
* @return \Doctrine\DBAL\Driver\OCI8\Driver|\Yajra\Oci8\PDO\Oci8Driver
|
||||
*/
|
||||
protected function getDoctrineDriver()
|
||||
{
|
||||
return class_exists(Version::class) ? new DoctrineDriver : new Oci8Driver();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a PL/SQL Function and return its value.
|
||||
* Usage: DB::executeFunction('function_name', ['binding_1' => 'hi', 'binding_n' =>
|
||||
* 'bye'], PDO::PARAM_LOB).
|
||||
*
|
||||
* @param string $functionName
|
||||
* @param array $bindings (kvp array)
|
||||
* @param int $returnType (PDO::PARAM_*)
|
||||
* @param int $length
|
||||
* @return mixed $returnType
|
||||
*/
|
||||
public function executeFunction($functionName, array $bindings = [], $returnType = PDO::PARAM_STR, $length = null)
|
||||
{
|
||||
$stmt = $this->createStatementFromFunction($functionName, $bindings);
|
||||
|
||||
$stmt = $this->addBindingsToStatement($stmt, $bindings);
|
||||
|
||||
$stmt->bindParam(':result', $result, $returnType, $length);
|
||||
$stmt->execute();
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a PL/SQL Procedure and return its results.
|
||||
*
|
||||
* Usage: DB::executeProcedure($procedureName, $bindings).
|
||||
* $bindings looks like:
|
||||
* $bindings = [
|
||||
* 'p_userid' => $id
|
||||
* ];
|
||||
*
|
||||
* @param string $procedureName
|
||||
* @param array $bindings
|
||||
* @return bool
|
||||
*/
|
||||
public function executeProcedure($procedureName, array $bindings = [])
|
||||
{
|
||||
$stmt = $this->createStatementFromProcedure($procedureName, $bindings);
|
||||
|
||||
$stmt = $this->addBindingsToStatement($stmt, $bindings);
|
||||
|
||||
return $stmt->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a PL/SQL Procedure and return its cursor result.
|
||||
* Usage: DB::executeProcedureWithCursor($procedureName, $bindings).
|
||||
*
|
||||
* https://docs.oracle.com/cd/E17781_01/appdev.112/e18555/ch_six_ref_cur.htm#TDPPH218
|
||||
*
|
||||
* @param string $procedureName
|
||||
* @param array $bindings
|
||||
* @param string $cursorName
|
||||
* @return array
|
||||
*/
|
||||
public function executeProcedureWithCursor($procedureName, array $bindings = [], $cursorName = ':cursor')
|
||||
{
|
||||
$stmt = $this->createStatementFromProcedure($procedureName, $bindings, $cursorName);
|
||||
|
||||
$stmt = $this->addBindingsToStatement($stmt, $bindings);
|
||||
|
||||
$cursor = null;
|
||||
$stmt->bindParam($cursorName, $cursor, PDO::PARAM_STMT);
|
||||
$stmt->execute();
|
||||
|
||||
$statement = new Statement($cursor, $this->getPdo(), $this->getPdo()->getOptions());
|
||||
$statement->execute();
|
||||
$results = $statement->fetchAll(PDO::FETCH_OBJ);
|
||||
$statement->closeCursor();
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates sql command to run a procedure with bindings.
|
||||
*
|
||||
* @param string $procedureName
|
||||
* @param array $bindings
|
||||
* @param string|bool $cursor
|
||||
* @return string
|
||||
*/
|
||||
public function createSqlFromProcedure($procedureName, array $bindings, $cursor = false)
|
||||
{
|
||||
$paramsString = implode(',', array_map(function ($param) {
|
||||
return ':'.$param;
|
||||
}, array_keys($bindings)));
|
||||
|
||||
$prefix = count($bindings) ? ',' : '';
|
||||
$cursor = $cursor ? $prefix.$cursor : null;
|
||||
|
||||
return sprintf('begin %s(%s%s); end;', $procedureName, $paramsString, $cursor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates statement from procedure.
|
||||
*
|
||||
* @param string $procedureName
|
||||
* @param array $bindings
|
||||
* @param string|bool $cursorName
|
||||
* @return PDOStatement
|
||||
*/
|
||||
public function createStatementFromProcedure($procedureName, array $bindings, $cursorName = false)
|
||||
{
|
||||
$sql = $this->createSqlFromProcedure($procedureName, $bindings, $cursorName);
|
||||
|
||||
return $this->getPdo()->prepare($sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create statement from function.
|
||||
*
|
||||
* @param string $functionName
|
||||
* @param array $bindings
|
||||
* @return PDOStatement
|
||||
*/
|
||||
public function createStatementFromFunction($functionName, array $bindings)
|
||||
{
|
||||
$bindings = $bindings ? ':'.implode(', :', array_keys($bindings)) : '';
|
||||
|
||||
$sql = sprintf('begin :result := %s(%s); end;', $functionName, $bindings);
|
||||
|
||||
return $this->getPdo()->prepare($sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default query grammar instance.
|
||||
*
|
||||
* @return \Illuminate\Database\Grammar|\Yajra\Oci8\Query\Grammars\OracleGrammar
|
||||
*/
|
||||
protected function getDefaultQueryGrammar()
|
||||
{
|
||||
return $this->withTablePrefix(new QueryGrammar());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the table prefix and return the grammar.
|
||||
*
|
||||
* @param \Illuminate\Database\Grammar|\Yajra\Oci8\Query\Grammars\OracleGrammar|\Yajra\Oci8\Schema\Grammars\OracleGrammar $grammar
|
||||
* @return \Illuminate\Database\Grammar
|
||||
*/
|
||||
public function withTablePrefix(Grammar $grammar)
|
||||
{
|
||||
return $this->withSchemaPrefix(parent::withTablePrefix($grammar));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the schema prefix and return the grammar.
|
||||
*
|
||||
* @param \Illuminate\Database\Grammar|\Yajra\Oci8\Query\Grammars\OracleGrammar|\Yajra\Oci8\Schema\Grammars\OracleGrammar $grammar
|
||||
* @return \Illuminate\Database\Grammar
|
||||
*/
|
||||
public function withSchemaPrefix(Grammar $grammar)
|
||||
{
|
||||
$grammar->setSchemaPrefix($this->getConfigSchemaPrefix());
|
||||
$grammar->setMaxLength($this->getConfigMaxLength());
|
||||
|
||||
return $grammar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get config schema prefix.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getConfigSchemaPrefix()
|
||||
{
|
||||
return isset($this->config['prefix_schema']) ? $this->config['prefix_schema'] : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get config max length.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getConfigMaxLength()
|
||||
{
|
||||
return isset($this->config['max_name_len']) ? $this->config['max_name_len'] : 30;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default schema grammar instance.
|
||||
*
|
||||
* @return \Illuminate\Database\Grammar|\Yajra\Oci8\Schema\Grammars\OracleGrammar
|
||||
*/
|
||||
protected function getDefaultSchemaGrammar()
|
||||
{
|
||||
return $this->withTablePrefix(new SchemaGrammar());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default post processor instance.
|
||||
*
|
||||
* @return \Yajra\Oci8\Query\Processors\OracleProcessor
|
||||
*/
|
||||
protected function getDefaultPostProcessor()
|
||||
{
|
||||
return new Processor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add bindings to statement.
|
||||
*
|
||||
* @param array $bindings
|
||||
* @param PDOStatement $stmt
|
||||
* @return PDOStatement
|
||||
*/
|
||||
public function addBindingsToStatement(PDOStatement $stmt, array $bindings)
|
||||
{
|
||||
foreach ($bindings as $key => &$binding) {
|
||||
$value = &$binding;
|
||||
$type = PDO::PARAM_STR;
|
||||
$length = -1;
|
||||
|
||||
if (is_array($binding)) {
|
||||
$value = &$binding['value'];
|
||||
$type = array_key_exists('type', $binding) ? $binding['type'] : PDO::PARAM_STR;
|
||||
$length = array_key_exists('length', $binding) ? $binding['length'] : -1;
|
||||
}
|
||||
|
||||
$stmt->bindParam(':'.$key, $value, $type, $length);
|
||||
}
|
||||
|
||||
return $stmt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given exception was caused by a lost connection.
|
||||
*
|
||||
* @param \Exception $e
|
||||
* @return bool
|
||||
*/
|
||||
protected function causedByLostConnection(Throwable $e)
|
||||
{
|
||||
if (parent::causedByLostConnection($e)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$lostConnectionErrors = [
|
||||
'ORA-03113', //End-of-file on communication channel
|
||||
'ORA-03114', //Not Connected to Oracle
|
||||
'ORA-03135', //Connection lost contact
|
||||
'ORA-12170', //Connect timeout occurred
|
||||
'ORA-12537', //Connection closed
|
||||
'ORA-27146', //Post/wait initialization failed
|
||||
'ORA-25408', //Can not safely replay call
|
||||
'ORA-56600', //Illegal Call
|
||||
];
|
||||
|
||||
$additionalErrors = null;
|
||||
|
||||
$options = isset($this->config['options']) ? $this->config['options'] : [];
|
||||
if (array_key_exists(static::RECONNECT_ERRORS, $options)) {
|
||||
$additionalErrors = $this->config['options'][static::RECONNECT_ERRORS];
|
||||
}
|
||||
|
||||
if (is_array($additionalErrors)) {
|
||||
$lostConnectionErrors = array_merge($lostConnectionErrors,
|
||||
$this->config['options'][static::RECONNECT_ERRORS]);
|
||||
}
|
||||
|
||||
return Str::contains($e->getMessage(), $lostConnectionErrors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set oracle NLS session to case insensitive search & sort.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function useCaseInsensitiveSession()
|
||||
{
|
||||
return $this->setSessionVars(['NLS_COMP' => 'LINGUISTIC', 'NLS_SORT' => 'BINARY_CI']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set oracle NLS session to case sensitive search & sort.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function useCaseSensitiveSession()
|
||||
{
|
||||
return $this->setSessionVars(['NLS_COMP' => 'BINARY', 'NLS_SORT' => 'BINARY']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind values to their parameters in the given statement.
|
||||
*
|
||||
* @param \Yajra\Pdo\Oci8\Statement $statement
|
||||
* @param array $bindings
|
||||
* @return void
|
||||
*/
|
||||
public function bindValues($statement, $bindings)
|
||||
{
|
||||
foreach ($bindings as $key => $value) {
|
||||
$statement->bindValue(is_string($key) ? $key : $key + 1, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
namespace Yajra\Oci8;
|
||||
|
||||
use Illuminate\Database\Connection;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Yajra\Oci8\Auth\OracleUserProvider;
|
||||
use Yajra\Oci8\Connectors\OracleConnector as Connector;
|
||||
|
||||
class Oci8ServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Indicates if loading of the provider is deferred.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $defer = false;
|
||||
|
||||
/**
|
||||
* Boot Oci8 Provider.
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
$this->publishes([
|
||||
__DIR__.'/../config/oracle.php' => config_path('oracle.php'),
|
||||
], 'oracle');
|
||||
|
||||
Auth::provider('oracle', function ($app, array $config) {
|
||||
return new OracleUserProvider($app['hash'], $config['model']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the service provider.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
if (file_exists(config_path('oracle.php'))) {
|
||||
$this->mergeConfigFrom(config_path('oracle.php'), 'database.connections');
|
||||
} else {
|
||||
$this->mergeConfigFrom(__DIR__.'/../config/oracle.php', 'database.connections');
|
||||
}
|
||||
|
||||
Connection::resolverFor('oracle', function ($connection, $database, $prefix, $config) {
|
||||
if (isset($config['dynamic']) && ! empty($config['dynamic'])) {
|
||||
call_user_func_array($config['dynamic'], [&$config]);
|
||||
}
|
||||
|
||||
$connector = new Connector();
|
||||
$connection = $connector->connect($config);
|
||||
$db = new Oci8Connection($connection, $database, $prefix, $config);
|
||||
|
||||
if (! empty($config['skip_session_vars'])) {
|
||||
return $db;
|
||||
}
|
||||
|
||||
// set oracle session variables
|
||||
$sessionVars = [
|
||||
'NLS_TIME_FORMAT' => 'HH24:MI:SS',
|
||||
'NLS_DATE_FORMAT' => 'YYYY-MM-DD HH24:MI:SS',
|
||||
'NLS_TIMESTAMP_FORMAT' => 'YYYY-MM-DD HH24:MI:SS',
|
||||
'NLS_TIMESTAMP_TZ_FORMAT' => 'YYYY-MM-DD HH24:MI:SS TZH:TZM',
|
||||
'NLS_NUMERIC_CHARACTERS' => '.,',
|
||||
];
|
||||
|
||||
// Like Postgres, Oracle allows the concept of "schema"
|
||||
if (isset($config['schema'])) {
|
||||
$sessionVars['CURRENT_SCHEMA'] = $config['schema'];
|
||||
}
|
||||
|
||||
if (isset($config['session'])) {
|
||||
$sessionVars = array_merge($sessionVars, $config['session']);
|
||||
}
|
||||
|
||||
if (isset($config['edition'])) {
|
||||
$sessionVars = array_merge(
|
||||
$sessionVars,
|
||||
['EDITION' => $config['edition']]
|
||||
);
|
||||
}
|
||||
|
||||
$db->setSessionVars($sessionVars);
|
||||
|
||||
return $db;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the services provided by the provider.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function provides()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace Yajra\Oci8;
|
||||
|
||||
use Illuminate\Validation\ValidationServiceProvider;
|
||||
use Yajra\Oci8\Validation\Oci8DatabasePresenceVerifier;
|
||||
|
||||
class Oci8ValidationServiceProvider extends ValidationServiceProvider
|
||||
{
|
||||
protected function registerPresenceVerifier()
|
||||
{
|
||||
$this->app->singleton('validation.presence', function ($app) {
|
||||
return new Oci8DatabasePresenceVerifier($app['db']);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,509 @@
|
||||
<?php
|
||||
|
||||
namespace Yajra\Oci8;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
trait OracleReservedWords
|
||||
{
|
||||
/**
|
||||
* Lists of reserved words.
|
||||
*
|
||||
* @see https://docs.oracle.com/cd/B19306_01/em.102/b40103/app_oracle_reserved_words.htm
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $reserves = [
|
||||
'ACCESS',
|
||||
'ACCOUNT',
|
||||
'ACTIVATE',
|
||||
'ADD',
|
||||
'ADMIN',
|
||||
'ADVISE',
|
||||
'AFTER',
|
||||
'ALL',
|
||||
'ALL_ROWS',
|
||||
'ALLOCATE',
|
||||
'ALTER',
|
||||
'ANALYZE',
|
||||
'AND',
|
||||
'ANY',
|
||||
'ARCHIVE',
|
||||
'ARCHIVELOG',
|
||||
'ARRAY',
|
||||
'AS',
|
||||
'ASC',
|
||||
'AT',
|
||||
'AUDIT',
|
||||
'AUTHENTICATED',
|
||||
'AUTHORIZATION',
|
||||
'AUTOEXTEND',
|
||||
'AUTOMATIC',
|
||||
'BACKUP',
|
||||
'BECOME',
|
||||
'BEFORE',
|
||||
'BEGIN',
|
||||
'BETWEEN',
|
||||
'BFILE',
|
||||
'BITMAP',
|
||||
'BLOB',
|
||||
'BLOCK',
|
||||
'BODY',
|
||||
'BY',
|
||||
'CACHE',
|
||||
'CACHE_INSTANCES',
|
||||
'CANCEL',
|
||||
'CASCADE',
|
||||
'CAST',
|
||||
'CFILE',
|
||||
'CHAINED',
|
||||
'CHANGE',
|
||||
'CHAR',
|
||||
'CHAR_CS',
|
||||
'CHARACTER',
|
||||
'CHECK',
|
||||
'CHECKPOINT',
|
||||
'CHOOSE',
|
||||
'CHUNK',
|
||||
'CLEAR',
|
||||
'CLOB',
|
||||
'CLONE',
|
||||
'CLOSE',
|
||||
'CLOSE_CACHED_OPEN_CURSORS',
|
||||
'CLUSTER',
|
||||
'COALESCE',
|
||||
'COLUMN',
|
||||
'COLUMNS',
|
||||
'COMMENT',
|
||||
'COMMIT',
|
||||
'COMMITTED',
|
||||
'COMPATIBILITY',
|
||||
'COMPILE',
|
||||
'COMPLETE',
|
||||
'COMPOSITE_LIMIT',
|
||||
'COMPRESS',
|
||||
'COMPUTE',
|
||||
'CONNECT',
|
||||
'CONNECT_TIME',
|
||||
'CONSTRAINT',
|
||||
'CONSTRAINTS',
|
||||
'CONTENTS',
|
||||
'CONTINUE',
|
||||
'CONTROLFILE',
|
||||
'CONVERT',
|
||||
'COST',
|
||||
'CPU_PER_CALL',
|
||||
'CPU_PER_SESSION',
|
||||
'CREATE',
|
||||
'CURRENT',
|
||||
'CURRENT_SCHEMA',
|
||||
'CURREN_USER',
|
||||
'CURSOR',
|
||||
'CYCLE',
|
||||
'DANGLING',
|
||||
'DATABASE',
|
||||
'DATAFILE',
|
||||
'DATAFILES',
|
||||
'DATAOBJNO',
|
||||
'DATE',
|
||||
'DBA',
|
||||
'DBHIGH',
|
||||
'DBLOW',
|
||||
'DBMAC',
|
||||
'DEALLOCATE',
|
||||
'DEBUG',
|
||||
'DEC',
|
||||
'DECIMAL',
|
||||
'DECLARE',
|
||||
'DEFAULT',
|
||||
'DEFERRABLE',
|
||||
'DEFERRED',
|
||||
'DEGREE',
|
||||
'DELETE',
|
||||
'DEREF',
|
||||
'DESC',
|
||||
'DIRECTORY',
|
||||
'DISABLE',
|
||||
'DISCONNECT',
|
||||
'DISMOUNT',
|
||||
'DISTINCT',
|
||||
'DISTRIBUTED',
|
||||
'DML',
|
||||
'DOUBLE',
|
||||
'DROP',
|
||||
'DUMP',
|
||||
'EACH',
|
||||
'ELSE',
|
||||
'ENABLE',
|
||||
'END',
|
||||
'ENFORCE',
|
||||
'ENTRY',
|
||||
'ESCAPE',
|
||||
'EXCEPT',
|
||||
'EXCEPTIONS',
|
||||
'EXCHANGE',
|
||||
'EXCLUDING',
|
||||
'EXCLUSIVE',
|
||||
'EXECUTE',
|
||||
'EXISTS',
|
||||
'EXPIRE',
|
||||
'EXPLAIN',
|
||||
'EXTENT',
|
||||
'EXTENTS',
|
||||
'EXTERNALLY',
|
||||
'FAILED_LOGIN_ATTEMPTS',
|
||||
'FALSE',
|
||||
'FAST',
|
||||
'FILE',
|
||||
'FIRST_ROWS',
|
||||
'FLAGGER',
|
||||
'FLOAT',
|
||||
'FLOB',
|
||||
'FLUSH',
|
||||
'FOR',
|
||||
'FORCE',
|
||||
'FOREIGN',
|
||||
'FREELIST',
|
||||
'FREELISTS',
|
||||
'FROM',
|
||||
'FULL',
|
||||
'FUNCTION',
|
||||
'GLOBAL',
|
||||
'GLOBALLY',
|
||||
'GLOBAL_NAME',
|
||||
'GRANT',
|
||||
'GROUP',
|
||||
'GROUPS',
|
||||
'HASH',
|
||||
'HASHKEYS',
|
||||
'HAVING',
|
||||
'HEADER',
|
||||
'HEAP',
|
||||
'IDENTIFIED',
|
||||
'IDGENERATORS',
|
||||
'IDLE_TIME',
|
||||
'IF',
|
||||
'IMMEDIATE',
|
||||
'IN',
|
||||
'INCLUDING',
|
||||
'INCREMENT',
|
||||
'INDEX',
|
||||
'INDEXED',
|
||||
'INDEXES',
|
||||
'INDICATOR',
|
||||
'IND_PARTITION',
|
||||
'INITIAL',
|
||||
'INITIALLY',
|
||||
'INITRANS',
|
||||
'INSERT',
|
||||
'INSTANCE',
|
||||
'INSTANCES',
|
||||
'INSTEAD',
|
||||
'INT',
|
||||
'INTEGER',
|
||||
'INTERMEDIATE',
|
||||
'INTERSECT',
|
||||
'INTO',
|
||||
'IS',
|
||||
'ISOLATION',
|
||||
'ISOLATION_LEVEL',
|
||||
'KEEP',
|
||||
'KEY',
|
||||
'KILL',
|
||||
'LABEL',
|
||||
'LAYER',
|
||||
'LESS',
|
||||
'LEVEL',
|
||||
'LIBRARY',
|
||||
'LIKE',
|
||||
'LIMIT',
|
||||
'LINK',
|
||||
'LIST',
|
||||
'LOB',
|
||||
'LOCAL',
|
||||
'LOCK',
|
||||
'LOCKED',
|
||||
'LOG',
|
||||
'LOGFILE',
|
||||
'LOGGING',
|
||||
'LOGICAL_READS_PER_CALL',
|
||||
'LOGICAL_READS_PER_SESSION',
|
||||
'LONG',
|
||||
'MANAGE',
|
||||
'MASTER',
|
||||
'MAX',
|
||||
'MAXARCHLOGS',
|
||||
'MAXDATAFILES',
|
||||
'MAXEXTENTS',
|
||||
'MAXINSTANCES',
|
||||
'MAXLOGFILES',
|
||||
'MAXLOGHISTORY',
|
||||
'MAXLOGMEMBERS',
|
||||
'MAXSIZE',
|
||||
'MAXTRANS',
|
||||
'MAXVALUE',
|
||||
'MIN',
|
||||
'MEMBER',
|
||||
'MINIMUM',
|
||||
'MINEXTENTS',
|
||||
'MINUS',
|
||||
'MINVALUE',
|
||||
'MLSLABEL',
|
||||
'MLS_LABEL_FORMAT',
|
||||
'MODE',
|
||||
'MODIFY',
|
||||
'MOUNT',
|
||||
'MOVE',
|
||||
'MTS_DISPATCHERS',
|
||||
'MULTISET',
|
||||
'NATIONAL',
|
||||
'NCHAR',
|
||||
'NCHAR_CS',
|
||||
'NCLOB',
|
||||
'NEEDED',
|
||||
'NESTED',
|
||||
'NETWORK',
|
||||
'NEW',
|
||||
'NEXT',
|
||||
'NOARCHIVELOG',
|
||||
'NOAUDIT',
|
||||
'NOCACHE',
|
||||
'NOCOMPRESS',
|
||||
'NOCYCLE',
|
||||
'NOFORCE',
|
||||
'NOLOGGING',
|
||||
'NOMAXVALUE',
|
||||
'NOMINVALUE',
|
||||
'NONE',
|
||||
'NOORDER',
|
||||
'NOOVERRIDE',
|
||||
'NOPARALLEL',
|
||||
'NOPARALLEL',
|
||||
'NOREVERSE',
|
||||
'NORMAL',
|
||||
'NOSORT',
|
||||
'NOT',
|
||||
'NOTHING',
|
||||
'NOWAIT',
|
||||
'NULL',
|
||||
'NUMBER',
|
||||
'NUMERIC',
|
||||
'NVARCHAR2',
|
||||
'OBJECT',
|
||||
'OBJNO',
|
||||
'OBJNO_REUSE',
|
||||
'OF',
|
||||
'OFF',
|
||||
'OFFLINE',
|
||||
'OID',
|
||||
'OIDINDEX',
|
||||
'OLD',
|
||||
'ON',
|
||||
'ONLINE',
|
||||
'ONLY',
|
||||
'OPCODE',
|
||||
'OPEN',
|
||||
'OPTIMAL',
|
||||
'OPTIMIZER_GOAL',
|
||||
'OPTION',
|
||||
'OR',
|
||||
'ORDER',
|
||||
'ORGANIZATION',
|
||||
'OSLABEL',
|
||||
'OVERFLOW',
|
||||
'OWN',
|
||||
'PACKAGE',
|
||||
'PARALLEL',
|
||||
'PARTITION',
|
||||
'PASSWORD',
|
||||
'PASSWORD_GRACE_TIME',
|
||||
'PASSWORD_LIFE_TIME',
|
||||
'PASSWORD_LOCK_TIME',
|
||||
'PASSWORD_REUSE_MAX',
|
||||
'PASSWORD_REUSE_TIME',
|
||||
'PASSWORD_VERIFY_FUNCTION',
|
||||
'PCTFREE',
|
||||
'PCTINCREASE',
|
||||
'PCTTHRESHOLD',
|
||||
'PCTUSED',
|
||||
'PCTVERSION',
|
||||
'PERCENT',
|
||||
'PERMANENT',
|
||||
'PLAN',
|
||||
'PLSQL_DEBUG',
|
||||
'POST_TRANSACTION',
|
||||
'PRECISION',
|
||||
'PRESERVE',
|
||||
'PRIMARY',
|
||||
'PRIOR',
|
||||
'PRIVATE',
|
||||
'PRIVATE_SGA',
|
||||
'PRIVILEGE',
|
||||
'PRIVILEGES',
|
||||
'PROCEDURE',
|
||||
'PROFILE',
|
||||
'PUBLIC',
|
||||
'PURGE',
|
||||
'QUEUE',
|
||||
'QUOTA',
|
||||
'RANGE',
|
||||
'RAW',
|
||||
'RBA',
|
||||
'READ',
|
||||
'READUP',
|
||||
'REAL',
|
||||
'REBUILD',
|
||||
'RECOVER',
|
||||
'RECOVERABLE',
|
||||
'RECOVERY',
|
||||
'REF',
|
||||
'REFERENCES',
|
||||
'REFERENCING',
|
||||
'REFRESH',
|
||||
'RENAME',
|
||||
'REPLACE',
|
||||
'RESET',
|
||||
'RESETLOGS',
|
||||
'RESIZE',
|
||||
'RESOURCE',
|
||||
'RESTRICTED',
|
||||
'RETURN',
|
||||
'RETURNING',
|
||||
'REUSE',
|
||||
'REVERSE',
|
||||
'REVOKE',
|
||||
'ROLE',
|
||||
// 'ROLES',
|
||||
'ROLLBACK',
|
||||
'ROW',
|
||||
'ROWID',
|
||||
'ROWNUM',
|
||||
'ROWS',
|
||||
'RULE',
|
||||
'SAMPLE',
|
||||
'SAVEPOINT',
|
||||
'SB4',
|
||||
'SCAN_INSTANCES',
|
||||
'SCHEMA',
|
||||
'SCN',
|
||||
'SCOPE',
|
||||
'SD_ALL',
|
||||
'SD_INHIBIT',
|
||||
'SD_SHOW',
|
||||
'SEGMENT',
|
||||
'SEG_BLOCK',
|
||||
'SEG_FILE',
|
||||
'SELECT',
|
||||
'SEQUENCE',
|
||||
'SERIALIZABLE',
|
||||
'SESSION',
|
||||
'SESSION_CACHED_CURSORS',
|
||||
'SESSIONS_PER_USER',
|
||||
'SET',
|
||||
'SHARE',
|
||||
'SHARED',
|
||||
'SHARED_POOL',
|
||||
'SHRINK',
|
||||
'SIZE',
|
||||
'SKIP',
|
||||
'SKIP_UNUSABLE_INDEXES',
|
||||
'SMALLINT',
|
||||
'SNAPSHOT',
|
||||
'SOME',
|
||||
'SORT',
|
||||
'SPECIFICATION',
|
||||
'SPLIT',
|
||||
'SQL_TRACE',
|
||||
'STANDBY',
|
||||
'START',
|
||||
'STATEMENT_ID',
|
||||
'STATISTICS',
|
||||
'STOP',
|
||||
'STORAGE',
|
||||
'STORE',
|
||||
'STRUCTURE',
|
||||
'SUCCESSFUL',
|
||||
'SWITCH',
|
||||
'SYS_OP_ENFORCE_NOT_NULL$',
|
||||
'SYS_OP_NTCIMG$',
|
||||
'SYNONYM',
|
||||
'SYSDATE',
|
||||
'SYSDBA',
|
||||
'SYSOPER',
|
||||
// 'SYSTEM',
|
||||
'TABLE',
|
||||
'TABLES',
|
||||
'TABLESPACE',
|
||||
'TABLESPACE_NO',
|
||||
'TABNO',
|
||||
'TEMPORARY',
|
||||
'THAN',
|
||||
'THE',
|
||||
'THEN',
|
||||
'THREAD',
|
||||
'TIMESTAMP',
|
||||
'TIME',
|
||||
'TO',
|
||||
'TOPLEVEL',
|
||||
'TRACE',
|
||||
'TRACING',
|
||||
'TRANSACTION',
|
||||
'TRANSITIONAL',
|
||||
'TRIGGER',
|
||||
'TRIGGERS',
|
||||
'TRUE',
|
||||
'TRUNCATE',
|
||||
'TX',
|
||||
// 'TYPE',
|
||||
'UB2',
|
||||
'UBA',
|
||||
'UID',
|
||||
'UNARCHIVED',
|
||||
'UNDO',
|
||||
'UNION',
|
||||
'UNIQUE',
|
||||
'UNLIMITED',
|
||||
'UNLOCK',
|
||||
'UNRECOVERABLE',
|
||||
'UNTIL',
|
||||
'UNUSABLE',
|
||||
'UNUSED',
|
||||
'UPDATABLE',
|
||||
'UPDATE',
|
||||
'USAGE',
|
||||
'USE',
|
||||
'USER',
|
||||
'USING',
|
||||
'VALIDATE',
|
||||
'VALIDATION',
|
||||
'VALUE',
|
||||
'VALUES',
|
||||
'VARCHAR',
|
||||
'VARCHAR2',
|
||||
'VARYING',
|
||||
'VIEW',
|
||||
'WHEN',
|
||||
'WHENEVER',
|
||||
'WHERE',
|
||||
'WITH',
|
||||
'WITHOUT',
|
||||
'WORK',
|
||||
'WRITE',
|
||||
'WRITEDOWN',
|
||||
'WRITEUP',
|
||||
'XID',
|
||||
'YEAR',
|
||||
'ZONE',
|
||||
];
|
||||
|
||||
/**
|
||||
* Check if value is an Oracle reserved word.
|
||||
*
|
||||
* @param string $value
|
||||
* @return bool
|
||||
*/
|
||||
public function isReserved($value)
|
||||
{
|
||||
return in_array(Str::upper(trim($value)), $this->reserves, true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Yajra\Oci8\PDO;
|
||||
|
||||
use Doctrine\DBAL\Driver\AbstractOracleDriver;
|
||||
use Illuminate\Database\PDO\Concerns\ConnectsToDatabase;
|
||||
|
||||
class Oci8Driver extends AbstractOracleDriver
|
||||
{
|
||||
use ConnectsToDatabase;
|
||||
}
|
||||
@@ -0,0 +1,632 @@
|
||||
<?php
|
||||
|
||||
namespace Yajra\Oci8\Query\Grammars;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
|
||||
use Illuminate\Database\Query\Builder;
|
||||
use Illuminate\Database\Query\Grammars\Grammar;
|
||||
use Illuminate\Support\Str;
|
||||
use Yajra\Oci8\OracleReservedWords;
|
||||
|
||||
class OracleGrammar extends Grammar
|
||||
{
|
||||
use OracleReservedWords;
|
||||
|
||||
/**
|
||||
* The keyword identifier wrapper format.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $wrapper = '%s';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $schema_prefix = '';
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $max_length;
|
||||
|
||||
/**
|
||||
* Compile a delete statement with joins into SQL.
|
||||
*
|
||||
* @param \Illuminate\Database\Query\Builder $query
|
||||
* @param string $table
|
||||
* @param string $where
|
||||
* @return string
|
||||
*/
|
||||
protected function compileDeleteWithJoins(Builder $query, $table, $where)
|
||||
{
|
||||
$alias = last(explode(' as ', $table));
|
||||
|
||||
$joins = $this->compileJoins($query, $query->joins);
|
||||
|
||||
return "delete (select * from {$alias} {$joins} {$where})";
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile an exists statement into SQL.
|
||||
*
|
||||
* @param \Illuminate\Database\Query\Builder $query
|
||||
* @return string
|
||||
*/
|
||||
public function compileExists(Builder $query)
|
||||
{
|
||||
$q = clone $query;
|
||||
$q->columns = [];
|
||||
$q->selectRaw('1 as "exists"')
|
||||
->whereRaw('rownum = 1');
|
||||
|
||||
return $this->compileSelect($q);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile a select query into SQL.
|
||||
*
|
||||
* @param \Illuminate\Database\Query\Builder
|
||||
* @return string
|
||||
*/
|
||||
public function compileSelect(Builder $query)
|
||||
{
|
||||
if ($query->unions && $query->aggregate) {
|
||||
return $this->compileUnionAggregate($query);
|
||||
}
|
||||
|
||||
// If the query does not have any columns set, we'll set the columns to the
|
||||
// * character to just get all of the columns from the database. Then we
|
||||
// can build the query and concatenate all the pieces together as one.
|
||||
$original = $query->columns;
|
||||
|
||||
if (is_null($query->columns)) {
|
||||
$query->columns = ['*'];
|
||||
}
|
||||
|
||||
$components = $this->compileComponents($query);
|
||||
|
||||
// To compile the query, we'll spin through each component of the query and
|
||||
// see if that component exists. If it does we'll just call the compiler
|
||||
// function for the component which is responsible for making the SQL.
|
||||
$sql = trim($this->concatenate($components));
|
||||
|
||||
// If an offset is present on the query, we will need to wrap the query in
|
||||
// a big "ANSI" offset syntax block. This is very nasty compared to the
|
||||
// other database systems but is necessary for implementing features.
|
||||
if ($this->isPaginationable($query, $components)) {
|
||||
return $this->compileAnsiOffset($query, $components);
|
||||
}
|
||||
|
||||
if ($query->unions) {
|
||||
$sql = $this->wrapUnion($sql).' '.$this->compileUnions($query);
|
||||
}
|
||||
|
||||
$query->columns = $original;
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Builder $query
|
||||
* @param array $components
|
||||
* @return bool
|
||||
*/
|
||||
protected function isPaginationable(Builder $query, array $components)
|
||||
{
|
||||
return ($query->limit > 0 || $query->offset > 0) && ! array_key_exists('lock', $components);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a full ANSI offset clause for the query.
|
||||
*
|
||||
* @param \Illuminate\Database\Query\Builder $query
|
||||
* @param array $components
|
||||
* @return string
|
||||
*/
|
||||
protected function compileAnsiOffset(Builder $query, $components)
|
||||
{
|
||||
// Improved response time with FIRST_ROWS(n) hint for ORDER BY queries
|
||||
if ($query->getConnection()->getConfig('server_version') == '12c') {
|
||||
$components['columns'] = str_replace('select', "select /*+ FIRST_ROWS({$query->limit}) */", $components['columns']);
|
||||
$offset = $query->offset ?: 0;
|
||||
$limit = $query->limit;
|
||||
$components['limit'] = "offset $offset rows fetch next $limit rows only";
|
||||
|
||||
return $this->concatenate($components);
|
||||
}
|
||||
|
||||
$constraint = $this->compileRowConstraint($query);
|
||||
|
||||
$sql = $this->concatenate($components);
|
||||
|
||||
// We are now ready to build the final SQL query so we'll create a common table
|
||||
// expression from the query and get the records with row numbers within our
|
||||
// given limit and offset value that we just put on as a query constraint.
|
||||
return $this->compileTableExpression($sql, $constraint, $query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile the limit / offset row constraint for a query.
|
||||
*
|
||||
* @param \Illuminate\Database\Query\Builder $query
|
||||
* @return string
|
||||
*/
|
||||
protected function compileRowConstraint($query)
|
||||
{
|
||||
$start = $query->offset + 1;
|
||||
$finish = $query->offset + $query->limit;
|
||||
|
||||
if ($query->limit == 1 && is_null($query->offset)) {
|
||||
return '= 1';
|
||||
}
|
||||
|
||||
if ($query->offset && is_null($query->limit)) {
|
||||
return ">= {$start}";
|
||||
}
|
||||
|
||||
return "between {$start} and {$finish}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile a common table expression for a query.
|
||||
*
|
||||
* @param string $sql
|
||||
* @param string $constraint
|
||||
* @param Builder $query
|
||||
* @return string
|
||||
*/
|
||||
protected function compileTableExpression($sql, $constraint, $query)
|
||||
{
|
||||
if ($query->limit == 1 && is_null($query->offset)) {
|
||||
return "select * from ({$sql}) where rownum {$constraint}";
|
||||
}
|
||||
|
||||
if (! is_null($query->limit && ! is_null($query->offset))) {
|
||||
$start = $query->offset + 1;
|
||||
$finish = $query->offset + $query->limit;
|
||||
|
||||
return "select t2.* from ( select rownum AS \"rn\", t1.* from ({$sql}) t1 where rownum <= {$finish}) t2 where t2.\"rn\" >= {$start}";
|
||||
}
|
||||
|
||||
return "select t2.* from ( select rownum AS \"rn\", t1.* from ({$sql}) t1 ) t2 where t2.\"rn\" {$constraint}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile a truncate table statement into SQL.
|
||||
*
|
||||
* @param \Illuminate\Database\Query\Builder $query
|
||||
* @return array
|
||||
*/
|
||||
public function compileTruncate(Builder $query)
|
||||
{
|
||||
return ['truncate table '.$this->wrapTable($query->from) => []];
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap a value in keyword identifiers.
|
||||
*
|
||||
* Override due to laravel's stringify integers.
|
||||
*
|
||||
* @param \Illuminate\Database\Query\Expression|string $value
|
||||
* @param bool $prefixAlias
|
||||
* @return string
|
||||
*/
|
||||
public function wrap($value, $prefixAlias = false)
|
||||
{
|
||||
if (is_int($value) || is_float($value)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
return parent::wrap($value, $prefixAlias);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap a table in keyword identifiers.
|
||||
*
|
||||
* @param \Illuminate\Database\Query\Expression|string $table
|
||||
* @return string
|
||||
*/
|
||||
public function wrapTable($table)
|
||||
{
|
||||
if ($this->isExpression($table)) {
|
||||
return $this->getValue($table);
|
||||
}
|
||||
|
||||
if (strpos(strtolower($table), ' as ') !== false) {
|
||||
$table = str_replace(' as ', ' ', strtolower($table));
|
||||
}
|
||||
|
||||
$tableName = $this->wrap($this->tablePrefix.$table, true);
|
||||
$segments = explode(' ', $table);
|
||||
if (count($segments) > 1) {
|
||||
$tableName = $this->wrap($this->tablePrefix.$segments[0]).' '.$segments[1];
|
||||
}
|
||||
|
||||
return $this->getSchemaPrefix().$tableName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the schema prefix.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSchemaPrefix()
|
||||
{
|
||||
return ! empty($this->schema_prefix) ? $this->wrapValue($this->schema_prefix).'.' : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get max length.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getMaxLength()
|
||||
{
|
||||
return ! empty($this->max_length) ? $this->max_length : 30;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the schema prefix.
|
||||
*
|
||||
* @param string $prefix
|
||||
*/
|
||||
public function setSchemaPrefix($prefix)
|
||||
{
|
||||
$this->schema_prefix = $prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set max length.
|
||||
*
|
||||
* @param int $length
|
||||
*/
|
||||
public function setMaxLength($length)
|
||||
{
|
||||
$this->max_length = $length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap a single string in keyword identifiers.
|
||||
*
|
||||
* @param string $value
|
||||
* @return string
|
||||
*/
|
||||
protected function wrapValue($value)
|
||||
{
|
||||
if ($value === '*') {
|
||||
return $value;
|
||||
}
|
||||
|
||||
$value = Str::upper($value);
|
||||
|
||||
return '"'.str_replace('"', '""', $value).'"';
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile an insert and get ID statement into SQL.
|
||||
*
|
||||
* @param \Illuminate\Database\Query\Builder $query
|
||||
* @param array $values
|
||||
* @param string $sequence
|
||||
* @return string
|
||||
*/
|
||||
public function compileInsertGetId(Builder $query, $values, $sequence = 'id')
|
||||
{
|
||||
if (empty($sequence)) {
|
||||
$sequence = 'id';
|
||||
}
|
||||
|
||||
$backtrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 4)[2]['object'];
|
||||
|
||||
if ($backtrace instanceof EloquentBuilder) {
|
||||
$model = $backtrace->getModel();
|
||||
if ($model->sequence && ! isset($values[$model->getKeyName()]) && $model->incrementing) {
|
||||
$values[$sequence] = null;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->compileInsert($query, $values).' returning '.$this->wrap($sequence).' into ?';
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile an insert statement into SQL.
|
||||
*
|
||||
* @param \Illuminate\Database\Query\Builder $query
|
||||
* @param array $values
|
||||
* @return string
|
||||
*/
|
||||
public function compileInsert(Builder $query, array $values)
|
||||
{
|
||||
// Essentially we will force every insert to be treated as a batch insert which
|
||||
// simply makes creating the SQL easier for us since we can utilize the same
|
||||
// basic routine regardless of an amount of records given to us to insert.
|
||||
$table = $this->wrapTable($query->from);
|
||||
|
||||
if (! is_array(reset($values))) {
|
||||
$values = [$values];
|
||||
}
|
||||
|
||||
$columns = $this->columnize(array_keys(reset($values)));
|
||||
|
||||
// We need to build a list of parameter place-holders of values that are bound
|
||||
// to the query. Each insert should have the exact same amount of parameter
|
||||
// bindings so we can just go off the first list of values in this array.
|
||||
$parameters = $this->parameterize(reset($values));
|
||||
|
||||
$value = array_fill(0, count($values), "($parameters)");
|
||||
|
||||
if (count($value) > 1) {
|
||||
$insertQueries = [];
|
||||
foreach ($value as $parameter) {
|
||||
$parameter = str_replace(['(', ')'], '', $parameter);
|
||||
$insertQueries[] = 'select '.$parameter.' from dual ';
|
||||
}
|
||||
$parameters = implode('union all ', $insertQueries);
|
||||
|
||||
return "insert into $table ($columns) $parameters";
|
||||
}
|
||||
$parameters = implode(', ', $value);
|
||||
|
||||
return "insert into $table ($columns) values $parameters";
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile an insert with blob field statement into SQL.
|
||||
*
|
||||
* @param \Illuminate\Database\Query\Builder $query
|
||||
* @param array $values
|
||||
* @param array $binaries
|
||||
* @param string $sequence
|
||||
* @return string
|
||||
*/
|
||||
public function compileInsertLob(Builder $query, $values, $binaries, $sequence = 'id')
|
||||
{
|
||||
if (empty($sequence)) {
|
||||
$sequence = 'id';
|
||||
}
|
||||
|
||||
$table = $this->wrapTable($query->from);
|
||||
|
||||
if (! is_array(reset($values))) {
|
||||
$values = [$values];
|
||||
}
|
||||
|
||||
if (! is_array(reset($binaries))) {
|
||||
$binaries = [$binaries];
|
||||
}
|
||||
|
||||
$columns = $this->columnize(array_keys(reset($values)));
|
||||
$binaryColumns = $this->columnize(array_keys(reset($binaries)));
|
||||
$columns .= (empty($columns) ? '' : ', ').$binaryColumns;
|
||||
|
||||
$parameters = $this->parameterize(reset($values));
|
||||
$binaryParameters = $this->parameterize(reset($binaries));
|
||||
|
||||
$value = array_fill(0, count($values), "$parameters");
|
||||
$binaryValue = array_fill(0, count($binaries), str_replace('?', 'EMPTY_BLOB()', $binaryParameters));
|
||||
|
||||
$value = array_merge($value, $binaryValue);
|
||||
$parameters = implode(', ', array_filter($value));
|
||||
|
||||
return "insert into $table ($columns) values ($parameters) returning ".$binaryColumns.', '.$this->wrap($sequence).' into '.$binaryParameters.', ?';
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile an update statement into SQL.
|
||||
*
|
||||
* @param \Illuminate\Database\Query\Builder $query
|
||||
* @param array $values
|
||||
* @param array $binaries
|
||||
* @param string $sequence
|
||||
* @return string
|
||||
*/
|
||||
public function compileUpdateLob(Builder $query, array $values, array $binaries, $sequence = 'id')
|
||||
{
|
||||
$table = $this->wrapTable($query->from);
|
||||
|
||||
// Each one of the columns in the update statements needs to be wrapped in the
|
||||
// keyword identifiers, also a place-holder needs to be created for each of
|
||||
// the values in the list of bindings so we can make the sets statements.
|
||||
$columns = [];
|
||||
|
||||
foreach ($values as $key => $value) {
|
||||
$columns[] = $this->wrap($key).' = '.$this->parameter($value);
|
||||
}
|
||||
|
||||
$columns = implode(', ', $columns);
|
||||
|
||||
// set blob variables
|
||||
if (! is_array(reset($binaries))) {
|
||||
$binaries = [$binaries];
|
||||
}
|
||||
$binaryColumns = $this->columnize(array_keys(reset($binaries)));
|
||||
$binaryParameters = $this->parameterize(reset($binaries));
|
||||
|
||||
// create EMPTY_BLOB sql for each binary
|
||||
$binarySql = [];
|
||||
foreach ((array) $binaryColumns as $binary) {
|
||||
$binarySql[] = "$binary = EMPTY_BLOB()";
|
||||
}
|
||||
|
||||
// prepare binary SQLs
|
||||
if (count($binarySql)) {
|
||||
$binarySql = (empty($columns) ? '' : ', ').implode(',', $binarySql);
|
||||
}
|
||||
|
||||
// If the query has any "join" clauses, we will setup the joins on the builder
|
||||
// and compile them so we can attach them to this update, as update queries
|
||||
// can get join statements to attach to other tables when they're needed.
|
||||
$joins = '';
|
||||
if (isset($query->joins)) {
|
||||
$joins = ' '.$this->compileJoins($query, $query->joins);
|
||||
}
|
||||
|
||||
// Of course, update queries may also be constrained by where clauses so we'll
|
||||
// need to compile the where clauses and attach it to the query so only the
|
||||
// intended records are updated by the SQL statements we generate to run.
|
||||
$where = $this->compileWheres($query);
|
||||
|
||||
return "update {$table}{$joins} set $columns$binarySql $where returning ".$binaryColumns.', '.$this->wrap($sequence).' into '.$binaryParameters.', ?';
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile the lock into SQL.
|
||||
*
|
||||
* @param \Illuminate\Database\Query\Builder $query
|
||||
* @param bool|string $value
|
||||
* @return string
|
||||
*/
|
||||
protected function compileLock(Builder $query, $value)
|
||||
{
|
||||
if (is_string($value)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if ($value) {
|
||||
return 'for update';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile the "limit" portions of the query.
|
||||
*
|
||||
* @param \Illuminate\Database\Query\Builder $query
|
||||
* @param int $limit
|
||||
* @return string
|
||||
*/
|
||||
protected function compileLimit(Builder $query, $limit)
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile the "offset" portions of the query.
|
||||
*
|
||||
* @param \Illuminate\Database\Query\Builder $query
|
||||
* @param int $offset
|
||||
* @return string
|
||||
*/
|
||||
protected function compileOffset(Builder $query, $offset)
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile a "where date" clause.
|
||||
*
|
||||
* @param \Illuminate\Database\Query\Builder $query
|
||||
* @param array $where
|
||||
* @return string
|
||||
*/
|
||||
protected function whereDate(Builder $query, $where)
|
||||
{
|
||||
$value = $this->parameter($where['value']);
|
||||
|
||||
return "trunc({$this->wrap($where['column'])}) {$where['operator']} $value";
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile a date based where clause.
|
||||
*
|
||||
* @param string $type
|
||||
* @param \Illuminate\Database\Query\Builder $query
|
||||
* @param array $where
|
||||
* @return string
|
||||
*/
|
||||
protected function dateBasedWhere($type, Builder $query, $where)
|
||||
{
|
||||
$value = $this->parameter($where['value']);
|
||||
|
||||
return "extract ($type from {$this->wrap($where['column'])}) {$where['operator']} $value";
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile a "where not in raw" clause.
|
||||
*
|
||||
* For safety, whereIntegerInRaw ensures this method is only used with integer values.
|
||||
*
|
||||
* @param \Illuminate\Database\Query\Builder $query
|
||||
* @param array $where
|
||||
* @return string
|
||||
*/
|
||||
protected function whereNotInRaw(Builder $query, $where)
|
||||
{
|
||||
if (! empty($where['values'])) {
|
||||
if (is_array($where['values']) && count($where['values']) > 1000) {
|
||||
return $this->resolveClause($where['column'], $where['values'], 'not in');
|
||||
} else {
|
||||
return $this->wrap($where['column']).' not in ('.implode(', ', $where['values']).')';
|
||||
}
|
||||
}
|
||||
|
||||
return '1 = 1';
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile a "where in raw" clause.
|
||||
*
|
||||
* For safety, whereIntegerInRaw ensures this method is only used with integer values.
|
||||
*
|
||||
* @param \Illuminate\Database\Query\Builder $query
|
||||
* @param array $where
|
||||
* @return string
|
||||
*/
|
||||
protected function whereInRaw(Builder $query, $where)
|
||||
{
|
||||
if (! empty($where['values'])) {
|
||||
if (is_array($where['values']) && count($where['values']) > 1000) {
|
||||
return $this->resolveClause($where['column'], $where['values'], 'in');
|
||||
} else {
|
||||
return $this->wrap($where['column']).' in ('.implode(', ', $where['values']).')';
|
||||
}
|
||||
}
|
||||
|
||||
return '0 = 1';
|
||||
}
|
||||
|
||||
private function resolveClause($column, $values, $type)
|
||||
{
|
||||
$chunks = array_chunk($values, 1000);
|
||||
$whereClause = '';
|
||||
$i = 0;
|
||||
$type = $this->wrap($column).' '.$type.' ';
|
||||
foreach ($chunks as $ch) {
|
||||
// Add or only at the second loop
|
||||
if ($i === 1) {
|
||||
$type = ' or '.$type.' ';
|
||||
}
|
||||
$whereClause .= $type.'('.implode(', ', $ch).')';
|
||||
$i++;
|
||||
}
|
||||
|
||||
return '('.$whereClause.')';
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile a union aggregate query into SQL.
|
||||
*
|
||||
* @param \Illuminate\Database\Query\Builder $query
|
||||
* @return string
|
||||
*/
|
||||
protected function compileUnionAggregate(Builder $query)
|
||||
{
|
||||
$sql = $this->compileAggregate($query, $query->aggregate);
|
||||
|
||||
$query->aggregate = null;
|
||||
|
||||
return $sql.' from ('.$this->compileSelect($query).') '.$this->wrapTable('temp_table');
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile the random statement into SQL.
|
||||
*
|
||||
* @param string $seed
|
||||
* @return string
|
||||
*/
|
||||
public function compileRandom($seed)
|
||||
{
|
||||
return 'DBMS_RANDOM.RANDOM';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
<?php
|
||||
|
||||
namespace Yajra\Oci8\Query;
|
||||
|
||||
use Illuminate\Contracts\Support\Arrayable;
|
||||
use Illuminate\Database\Query\Builder;
|
||||
use Illuminate\Database\Query\Expression;
|
||||
|
||||
class OracleBuilder extends Builder
|
||||
{
|
||||
/**
|
||||
* Run a pagination count query.
|
||||
*
|
||||
* @param array $columns
|
||||
* @return array
|
||||
*/
|
||||
protected function runPaginationCountQuery($columns = ['*'])
|
||||
{
|
||||
if ($this->groups || $this->havings) {
|
||||
$clone = $this->cloneForPaginationCount();
|
||||
|
||||
if (is_null($clone->columns) && ! empty($this->joins)) {
|
||||
$clone->select($this->from.'.*');
|
||||
}
|
||||
|
||||
return $this->newQuery()
|
||||
->from(new Expression('('.$clone->toSql().')'))
|
||||
->mergeBindings($clone)
|
||||
->setAggregate('count', $this->withoutSelectAliases($columns))
|
||||
->get()->all();
|
||||
}
|
||||
|
||||
$without = $this->unions ? ['orders', 'limit', 'offset'] : ['columns', 'orders', 'limit', 'offset'];
|
||||
|
||||
return $this->cloneWithout($without)
|
||||
->cloneWithoutBindings($this->unions ? ['order'] : ['select', 'order'])
|
||||
->setAggregate('count', $this->withoutSelectAliases($columns))
|
||||
->get()->all();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the count of the total records for the paginator.
|
||||
*
|
||||
* @param array $columns
|
||||
* @return int
|
||||
*/
|
||||
public function getCountForPagination($columns = ['*'])
|
||||
{
|
||||
$results = $this->runPaginationCountQuery($columns);
|
||||
|
||||
// Once we have run the pagination count query, we will get the resulting count and
|
||||
// take into account what type of query it was. When there is a group by we will
|
||||
// just return the count of the entire results set since that will be correct.
|
||||
if (! isset($results[0])) {
|
||||
return 0;
|
||||
} elseif (is_object($results[0])) {
|
||||
return (int) (property_exists($results[0], 'AGGREGATE') ? $results[0]->AGGREGATE : $results[0]->aggregate); // to solve the Oracle issue: auto-convert field to uppercase
|
||||
}
|
||||
|
||||
return (int) array_change_key_case((array) $results[0])['aggregate'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a new record and get the value of the primary key.
|
||||
*
|
||||
* @param array $values
|
||||
* @param array $binaries
|
||||
* @param string $sequence
|
||||
* @return int
|
||||
*/
|
||||
public function insertLob(array $values, array $binaries, $sequence = 'id')
|
||||
{
|
||||
/** @var \Yajra\Oci8\Query\Grammars\OracleGrammar $grammar */
|
||||
$grammar = $this->grammar;
|
||||
$sql = $grammar->compileInsertLob($this, $values, $binaries, $sequence);
|
||||
|
||||
$values = $this->cleanBindings($values);
|
||||
$binaries = $this->cleanBindings($binaries);
|
||||
|
||||
/** @var \Yajra\Oci8\Query\Processors\OracleProcessor $processor */
|
||||
$processor = $this->processor;
|
||||
|
||||
return $processor->saveLob($this, $sql, $values, $binaries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a new record with blob field.
|
||||
*
|
||||
* @param array $values
|
||||
* @param array $binaries
|
||||
* @param string $sequence
|
||||
* @return bool
|
||||
*/
|
||||
public function updateLob(array $values, array $binaries, $sequence = 'id')
|
||||
{
|
||||
$bindings = array_values(array_merge($values, $this->getBindings()));
|
||||
|
||||
/** @var \Yajra\Oci8\Query\Grammars\OracleGrammar $grammar */
|
||||
$grammar = $this->grammar;
|
||||
$sql = $grammar->compileUpdateLob($this, $values, $binaries, $sequence);
|
||||
|
||||
$values = $this->cleanBindings($bindings);
|
||||
$binaries = $this->cleanBindings($binaries);
|
||||
|
||||
/** @var \Yajra\Oci8\Query\Processors\OracleProcessor $processor */
|
||||
$processor = $this->processor;
|
||||
|
||||
return $processor->saveLob($this, $sql, $values, $binaries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a "where in" clause to the query.
|
||||
* Split one WHERE IN clause into multiple clauses each
|
||||
* with up to 1000 expressions to avoid ORA-01795.
|
||||
*
|
||||
* @param string $column
|
||||
* @param mixed $values
|
||||
* @param string $boolean
|
||||
* @param bool $not
|
||||
* @return \Illuminate\Database\Query\Builder|\Yajra\Oci8\Query\OracleBuilder
|
||||
*/
|
||||
public function whereIn($column, $values, $boolean = 'and', $not = false)
|
||||
{
|
||||
$type = $not ? 'NotIn' : 'In';
|
||||
|
||||
if ($values instanceof Arrayable) {
|
||||
$values = $values->toArray();
|
||||
}
|
||||
|
||||
if (is_array($values) && count($values) > 1000) {
|
||||
$chunks = array_chunk($values, 1000);
|
||||
|
||||
return $this->where(function ($query) use ($column, $chunks, $type, $not) {
|
||||
foreach ($chunks as $ch) {
|
||||
$sqlClause = $not ? 'where'.$type : 'orWhere'.$type;
|
||||
$query->{$sqlClause}($column, $ch);
|
||||
}
|
||||
}, null, null, $boolean);
|
||||
}
|
||||
|
||||
return parent::whereIn($column, $values, $boolean, $not);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the query as a "select" statement against the connection.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function runSelect()
|
||||
{
|
||||
if ($this->lock) {
|
||||
$this->connection->beginTransaction();
|
||||
$result = $this->connection->select($this->toSql(), $this->getBindings(), ! $this->useWritePdo);
|
||||
$this->connection->commit();
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
return $this->connection->select($this->toSql(), $this->getBindings(), ! $this->useWritePdo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the table which the query is targeting.
|
||||
*
|
||||
* @param \Closure|\Illuminate\Database\Query\Builder|string $table
|
||||
* @param string|null $as
|
||||
* @return $this
|
||||
*/
|
||||
public function from($table, $as = null)
|
||||
{
|
||||
if ($this->isQueryable($table)) {
|
||||
return $this->fromSub($table, $as);
|
||||
}
|
||||
|
||||
$this->from = $as ? "{$table} {$as}" : $table;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes "from" fetch from a subquery.
|
||||
*
|
||||
* @param \Closure|\Illuminate\Database\Query\Builder|string $query
|
||||
* @param string $as
|
||||
* @return \Illuminate\Database\Query\Builder|static
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function fromSub($query, $as)
|
||||
{
|
||||
[$query, $bindings] = $this->createSub($query);
|
||||
|
||||
return $this->fromRaw('('.$query.') '.$this->grammar->wrapTable($as), $bindings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a subquery join clause to the query.
|
||||
*
|
||||
* @param \Closure|\Illuminate\Database\Query\Builder|string $query
|
||||
* @param string $as
|
||||
* @param \Closure|string $first
|
||||
* @param string|null $operator
|
||||
* @param string|null $second
|
||||
* @param string $type
|
||||
* @param bool $where
|
||||
* @return \Illuminate\Database\Query\Builder|static
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function joinSub($query, $as, $first, $operator = null, $second = null, $type = 'inner', $where = false)
|
||||
{
|
||||
[$query, $bindings] = $this->createSub($query);
|
||||
|
||||
$expression = '('.$query.') '.$this->grammar->wrapTable($as);
|
||||
|
||||
$this->addBinding($bindings, 'join');
|
||||
|
||||
return $this->join(new Expression($expression), $first, $operator, $second, $type, $where);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a subquery cross join to the query.
|
||||
*
|
||||
* @param \Closure|\Illuminate\Database\Query\Builder|string $query
|
||||
* @param string $as
|
||||
* @return $this
|
||||
*/
|
||||
public function crossJoinSub($query, $as)
|
||||
{
|
||||
[$query, $bindings] = $this->createSub($query);
|
||||
|
||||
$expression = '('.$query.') '.$this->grammar->wrapTable($as);
|
||||
|
||||
$this->addBinding($bindings, 'join');
|
||||
|
||||
$this->joins[] = $this->newJoinClause($this, 'cross', new Expression($expression));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone the query.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function clone()
|
||||
{
|
||||
return clone $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
<?php
|
||||
|
||||
namespace Yajra\Oci8\Query\Processors;
|
||||
|
||||
use DateTime;
|
||||
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
|
||||
use Illuminate\Database\Query\Builder;
|
||||
use Illuminate\Database\Query\Processors\Processor;
|
||||
use PDO;
|
||||
|
||||
class OracleProcessor extends Processor
|
||||
{
|
||||
/**
|
||||
* Process an "insert get ID" query.
|
||||
*
|
||||
* @param Builder $query
|
||||
* @param string $sql
|
||||
* @param array $values
|
||||
* @param string $sequence
|
||||
* @return int
|
||||
*/
|
||||
public function processInsertGetId(Builder $query, $sql, $values, $sequence = null)
|
||||
{
|
||||
$connection = $query->getConnection();
|
||||
|
||||
$connection->recordsHaveBeenModified();
|
||||
$start = microtime(true);
|
||||
|
||||
$id = 0;
|
||||
$parameter = 1;
|
||||
$statement = $this->prepareStatement($query, $sql);
|
||||
$values = $this->incrementBySequence($values, $sequence);
|
||||
$parameter = $this->bindValues($values, $statement, $parameter);
|
||||
$statement->bindParam($parameter, $id, PDO::PARAM_INT, -1);
|
||||
$statement->execute();
|
||||
|
||||
$connection->logQuery($sql, $values, $start);
|
||||
|
||||
return (int) $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get prepared statement.
|
||||
*
|
||||
* @param Builder $query
|
||||
* @param string $sql
|
||||
* @return \PDOStatement|\Yajra\Pdo\Oci8
|
||||
*/
|
||||
private function prepareStatement(Builder $query, $sql)
|
||||
{
|
||||
/** @var \Yajra\Oci8\Oci8Connection $connection */
|
||||
$connection = $query->getConnection();
|
||||
$pdo = $connection->getPdo();
|
||||
|
||||
return $pdo->prepare($sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a new record and get the value of the primary key.
|
||||
*
|
||||
* @param array $values
|
||||
* @param string $sequence
|
||||
* @return array
|
||||
*/
|
||||
protected function incrementBySequence(array $values, $sequence)
|
||||
{
|
||||
$builder = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 5)[3]['object'];
|
||||
$builderArgs = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 5)[2]['args'];
|
||||
|
||||
if (! isset($builderArgs[1][0][$sequence])) {
|
||||
if ($builder instanceof EloquentBuilder) {
|
||||
/** @var \Yajra\Oci8\Eloquent\OracleEloquent $model */
|
||||
$model = $builder->getModel();
|
||||
/** @var \Yajra\Oci8\Oci8Connection $connection */
|
||||
$connection = $model->getConnection();
|
||||
if ($model->sequence && $model->incrementing) {
|
||||
$values[] = (int) $connection->getSequence()->nextValue($model->sequence);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind values to PDO statement.
|
||||
*
|
||||
* @param array $values
|
||||
* @param \PDOStatement $statement
|
||||
* @param int $parameter
|
||||
* @return int
|
||||
*/
|
||||
private function bindValues(&$values, $statement, $parameter)
|
||||
{
|
||||
$count = count($values);
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
if (is_object($values[$i])) {
|
||||
if ($values[$i] instanceof DateTime) {
|
||||
$values[$i] = $values[$i]->format('Y-m-d H:i:s');
|
||||
} else {
|
||||
$values[$i] = (string) $values[$i];
|
||||
}
|
||||
}
|
||||
$type = $this->getPdoType($values[$i]);
|
||||
$statement->bindParam($parameter, $values[$i], $type);
|
||||
$parameter++;
|
||||
}
|
||||
|
||||
return $parameter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get PDO Type depending on value.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @return int
|
||||
*/
|
||||
private function getPdoType($value)
|
||||
{
|
||||
if (is_int($value)) {
|
||||
return PDO::PARAM_INT;
|
||||
}
|
||||
|
||||
if (is_bool($value)) {
|
||||
return PDO::PARAM_BOOL;
|
||||
}
|
||||
|
||||
if (is_null($value)) {
|
||||
return PDO::PARAM_NULL;
|
||||
}
|
||||
|
||||
return PDO::PARAM_STR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save Query with Blob returning primary key value.
|
||||
*
|
||||
* @param Builder $query
|
||||
* @param string $sql
|
||||
* @param array $values
|
||||
* @param array $binaries
|
||||
* @return int
|
||||
*/
|
||||
public function saveLob(Builder $query, $sql, array $values, array $binaries)
|
||||
{
|
||||
$connection = $query->getConnection();
|
||||
|
||||
$connection->recordsHaveBeenModified();
|
||||
$start = microtime(true);
|
||||
|
||||
$id = 0;
|
||||
$parameter = 1;
|
||||
$statement = $this->prepareStatement($query, $sql);
|
||||
|
||||
$parameter = $this->bindValues($values, $statement, $parameter);
|
||||
|
||||
$countBinary = count($binaries);
|
||||
for ($i = 0; $i < $countBinary; $i++) {
|
||||
$statement->bindParam($parameter, $binaries[$i], PDO::PARAM_LOB, -1);
|
||||
$parameter++;
|
||||
}
|
||||
|
||||
// bind output param for the returning clause.
|
||||
$statement->bindParam($parameter, $id, PDO::PARAM_INT, -1);
|
||||
|
||||
if (! $statement->execute()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$connection->logQuery($sql, $values, $start);
|
||||
|
||||
return (int) $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the results of a column listing query.
|
||||
*
|
||||
* @param array $results
|
||||
* @return array
|
||||
*/
|
||||
public function processColumnListing($results)
|
||||
{
|
||||
$mapping = function ($r) {
|
||||
$r = (object) $r;
|
||||
|
||||
return strtolower($r->column_name);
|
||||
};
|
||||
|
||||
return array_map($mapping, $results);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
namespace Yajra\Oci8\Schema;
|
||||
|
||||
use Illuminate\Database\Connection;
|
||||
use Illuminate\Database\Schema\Grammars\Grammar;
|
||||
use Yajra\Oci8\OracleReservedWords;
|
||||
|
||||
class Comment extends Grammar
|
||||
{
|
||||
use OracleReservedWords;
|
||||
|
||||
/**
|
||||
* @var \Illuminate\Database\Connection
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* @param Connection $connection
|
||||
*/
|
||||
public function __construct(Connection $connection)
|
||||
{
|
||||
$this->connection = $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set table and column comments.
|
||||
*
|
||||
* @param \Yajra\Oci8\Schema\OracleBlueprint $blueprint
|
||||
*/
|
||||
public function setComments(OracleBlueprint $blueprint)
|
||||
{
|
||||
$this->commentTable($blueprint);
|
||||
|
||||
$this->fluentComments($blueprint);
|
||||
|
||||
$this->commentColumns($blueprint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the comment on table statement.
|
||||
* Comment set by $table->comment = 'comment';.
|
||||
*
|
||||
* @param \Yajra\Oci8\Schema\OracleBlueprint $blueprint
|
||||
*/
|
||||
private function commentTable(OracleBlueprint $blueprint)
|
||||
{
|
||||
$table = $this->wrapValue($blueprint->getTable());
|
||||
|
||||
if ($blueprint->comment != null) {
|
||||
$this->connection->statement("comment on table {$table} is '{$blueprint->comment}'");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap reserved words.
|
||||
*
|
||||
* @param string $value
|
||||
* @return string
|
||||
*/
|
||||
protected function wrapValue($value)
|
||||
{
|
||||
return $this->isReserved($value) ? parent::wrapValue($value) : $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add comments set via fluent setter.
|
||||
* Comments set by $table->string('column')->comment('comment');.
|
||||
*
|
||||
* @param \Yajra\Oci8\Schema\OracleBlueprint $blueprint
|
||||
*/
|
||||
private function fluentComments(OracleBlueprint $blueprint)
|
||||
{
|
||||
foreach ($blueprint->getColumns() as $column) {
|
||||
if (isset($column['comment'])) {
|
||||
$this->commentColumn($blueprint->getTable(), $column['name'], $column['comment']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the comment on column statement.
|
||||
*
|
||||
* @param string $table
|
||||
* @param string $column
|
||||
* @param string $comment
|
||||
*/
|
||||
private function commentColumn($table, $column, $comment)
|
||||
{
|
||||
$table = $this->wrapValue($table);
|
||||
$table = $this->connection->getTablePrefix().$table;
|
||||
$column = $this->wrapValue($column);
|
||||
|
||||
$this->connection->statement("comment on column {$table}.{$column} is '{$comment}'");
|
||||
}
|
||||
|
||||
/**
|
||||
* Add comments on columns.
|
||||
* Comments set by $table->commentColumns = ['column' => 'comment'];.
|
||||
*
|
||||
* @param \Yajra\Oci8\Schema\OracleBlueprint $blueprint
|
||||
*/
|
||||
private function commentColumns(OracleBlueprint $blueprint)
|
||||
{
|
||||
foreach ($blueprint->commentColumns as $column => $comment) {
|
||||
$this->commentColumn($blueprint->getTable(), $column, $comment);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,851 @@
|
||||
<?php
|
||||
|
||||
namespace Yajra\Oci8\Schema\Grammars;
|
||||
|
||||
use Illuminate\Database\Connection;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Schema\Grammars\Grammar;
|
||||
use Illuminate\Support\Fluent;
|
||||
use Illuminate\Support\Str;
|
||||
use Yajra\Oci8\OracleReservedWords;
|
||||
|
||||
class OracleGrammar extends Grammar
|
||||
{
|
||||
use OracleReservedWords;
|
||||
|
||||
/**
|
||||
* The keyword identifier wrapper format.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $wrapper = '%s';
|
||||
|
||||
/**
|
||||
* The possible column modifiers.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $modifiers = ['Increment', 'Nullable', 'Default'];
|
||||
|
||||
/**
|
||||
* The possible column serials.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $serials = ['bigInteger', 'integer', 'mediumInteger', 'smallInteger', 'tinyInteger'];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $schema_prefix = '';
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $max_length = 30;
|
||||
|
||||
/**
|
||||
* If this Grammar supports schema changes wrapped in a transaction.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $transactions = true;
|
||||
|
||||
/**
|
||||
* Compile a create table command.
|
||||
*
|
||||
* @param \Illuminate\Database\Schema\Blueprint $blueprint
|
||||
* @param \Illuminate\Support\Fluent $command
|
||||
* @return string
|
||||
*/
|
||||
public function compileCreate(Blueprint $blueprint, Fluent $command)
|
||||
{
|
||||
$columns = implode(', ', $this->getColumns($blueprint));
|
||||
|
||||
$sql = 'create table '.$this->wrapTable($blueprint)." ( $columns";
|
||||
|
||||
/*
|
||||
* To be able to name the primary/foreign keys when the table is
|
||||
* initially created we will need to check for a primary/foreign
|
||||
* key commands and add the columns to the table's declaration
|
||||
* here so they can be created on the tables.
|
||||
*/
|
||||
$sql .= (string) $this->addForeignKeys($blueprint);
|
||||
|
||||
$sql .= (string) $this->addPrimaryKeys($blueprint);
|
||||
|
||||
$sql .= ' )';
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap a table in keyword identifiers.
|
||||
*
|
||||
* @param mixed $table
|
||||
* @return string
|
||||
*/
|
||||
public function wrapTable($table)
|
||||
{
|
||||
return $this->getSchemaPrefix().parent::wrapTable($table);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the schema prefix.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSchemaPrefix()
|
||||
{
|
||||
return ! empty($this->schema_prefix) ? $this->schema_prefix.'.' : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get max length.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getMaxLength()
|
||||
{
|
||||
return ! empty($this->max_length) ? $this->max_length : 30;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the schema prefix.
|
||||
*
|
||||
* @param string $prefix
|
||||
*/
|
||||
public function setSchemaPrefix($prefix)
|
||||
{
|
||||
$this->schema_prefix = $prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set max length.
|
||||
*
|
||||
* @param int $length
|
||||
*/
|
||||
public function setMaxLength($length)
|
||||
{
|
||||
$this->max_length = $length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the foreign key syntax for a table creation statement.
|
||||
*
|
||||
* @param \Illuminate\Database\Schema\Blueprint $blueprint
|
||||
* @return string
|
||||
*/
|
||||
protected function addForeignKeys(Blueprint $blueprint)
|
||||
{
|
||||
$sql = '';
|
||||
|
||||
$foreigns = $this->getCommandsByName($blueprint, 'foreign');
|
||||
|
||||
// Once we have all the foreign key commands for the table creation statement
|
||||
// we'll loop through each of them and add them to the create table SQL we
|
||||
// are building
|
||||
foreach ($foreigns as $foreign) {
|
||||
$on = $this->wrapTable($foreign->on);
|
||||
|
||||
$columns = $this->columnize($foreign->columns);
|
||||
|
||||
$onColumns = $this->columnize((array) $foreign->references);
|
||||
|
||||
$sql .= ", constraint {$foreign->index} foreign key ( {$columns} ) references {$on} ( {$onColumns} )";
|
||||
|
||||
// Once we have the basic foreign key creation statement constructed we can
|
||||
// build out the syntax for what should happen on an update or delete of
|
||||
// the affected columns, which will get something like "cascade", etc.
|
||||
if (! is_null($foreign->onDelete)) {
|
||||
$sql .= " on delete {$foreign->onDelete}";
|
||||
}
|
||||
}
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the primary key syntax for a table creation statement.
|
||||
*
|
||||
* @param \Illuminate\Database\Schema\Blueprint $blueprint
|
||||
* @return string|null
|
||||
*/
|
||||
protected function addPrimaryKeys(Blueprint $blueprint)
|
||||
{
|
||||
$primary = $this->getCommandByName($blueprint, 'primary');
|
||||
|
||||
if (! is_null($primary)) {
|
||||
$columns = $this->columnize($primary->columns);
|
||||
|
||||
return ", constraint {$primary->index} primary key ( {$columns} )";
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile the query to determine if a table exists.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function compileTableExists()
|
||||
{
|
||||
return 'select * from all_tables where upper(owner) = upper(?) and upper(table_name) = upper(?)';
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile the query to determine the list of columns.
|
||||
*
|
||||
* @param string $database
|
||||
* @param string $table
|
||||
* @return string
|
||||
*/
|
||||
public function compileColumnExists($database, $table)
|
||||
{
|
||||
return "select column_name from all_tab_cols where upper(owner) = upper('{$database}') and upper(table_name) = upper('{$table}')";
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile an add column command.
|
||||
*
|
||||
* @param \Illuminate\Database\Schema\Blueprint $blueprint
|
||||
* @param \Illuminate\Support\Fluent $command
|
||||
* @return string
|
||||
*/
|
||||
public function compileAdd(Blueprint $blueprint, Fluent $command)
|
||||
{
|
||||
$columns = implode(', ', $this->getColumns($blueprint));
|
||||
|
||||
$sql = 'alter table '.$this->wrapTable($blueprint)." add ( $columns";
|
||||
|
||||
$sql .= (string) $this->addPrimaryKeys($blueprint);
|
||||
|
||||
return $sql .= ' )';
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile a primary key command.
|
||||
*
|
||||
* @param \Illuminate\Database\Schema\Blueprint $blueprint
|
||||
* @param \Illuminate\Support\Fluent $command
|
||||
* @return string
|
||||
*/
|
||||
public function compilePrimary(Blueprint $blueprint, Fluent $command)
|
||||
{
|
||||
$create = $this->getCommandByName($blueprint, 'create');
|
||||
|
||||
if (is_null($create)) {
|
||||
$columns = $this->columnize($command->columns);
|
||||
|
||||
$table = $this->wrapTable($blueprint);
|
||||
|
||||
return "alter table {$table} add constraint {$command->index} primary key ({$columns})";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile a foreign key command.
|
||||
*
|
||||
* @param \Illuminate\Database\Schema\Blueprint $blueprint
|
||||
* @param \Illuminate\Support\Fluent $command
|
||||
* @return string|void
|
||||
*/
|
||||
public function compileForeign(Blueprint $blueprint, Fluent $command)
|
||||
{
|
||||
$create = $this->getCommandByName($blueprint, 'create');
|
||||
|
||||
if (is_null($create)) {
|
||||
$table = $this->wrapTable($blueprint);
|
||||
|
||||
$on = $this->wrapTable($command->on);
|
||||
|
||||
// We need to prepare several of the elements of the foreign key definition
|
||||
// before we can create the SQL, such as wrapping the tables and convert
|
||||
// an array of columns to comma-delimited strings for the SQL queries.
|
||||
$columns = $this->columnize($command->columns);
|
||||
|
||||
$onColumns = $this->columnize((array) $command->references);
|
||||
|
||||
$sql = "alter table {$table} add constraint {$command->index} ";
|
||||
|
||||
$sql .= "foreign key ( {$columns} ) references {$on} ( {$onColumns} )";
|
||||
|
||||
// Once we have the basic foreign key creation statement constructed we can
|
||||
// build out the syntax for what should happen on an update or delete of
|
||||
// the affected columns, which will get something like "cascade", etc.
|
||||
if (! is_null($command->onDelete)) {
|
||||
$sql .= " on delete {$command->onDelete}";
|
||||
}
|
||||
|
||||
return $sql;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile a unique key command.
|
||||
*
|
||||
* @param \Illuminate\Database\Schema\Blueprint $blueprint
|
||||
* @param \Illuminate\Support\Fluent $command
|
||||
* @return string
|
||||
*/
|
||||
public function compileUnique(Blueprint $blueprint, Fluent $command)
|
||||
{
|
||||
return 'alter table '.$this->wrapTable($blueprint)." add constraint {$command->index} unique ( ".$this->columnize($command->columns).' )';
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile a plain index key command.
|
||||
*
|
||||
* @param \Illuminate\Database\Schema\Blueprint $blueprint
|
||||
* @param \Illuminate\Support\Fluent $command
|
||||
* @return string
|
||||
*/
|
||||
public function compileIndex(Blueprint $blueprint, Fluent $command)
|
||||
{
|
||||
return "create index {$command->index} on ".$this->wrapTable($blueprint).' ( '.$this->columnize($command->columns).' )';
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile a drop table command.
|
||||
*
|
||||
* @param \Illuminate\Database\Schema\Blueprint $blueprint
|
||||
* @param \Illuminate\Support\Fluent $command
|
||||
* @return string
|
||||
*/
|
||||
public function compileDrop(Blueprint $blueprint, Fluent $command)
|
||||
{
|
||||
return 'drop table '.$this->wrapTable($blueprint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile the SQL needed to drop all tables.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function compileDropAllTables()
|
||||
{
|
||||
return 'BEGIN
|
||||
FOR c IN (SELECT table_name FROM user_tables) LOOP
|
||||
EXECUTE IMMEDIATE (\'DROP TABLE "\' || c.table_name || \'" CASCADE CONSTRAINTS\');
|
||||
END LOOP;
|
||||
|
||||
FOR s IN (SELECT sequence_name FROM user_sequences) LOOP
|
||||
EXECUTE IMMEDIATE (\'DROP SEQUENCE \' || s.sequence_name);
|
||||
END LOOP;
|
||||
|
||||
END;';
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile a drop table (if exists) command.
|
||||
*
|
||||
* @param \Illuminate\Database\Schema\Blueprint $blueprint
|
||||
* @param \Illuminate\Support\Fluent $command
|
||||
* @return string
|
||||
*/
|
||||
public function compileDropIfExists(Blueprint $blueprint, Fluent $command)
|
||||
{
|
||||
$table = $this->wrapTable($blueprint);
|
||||
|
||||
return "declare c int;
|
||||
begin
|
||||
select count(*) into c from user_tables where table_name = upper('$table');
|
||||
if c = 1 then
|
||||
execute immediate 'drop table $table';
|
||||
end if;
|
||||
end;";
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile a drop column command.
|
||||
*
|
||||
* @param \Illuminate\Database\Schema\Blueprint $blueprint
|
||||
* @param \Illuminate\Support\Fluent $command
|
||||
* @return string
|
||||
*/
|
||||
public function compileDropColumn(Blueprint $blueprint, Fluent $command)
|
||||
{
|
||||
$columns = $this->wrapArray($command->columns);
|
||||
|
||||
$table = $this->wrapTable($blueprint);
|
||||
|
||||
return 'alter table '.$table.' drop ( '.implode(', ', $columns).' )';
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile a drop primary key command.
|
||||
*
|
||||
* @param \Illuminate\Database\Schema\Blueprint $blueprint
|
||||
* @param \Illuminate\Support\Fluent $command
|
||||
* @return string
|
||||
*/
|
||||
public function compileDropPrimary(Blueprint $blueprint, Fluent $command)
|
||||
{
|
||||
return $this->dropConstraint($blueprint, $command, 'primary');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Blueprint $blueprint
|
||||
* @param Fluent $command
|
||||
* @param string $type
|
||||
* @return string
|
||||
*/
|
||||
private function dropConstraint(Blueprint $blueprint, Fluent $command, $type)
|
||||
{
|
||||
$table = $this->wrapTable($blueprint);
|
||||
|
||||
$index = substr($command->index, 0, $this->getMaxLength());
|
||||
|
||||
if ($type === 'index') {
|
||||
return "drop index {$index}";
|
||||
}
|
||||
|
||||
return "alter table {$table} drop constraint {$index}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile a drop unique key command.
|
||||
*
|
||||
* @param \Illuminate\Database\Schema\Blueprint $blueprint
|
||||
* @param \Illuminate\Support\Fluent $command
|
||||
* @return string
|
||||
*/
|
||||
public function compileDropUnique(Blueprint $blueprint, Fluent $command)
|
||||
{
|
||||
return $this->dropConstraint($blueprint, $command, 'unique');
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile a drop index command.
|
||||
*
|
||||
* @param \Illuminate\Database\Schema\Blueprint $blueprint
|
||||
* @param \Illuminate\Support\Fluent $command
|
||||
* @return string
|
||||
*/
|
||||
public function compileDropIndex(Blueprint $blueprint, Fluent $command)
|
||||
{
|
||||
return $this->dropConstraint($blueprint, $command, 'index');
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile a drop foreign key command.
|
||||
*
|
||||
* @param \Illuminate\Database\Schema\Blueprint $blueprint
|
||||
* @param \Illuminate\Support\Fluent $command
|
||||
* @return string
|
||||
*/
|
||||
public function compileDropForeign(Blueprint $blueprint, Fluent $command)
|
||||
{
|
||||
return $this->dropConstraint($blueprint, $command, 'foreign');
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile a rename table command.
|
||||
*
|
||||
* @param \Illuminate\Database\Schema\Blueprint $blueprint
|
||||
* @param \Illuminate\Support\Fluent $command
|
||||
* @return string
|
||||
*/
|
||||
public function compileRename(Blueprint $blueprint, Fluent $command)
|
||||
{
|
||||
$from = $this->wrapTable($blueprint);
|
||||
|
||||
return "alter table {$from} rename to ".$this->wrapTable($command->to);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile a rename column command.
|
||||
*
|
||||
* @param \Illuminate\Database\Schema\Blueprint $blueprint
|
||||
* @param \Illuminate\Support\Fluent $command
|
||||
* @param \Illuminate\Database\Connection $connection
|
||||
* @return array
|
||||
*/
|
||||
public function compileRenameColumn(Blueprint $blueprint, Fluent $command, Connection $connection)
|
||||
{
|
||||
$table = $this->wrapTable($blueprint);
|
||||
|
||||
$rs = [];
|
||||
$rs[0] = 'alter table '.$table.' rename column '.$command->from.' to '.$command->to;
|
||||
|
||||
return (array) $rs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the column definition for a char type.
|
||||
*
|
||||
* @param \Illuminate\Support\Fluent $column
|
||||
* @return string
|
||||
*/
|
||||
protected function typeChar(Fluent $column)
|
||||
{
|
||||
return "char({$column->length})";
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the column definition for a string type.
|
||||
*
|
||||
* @param \Illuminate\Support\Fluent $column
|
||||
* @return string
|
||||
*/
|
||||
protected function typeString(Fluent $column)
|
||||
{
|
||||
return "varchar2({$column->length})";
|
||||
}
|
||||
|
||||
/**
|
||||
* Create column definition for a nvarchar type.
|
||||
*
|
||||
* @param \Illuminate\Support\Fluent $column
|
||||
* @return string
|
||||
*/
|
||||
protected function typeNvarchar2(Fluent $column)
|
||||
{
|
||||
return "nvarchar2({$column->length})";
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the column definition for a text type.
|
||||
*
|
||||
* @param \Illuminate\Support\Fluent $column
|
||||
* @return string
|
||||
*/
|
||||
protected function typeText(Fluent $column)
|
||||
{
|
||||
return 'clob';
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the column definition for a medium text type.
|
||||
*
|
||||
* @param \Illuminate\Support\Fluent $column
|
||||
* @return string
|
||||
*/
|
||||
protected function typeMediumText(Fluent $column)
|
||||
{
|
||||
return 'clob';
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the column definition for a long text type.
|
||||
*
|
||||
* @param \Illuminate\Support\Fluent $column
|
||||
* @return string
|
||||
*/
|
||||
protected function typeLongText(Fluent $column)
|
||||
{
|
||||
return 'clob';
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the column definition for a integer type.
|
||||
*
|
||||
* @param \Illuminate\Support\Fluent $column
|
||||
* @return string
|
||||
*/
|
||||
protected function typeInteger(Fluent $column)
|
||||
{
|
||||
$length = ($column->length) ? $column->length : 10;
|
||||
|
||||
return "number({$length},0)";
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the column definition for a integer type.
|
||||
*
|
||||
* @param \Illuminate\Support\Fluent $column
|
||||
* @return string
|
||||
*/
|
||||
protected function typeBigInteger(Fluent $column)
|
||||
{
|
||||
$length = ($column->length) ? $column->length : 19;
|
||||
|
||||
return "number({$length},0)";
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the column definition for a medium integer type.
|
||||
*
|
||||
* @param \Illuminate\Support\Fluent $column
|
||||
* @return string
|
||||
*/
|
||||
protected function typeMediumInteger(Fluent $column)
|
||||
{
|
||||
$length = ($column->length) ? $column->length : 7;
|
||||
|
||||
return "number({$length},0)";
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the column definition for a small integer type.
|
||||
*
|
||||
* @param \Illuminate\Support\Fluent $column
|
||||
* @return string
|
||||
*/
|
||||
protected function typeSmallInteger(Fluent $column)
|
||||
{
|
||||
$length = ($column->length) ? $column->length : 5;
|
||||
|
||||
return "number({$length},0)";
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the column definition for a tiny integer type.
|
||||
*
|
||||
* @param \Illuminate\Support\Fluent $column
|
||||
* @return string
|
||||
*/
|
||||
protected function typeTinyInteger(Fluent $column)
|
||||
{
|
||||
$length = ($column->length) ? $column->length : 3;
|
||||
|
||||
return "number({$length},0)";
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the column definition for a float type.
|
||||
*
|
||||
* @param \Illuminate\Support\Fluent $column
|
||||
* @return string
|
||||
*/
|
||||
protected function typeFloat(Fluent $column)
|
||||
{
|
||||
return "number({$column->total}, {$column->places})";
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the column definition for a double type.
|
||||
*
|
||||
* @param \Illuminate\Support\Fluent $column
|
||||
* @return string
|
||||
*/
|
||||
protected function typeDouble(Fluent $column)
|
||||
{
|
||||
return "number({$column->total}, {$column->places})";
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the column definition for a decimal type.
|
||||
*
|
||||
* @param \Illuminate\Support\Fluent $column
|
||||
* @return string
|
||||
*/
|
||||
protected function typeDecimal(Fluent $column)
|
||||
{
|
||||
return "number({$column->total}, {$column->places})";
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the column definition for a boolean type.
|
||||
*
|
||||
* @param \Illuminate\Support\Fluent $column
|
||||
* @return string
|
||||
*/
|
||||
protected function typeBoolean(Fluent $column)
|
||||
{
|
||||
return 'char(1)';
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the column definition for a enum type.
|
||||
*
|
||||
* @param \Illuminate\Support\Fluent $column
|
||||
* @return string
|
||||
*/
|
||||
protected function typeEnum(Fluent $column)
|
||||
{
|
||||
$length = ($column->length) ? $column->length : 255;
|
||||
|
||||
return "varchar2({$length})";
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the column definition for a date type.
|
||||
*
|
||||
* @param \Illuminate\Support\Fluent $column
|
||||
* @return string
|
||||
*/
|
||||
protected function typeDate(Fluent $column)
|
||||
{
|
||||
return 'date';
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the column definition for a date-time type.
|
||||
*
|
||||
* @param \Illuminate\Support\Fluent $column
|
||||
* @return string
|
||||
*/
|
||||
protected function typeDateTime(Fluent $column)
|
||||
{
|
||||
return 'date';
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the column definition for a time type.
|
||||
*
|
||||
* @param \Illuminate\Support\Fluent $column
|
||||
* @return string
|
||||
*/
|
||||
protected function typeTime(Fluent $column)
|
||||
{
|
||||
return 'date';
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the column definition for a timestamp type.
|
||||
*
|
||||
* @param \Illuminate\Support\Fluent $column
|
||||
* @return string
|
||||
*/
|
||||
protected function typeTimestamp(Fluent $column)
|
||||
{
|
||||
return 'timestamp';
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the column definition for a timestamp type with timezone.
|
||||
*
|
||||
* @param Fluent $column
|
||||
* @return string
|
||||
*/
|
||||
protected function typeTimestampTz(Fluent $column)
|
||||
{
|
||||
return 'timestamp with time zone';
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the column definition for a binary type.
|
||||
*
|
||||
* @param \Illuminate\Support\Fluent $column
|
||||
* @return string
|
||||
*/
|
||||
protected function typeBinary(Fluent $column)
|
||||
{
|
||||
return 'blob';
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the column definition for a uuid type.
|
||||
*
|
||||
* @param \Illuminate\Support\Fluent $column
|
||||
* @return string
|
||||
*/
|
||||
protected function typeUuid(Fluent $column)
|
||||
{
|
||||
return 'char(36)';
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the column definition for an IP address type.
|
||||
*
|
||||
* @param \Illuminate\Support\Fluent $column
|
||||
* @return string
|
||||
*/
|
||||
protected function typeIpAddress(Fluent $column)
|
||||
{
|
||||
return 'varchar(45)';
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the column definition for a MAC address type.
|
||||
*
|
||||
* @param \Illuminate\Support\Fluent $column
|
||||
* @return string
|
||||
*/
|
||||
protected function typeMacAddress(Fluent $column)
|
||||
{
|
||||
return 'varchar(17)';
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the column definition for a json type.
|
||||
*
|
||||
* @param \Illuminate\Support\Fluent $column
|
||||
* @return string
|
||||
*/
|
||||
protected function typeJson(Fluent $column)
|
||||
{
|
||||
return 'clob';
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the column definition for a jsonb type.
|
||||
*
|
||||
* @param \Illuminate\Support\Fluent $column
|
||||
* @return string
|
||||
*/
|
||||
protected function typeJsonb(Fluent $column)
|
||||
{
|
||||
return 'clob';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the SQL for a nullable column modifier.
|
||||
*
|
||||
* @param \Illuminate\Database\Schema\Blueprint $blueprint
|
||||
* @param \Illuminate\Support\Fluent $column
|
||||
* @return string
|
||||
*/
|
||||
protected function modifyNullable(Blueprint $blueprint, Fluent $column)
|
||||
{
|
||||
// check if field is declared as enum
|
||||
$enum = '';
|
||||
if (count((array) $column->allowed)) {
|
||||
$columnName = $this->wrapValue($column->name);
|
||||
$enum = " check ({$columnName} in ('".implode("', '", $column->allowed)."'))";
|
||||
}
|
||||
|
||||
$null = $column->nullable ? ' null' : ' not null';
|
||||
$null .= $enum;
|
||||
|
||||
if (! is_null($column->default)) {
|
||||
return ' default '.$this->getDefaultValue($column->default).$null;
|
||||
}
|
||||
|
||||
return $null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the SQL for a default column modifier.
|
||||
*
|
||||
* @param \Illuminate\Database\Schema\Blueprint $blueprint
|
||||
* @param \Illuminate\Support\Fluent $column
|
||||
* @return string
|
||||
*/
|
||||
protected function modifyDefault(Blueprint $blueprint, Fluent $column)
|
||||
{
|
||||
// implemented @modifyNullable
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the SQL for an auto-increment column modifier.
|
||||
*
|
||||
* @param \Illuminate\Database\Schema\Blueprint $blueprint
|
||||
* @param \Illuminate\Support\Fluent $column
|
||||
* @return string|null
|
||||
*/
|
||||
protected function modifyIncrement(Blueprint $blueprint, Fluent $column)
|
||||
{
|
||||
if (in_array($column->type, $this->serials) && $column->autoIncrement) {
|
||||
$blueprint->primary($column->name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap a single string in keyword identifiers.
|
||||
*
|
||||
* @param string $value
|
||||
* @return string
|
||||
*/
|
||||
protected function wrapValue($value)
|
||||
{
|
||||
if ($this->isReserved($value)) {
|
||||
return Str::upper(parent::wrapValue($value));
|
||||
}
|
||||
|
||||
return $value !== '*' ? sprintf($this->wrapper, $value) : $value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
<?php
|
||||
|
||||
namespace Yajra\Oci8\Schema;
|
||||
|
||||
use Illuminate\Database\Connection;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
|
||||
class OracleAutoIncrementHelper
|
||||
{
|
||||
/**
|
||||
* @var \Illuminate\Database\Connection
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* @var \Yajra\Oci8\Schema\Trigger
|
||||
*/
|
||||
protected $trigger;
|
||||
|
||||
/**
|
||||
* @var \Yajra\Oci8\Schema\Sequence
|
||||
*/
|
||||
protected $sequence;
|
||||
|
||||
/**
|
||||
* @param \Illuminate\Database\Connection $connection
|
||||
*/
|
||||
public function __construct(Connection $connection)
|
||||
{
|
||||
$this->connection = $connection;
|
||||
$this->sequence = new Sequence($connection);
|
||||
$this->trigger = new Trigger($connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* create sequence and trigger for autoIncrement support.
|
||||
*
|
||||
* @param Blueprint $blueprint
|
||||
* @param string $table
|
||||
* @return null
|
||||
*/
|
||||
public function createAutoIncrementObjects(Blueprint $blueprint, $table)
|
||||
{
|
||||
$column = $this->getQualifiedAutoIncrementColumn($blueprint);
|
||||
|
||||
// return if no qualified AI column
|
||||
if (is_null($column)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$col = $column->name;
|
||||
$start = isset($column->start) ? $column->start : 1;
|
||||
|
||||
// get table prefix
|
||||
$prefix = $this->connection->getTablePrefix();
|
||||
|
||||
// create sequence for auto increment
|
||||
$sequenceName = $this->createObjectName($prefix, $table, $col, 'seq');
|
||||
$this->sequence->create($sequenceName, $start, $column->nocache);
|
||||
|
||||
// create trigger for auto increment work around
|
||||
$triggerName = $this->createObjectName($prefix, $table, $col, 'trg');
|
||||
$this->trigger->autoIncrement($prefix.$table, $col, $triggerName, $sequenceName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get qualified autoincrement column.
|
||||
*
|
||||
* @param Blueprint $blueprint
|
||||
* @return \Illuminate\Support\Fluent|null
|
||||
*/
|
||||
public function getQualifiedAutoIncrementColumn(Blueprint $blueprint)
|
||||
{
|
||||
$columns = $blueprint->getColumns();
|
||||
|
||||
// search for primary key / autoIncrement column
|
||||
foreach ($columns as $column) {
|
||||
// if column is autoIncrement set the primary col name
|
||||
if ($column->autoIncrement) {
|
||||
return $column;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an object name that limits to 30 chars.
|
||||
*
|
||||
* @param string $prefix
|
||||
* @param string $table
|
||||
* @param string $col
|
||||
* @param string $type
|
||||
* @return string
|
||||
*/
|
||||
private function createObjectName($prefix, $table, $col, $type)
|
||||
{
|
||||
// max object name length is 30 chars
|
||||
$max_length = $this->connection->getSchemaGrammar()->getMaxLength();
|
||||
|
||||
return substr($prefix.$table.'_'.$col.'_'.$type, 0, $max_length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop sequence and triggers if exists, autoincrement objects.
|
||||
*
|
||||
* @param string $table
|
||||
* @return null
|
||||
*/
|
||||
public function dropAutoIncrementObjects($table)
|
||||
{
|
||||
// drop sequence and trigger object
|
||||
$prefix = $this->connection->getTablePrefix();
|
||||
// get the actual primary column name from table
|
||||
$col = $this->getPrimaryKey($prefix.$table);
|
||||
// if primary key col is set, drop auto increment objects
|
||||
if (isset($col) && ! empty($col)) {
|
||||
// drop sequence for auto increment
|
||||
$sequenceName = $this->createObjectName($prefix, $table, $col, 'seq');
|
||||
$this->sequence->drop($sequenceName);
|
||||
|
||||
// drop trigger for auto increment work around
|
||||
$triggerName = $this->createObjectName($prefix, $table, $col, 'trg');
|
||||
$this->trigger->drop($triggerName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get table's primary key.
|
||||
*
|
||||
* @param string $table
|
||||
* @return string
|
||||
*/
|
||||
public function getPrimaryKey($table)
|
||||
{
|
||||
if (! $table) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$sql = "SELECT cols.column_name
|
||||
FROM all_constraints cons, all_cons_columns cols
|
||||
WHERE upper(cols.table_name) = upper('{$table}')
|
||||
AND cons.constraint_type = 'P'
|
||||
AND cons.constraint_name = cols.constraint_name
|
||||
AND cons.owner = cols.owner
|
||||
AND cols.position = 1
|
||||
AND cons.owner = (select user from dual)
|
||||
ORDER BY cols.table_name, cols.position";
|
||||
$data = $this->connection->selectOne($sql);
|
||||
|
||||
if ($data) {
|
||||
return $data->column_name;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get sequence instance.
|
||||
*
|
||||
* @return Sequence
|
||||
*/
|
||||
public function getSequence()
|
||||
{
|
||||
return $this->sequence;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set sequence instance.
|
||||
*
|
||||
* @param Sequence $sequence
|
||||
*/
|
||||
public function setSequence($sequence)
|
||||
{
|
||||
$this->sequence = $sequence;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get trigger instance.
|
||||
*
|
||||
* @return Trigger
|
||||
*/
|
||||
public function getTrigger()
|
||||
{
|
||||
return $this->trigger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the trigger instance.
|
||||
*
|
||||
* @param Trigger $trigger
|
||||
*/
|
||||
public function setTrigger($trigger)
|
||||
{
|
||||
$this->trigger = $trigger;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
namespace Yajra\Oci8\Schema;
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
|
||||
class OracleBlueprint extends Blueprint
|
||||
{
|
||||
/**
|
||||
* Table comment.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $comment = null;
|
||||
|
||||
/**
|
||||
* Column comments.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $commentColumns = [];
|
||||
|
||||
/**
|
||||
* Database prefix variable.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $prefix;
|
||||
|
||||
/**
|
||||
* Database table max_length variable.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $max_length = 30;
|
||||
|
||||
/**
|
||||
* Set table prefix settings.
|
||||
*
|
||||
* @param string $prefix
|
||||
*/
|
||||
public function setTablePrefix($prefix = '')
|
||||
{
|
||||
$this->prefix = $prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set index/table max length name settings.
|
||||
*
|
||||
* @param int $maxLength
|
||||
*/
|
||||
public function setMaxLength($maxLength = 30)
|
||||
{
|
||||
$this->max_length = $maxLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a default index name for the table.
|
||||
*
|
||||
* @param string $type
|
||||
* @param array $columns
|
||||
* @return string
|
||||
*/
|
||||
protected function createIndexName($type, array $columns)
|
||||
{
|
||||
$short_type = [
|
||||
'primary' => 'pk',
|
||||
'foreign' => 'fk',
|
||||
'unique' => 'uk',
|
||||
];
|
||||
|
||||
$type = isset($short_type[$type]) ? $short_type[$type] : $type;
|
||||
|
||||
$index = strtolower($this->prefix.$this->table.'_'.implode('_', $columns).'_'.$type);
|
||||
|
||||
$index = str_replace(['-', '.'], '_', $index);
|
||||
while (strlen($index) > $this->max_length) {
|
||||
$parts = explode('_', $index);
|
||||
|
||||
for ($i = 0; $i < count($parts); $i++) {
|
||||
//if any part is longer than 2 chars, take one off
|
||||
$len = strlen($parts[$i]);
|
||||
if ($len > 2) {
|
||||
$parts[$i] = substr($parts[$i], 0, $len - 1);
|
||||
}
|
||||
}
|
||||
|
||||
$index = implode('_', $parts);
|
||||
}
|
||||
|
||||
return $index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new nvarchar2 column on the table.
|
||||
*
|
||||
* @param string $column
|
||||
* @param int $length
|
||||
* @return \Illuminate\Support\Fluent
|
||||
*/
|
||||
public function nvarchar2($column, $length = 255)
|
||||
{
|
||||
return $this->addColumn('nvarchar2', $column, compact('length'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
<?php
|
||||
|
||||
namespace Yajra\Oci8\Schema;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Database\Connection;
|
||||
use Illuminate\Database\Schema\Builder;
|
||||
|
||||
class OracleBuilder extends Builder
|
||||
{
|
||||
/**
|
||||
* @var \Yajra\Oci8\Schema\OracleAutoIncrementHelper
|
||||
*/
|
||||
public $helper;
|
||||
|
||||
/**
|
||||
* @var \Yajra\Oci8\Schema\Comment
|
||||
*/
|
||||
public $comment;
|
||||
|
||||
/**
|
||||
* @param Connection $connection
|
||||
*/
|
||||
public function __construct(Connection $connection)
|
||||
{
|
||||
parent::__construct($connection);
|
||||
$this->helper = new OracleAutoIncrementHelper($connection);
|
||||
$this->comment = new Comment($connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new table on the schema.
|
||||
*
|
||||
* @param string $table
|
||||
* @param Closure $callback
|
||||
* @return \Illuminate\Database\Schema\Blueprint
|
||||
*/
|
||||
public function create($table, Closure $callback)
|
||||
{
|
||||
$blueprint = $this->createBlueprint($table);
|
||||
|
||||
$blueprint->create();
|
||||
|
||||
$callback($blueprint);
|
||||
|
||||
$this->build($blueprint);
|
||||
|
||||
$this->comment->setComments($blueprint);
|
||||
|
||||
$this->helper->createAutoIncrementObjects($blueprint, $table);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new command set with a Closure.
|
||||
*
|
||||
* @param string $table
|
||||
* @param Closure $callback
|
||||
* @return \Illuminate\Database\Schema\Blueprint
|
||||
*/
|
||||
protected function createBlueprint($table, Closure $callback = null)
|
||||
{
|
||||
$blueprint = new OracleBlueprint($table, $callback);
|
||||
$blueprint->setTablePrefix($this->connection->getTablePrefix());
|
||||
$blueprint->setMaxLength($this->grammar->getMaxLength());
|
||||
|
||||
return $blueprint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes an existing table on the schema.
|
||||
*
|
||||
* @param string $table
|
||||
* @param Closure $callback
|
||||
* @return \Illuminate\Database\Schema\Blueprint
|
||||
*/
|
||||
public function table($table, Closure $callback)
|
||||
{
|
||||
$blueprint = $this->createBlueprint($table);
|
||||
|
||||
$callback($blueprint);
|
||||
|
||||
foreach ($blueprint->getCommands() as $command) {
|
||||
if ($command->get('name') == 'drop') {
|
||||
$this->helper->dropAutoIncrementObjects($table);
|
||||
}
|
||||
}
|
||||
|
||||
$this->build($blueprint);
|
||||
|
||||
$this->comment->setComments($blueprint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop a table from the schema.
|
||||
*
|
||||
* @param string $table
|
||||
* @return \Illuminate\Database\Schema\Blueprint
|
||||
*/
|
||||
public function drop($table)
|
||||
{
|
||||
$this->helper->dropAutoIncrementObjects($table);
|
||||
parent::drop($table);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop all tables from the database.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function dropAllTables()
|
||||
{
|
||||
$this->connection->statement($this->grammar->compileDropAllTables());
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the table should be dropped if it exists.
|
||||
*
|
||||
* @param string $table
|
||||
* @return \Illuminate\Support\Fluent
|
||||
*/
|
||||
public function dropIfExists($table)
|
||||
{
|
||||
$this->helper->dropAutoIncrementObjects($table);
|
||||
parent::dropIfExists($table);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given table exists.
|
||||
*
|
||||
* @param string $table
|
||||
* @return bool
|
||||
*/
|
||||
public function hasTable($table)
|
||||
{
|
||||
/** @var \Yajra\Oci8\Schema\Grammars\OracleGrammar $grammar */
|
||||
$grammar = $this->grammar;
|
||||
$sql = $grammar->compileTableExists();
|
||||
|
||||
$database = $this->connection->getConfig('username');
|
||||
if ($this->connection->getConfig('prefix_schema')) {
|
||||
$database = $this->connection->getConfig('prefix_schema');
|
||||
}
|
||||
$table = $this->connection->getTablePrefix().$table;
|
||||
|
||||
return count($this->connection->select($sql, [$database, $table])) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the column listing for a given table.
|
||||
*
|
||||
* @param string $table
|
||||
* @return array
|
||||
*/
|
||||
public function getColumnListing($table)
|
||||
{
|
||||
$database = $this->connection->getConfig('username');
|
||||
$table = $this->connection->getTablePrefix().$table;
|
||||
/** @var \Yajra\Oci8\Schema\Grammars\OracleGrammar $grammar */
|
||||
$grammar = $this->grammar;
|
||||
$results = $this->connection->select($grammar->compileColumnExists($database, $table));
|
||||
|
||||
return $this->connection->getPostProcessor()->processColumnListing($results);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
<?php
|
||||
|
||||
namespace Yajra\Oci8\Schema;
|
||||
|
||||
use Illuminate\Database\Connection;
|
||||
|
||||
class Sequence
|
||||
{
|
||||
/**
|
||||
* @var \Illuminate\Database\Connection|\Yajra\Oci8\Oci8Connection
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* @param Connection $connection
|
||||
*/
|
||||
public function __construct(Connection $connection)
|
||||
{
|
||||
$this->connection = $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* function to create oracle sequence.
|
||||
*
|
||||
* @param string $name
|
||||
* @param int $start
|
||||
* @param bool $nocache
|
||||
* @param int $min
|
||||
* @param bool $max
|
||||
* @param int $increment
|
||||
* @return bool
|
||||
*/
|
||||
public function create($name, $start = 1, $nocache = false, $min = 1, $max = false, $increment = 1)
|
||||
{
|
||||
if (! $name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$name = $this->wrap($name);
|
||||
|
||||
$nocache = $nocache ? 'nocache' : '';
|
||||
|
||||
$max = $max ? " maxvalue {$max}" : '';
|
||||
|
||||
$sequence_stmt = "create sequence {$name} minvalue {$min} {$max} start with {$start} increment by {$increment} {$nocache}";
|
||||
|
||||
return $this->connection->statement($sequence_stmt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap sequence name with schema prefix.
|
||||
*
|
||||
* @param string $name
|
||||
* @return string
|
||||
*/
|
||||
public function wrap($name)
|
||||
{
|
||||
if ($this->connection->getConfig('prefix_schema')) {
|
||||
return $this->connection->getConfig('prefix_schema').'.'.$name;
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* function to safely drop sequence db object.
|
||||
*
|
||||
* @param string $name
|
||||
* @return bool
|
||||
*/
|
||||
public function drop($name)
|
||||
{
|
||||
// check if a valid name and sequence exists
|
||||
if (! $name || ! $this->exists($name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$name = $this->wrap($name);
|
||||
|
||||
return $this->connection->statement("
|
||||
declare
|
||||
e exception;
|
||||
pragma exception_init(e,-02289);
|
||||
begin
|
||||
execute immediate 'drop sequence {$name}';
|
||||
exception
|
||||
when e then
|
||||
null;
|
||||
end;");
|
||||
}
|
||||
|
||||
/**
|
||||
* function to check if sequence exists.
|
||||
*
|
||||
* @param string $name
|
||||
* @return bool
|
||||
*/
|
||||
public function exists($name)
|
||||
{
|
||||
if (! $name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$name = $this->wrap($name);
|
||||
|
||||
return $this->connection->selectOne(
|
||||
"select * from all_sequences where sequence_name=upper('{$name}') and sequence_owner=upper(user)"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* get sequence next value.
|
||||
*
|
||||
* @param string $name
|
||||
* @return int
|
||||
*/
|
||||
public function nextValue($name)
|
||||
{
|
||||
if (! $name) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$name = $this->wrap($name);
|
||||
|
||||
return $this->connection->selectOne("SELECT $name.NEXTVAL as \"id\" FROM DUAL")->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* same function as lastInsertId. added for clarity with oracle sql statement.
|
||||
*
|
||||
* @param string $name
|
||||
* @return int
|
||||
*/
|
||||
public function currentValue($name)
|
||||
{
|
||||
return $this->lastInsertId($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* function to get oracle sequence last inserted id.
|
||||
*
|
||||
* @param string $name
|
||||
* @return int
|
||||
*/
|
||||
public function lastInsertId($name)
|
||||
{
|
||||
// check if a valid name and sequence exists
|
||||
if (! $name || ! $this->exists($name)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$name = $this->wrap($name);
|
||||
|
||||
return $this->connection->selectOne("select {$name}.currval as \"id\" from dual")->id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
namespace Yajra\Oci8\Schema;
|
||||
|
||||
use Illuminate\Database\Connection;
|
||||
use Illuminate\Support\Str;
|
||||
use Yajra\Oci8\OracleReservedWords;
|
||||
|
||||
class Trigger
|
||||
{
|
||||
use OracleReservedWords;
|
||||
|
||||
/**
|
||||
* @var \Illuminate\Database\Connection|\Yajra\Oci8\Oci8Connection
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* @param Connection $connection
|
||||
*/
|
||||
public function __construct(Connection $connection)
|
||||
{
|
||||
$this->connection = $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to create auto increment trigger for a table.
|
||||
*
|
||||
* @param string $table
|
||||
* @param string $column
|
||||
* @param string $triggerName
|
||||
* @param string $sequenceName
|
||||
* @return bool
|
||||
*/
|
||||
public function autoIncrement($table, $column, $triggerName, $sequenceName)
|
||||
{
|
||||
if (! $table || ! $column || ! $triggerName || ! $sequenceName) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->connection->getConfig('prefix_schema')) {
|
||||
$table = $this->connection->getConfig('prefix_schema').'.'.$table;
|
||||
$triggerName = $this->connection->getConfig('prefix_schema').'.'.$triggerName;
|
||||
$sequenceName = $this->connection->getConfig('prefix_schema').'.'.$sequenceName;
|
||||
}
|
||||
|
||||
$table = $this->wrapValue($table);
|
||||
$column = $this->wrapValue($column);
|
||||
|
||||
return $this->connection->statement("
|
||||
create trigger $triggerName
|
||||
before insert on {$table}
|
||||
for each row
|
||||
begin
|
||||
if :new.{$column} is null then
|
||||
select {$sequenceName}.nextval into :new.{$column} from dual;
|
||||
end if;
|
||||
end;");
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap value if reserved word.
|
||||
*
|
||||
* @param string $value
|
||||
* @return string
|
||||
*/
|
||||
protected function wrapValue($value)
|
||||
{
|
||||
$value = Str::upper($value);
|
||||
|
||||
return $this->isReserved($value) ? '"'.$value.'"' : $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to safely drop trigger db object.
|
||||
*
|
||||
* @param string $name
|
||||
* @return bool
|
||||
*/
|
||||
public function drop($name)
|
||||
{
|
||||
if (! $name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->connection->statement("declare
|
||||
e exception;
|
||||
pragma exception_init(e,-4080);
|
||||
begin
|
||||
execute immediate 'drop trigger {$name}';
|
||||
exception
|
||||
when e then
|
||||
null;
|
||||
end;");
|
||||
}
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace Yajra\Oci8\Validation;
|
||||
|
||||
use Illuminate\Validation\DatabasePresenceVerifier;
|
||||
use Yajra\Oci8\Oci8Connection;
|
||||
|
||||
class Oci8DatabasePresenceVerifier extends DatabasePresenceVerifier
|
||||
{
|
||||
/**
|
||||
* Count the number of objects in a collection having the given value.
|
||||
*
|
||||
* @param string $collection
|
||||
* @param string $column
|
||||
* @param string $value
|
||||
* @param int|null $excludeId
|
||||
* @param string|null $idColumn
|
||||
* @param array $extra
|
||||
* @return int
|
||||
*/
|
||||
public function getCount($collection, $column, $value, $excludeId = null, $idColumn = null, array $extra = [])
|
||||
{
|
||||
$connection = $this->table($collection)->getConnection();
|
||||
|
||||
if (! $connection instanceof Oci8Connection) {
|
||||
return parent::getCount($collection, $column, $value, $excludeId, $idColumn, $extra);
|
||||
}
|
||||
|
||||
$connection->useCaseInsensitiveSession();
|
||||
$count = parent::getCount($collection, $column, $value, $excludeId, $idColumn, $extra);
|
||||
$connection->useCaseSensitiveSession();
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count the number of objects in a collection with the given values.
|
||||
*
|
||||
* @param string $collection
|
||||
* @param string $column
|
||||
* @param array $values
|
||||
* @param array $extra
|
||||
* @return int
|
||||
*/
|
||||
public function getMultiCount($collection, $column, array $values, array $extra = [])
|
||||
{
|
||||
$connection = $this->table($collection)->getConnection();
|
||||
|
||||
if (! $connection instanceof Oci8Connection) {
|
||||
return parent::getMultiCount($collection, $column, $values, $extra);
|
||||
}
|
||||
|
||||
$connection->useCaseInsensitiveSession();
|
||||
$count = parent::getMultiCount($collection, $column, $values, $extra);
|
||||
$connection->useCaseSensitiveSession();
|
||||
|
||||
return $count;
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'oracle' => [
|
||||
'driver' => 'oracle',
|
||||
'tns' => env('DB_TNS', ''),
|
||||
'host' => env('DB_HOST', ''),
|
||||
'port' => env('DB_PORT', '1521'),
|
||||
'database' => env('DB_DATABASE', ''),
|
||||
'service_name' => env('DB_SERVICENAME', ''),
|
||||
'username' => env('DB_USERNAME', ''),
|
||||
'password' => env('DB_PASSWORD', ''),
|
||||
'charset' => env('DB_CHARSET', 'AL32UTF8'),
|
||||
'prefix' => env('DB_PREFIX', ''),
|
||||
'prefix_schema' => env('DB_SCHEMA_PREFIX', ''),
|
||||
'edition' => env('DB_EDITION', 'ora$base'),
|
||||
'server_version' => env('DB_SERVER_VERSION', '11g'),
|
||||
'load_balance' => env('DB_LOAD_BALANCE', 'yes'),
|
||||
'max_name_len' => env('ORA_MAX_NAME_LEN', 30),
|
||||
'dynamic' => [],
|
||||
],
|
||||
];
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
if (! function_exists('config_path')) {
|
||||
/**
|
||||
* Get the configuration path.
|
||||
*
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
function config_path($path = '')
|
||||
{
|
||||
return app()->basePath().'/config'.($path ? '/'.$path : $path);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
preset: laravel
|
||||
|
||||
enabled:
|
||||
- align_double_arrow
|
||||
- align_equals
|
||||
|
||||
disabled:
|
||||
- concat_without_spaces
|
||||
- unalign_equals
|
||||
+171
@@ -0,0 +1,171 @@
|
||||
# PDO-VIA-OCI8 Changelog
|
||||
|
||||
## v2.4.0 - 2022-02-09
|
||||
|
||||
- Add method to set client identifier. #101
|
||||
- Fix yajra/laravel-oci8#453
|
||||
- Fix #99
|
||||
|
||||
## v2.3.0 - 2022-01-31
|
||||
|
||||
- Fix #96, add method to get db resource. #97
|
||||
|
||||
## v2.2.0 - 2020-12-11
|
||||
|
||||
- Added support for oci_set_call_timeout function [#85](https://github.com/yajra/pdo-via-oci8/pull/85), credits to @ognjenVlad.
|
||||
- Fix [yajra/laravel-oci8#621](https://github.com/yajra/laravel-oci8/issues/621).
|
||||
|
||||
## v2.1.3 - 2020-10-20
|
||||
|
||||
- Return ora code on failed connection. [#78](https://github.com/yajra/pdo-via-oci8/pull/78), credits to @bendzaminas.
|
||||
|
||||
## v2.1.2 - 2020-10-20
|
||||
|
||||
- Fix PDO::ATTR_PERSISTENT when is set to false [#80](https://github.com/yajra/pdo-via-oci8/pull/80), credits to @cvillegaslobos.
|
||||
|
||||
## v2.1.1 - 2019-12-05
|
||||
|
||||
- Fix SQLT_NTY collection binding. [#74](https://github.com/yajra/pdo-via-oci8/pull/74), credits to @barrigapicante.
|
||||
- Fix PDO::FETCH_CLASS without PDO::FETCH_PROPS_LATE. [#75](https://github.com/yajra/pdo-via-oci8/pull/75), credits to @vlaaadislav.
|
||||
|
||||
## v2.1.0 - 2019-11-16
|
||||
|
||||
- Added support for PDO-style connection strings [#72](https://github.com/yajra/pdo-via-oci8/pull/72), credits to @wernerm.
|
||||
|
||||
## v2.0.0 - 2019-09-04
|
||||
|
||||
- Fix [#64](https://github.com/yajra/pdo-via-oci8/issues/64) for single bind params [#67](https://github.com/yajra/pdo-via-oci8/pull/67), credits to @istaveren.
|
||||
|
||||
## v1.4.0 - 2019-05-09
|
||||
|
||||
- Fix [#63](https://github.com/yajra/pdo-via-oci8/issues/63) query params [#62](https://github.com/yajra/pdo-via-oci8/pull/62). Credits to @istaveren.
|
||||
- Add call to oci_close on destruct of the object. [#7f10323](https://github.com/yajra/pdo-via-oci8/commit/7f103234e3b47402588010838e181aa5dcd08add)
|
||||
- Update docker container to use for TravisCI [#65](https://github.com/yajra/pdo-via-oci8/pull/65).
|
||||
- Add tests.
|
||||
|
||||
|
||||
## v1.3.8 - 2019-04-24
|
||||
|
||||
- Added support for oci session_mode (wallet) [#61](https://github.com/yajra/pdo-via-oci8/pull/61), credits to @Blizzke.
|
||||
|
||||
## v1.3.7 - 2017-11-28
|
||||
|
||||
- Fix for large param for blob and clob. [#56](https://github.com/yajra/pdo-via-oci8/pull/56)
|
||||
- Fix fetch mode usage and use `PDO::FETCH_BOTH` as default. [#59](https://github.com/yajra/pdo-via-oci8/pull/59)
|
||||
- Init ci testing. [#58](https://github.com/yajra/pdo-via-oci8/pull/58)
|
||||
|
||||
## v1.3.6 - 2017-11-28
|
||||
|
||||
- Revert "Changed to LONG type because number type can not be registered normally" [#55](https://github.com/yajra/pdo-via-oci8/pull/55)
|
||||
- Fix [yajra/laravel-oci8#386](https://github.com/yajra/laravel-oci8/issues/386).
|
||||
- Fix failing unit tests Failed asserting that '40' is identical to 40.
|
||||
|
||||
## v1.3.5 - 2017-11-28
|
||||
|
||||
- Do not quote numeric values. [#51](https://github.com/yajra/pdo-via-oci8/pull/51)
|
||||
- Fix changelog markdown lint.
|
||||
|
||||
## v1.3.4 - 2017-11-16
|
||||
|
||||
- Changed to LONG type because number type can not be registered normally [#42](https://github.com/yajra/pdo-via-oci8/pull/49) credits to @reo0306.
|
||||
|
||||
## v1.3.3 - 2017-11-16
|
||||
|
||||
- Handle ROWID output exception. [#49](https://github.com/yajra/pdo-via-oci8/pull/49)
|
||||
|
||||
## v1.3.2 - 2017-06-24
|
||||
|
||||
- Fixes for Array bindings.
|
||||
- PR [#43](https://github.com/yajra/pdo-via-oci8/pull/43) credits to @shannonfairchild.
|
||||
|
||||
## v1.3.1 - 2017-02-02
|
||||
|
||||
- Fix fetchAll fetchMode parameter and set to null by default.
|
||||
- Fix [#246](https://github.com/yajra/laravel-oci8/issues/246).
|
||||
|
||||
## v1.3.0 - 2017-01-05
|
||||
|
||||
- Add .gitattributes and config files.
|
||||
- Add code of conduct.
|
||||
- Add change log.
|
||||
- Update license to 2017.
|
||||
- Add github templates.
|
||||
|
||||
## v1.2.1
|
||||
|
||||
- Change regex replacement pattern in prepare. PR #38, credits to @jjware.
|
||||
- Fix #37.
|
||||
- Refactor regex replacement using preg_replace_callback.
|
||||
|
||||
## v1.2.0
|
||||
|
||||
- Implement closeCursor.
|
||||
- Add oci8 connection options getter.
|
||||
|
||||
## v1.1.1
|
||||
|
||||
- 3rd argument in Statement::fetchAll(), $ctorArgs, should allow null.
|
||||
- PR #34, credits to @nhowell.
|
||||
|
||||
## v1.1.0
|
||||
|
||||
- Add support for CLOB data types.
|
||||
- PR #31, credits to @Tylerian.
|
||||
|
||||
## v1.0.5
|
||||
|
||||
- fetchAll also checks if fetch() returning object.
|
||||
- PR #30, credits to @apit
|
||||
|
||||
## v1.0.4
|
||||
|
||||
- Fix non-FETCH_COLUMN queries.
|
||||
- PR #29, credits to @snelg
|
||||
|
||||
## v1.0.3
|
||||
|
||||
- Function fetchAll() bug fix. Fix #27
|
||||
- PR #28, credits to @dkochnov.
|
||||
|
||||
## v1.0.2
|
||||
|
||||
- Parse DSN - check charset option in connection string and clean "PDO style".
|
||||
- PR #26, credits to @eisberg.
|
||||
|
||||
## v1.0.1
|
||||
|
||||
- PHP 7 fix for bindValue. PR #23 by @snelg.
|
||||
|
||||
## v1.0.0
|
||||
|
||||
- Change of namespace from yajra to Yajra (capital Y).
|
||||
- Enhance error dump with proper bindings.
|
||||
- Convert to PSR-2 & PSR-4 standard.
|
||||
- Safe refactoring of variable names.
|
||||
- Auto-saving of BLOB objects.
|
||||
|
||||
## v0.15.0
|
||||
|
||||
- Account for PDO::PARAM_INPUT_OUTPUT in bindParam.
|
||||
|
||||
## v0.14.0
|
||||
|
||||
- Add bindArray special non-pdo function for extensive support for oci_bind_array_by_name.
|
||||
|
||||
## v0.13.0
|
||||
|
||||
- Add support for oci_bind_array_by_name.
|
||||
|
||||
## v0.12.0
|
||||
|
||||
- Add support for procedure returning a cursor.
|
||||
|
||||
## v0.11.0
|
||||
|
||||
- Rename github package name to pdo-via-oci8 from laravel-pdo-via-oci8 making the package not specific to Laravel.
|
||||
- Sequence name can now be passed in the `lastInsertId` function. Table name that will be used by default will be based on the last table used on insert query.
|
||||
|
||||
```php
|
||||
$pdo->lastInsertId(); // will use TABLE_ID_SEQ
|
||||
$pdo->lastInsertId('TABLE_SEQ'); // will use TABLE_SEQ
|
||||
```
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
# Contributor Code of Conduct
|
||||
|
||||
As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
|
||||
|
||||
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, or nationality.
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery
|
||||
* Personal attacks
|
||||
* Trolling or insulting/derogatory comments
|
||||
* Public or private harassment
|
||||
* Publishing other's private information, such as physical or electronic addresses, without explicit permission
|
||||
* Other unethical or unprofessional conduct.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. By adopting this Code of Conduct, project maintainers commit themselves to fairly and consistently applying these principles to every aspect of managing this project. Project maintainers who do not follow or enforce the Code of Conduct may be permanently removed from the project team.
|
||||
|
||||
This code of conduct applies both within project spaces and in public spaces when an individual is representing the project or its community in a direct capacity. Personal views, beliefs and values of individuals do not necessarily reflect those of the organisation or affiliated individuals and organisations.
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.2.0, available at [http://contributor-covenant.org/version/1/2/0/](http://contributor-covenant.org/version/1/2/0/)
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2013-2019 Arjay Angeles <aqangeles@gmail.com>,
|
||||
Oracle Corporation
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
'Software'), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "yajra/laravel-pdo-via-oci8",
|
||||
"description": "PDO userspace driver proxying calls to PHP OCI8 driver",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Arjay Angeles",
|
||||
"email": "aqangeles@gmail.com"
|
||||
}
|
||||
],
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^6.4"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Yajra\\": "src/"
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.0-dev"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"docker": "docker run -d -p 49160:22 -p 49161:1521 deepdiver/docker-oracle-xe-11g"
|
||||
},
|
||||
"minimum-stability": "stable"
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# install deps
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get -y install -qq build-essential unzip wget libaio1
|
||||
|
||||
# install oci8 libs & extension
|
||||
sudo mkdir -p /opt/oracle
|
||||
|
||||
wget https://github.com/bumpx/oracle-instantclient/raw/master/instantclient-basic-linux.x64-12.1.0.2.0.zip
|
||||
wget https://github.com/bumpx/oracle-instantclient/raw/master/instantclient-sdk-linux.x64-12.1.0.2.0.zip
|
||||
|
||||
sudo unzip -o ./instantclient-basic-linux.x64-12.1.0.2.0.zip -d /opt/oracle
|
||||
sudo unzip -o ./instantclient-sdk-linux.x64-12.1.0.2.0.zip -d /opt/oracle
|
||||
|
||||
sudo ln -s /opt/oracle/instantclient/sqlplus /usr/bin/sqlplus
|
||||
sudo ln -s /opt/oracle/instantclient_12_1 /opt/oracle/instantclient
|
||||
sudo ln -s /opt/oracle/instantclient/libclntsh.so.12.1 /opt/oracle/instantclient/libclntsh.so
|
||||
sudo ln -s /opt/oracle/instantclient/libocci.so.12.1 /opt/oracle/instantclient/libocci.so
|
||||
|
||||
sudo pecl channel-update pecl.php.net
|
||||
|
||||
echo 'instantclient,/opt/oracle/instantclient' | pecl install oci8
|
||||
|
||||
# run oracle db via docker
|
||||
docker run -d -p 49160:22 -p 49161:1521 deepdiver/docker-oracle-xe-11g
|
||||
|
||||
# wait for start
|
||||
sleep 40
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
# Oracle PDO Userspace Driver for OCI8
|
||||
|
||||
## PDO via Oci8
|
||||
|
||||
[](https://travis-ci.org/yajra/pdo-via-oci8)
|
||||
[](https://packagist.org/packages/yajra/laravel-pdo-via-oci8)
|
||||
[](https://packagist.org/packages/yajra/laravel-pdo-via-oci8)
|
||||
[](https://packagist.org/packages/yajra/laravel-pdo-via-oci8)
|
||||
[](LICENSE.md)
|
||||
|
||||
|
||||
The [yajra/pdo-via-oci8](https://github.com/yajra/pdo-via-oci8) package is a simple userspace driver for PDO that uses the tried and
|
||||
tested [OCI8](http://php.net/oci8) functions instead of using the still experimental and not all that functionnal
|
||||
[PDO_OCI](http://www.php.net/manual/en/ref.pdo-oci.php) library.
|
||||
|
||||
**Please report any bugs you may find.**
|
||||
|
||||
- [Installation](#installation)
|
||||
- [Credits](#credits)
|
||||
|
||||
## Installation
|
||||
|
||||
Add `yajra/laravel-pdo-via-oci8` as a requirement to composer.json:
|
||||
|
||||
```json
|
||||
{
|
||||
"require": {
|
||||
"yajra/laravel-pdo-via-oci8": "2.*"
|
||||
}
|
||||
}
|
||||
```
|
||||
And then run `composer update`
|
||||
|
||||
## Testing
|
||||
|
||||
There is a test suite (using `PHPUnit` with a version bigger than 6.x) on the `test` directory. If you want to
|
||||
test (you must test your code!), create a table called `people` with two
|
||||
columns:
|
||||
|
||||
1. `name` as `varchar2(50)`
|
||||
2. `email` as `varchar2(30)`
|
||||
|
||||
And some environment variables:
|
||||
|
||||
1. `OCI_USER` with the database user name
|
||||
2. `OCI_PWD` with the database password
|
||||
3. `OCI_STR` with the database connection string
|
||||
|
||||
And then go to the `test` dir and run `PHPUnit` like:
|
||||
|
||||
```
|
||||
phpunit --colors .
|
||||
```
|
||||
Examle to get it up and running on docker DB container-registry.oracle.com/database/enterprise:12.2.0.1
|
||||
|
||||
create pluggable database testpdb admin user oracle identified by system file_name_convert = ('/pdbseed/', '/testpdb01/');
|
||||
alter pluggable database testpdb open;
|
||||
|
||||
ALTER SESSION SET CONTAINER=testpdb;
|
||||
|
||||
CREATE TABLE person (name NVARCHAR2(50), email NVARCHAR2(30));
|
||||
|
||||
## Buy me a coffee
|
||||
[](https://www.paypal.me/yajra)
|
||||
<a href='https://www.patreon.com/bePatron?u=4521203'><img alt='Become a Patron' src='https://s3.amazonaws.com/patreon_public_assets/toolbox/patreon.png' border='0' width='200px' ></a>
|
||||
|
||||
## License
|
||||
|
||||
The MIT License (MIT). Please see [License File](LICENSE.md) for more information.
|
||||
|
||||
## Credits
|
||||
|
||||
- [crazycodr/pdo-via-oci8](https://github.com/crazycodr/pdo-via-oci8)
|
||||
- [ramsey/pdo_oci8](https://github.com/ramsey/pdo_oci8)
|
||||
- To all contributors of this project
|
||||
+601
@@ -0,0 +1,601 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PDO userspace driver proxying calls to PHP OCI8 driver.
|
||||
*
|
||||
* @category Database
|
||||
* @author Arjay Angeles <aqangeles@gmail.com>
|
||||
* @copyright Copyright (c) 2013 Arjay Angeles
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
namespace Yajra\Pdo;
|
||||
|
||||
use PDO;
|
||||
use Yajra\Pdo\Oci8\Statement;
|
||||
use Yajra\Pdo\Oci8\Exceptions\Oci8Exception;
|
||||
|
||||
/**
|
||||
* Oci8 class to mimic the interface of the PDO class
|
||||
* This class extends PDO but overrides all of its methods. It does this so
|
||||
* that instanceof checks and type-hinting of existing code will work
|
||||
* seamlessly.
|
||||
*/
|
||||
class Oci8 extends PDO
|
||||
{
|
||||
/**
|
||||
* Database handler.
|
||||
*
|
||||
* @var resource
|
||||
*/
|
||||
private $dbh;
|
||||
|
||||
/**
|
||||
* Driver options.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $options = [];
|
||||
|
||||
/**
|
||||
* Whether currently in a transaction.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $inTransaction = false;
|
||||
|
||||
/**
|
||||
* Insert query statement table variable.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $table;
|
||||
|
||||
/**
|
||||
* Creates a PDO instance representing a connection to a database.
|
||||
*
|
||||
* Supports any connection string that is supported by oci_connect(),
|
||||
* or a valid PDO-style DSN (oci:host=host;port=port;dbname=dbname;charset=charset)
|
||||
* @link https://www.php.net/manual/en/function.oci-connect.php
|
||||
* @link https://www.php.net/manual/en/pdo.construct.php
|
||||
*
|
||||
* @param string $dsn
|
||||
* @param string $username
|
||||
* @param string $password
|
||||
* @param array $options
|
||||
* @throws Oci8Exception
|
||||
*/
|
||||
public function __construct($dsn, $username, $password, array $options = [])
|
||||
{
|
||||
$dsn = (string) trim($dsn);
|
||||
if (strpos($dsn, 'oci:') === 0) {
|
||||
$connectStr = preg_replace('/^oci:/', '', $dsn);
|
||||
parse_str(str_replace(';', '&', $connectStr), $connectParams);
|
||||
if (empty($connectParams['dbname'])) {
|
||||
throw new Oci8Exception('Invalid connection string');
|
||||
} else {
|
||||
$dsnStr = str_replace('//', '', $connectParams['dbname']);
|
||||
if (isset($connectParams['host']) && isset($connectParams['port'])) {
|
||||
$host = $connectParams['host'] . ':' . $connectParams['port'];
|
||||
} elseif (isset($connectParams['host'])) {
|
||||
$host = $connectParams['host'];
|
||||
}
|
||||
if (! empty($host)) {
|
||||
$dsnStr = $host . '/' . $dsnStr;
|
||||
}
|
||||
// A charset specified in the connection string takes
|
||||
// precedence over one specified in $options
|
||||
! empty($connectParams['charset'])
|
||||
? $charset = $this->configureCharset($connectParams)
|
||||
: $charset = $this->configureCharset($options);
|
||||
$dsn = $dsnStr;
|
||||
}
|
||||
} else {
|
||||
$charset = $this->configureCharset($options);
|
||||
}
|
||||
$this->connect($dsn, $username, $password, $options, $charset);
|
||||
// Save the options
|
||||
$this->options = $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a statement for execution and returns a statement object.
|
||||
*
|
||||
* @param string $statement This must be a valid SQL statement for the
|
||||
* target database server.
|
||||
* @param array $options [optional] This array holds one or more key=>value
|
||||
* pairs to set attribute values for the PDOStatement object that this
|
||||
* method returns.
|
||||
* @throws Oci8Exception
|
||||
* @return Statement
|
||||
*/
|
||||
public function prepare($statement, $options = null)
|
||||
{
|
||||
// Get instance options
|
||||
if ($options == null) {
|
||||
$options = $this->options;
|
||||
}
|
||||
|
||||
// Skip replacing ? with a pseudo named parameter on alter/create table command
|
||||
if ($this->isNamedParameterable($statement)) {
|
||||
// Replace ? with a pseudo named parameter
|
||||
$parameter = 0;
|
||||
$statement = preg_replace_callback('/(?:\'[^\']*\')(*SKIP)(*F)|\?/', function () use (&$parameter) {
|
||||
return ':p' . $parameter++;
|
||||
}, $statement);
|
||||
}
|
||||
|
||||
// check if statement is insert function
|
||||
if (strpos(strtolower($statement), 'insert into') !== false) {
|
||||
preg_match('/insert into\s+([^\s\(]*)?/', strtolower($statement), $matches);
|
||||
// store insert into table name
|
||||
$this->table = $matches[1];
|
||||
}
|
||||
|
||||
// Prepare the statement
|
||||
$sth = @oci_parse($this->dbh, $statement);
|
||||
|
||||
if (! $sth) {
|
||||
$e = oci_error($this->dbh);
|
||||
throw new Oci8Exception($e['message']);
|
||||
}
|
||||
|
||||
if (! is_array($options)) {
|
||||
$options = [];
|
||||
}
|
||||
|
||||
return new Statement($sth, $this, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates a transaction.
|
||||
*
|
||||
* @throws Oci8Exception
|
||||
* @return bool TRUE on success or FALSE on failure
|
||||
*/
|
||||
public function beginTransaction()
|
||||
{
|
||||
if ($this->inTransaction()) {
|
||||
throw new Oci8Exception('There is already an active transaction');
|
||||
}
|
||||
|
||||
$this->inTransaction = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the current process is in a transaction.
|
||||
*
|
||||
* @deprecated Use inTransaction() instead
|
||||
* @return bool
|
||||
*/
|
||||
public function isTransaction()
|
||||
{
|
||||
return $this->inTransaction();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if inside a transaction.
|
||||
*
|
||||
* @return bool TRUE if a transaction is currently active, and FALSE if not.
|
||||
*/
|
||||
public function inTransaction()
|
||||
{
|
||||
return $this->inTransaction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Commits a transaction.
|
||||
*
|
||||
* @return bool TRUE on success or FALSE on failure.
|
||||
*/
|
||||
public function commit()
|
||||
{
|
||||
if (oci_commit($this->dbh)) {
|
||||
$this->inTransaction = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rolls back a transaction.
|
||||
*
|
||||
* @throws Oci8Exception
|
||||
* @return bool TRUE on success or FALSE on failure.
|
||||
*/
|
||||
public function rollBack()
|
||||
{
|
||||
if (! $this->inTransaction()) {
|
||||
throw new Oci8Exception('There is no active transaction');
|
||||
}
|
||||
|
||||
if (oci_rollback($this->dbh)) {
|
||||
$this->inTransaction = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an attribute on the database handle.
|
||||
*
|
||||
* @param int $attribute
|
||||
* @param mixed $value
|
||||
* @return bool TRUE on success or FALSE on failure.
|
||||
*/
|
||||
public function setAttribute($attribute, $value)
|
||||
{
|
||||
$this->options[$attribute] = $value;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes an SQL statement and returns the number of affected rows.
|
||||
*
|
||||
* @param string $statement The SQL statement to prepare and execute.
|
||||
* @return int The number of rows that were modified or deleted by the SQL
|
||||
* statement you issued.
|
||||
*/
|
||||
public function exec($statement)
|
||||
{
|
||||
$stmt = $this->prepare($statement);
|
||||
$stmt->execute();
|
||||
|
||||
return $stmt->rowCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes an SQL statement, returning the results as a
|
||||
* Yajra\Pdo\Oci8\Statement object.
|
||||
*
|
||||
* @param string $statement The SQL statement to prepare and execute.
|
||||
* @param int|null $fetchMode The fetch mode must be one of the
|
||||
* PDO::FETCH_* constants.
|
||||
* @param mixed|null $modeArg Column number, class name or object.
|
||||
* @param array|null $ctorArgs Constructor arguments.
|
||||
* @return Statement
|
||||
*/
|
||||
public function query($statement, $fetchMode = null, $modeArg = null, $ctorArgs = null)
|
||||
{
|
||||
$stmt = $this->prepare($statement);
|
||||
$stmt->execute();
|
||||
if ($fetchMode) {
|
||||
$stmt->setFetchMode($fetchMode, $modeArg, $ctorArgs);
|
||||
}
|
||||
|
||||
return $stmt;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the current value of the sequence related to the table where
|
||||
* record is inserted by default. The sequence name should follow this for it to work
|
||||
* properly:
|
||||
* {$table}.'_id_seq'
|
||||
* If the sequence name is passed, then the function will check using that value.
|
||||
* Oracle does not support the last inserted ID functionality like MySQL.
|
||||
* If the above sequence does not exist, the method will return 0;.
|
||||
*
|
||||
* @param string $sequence Sequence name
|
||||
* @return mixed Last sequence number or 0 if sequence does not exist
|
||||
*/
|
||||
public function lastInsertId($sequence = null)
|
||||
{
|
||||
if (is_null($sequence)) {
|
||||
$sequence = $this->table . '_id_seq';
|
||||
}
|
||||
|
||||
if (! $this->checkSequence($sequence)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$stmt = $this->query("SELECT {$sequence}.CURRVAL FROM DUAL", PDO::FETCH_COLUMN);
|
||||
$id = $stmt->fetch();
|
||||
|
||||
return $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the SQLSTATE associated with the last operation on the database
|
||||
* handle.
|
||||
* While this returns an error code, it merely emulates the action. If
|
||||
* there are no errors, it returns the success SQLSTATE code (00000).
|
||||
* If there are errors, it returns HY000. See errorInfo() to retrieve
|
||||
* the actual Oracle error code and message.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function errorCode()
|
||||
{
|
||||
$error = $this->errorInfo();
|
||||
|
||||
return $error[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns extended error information for the last operation on the database handle.
|
||||
* The array consists of the following fields:
|
||||
* 0 SQLSTATE error code (a five characters alphanumeric identifier
|
||||
* defined in the ANSI SQL standard).
|
||||
* 1 Driver-specific error code.
|
||||
* 2 Driver-specific error message.
|
||||
*
|
||||
* @return array Error information
|
||||
*/
|
||||
public function errorInfo()
|
||||
{
|
||||
$e = $this->dbh ? oci_error($this->dbh) : null;
|
||||
|
||||
if (is_array($e)) {
|
||||
return [
|
||||
'HY000',
|
||||
$e['code'],
|
||||
$e['message'],
|
||||
];
|
||||
}
|
||||
|
||||
return ['00000', null, null];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a database connection attribute.
|
||||
*
|
||||
* @param int $attribute
|
||||
* @return mixed A successful call returns the value of the requested PDO
|
||||
* attribute. An unsuccessful call returns null.
|
||||
*/
|
||||
public function getAttribute($attribute)
|
||||
{
|
||||
if ($attribute == PDO::ATTR_DRIVER_NAME) {
|
||||
return 'oci8';
|
||||
}
|
||||
|
||||
if (isset($this->options[$attribute])) {
|
||||
return $this->options[$attribute];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Special non PDO function used to start cursors in the database
|
||||
* Remember to call oci_free_statement() on your cursor.
|
||||
*
|
||||
* @return mixed New statement handle, or FALSE on error.
|
||||
*/
|
||||
public function getNewCursor()
|
||||
{
|
||||
return oci_new_cursor($this->dbh);
|
||||
}
|
||||
|
||||
/**
|
||||
* Special non PDO function used to start descriptor in the database
|
||||
* Remember to call oci_free_statement() on your cursor.
|
||||
*
|
||||
* @param int $type One of OCI_DTYPE_FILE, OCI_DTYPE_LOB or OCI_DTYPE_ROWID.
|
||||
* @return mixed New LOB or FILE descriptor on success, FALSE on error.
|
||||
*/
|
||||
public function getNewDescriptor($type = OCI_D_LOB)
|
||||
{
|
||||
return oci_new_descriptor($this->dbh, $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Special non PDO function used to close an open cursor in the database.
|
||||
*
|
||||
* @param mixed $cursor A valid OCI statement identifier.
|
||||
* @return mixed Returns TRUE on success or FALSE on failure.
|
||||
*/
|
||||
public function closeCursor($cursor)
|
||||
{
|
||||
return oci_free_statement($cursor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Places quotes around the input string
|
||||
* If you are using this function to build SQL statements, you are strongly
|
||||
* recommended to use prepare() to prepare SQL statements with bound
|
||||
* parameters instead of using quote() to interpolate user input into an SQL
|
||||
* statement. Prepared statements with bound parameters are not only more
|
||||
* portable, more convenient, immune to SQL injection, but are often much
|
||||
* faster to execute than interpolated queries, as both the server and
|
||||
* client side can cache a compiled form of the query.
|
||||
*
|
||||
* @param string $string The string to be quoted.
|
||||
* @param int $paramType Provides a data type hint for drivers that have
|
||||
* alternate quoting styles
|
||||
* @return string Returns a quoted string that is theoretically safe to pass
|
||||
* into an SQL statement.
|
||||
* @todo Implement support for $paramType.
|
||||
*/
|
||||
public function quote($string, $paramType = PDO::PARAM_STR)
|
||||
{
|
||||
if (is_numeric($string)) {
|
||||
return $string;
|
||||
}
|
||||
|
||||
return "'" . str_replace("'", "''", $string) . "'";
|
||||
}
|
||||
|
||||
/**
|
||||
* Special non PDO function to check if sequence exists.
|
||||
*
|
||||
* @param string $name
|
||||
* @return bool
|
||||
*/
|
||||
public function checkSequence($name)
|
||||
{
|
||||
try {
|
||||
$stmt = $this->query(
|
||||
"SELECT count(*) FROM ALL_SEQUENCES WHERE SEQUENCE_NAME=UPPER('{$name}') AND SEQUENCE_OWNER=UPPER(USER)",
|
||||
PDO::FETCH_COLUMN
|
||||
);
|
||||
|
||||
return $stmt->fetch();
|
||||
} catch (\Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if statement can use pseudo named parameter.
|
||||
*
|
||||
* @param string $statement
|
||||
* @return bool
|
||||
*/
|
||||
private function isNamedParameterable($statement)
|
||||
{
|
||||
return ! preg_match('/^alter+ +table/', strtolower(trim($statement)))
|
||||
and ! preg_match('/^create+ +table/', strtolower(trim($statement)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to database.
|
||||
*
|
||||
* @param string $dsn
|
||||
* @param string $username
|
||||
* @param string $password
|
||||
* @param array $options
|
||||
* @param string $charset
|
||||
* @throws Oci8Exception
|
||||
*/
|
||||
private function connect($dsn, $username, $password, array $options, $charset)
|
||||
{
|
||||
$sessionMode = array_key_exists('session_mode', $options) ? $options['session_mode'] : null;
|
||||
|
||||
if (array_key_exists(PDO::ATTR_PERSISTENT, $options) && $options[PDO::ATTR_PERSISTENT]) {
|
||||
$this->dbh = @oci_pconnect($username, $password, $dsn, $charset, $sessionMode);
|
||||
} else {
|
||||
$this->dbh = @oci_connect($username, $password, $dsn, $charset, $sessionMode);
|
||||
}
|
||||
|
||||
if (! $this->dbh) {
|
||||
$e = oci_error();
|
||||
throw new Oci8Exception($e['message'], $e['code']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a timeout limiting the maxium time a database round-trip
|
||||
*
|
||||
* @param int $time_out
|
||||
* @return bool
|
||||
*/
|
||||
public function setCallTimeout($time_out)
|
||||
{
|
||||
if (! $this->dbh) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return oci_set_call_timeout($this->dbh, $time_out);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the client identifier.
|
||||
*
|
||||
* @param $identifier
|
||||
* @return bool
|
||||
*/
|
||||
public function setClientIdentifier($identifier)
|
||||
{
|
||||
if (! $this->dbh) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return oci_set_client_identifier($this->dbh, $identifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure proper charset.
|
||||
*
|
||||
* @param array $options
|
||||
* @return string
|
||||
*/
|
||||
private function configureCharset(array $options)
|
||||
{
|
||||
$defaultCharset = 'AL32UTF8';
|
||||
if (! empty($options['charset'])) {
|
||||
// Convert UTF8 charset to AL32UTF8
|
||||
return strtolower($options['charset']) == 'utf8' ? $defaultCharset : $options['charset'];
|
||||
}
|
||||
return $defaultCharset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Special non PDO function
|
||||
* Allocates new collection object.
|
||||
*
|
||||
* @param string $typeName Should be a valid named type (uppercase).
|
||||
* @param string $schema Should point to the scheme, where the named type was created.
|
||||
* The name of the current user is the default value.
|
||||
* @return \OCI_Collection
|
||||
*/
|
||||
public function getNewCollection($typeName, $schema)
|
||||
{
|
||||
return oci_new_collection($this->dbh, $typeName, $schema);
|
||||
}
|
||||
|
||||
/**
|
||||
* Special not PDO function.
|
||||
* Get options used in creating the connection.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getOptions()
|
||||
{
|
||||
return $this->options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Special non PDO function to get DB resource.
|
||||
*
|
||||
* @return resource
|
||||
*/
|
||||
public function getResource()
|
||||
{
|
||||
return $this->dbh;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return available drivers
|
||||
* Will insert the OCI driver on the list, if not exist.
|
||||
*
|
||||
* @return array with drivers
|
||||
*/
|
||||
public static function getAvailableDrivers()
|
||||
{
|
||||
$drivers = \PDO::getAvailableDrivers();
|
||||
if (! in_array('oci', $drivers)) {
|
||||
array_push($drivers, 'oci');
|
||||
}
|
||||
|
||||
return $drivers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the connection.
|
||||
*
|
||||
* @link https://www.oracle.com/technetwork/topics/php/php-scalability-ha-twp-128842.pdf oci_close should be called if the connection is pooled
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
if ($this->dbh) {
|
||||
oci_close($this->dbh);
|
||||
$this->dbh = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the connection when object is removed.
|
||||
*
|
||||
* @link https://www.php.net/manual/en/pdo.connections.php PDO should remove the connection
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
$this->close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Yajra\Pdo\Oci8\Exceptions;
|
||||
|
||||
use PDOException;
|
||||
|
||||
class Oci8Exception extends PDOException
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,853 @@
|
||||
<?php
|
||||
|
||||
namespace Yajra\Pdo\Oci8;
|
||||
|
||||
use PDO;
|
||||
use PDOStatement;
|
||||
use Yajra\Pdo\Oci8;
|
||||
use Yajra\Pdo\Oci8\Exceptions\Oci8Exception;
|
||||
|
||||
/**
|
||||
* Oci8 Statement class to mimic the interface of the PDOStatement class
|
||||
* This class extends PDOStatement but overrides all of its methods. It does
|
||||
* this so that instanceof check and type-hinting of existing code will work
|
||||
* seamlessly.
|
||||
*/
|
||||
class Statement extends PDOStatement
|
||||
{
|
||||
/**
|
||||
* Statement handler.
|
||||
*
|
||||
* @var resource
|
||||
*/
|
||||
private $sth;
|
||||
|
||||
/**
|
||||
* PDO Oci8 connection.
|
||||
*
|
||||
* @var \Yajra\Pdo\Oci8
|
||||
*/
|
||||
private $connection;
|
||||
|
||||
/**
|
||||
* Flag to convert LOB to string or not.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $returnLobs = true;
|
||||
|
||||
/**
|
||||
* Statement options.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $options = [];
|
||||
|
||||
/**
|
||||
* Fetch mode selected via setFetchMode().
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $fetchMode = PDO::FETCH_BOTH;
|
||||
|
||||
/**
|
||||
* Column number for PDO::FETCH_COLUMN fetch mode.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $fetchColNo = 0;
|
||||
|
||||
/**
|
||||
* Class name for PDO::FETCH_CLASS fetch mode.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $fetchClassName = '\stdClass';
|
||||
|
||||
/**
|
||||
* Constructor arguments for PDO::FETCH_CLASS.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $fetchCtorArgs = [];
|
||||
|
||||
/**
|
||||
* Object reference for PDO::FETCH_INTO fetch mode.
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
private $fetchIntoObject = null;
|
||||
|
||||
/**
|
||||
* PDO result set.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $results = [];
|
||||
|
||||
/**
|
||||
* Lists of binding values.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $bindings = [];
|
||||
|
||||
/**
|
||||
* Lists of LOB variables.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $blobObjects = [];
|
||||
|
||||
/**
|
||||
* Lists of LOB object binding values.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $blobBindings = [];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param resource $sth Statement handle created with oci_parse()
|
||||
* @param Oci8 $connection The Pdo_Oci8 object for this statement
|
||||
* @param array $options Options for the statement handle
|
||||
* @throws Oci8Exception
|
||||
*/
|
||||
public function __construct($sth, Oci8 $connection, array $options = [])
|
||||
{
|
||||
if (strtolower(get_resource_type($sth)) != 'oci8 statement') {
|
||||
throw new Oci8Exception(
|
||||
'Resource expected of type oci8 statement; '
|
||||
. (string) get_resource_type($sth) . ' received instead'
|
||||
);
|
||||
}
|
||||
|
||||
$this->sth = $sth;
|
||||
$this->connection = $connection;
|
||||
$this->options = $options;
|
||||
|
||||
$fetchMode = $connection->getAttribute(PDO::ATTR_DEFAULT_FETCH_MODE);
|
||||
if ($fetchMode) {
|
||||
$this->setFetchMode($fetchMode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a prepared statement.
|
||||
*
|
||||
* @param array $inputParams An array of values with as many elements as
|
||||
* there are bound parameters in the SQL statement being executed.
|
||||
* @throws Oci8Exception
|
||||
* @return bool TRUE on success or FALSE on failure
|
||||
*/
|
||||
public function execute($inputParams = null)
|
||||
{
|
||||
$mode = OCI_COMMIT_ON_SUCCESS;
|
||||
if ($this->connection->inTransaction() || count($this->blobObjects) > 0) {
|
||||
$mode = OCI_DEFAULT;
|
||||
}
|
||||
|
||||
// Set up bound parameters, if passed in.
|
||||
if (is_array($inputParams)) {
|
||||
foreach ($inputParams as $key => $value) {
|
||||
$this->bindings[] = $value;
|
||||
$this->bindParam($key, $inputParams[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
$result = @oci_execute($this->sth, $mode);
|
||||
|
||||
// Save blob objects if set.
|
||||
if ($result && count($this->blobObjects) > 0) {
|
||||
foreach ($this->blobObjects as $param => $blob) {
|
||||
/* @var \OCI_Lob $blob */
|
||||
$blob->save($this->blobBindings[$param]);
|
||||
}
|
||||
}
|
||||
|
||||
if (! $this->connection->inTransaction() && count($this->blobObjects) > 0) {
|
||||
$this->connection->commit();
|
||||
}
|
||||
|
||||
if ($result != true) {
|
||||
$e = oci_error($this->sth);
|
||||
|
||||
$message = '';
|
||||
$message = $message . 'Error Code : ' . $e['code'] . PHP_EOL;
|
||||
$message = $message . 'Error Message : ' . $e['message'] . PHP_EOL;
|
||||
$message = $message . 'Position : ' . $e['offset'] . PHP_EOL;
|
||||
$message = $message . 'Statement : ' . $e['sqltext'] . PHP_EOL;
|
||||
$message = $message . 'Bindings : [' . $this->displayBindings() . ']' . PHP_EOL;
|
||||
|
||||
throw new Oci8Exception($message, $e['code']);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Special not PDO function to format display of query bindings.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function displayBindings()
|
||||
{
|
||||
$bindings = [];
|
||||
foreach ($this->bindings as $binding) {
|
||||
if (is_object($binding)) {
|
||||
$bindings[] = get_class($binding);
|
||||
} elseif (is_array($binding)) {
|
||||
$bindings[] = 'Array';
|
||||
} else {
|
||||
$bindings[] = (string) $binding;
|
||||
}
|
||||
}
|
||||
|
||||
return implode(',', $bindings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the next row from a result set.
|
||||
*
|
||||
* @param int|null $fetchMode Controls how the next row will be returned to
|
||||
* the caller. This value must be one of the PDO::FETCH_* constants,
|
||||
* defaulting to value of PDO::ATTR_DEFAULT_FETCH_MODE (which defaults to
|
||||
* PDO::FETCH_BOTH).
|
||||
* @param int $cursorOrientation For a PDOStatement object representing a
|
||||
* scrollable cursor, this value determines which row will be returned to
|
||||
* the caller. This value must be one of the PDO::FETCH_ORI_* constants,
|
||||
* defaulting to PDO::FETCH_ORI_NEXT. To request a scrollable cursor for
|
||||
* your PDOStatement object, you must set the PDO::ATTR_CURSOR attribute
|
||||
* to PDO::CURSOR_SCROLL when you prepare the SQL statement with
|
||||
* PDO::prepare.
|
||||
* @param int $cursorOffset [optional]
|
||||
* @return mixed The return value of this function on success depends on the
|
||||
* fetch type. In all cases, FALSE is returned on failure.
|
||||
* @todo Implement cursorOrientation and cursorOffset
|
||||
*/
|
||||
public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0)
|
||||
{
|
||||
// If not fetchMode was specified, used the default value of or the mode
|
||||
// set by the last call to setFetchMode()
|
||||
if ($fetchMode === null) {
|
||||
$fetchMode = $this->fetchMode;
|
||||
}
|
||||
|
||||
// Convert array keys (or object properties) to lowercase
|
||||
$toLowercase = ($this->getAttribute(PDO::ATTR_CASE) == PDO::CASE_LOWER);
|
||||
// Convert null value to empty string
|
||||
$nullToString = ($this->getAttribute(PDO::ATTR_ORACLE_NULLS) == PDO::NULL_TO_STRING);
|
||||
// Convert empty string to null
|
||||
$nullEmptyString = ($this->getAttribute(PDO::ATTR_ORACLE_NULLS) == PDO::NULL_EMPTY_STRING);
|
||||
|
||||
// Determine the fetch mode
|
||||
switch ($fetchMode) {
|
||||
case PDO::FETCH_BOTH:
|
||||
$rs = oci_fetch_array($this->sth); // Fetches both; nice!
|
||||
if ($rs === false) {
|
||||
return false;
|
||||
}
|
||||
if ($toLowercase) {
|
||||
$rs = array_change_key_case($rs);
|
||||
}
|
||||
if ($this->returnLobs && is_array($rs)) {
|
||||
foreach ($rs as $field => $value) {
|
||||
if (is_object($value)) {
|
||||
$rs[$field] = $this->loadLob($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $rs;
|
||||
|
||||
case PDO::FETCH_ASSOC:
|
||||
$rs = oci_fetch_assoc($this->sth);
|
||||
if ($rs === false) {
|
||||
return false;
|
||||
}
|
||||
if ($toLowercase) {
|
||||
$rs = array_change_key_case($rs);
|
||||
}
|
||||
if ($this->returnLobs && is_array($rs)) {
|
||||
foreach ($rs as $field => $value) {
|
||||
if (is_object($value)) {
|
||||
$rs[$field] = $this->loadLob($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $rs;
|
||||
|
||||
case PDO::FETCH_NUM:
|
||||
$rs = oci_fetch_row($this->sth);
|
||||
if ($rs === false) {
|
||||
return false;
|
||||
}
|
||||
if ($this->returnLobs && is_array($rs)) {
|
||||
foreach ($rs as $field => $value) {
|
||||
if (is_object($value)) {
|
||||
$rs[$field] = $this->loadLob($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $rs;
|
||||
|
||||
case PDO::FETCH_COLUMN:
|
||||
$rs = oci_fetch_row($this->sth);
|
||||
$colNo = (int) $this->fetchColNo;
|
||||
if (is_array($rs) && array_key_exists($colNo, $rs)) {
|
||||
$value = $rs[$colNo];
|
||||
if (is_object($value)) {
|
||||
return $this->loadLob($value);
|
||||
}
|
||||
|
||||
return $value;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case PDO::FETCH_OBJ:
|
||||
case PDO::FETCH_INTO:
|
||||
case PDO::FETCH_CLASS:
|
||||
case PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE:
|
||||
$rs = oci_fetch_assoc($this->sth);
|
||||
if ($rs === false) {
|
||||
return false;
|
||||
}
|
||||
if ($toLowercase) {
|
||||
$rs = array_change_key_case($rs);
|
||||
}
|
||||
|
||||
if ($fetchMode === PDO::FETCH_INTO) {
|
||||
if (is_object($this->fetchIntoObject)) {
|
||||
$object = $this->fetchIntoObject;
|
||||
} else {
|
||||
// Object to set into has not been set
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if ($fetchMode === PDO::FETCH_OBJ) {
|
||||
$className = '\stdClass';
|
||||
$ctorargs = [];
|
||||
} else {
|
||||
$className = $this->fetchClassName;
|
||||
$ctorargs = $this->fetchCtorArgs ? array_values($this->fetchCtorArgs) : [];
|
||||
}
|
||||
|
||||
$object = $fetchMode === PDO::FETCH_CLASS
|
||||
? (new \ReflectionClass($className))->newInstanceWithoutConstructor()
|
||||
: new $className(...$ctorargs);
|
||||
}
|
||||
|
||||
// Format recordsets values depending on options
|
||||
foreach ($rs as $field => $value) {
|
||||
// convert null to empty string
|
||||
if (is_null($value) && $nullToString) {
|
||||
$rs[$field] = '';
|
||||
}
|
||||
|
||||
// convert empty string to null
|
||||
if (empty($rs[$field]) && $nullEmptyString) {
|
||||
$rs[$field] = null;
|
||||
}
|
||||
|
||||
// convert LOB to string
|
||||
if ($this->returnLobs && is_object($value)) {
|
||||
$ociFieldIndex = is_int($field) ? $field : array_search($field, array_keys($rs));
|
||||
// oci field type index is base 1.
|
||||
if (oci_field_type($this->sth, $ociFieldIndex + 1) == 'ROWID') {
|
||||
throw new Oci8Exception('ROWID output is not yet supported. Please use ROWIDTOCHAR(ROWID) function as workaround.');
|
||||
} else {
|
||||
$object->$field = $this->loadLob($value);
|
||||
}
|
||||
} else {
|
||||
$object->$field = $value;
|
||||
}
|
||||
}
|
||||
|
||||
if ($fetchMode === PDO::FETCH_CLASS && method_exists($object, '__construct')) {
|
||||
$object->__construct(...$ctorargs);
|
||||
}
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a LOB object value.
|
||||
*
|
||||
* @param mixed $lob
|
||||
* @return mixed
|
||||
*/
|
||||
private function loadLob($lob)
|
||||
{
|
||||
try {
|
||||
return $lob->load();
|
||||
} catch (\Exception $e) {
|
||||
return $lob;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds a parameter to the specified variable name.
|
||||
*
|
||||
* @param string $parameter Parameter identifier. For a prepared statement
|
||||
* using named placeholders, this will be a parameter name of the form
|
||||
* :name. For a prepared statement using question mark placeholders, this
|
||||
* will be the 1-indexed position of the parameter.
|
||||
* @param mixed $variable Name of the PHP variable to bind to the SQL
|
||||
* statement parameter.
|
||||
* @param int $dataType Explicit data type for the parameter using the
|
||||
* PDO::PARAM_* constants.
|
||||
* @param int $maxLength Length of the data type. To indicate that a
|
||||
* parameter is an OUT parameter from a stored procedure, you must
|
||||
* explicitly set the length.
|
||||
* @param array $options [optional]
|
||||
* @return bool TRUE on success or FALSE on failure.
|
||||
* @todo Map PDO datatypes to oci8 datatypes and implement support for
|
||||
* datatypes and length.
|
||||
*/
|
||||
public function bindParam($parameter, &$variable, $dataType = PDO::PARAM_STR, $maxLength = -1, $options = null)
|
||||
{
|
||||
// strip INOUT type for oci
|
||||
$dataType &= ~PDO::PARAM_INPUT_OUTPUT;
|
||||
|
||||
// Replace the first @oci8param to a pseudo named parameter
|
||||
if (is_numeric($parameter)) {
|
||||
$parameter = ':p' . intval($parameter - 1);
|
||||
}
|
||||
|
||||
// Adapt the type
|
||||
switch ($dataType) {
|
||||
case PDO::PARAM_BOOL:
|
||||
$ociType = SQLT_INT;
|
||||
break;
|
||||
|
||||
case PDO::PARAM_NULL:
|
||||
$ociType = SQLT_CHR;
|
||||
break;
|
||||
|
||||
case PDO::PARAM_INT:
|
||||
$ociType = SQLT_INT;
|
||||
break;
|
||||
|
||||
case PDO::PARAM_STR:
|
||||
$ociType = SQLT_CHR;
|
||||
break;
|
||||
|
||||
case PDO::PARAM_LOB:
|
||||
$ociType = OCI_B_BLOB;
|
||||
|
||||
$this->blobBindings[$parameter] = $variable;
|
||||
|
||||
$variable = $this->connection->getNewDescriptor();
|
||||
$variable->writeTemporary($this->blobBindings[$parameter], OCI_TEMP_BLOB);
|
||||
|
||||
$this->blobObjects[$parameter] = &$variable;
|
||||
break;
|
||||
|
||||
case PDO::PARAM_STMT:
|
||||
$ociType = OCI_B_CURSOR;
|
||||
|
||||
// Result sets require a cursor
|
||||
$variable = $this->connection->getNewCursor();
|
||||
break;
|
||||
|
||||
case SQLT_NTY:
|
||||
$ociType = SQLT_NTY;
|
||||
|
||||
$schema = isset($options['schema']) ? $options['schema'] : '';
|
||||
$type_name = isset($options['type_name']) ? $options['type_name'] : '';
|
||||
|
||||
if (strtoupper(get_class($variable)) == "OCI-COLLECTION") {
|
||||
$collection_temp = $this->connection->getNewCollection($type_name, $schema);
|
||||
$collection_temp->assign($variable);
|
||||
|
||||
$variable = $this->connection->getNewCollection($type_name, $schema);
|
||||
$variable->assign($collection_temp);
|
||||
|
||||
$collection_temp->free();
|
||||
} else {
|
||||
// set params required to use custom type.
|
||||
$variable = $this->connection->getNewCollection($type_name, $schema);
|
||||
}
|
||||
break;
|
||||
|
||||
case SQLT_CLOB:
|
||||
$ociType = OCI_B_CLOB;
|
||||
|
||||
$this->blobBindings[$parameter] = $variable;
|
||||
|
||||
$variable = $this->connection->getNewDescriptor();
|
||||
$variable->writeTemporary($this->blobBindings[$parameter], OCI_TEMP_CLOB);
|
||||
|
||||
$this->blobObjects[$parameter] = &$variable;
|
||||
break;
|
||||
|
||||
default:
|
||||
$ociType = SQLT_CHR;
|
||||
break;
|
||||
}
|
||||
|
||||
if (is_array($variable)) {
|
||||
return $this->bindArray($parameter, $variable, count($variable), $maxLength, $ociType);
|
||||
}
|
||||
|
||||
$this->bindings[] = &$variable;
|
||||
|
||||
return oci_bind_by_name($this->sth, $parameter, $variable, $maxLength, $ociType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Special non-PDO function that binds an array parameter to the specified variable name.
|
||||
*
|
||||
* @see http://php.net/manual/en/function.oci-bind-array-by-name.php
|
||||
* @param string $parameter The Oracle placeholder.
|
||||
* @param array $variable An array.
|
||||
* @param int $maxTableLength Sets the maximum length both for incoming and result arrays.
|
||||
* @param int $maxItemLength Sets maximum length for array items.
|
||||
* If not specified or equals to -1, oci_bind_array_by_name() will find
|
||||
* the longest element in the incoming array and will use it as the maximum length.
|
||||
* @param int $type Explicit data type for the parameter using the
|
||||
* @return bool TRUE on success or FALSE on failure.
|
||||
*/
|
||||
public function bindArray($parameter, &$variable, $maxTableLength, $maxItemLength = -1, $type = SQLT_CHR)
|
||||
{
|
||||
$this->bindings[] = $variable;
|
||||
|
||||
return oci_bind_array_by_name($this->sth, $parameter, $variable, $maxTableLength, $maxItemLength, $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds a column to a PHP variable.
|
||||
*
|
||||
* @param mixed $column Number of the column (1-indexed) or name of the
|
||||
* column in the result set. If using the column name, be aware that the
|
||||
* name should match the case of the column, as returned by the driver.
|
||||
* @param mixed $variable The PHP to which the column should be bound.
|
||||
* @param int $dataType Data type of the parameter, specified by the
|
||||
* PDO::PARAM_* constants.
|
||||
* @param int $maxLength A hint for pre-allocation.
|
||||
* @param array $options [optional] Optional parameter(s) for the driver.
|
||||
* @throws Oci8Exception
|
||||
* @return bool TRUE on success or FALSE on failure.
|
||||
* @todo Implement this functionality by creating a table map of the
|
||||
* variables passed in here, and, when iterating over the values
|
||||
* of the query or fetching rows, assign data from each column
|
||||
* to their respective variable in the map.
|
||||
*/
|
||||
public function bindColumn($column, &$variable, $dataType = null, $maxLength = -1, $options = null)
|
||||
{
|
||||
throw new Oci8Exception('bindColumn has not been implemented');
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds a value to a parameter.
|
||||
*
|
||||
* @param string $parameter Parameter identifier. For a prepared statement
|
||||
* using named placeholders, this will be a parameter name of the form
|
||||
* :name. For a prepared statement using question mark placeholders, this
|
||||
* will be the 1-indexed position of the parameter.
|
||||
* @param mixed $variable The value to bind to the parameter.
|
||||
* @param int $dataType Explicit data type for the parameter using the
|
||||
* PDO::PARAM_* constants.
|
||||
* @return bool TRUE on success or FALSE on failure.
|
||||
*/
|
||||
public function bindValue($parameter, $variable, $dataType = PDO::PARAM_STR)
|
||||
{
|
||||
return $this->bindParam($parameter, $variable, $dataType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of rows affected by the last executed statement.
|
||||
*
|
||||
* @return int The number of rows.
|
||||
*/
|
||||
public function rowCount()
|
||||
{
|
||||
return oci_num_rows($this->sth);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a single column from the next row of a result set.
|
||||
*
|
||||
* @param int $colNumber 0-indexed number of the column you wish to retrieve
|
||||
* from the row. If no value is supplied, it fetches the first column.
|
||||
* @return string Returns a single column in the next row of a result set.
|
||||
*/
|
||||
public function fetchColumn($colNumber = null)
|
||||
{
|
||||
$this->setFetchMode(PDO::FETCH_COLUMN, $colNumber);
|
||||
|
||||
return $this->fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array containing all of the result set rows.
|
||||
*
|
||||
* @param int $fetchMode Controls the contents of the returned array as
|
||||
* documented in PDOStatement::fetch.
|
||||
* @param mixed $fetchArgument This argument has a different meaning
|
||||
* depending on the value of the fetchMode parameter.
|
||||
* @param array $ctorArgs [optional] Arguments of custom class constructor
|
||||
* when the fetch_style parameter is PDO::FETCH_CLASS.
|
||||
* @return array Array containing all of the remaining rows in the result
|
||||
* set. The array represents each row as either an array of column values
|
||||
* or an object with properties corresponding to each column name.
|
||||
*/
|
||||
public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = [])
|
||||
{
|
||||
if (is_null($fetchMode)) {
|
||||
$fetchMode = $this->fetchMode;
|
||||
}
|
||||
|
||||
$this->setFetchMode($fetchMode, $fetchArgument, $ctorArgs);
|
||||
|
||||
$this->results = [];
|
||||
while ($row = $this->fetch()) {
|
||||
if ((is_array($row) || is_object($row)) && is_resource(reset($row))) {
|
||||
$stmt = new self(reset($row), $this->connection, $this->options);
|
||||
$stmt->execute();
|
||||
$stmt->setFetchMode($fetchMode, $fetchArgument, $ctorArgs);
|
||||
while ($rs = $stmt->fetch()) {
|
||||
$this->results[] = $rs;
|
||||
}
|
||||
} else {
|
||||
$this->results[] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the next row and returns it as an object.
|
||||
*
|
||||
* @param string $className
|
||||
* @param array $ctorArgs
|
||||
* @return mixed
|
||||
*/
|
||||
public function fetchObject($className = null, $ctorArgs = [])
|
||||
{
|
||||
$this->setFetchMode(PDO::FETCH_CLASS, $className, $ctorArgs);
|
||||
|
||||
return $this->fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the SQLSTATE associated with the last operation on the resource handle.
|
||||
* While this returns an error code, it merely emulates the action. If
|
||||
* there are no errors, it returns the success SQLSTATE code (00000).
|
||||
* If there are errors, it returns HY000. See errorInfo() to retrieve
|
||||
* the actual Oracle error code and message.
|
||||
*
|
||||
* @return string Error code
|
||||
*/
|
||||
public function errorCode()
|
||||
{
|
||||
$error = $this->errorInfo();
|
||||
|
||||
return $error[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch extended error information associated with the last operation on
|
||||
* the resource handle.
|
||||
*
|
||||
* @return array Array of error information about the last operation
|
||||
* performed
|
||||
*/
|
||||
public function errorInfo()
|
||||
{
|
||||
$e = oci_error($this->sth);
|
||||
|
||||
if (is_array($e)) {
|
||||
return [
|
||||
'HY000',
|
||||
$e['code'],
|
||||
$e['message'],
|
||||
];
|
||||
}
|
||||
|
||||
return ['00000', null, null];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a statement attribute.
|
||||
*
|
||||
* @param int $attribute
|
||||
* @param mixed $value
|
||||
* @return true on success or FALSE on failure.
|
||||
*/
|
||||
public function setAttribute($attribute, $value)
|
||||
{
|
||||
$this->options[$attribute] = $value;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a statement attribute.
|
||||
*
|
||||
* @param int $attribute
|
||||
* @return mixed The attribute value.
|
||||
*/
|
||||
public function getAttribute($attribute)
|
||||
{
|
||||
if (isset($this->options[$attribute])) {
|
||||
return $this->options[$attribute];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of columns in the result set.
|
||||
*
|
||||
* @return int The number of columns in the statement result set. If there
|
||||
* is no result set, it returns 0.
|
||||
*/
|
||||
public function columnCount()
|
||||
{
|
||||
return oci_num_fields($this->sth);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns metadata for a column in a result set.
|
||||
* The array returned by this function is patterned after that
|
||||
* returned by \PDO::getColumnMeta(). It includes the following
|
||||
* elements:
|
||||
* native_type
|
||||
* driver:decl_type
|
||||
* flags
|
||||
* name
|
||||
* table
|
||||
* len
|
||||
* precision
|
||||
* pdo_type.
|
||||
*
|
||||
* @param int $column The 0-indexed column in the result set.
|
||||
* @return array An associative array containing the above metadata values
|
||||
* for a single column.
|
||||
*/
|
||||
public function getColumnMeta($column)
|
||||
{
|
||||
// Columns in oci8 are 1-based; add 1 if it's a number
|
||||
if (is_numeric($column)) {
|
||||
$column++;
|
||||
}
|
||||
|
||||
$meta = [];
|
||||
$meta['native_type'] = oci_field_type($this->sth, $column);
|
||||
$meta['driver:decl_type'] = oci_field_type_raw($this->sth, $column);
|
||||
$meta['flags'] = [];
|
||||
$meta['name'] = oci_field_name($this->sth, $column);
|
||||
$meta['table'] = null;
|
||||
$meta['len'] = oci_field_size($this->sth, $column);
|
||||
$meta['precision'] = oci_field_precision($this->sth, $column);
|
||||
$meta['pdo_type'] = null;
|
||||
$meta['is_null'] = oci_field_is_null($this->sth, $column);
|
||||
|
||||
return $meta;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default fetch mode for this statement.
|
||||
*
|
||||
* @param int|null $fetchMode The fetch mode must be one of the
|
||||
* PDO::FETCH_* constants.
|
||||
* @param mixed|null $modeArg Column number, class name or object.
|
||||
* @param array|null $ctorArgs Constructor arguments.
|
||||
* @throws Oci8Exception
|
||||
* @return bool TRUE on success or FALSE on failure.
|
||||
*/
|
||||
public function setFetchMode($fetchMode, $modeArg = null, $ctorArgs = [])
|
||||
{
|
||||
// See which fetch mode we have
|
||||
switch ($fetchMode) {
|
||||
case PDO::FETCH_ASSOC:
|
||||
case PDO::FETCH_NUM:
|
||||
case PDO::FETCH_BOTH:
|
||||
case PDO::FETCH_OBJ:
|
||||
$this->fetchMode = $fetchMode;
|
||||
$this->fetchColNo = 0;
|
||||
$this->fetchClassName = '\stdClass';
|
||||
$this->fetchCtorArgs = [];
|
||||
$this->fetchIntoObject = null;
|
||||
break;
|
||||
case PDO::FETCH_CLASS:
|
||||
case PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE:
|
||||
$this->fetchMode = $fetchMode;
|
||||
$this->fetchColNo = 0;
|
||||
$this->fetchClassName = '\stdClass';
|
||||
if ($modeArg) {
|
||||
$this->fetchClassName = $modeArg;
|
||||
}
|
||||
$this->fetchCtorArgs = $ctorArgs;
|
||||
$this->fetchIntoObject = null;
|
||||
break;
|
||||
case PDO::FETCH_INTO:
|
||||
if (! is_object($modeArg)) {
|
||||
throw new Oci8Exception(
|
||||
'$modeArg must be instance of an object'
|
||||
);
|
||||
}
|
||||
$this->fetchMode = $fetchMode;
|
||||
$this->fetchColNo = 0;
|
||||
$this->fetchClassName = '\stdClass';
|
||||
$this->fetchCtorArgs = [];
|
||||
$this->fetchIntoObject = $modeArg;
|
||||
break;
|
||||
case PDO::FETCH_COLUMN:
|
||||
$this->fetchMode = $fetchMode;
|
||||
$this->fetchColNo = (int) $modeArg;
|
||||
$this->fetchClassName = '\stdClass';
|
||||
$this->fetchCtorArgs = [];
|
||||
$this->fetchIntoObject = null;
|
||||
break;
|
||||
default:
|
||||
throw new Oci8Exception('Requested fetch mode is not supported ' .
|
||||
'by this implementation');
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Advances to the next rowset in a multi-rowset statement handle.
|
||||
*
|
||||
* @throws Oci8Exception
|
||||
* @return bool TRUE on success or FALSE on failure.
|
||||
* @todo Implement method
|
||||
*/
|
||||
public function nextRowset()
|
||||
{
|
||||
throw new Oci8Exception('setFetchMode has not been implemented');
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the cursor, enabling the statement to be executed again.
|
||||
*
|
||||
* @return bool TRUE on success or FALSE on failure.
|
||||
*/
|
||||
public function closeCursor()
|
||||
{
|
||||
return oci_free_cursor($this->sth);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump a SQL prepared command.
|
||||
*
|
||||
* @throws Oci8Exception
|
||||
* @return bool TRUE on success or FALSE on failure.
|
||||
* @todo Implement method
|
||||
*/
|
||||
public function debugDumpParams()
|
||||
{
|
||||
throw new Oci8Exception('setFetchMode has not been implemented');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user