Completed
Push — namespace-model ( c67c40...018a87 )
by Sam
07:37
created

Database::manipulate()   C

Complexity

Conditions 11
Paths 15

Size

Total Lines 47
Code Lines 24

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 11
eloc 24
nc 15
nop 1
dl 0
loc 47
rs 5.2653

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * Abstract database connectivity class.
5
 * Sub-classes of this implement the actual database connection libraries
6
 *
7
 * @package framework
8
 * @subpackage model
9
 */
10
11
namespace SilverStripe\Model\Connect;
12
13
use Director;
14
use Debug;
15
use SQLUpdate;
16
use SQLInsert;
17
use BadMethodCallException;
18
use Deprecation;
19
use SQLExpression;
20
use SilverStripe\Model\Connect\DBQueryBuilder;
21
22
23
24
25
abstract class Database {
26
27
	/**
28
	 * Database connector object
29
	 *
30
	 * @var DBConnector
31
	 */
32
	protected $connector = null;
33
34
	/**
35
	 * Get the current connector
36
	 *
37
	 * @return DBConnector
38
	 */
39
	public function getConnector() {
40
		return $this->connector;
41
	}
42
43
	/**
44
	 * Injector injection point for connector dependency
45
	 *
46
	 * @param DBConnector $connector
47
	 */
48
	public function setConnector(DBConnector $connector) {
49
		$this->connector = $connector;
50
	}
51
52
	/**
53
	 * Database schema manager object
54
	 *
55
	 * @var DBSchemaManager
56
	 */
57
	protected $schemaManager = null;
58
59
	/**
60
	 * Returns the current schema manager
61
	 *
62
	 * @return DBSchemaManager
63
	 */
64
	public function getSchemaManager() {
65
		return $this->schemaManager;
66
	}
67
68
	/**
69
	 * Injector injection point for schema manager
70
	 *
71
	 * @param DBSchemaManager $schemaManager
72
	 */
73
	public function setSchemaManager(DBSchemaManager $schemaManager) {
74
		$this->schemaManager = $schemaManager;
75
76
		if ($this->schemaManager) {
77
			$this->schemaManager->setDatabase($this);
78
		}
79
	}
80
81
	/**
82
	 * Query builder object
83
	 *
84
	 * @var DBQueryBuilder
85
	 */
86
	protected $queryBuilder = null;
87
88
	/**
89
	 * Returns the current query builder
90
	 *
91
	 * @return DBQueryBuilder
92
	 */
93
	public function getQueryBuilder() {
94
		return $this->queryBuilder;
95
	}
96
97
	/**
98
	 * Injector injection point for schema manager
99
	 *
100
	 * @param DBQueryBuilder $queryBuilder
101
	 */
102
	public function setQueryBuilder(DBQueryBuilder $queryBuilder) {
103
		$this->queryBuilder = $queryBuilder;
104
	}
105
106
	/**
107
	 * Execute the given SQL query.
108
	 *
109
	 * @param string $sql The SQL query to execute
110
	 * @param int $errorLevel The level of error reporting to enable for the query
111
	 * @return SS_Query
112
	 */
113 View Code Duplication
	public function query($sql, $errorLevel = E_USER_ERROR) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
114
		// Check if we should only preview this query
115
		if ($this->previewWrite($sql)) {
116
			return;
117
		}
118
119
		// Benchmark query
120
		$connector = $this->connector;
121
		return $this->benchmarkQuery(
122
			$sql,
123
			function($sql) use($connector, $errorLevel) {
124
				return $connector->query($sql, $errorLevel);
125
			}
126
		);
127
	}
128
129
130
	/**
131
	 * Execute the given SQL parameterised query with the specified arguments
132
	 *
133
	 * @param string $sql The SQL query to execute. The ? character will denote parameters.
134
	 * @param array $parameters An ordered list of arguments.
135
	 * @param int $errorLevel The level of error reporting to enable for the query
136
	 * @return SS_Query
137
	 */
138 View Code Duplication
	public function preparedQuery($sql, $parameters, $errorLevel = E_USER_ERROR) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
139
		// Check if we should only preview this query
140
		if ($this->previewWrite($sql)) {
141
			return;
142
		}
143
144
		// Benchmark query
145
		$connector = $this->connector;
146
		return $this->benchmarkQuery(
147
			$sql,
148
			function($sql) use($connector, $parameters, $errorLevel) {
149
				return $connector->preparedQuery($sql, $parameters, $errorLevel);
150
			},
151
			$parameters
152
		);
153
	}
154
155
	/**
156
	 * Determines if the query should be previewed, and thus interrupted silently.
157
	 * If so, this function also displays the query via the debuging system.
158
	 * Subclasess should respect the results of this call for each query, and not
159
	 * execute any queries that generate a true response.
160
	 *
161
	 * @param string $sql The query to be executed
162
	 * @return boolean Flag indicating that the query was previewed
163
	 */
164
	protected function previewWrite($sql) {
165
		// Only preview if previewWrite is set, we are in dev mode, and
166
		// the query is mutable
167
		if (isset($_REQUEST['previewwrite'])
168
			&& Director::isDev()
169
			&& $this->connector->isQueryMutable($sql)
170
		) {
171
			// output preview message
172
			Debug::message("Will execute: $sql");
173
			return true;
174
		} else {
175
			return false;
176
		}
177
	}
178
179
	/**
180
	 * Allows the display and benchmarking of queries as they are being run
181
	 *
182
	 * @param string $sql Query to run, and single parameter to callback
183
	 * @param callable $callback Callback to execute code
184
	 * @param array $parameters Parameters to display
185
	 * @return mixed Result of query
186
	 */
187
	protected function benchmarkQuery($sql, $callback, $parameters = null) {
188
		if (isset($_REQUEST['showqueries']) && Director::isDev()) {
189
			$starttime = microtime(true);
190
			$result = $callback($sql);
191
			$endtime = round(microtime(true) - $starttime, 4);
192
			$message = $sql;
193
			if($parameters) {
194
				$message .= "\nparams: \"" . implode('", "', $parameters) . '"';
195
			}
196
			Debug::message("\n{$message}\n{$endtime}s\n", false);
197
198
			return $result;
199
		} else {
200
			return $callback($sql);
201
		}
202
	}
203
204
	/**
205
	 * Get the autogenerated ID from the previous INSERT query.
206
	 *
207
	 * @param string $table The name of the table to get the generated ID for
208
	 * @return integer the most recently generated ID for the specified table
209
	 */
210
	public function getGeneratedID($table) {
211
		return $this->connector->getGeneratedID($table);
212
	}
213
214
	/**
215
	 * Determines if we are connected to a server AND have a valid database
216
	 * selected.
217
	 *
218
	 * @return boolean Flag indicating that a valid database is connected
219
	 */
220
	public function isActive() {
221
		return $this->connector->isActive();
222
	}
223
224
	/**
225
	 * Returns an escaped string. This string won't be quoted, so would be suitable
226
	 * for appending to other quoted strings.
227
	 *
228
	 * @param mixed $value Value to be prepared for database query
229
	 * @return string Prepared string
230
	 */
231
	public function escapeString($value) {
232
		return $this->connector->escapeString($value);
233
	}
234
235
	/**
236
	 * Wrap a string into DB-specific quotes.
237
	 *
238
	 * @param mixed $value Value to be prepared for database query
239
	 * @return string Prepared string
240
	 */
241
	public function quoteString($value) {
242
		return $this->connector->quoteString($value);
243
	}
244
245
	/**
246
	 * Escapes an identifier (table / database name). Typically the value
247
	 * is simply double quoted. Don't pass in already escaped identifiers in,
248
	 * as this will double escape the value!
249
	 *
250
	 * @param string $value The identifier to escape
251
	 * @param string $separator optional identifier splitter
252
	 */
253
	public function escapeIdentifier($value, $separator = '.') {
254
		return $this->connector->escapeIdentifier($value, $separator);
255
	}
256
257
	/**
258
	 * Escapes unquoted columns keys in an associative array
259
	 *
260
	 * @param array $fieldValues
261
	 * @return array List of field values with the keys as escaped column names
262
	 */
263
	protected function escapeColumnKeys($fieldValues) {
264
		$out = array();
265
		foreach($fieldValues as $field => $value) {
266
			$out[$this->escapeIdentifier($field)] = $value;
267
		}
268
		return $out;
269
	}
270
271
	/**
272
	 * Execute a complex manipulation on the database.
273
	 * A manipulation is an array of insert / or update sequences.  The keys of the array are table names,
274
	 * and the values are map containing 'command' and 'fields'.  Command should be 'insert' or 'update',
275
	 * and fields should be a map of field names to field values, NOT including quotes.
276
	 *
277
	 * The field values could also be in paramaterised format, such as
278
	 * array('MAX(?,?)' => array(42, 69)), allowing the use of raw SQL values such as
279
	 * array('NOW()' => array()).
280
	 *
281
	 * @see SQLWriteExpression::addAssignments for syntax examples
282
	 *
283
	 * @param array $manipulation
284
	 */
285
	public function manipulate($manipulation) {
286
		if (empty($manipulation)) return;
287
288
		foreach ($manipulation as $table => $writeInfo) {
289
			if(empty($writeInfo['fields'])) continue;
290
			// Note: keys of $fieldValues are not escaped
291
			$fieldValues = $writeInfo['fields'];
292
293
			// Switch command type
294
			switch ($writeInfo['command']) {
295
				case "update":
296
297
					// Build update
298
					$query = new SQLUpdate("\"$table\"", $this->escapeColumnKeys($fieldValues));
299
300
					// Set best condition to use
301
					if(!empty($writeInfo['where'])) {
302
						$query->addWhere($writeInfo['where']);
303
					} elseif(!empty($writeInfo['id'])) {
304
						$query->addWhere(array('"ID"' => $writeInfo['id']));
305
					}
306
307
					// Test to see if this update query shouldn't, in fact, be an insert
308
					if($query->toSelect()->count()) {
309
						$query->execute();
310
						break;
311
					}
312
					// ...if not, we'll skip on to the insert code
313
314
				case "insert":
315
					// Ensure that the ID clause is given if possible
316
					if (!isset($fieldValues['ID']) && isset($writeInfo['id'])) {
317
						$fieldValues['ID'] = $writeInfo['id'];
318
					}
319
320
					// Build insert
321
					$query = new SQLInsert("\"$table\"", $this->escapeColumnKeys($fieldValues));
322
323
					$query->execute();
324
					break;
325
326
				default:
327
					user_error("SS_Database::manipulate() Can't recognise command '{$writeInfo['command']}'",
328
								E_USER_ERROR);
329
			}
330
		}
331
	}
332
333
	/**
334
	 * Enable supression of database messages.
335
	 */
336
	public function quiet() {
337
		$this->schemaManager->quiet();
338
	}
339
340
	/**
341
	 * Clear all data out of the database
342
	 */
343
	public function clearAllData() {
344
		$tables = $this->getSchemaManager()->tableList();
345
		foreach ($tables as $table) {
346
			$this->clearTable($table);
347
		}
348
	}
349
350
	/**
351
	 * Clear all data in a given table
352
	 *
353
	 * @param string $table Name of table
354
	 */
355
	public function clearTable($table) {
356
		$this->query("TRUNCATE \"$table\"");
357
	}
358
359
	/**
360
	 * Generates a WHERE clause for null comparison check
361
	 *
362
	 * @param string $field Quoted field name
363
	 * @param bool $isNull Whether to check for NULL or NOT NULL
364
	 * @return string Non-parameterised null comparison clause
365
	 */
366
	public function nullCheckClause($field, $isNull) {
367
		$clause = $isNull
368
			? "%s IS NULL"
369
			: "%s IS NOT NULL";
370
		return sprintf($clause, $field);
371
	}
372
373
	/**
374
	 * Generate a WHERE clause for text matching.
375
	 *
376
	 * @param String $field Quoted field name
377
	 * @param String $value Escaped search. Can include percentage wildcards.
378
	 * Ignored if $parameterised is true.
379
	 * @param boolean $exact Exact matches or wildcard support.
380
	 * @param boolean $negate Negate the clause.
381
	 * @param boolean $caseSensitive Enforce case sensitivity if TRUE or FALSE.
382
	 * Fallback to default collation if set to NULL.
383
	 * @param boolean $parameterised Insert the ? placeholder rather than the
384
	 * given value. If this is true then $value is ignored.
385
	 * @return String SQL
386
	 */
387
	abstract public function comparisonClause($field, $value, $exact = false, $negate = false, $caseSensitive = null,
388
											$parameterised = false);
389
390
	/**
391
	 * function to return an SQL datetime expression that can be used with the adapter in use
392
	 * used for querying a datetime in a certain format
393
	 *
394
	 * @param string $date to be formated, can be either 'now', literal datetime like '1973-10-14 10:30:00' or
395
	 *                     field name, e.g. '"SiteTree"."Created"'
396
	 * @param string $format to be used, supported specifiers:
397
	 * %Y = Year (four digits)
398
	 * %m = Month (01..12)
399
	 * %d = Day (01..31)
400
	 * %H = Hour (00..23)
401
	 * %i = Minutes (00..59)
402
	 * %s = Seconds (00..59)
403
	 * %U = unix timestamp, can only be used on it's own
404
	 * @return string SQL datetime expression to query for a formatted datetime
405
	 */
406
	abstract public function formattedDatetimeClause($date, $format);
407
408
	/**
409
	 * function to return an SQL datetime expression that can be used with the adapter in use
410
	 * used for querying a datetime addition
411
	 *
412
	 * @param string $date, can be either 'now', literal datetime like '1973-10-14 10:30:00' or field name,
0 ignored issues
show
Bug introduced by
There is no parameter named $date,. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
413
	 *                      e.g. '"SiteTree"."Created"'
414
	 * @param string $interval to be added, use the format [sign][integer] [qualifier], e.g. -1 Day, +15 minutes,
415
	 *                         +1 YEAR
416
	 * supported qualifiers:
417
	 * - years
418
	 * - months
419
	 * - days
420
	 * - hours
421
	 * - minutes
422
	 * - seconds
423
	 * This includes the singular forms as well
424
	 * @return string SQL datetime expression to query for a datetime (YYYY-MM-DD hh:mm:ss) which is the result of
425
	 *                the addition
426
	 */
427
	abstract public function datetimeIntervalClause($date, $interval);
428
429
	/**
430
	 * function to return an SQL datetime expression that can be used with the adapter in use
431
	 * used for querying a datetime substraction
432
	 *
433
	 * @param string $date1, can be either 'now', literal datetime like '1973-10-14 10:30:00' or field name
0 ignored issues
show
Documentation introduced by
There is no parameter named $date1,. Did you maybe mean $date1?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
434
	 *                       e.g. '"SiteTree"."Created"'
435
	 * @param string $date2 to be substracted of $date1, can be either 'now', literal datetime
436
	 *                      like '1973-10-14 10:30:00' or field name, e.g. '"SiteTree"."Created"'
437
	 * @return string SQL datetime expression to query for the interval between $date1 and $date2 in seconds which
438
	 *                is the result of the substraction
439
	 */
440
	abstract public function datetimeDifferenceClause($date1, $date2);
441
442
	/**
443
	 * Returns true if this database supports collations
444
	 *
445
	 * @return boolean
446
	 */
447
	abstract public function supportsCollations();
448
449
	/**
450
	 * Can the database override timezone as a connection setting,
451
	 * or does it use the system timezone exclusively?
452
	 *
453
	 * @return Boolean
454
	 */
455
	abstract public function supportsTimezoneOverride();
456
457
	/**
458
	 * Query for the version of the currently connected database
459
	 * @return string Version of this database
460
	 */
461
	public function getVersion() {
462
		return $this->connector->getVersion();
463
	}
464
465
	/**
466
	 * Get the database server type (e.g. mysql, postgresql).
467
	 * This value is passed to the connector as the 'driver' argument when
468
	 * initiating a database connection
469
	 *
470
	 * @return string
471
	 */
472
	abstract public function getDatabaseServer();
473
474
	/**
475
	 * Return the number of rows affected by the previous operation.
476
	 * @return int
477
	 */
478
	public function affectedRows() {
479
		return $this->connector->affectedRows();
480
	}
481
482
	/**
483
	 * The core search engine, used by this class and its subclasses to do fun stuff.
484
	 * Searches both SiteTree and File.
485
	 *
486
	 * @param array $classesToSearch List of classes to search
487
	 * @param string $keywords Keywords as a string.
488
	 * @param integer $start Item to start returning results from
489
	 * @param integer $pageLength Number of items per page
490
	 * @param string $sortBy Sort order expression
491
	 * @param string $extraFilter Additional filter
492
	 * @param boolean $booleanSearch Flag for boolean search mode
493
	 * @param string $alternativeFileFilter
494
	 * @param boolean $invertedMatch
495
	 * @return PaginatedList Search results
496
	 */
497
	abstract public function searchEngine($classesToSearch, $keywords, $start, $pageLength, $sortBy = "Relevance DESC",
498
		$extraFilter = "", $booleanSearch = false, $alternativeFileFilter = "", $invertedMatch = false);
499
500
	/**
501
	 * Determines if this database supports transactions
502
	 *
503
	 * @return boolean Flag indicating support for transactions
504
	 */
505
	abstract public function supportsTransactions();
506
507
	/**
508
	 * Invoke $callback within a transaction
509
	 *
510
	 * @param callable $callback Callback to run
511
	 * @param callable $errorCallback Optional callback to run after rolling back transaction.
512
	 * @param bool|string $transactionMode Optional transaction mode to use
513
	 * @param bool $errorIfTransactionsUnsupported If true, this method will fail if transactions are unsupported.
514
	 * Otherwise, the $callback will potentially be invoked outside of a transaction.
515
	 * @throws Exception
516
	 */
517
	public function withTransaction(
518
		$callback, $errorCallback = null, $transactionMode = false, $errorIfTransactionsUnsupported = false
519
	) {
520
		$supported = $this->supportsTransactions();
521
		if(!$supported && $errorIfTransactionsUnsupported) {
522
			throw new BadMethodCallException("Transactions not supported by this database.");
523
		}
524
		if($supported) {
525
			$this->transactionStart($transactionMode);
526
		}
527
		try {
528
			call_user_func($callback);
529
		} catch (Exception $ex) {
0 ignored issues
show
Bug introduced by
The class SilverStripe\Model\Connect\Exception does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
530
			if($supported) {
531
				$this->transactionRollback();
532
			}
533
			if($errorCallback) {
534
				call_user_func($errorCallback);
535
			}
536
			throw $ex;
537
		}
538
		if($supported) {
539
			$this->transactionEnd();
540
		}
541
	}
542
543
	/*
544
	 * Determines if the current database connection supports a given list of extensions
545
	 *
546
	 * @param array $extensions List of extensions to check for support of. The key of this array
547
	 * will be an extension name, and the value the configuration for that extension. This
548
	 * could be one of partitions, tablespaces, or clustering
549
	 * @return boolean Flag indicating support for all of the above
550
	 * @todo Write test cases
551
	 */
552
	protected function supportsExtensions($extensions) {
0 ignored issues
show
Unused Code introduced by
The parameter $extensions is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
553
		return false;
554
	}
555
556
	/**
557
	 * Start a prepared transaction
558
	 * See http://developer.postgresql.org/pgdocs/postgres/sql-set-transaction.html for details on
559
	 * transaction isolation options
560
	 *
561
	 * @param string|boolean $transactionMode Transaction mode, or false to ignore
562
	 * @param string|boolean $sessionCharacteristics Session characteristics, or false to ignore
563
	 */
564
	abstract public function transactionStart($transactionMode = false, $sessionCharacteristics = false);
565
566
	/**
567
	 * Create a savepoint that you can jump back to if you encounter problems
568
	 *
569
	 * @param string $savepoint Name of savepoint
570
	 */
571
	abstract public function transactionSavepoint($savepoint);
572
573
	/**
574
	 * Rollback or revert to a savepoint if your queries encounter problems
575
	 * If you encounter a problem at any point during a transaction, you may
576
	 * need to rollback that particular query, or return to a savepoint
577
	 *
578
	 * @param string|boolean $savepoint Name of savepoint, or leave empty to rollback
579
	 * to last savepoint
580
	 */
581
	abstract public function transactionRollback($savepoint = false);
582
583
	/**
584
	 * Commit everything inside this transaction so far
585
	 *
586
	 * @param boolean $chain
587
	 */
588
	abstract public function transactionEnd($chain = false);
589
590
	/**
591
	 * Determines if the used database supports application-level locks,
592
	 * which is different from table- or row-level locking.
593
	 * See {@link getLock()} for details.
594
	 *
595
	 * @return boolean Flag indicating that locking is available
596
	 */
597
	public function supportsLocks() {
598
		return false;
599
	}
600
601
	/**
602
	 * Returns if the lock is available.
603
	 * See {@link supportsLocks()} to check if locking is generally supported.
604
	 *
605
	 * @param string $name Name of the lock
606
	 * @return boolean
607
	 */
608
	public function canLock($name) {
609
		return false;
610
	}
611
612
	/**
613
	 * Sets an application-level lock so that no two processes can run at the same time,
614
	 * also called a "cooperative advisory lock".
615
	 *
616
	 * Return FALSE if acquiring the lock fails; otherwise return TRUE, if lock was acquired successfully.
617
	 * Lock is automatically released if connection to the database is broken (either normally or abnormally),
618
	 * making it less prone to deadlocks than session- or file-based locks.
619
	 * Should be accompanied by a {@link releaseLock()} call after the logic requiring the lock has completed.
620
	 * Can be called multiple times, in which case locks "stack" (PostgreSQL, SQL Server),
621
	 * or auto-releases the previous lock (MySQL).
622
	 *
623
	 * Note that this might trigger the database to wait for the lock to be released, delaying further execution.
624
	 *
625
	 * @param string $name Name of lock
626
	 * @param integer $timeout Timeout in seconds
627
	 * @return boolean
628
	 */
629
	public function getLock($name, $timeout = 5) {
630
		return false;
631
	}
632
633
	/**
634
	 * Remove an application-level lock file to allow another process to run
635
	 * (if the execution aborts (e.g. due to an error) all locks are automatically released).
636
	 *
637
	 * @param string $name Name of the lock
638
	 * @return boolean Flag indicating whether the lock was successfully released
639
	 */
640
	public function releaseLock($name) {
641
		return false;
642
	}
643
644
	/**
645
	 * Instruct the database to generate a live connection
646
	 *
647
	 * @param array $parameters An map of parameters, which should include:
648
	 *  - server: The server, eg, localhost
649
	 *  - username: The username to log on with
650
	 *  - password: The password to log on with
651
	 *  - database: The database to connect to
652
	 *  - charset: The character set to use. Defaults to utf8
653
	 *  - timezone: (optional) The timezone offset. For example: +12:00, "Pacific/Auckland", or "SYSTEM"
654
	 *  - driver: (optional) Driver name
655
	 */
656
	public function connect($parameters) {
657
		// Ensure that driver is available (required by PDO)
658
		if(empty($parameters['driver'])) {
659
			$parameters['driver'] = $this->getDatabaseServer();
660
		}
661
662
		// Notify connector of parameters
663
		$this->connector->connect($parameters);
664
665
		// SS_Database subclass maintains responsibility for selecting database
666
		// once connected in order to correctly handle schema queries about
667
		// existence of database, error handling at the correct level, etc
668
		if (!empty($parameters['database'])) {
669
			$this->selectDatabase($parameters['database'], false, false);
670
		}
671
	}
672
673
	/**
674
	 * Determine if the database with the specified name exists
675
	 *
676
	 * @param string $name Name of the database to check for
677
	 * @return boolean Flag indicating whether this database exists
678
	 */
679
	public function databaseExists($name) {
680
		return $this->schemaManager->databaseExists($name);
681
	}
682
683
	/**
684
	 * Retrieves the list of all databases the user has access to
685
	 *
686
	 * @return array List of database names
687
	 */
688
	public function databaseList() {
689
		return $this->schemaManager->databaseList();
690
	}
691
692
	/**
693
	 * Change the connection to the specified database, optionally creating the
694
	 * database if it doesn't exist in the current schema.
695
	 *
696
	 * @param string $name Name of the database
697
	 * @param boolean $create Flag indicating whether the database should be created
698
	 * if it doesn't exist. If $create is false and the database doesn't exist
699
	 * then an error will be raised
700
	 * @param int|boolean $errorLevel The level of error reporting to enable for the query, or false if no error
701
	 * should be raised
702
	 * @return boolean Flag indicating success
703
	 */
704
	public function selectDatabase($name, $create = false, $errorLevel = E_USER_ERROR) {
705
		if (!$this->schemaManager->databaseExists($name)) {
706
			// Check DB creation permisson
707
			if (!$create) {
708
				if ($errorLevel !== false) {
709
					user_error("Attempted to connect to non-existing database \"$name\"", $errorLevel);
710
				}
711
				// Unselect database
712
				$this->connector->unloadDatabase();
713
				return false;
714
			}
715
			$this->schemaManager->createDatabase($name);
716
		}
717
		return $this->connector->selectDatabase($name);
718
	}
719
720
	/**
721
	 * Drop the database that this object is currently connected to.
722
	 * Use with caution.
723
	 */
724
	public function dropSelectedDatabase() {
725
		$databaseName = $this->connector->getSelectedDatabase();
726
		if ($databaseName) {
727
			$this->connector->unloadDatabase();
728
			$this->schemaManager->dropDatabase($databaseName);
729
		}
730
	}
731
732
	/**
733
	 * Returns the name of the currently selected database
734
	 *
735
	 * @return string|null Name of the selected database, or null if none selected
736
	 */
737
	public function getSelectedDatabase() {
738
		return $this->connector->getSelectedDatabase();
739
	}
740
741
	/**
742
	 * Return SQL expression used to represent the current date/time
743
	 *
744
	 * @return string Expression for the current date/time
745
	 */
746
	abstract public function now();
747
748
	/**
749
	 * Returns the database-specific version of the random() function
750
	 *
751
	 * @return string Expression for a random value
752
	 */
753
	abstract public function random();
754
755
	/**
756
	 * @deprecated since version 4.0 Use DB::get_schema()->dbDataType($type) instead
757
	 */
758
	public function dbDataType($type){
759
		Deprecation::notice('4.0', 'Use DB::get_schema()->dbDataType($type) instead');
760
		return $this->getSchemaManager()->dbDataType($type);
761
	}
762
763
	/**
764
	 * @deprecated since version 4.0 Use selectDatabase('dbname', true) instead
765
	 */
766
	public function createDatabase() {
767
		Deprecation::notice('4.0', 'Use selectDatabase(\'dbname\',true) instead');
768
		$database = $this->connector->getSelectedDatabase();
769
		$this->selectDatabase($database, true);
770
		return $this->isActive();
771
	}
772
773
	/**
774
	 * @deprecated since version 4.0 SS_Database::getConnect was never implemented and is obsolete
775
	 */
776
	public function getConnect($parameters) {
0 ignored issues
show
Unused Code introduced by
The parameter $parameters is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
777
		Deprecation::notice('4.0', 'SS_Database::getConnect was never implemented and is obsolete');
778
	}
779
780
	/**
781
	 * @deprecated since version 4.0 Use Convert::raw2sql($string, true) instead
782
	 */
783
	public function prepStringForDB($string) {
784
		Deprecation::notice('4.0', 'Use Convert::raw2sql($string, true) instead');
785
		return $this->quoteString($string);
786
	}
787
788
	/**
789
	 * @deprecated since version 4.0 Use dropSelectedDatabase instead
790
	 */
791
	public function dropDatabase() {
792
		Deprecation::notice('4.0', 'Use dropSelectedDatabase instead');
793
		$this->dropSelectedDatabase();
794
	}
795
796
	/**
797
	 * @deprecated since version 4.0 Use databaseList instead
798
	 */
799
	public function allDatabaseNames() {
800
		Deprecation::notice('4.0', 'Use databaseList instead');
801
		return $this->databaseList();
802
	}
803
804
	/**
805
	 * @deprecated since version 4.0 Use DB::create_table instead
806
	 */
807
	public function createTable($table, $fields = null, $indexes = null, $options = null, $advancedOptions = null) {
808
		Deprecation::notice('4.0', 'Use DB::create_table instead');
809
		return $this->getSchemaManager()->createTable($table, $fields, $indexes, $options, $advancedOptions);
810
	}
811
812
	/**
813
	 * @deprecated since version 4.0 Use DB::get_schema()->alterTable() instead
814
	 */
815
	public function alterTable($table, $newFields = null, $newIndexes = null,
816
		$alteredFields = null, $alteredIndexes = null, $alteredOptions = null,
817
		$advancedOptions = null
818
	) {
819
		Deprecation::notice('4.0', 'Use DB::get_schema()->alterTable() instead');
820
		return $this->getSchemaManager()->alterTable(
821
			$table, $newFields, $newIndexes, $alteredFields,
822
			$alteredIndexes, $alteredOptions, $advancedOptions
823
		);
824
	}
825
826
	/**
827
	 * @deprecated since version 4.0 Use DB::get_schema()->renameTable() instead
828
	 */
829
	public function renameTable($oldTableName, $newTableName) {
830
		Deprecation::notice('4.0', 'Use DB::get_schema()->renameTable() instead');
831
		$this->getSchemaManager()->renameTable($oldTableName, $newTableName);
832
	}
833
834
	/**
835
	 * @deprecated since version 4.0 Use DB::create_field() instead
836
	 */
837
	public function createField($table, $field, $spec) {
838
		Deprecation::notice('4.0', 'Use DB::create_field() instead');
839
		$this->getSchemaManager()->createField($table, $field, $spec);
840
	}
841
842
	/**
843
	 * @deprecated since version 4.0 Use DB::get_schema()->renameField() instead
844
	 */
845
	public function renameField($tableName, $oldName, $newName) {
846
		Deprecation::notice('4.0', 'Use DB::get_schema()->renameField() instead');
847
		$this->getSchemaManager()->renameField($tableName, $oldName, $newName);
848
	}
849
850
	/**
851
	 * @deprecated since version 4.0 Use getSelectedDatabase instead
852
	 */
853
	public function currentDatabase() {
854
		Deprecation::notice('4.0', 'Use getSelectedDatabase instead');
855
		return $this->getSelectedDatabase();
856
	}
857
858
	/**
859
	 * @deprecated since version 4.0 Use DB::field_list instead
860
	 */
861
	public function fieldList($table) {
862
		Deprecation::notice('4.0', 'Use DB::field_list instead');
863
		return $this->getSchemaManager()->fieldList($table);
864
	}
865
866
	/**
867
	 * @deprecated since version 4.0 Use DB::table_list instead
868
	 */
869
	public function tableList() {
870
		Deprecation::notice('4.0', 'Use DB::table_list instead');
871
		return $this->getSchemaManager()->tableList();
872
	}
873
874
	/**
875
	 * @deprecated since version 4.0 Use DB::get_schema()->hasTable() instead
876
	 */
877
	public function hasTable($tableName) {
878
		Deprecation::notice('4.0', 'Use DB::get_schema()->hasTable() instead');
879
		return $this->getSchemaManager()->hasTable($tableName);
880
	}
881
882
	/**
883
	 * @deprecated since version 4.0 Use DB::get_schema()->enumValuesForField() instead
884
	 */
885
	public function enumValuesForField($tableName, $fieldName) {
886
		Deprecation::notice('4.0', 'Use DB::get_schema()->enumValuesForField() instead');
887
		return $this->getSchemaManager()->enumValuesForField($tableName, $fieldName);
888
	}
889
890
	/**
891
	 * @deprecated since version 4.0 Use Convert::raw2sql instead
892
	 */
893
	public function addslashes($value) {
894
		Deprecation::notice('4.0', 'Use Convert::raw2sql instead');
895
		return $this->escapeString($value);
896
	}
897
898
	/**
899
	 * @deprecated since version 3.2 Use DB::get_schema()->schemaUpdate with a callback instead
900
	 */
901
	public function beginSchemaUpdate() {
902
		Deprecation::notice('3.2', 'Use DB::get_schema()->schemaUpdate with a callback instead');
903
		// Unable to recover so throw appropriate exception
904
		throw new BadMethodCallException('Use DB::get_schema()->schemaUpdate with a callback instead');
905
	}
906
907
	/**
908
	 * @deprecated since version 3.2 Use DB::get_schema()->schemaUpdate with a callback instead
909
	 */
910
	public function endSchemaUpdate() {
911
		Deprecation::notice('3.2', 'Use DB::get_schema()->schemaUpdate with a callback instead');
912
		// Unable to recover so throw appropriate exception
913
		throw new BadMethodCallException('Use DB::get_schema()->schemaUpdate with a callback instead');
914
	}
915
916
	/**
917
	 * @deprecated since version 4.0 Use DB::get_schema()->cancelSchemaUpdate instead
918
	 */
919
	public function cancelSchemaUpdate() {
920
		Deprecation::notice('4.0', 'Use DB::get_schema()->cancelSchemaUpdate instead');
921
		$this->getSchemaManager()->cancelSchemaUpdate();
922
	}
923
924
	/**
925
	 * @deprecated since version 4.0 Use DB::get_schema()->isSchemaUpdating() instead
926
	 */
927
	public function isSchemaUpdating() {
928
		Deprecation::notice('4.0', 'Use DB::get_schema()->isSchemaUpdating() instead');
929
		return $this->getSchemaManager()->isSchemaUpdating();
930
	}
931
932
	/**
933
	 * @deprecated since version 4.0 Use DB::get_schema()->doesSchemaNeedUpdating() instead
934
	 */
935
	public function doesSchemaNeedUpdating() {
936
		Deprecation::notice('4.0', 'Use DB::get_schema()->doesSchemaNeedUpdating() instead');
937
		return $this->getSchemaManager()->doesSchemaNeedUpdating();
938
	}
939
940
	/**
941
	 * @deprecated since version 4.0 Use DB::get_schema()->transCreateTable() instead
942
	 */
943
	public function transCreateTable($table, $options = null, $advanced_options = null) {
944
		Deprecation::notice('4.0', 'Use DB::get_schema()->transCreateTable() instead');
945
		$this->getSchemaManager()->transCreateTable($table, $options, $advanced_options);
946
	}
947
948
	/**
949
	 * @deprecated since version 4.0 Use DB::get_schema()->transAlterTable() instead
950
	 */
951
	public function transAlterTable($table, $options, $advanced_options) {
0 ignored issues
show
Unused Code introduced by
The parameter $options is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $advanced_options is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
952
		Deprecation::notice('4.0', 'Use DB::get_schema()->transAlterTable() instead');
953
		$this->getSchemaManager()->transAlterTable($table, $index, $schema);
0 ignored issues
show
Bug introduced by
The variable $index does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $schema does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
954
	}
955
956
	/**
957
	 * @deprecated since version 4.0 Use DB::get_schema()->transCreateField() instead
958
	 */
959
	public function transCreateField($table, $field, $schema) {
0 ignored issues
show
Unused Code introduced by
The parameter $field is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
960
		Deprecation::notice('4.0', 'Use DB::get_schema()->transCreateField() instead');
961
		$this->getSchemaManager()->transCreateField($table, $index, $schema);
0 ignored issues
show
Bug introduced by
The variable $index does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
962
	}
963
964
	/**
965
	 * @deprecated since version 4.0 Use DB::get_schema()->transCreateIndex() instead
966
	 */
967
	public function transCreateIndex($table, $index, $schema) {
968
		Deprecation::notice('4.0', 'Use DB::get_schema()->transCreateIndex() instead');
969
		$this->getSchemaManager()->transCreateIndex($table, $index, $schema);
970
	}
971
972
	/**
973
	 * @deprecated since version 4.0 Use DB::get_schema()->transAlterField() instead
974
	 */
975
	public function transAlterField($table, $field, $schema) {
0 ignored issues
show
Unused Code introduced by
The parameter $field is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
976
		Deprecation::notice('4.0', 'Use DB::get_schema()->transAlterField() instead');
977
		$this->getSchemaManager()->transAlterField($table, $index, $schema);
0 ignored issues
show
Bug introduced by
The variable $index does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
978
	}
979
980
	/**
981
	 * @deprecated since version 4.0 Use DB::get_schema()->transAlterIndex() instead
982
	 */
983
	public function transAlterIndex($table, $index, $schema) {
984
		Deprecation::notice('4.0', 'Use DB::get_schema()->transAlterIndex() instead');
985
		$this->getSchemaManager()->transAlterIndex($table, $index, $schema);
986
	}
987
988
	/**
989
	 * @deprecated since version 4.0 Use DB::require_table() instead
990
	 */
991
	public function requireTable($table, $fieldSchema = null, $indexSchema = null,
992
		$hasAutoIncPK = true, $options = array(), $extensions = false
993
	) {
994
		Deprecation::notice('4.0', 'Use DB::require_table() instead');
995
		return $this->getSchemaManager()->requireTable(
996
			$table, $fieldSchema, $indexSchema, $hasAutoIncPK, $options, $extensions
0 ignored issues
show
Documentation introduced by
$extensions is of type boolean, but the function expects a false|array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
997
		);
998
	}
999
1000
	/**
1001
	 * @deprecated since version 4.0 Use DB::dont_require_table() instead
1002
	 */
1003
	public function dontRequireTable($table) {
1004
		Deprecation::notice('4.0', 'Use DB::dont_require_table() instead');
1005
		$this->getSchemaManager()->dontRequireTable($table);
1006
	}
1007
1008
	/**
1009
	 * @deprecated since version 4.0 Use DB::require_index() instead
1010
	 */
1011
	public function requireIndex($table, $index, $spec) {
1012
		Deprecation::notice('4.0', 'Use DB::require_index() instead');
1013
		$this->getSchemaManager()->requireIndex($table, $index, $spec);
1014
	}
1015
1016
	/**
1017
	 * @deprecated since version 4.0 Use DB::get_schema()->hasField() instead
1018
	 */
1019
	public function hasField($tableName, $fieldName) {
1020
		Deprecation::notice('4.0', 'Use DB::get_schema()->hasField() instead');
1021
		return $this->getSchemaManager()->hasField($tableName, $fieldName);
1022
	}
1023
1024
	/**
1025
	 * @deprecated since version 4.0 Use DB::require_field() instead
1026
	 */
1027
	public function requireField($table, $field, $spec) {
1028
		Deprecation::notice('4.0', 'Use DB::require_field() instead');
1029
		$this->getSchemaManager()->requireField($table, $field, $spec);
1030
	}
1031
1032
	/**
1033
	 * @deprecated since version 4.0 Use DB::dont_require_field() instead
1034
	 */
1035
	public function dontRequireField($table, $fieldName) {
1036
		Deprecation::notice('4.0', 'Use DB::dont_require_field() instead');
1037
		$this->getSchemaManager()->dontRequireField($table, $fieldName);
1038
	}
1039
1040
	/**
1041
	 * @deprecated since version 4.0 Use DB::build_sql() instead
1042
	 */
1043
	public function sqlQueryToString(SQLExpression $query, &$parameters = array()) {
1044
		Deprecation::notice('4.0', 'Use DB::build_sql() instead');
1045
		return $this->getQueryBuilder()->buildSQL($query, $parameters);
1046
	}
1047
}
1048