feat: commit vendor folder directly to bypass air-gapped build restrictions

This commit is contained in:
2026-09-03 16:27:31 +07:00
parent 361347d312
commit b24dea3095
6610 changed files with 839660 additions and 8 deletions
+22
View File
@@ -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.
+181
View File
@@ -0,0 +1,181 @@
# Oracle DB driver for Laravel 4|5|6|7|8 via OCI8
[![Build Status](https://github.com/yajra/laravel-oci8/workflows/tests/badge.svg)](https://github.com/yajra/laravel-oci8/actions)
[![Total Downloads](https://poser.pugx.org/yajra/laravel-oci8/d/total.svg)](https://packagist.org/packages/yajra/laravel-oci8)
[![Latest Stable Version](https://poser.pugx.org/yajra/laravel-oci8/v/stable.svg)](https://packagist.org/packages/yajra/laravel-oci8)
[![License](https://poser.pugx.org/yajra/laravel-oci8/license.svg)](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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
}
}
+498
View File
@@ -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);
}
}
+11
View File
@@ -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);
}
}
+109
View File
@@ -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);
}
}
+156
View File
@@ -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;
}
}
+96
View File
@@ -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;");
}
}
@@ -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
View File
@@ -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
View File
@@ -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);
}
}