1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Yajra\Oci8; |
4
|
|
|
|
5
|
|
|
use PDO; |
6
|
|
|
use Exception; |
7
|
|
|
use PDOStatement; |
8
|
|
|
use Illuminate\Support\Str; |
9
|
|
|
use Yajra\Pdo\Oci8\Statement; |
10
|
|
|
use Yajra\Oci8\Schema\Trigger; |
11
|
|
|
use Yajra\Oci8\Schema\Sequence; |
12
|
|
|
use Illuminate\Database\Grammar; |
13
|
|
|
use Illuminate\Database\Connection; |
14
|
|
|
use Doctrine\DBAL\Connection as DoctrineConnection; |
15
|
|
|
use Yajra\Oci8\Query\OracleBuilder as QueryBuilder; |
16
|
|
|
use Yajra\Oci8\Schema\OracleBuilder as SchemaBuilder; |
17
|
|
|
use Doctrine\DBAL\Driver\OCI8\Driver as DoctrineDriver; |
18
|
|
|
use Yajra\Oci8\Query\Grammars\OracleGrammar as QueryGrammar; |
19
|
|
|
use Yajra\Oci8\Query\Processors\OracleProcessor as Processor; |
20
|
|
|
use Yajra\Oci8\Schema\Grammars\OracleGrammar as SchemaGrammar; |
21
|
|
|
|
22
|
|
|
class Oci8Connection extends Connection |
23
|
|
|
{ |
24
|
|
|
const RECONNECT_ERRORS = 'reconnect_errors'; |
25
|
|
|
|
26
|
|
|
/** |
27
|
|
|
* @var string |
28
|
|
|
*/ |
29
|
|
|
protected $schema; |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* @var \Yajra\Oci8\Schema\Sequence |
33
|
|
|
*/ |
34
|
|
|
protected $sequence; |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* @var \Yajra\Oci8\Schema\Trigger |
38
|
|
|
*/ |
39
|
|
|
protected $trigger; |
40
|
|
|
|
41
|
|
|
/** |
42
|
|
|
* @param PDO|\Closure $pdo |
43
|
|
|
* @param string $database |
44
|
|
|
* @param string $tablePrefix |
45
|
|
|
* @param array $config |
46
|
|
|
*/ |
47
|
|
|
public function __construct($pdo, $database = '', $tablePrefix = '', array $config = []) |
48
|
|
|
{ |
49
|
|
|
parent::__construct($pdo, $database, $tablePrefix, $config); |
50
|
|
|
$this->sequence = new Sequence($this); |
51
|
|
|
$this->trigger = new Trigger($this); |
52
|
|
|
} |
53
|
|
|
|
54
|
|
|
/** |
55
|
|
|
* Get current schema. |
56
|
|
|
* |
57
|
|
|
* @return string |
58
|
|
|
*/ |
59
|
|
|
public function getSchema() |
60
|
|
|
{ |
61
|
|
|
return $this->schema; |
62
|
|
|
} |
63
|
|
|
|
64
|
|
|
/** |
65
|
|
|
* Set current schema. |
66
|
|
|
* |
67
|
|
|
* @param string $schema |
68
|
|
|
* @return $this |
69
|
|
|
*/ |
70
|
|
|
public function setSchema($schema) |
71
|
|
|
{ |
72
|
|
|
$this->schema = $schema; |
73
|
|
|
$sessionVars = [ |
74
|
|
|
'CURRENT_SCHEMA' => $schema, |
75
|
|
|
]; |
76
|
|
|
|
77
|
|
|
return $this->setSessionVars($sessionVars); |
78
|
|
|
} |
79
|
|
|
|
80
|
|
|
/** |
81
|
|
|
* Update oracle session variables. |
82
|
|
|
* |
83
|
|
|
* @param array $sessionVars |
84
|
|
|
* @return $this |
85
|
|
|
*/ |
86
|
|
|
public function setSessionVars(array $sessionVars) |
87
|
|
|
{ |
88
|
|
|
$vars = []; |
89
|
|
|
foreach ($sessionVars as $option => $value) { |
90
|
|
|
$vars[] = (strtoupper($option) == 'CURRENT_SCHEMA') ? |
91
|
|
|
"$option = $value" : |
92
|
|
|
"$option = '$value'"; |
93
|
|
|
} |
94
|
|
|
|
95
|
|
|
if ($vars) { |
|
|
|
|
96
|
|
|
$sql = 'ALTER SESSION SET ' . implode(' ', $vars); |
97
|
|
|
$this->statement($sql); |
98
|
|
|
} |
99
|
|
|
|
100
|
|
|
return $this; |
101
|
|
|
} |
102
|
|
|
|
103
|
|
|
/** |
104
|
|
|
* Get sequence class. |
105
|
|
|
* |
106
|
|
|
* @return \Yajra\Oci8\Schema\Sequence |
107
|
|
|
*/ |
108
|
|
|
public function getSequence() |
109
|
|
|
{ |
110
|
|
|
return $this->sequence; |
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
/** |
114
|
|
|
* Set sequence class. |
115
|
|
|
* |
116
|
|
|
* @param \Yajra\Oci8\Schema\Sequence $sequence |
117
|
|
|
* @return \Yajra\Oci8\Schema\Sequence |
118
|
|
|
*/ |
119
|
|
|
public function setSequence(Sequence $sequence) |
120
|
|
|
{ |
121
|
|
|
return $this->sequence = $sequence; |
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
/** |
125
|
|
|
* Get oracle trigger class. |
126
|
|
|
* |
127
|
|
|
* @return \Yajra\Oci8\Schema\Trigger |
128
|
|
|
*/ |
129
|
|
|
public function getTrigger() |
130
|
|
|
{ |
131
|
|
|
return $this->trigger; |
132
|
|
|
} |
133
|
|
|
|
134
|
|
|
/** |
135
|
|
|
* Set oracle trigger class. |
136
|
|
|
* |
137
|
|
|
* @param \Yajra\Oci8\Schema\Trigger $trigger |
138
|
|
|
* @return \Yajra\Oci8\Schema\Trigger |
139
|
|
|
*/ |
140
|
|
|
public function setTrigger(Trigger $trigger) |
141
|
|
|
{ |
142
|
|
|
return $this->trigger = $trigger; |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
/** |
146
|
|
|
* Get a schema builder instance for the connection. |
147
|
|
|
* |
148
|
|
|
* @return \Yajra\Oci8\Schema\OracleBuilder |
149
|
|
|
*/ |
150
|
|
|
public function getSchemaBuilder() |
151
|
|
|
{ |
152
|
|
|
if (is_null($this->schemaGrammar)) { |
153
|
|
|
$this->useDefaultSchemaGrammar(); |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
return new SchemaBuilder($this); |
157
|
|
|
} |
158
|
|
|
|
159
|
|
|
/** |
160
|
|
|
* Begin a fluent query against a database table. |
161
|
|
|
* |
162
|
|
|
* @param string $table |
163
|
|
|
* @return \Yajra\Oci8\Query\OracleBuilder |
164
|
|
|
*/ |
165
|
|
|
public function table($table) |
166
|
|
|
{ |
167
|
|
|
$processor = $this->getPostProcessor(); |
168
|
|
|
|
169
|
|
|
$query = new QueryBuilder($this, $this->getQueryGrammar(), $processor); |
170
|
|
|
|
171
|
|
|
return $query->from($table); |
172
|
|
|
} |
173
|
|
|
|
174
|
|
|
/** |
175
|
|
|
* Set oracle session date format. |
176
|
|
|
* |
177
|
|
|
* @param string $format |
178
|
|
|
* @return $this |
179
|
|
|
*/ |
180
|
|
|
public function setDateFormat($format = 'YYYY-MM-DD HH24:MI:SS') |
181
|
|
|
{ |
182
|
|
|
$sessionVars = [ |
183
|
|
|
'NLS_DATE_FORMAT' => $format, |
184
|
|
|
'NLS_TIMESTAMP_FORMAT' => $format, |
185
|
|
|
]; |
186
|
|
|
|
187
|
|
|
return $this->setSessionVars($sessionVars); |
188
|
|
|
} |
189
|
|
|
|
190
|
|
|
/** |
191
|
|
|
* Get doctrine connection. |
192
|
|
|
* |
193
|
|
|
* @return \Doctrine\DBAL\Connection |
194
|
|
|
*/ |
195
|
|
|
public function getDoctrineConnection() |
196
|
|
|
{ |
197
|
|
|
if (is_null($this->doctrineConnection)) { |
198
|
|
|
$data = ['pdo' => $this->getPdo(), 'user' => $this->getConfig('username')]; |
199
|
|
|
$this->doctrineConnection = new DoctrineConnection( |
200
|
|
|
$data, |
201
|
|
|
$this->getDoctrineDriver() |
202
|
|
|
); |
203
|
|
|
} |
204
|
|
|
|
205
|
|
|
return $this->doctrineConnection; |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
/** |
209
|
|
|
* Get doctrine driver. |
210
|
|
|
* |
211
|
|
|
* @return \Doctrine\DBAL\Driver\OCI8\Driver |
212
|
|
|
*/ |
213
|
|
|
protected function getDoctrineDriver() |
214
|
|
|
{ |
215
|
|
|
return new DoctrineDriver(); |
216
|
|
|
} |
217
|
|
|
|
218
|
|
|
/** |
219
|
|
|
* Execute a PL/SQL Function and return its value. |
220
|
|
|
* Usage: DB::executeFunction('function_name(:binding_1,:binding_n)', [':binding_1' => 'hi', ':binding_n' => |
221
|
|
|
* 'bye'], PDO::PARAM_LOB). |
222
|
|
|
* |
223
|
|
|
* @param string $functionName |
224
|
|
|
* @param array $bindings (kvp array) |
225
|
|
|
* @param int $returnType (PDO::PARAM_*) |
226
|
|
|
* @param int $length |
227
|
|
|
* @return mixed $returnType |
228
|
|
|
*/ |
229
|
|
|
public function executeFunction($functionName, array $bindings = [], $returnType = PDO::PARAM_STR, $length = null) |
230
|
|
|
{ |
231
|
|
|
$stmt = $this->createStatementFromFunction($functionName, $bindings); |
232
|
|
|
|
233
|
|
|
$stmt = $this->addBindingsToStatement($stmt, $bindings); |
234
|
|
|
|
235
|
|
|
$stmt->bindParam(':result', $result, $returnType, $length); |
236
|
|
|
$stmt->execute(); |
237
|
|
|
|
238
|
|
|
return $result; |
239
|
|
|
} |
240
|
|
|
|
241
|
|
|
/** |
242
|
|
|
* Execute a PL/SQL Procedure and return its results. |
243
|
|
|
* |
244
|
|
|
* Usage: DB::executeProcedure($procedureName, $bindings). |
245
|
|
|
* $bindings looks like: |
246
|
|
|
* $bindings = [ |
247
|
|
|
* 'p_userid' => $id |
248
|
|
|
* ]; |
249
|
|
|
* |
250
|
|
|
* @param string $procedureName |
251
|
|
|
* @param array $bindings |
252
|
|
|
* @return bool |
253
|
|
|
*/ |
254
|
|
|
public function executeProcedure($procedureName, array $bindings = []) |
255
|
|
|
{ |
256
|
|
|
$stmt = $this->createStatementFromProcedure($procedureName, $bindings); |
257
|
|
|
|
258
|
|
|
$stmt = $this->addBindingsToStatement($stmt, $bindings); |
259
|
|
|
|
260
|
|
|
return $stmt->execute(); |
261
|
|
|
} |
262
|
|
|
|
263
|
|
|
/** |
264
|
|
|
* Execute a PL/SQL Procedure and return its cursor result. |
265
|
|
|
* Usage: DB::executeProcedureWithCursor($procedureName, $bindings). |
266
|
|
|
* |
267
|
|
|
* https://docs.oracle.com/cd/E17781_01/appdev.112/e18555/ch_six_ref_cur.htm#TDPPH218 |
268
|
|
|
* |
269
|
|
|
* @param string $procedureName |
270
|
|
|
* @param array $bindings |
271
|
|
|
* @param string $cursorName |
272
|
|
|
* @return array |
273
|
|
|
*/ |
274
|
|
|
public function executeProcedureWithCursor($procedureName, array $bindings = [], $cursorName = ':cursor') |
275
|
|
|
{ |
276
|
|
|
$stmt = $this->createStatementFromProcedure($procedureName, $bindings, $cursorName); |
277
|
|
|
|
278
|
|
|
$stmt = $this->addBindingsToStatement($stmt, $bindings); |
279
|
|
|
|
280
|
|
|
$cursor = null; |
281
|
|
|
$stmt->bindParam($cursorName, $cursor, PDO::PARAM_STMT); |
282
|
|
|
$stmt->execute(); |
283
|
|
|
|
284
|
|
|
$statement = new Statement($cursor, $this->getPdo(), $this->getPdo()->getOptions()); |
|
|
|
|
285
|
|
|
$statement->execute(); |
286
|
|
|
$results = $statement->fetchAll(PDO::FETCH_OBJ); |
287
|
|
|
$statement->closeCursor(); |
288
|
|
|
|
289
|
|
|
return $results; |
290
|
|
|
} |
291
|
|
|
|
292
|
|
|
/** |
293
|
|
|
* Creates sql command to run a procedure with bindings. |
294
|
|
|
* |
295
|
|
|
* @param string $procedureName |
296
|
|
|
* @param array $bindings |
297
|
|
|
* @param string|bool $cursor |
298
|
|
|
* @return string |
299
|
|
|
*/ |
300
|
|
|
public function createSqlFromProcedure($procedureName, array $bindings, $cursor = false) |
301
|
|
|
{ |
302
|
|
|
$paramsString = implode(',', array_map(function ($param) { |
303
|
|
|
return ':' . $param; |
304
|
|
|
}, array_keys($bindings))); |
305
|
|
|
|
306
|
|
|
$prefix = count($bindings) ? ',' : ''; |
307
|
|
|
$cursor = $cursor ? $prefix . $cursor : null; |
308
|
|
|
|
309
|
|
|
return sprintf('begin %s(%s%s); end;', $procedureName, $paramsString, $cursor); |
310
|
|
|
} |
311
|
|
|
|
312
|
|
|
/** |
313
|
|
|
* Creates statement from procedure. |
314
|
|
|
* |
315
|
|
|
* @param string $procedureName |
316
|
|
|
* @param array $bindings |
317
|
|
|
* @param string|bool $cursorName |
318
|
|
|
* @return PDOStatement |
319
|
|
|
*/ |
320
|
|
|
public function createStatementFromProcedure($procedureName, array $bindings, $cursorName = false) |
321
|
|
|
{ |
322
|
|
|
$sql = $this->createSqlFromProcedure($procedureName, $bindings, $cursorName); |
323
|
|
|
|
324
|
|
|
return $this->getPdo()->prepare($sql); |
325
|
|
|
} |
326
|
|
|
|
327
|
|
|
/** |
328
|
|
|
* Create statement from function. |
329
|
|
|
* |
330
|
|
|
* @param string $functionName |
331
|
|
|
* @param array $bindings |
332
|
|
|
* @return PDOStatement |
333
|
|
|
*/ |
334
|
|
|
public function createStatementFromFunction($functionName, array $bindings) |
335
|
|
|
{ |
336
|
|
|
$bindings = $bindings ? ':' . implode(', :', array_keys($bindings)) : ''; |
337
|
|
|
|
338
|
|
|
$sql = sprintf('begin :result := %s(%s); end;', $functionName, $bindings); |
339
|
|
|
|
340
|
|
|
return $this->getPdo()->prepare($sql); |
341
|
|
|
} |
342
|
|
|
|
343
|
|
|
/** |
344
|
|
|
* Bind values to their parameters in the given statement. |
345
|
|
|
* |
346
|
|
|
* @param PDOStatement $statement |
347
|
|
|
* @param array $bindings |
348
|
|
|
*/ |
349
|
|
|
public function bindValues($statement, $bindings) |
350
|
|
|
{ |
351
|
|
|
foreach ($bindings as $key => $value) { |
352
|
|
|
$statement->bindParam($key, $bindings[$key]); |
353
|
|
|
} |
354
|
|
|
} |
355
|
|
|
|
356
|
|
|
/** |
357
|
|
|
* Get the default query grammar instance. |
358
|
|
|
* |
359
|
|
|
* @return \Illuminate\Database\Grammar|\Yajra\Oci8\Query\Grammars\OracleGrammar |
360
|
|
|
*/ |
361
|
|
|
protected function getDefaultQueryGrammar() |
362
|
|
|
{ |
363
|
|
|
return $this->withTablePrefix(new QueryGrammar()); |
364
|
|
|
} |
365
|
|
|
|
366
|
|
|
/** |
367
|
|
|
* Set the table prefix and return the grammar. |
368
|
|
|
* |
369
|
|
|
* @param \Illuminate\Database\Grammar|\Yajra\Oci8\Query\Grammars\OracleGrammar|\Yajra\Oci8\Schema\Grammars\OracleGrammar $grammar |
370
|
|
|
* @return \Illuminate\Database\Grammar |
371
|
|
|
*/ |
372
|
|
|
public function withTablePrefix(Grammar $grammar) |
373
|
|
|
{ |
374
|
|
|
return $this->withSchemaPrefix(parent::withTablePrefix($grammar)); |
375
|
|
|
} |
376
|
|
|
|
377
|
|
|
/** |
378
|
|
|
* Set the schema prefix and return the grammar. |
379
|
|
|
* |
380
|
|
|
* @param \Illuminate\Database\Grammar|\Yajra\Oci8\Query\Grammars\OracleGrammar|\Yajra\Oci8\Schema\Grammars\OracleGrammar $grammar |
381
|
|
|
* @return \Illuminate\Database\Grammar |
382
|
|
|
*/ |
383
|
|
|
public function withSchemaPrefix(Grammar $grammar) |
384
|
|
|
{ |
385
|
|
|
$grammar->setSchemaPrefix($this->getConfigSchemaPrefix()); |
|
|
|
|
386
|
|
|
|
387
|
|
|
return $grammar; |
388
|
|
|
} |
389
|
|
|
|
390
|
|
|
/** |
391
|
|
|
* Get config schema prefix. |
392
|
|
|
* |
393
|
|
|
* @return string |
394
|
|
|
*/ |
395
|
|
|
protected function getConfigSchemaPrefix() |
396
|
|
|
{ |
397
|
|
|
return isset($this->config['prefix_schema']) ? $this->config['prefix_schema'] : ''; |
398
|
|
|
} |
399
|
|
|
|
400
|
|
|
/** |
401
|
|
|
* Get the default schema grammar instance. |
402
|
|
|
* |
403
|
|
|
* @return \Illuminate\Database\Grammar|\Yajra\Oci8\Schema\Grammars\OracleGrammar |
404
|
|
|
*/ |
405
|
|
|
protected function getDefaultSchemaGrammar() |
406
|
|
|
{ |
407
|
|
|
return $this->withTablePrefix(new SchemaGrammar()); |
408
|
|
|
} |
409
|
|
|
|
410
|
|
|
/** |
411
|
|
|
* Get the default post processor instance. |
412
|
|
|
* |
413
|
|
|
* @return \Yajra\Oci8\Query\Processors\OracleProcessor |
414
|
|
|
*/ |
415
|
|
|
protected function getDefaultPostProcessor() |
416
|
|
|
{ |
417
|
|
|
return new Processor(); |
418
|
|
|
} |
419
|
|
|
|
420
|
|
|
/** |
421
|
|
|
* Add bindings to statement. |
422
|
|
|
* |
423
|
|
|
* @param array $bindings |
424
|
|
|
* @param PDOStatement $stmt |
425
|
|
|
* @return PDOStatement |
426
|
|
|
*/ |
427
|
|
|
public function addBindingsToStatement(PDOStatement $stmt, array $bindings) |
428
|
|
|
{ |
429
|
|
|
foreach ($bindings as $key => &$binding) { |
430
|
|
|
|
431
|
|
|
//defaults |
432
|
|
|
$value = &$binding; |
433
|
|
|
$type = PDO::PARAM_STR; |
434
|
|
|
$length = null; |
435
|
|
|
|
436
|
|
|
// extended array syntax for bindings |
437
|
|
|
if (is_array($binding)) { |
438
|
|
|
$value = &$binding['value']; |
439
|
|
|
$type = array_key_exists('type', $binding) ? $binding['type'] : PDO::PARAM_STR; |
440
|
|
|
$length = array_key_exists('length', $binding) ? $binding['length'] : null; |
441
|
|
|
} |
442
|
|
|
|
443
|
|
|
$stmt->bindParam(':' . $key, $value, $type, $length); |
444
|
|
|
} |
445
|
|
|
|
446
|
|
|
return $stmt; |
447
|
|
|
} |
448
|
|
|
|
449
|
|
|
/** |
450
|
|
|
* Determine if the given exception was caused by a lost connection. |
451
|
|
|
* |
452
|
|
|
* @param \Exception $e |
453
|
|
|
* @return bool |
454
|
|
|
*/ |
455
|
|
|
public function causedByLostConnection(Exception $e) |
456
|
|
|
{ |
457
|
|
|
if (parent::causedByLostConnection($e)) { |
458
|
|
|
return true; |
459
|
|
|
} |
460
|
|
|
|
461
|
|
|
$lostConnectionErrors = [ |
462
|
|
|
'ORA-03113', //End-of-file on communication channel |
463
|
|
|
'ORA-03114', //Not Connected to Oracle |
464
|
|
|
'ORA-03135', //Connection lost contact |
465
|
|
|
'ORA-12170', //Connect timeout occurred |
466
|
|
|
'ORA-12537', //Connection closed |
467
|
|
|
'ORA-27146', //Post/wait initialization failed |
468
|
|
|
'ORA-25408', //Can not safely replay call |
469
|
|
|
'ORA-56600', //Illegal Call |
470
|
|
|
]; |
471
|
|
|
|
472
|
|
|
$additionalErrors = null; |
473
|
|
|
|
474
|
|
|
$options = isset($this->config['options']) ? $this->config['options'] : []; |
475
|
|
|
if (array_key_exists(static::RECONNECT_ERRORS, $options)) { |
476
|
|
|
$additionalErrors = $this->config['options'][static::RECONNECT_ERRORS]; |
477
|
|
|
} |
478
|
|
|
|
479
|
|
|
if (is_array($additionalErrors)) { |
480
|
|
|
$lostConnectionErrors = array_merge($lostConnectionErrors, |
481
|
|
|
$this->config['options'][static::RECONNECT_ERRORS]); |
482
|
|
|
} |
483
|
|
|
|
484
|
|
|
return Str::contains($e->getMessage(), $lostConnectionErrors); |
485
|
|
|
} |
486
|
|
|
} |
487
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.