Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
oracleconnection.php
1<?php
2
3namespace Bitrix\Main\DB;
4
7
15{
16 private $transaction = OCI_COMMIT_ON_SUCCESS;
17
18 protected $lastInsertedId;
19
20 /**********************************************************
21 * SqlHelper
22 **********************************************************/
23
24 protected function createSqlHelper()
25 {
26 return new OracleSqlHelper($this);
27 }
28
29 /***********************************************************
30 * Connection and disconnection
31 ***********************************************************/
32
41 protected function connectInternal()
42 {
43 if ($this->isConnected)
44 return;
45
46 if (($this->options & self::PERSISTENT) != 0)
47 $connection = oci_pconnect($this->login, $this->password, $this->database);
48 else
49 $connection = oci_new_connect($this->login, $this->password, $this->database);
50
51 if (!$connection)
52 throw new ConnectionException('Oracle connect error', $this->getErrorMessage());
53
54 $this->isConnected = true;
55 $this->resource = $connection;
56
57 $this->afterConnected();
58 }
59
66 protected function disconnectInternal()
67 {
68 if (!$this->isConnected)
69 return;
70
71 $this->isConnected = false;
72 oci_close($this->resource);
73 }
74
75 /*********************************************************
76 * Query
77 *********************************************************/
78
82 protected function queryInternal($sql, array $binds = null, \Bitrix\Main\Diag\SqlTrackerQuery $trackerQuery = null)
83 {
84 $this->connectInternal();
85
86 $trackerQuery?->startQuery($sql, $binds);
87
88 $result = oci_parse($this->resource, $sql);
89
90 if (!$result)
91 {
92 $trackerQuery?->finishQuery();
93
94 throw new SqlQueryException("", $this->getErrorMessage($this->resource), $sql);
95 }
96
97 $executionMode = $this->transaction;
98
100 $clob = array();
101
102 if (!empty($binds))
103 {
104 $executionMode = OCI_DEFAULT;
105 foreach ($binds as $key => $val)
106 {
107 $clob[$key] = oci_new_descriptor($this->resource);
108 oci_bind_by_name($result, ":".$key, $clob[$key], -1, OCI_B_CLOB);
109 }
110 }
111
112 if (!oci_execute($result, $executionMode))
113 {
114 $trackerQuery?->finishQuery();
115
116 throw new SqlQueryException("", $this->getErrorMessage($result), $sql);
117 }
118
119 if (!empty($binds))
120 {
121 if (oci_num_rows($result) > 0)
122 {
123 foreach ($binds as $key => $val)
124 {
125 if($clob[$key])
126 {
127 $clob[$key]->save($val);
128 }
129 }
130 }
131
132 if ($this->transaction == OCI_COMMIT_ON_SUCCESS)
133 {
134 oci_commit($this->resource);
135 }
136
137 foreach ($binds as $key => $val)
138 {
139 if($clob[$key])
140 {
141 $clob[$key]->free();
142 }
143 }
144 }
145
146 $trackerQuery?->finishQuery();
147
148 $this->lastQueryResult = $result;
149
150 return $result;
151 }
152
156 protected function createResult($result, \Bitrix\Main\Diag\SqlTrackerQuery $trackerQuery = null)
157 {
158 return new OracleResult($result, $this, $trackerQuery);
159 }
160
164 public function query($sql)
165 {
166 list($sql, $binds, $offset, $limit) = self::parseQueryFunctionArgs(func_get_args());
167
168 if (!empty($binds))
169 {
170 $binds1 = $binds2 = "";
171 foreach ($binds as $key => $value)
172 {
173 if ($value <> '')
174 {
175 if ($binds1 != "")
176 {
177 $binds1 .= ",";
178 $binds2 .= ",";
179 }
180
181 $binds1 .= $key;
182 $binds2 .= ":".$key;
183 }
184 }
185
186 if ($binds1 != "")
187 $sql .= " RETURNING ".$binds1." INTO ".$binds2;
188 }
189
190 return parent::query($sql, $binds, $offset, $limit);
191 }
192
196 public function add($tableName, array $data, $identity = "ID")
197 {
198 if($identity !== null && !isset($data[$identity]))
199 $data[$identity] = $this->getNextId("sq_".$tableName);
200
201 $insert = $this->getSqlHelper()->prepareInsert($tableName, $data);
202
203 $binds = $insert[2];
204
205 $sql =
206 "INSERT INTO ".$tableName."(".$insert[0].") ".
207 "VALUES (".$insert[1].")";
208
209 $this->queryExecute($sql, $binds);
210
211 $this->lastInsertedId = $data[$identity];
212
213 return $data[$identity];
214 }
215
226 public function getNextId($name = "")
227 {
228 $name = preg_replace("/[^A-Za-z0-9_]+/i", "", $name);
229 $name = trim($name);
230
231 if($name == '')
232 throw new \Bitrix\Main\ArgumentNullException("name");
233
234 $sql = "SELECT ".$this->getSqlHelper()->quote($name).".NEXTVAL FROM DUAL";
235
236 $result = $this->query($sql);
237 if ($row = $result->fetch())
238 {
239 return array_shift($row);
240 }
241
242 return null;
243 }
244
248 public function getInsertedId()
249 {
251 }
252
256 public function getAffectedRowsCount()
257 {
258 return oci_num_rows($this->lastQueryResult);
259 }
260
264 public function isTableExists($tableName)
265 {
266 if (empty($tableName))
267 return false;
268
269 $result = $this->queryScalar("
270 SELECT COUNT(TABLE_NAME)
271 FROM USER_TABLES
272 WHERE TABLE_NAME LIKE UPPER('".$this->getSqlHelper()->forSql($tableName)."')
273 ");
274 return ($result > 0);
275 }
276
280 public function isIndexExists($tableName, array $columns)
281 {
282 return $this->getIndexName($tableName, $columns) !== null;
283 }
284
288 public function getIndexName($tableName, array $columns, $strict = false)
289 {
290 if (empty($columns))
291 {
292 return null;
293 }
294
295 $isFunc = false;
296 $indexes = array();
297
298 $result = $this->query("SELECT * FROM USER_IND_COLUMNS WHERE TABLE_NAME = upper('".$this->getSqlHelper()->forSql($tableName)."')");
299 while ($ar = $result->fetch())
300 {
301 $indexes[$ar["INDEX_NAME"]][$ar["COLUMN_POSITION"] - 1] = $ar["COLUMN_NAME"];
302 if (strncmp($ar["COLUMN_NAME"], "SYS_NC", 6) === 0)
303 {
304 $isFunc = true;
305 }
306 }
307
308 if ($isFunc)
309 {
310 $result = $this->query("SELECT * FROM USER_IND_EXPRESSIONS WHERE TABLE_NAME = upper('".$this->getSqlHelper()->forSql($tableName)."')");
311 while ($ar = $result->fetch())
312 {
313 $indexes[$ar["INDEX_NAME"]][$ar["COLUMN_POSITION"] - 1] = $ar["COLUMN_EXPRESSION"];
314 }
315 }
316
317 return static::findIndex($indexes, $columns, $strict);
318 }
319
323 public function getTableFields($tableName)
324 {
325 if (!isset($this->tableColumnsCache[$tableName]))
326 {
327 $this->connectInternal();
328
329 $query = $this->queryInternal("SELECT * FROM ".$this->getSqlHelper()->quote($tableName)." WHERE ROWNUM = 0");
330
331 $result = $this->createResult($query);
332
333 $this->tableColumnsCache[$tableName] = $result->getFields();
334 }
335 return $this->tableColumnsCache[$tableName];
336 }
337
341 public function createTable($tableName, $fields, $primary = array(), $autoincrement = array())
342 {
343 $sql = 'CREATE TABLE '.$this->getSqlHelper()->quote($tableName).' (';
344 $sqlFields = array();
345
346 foreach ($fields as $columnName => $field)
347 {
348 if (!($field instanceof ScalarField))
349 {
350 throw new ArgumentException(sprintf(
351 'Field `%s` should be an Entity\ScalarField instance', $columnName
352 ));
353 }
354
355 $realColumnName = $field->getColumnName();
356
357 $sqlFields[] = $this->getSqlHelper()->quote($realColumnName)
358 . ' ' . $this->getSqlHelper()->getColumnTypeByField($field)
359 . ' ' . (in_array($columnName, $primary, true) ? 'NOT NULL' : 'NULL')
360 ;
361 }
362
363 $sql .= join(', ', $sqlFields);
364
365 if (!empty($primary))
366 {
367 foreach ($primary as &$primaryColumn)
368 {
369 $realColumnName = $fields[$primaryColumn]->getColumnName();
370 $primaryColumn = $this->getSqlHelper()->quote($realColumnName);
371 }
372
373 $sql .= ', PRIMARY KEY('.join(', ', $primary).')';
374 }
375
376 $sql .= ')';
377
378 $this->query($sql);
379
380 // autoincrement field
381 if (!empty($autoincrement))
382 {
383 foreach ($autoincrement as $autoincrementColumn)
384 {
385 $autoincrementColumn = $fields[$autoincrementColumn]->getColumnName();
386
387 if ($autoincrementColumn == 'ID')
388 {
389 // old-school hack
390 $aiName = $tableName;
391 }
392 else
393 {
394 $aiName = $tableName.'_'.$autoincrementColumn;
395 }
396
397 $this->query('CREATE SEQUENCE '.$this->getSqlHelper()->quote('sq_'.$aiName));
398
399 $this->query('CREATE OR REPLACE TRIGGER '.$this->getSqlHelper()->quote($aiName.'_insert').'
400 BEFORE INSERT
401 ON '.$this->getSqlHelper()->quote($tableName).'
402 FOR EACH ROW
403 BEGIN
404 IF :NEW.'.$this->getSqlHelper()->quote($autoincrementColumn).' IS NULL THEN
405 SELECT '.$this->getSqlHelper()->quote('sq_'.$aiName).'.NEXTVAL
406 INTO :NEW.'.$this->getSqlHelper()->quote($autoincrementColumn).' FROM dual;
407 END IF;
408 END;'
409 );
410 }
411 }
412 }
413
417 public function renameTable($currentName, $newName)
418 {
419 $this->query('RENAME '.$this->getSqlHelper()->quote($currentName).' TO '.$this->getSqlHelper()->quote($newName));
420
421 // handle auto increment: rename primary sequence for ID
422 // properly we should check PRIMARY fields instead of ID: $aiName = $currentName.'_'.$fieldName, see createTable
423 $aiName = $currentName;
424
425 if ($this->queryScalar("SELECT 1 FROM user_sequences WHERE sequence_name=upper('".$this->getSqlHelper()->forSql('sq_'.$aiName)."')"))
426 {
427 // for fields excpet for ID here should be $newName.'_'.$fieldName, see createTable
428 $newAiName = $newName;
429
430 // rename sequence
431 $this->query('RENAME '.$this->getSqlHelper()->quote('sq_'.$aiName).' TO '.$this->getSqlHelper()->quote('sq_'.$newAiName));
432
433 // recreate trigger
434 $this->query('DROP TRIGGER '.$this->getSqlHelper()->quote($aiName.'_insert'));
435
436 $this->query('CREATE OR REPLACE TRIGGER '.$this->getSqlHelper()->quote($newAiName.'_insert').'
437 BEFORE INSERT
438 ON '.$this->getSqlHelper()->quote($newName).'
439 FOR EACH ROW
440 BEGIN
441 IF :NEW.'.$this->getSqlHelper()->quote('ID').' IS NULL THEN
442 SELECT '.$this->getSqlHelper()->quote('sq_'.$newAiName).'.NEXTVAL
443 INTO :NEW.'.$this->getSqlHelper()->quote('ID').' FROM dual;
444 END IF;
445 END;'
446 );
447 }
448 }
449
453 public function dropTable($tableName)
454 {
455 $this->query('DROP TABLE '.$this->getSqlHelper()->quote($tableName).' CASCADE CONSTRAINTS');
456
457 // handle auto increment: delete primary sequence for ID
458 // properly we should check PRIMARY fields instead of ID: $aiName = $currentName.'_'.$fieldName, see createTable
459 $aiName = $tableName;
460
461 if ($this->queryScalar("SELECT 1 FROM user_sequences WHERE sequence_name=upper('".$this->getSqlHelper()->forSql('sq_'.$aiName)."')"))
462 {
463 $this->query('DROP SEQUENCE '.$this->getSqlHelper()->quote('sq_'.$aiName));
464 }
465 }
466
467 /*********************************************************
468 * Transaction
469 *********************************************************/
470
474 public function startTransaction()
475 {
476 $this->transaction = OCI_DEFAULT;
477 }
478
482 public function commitTransaction()
483 {
484 $this->connectInternal();
485 OCICommit($this->resource);
486 $this->transaction = OCI_COMMIT_ON_SUCCESS;
487 }
488
492 public function rollbackTransaction()
493 {
494 $this->connectInternal();
495 OCIRollback($this->resource);
496 $this->transaction = OCI_COMMIT_ON_SUCCESS;
497 }
498
499 /*********************************************************
500 * Type, version, cache, etc.
501 *********************************************************/
502
512 public function getType()
513 {
514 return "oracle";
515 }
516
520 public function getVersion()
521 {
522 if ($this->version == null)
523 {
524 $version = $this->queryScalar('SELECT BANNER FROM v$version');
525 if ($version != null)
526 {
527 $version = trim($version);
528 $this->versionExpress = (mb_strpos($version, "Express Edition") > 0);
529 preg_match("#[0-9]+\\.[0-9]+\\.[0-9]+#", $version, $arr);
530 $this->version = $arr[0];
531 }
532 }
533
534 return array($this->version, $this->versionExpress);
535 }
536
540 public function getErrorMessage($resource = null)
541 {
542 if ($resource)
543 $error = oci_error($resource);
544 else
545 $error = oci_error();
546
547 if (!$error)
548 return "";
549
550 $result = sprintf("[%s] %s", $error["code"], $error["message"]);
551 if (!empty($error["sqltext"]))
552 $result .= sprintf(" (%s)", $error["sqltext"]);
553
554 return $result;
555 }
556}
getIndexName($tableName, array $columns, $strict=false)
renameTable($currentName, $newName)
isIndexExists($tableName, array $columns)
createResult($result, \Bitrix\Main\Diag\SqlTrackerQuery $trackerQuery=null)
add($tableName, array $data, $identity="ID")
createTable($tableName, $fields, $primary=array(), $autoincrement=array())