Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 188 |
|
0.00% |
0 / 22 |
CRAP | |
0.00% |
0 / 1 |
Oracle | |
0.00% |
0 / 188 |
|
0.00% |
0 / 22 |
3660 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
6 | |||
getHandle | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
__destruct | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
6 | |||
prepare | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
prepRowId | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
getDataTypeConstant | |
0.00% |
0 / 15 |
|
0.00% |
0 / 1 |
110 | |||
bindParam | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
6 | |||
returnParam | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
6 | |||
exec | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
commit | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
rollback | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
free | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
simpleSelect | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
12 | |||
simpleDelete | |
0.00% |
0 / 24 |
|
0.00% |
0 / 1 |
20 | |||
simpleInsert | |
0.00% |
0 / 30 |
|
0.00% |
0 / 1 |
42 | |||
simpleSql | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
12 | |||
clearError | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
handleError | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
getLastError | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getLastErrorType | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getLastSql | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getHtmlError | |
0.00% |
0 / 31 |
|
0.00% |
0 / 1 |
72 |
1 | <?php |
2 | |
3 | /** |
4 | * Oracle support code for VTLS Virtua Driver |
5 | * |
6 | * PHP version 8 |
7 | * |
8 | * Copyright (C) University of Southern Queensland 2008. |
9 | * |
10 | * This program is free software; you can redistribute it and/or modify |
11 | * it under the terms of the GNU General Public License version 2, |
12 | * as published by the Free Software Foundation. |
13 | * |
14 | * This program is distributed in the hope that it will be useful, |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 | * GNU General Public License for more details. |
18 | * |
19 | * You should have received a copy of the GNU General Public License |
20 | * along with this program; if not, write to the Free Software |
21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
22 | * |
23 | * @category VuFind |
24 | * @package Oracle |
25 | * @author Greg Pendlebury <vufind-tech@lists.sourceforge.net> |
26 | * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License |
27 | * @link https://vufind.org/wiki/development Wiki |
28 | */ |
29 | |
30 | namespace VuFind\Connection; |
31 | |
32 | use function count; |
33 | use function is_array; |
34 | |
35 | /** |
36 | * Oracle support code for VTLS Virtua Driver |
37 | * |
38 | * @category VuFind |
39 | * @package Oracle |
40 | * @author Greg Pendlebury <vufind-tech@lists.sourceforge.net> |
41 | * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License |
42 | * @link https://vufind.org/wiki/development Wiki |
43 | */ |
44 | class Oracle |
45 | { |
46 | /** |
47 | * Database Handle |
48 | * |
49 | * @var resource |
50 | */ |
51 | protected $dbHandle; |
52 | |
53 | /** |
54 | * Error information - message |
55 | * |
56 | * @var string |
57 | */ |
58 | protected $lastError; |
59 | |
60 | /** |
61 | * Error information - type |
62 | * |
63 | * @var string |
64 | */ |
65 | protected $lastErrorType; |
66 | |
67 | /** |
68 | * Error information - bind params |
69 | * |
70 | * @var array |
71 | */ |
72 | protected $lastErrorFields; |
73 | |
74 | /** |
75 | * Error information - SQL attempted |
76 | * |
77 | * @var string |
78 | */ |
79 | protected $lastSql; |
80 | |
81 | /** |
82 | * Constructor -- connect to database. |
83 | * |
84 | * @param string $username Username for connection |
85 | * @param string $password Password for connection |
86 | * @param string $tns TNS specification for connection |
87 | */ |
88 | public function __construct($username, $password, $tns) |
89 | { |
90 | $this->clearError(); |
91 | $tmp = error_reporting(1); |
92 | if ($this->dbHandle = @oci_connect($username, $password, $tns)) { |
93 | error_reporting($tmp); |
94 | } else { |
95 | error_reporting($tmp); |
96 | $this->handleError('connect', oci_error()); |
97 | throw new \Exception('Oracle connection problem.'); |
98 | } |
99 | } |
100 | |
101 | /** |
102 | * Get access to the Oracle handle. |
103 | * |
104 | * @return resource |
105 | */ |
106 | public function getHandle() |
107 | { |
108 | return $this->dbHandle; |
109 | } |
110 | |
111 | /** |
112 | * Destructor |
113 | * |
114 | * @return void |
115 | */ |
116 | public function __destruct() |
117 | { |
118 | // Close the OCI connection unless we failed to establish it: |
119 | if ($this->dbHandle !== false) { |
120 | oci_close($this->dbHandle); |
121 | } |
122 | } |
123 | |
124 | /** |
125 | * Wrapper around oci_parse. |
126 | * |
127 | * @param string $sql SQL statement to prepare. |
128 | * |
129 | * @return mixed SQL resource on success, boolean false otherwise. |
130 | */ |
131 | public function prepare($sql) |
132 | { |
133 | if ($parsed = @oci_parse($this->dbHandle, $sql)) { |
134 | return $parsed; |
135 | } else { |
136 | $this->handleError('parsing', oci_error($this->dbHandle), $sql); |
137 | return false; |
138 | } |
139 | } |
140 | |
141 | /** |
142 | * Wrapper around oci_new_descriptor. |
143 | * |
144 | * @return mixed New descriptor on success, boolean false otherwise. |
145 | */ |
146 | public function prepRowId() |
147 | { |
148 | if ($new_id = @oci_new_descriptor($this->dbHandle, OCI_D_ROWID)) { |
149 | return $new_id; |
150 | } else { |
151 | $this->handleError('new_descriptor', oci_error($this->dbHandle)); |
152 | return false; |
153 | } |
154 | } |
155 | |
156 | /** |
157 | * Convert data type name into constant |
158 | * |
159 | * @param string $data_type Data type (string, integer, float, long, date, |
160 | * row_id, clob, or blob) |
161 | * |
162 | * @return int |
163 | */ |
164 | protected function getDataTypeConstant($data_type) |
165 | { |
166 | switch ($data_type) { |
167 | case 'integer': |
168 | return SQLT_INT; |
169 | case 'float': |
170 | return SQLT_FLT; |
171 | case 'long': |
172 | return SQLT_LNG; |
173 | case 'row_id': |
174 | return SQLT_RDD; |
175 | case 'clob': |
176 | return SQLT_CLOB; |
177 | case 'blob': |
178 | return SQLT_BLOB; |
179 | case 'string': |
180 | case 'date': |
181 | default: |
182 | // Date and string are redundant since default is varchar, |
183 | // but they're here for clarity. |
184 | return SQLT_CHR; |
185 | } |
186 | } |
187 | |
188 | /** |
189 | * Wrapper around oci_bind_by_name. |
190 | * |
191 | * @param resource $parsed Result returned by prepare() method. |
192 | * @param string $place_holder The colon-prefixed bind variable placeholder |
193 | * used in the statement. |
194 | * @param string $data The PHP variable to be associated with |
195 | * $place_holder |
196 | * @param string $data_type The type of $data (string, integer, float, |
197 | * long, date, row_id, clob, or blob) |
198 | * @param int $length Sets the maximum length for the data. If you |
199 | * set it to -1, this function will use the current length of variable to set |
200 | * the maximum length. |
201 | * |
202 | * @return bool |
203 | */ |
204 | public function bindParam( |
205 | $parsed, |
206 | $place_holder, |
207 | $data, |
208 | $data_type = 'string', |
209 | $length = -1 |
210 | ) { |
211 | $success = @oci_bind_by_name( |
212 | $parsed, |
213 | $place_holder, |
214 | $data, |
215 | $length, |
216 | $this->getDataTypeConstant($data_type) |
217 | ); |
218 | if ($success) { |
219 | return true; |
220 | } else { |
221 | $this->handleError('binding', oci_error()); |
222 | return false; |
223 | } |
224 | } |
225 | |
226 | /** |
227 | * Same as bindParam(), but variable is parsed by reference to allow for correct |
228 | * functioning of the 'RETURNING' sql statement. Annoying, but putting it in two |
229 | * separate functions allows the user to pass string literals into bindParam |
230 | * without a fatal error. |
231 | * |
232 | * @param resource $parsed Result returned by prepare() method. |
233 | * @param string $place_holder The colon-prefixed bind variable placeholder |
234 | * used in the statement. |
235 | * @param string $data The PHP variable to be associated with |
236 | * $place_holder |
237 | * @param string $data_type The type of $data (string, integer, float, |
238 | * long, date, row_id, clob, or blob) |
239 | * @param int $length Sets the maximum length for the data. If you |
240 | * set it to -1, this function will use the current length of variable to set |
241 | * the maximum length. |
242 | * |
243 | * @return bool |
244 | */ |
245 | public function returnParam( |
246 | $parsed, |
247 | $place_holder, |
248 | &$data, |
249 | $data_type = 'string', |
250 | $length = -1 |
251 | ) { |
252 | $success = @oci_bind_by_name( |
253 | $parsed, |
254 | $place_holder, |
255 | $data, |
256 | $length, |
257 | $this->getDataTypeConstant($data_type) |
258 | ); |
259 | if ($success) { |
260 | return true; |
261 | } else { |
262 | $this->handleError('binding', oci_error()); |
263 | return false; |
264 | } |
265 | } |
266 | |
267 | /** |
268 | * Wrapper around oci_execute. |
269 | * |
270 | * @param resource $parsed Result returned by prepare() method. |
271 | * |
272 | * @return bool |
273 | */ |
274 | public function exec($parsed) |
275 | { |
276 | // OCI_DEFAULT == DO NOT COMMIT!!! |
277 | if (@oci_execute($parsed, OCI_DEFAULT)) { |
278 | return true; |
279 | } else { |
280 | $this->handleError('executing', oci_error($parsed)); |
281 | return false; |
282 | } |
283 | } |
284 | |
285 | /** |
286 | * Wrapper around oci_commit. |
287 | * |
288 | * @return bool |
289 | */ |
290 | public function commit() |
291 | { |
292 | if (@oci_commit($this->dbHandle)) { |
293 | return true; |
294 | } else { |
295 | $this->handleError('commit', oci_error($this->dbHandle)); |
296 | return false; |
297 | } |
298 | } |
299 | |
300 | /** |
301 | * Wrapper around oci_rollback. |
302 | * |
303 | * @return bool |
304 | */ |
305 | public function rollback() |
306 | { |
307 | if (@oci_rollback($this->dbHandle)) { |
308 | return true; |
309 | } else { |
310 | $this->handleError('rollback', oci_error($this->dbHandle)); |
311 | return false; |
312 | } |
313 | } |
314 | |
315 | /** |
316 | * Wrapper around oci_free_statement. |
317 | * |
318 | * @param resource $parsed Result returned by prepare() method. |
319 | * |
320 | * @return bool |
321 | */ |
322 | public function free($parsed) |
323 | { |
324 | if (@oci_free_statement($parsed)) { |
325 | return true; |
326 | } else { |
327 | $this->handleError('free', oci_error($this->dbHandle)); |
328 | return false; |
329 | } |
330 | } |
331 | |
332 | /** |
333 | * Execute a SQL statement and return the results. |
334 | * |
335 | * @param string $sql SQL to execute |
336 | * @param array $fields Bind parameters (optional) |
337 | * |
338 | * @return array|bool Results on success, false on error. |
339 | */ |
340 | public function simpleSelect($sql, $fields = []) |
341 | { |
342 | $stmt = $this->prepare($sql); |
343 | foreach ($fields as $field => $datum) { |
344 | [$column, $type] = explode(':', $field); |
345 | $this->bindParam($stmt, ':' . $column, $datum, $type); |
346 | } |
347 | |
348 | if ($this->exec($stmt)) { |
349 | oci_fetch_all($stmt, $return_array, 0, -1, OCI_FETCHSTATEMENT_BY_ROW); |
350 | $this->free($stmt); |
351 | return $return_array; |
352 | } else { |
353 | $this->lastErrorFields = $fields; |
354 | $this->free($stmt); |
355 | return false; |
356 | } |
357 | } |
358 | |
359 | /** |
360 | * Delete row(s) from a table. |
361 | * |
362 | * @param string $table Table to update. |
363 | * @param array $fields Fields to use to match rows to delete. |
364 | * |
365 | * @return bool |
366 | */ |
367 | public function simpleDelete($table, $fields = []) |
368 | { |
369 | $types = []; |
370 | $data = []; |
371 | $clauses = []; |
372 | |
373 | // Split all the fields up into arrays |
374 | foreach ($fields as $field => $datum) { |
375 | [$column, $type] = explode(':', $field); |
376 | $types[$column] = $type; |
377 | $data[$column] = $datum; |
378 | $clauses[] = "$column = :$column"; |
379 | } |
380 | |
381 | // Prepare the SQL for child table - turn the columns in placeholders for |
382 | // the bind |
383 | $sql = "DELETE FROM $table WHERE " . implode(' AND ', $clauses); |
384 | $delete = $this->prepare($sql); |
385 | |
386 | // Bind Variables |
387 | foreach (array_keys($data) as $column) { |
388 | $this->bindParam( |
389 | $delete, |
390 | ':' . $column, |
391 | $data[$column], |
392 | $types[$column] |
393 | ); |
394 | } |
395 | |
396 | // Execute |
397 | if ($this->exec($delete)) { |
398 | $this->commit(); |
399 | $this->free($delete); |
400 | return true; |
401 | } else { |
402 | $this->lastErrorFields = $fields; |
403 | $this->free($delete); |
404 | return false; |
405 | } |
406 | } |
407 | |
408 | /** |
409 | * Insert a row into a table. |
410 | * |
411 | * @param string $table Table to append to. |
412 | * @param array $fields Data to write to table. |
413 | * |
414 | * @return bool |
415 | */ |
416 | public function simpleInsert($table, $fields = []) |
417 | { |
418 | $types = []; |
419 | $data = []; |
420 | $columns = []; |
421 | $values = []; |
422 | |
423 | // Split all the fields up into arrays |
424 | foreach ($fields as $field => $datum) { |
425 | $tmp = explode(':', $field); |
426 | $column = array_shift($tmp); |
427 | |
428 | // For binding |
429 | $types[$column] = array_shift($tmp); |
430 | $data[$column] = $datum; |
431 | |
432 | // For building the sql |
433 | $columns[] = $column; |
434 | // Dates are special |
435 | if (count($tmp) > 0 && null !== $datum) { |
436 | $values[] = "TO_DATE(:$column, '" . implode(':', $tmp) . "')"; |
437 | } else { |
438 | $values[] = ":$column"; |
439 | } |
440 | } |
441 | |
442 | $sql = "INSERT INTO $table (" . implode(', ', $columns) . ') VALUES (' . |
443 | implode(', ', $values) . ')'; |
444 | $insert = $this->prepare($sql); |
445 | |
446 | // Bind Variables |
447 | foreach (array_keys($data) as $column) { |
448 | $this->bindParam( |
449 | $insert, |
450 | ':' . $column, |
451 | $data[$column], |
452 | $types[$column] |
453 | ); |
454 | } |
455 | |
456 | // Execute |
457 | if ($this->exec($insert)) { |
458 | $this->commit(); |
459 | $this->free($insert); |
460 | return true; |
461 | } else { |
462 | $this->lastErrorFields = $fields; |
463 | $this->free($insert); |
464 | return false; |
465 | } |
466 | } |
467 | |
468 | /** |
469 | * Execute a simple SQL statement. |
470 | * |
471 | * @param string $sql SQL to execute |
472 | * @param array $fields Bind parameters (optional) |
473 | * |
474 | * @return bool |
475 | */ |
476 | public function simpleSql($sql, $fields = []) |
477 | { |
478 | $stmt = $this->prepare($sql); |
479 | foreach ($fields as $field => $datum) { |
480 | [$column, $type] = explode(':', $field); |
481 | $this->bindParam($stmt, ':' . $column, $datum, $type); |
482 | } |
483 | if ($this->exec($stmt)) { |
484 | $this->commit(); |
485 | $this->free($stmt); |
486 | return true; |
487 | } else { |
488 | $this->lastErrorFields = $fields; |
489 | $this->free($stmt); |
490 | return false; |
491 | } |
492 | } |
493 | |
494 | /** |
495 | * Clear out internal error tracking details. |
496 | * |
497 | * @return void |
498 | */ |
499 | protected function clearError() |
500 | { |
501 | $this->lastError = null; |
502 | $this->lastErrorType = null; |
503 | $this->lastErrorFields = null; |
504 | $this->lastSql = null; |
505 | } |
506 | |
507 | /** |
508 | * Store information about an error. |
509 | * |
510 | * @param string $type Type of error |
511 | * @param string $error Detailed error message |
512 | * @param string $sql SQL statement that caused error |
513 | * |
514 | * @return void |
515 | */ |
516 | protected function handleError($type, $error, $sql = '') |
517 | { |
518 | // All we are doing at the moment is storing it |
519 | $this->lastError = $error; |
520 | $this->lastErrorType = $type; |
521 | $this->lastSql = $sql; |
522 | } |
523 | |
524 | /** |
525 | * Error Retrieval -- last error message. |
526 | * |
527 | * @return string |
528 | */ |
529 | public function getLastError() |
530 | { |
531 | return $this->lastError; |
532 | } |
533 | |
534 | /** |
535 | * Error Retrieval -- last error type. |
536 | * |
537 | * @return string |
538 | */ |
539 | public function getLastErrorType() |
540 | { |
541 | return $this->lastErrorType; |
542 | } |
543 | |
544 | /** |
545 | * Error Retrieval -- SQL that triggered last error. |
546 | * |
547 | * @return string |
548 | */ |
549 | public function getLastSql() |
550 | { |
551 | return $this->lastSql; |
552 | } |
553 | |
554 | /** |
555 | * Error Retrieval -- full details formatted as HTML. |
556 | * |
557 | * @return string |
558 | */ |
559 | public function getHtmlError() |
560 | { |
561 | if ($this->lastError == null) { |
562 | return 'No error found!'; |
563 | } |
564 | |
565 | // Generic stuff |
566 | $output = "<b>ORACLE ERROR</b><br/>\n"; |
567 | $output .= "Oracle '" . $this->lastErrorType . "' Error<br />\n"; |
568 | $output .= "=============<br />\n"; |
569 | foreach ($this->lastError as $key => $value) { |
570 | $output .= "($key) => $value<br />\n"; |
571 | } |
572 | |
573 | // Anything special for this error type? |
574 | switch ($this->lastErrorType) { |
575 | case 'parsing': |
576 | $output .= "=============<br />\n"; |
577 | $output .= "Offset into SQL:<br />\n"; |
578 | $output .= |
579 | substr($this->lastError['sqltext'], $this->lastError['offset']) . |
580 | "\n"; |
581 | break; |
582 | case 'executing': |
583 | $output .= "=============<br />\n"; |
584 | $output .= "Offset into SQL:<br />\n"; |
585 | $output .= |
586 | substr($this->lastError['sqltext'], $this->lastError['offset']) . |
587 | "<br />\n"; |
588 | if (count($this->lastErrorFields) > 0) { |
589 | $output .= "=============<br />\n"; |
590 | $output .= "Bind Variables:<br />\n"; |
591 | foreach ($this->lastErrorFields as $k => $l) { |
592 | if (is_array($l)) { |
593 | $output .= "$k => (" . implode(', ', $l) . ")<br />\n"; |
594 | } else { |
595 | $output .= "$k => $l<br />\n"; |
596 | } |
597 | } |
598 | } |
599 | break; |
600 | } |
601 | |
602 | $this->clearError(); |
603 | return $output; |
604 | } |
605 | } |