1 <?php
2 /**
3 * Global database interface, complete with static methods.
4 * Use this class for interacting with the database.
5 * @package sapphire
6 * @subpackage model
7 */
8 class DB {
9 /**
10 * This constant was added in SilverStripe 2.4 to indicate that SQL-queries
11 * should now use ANSI-compatible syntax. The most notable affect of this
12 * change is that table and field names should be escaped with double quotes
13 * and not backticks
14 */
15 const USE_ANSI_SQL = true;
16
17
18 /**
19 * The global database connection.
20 * @var SS_Database
21 */
22 private static $connections = array();
23
24 /**
25 * The last SQL query run.
26 * @var string
27 */
28 public static $lastQuery;
29
30 /**
31 * Internal flag to keep track of when db connection was attempted.
32 */
33 private static $connection_attempted = false;
34
35 /**
36 * Set the global database connection.
37 * Pass an object that's a subclass of SS_Database. This object will be used when {@link DB::query()}
38 * is called.
39 * @param $connection The connecton object to set as the connection.
40 * @param $name The name to give to this connection. If you omit this argument, the connection
41 * will be the default one used by the ORM. However, you can store other named connections to
42 * be accessed through DB::getConn($name). This is useful when you have an application that
43 * needs to connect to more than one database.
44 */
45 static function setConn(SS_Database $connection, $name = 'default') {
46 self::$connections[$name] = $connection;
47 }
48
49 /**
50 * Get the global database connection.
51 * @param $name An optional name given to a connection in the DB::setConn() call. If omitted,
52 * the default connection is returned.
53 * @return SS_Database
54 */
55 static function getConn($name = 'default') {
56 if(isset(self::$connections[$name])) {
57 return self::$connections[$name];
58 }
59 }
60
61 /**
62 * Set an alternative database to use for this browser session.
63 * This is useful when using testing systems other than SapphireTest; for example, Windmill.
64 * Set it to null to revert to the main database.
65 */
66 static function set_alternative_database_name($dbname) {
67 $_SESSION["alternativeDatabaseName"] = $dbname;
68 }
69
70 /**
71 * Get the name of the database in use
72 */
73 static function get_alternative_database_name() {
74 return $_SESSION["alternativeDatabaseName"];
75 }
76
77 /**
78 * Connect to a database.
79 * Given the database configuration, this method will create the correct subclass of SS_Database,
80 * and set it as the global connection.
81 * @param array $database A map of options. The 'type' is the name of the subclass of SS_Database to use. For the rest of the options, see the specific class.
82 */
83 static function connect($databaseConfig) {
84 // This is used by TestRunner::startsession() to test up a test session using an alt
85 if(isset($_SESSION["alternativeDatabaseName"]) && $dbname = $_SESSION["alternativeDatabaseName"]) $databaseConfig['database'] = $dbname;
86
87 if(!isset($databaseConfig['type']) || empty($databaseConfig['type'])) {
88 user_error("DB::connect: Not passed a valid database config", E_USER_ERROR);
89 }
90
91 self::$connection_attempted = true;
92
93 $dbClass = $databaseConfig['type'];
94 $conn = new $dbClass($databaseConfig);
95
96 self::setConn($conn);
97 }
98
99 /**
100 * Returns true if a database connection has been attempted.
101 * In particular, it lets the caller know if we're still so early in the execution pipeline that
102 * we haven't even tried to connect to the database yet.
103 */
104 public static function connection_attempted() {
105 return self::$connection_attempted;
106 }
107
108 /**
109 * Build the connection string from input.
110 * @param array $parameters The connection details.
111 * @return string $connect The connection string.
112 **/
113 public static function getConnect($parameters) {
114 return self::getConn()->getConnect($parameters);
115 }
116
117 /**
118 * Execute the given SQL query.
119 * @param string $sql The SQL query to execute
120 * @param int $errorLevel The level of error reporting to enable for the query
121 * @return SS_Query
122 */
123 static function query($sql, $errorLevel = E_USER_ERROR) {
124 self::$lastQuery = $sql;
125
126 return self::getConn()->query($sql, $errorLevel);
127 }
128
129 /**
130 * Execute a complex manipulation on the database.
131 * A manipulation is an array of insert / or update sequences. The keys of the array are table names,
132 * and the values are map containing 'command' and 'fields'. Command should be 'insert' or 'update',
133 * and fields should be a map of field names to field values, including quotes. The field value can
134 * also be a SQL function or similar.
135 * @param array $manipulation
136 */
137 static function manipulate($manipulation) {
138 self::$lastQuery = $manipulation;
139 return self::getConn()->manipulate($manipulation);
140 }
141
142 /**
143 * Get the autogenerated ID from the previous INSERT query.
144 * @return int
145 */
146 static function getGeneratedID($table) {
147 return self::getConn()->getGeneratedID($table);
148 }
149
150 /**
151 * Check if the connection to the database is active.
152 * @return boolean
153 */
154 static function isActive() {
155 if($conn = self::getConn()) return $conn->isActive();
156 else return false;
157 }
158
159 /**
160 * Create the database and connect to it. This can be called if the
161 * initial database connection is not successful because the database
162 * does not exist.
163 * @param string $connect Connection string
164 * @param string $username SS_Database username
165 * @param string $password SS_Database Password
166 * @param string $database SS_Database to which to create
167 * @return boolean Returns true if successful
168 */
169 static function createDatabase($connect, $username, $password, $database) {
170 return self::getConn()->createDatabase($connect, $username, $password, $database);
171 }
172
173 /**
174 * Create a new table.
175 * @param $tableName The name of the table
176 * @param $fields A map of field names to field types
177 * @param $indexes A map of indexes
178 * @param $options An map of additional options. The available keys are as follows:
179 * - 'MSSQLDatabase'/'MySQLDatabase'/'PostgreSQLDatabase' - database-specific options such as "engine" for MySQL.
180 * - 'temporary' - If true, then a temporary table will be created
181 * @return The table name generated. This may be different from the table name, for example with temporary tables.
182 */
183 static function createTable($table, $fields = null, $indexes = null, $options = null) {
184 return self::getConn()->createTable($table, $fields, $indexes, $options);
185 }
186
187 /**
188 * Create a new field on a table.
189 * @param string $table Name of the table.
190 * @param string $field Name of the field to add.
191 * @param string $spec The field specification, eg 'INTEGER NOT NULL'
192 */
193 static function createField($table, $field, $spec) {
194 return self::getConn()->createField($table, $field, $spec);
195 }
196
197 /**
198 * Generate the following table in the database, modifying whatever already exists
199 * as necessary.
200 * @param string $table The name of the table
201 * @param string $fieldSchema A list of the fields to create, in the same form as DataObject::$db
202 * @param string $indexSchema A list of indexes to create. The keys of the array are the names of the index.
203 * @param boolean $hasAutoIncPK A flag indicating that the primary key on this table is an autoincrement type
204 * The values of the array can be one of:
205 * - true: Create a single column index on the field named the same as the index.
206 * - array('fields' => array('A','B','C'), 'type' => 'index/unique/fulltext'): This gives you full
207 * control over the index.
208 * @param string $options SQL statement to append to the CREATE TABLE call.
209 */
210 static function requireTable($table, $fieldSchema = null, $indexSchema = null, $hasAutoIncPK=true, $options = null, $extensions=null) {
211 return self::getConn()->requireTable($table, $fieldSchema, $indexSchema, $hasAutoIncPK, $options, $extensions);
212 }
213
214 /**
215 * Generate the given field on the table, modifying whatever already exists as necessary.
216 * @param string $table The table name.
217 * @param string $field The field name.
218 * @param string $spec The field specification.
219 */
220 static function requireField($table, $field, $spec) {
221 return self::getConn()->requireField($table, $field, $spec);
222 }
223
224 /**
225 * Generate the given index in the database, modifying whatever already exists as necessary.
226 * @param string $table The table name.
227 * @param string $index The index name.
228 * @param string|boolean $spec The specification of the index. See requireTable() for more information.
229 */
230 static function requireIndex($table, $index, $spec) {
231 return self::getConn()->requireIndex($table, $index, $spec);
232 }
233
234 /**
235 * If the given table exists, move it out of the way by renaming it to _obsolete_(tablename).
236 * @param string $table The table name.
237 */
238 static function dontRequireTable($table) {
239 return self::getConn()->dontRequireTable($table);
240 }
241
242 /**
243 * See {@link SS_Database->dontRequireField()}.
244 *
245 * @param string $table The table name.
246 * @param string $fieldName
247 */
248 static function dontRequireField($table, $fieldName) {
249 return self::getConn()->dontRequireField($table, $fieldName);
250 }
251
252 /**
253 * Checks a table's integrity and repairs it if necessary.
254 * @var string $tableName The name of the table.
255 * @return boolean Return true if the table has integrity after the method is complete.
256 */
257 static function checkAndRepairTable($table) {
258 return self::getConn()->checkAndRepairTable($table);
259 }
260
261 /**
262 * Return the number of rows affected by the previous operation.
263 * @return int
264 */
265 static function affectedRows() {
266 return self::getConn()->affectedRows();
267 }
268
269 /**
270 * Returns a list of all tables in the database.
271 * The table names will be in lower case.
272 * @return array
273 */
274 static function tableList() {
275 return self::getConn()->tableList();
276 }
277
278 /**
279 * Get a list of all the fields for the given table.
280 * Returns a map of field name => field spec.
281 * @param string $table The table name.
282 * @return array
283 */
284 static function fieldList($table) {
285 return self::getConn()->fieldList($table);
286 }
287
288 /**
289 * Enable supression of database messages.
290 */
291 static function quiet() {
292 return self::getConn()->quiet();
293 }
294
295 /**
296 * Show a message about database alteration.
297 */
298 static function alteration_message($message,$type="") {
299 return self::getConn()->alterationMessage($message, $type);
300 }
301
302 }
303 ?>