PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 5.0.7
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v5.0.7
5.13.0 5.12.1 5.12.0 5.11.1 5.11.0 5.10.2 5.10.1 trunk 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.3.0 1.3.1 1.3.2 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.1.0 4.1.1 4.1.2 4.1.3 4.10.0 4.11.0 4.12.0 4.13.0 4.13.2 4.13.3 4.13.4 4.13.5 4.14.0 4.14.1 4.14.2 4.15.0 4.15.1 4.15.2 4.15.3 4.2.0 4.3.0 4.3.1 4.4.1 4.4.2 4.5.0 4.6.0 5.0.1 5.0.2 5.0.3 5.0.4 5.0.5 5.0.6 5.0.7 5.0.8 5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.10.0 5.2.0 5.2.1 5.2.2 5.3.0 5.3.1 5.3.2 5.3.3 5.6.0 5.6.1 5.7.0 5.7.1 5.8.0 5.8.1 5.8.2
matomo / app / libs / Zend / Db / Adapter / Sqlsrv.php
matomo / app / libs / Zend / Db / Adapter Last commit date
Db2 2 years ago Mysqli 2 years ago Oracle 2 years ago Pdo 2 years ago Sqlsrv 2 years ago Abstract.php 2 years ago Db2.php 2 years ago Exception.php 2 years ago Mysqli.php 2 years ago Oracle.php 2 years ago Sqlsrv.php 2 years ago
Sqlsrv.php
580 lines
1 <?php
2
3 namespace {
4 /**
5 * Zend Framework
6 *
7 * LICENSE
8 *
9 * This source file is subject to the new BSD license that is bundled
10 * with this package in the file LICENSE.txt.
11 * It is also available through the world-wide-web at this URL:
12 * http://framework.zend.com/license/new-bsd
13 * If you did not receive a copy of the license and are unable to
14 * obtain it through the world-wide-web, please send an email
15 * to license@zend.com so we can send you a copy immediately.
16 *
17 * @category Zend
18 * @package Zend_Db
19 * @subpackage Adapter
20 * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
21 * @license http://framework.zend.com/license/new-bsd New BSD License
22 * @version $Id: Sqlsrv.php 23775 2011-03-01 17:25:24Z ralph $
23 */
24 /**
25 * @see Zend_Db_Adapter_Abstract
26 */
27 // require_once 'Zend/Db/Adapter/Abstract.php';
28 /**
29 * @see Zend_Db_Statement_Sqlsrv
30 */
31 // require_once 'Zend/Db/Statement/Sqlsrv.php';
32 /**
33 * @category Zend
34 * @package Zend_Db
35 * @subpackage Adapter
36 * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
37 * @license http://framework.zend.com/license/new-bsd New BSD License
38 */
39 class Zend_Db_Adapter_Sqlsrv extends \Zend_Db_Adapter_Abstract
40 {
41 /**
42 * User-provided configuration.
43 *
44 * Basic keys are:
45 *
46 * username => (string) Connect to the database as this username.
47 * password => (string) Password associated with the username.
48 * dbname => The name of the local SQL Server instance
49 *
50 * @var array
51 */
52 protected $_config = array('dbname' => null, 'username' => null, 'password' => null);
53 /**
54 * Last insert id from INSERT query
55 *
56 * @var int
57 */
58 protected $_lastInsertId;
59 /**
60 * Query used to fetch last insert id
61 *
62 * @var string
63 */
64 protected $_lastInsertSQL = 'SELECT SCOPE_IDENTITY() as Current_Identity';
65 /**
66 * Keys are UPPERCASE SQL datatypes or the constants
67 * Zend_Db::INT_TYPE, Zend_Db::BIGINT_TYPE, or Zend_Db::FLOAT_TYPE.
68 *
69 * Values are:
70 * 0 = 32-bit integer
71 * 1 = 64-bit integer
72 * 2 = float or decimal
73 *
74 * @var array Associative array of datatypes to values 0, 1, or 2.
75 */
76 protected $_numericDataTypes = array(\Zend_Db::INT_TYPE => \Zend_Db::INT_TYPE, \Zend_Db::BIGINT_TYPE => \Zend_Db::BIGINT_TYPE, \Zend_Db::FLOAT_TYPE => \Zend_Db::FLOAT_TYPE, 'INT' => \Zend_Db::INT_TYPE, 'SMALLINT' => \Zend_Db::INT_TYPE, 'TINYINT' => \Zend_Db::INT_TYPE, 'BIGINT' => \Zend_Db::BIGINT_TYPE, 'DECIMAL' => \Zend_Db::FLOAT_TYPE, 'FLOAT' => \Zend_Db::FLOAT_TYPE, 'MONEY' => \Zend_Db::FLOAT_TYPE, 'NUMERIC' => \Zend_Db::FLOAT_TYPE, 'REAL' => \Zend_Db::FLOAT_TYPE, 'SMALLMONEY' => \Zend_Db::FLOAT_TYPE);
77 /**
78 * Default class name for a DB statement.
79 *
80 * @var string
81 */
82 protected $_defaultStmtClass = 'Zend_Db_Statement_Sqlsrv';
83 /**
84 * Creates a connection resource.
85 *
86 * @return void
87 * @throws Zend_Db_Adapter_Sqlsrv_Exception
88 */
89 protected function _connect()
90 {
91 if (\is_resource($this->_connection)) {
92 // connection already exists
93 return;
94 }
95 if (!\extension_loaded('sqlsrv')) {
96 /**
97 * @see Zend_Db_Adapter_Sqlsrv_Exception
98 */
99 // require_once 'Zend/Db/Adapter/Sqlsrv/Exception.php';
100 throw new \Zend_Db_Adapter_Sqlsrv_Exception('The Sqlsrv extension is required for this adapter but the extension is not loaded');
101 }
102 $serverName = $this->_config['host'];
103 if (isset($this->_config['port'])) {
104 $port = (int) $this->_config['port'];
105 $serverName .= ', ' . $port;
106 }
107 $connectionInfo = array('Database' => $this->_config['dbname']);
108 if (isset($this->_config['username']) && isset($this->_config['password'])) {
109 $connectionInfo += array('UID' => $this->_config['username'], 'PWD' => $this->_config['password']);
110 }
111 // else - windows authentication
112 if (!empty($this->_config['driver_options'])) {
113 foreach ($this->_config['driver_options'] as $option => $value) {
114 // A value may be a constant.
115 if (\is_string($value)) {
116 $constantName = \strtoupper($value);
117 if (\defined($constantName)) {
118 $connectionInfo[$option] = \constant($constantName);
119 } else {
120 $connectionInfo[$option] = $value;
121 }
122 }
123 }
124 }
125 $this->_connection = \sqlsrv_connect($serverName, $connectionInfo);
126 if (!$this->_connection) {
127 /**
128 * @see Zend_Db_Adapter_Sqlsrv_Exception
129 */
130 // require_once 'Zend/Db/Adapter/Sqlsrv/Exception.php';
131 throw new \Zend_Db_Adapter_Sqlsrv_Exception(\sqlsrv_errors());
132 }
133 }
134 /**
135 * Check for config options that are mandatory.
136 * Throw exceptions if any are missing.
137 *
138 * @param array $config
139 * @throws Zend_Db_Adapter_Exception
140 */
141 protected function _checkRequiredOptions(array $config)
142 {
143 // we need at least a dbname
144 if (!\array_key_exists('dbname', $config)) {
145 /** @see Zend_Db_Adapter_Exception */
146 // require_once 'Zend/Db/Adapter/Exception.php';
147 throw new \Zend_Db_Adapter_Exception("Configuration array must have a key for 'dbname' that names the database instance");
148 }
149 if (!\array_key_exists('password', $config) && \array_key_exists('username', $config)) {
150 /**
151 * @see Zend_Db_Adapter_Exception
152 */
153 // require_once 'Zend/Db/Adapter/Exception.php';
154 throw new \Zend_Db_Adapter_Exception("Configuration array must have a key for 'password' for login credentials.\n If Windows Authentication is desired, both keys 'username' and 'password' should be ommited from config.");
155 }
156 if (\array_key_exists('password', $config) && !\array_key_exists('username', $config)) {
157 /**
158 * @see Zend_Db_Adapter_Exception
159 */
160 // require_once 'Zend/Db/Adapter/Exception.php';
161 throw new \Zend_Db_Adapter_Exception("Configuration array must have a key for 'username' for login credentials.\n If Windows Authentication is desired, both keys 'username' and 'password' should be ommited from config.");
162 }
163 }
164 /**
165 * Set the transaction isoltion level.
166 *
167 * @param integer|null $level A fetch mode from SQLSRV_TXN_*.
168 * @return true
169 * @throws Zend_Db_Adapter_Sqlsrv_Exception
170 */
171 public function setTransactionIsolationLevel($level = null)
172 {
173 $this->_connect();
174 $sql = null;
175 // Default transaction level in sql server
176 if ($level === null) {
177 $level = \SQLSRV_TXN_READ_COMMITTED;
178 }
179 switch ($level) {
180 case \SQLSRV_TXN_READ_UNCOMMITTED:
181 $sql = "READ UNCOMMITTED";
182 break;
183 case \SQLSRV_TXN_READ_COMMITTED:
184 $sql = "READ COMMITTED";
185 break;
186 case \SQLSRV_TXN_REPEATABLE_READ:
187 $sql = "REPEATABLE READ";
188 break;
189 case \SQLSRV_TXN_SNAPSHOT:
190 $sql = "SNAPSHOT";
191 break;
192 case \SQLSRV_TXN_SERIALIZABLE:
193 $sql = "SERIALIZABLE";
194 break;
195 default:
196 // require_once 'Zend/Db/Adapter/Sqlsrv/Exception.php';
197 throw new \Zend_Db_Adapter_Sqlsrv_Exception("Invalid transaction isolation level mode '{$level}' specified");
198 }
199 if (!\sqlsrv_query($this->_connection, "SET TRANSACTION ISOLATION LEVEL {$sql};")) {
200 // require_once 'Zend/Db/Adapter/Sqlsrv/Exception.php';
201 throw new \Zend_Db_Adapter_Sqlsrv_Exception("Transaction cannot be changed to '{$level}'");
202 }
203 return \true;
204 }
205 /**
206 * Test if a connection is active
207 *
208 * @return boolean
209 */
210 public function isConnected()
211 {
212 return \is_resource($this->_connection) && \get_resource_type($this->_connection) == 'SQL Server Connection';
213 }
214 /**
215 * Force the connection to close.
216 *
217 * @return void
218 */
219 public function closeConnection()
220 {
221 if ($this->isConnected()) {
222 \sqlsrv_close($this->_connection);
223 }
224 $this->_connection = null;
225 }
226 /**
227 * Returns an SQL statement for preparation.
228 *
229 * @param string $sql The SQL statement with placeholders.
230 * @return Zend_Db_Statement_Sqlsrv
231 */
232 public function prepare($sql)
233 {
234 $this->_connect();
235 $stmtClass = $this->_defaultStmtClass;
236 if (!\class_exists($stmtClass)) {
237 /**
238 * @see Zend_Loader
239 */
240 // require_once 'Zend/Loader.php';
241 \Zend_Loader::loadClass($stmtClass);
242 }
243 $stmt = new $stmtClass($this, $sql);
244 $stmt->setFetchMode($this->_fetchMode);
245 return $stmt;
246 }
247 /**
248 * Quote a raw string.
249 *
250 * @param string $value Raw string
251 * @return string Quoted string
252 */
253 protected function _quote($value)
254 {
255 if (\is_int($value)) {
256 return $value;
257 } elseif (\is_float($value)) {
258 return \sprintf('%F', $value);
259 }
260 return "'" . \str_replace("'", "''", $value) . "'";
261 }
262 /**
263 * Gets the last ID generated automatically by an IDENTITY/AUTOINCREMENT column.
264 *
265 * As a convention, on RDBMS brands that support sequences
266 * (e.g. Oracle, PostgreSQL, DB2), this method forms the name of a sequence
267 * from the arguments and returns the last id generated by that sequence.
268 * On RDBMS brands that support IDENTITY/AUTOINCREMENT columns, this method
269 * returns the last value generated for such a column, and the table name
270 * argument is disregarded.
271 *
272 * @param string $tableName OPTIONAL Name of table.
273 * @param string $primaryKey OPTIONAL Name of primary key column.
274 * @return string
275 */
276 public function lastInsertId($tableName = null, $primaryKey = null)
277 {
278 if ($tableName) {
279 $tableName = $this->quote($tableName);
280 $sql = 'SELECT IDENT_CURRENT (' . $tableName . ') as Current_Identity';
281 return (string) $this->fetchOne($sql);
282 }
283 if ($this->_lastInsertId > 0) {
284 return (string) $this->_lastInsertId;
285 }
286 $sql = $this->_lastInsertSQL;
287 return (string) $this->fetchOne($sql);
288 }
289 /**
290 * Inserts a table row with specified data.
291 *
292 * @param mixed $table The table to insert data into.
293 * @param array $bind Column-value pairs.
294 * @return int The number of affected rows.
295 */
296 public function insert($table, array $bind)
297 {
298 // extract and quote col names from the array keys
299 $cols = array();
300 $vals = array();
301 foreach ($bind as $col => $val) {
302 $cols[] = $this->quoteIdentifier($col, \true);
303 if ($val instanceof \Zend_Db_Expr) {
304 $vals[] = $val->__toString();
305 unset($bind[$col]);
306 } else {
307 $vals[] = '?';
308 }
309 }
310 // build the statement
311 $sql = "INSERT INTO " . $this->quoteIdentifier($table, \true) . ' (' . \implode(', ', $cols) . ') ' . 'VALUES (' . \implode(', ', $vals) . ')' . ' ' . $this->_lastInsertSQL;
312 // execute the statement and return the number of affected rows
313 $stmt = $this->query($sql, \array_values($bind));
314 $result = $stmt->rowCount();
315 $stmt->nextRowset();
316 $this->_lastInsertId = $stmt->fetchColumn();
317 return $result;
318 }
319 /**
320 * Returns a list of the tables in the database.
321 *
322 * @return array
323 */
324 public function listTables()
325 {
326 $this->_connect();
327 $sql = "SELECT name FROM sysobjects WHERE type = 'U' ORDER BY name";
328 return $this->fetchCol($sql);
329 }
330 /**
331 * Returns the column descriptions for a table.
332 *
333 * The return value is an associative array keyed by the column name,
334 * as returned by the RDBMS.
335 *
336 * The value of each array element is an associative array
337 * with the following keys:
338 *
339 * SCHEMA_NAME => string; name of schema
340 * TABLE_NAME => string;
341 * COLUMN_NAME => string; column name
342 * COLUMN_POSITION => number; ordinal position of column in table
343 * DATA_TYPE => string; SQL datatype name of column
344 * DEFAULT => string; default expression of column, null if none
345 * NULLABLE => boolean; true if column can have nulls
346 * LENGTH => number; length of CHAR/VARCHAR
347 * SCALE => number; scale of NUMERIC/DECIMAL
348 * PRECISION => number; precision of NUMERIC/DECIMAL
349 * UNSIGNED => boolean; unsigned property of an integer type
350 * PRIMARY => boolean; true if column is part of the primary key
351 * PRIMARY_POSITION => integer; position of column in primary key
352 * IDENTITY => integer; true if column is auto-generated with unique values
353 *
354 * @todo Discover integer unsigned property.
355 *
356 * @param string $tableName
357 * @param string $schemaName OPTIONAL
358 * @return array
359 */
360 public function describeTable($tableName, $schemaName = null)
361 {
362 /**
363 * Discover metadata information about this table.
364 */
365 $sql = "exec sp_columns @table_name = " . $this->quoteIdentifier($tableName, \true);
366 $stmt = $this->query($sql);
367 $result = $stmt->fetchAll(\Zend_Db::FETCH_NUM);
368 // ZF-7698
369 $stmt->closeCursor();
370 if (\count($result) == 0) {
371 return array();
372 }
373 $owner = 1;
374 $table_name = 2;
375 $column_name = 3;
376 $type_name = 5;
377 $precision = 6;
378 $length = 7;
379 $scale = 8;
380 $nullable = 10;
381 $column_def = 12;
382 $column_position = 16;
383 /**
384 * Discover primary key column(s) for this table.
385 */
386 $tableOwner = $result[0][$owner];
387 $sql = "exec sp_pkeys @table_owner = " . $tableOwner . ", @table_name = " . $this->quoteIdentifier($tableName, \true);
388 $stmt = $this->query($sql);
389 $primaryKeysResult = $stmt->fetchAll(\Zend_Db::FETCH_NUM);
390 $primaryKeyColumn = array();
391 // Per http://msdn.microsoft.com/en-us/library/ms189813.aspx,
392 // results from sp_keys stored procedure are:
393 // 0=TABLE_QUALIFIER 1=TABLE_OWNER 2=TABLE_NAME 3=COLUMN_NAME 4=KEY_SEQ 5=PK_NAME
394 $pkey_column_name = 3;
395 $pkey_key_seq = 4;
396 foreach ($primaryKeysResult as $pkeysRow) {
397 $primaryKeyColumn[$pkeysRow[$pkey_column_name]] = $pkeysRow[$pkey_key_seq];
398 }
399 $desc = array();
400 $p = 1;
401 foreach ($result as $key => $row) {
402 $identity = \false;
403 $words = \explode(' ', $row[$type_name], 2);
404 if (isset($words[0])) {
405 $type = $words[0];
406 if (isset($words[1])) {
407 $identity = (bool) \preg_match('/identity/', $words[1]);
408 }
409 }
410 $isPrimary = \array_key_exists($row[$column_name], $primaryKeyColumn);
411 if ($isPrimary) {
412 $primaryPosition = $primaryKeyColumn[$row[$column_name]];
413 } else {
414 $primaryPosition = null;
415 }
416 $desc[$this->foldCase($row[$column_name])] = array(
417 'SCHEMA_NAME' => null,
418 // @todo
419 'TABLE_NAME' => $this->foldCase($row[$table_name]),
420 'COLUMN_NAME' => $this->foldCase($row[$column_name]),
421 'COLUMN_POSITION' => (int) $row[$column_position],
422 'DATA_TYPE' => $type,
423 'DEFAULT' => $row[$column_def],
424 'NULLABLE' => (bool) $row[$nullable],
425 'LENGTH' => $row[$length],
426 'SCALE' => $row[$scale],
427 'PRECISION' => $row[$precision],
428 'UNSIGNED' => null,
429 // @todo
430 'PRIMARY' => $isPrimary,
431 'PRIMARY_POSITION' => $primaryPosition,
432 'IDENTITY' => $identity,
433 );
434 }
435 return $desc;
436 }
437 /**
438 * Leave autocommit mode and begin a transaction.
439 *
440 * @return void
441 * @throws Zend_Db_Adapter_Sqlsrv_Exception
442 */
443 protected function _beginTransaction()
444 {
445 if (!\sqlsrv_begin_transaction($this->_connection)) {
446 // require_once 'Zend/Db/Adapter/Sqlsrv/Exception.php';
447 throw new \Zend_Db_Adapter_Sqlsrv_Exception(\sqlsrv_errors());
448 }
449 }
450 /**
451 * Commit a transaction and return to autocommit mode.
452 *
453 * @return void
454 * @throws Zend_Db_Adapter_Sqlsrv_Exception
455 */
456 protected function _commit()
457 {
458 if (!\sqlsrv_commit($this->_connection)) {
459 // require_once 'Zend/Db/Adapter/Sqlsrv/Exception.php';
460 throw new \Zend_Db_Adapter_Sqlsrv_Exception(\sqlsrv_errors());
461 }
462 }
463 /**
464 * Roll back a transaction and return to autocommit mode.
465 *
466 * @return void
467 * @throws Zend_Db_Adapter_Sqlsrv_Exception
468 */
469 protected function _rollBack()
470 {
471 if (!\sqlsrv_rollback($this->_connection)) {
472 // require_once 'Zend/Db/Adapter/Sqlsrv/Exception.php';
473 throw new \Zend_Db_Adapter_Sqlsrv_Exception(\sqlsrv_errors());
474 }
475 }
476 /**
477 * Set the fetch mode.
478 *
479 * @todo Support FETCH_CLASS and FETCH_INTO.
480 *
481 * @param integer $mode A fetch mode.
482 * @return void
483 * @throws Zend_Db_Adapter_Sqlsrv_Exception
484 */
485 public function setFetchMode($mode)
486 {
487 switch ($mode) {
488 case \Zend_Db::FETCH_NUM:
489 // seq array
490 case \Zend_Db::FETCH_ASSOC:
491 // assoc array
492 case \Zend_Db::FETCH_BOTH:
493 // seq+assoc array
494 case \Zend_Db::FETCH_OBJ:
495 // object
496 $this->_fetchMode = $mode;
497 break;
498 case \Zend_Db::FETCH_BOUND:
499 // bound to PHP variable
500 // require_once 'Zend/Db/Adapter/Sqlsrv/Exception.php';
501 throw new \Zend_Db_Adapter_Sqlsrv_Exception('FETCH_BOUND is not supported yet');
502 break;
503 default:
504 // require_once 'Zend/Db/Adapter/Sqlsrv/Exception.php';
505 throw new \Zend_Db_Adapter_Sqlsrv_Exception("Invalid fetch mode '{$mode}' specified");
506 break;
507 }
508 }
509 /**
510 * Adds an adapter-specific LIMIT clause to the SELECT statement.
511 *
512 * @param string $sql
513 * @param integer $count
514 * @param integer $offset OPTIONAL
515 * @return string
516 * @throws Zend_Db_Adapter_Sqlsrv_Exception
517 */
518 public function limit($sql, $count, $offset = 0)
519 {
520 $count = \intval($count);
521 if ($count <= 0) {
522 // require_once 'Zend/Db/Adapter/Exception.php';
523 throw new \Zend_Db_Adapter_Exception("LIMIT argument count={$count} is not valid");
524 }
525 $offset = \intval($offset);
526 if ($offset < 0) {
527 /** @see Zend_Db_Adapter_Exception */
528 // require_once 'Zend/Db/Adapter/Exception.php';
529 throw new \Zend_Db_Adapter_Exception("LIMIT argument offset={$offset} is not valid");
530 }
531 if ($offset == 0) {
532 $sql = \preg_replace('/^SELECT\\s/i', 'SELECT TOP ' . $count . ' ', $sql);
533 } else {
534 $orderby = \stristr($sql, 'ORDER BY');
535 if (!$orderby) {
536 $over = 'ORDER BY (SELECT 0)';
537 } else {
538 $over = \preg_replace('/\\"[^,]*\\".\\"([^,]*)\\"/i', '"inner_tbl"."$1"', $orderby);
539 }
540 // Remove ORDER BY clause from $sql
541 $sql = \preg_replace('/\\s+ORDER BY(.*)/', '', $sql);
542 // Add ORDER BY clause as an argument for ROW_NUMBER()
543 $sql = "SELECT ROW_NUMBER() OVER ({$over}) AS \"ZEND_DB_ROWNUM\", * FROM ({$sql}) AS inner_tbl";
544 $start = $offset + 1;
545 $end = $offset + $count;
546 $sql = "WITH outer_tbl AS ({$sql}) SELECT * FROM outer_tbl WHERE \"ZEND_DB_ROWNUM\" BETWEEN {$start} AND {$end}";
547 }
548 return $sql;
549 }
550 /**
551 * Check if the adapter supports real SQL parameters.
552 *
553 * @param string $type 'positional' or 'named'
554 * @return bool
555 */
556 public function supportsParameters($type)
557 {
558 if ($type == 'positional') {
559 return \true;
560 }
561 // if its 'named' or anything else
562 return \false;
563 }
564 /**
565 * Retrieve server version in PHP style
566 *
567 * @return string
568 */
569 public function getServerVersion()
570 {
571 $this->_connect();
572 $serverInfo = \sqlsrv_server_info($this->_connection);
573 if ($serverInfo !== \false) {
574 return $serverInfo['SQLServerVersion'];
575 }
576 return null;
577 }
578 }
579 }
580