499 lines
13 KiB
PHP
499 lines
13 KiB
PHP
<?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);
|
|
}
|
|
}
|
|
}
|