Merge lp://staging/~drupal-sql-server/pressflow/drupal-sql-server into lp://staging/pressflow
- drupal-sql-server
- Merge into 6
Proposed by
David Strauss
Status: | Work in progress |
---|---|
Proposed branch: | lp://staging/~drupal-sql-server/pressflow/drupal-sql-server |
Merge into: | lp://staging/pressflow |
Diff against target: |
1385 lines (+1303/-6) 8 files modified
INSTALL.sqlsrv.txt (+28/-0) includes/bootstrap.inc (+3/-2) includes/database.sqlsrv.inc (+1105/-0) includes/install.inc (+1/-1) includes/install.sqlsrv.inc (+158/-0) includes/menu.inc (+1/-1) modules/system/system.admin.inc (+6/-2) sites/default/default.settings.php (+1/-0) |
To merge this branch: | bzr merge lp://staging/~drupal-sql-server/pressflow/drupal-sql-server |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Pressflow Administrators | Pending | ||
Review via email: mp+15629@code.staging.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
- 11. By Chrisdpucci
-
Initial bug fixes. Modified sql string in includes/
database. sqlsrv. inc. Made small changes to includes/menu.inc and modules/ system/ system. admin.inc to improve SQL Server compatibility. - 12. By David Strauss
-
Merge Drupal 6.15
- 13. By Chrisdpucci
-
Found/fixed error bug.
Unmerged revisions
- 13. By Chrisdpucci
-
Found/fixed error bug.
- 12. By David Strauss
-
Merge Drupal 6.15
- 11. By Chrisdpucci
-
Initial bug fixes. Modified sql string in includes/
database. sqlsrv. inc. Made small changes to includes/menu.inc and modules/ system/ system. admin.inc to improve SQL Server compatibility. - 10. By David Strauss
-
Apply SQL Server 2005 support patch from Microsoft.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === added file 'INSTALL.sqlsrv.txt' |
2 | --- INSTALL.sqlsrv.txt 1970-01-01 00:00:00 +0000 |
3 | +++ INSTALL.sqlsrv.txt 2009-12-22 23:47:12 +0000 |
4 | @@ -0,0 +1,28 @@ |
5 | +// $Id $ |
6 | + |
7 | +CREATE THE SQL Server 2005 DATABASE |
8 | +------------------------------ |
9 | + |
10 | +1. CREATE DATABASE USER |
11 | + |
12 | +Open SQL Server Management Studio and log in as an administrator. In the |
13 | +Object Explorer window, choose your server. Right click on "Security" and |
14 | +select "New" -> "Login..." in the context menu. If you would like to use a |
15 | +Windows login, enter the name of the user in the username box. Otherwise |
16 | +select "SQL Server authentication" and enter a username and password for the |
17 | +user. Change any other settings as needed. (No other changes are necessary |
18 | +for Drupal to function.) Click OK to save your login. |
19 | + |
20 | +2. CREATE THE DRUPAL DATABASE |
21 | + |
22 | +In SQL Server Management Studio's "Object Explorer" window, right click on |
23 | +"Databases" and select "New Database...". Give the database a name and select |
24 | +the user created in step 1 as the owner. |
25 | + |
26 | +3. SET THE DATABASE USER'S DEFAULT DATABASE |
27 | + |
28 | +In SQL Server Management Studio's "Object Explorer" window, select "Security" |
29 | +-> "Logins" and edit the Drupal user's properties by right-clicking the user |
30 | +and selecing "Properties" in the context menu. Change the user's default |
31 | +database from "master" to the database created in step 2. Click OK to save the |
32 | +changes. |
33 | |
34 | === modified file 'includes/bootstrap.inc' |
35 | --- includes/bootstrap.inc 2009-11-03 07:16:43 +0000 |
36 | +++ includes/bootstrap.inc 2009-12-22 23:47:12 +0000 |
37 | @@ -1037,8 +1037,9 @@ |
38 | * generate an equivalent using other environment variables. |
39 | */ |
40 | function request_uri() { |
41 | - |
42 | - if (isset($_SERVER['REQUEST_URI'])) { |
43 | +// $_SERVER['REQUEST_URI'] not reliable on Win OS |
44 | +// check for current OS and build URI appropriately |
45 | + if (isset($_SERVER['REQUEST_URI']) && (substr(PHP_OS, 0, 3) != 'WIN')) { |
46 | $uri = $_SERVER['REQUEST_URI']; |
47 | } |
48 | else { |
49 | |
50 | === added file 'includes/database.sqlsrv.inc' |
51 | --- includes/database.sqlsrv.inc 1970-01-01 00:00:00 +0000 |
52 | +++ includes/database.sqlsrv.inc 2009-12-22 23:47:12 +0000 |
53 | @@ -0,0 +1,1105 @@ |
54 | +<?php |
55 | +// $Id$ |
56 | + |
57 | +/** |
58 | + * @file |
59 | + * Database interface code for SQL Server database servers. |
60 | + */ |
61 | + |
62 | +/** |
63 | + * @ingroup database |
64 | + * @{ |
65 | + */ |
66 | +/*****************************************************************************\ |
67 | +/******** DRUPAL SQL SERVER METHODS *********\ |
68 | +******************************************************************************* |
69 | +// SQLSRV_RESERVED_REGEX is needed to put quotes around SQL Server reserved words |
70 | +// that are used as identifiers. The regex is a little hairy, but I've done my best |
71 | +// to optimize. Hopefully it's to the point that db access times overshadow it. |
72 | +// If you need to modify it, make sure you've read Friedl first. |
73 | +*/ |
74 | +define('SQLSRV_RESERVED_LIST','action|admin|alias|any|are|array|at|begin|boolean|class|commit|contains|current|data|date|day|depth|domain|end|escape|external|file|full|function|get|go|host|input|language|last|less|local|map|min|module|new|no|object|old|open|operation|parameter|parameters|path|plan|prefix|proc|public|ref|result|returns|role|row|rows|save|search|second|section|session|size|state|statistics|substring|temporary|than|time|timestamp|tran|translate|translation|trim|user|value|variable|view|without'); |
75 | + |
76 | +$DQSTRING = '" [^\\\\"] * (?: \\\\. [^\\\\"] *) * " '; |
77 | +$SQSTRING = '\' [^\\\\\']* (?: \\\\. [^\\\\\']*) * \''; |
78 | + |
79 | +define('SQLSRV_RESERVED_REGEX','/\G |
80 | + \b(?: ('.SQLSRV_RESERVED_LIST.') | ([a-z]+) )\b | |
81 | + \b( [^a-z\'"\\\\]+ )\b | |
82 | + (?=[\'"])( '.$DQSTRING.'|'.$SQSTRING.') |
83 | +/Six'); |
84 | + |
85 | +define('DRUPAL_MINIMUM_SQLSRV', 9); |
86 | + |
87 | +function _sqlsrv_reserved_callback($matches) { |
88 | + if (strlen($matches[1])) { |
89 | + //put quotes around reserved words |
90 | + return '"'.$matches[1].'"'; |
91 | + } elseif (strlen($matches[2])) { |
92 | + //return everything else unchanged |
93 | + return $matches[2]; |
94 | + } elseif (strlen($matches[3])){ |
95 | + return $matches[3]; |
96 | + } elseif (strlen($matches[4])){ |
97 | + return $matches[4]; |
98 | + } |
99 | + return ''; |
100 | +} |
101 | + |
102 | +function _sqlsrv_db_query_callback($match, $init = false) { |
103 | + |
104 | + static $args = null; |
105 | + static $curr_arg = 1; |
106 | + if ($init) { |
107 | + $args = $match; |
108 | + $curr_arg = 1; |
109 | + return; |
110 | + } |
111 | + |
112 | + //XXX: this function is only used with proper parameterization enabled. In |
113 | + //its current state it's broken. Because Drupal allows strings outside queries |
114 | + if ($match[1] == '%%') { |
115 | + return ''; |
116 | + return '%'; |
117 | + } |
118 | + return '?'; |
119 | +} |
120 | + |
121 | +/** |
122 | + * Report database status. |
123 | + */ |
124 | +function db_status_report() { |
125 | + $t = get_t(); |
126 | + $version = db_version(); |
127 | + $form['sqlsrv'] = array( |
128 | + 'title' => $t('SQL Server database'), |
129 | + 'value' => $version, |
130 | + ); |
131 | + |
132 | + if (version_compare($version, DRUPAL_MINIMUM_SQLSRV) < 0) { |
133 | + $form['sqlsrv']['severity'] = REQUIREMENT_ERROR; |
134 | + $form['sqlsrv']['description'] = $t('Your SQL Server is too old. Drupal requires at least SQL Server %version.', array('%version' => DRUPAL_MINIMUM_SQLSRV)); |
135 | + } |
136 | + return $form; |
137 | +} |
138 | + |
139 | +/** |
140 | + * Returns the version of the database server currently in use. |
141 | + * |
142 | + * @return Database server version |
143 | + */ |
144 | +function db_version() { |
145 | + global $active_db; |
146 | + $arrInfo = sqlsrv_server_info($active_db); |
147 | + return $arrInfo['SQLServerVersion']; |
148 | +} |
149 | + |
150 | +/** |
151 | + * Initialize a database connection. |
152 | + */ |
153 | +function db_connect($url) { |
154 | + // Check if SQL Server support is present in PHP |
155 | + if (!extension_loaded('sqlsrv')) { |
156 | + drupal_maintenance_theme(); |
157 | + drupal_set_title('PHP SQL Server support is not enabled'); |
158 | + print theme('maintenance_page', '<p>We were unable to use the SQL Server database because the sqlsrv extension for PHP is not installed. Check your <code>PHP.ini</code> to see how you can enable it.</p> |
159 | +<p>For more help, see the <a href="http://drupal.org/node/258">Installation and upgrading handbook</a>. If you are unsure what these terms mean you should probably contact your hosting provider.</p>'); |
160 | + exit; |
161 | + } |
162 | + |
163 | + $url = parse_url($url); |
164 | + $conn_string = ''; |
165 | + $conn_params = array(); |
166 | + |
167 | + // Decode url-encoded information in the db connection string |
168 | + if (isset($url['user'])) { |
169 | + $conn_params['UID'] = urldecode($url['user']); |
170 | + } |
171 | + if (isset($url['pass'])) { |
172 | + $conn_params['PWD'] = urldecode($url['pass']); |
173 | + } |
174 | + if (isset($url['host'])) { |
175 | + $conn_string .= urldecode($url['host']); |
176 | + } |
177 | + if (isset($url['path'])) { |
178 | + //not supported at the moment |
179 | + } |
180 | + if (isset($url['port'])) { |
181 | + //not supported at the moment |
182 | + } |
183 | + |
184 | + if (array_key_exists('UID', $conn_params) && array_key_exists('PWD', $conn_params)) { |
185 | + // connect using SQL authentication |
186 | + $connection = sqlsrv_connect($conn_string, $conn_params); |
187 | + } |
188 | + else { |
189 | + //otherwise, connect using Windows NT-Auth |
190 | + $connection = sqlsrv_connect($conn_string); |
191 | + } |
192 | + |
193 | + if (!$connection) { |
194 | + drupal_maintenance_theme(); |
195 | + drupal_set_header('HTTP/1.1 503 Service Unavailable'); |
196 | + drupal_set_title('Unable to connect to database'); |
197 | + |
198 | + //XXX: Make sure this prints the right error message (or messages) on connection failure. |
199 | + print theme('maintenance_page', '<p>If you still have to install Drupal, proceed to the <a href="'. base_path() .'install.php">installation page</a>.</p> |
200 | +<p>If you have already finished installing Drupal, this either means that the username and password information in your <code>settings.php</code> file is incorrect or that we can\'t connect to the SQL Server database server. This could mean your hosting provider\'s database server is down.</p> |
201 | +<p>The SQL Server error was: '. theme('placeholder', decode_entities($php_errormsg)) .'</p> |
202 | +<p>Currently, the database is '. theme('placeholder', substr($url['path'], 1)) .', the username is '. theme('placeholder', $url['user']) .', and the database server is '. theme('placeholder', $url['host']) .'.</p> |
203 | +<ul> |
204 | + <li>Are you sure you have the correct username and password?</li> |
205 | + <li>Are you sure that you have typed the correct hostname?</li> |
206 | + <li>Are you sure you have the correct database name?</li> |
207 | + <li>Are you sure that the database server is running?</li> |
208 | +</ul> |
209 | +<p>For more help, see the <a href="http://drupal.org/node/258">Installation and upgrading handbook</a>. If you are unsure what these terms mean you should probably contact your hosting provider.</p>'); |
210 | + exit; |
211 | + } |
212 | + return $connection; |
213 | +} |
214 | + |
215 | +/** |
216 | + * Runs a basic query in the active database. |
217 | + * |
218 | + * User-supplied arguments to the query should be passed in as separate |
219 | + * parameters so that they can be properly escaped to avoid SQL injection |
220 | + * attacks. |
221 | + * |
222 | + * @param $query |
223 | + * A string containing an SQL query. |
224 | + * @param ... |
225 | + * A variable number of arguments which are substituted into the query |
226 | + * using printf() syntax. Instead of a variable number of query arguments, |
227 | + * you may also pass a single array containing the query arguments. |
228 | + * |
229 | + * Valid %-modifiers are: %s, %d, %f, %b (binary data, do not enclose |
230 | + * in '') and %%. |
231 | + * |
232 | + * NOTE: using this syntax will cast NULL and FALSE values to decimal 0, |
233 | + * and TRUE values to decimal 1. |
234 | + * |
235 | + * @return |
236 | + * A database query result resource, or FALSE if the query was not |
237 | + * executed correctly. |
238 | + */ |
239 | +function db_query($query) { |
240 | + $query = db_prefix_tables($query); |
241 | + //get args and remove query |
242 | + $args = func_get_args(); |
243 | + array_shift($args); |
244 | + |
245 | + if (isset($args[0]) && is_array($args[0])) { |
246 | + // 'All arguments in one array' syntax |
247 | + $args = $args[0]; |
248 | + } else if (is_array($args) && isset($args[0]) && count($args) == 1 && is_bool($args[0])) { |
249 | + $args = array(); |
250 | + } |
251 | + |
252 | + list($query, $args) = _db_prep_query($query, $args); |
253 | + |
254 | + return _db_query($query); |
255 | +} |
256 | + |
257 | +/** |
258 | + * Helper function for db_query(). |
259 | + */ |
260 | +function _db_query($query, $args = null) { |
261 | + global $active_db, $last_result, $queries; |
262 | + |
263 | + if (variable_get('dev_query', 0)) { |
264 | + list($usec, $sec) = explode(' ', microtime()); |
265 | + $timer = (float)$usec + (float)$sec; |
266 | + } |
267 | + |
268 | + $last_result = @sqlsrv_query($active_db, $query); |
269 | + |
270 | + if (variable_get('dev_query', 0)) { |
271 | + $bt = debug_backtrace(); |
272 | + $query = $bt[2]['function'] ."\n". $query; |
273 | + list($usec, $sec) = explode(' ', microtime()); |
274 | + $stop = (float)$usec + (float)$sec; |
275 | + $diff = $stop - $timer; |
276 | + $queries[] = array($query, $diff); |
277 | + } |
278 | + |
279 | + if (is_resource($last_result)) { |
280 | + return $last_result; |
281 | + } else { |
282 | + // Indicate to drupal_error_handler that this is a database error. |
283 | + ${DB_ERROR} = TRUE; |
284 | + foreach (sqlsrv_errors() as $error_array) { |
285 | + $error = 'error '.$error_array['code'].': '.$error_array['message']; |
286 | + trigger_error(check_plain($error ."\nfor query: ". $query), E_USER_WARNING); |
287 | + } |
288 | + return FALSE; |
289 | + } |
290 | +} |
291 | + |
292 | +/** |
293 | + * Fetch one result row from the previous query as an object. |
294 | + * |
295 | + * @param $result |
296 | + * A database query result resource, as returned from db_query(). |
297 | + * @return |
298 | + * An object representing the next row of the result, or FALSE. The attributes |
299 | + * of this object are the table fields selected by the query. |
300 | + */ |
301 | + function db_fetch_object($result) { |
302 | + $result_obj = sqlsrv_fetch_object($result); |
303 | + return $result_obj ? $result_obj : NULL; |
304 | +} |
305 | + |
306 | +/** |
307 | + * Fetch one result row from the previous query as an array. |
308 | + * |
309 | + * @param $result |
310 | + * A database query result resource, as returned from db_query(). |
311 | + * @return |
312 | + * An associative array representing the next row of the result, or FALSE. |
313 | + * The keys of this object are the names of the table fields selected by the |
314 | + * query, and the values are the field values for this result row. |
315 | + */ |
316 | +function db_fetch_array($result) { |
317 | + if (is_bool($result)) { |
318 | + error_log("RESULT IS BOOL BEFORE sqlsrv_fetch_array call"); |
319 | + error_log("BACKTRACE: ".print_r(debug_backtrace(),true)); |
320 | + } |
321 | + $result_array = sqlsrv_fetch_array($result, SQLSRV_FETCH_ASSOC); |
322 | + return $result_array ? $result_array : NULL; |
323 | +} |
324 | + |
325 | +/** |
326 | + * Return an individual result field from the previous query. |
327 | + * |
328 | + * Only use this function if exactly one field is being selected; otherwise, |
329 | + * use db_fetch_object() or db_fetch_array(). |
330 | + * |
331 | + * @param $result |
332 | + * A database query result resource, as returned from db_query(). |
333 | + * @return |
334 | + * The resulting field or FALSE. |
335 | + */ |
336 | +function db_result($result) { |
337 | + $array = sqlsrv_fetch_array($result, SQLSRV_FETCH_NUMERIC); |
338 | + return (!empty($array[0])) ? $array[0] : FALSE; |
339 | +} |
340 | + |
341 | +/** |
342 | + * Determine whether the previous query caused an error. |
343 | + */ |
344 | +function db_error() { |
345 | + global $active_db; |
346 | + $sqlsrv_errors = sqlsrv_errors(); |
347 | + if ($sqlsrv_errors) { |
348 | + $error = 'error '.$sqlsrv_errors[0]['code'].': '.$sqlsrv_errors[0]['message']; |
349 | + return $error; |
350 | + } |
351 | +} |
352 | + |
353 | +/** |
354 | + * Returns the last insert id. This function is thread safe. |
355 | + * |
356 | + * @param $table |
357 | + * The name of the table you inserted into. |
358 | + * @param $field |
359 | + * The name of the autoincrement field. |
360 | + */ |
361 | +function db_last_insert_id($table, $field) { |
362 | + return db_result(db_query("SELECT IDENT_CURRENT('{$table}')")); |
363 | +} |
364 | + |
365 | +/** |
366 | + * Determine the number of rows changed by the preceding query. |
367 | + */ |
368 | +function db_affected_rows() { |
369 | + global $last_result; |
370 | + return empty($last_result) ? 0 : sqlsrv_rows_affected($last_result); |
371 | +} |
372 | + |
373 | +/** |
374 | + * Runs a limited-range query in the active database. |
375 | + * |
376 | + * Use this as a substitute for db_query() when a subset of the query |
377 | + * is to be returned. |
378 | + * User-supplied arguments to the query should be passed in as separate |
379 | + * parameters so that they can be properly escaped to avoid SQL injection |
380 | + * attacks. |
381 | + * |
382 | + * @param $query |
383 | + * A string containing an SQL query. |
384 | + * @param ... |
385 | + * A variable number of arguments which are substituted into the query |
386 | + * using printf() syntax. Instead of a variable number of query arguments, |
387 | + * you may also pass a single array containing the query arguments. |
388 | + * Valid %-modifiers are: %s, %d, %f, %b (binary data, do not enclose |
389 | + * in '') and %%. |
390 | + * |
391 | + * NOTE: using this syntax will cast NULL and FALSE values to decimal 0, |
392 | + * and TRUE values to decimal 1. |
393 | + * |
394 | + * @param $from |
395 | + * The first result row to return. |
396 | + * @param $count |
397 | + * The maximum number of result rows to return. |
398 | + * @return |
399 | + * A database query result resource, or FALSE if the query was not executed |
400 | + * correctly. |
401 | + */ |
402 | +function db_query_range($query) { |
403 | + $args = func_get_args(); |
404 | + $count = array_pop($args); |
405 | + $from = array_pop($args); |
406 | + array_shift($args); |
407 | + |
408 | + $query = db_prefix_tables($query); |
409 | + if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax |
410 | + $args = $args[0]; |
411 | + } |
412 | + |
413 | + list($query, $args) = _db_prep_query($query, $args); |
414 | + $query = _db_sqlsrv_limit($query, $count, $from); |
415 | + |
416 | + return _db_query($query); |
417 | +} |
418 | + |
419 | +/** |
420 | +* Converts LIMIT queries to MSSQL TOP queries |
421 | +* |
422 | +* This function is used by db_query_range |
423 | +* |
424 | +* @param $query |
425 | +* The limit query to be rewritten |
426 | +* |
427 | +* @param $total |
428 | +* The 'count' variable |
429 | +* |
430 | +* @param $offset |
431 | +* The amount to offset the result set |
432 | +**/ |
433 | +function _db_sqlsrv_limit($query, $total, $offset = 0) |
434 | + { |
435 | + if($offset === false || $offset == 0) { |
436 | + if (strpos($query, "SELECT") === false) { |
437 | + $query = "TOP {$total} " . $query; |
438 | + } else { |
439 | + $query = preg_replace('/SELECT(\s*DISTINCT)?/Dsi', 'SELECT$1 TOP '.$total, $query); |
440 | + } |
441 | + } else { |
442 | + $query = preg_replace('/SELECT(\s*DISTINCT)?/Dsi', 'SELECT$1 TOP(10000000) ', $query); |
443 | + $query = ' |
444 | + SELECT * FROM ( |
445 | + SELECT sub2.*, ROW_NUMBER() OVER(ORDER BY sub2.line2) AS line3 FROM ( |
446 | + SELECT 1 AS line2, sub1.* FROM (' . $query . ') AS sub1 |
447 | + ) as sub2 |
448 | + ) AS sub3 |
449 | + WHERE line3 BETWEEN ' . ($offset+1) . ' AND ' . ($offset + $total); |
450 | + } |
451 | + return $query; |
452 | + } |
453 | + |
454 | +function _db_prep_query($query, $args) { |
455 | + |
456 | + $query = str_replace('POW(', 'POWER(', $query); |
457 | + $greatest_count = 0; |
458 | + $query = str_replace('GREATEST(', 'dbo.GREATEST(', $query, $greatest_count); |
459 | + |
460 | + if ($greatest_count) { |
461 | + error_log("messing with GREATEST query evar: $query"); |
462 | + } |
463 | + |
464 | + _db_query_callback($args, true); |
465 | + $query = preg_replace_callback(SQLSRV_RESERVED_REGEX,'_sqlsrv_reserved_callback',$query); |
466 | + $query = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $query); |
467 | + |
468 | + return array($query, $args); |
469 | +} |
470 | + |
471 | +/** |
472 | + * Runs a SELECT query and stores its results in a temporary table. |
473 | + * |
474 | + * Use this as a substitute for db_query() when the results need to stored |
475 | + * in a temporary table. Temporary tables exist for the duration of the page |
476 | + * request. |
477 | + * User-supplied arguments to the query should be passed in as separate parameters |
478 | + * so that they can be properly escaped to avoid SQL injection attacks. |
479 | + * |
480 | + * Note that if you need to know how many results were returned, you should do |
481 | + * a SELECT COUNT(*) on the temporary table afterwards. db_affected_rows() does |
482 | + * not give consistent result across different database types in this case. |
483 | + * |
484 | + * @param $query |
485 | + * A string containing a normal SELECT SQL query. |
486 | + * @param ... |
487 | + * A variable number of arguments which are substituted into the query |
488 | + * using printf() syntax. The query arguments can be enclosed in one |
489 | + * array instead. |
490 | + * Valid %-modifiers are: %s, %d, %f, %b (binary data, do not enclose |
491 | + * in '') and %%. |
492 | + * |
493 | + * NOTE: using this syntax will cast NULL and FALSE values to decimal 0, |
494 | + * and TRUE values to decimal 1. |
495 | + * |
496 | + * @param $table |
497 | + * The name of the temporary table to select into. This name will not be |
498 | + * prefixed as there is no risk of collision. |
499 | + * @return |
500 | + * A database query result resource, or FALSE if the query was not executed |
501 | + * correctly. |
502 | + */ |
503 | +function db_query_temporary($query) { |
504 | + global $temporary_table_names; |
505 | + $args = func_get_args(); |
506 | + $tablename = array_pop($args); |
507 | + array_shift($args); |
508 | + |
509 | + $query = preg_replace('/^SELECT(.*?)FROM/i', 'SELECT \1 INTO '. $tablename .' FROM ', db_prefix_tables($query)); |
510 | + if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax |
511 | + $args = $args[0]; |
512 | + } |
513 | + |
514 | + if (is_null($temporary_table_names)) { |
515 | + $temporary_table_names = array($tablename); |
516 | + //It's either this or rewrite more queries. |
517 | + register_shutdown_function('_sqlsrv_temp_table_cleanup'); |
518 | + } |
519 | + else { |
520 | + $temporary_table_names[] = $tablename; |
521 | + } |
522 | + |
523 | + list($query, $args) = _db_prep_query($query, $args); |
524 | + |
525 | + return _db_query($query); |
526 | +} |
527 | + |
528 | +/** |
529 | + * helper function to clean up temporary tables |
530 | + */ |
531 | +function _sqlsrv_temp_table_cleanup() { |
532 | + global $temporary_table_names, $active_db; |
533 | + foreach ($temporary_table_names as $table) { |
534 | + db_query("DROP TABLE $table;"); |
535 | + } |
536 | +} |
537 | + |
538 | +/** |
539 | + * Returns a properly formatted Binary Large OBject value. |
540 | + * |
541 | + * @param $data |
542 | + * Data to encode. |
543 | + * @return |
544 | + * Encoded data. |
545 | + */ |
546 | +function db_encode_blob($data) { |
547 | + //Worst. Escaping. Evar. |
548 | + $data = base64_encode($data); |
549 | + return "'".$data."'"; |
550 | +} |
551 | + |
552 | +/** |
553 | + * Returns text from a Binary Large OBject value. |
554 | + * |
555 | + * @param $data |
556 | + * Data to decode. |
557 | + * @return |
558 | + * Decoded data. |
559 | + */ |
560 | +function db_decode_blob($data) { |
561 | + return base64_decode($data); |
562 | +} |
563 | + |
564 | +/** |
565 | + * Prepare user input for use in a database query, preventing SQL injection attacks. |
566 | + */ |
567 | +function db_escape_string($text) { |
568 | + return str_replace("'", "''", $text); |
569 | +} |
570 | + |
571 | +/** |
572 | + * Lock a table. |
573 | + * This function automatically starts a transaction. |
574 | + */ |
575 | +function db_lock_table($table) { |
576 | + db_query('BEGIN TRANSACTION'); |
577 | +} |
578 | + |
579 | +/** |
580 | + * Unlock all locked tables. |
581 | + * This function automatically commits a transaction. |
582 | + */ |
583 | +function db_unlock_tables() { |
584 | + db_query('COMMIT TRANSACTION'); |
585 | +} |
586 | + |
587 | +/** |
588 | + * Check if a table exists. |
589 | + */ |
590 | +function db_table_exists($table) { |
591 | + return db_result(db_query("SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = '{".db_escape_table($table)."}'")); |
592 | +} |
593 | + |
594 | +/** |
595 | + * Check if a column exists in the given table. |
596 | + */ |
597 | +function db_column_exists($table, $column) { |
598 | + return db_result(db_query("SELECT COALESCE(COL_LENGTH('{".db_escape_table($table)."}','%s'),0)", $column)); |
599 | +} |
600 | + |
601 | +/** |
602 | + * Verify if the database is set up correctly. |
603 | + */ |
604 | +function db_check_setup() { |
605 | + return; |
606 | +} |
607 | + |
608 | +/** |
609 | + * Wraps the given table.field entry with a DISTINCT(). The wrapper is added to |
610 | + * the SELECT list entry of the given query and the resulting query is returned. |
611 | + * This function only applies the wrapper if a DISTINCT doesn't already exist in |
612 | + * the query. |
613 | + * |
614 | + * @param $table Table containing the field to set as DISTINCT |
615 | + * @param $field Field to set as DISTINCT |
616 | + * @param $query Query to apply the wrapper to |
617 | + * @return SQL query with the DISTINCT wrapper surrounding the given table.field. |
618 | + */ |
619 | +function db_distinct_field($table, $field, $query) { |
620 | + $field_to_select = "DISTINCT ON ($table.$field) $table.$field"; |
621 | + // (?<!foo) is to avoid rewriting queries that already use DISTINCT |
622 | + $query = preg_replace( |
623 | + "/(SELECT.*)(?:$table\.|\s)(?<!DISTINCT\()(?<!DISTINCT\($table\.)$field(.*FROM )/AUsi", |
624 | + '\1 '. $field_to_select .'\2', |
625 | + $query |
626 | + ); |
627 | + return $query; |
628 | +} |
629 | + |
630 | +/** |
631 | + * @} End of "ingroup database". |
632 | + */ |
633 | + |
634 | +/** |
635 | + * @ingroup schemaapi |
636 | + * @{ |
637 | + */ |
638 | + |
639 | +/** |
640 | + * This maps a generic data type in combination with its data size |
641 | + * to the engine-specific data type. |
642 | + */ |
643 | +function db_type_map() { |
644 | + // Put :normal last so it gets preserved by array_flip. This makes |
645 | + // it much easier for modules (such as schema.module) to map |
646 | + // database types back into schema types. |
647 | + $map = array( |
648 | + 'varchar:normal' => 'nvarchar', |
649 | + |
650 | + //Unspecified size means 1, which is usually not The Right Thing. |
651 | + //Hopefully this is closer. |
652 | + 'text:tiny' => 'nvarchar(256)', |
653 | + 'text:small' => 'nvarchar(256)', |
654 | + 'text:medium' => 'nvarchar(max)', |
655 | + 'text:big' => 'nvarchar(max)', |
656 | + 'text:normal' => 'nvarchar(4000)', |
657 | + |
658 | + //use small because sqlsrv tiny is unsigned while mysql tiny is signed |
659 | + 'int:tiny' => 'smallint', |
660 | + 'int:small' => 'smallint', |
661 | + 'int:medium' => 'int', |
662 | + 'int:big' => 'bigint', |
663 | + 'int:normal' => 'int', |
664 | + |
665 | + 'float:tiny' => 'real', |
666 | + 'float:small' => 'real', |
667 | + 'float:medium' => 'real', |
668 | + 'float:big' => 'float', |
669 | + 'float:normal' => 'real', |
670 | + |
671 | + 'numeric:normal' => 'numeric', |
672 | + |
673 | + //XXX: eew, eew, eew |
674 | + 'blob:big' => 'nvarchar(max)', |
675 | + 'blob:normal' => 'nvarchar(max)', |
676 | + |
677 | + 'datetime:normal' => 'timestamp', |
678 | + |
679 | + //other code takes care of the serial magic |
680 | + 'serial:tiny' => 'smallint', |
681 | + 'serial:small' => 'smallint', |
682 | + 'serial:medium' => 'int', |
683 | + 'serial:big' => 'bigint', |
684 | + 'serial:normal' => 'int', |
685 | + ); |
686 | + return $map; |
687 | +} |
688 | + |
689 | +/** |
690 | + * Generate SQL to create a new table from a Drupal schema definition. |
691 | + * |
692 | + * @param $name |
693 | + * The name of the table to create. |
694 | + * @param $table |
695 | + * A Schema API table definition array. |
696 | + * @return |
697 | + * An array of SQL statements to create the table. |
698 | + */ |
699 | +function db_create_table_sql($name, $table) { |
700 | + $sql_fields = array(); |
701 | + foreach ($table['fields'] as $field_name => $field) { |
702 | + $sql_fields[] = _db_create_field_sql($field_name, _db_process_field($field)); |
703 | + } |
704 | + |
705 | + $sql_keys = array(); |
706 | + if (isset($table['primary key']) && is_array($table['primary key'])) { |
707 | + $sql_keys[] = 'CONSTRAINT PK_{'. $name .'} PRIMARY KEY ("'. implode('","', $table['primary key']) .'")'; |
708 | + } |
709 | + if (isset($table['unique keys']) && is_array($table['unique keys'])) { |
710 | + foreach ($table['unique keys'] as $key_name => $key) { |
711 | + $sql_keys[] = 'CONSTRAINT UNIQ_{'. $name .'}_'. $key_name .' UNIQUE ("'. implode('","', $key) .'")'; |
712 | + } |
713 | + } |
714 | + |
715 | + $sql = 'CREATE TABLE "{'. $name .'}" ( '."\n\t"; |
716 | + $sql .= implode(",\n\t", $sql_fields); |
717 | + if (count($sql_keys) > 0) { |
718 | + $sql .= ",\n\t"; |
719 | + } |
720 | + $sql .= implode(",\n\t", $sql_keys); |
721 | + $sql .= "\n)"; |
722 | + $statements[] = $sql; |
723 | + |
724 | + if (isset($table['indexes']) && is_array($table['indexes'])) { |
725 | + foreach ($table['indexes'] as $key_name => $key) { |
726 | + $statements[] = _db_create_index_sql($name, $key_name, $key); |
727 | + } |
728 | + } |
729 | + |
730 | + //default constraints have to be added here because I can't generate a |
731 | + //canonical name for them in _db_create_field_sql. Since I need a name |
732 | + //to remove a default constraint, and since modifying a table requires |
733 | + //removing all constraints, I have to do this here. |
734 | + foreach ($table['fields'] as $field_name => $field_spec) { |
735 | + if (isset($field_spec['default'])){ |
736 | + $statements[] = db_field_set_default_sql($name,$field_name,$field_spec['default']); |
737 | + } |
738 | + } |
739 | + |
740 | + return $statements; |
741 | +} |
742 | + |
743 | +function _db_create_index_sql($table, $name, $fields) { |
744 | + $query = "CREATE INDEX $name ON {$table} (". _db_create_key_sql($fields) .') ON [PRIMARY]'; |
745 | + return $query; |
746 | +} |
747 | + |
748 | +function _db_create_key_sql($fields) { |
749 | + $ret = array(); |
750 | + foreach ($fields as $field) { |
751 | + if (is_array($field)) { |
752 | + //$ret[] = 'substr('. $field[0] .', 1, '. $field[1] .')'; |
753 | + //XXX: afaict SQL Server 2005 doesn't support indexing on a subset of the string. |
754 | + $ret[] = $field[0]; |
755 | + } |
756 | + else { |
757 | + $ret[] = $field; |
758 | + } |
759 | + } |
760 | + return implode(', ', $ret); |
761 | +} |
762 | + |
763 | +function _db_create_keys(&$ret, $table, $new_keys) { |
764 | + if (isset($new_keys['primary key'])) { |
765 | + db_add_primary_key($ret, $table, $new_keys['primary key']); |
766 | + } |
767 | + if (isset($new_keys['unique keys'])) { |
768 | + foreach ($new_keys['unique keys'] as $name => $fields) { |
769 | + db_add_unique_key($ret, $table, $name, $fields); |
770 | + } |
771 | + } |
772 | + if (isset($new_keys['indexes'])) { |
773 | + foreach ($new_keys['indexes'] as $name => $fields) { |
774 | + db_add_index($ret, $table, $name, $fields); |
775 | + } |
776 | + } |
777 | +} |
778 | + |
779 | +/** |
780 | + * Set database-engine specific properties for a field. |
781 | + * |
782 | + * @param $field |
783 | + * A field description array, as specified in the schema documentation. |
784 | + */ |
785 | +function _db_process_field($field) { |
786 | + if (!isset($field['size'])) { |
787 | + $field['size'] = 'normal'; |
788 | + } |
789 | + // Set the correct database-engine specific datatype. |
790 | + if (!isset($field['sqlsrv_type'])) { |
791 | + $map = db_type_map(); |
792 | + $field['sqlsrv_type'] = $map[$field['type'] .':'. $field['size']]; |
793 | + } |
794 | + if ($field['type'] == 'serial') { |
795 | + unset($field['not null']); |
796 | + } |
797 | + return $field; |
798 | +} |
799 | + |
800 | +/** |
801 | + * Create an SQL string for a field to be used in table creation or alteration. |
802 | + * |
803 | + * Before passing a field out of a schema definition into this function it has |
804 | + * to be processed by _db_process_field(). |
805 | + * |
806 | + * @param $name |
807 | + * Name of the field. |
808 | + * @param $spec |
809 | + * The field specification, as per the schema data structure format. |
810 | + */ |
811 | +function _db_create_field_sql($name, $spec) { |
812 | + $sql = $name .' '. $spec['sqlsrv_type']; |
813 | + |
814 | + if (!empty($spec['length'])) { |
815 | + $sql .= '('. $spec['length'] .')'; |
816 | + } |
817 | + elseif (isset($spec['precision']) && isset($spec['scale'])) { |
818 | + $sql .= '('. $spec['precision'] .', '. $spec['scale'] .')'; |
819 | + } |
820 | + |
821 | + if ($spec['type'] == 'serial') { |
822 | + unset($spec['not null']); |
823 | + $sql .= ' IDENTITY '; |
824 | + } |
825 | + |
826 | + if (isset($spec['not null']) && $spec['not null']) { |
827 | + $sql .= ' NOT NULL'; |
828 | + } |
829 | + |
830 | + if (!empty($spec['unsigned'])) { |
831 | + $sql .= " CHECK ($name >= 0)"; |
832 | + } |
833 | + |
834 | + return $sql; |
835 | +} |
836 | + |
837 | +/** |
838 | + * Rename a table. |
839 | + * |
840 | + * @param $ret |
841 | + * Array to which query results will be added. |
842 | + * @param $table |
843 | + * The table to be renamed. |
844 | + * @param $new_name |
845 | + * The new name for the table. |
846 | + */ |
847 | +function db_rename_table(&$ret, $table, $new_name) { |
848 | + $ret[] = update_sql('EXECUTE sp_rename "{'.$table.'}", "{'.$new_name.'}"'); |
849 | +} |
850 | + |
851 | +/** |
852 | + * Drop a table. |
853 | + * |
854 | + * @param $ret |
855 | + * Array to which query results will be added. |
856 | + * @param $table |
857 | + * The table to be dropped. |
858 | + */ |
859 | +function db_drop_table(&$ret, $table) { |
860 | + $ret[] = update_sql('DROP TABLE "{'. $table .'}"'); |
861 | +} |
862 | + |
863 | +/** |
864 | + * Add a new field to a table. |
865 | + * |
866 | + * @param $ret |
867 | + * Array to which query results will be added. |
868 | + * @param $table |
869 | + * Name of the table to be altered. |
870 | + * @param $field |
871 | + * Name of the field to be added. |
872 | + * @param $spec |
873 | + * The field specification array, as taken from a schema definition. |
874 | + * The specification may also contain the key 'initial', the newly |
875 | + * created field will be set to the value of the key in all rows. |
876 | + * This is most useful for creating NOT NULL columns with no default |
877 | + * value in existing tables. |
878 | + * @param $keys_new |
879 | + * Optional keys and indexes specification to be created on the |
880 | + * table along with adding the field. The format is the same as a |
881 | + * table specification but without the 'fields' element. If you are |
882 | + * adding a type 'serial' field, you MUST specify at least one key |
883 | + * or index including it in this array. @see db_change_field for more |
884 | + * explanation why. |
885 | + */ |
886 | +function db_add_field(&$ret, $table, $field, $spec, $new_keys = array()) { |
887 | + $fixnull = FALSE; |
888 | + if (!empty($spec['not null']) && !isset($spec['default'])) { |
889 | + $fixnull = TRUE; |
890 | + $spec['not null'] = FALSE; |
891 | + } |
892 | + $query = 'ALTER TABLE "{'. $table .'}" ADD '; |
893 | + $query .= _db_create_field_sql($field, _db_process_field($spec)); |
894 | + $ret[] = update_sql($query); |
895 | + if (isset($spec['initial'])) { |
896 | + // All this because update_sql does not support %-placeholders. |
897 | + $sql = 'UPDATE {'. $table .'} SET '. $field .' = '. db_type_placeholder($spec['type']); |
898 | + $result = db_query($sql, $spec['initial']); |
899 | + $ret[] = array('success' => $result !== FALSE, 'query' => check_plain($sql .' ('. $spec['initial'] .')')); |
900 | + } |
901 | + if ($fixnull) { |
902 | + $spec['not null'] = TRUE; |
903 | + $ret[] = update_sql('ALTER TABLE "{'.$table.'}" ALTER COLUMN '. _db_create_field_sql($field, _db_process_field($spec))); |
904 | + } |
905 | + if (isset($new_keys)) { |
906 | + _db_create_keys($ret, $table, $new_keys); |
907 | + } |
908 | +} |
909 | + |
910 | +/** |
911 | + * Drop a field. |
912 | + * |
913 | + * @param $ret |
914 | + * Array to which query results will be added. |
915 | + * @param $table |
916 | + * The table to be altered. |
917 | + * @param $field |
918 | + * The field to be dropped. |
919 | + */ |
920 | +function db_drop_field(&$ret, $table, $field) { |
921 | + $ret[] = update_sql('ALTER TABLE "{'. $table .'}" DROP COLUMN "'. $field .'"'); |
922 | +} |
923 | + |
924 | +/** |
925 | + * Set the default value for a field. |
926 | + * |
927 | + * @param $ret |
928 | + * Array to which query results will be added. |
929 | + * @param $table |
930 | + * The table to be altered. |
931 | + * @param $field |
932 | + * The field to be altered. |
933 | + * @param $default |
934 | + * Default value to be set. NULL for 'default NULL'. |
935 | + */ |
936 | +function db_field_set_default(&$ret, $table, $field, $default) { |
937 | + $ret[] = update_sql(db_field_set_default_sql($table, $field, $default)); |
938 | +} |
939 | + |
940 | +/** |
941 | + * Helper function for db_field_set_default |
942 | + * |
943 | + * @param $table |
944 | + * The table to be altered. |
945 | + * @param $field |
946 | + * The field to be altered. |
947 | + * @param $default |
948 | + * Default value to be set. NULL for 'default NULL'. |
949 | + */ |
950 | + |
951 | +function db_field_set_default_sql($table, $field, $default) { |
952 | + if ($default === NULL) { |
953 | + $default = 'NULL'; |
954 | + } |
955 | + else { |
956 | + $default = is_string($default) ? "'$default'" : $default; |
957 | + } |
958 | + |
959 | + return 'ALTER TABLE "{'.$table.'}" ADD CONSTRAINT DF_{'.$table.'}_'.$field.' DEFAULT '.$default.' FOR "'.$field.'"'; |
960 | +} |
961 | + |
962 | +/** |
963 | + * Set a field to have no default value. |
964 | + * |
965 | + * @param $ret |
966 | + * Array to which query results will be added. |
967 | + * @param $table |
968 | + * The table to be altered. |
969 | + * @param $field |
970 | + * The field to be altered. |
971 | + */ |
972 | +function db_field_set_no_default(&$ret, $table, $field) { |
973 | + $ret[] = update_sql('ALTER TABLE "{'.$table.'}" DROP CONSTRAINT DF_{'.$table.'}_$field'); |
974 | +} |
975 | + |
976 | +/** |
977 | + * Add a primary key. |
978 | + * |
979 | + * @param $ret |
980 | + * Array to which query results will be added. |
981 | + * @param $table |
982 | + * The table to be altered. |
983 | + * @param $fields |
984 | + * Fields for the primary key. |
985 | + */ |
986 | +function db_add_primary_key(&$ret, $table, $fields) { |
987 | + $ret[] = update_sql('ALTER TABLE "{'. $table .'}"'. |
988 | + ' ADD CONSTRAINT PK_{'. $table .'} PRIMARY KEY ("'. implode('","', $fields) .'")'. |
989 | + ' ON [PRIMARY]'); |
990 | +} |
991 | + |
992 | +/** |
993 | + * Drop the primary key. |
994 | + * |
995 | + * @param $ret |
996 | + * Array to which query results will be added. |
997 | + * @param $table |
998 | + * The table to be altered. |
999 | + */ |
1000 | +function db_drop_primary_key(&$ret, $table) { |
1001 | + $ret[] = update_sql('ALTER TABLE "{'. $table .'}" DROP CONSTRAINT PK_{'. $table .'}'); |
1002 | +} |
1003 | + |
1004 | +/** |
1005 | + * Add a unique key. |
1006 | + * |
1007 | + * @param $ret |
1008 | + * Array to which query results will be added. |
1009 | + * @param $table |
1010 | + * The table to be altered. |
1011 | + * @param $name |
1012 | + * The name of the key. |
1013 | + * @param $fields |
1014 | + * An array of field names. |
1015 | + */ |
1016 | +function db_add_unique_key(&$ret, $table, $name, $fields) { |
1017 | + $name = 'UNIQ_{'. $table .'}_'. $name; |
1018 | + $ret[] = update_sql('ALTER TABLE {'. $table .'} ADD CONSTRAINT '. |
1019 | + $name .' UNIQUE ("'. implode('","', $fields) .'")'); |
1020 | +} |
1021 | + |
1022 | +/** |
1023 | + * Drop a unique key. |
1024 | + *ALTER TABLE test1 DROP COLUMN title_old |
1025 | + * @param $ret |
1026 | + * Array to which query results will be added. |
1027 | + * @param $table |
1028 | + * The table to be altered. |
1029 | + * @param $name |
1030 | + * The name of the key. |
1031 | + */ |
1032 | +function db_drop_unique_key(&$ret, $table, $name) { |
1033 | + $name = 'UNIQ_{'. $table .'}_'. $name; |
1034 | + $ret[] = update_sql('ALTER TABLE {'. $table .'} DROP CONSTRAINT '. $name); |
1035 | +} |
1036 | + |
1037 | +/** |
1038 | + * Add an index. |
1039 | + * |
1040 | + * @param $ret |
1041 | + * Array to which query results will be added. |
1042 | + * @param $table |
1043 | + * The table to be altered. |
1044 | + * @param $name |
1045 | + * The name of the index. |
1046 | + * @param $fields |
1047 | + * An array of field names. |
1048 | + */ |
1049 | +function db_add_index(&$ret, $table, $name, $fields) { |
1050 | + $ret[] = update_sql(_db_create_index_sql($table, $name, $fields)); |
1051 | +} |
1052 | + |
1053 | +/** |
1054 | + * Drop an index. |
1055 | + * |
1056 | + * @param $ret |
1057 | + * Array to which query results will be added. |
1058 | + * @param $table |
1059 | + * The table to be altered. |
1060 | + * @param $name |
1061 | + * The name of the index. |
1062 | + */ |
1063 | +function db_drop_index(&$ret, $table, $name) { |
1064 | + $ret[] = update_sql('DROP INDEX '. $name .' ON "{'. $table .'}"'); |
1065 | +} |
1066 | + |
1067 | +/** |
1068 | + * Change a field definition. |
1069 | + * |
1070 | + * IMPORTANT NOTE: To maintain database portability, you have to explicitly |
1071 | + * recreate all indices and primary keys that are using the changed field. |
1072 | + * |
1073 | + * That means that you have to drop all affected keys and indexes with |
1074 | + * db_drop_{primary_key,unique_key,index}() before calling db_change_field(). |
1075 | + * To recreate the keys and indices, pass the key definitions as the |
1076 | + * optional $new_keys argument directly to db_change_field(). |
1077 | + * |
1078 | + * For example, suppose you have: |
1079 | + * @code |
1080 | + * $schema['foo'] = array( |
1081 | + * 'fields' => array( |
1082 | + * 'bar' => array('type' => 'int', 'not null' => TRUE) |
1083 | + * ), |
1084 | + * 'primary key' => array('bar') |
1085 | + * ); |
1086 | + * @endcode |
1087 | + * and you want to change foo.bar to be type serial, leaving it as the |
1088 | + * primary key. The correct sequence is: |
1089 | + * @code |
1090 | + * db_drop_primary_key($ret, 'foo'); |
1091 | + * db_change_field($ret, 'foo', 'bar', 'bar', |
1092 | + * array('type' => 'serial', 'not null' => TRUE), |
1093 | + * array('primary key' => array('bar'))); |
1094 | + * @endcode |
1095 | + * |
1096 | + * The reasons for this are due to the different database engines: |
1097 | + * |
1098 | + * On PostgreSQL, changing a field definition involves adding a new field |
1099 | + * and dropping an old one which* causes any indices, primary keys and |
1100 | + * sequences (from serial-type fields) that use the changed field to be dropped. |
1101 | + * |
1102 | + * On MySQL, all type 'serial' fields must be part of at least one key |
1103 | + * or index as soon as they are created. You cannot use |
1104 | + * db_add_{primary_key,unique_key,index}() for this purpose because |
1105 | + * the ALTER TABLE command will fail to add the column without a key |
1106 | + * or index specification. The solution is to use the optional |
1107 | + * $new_keys argument to create the key or index at the same time as |
1108 | + * field. |
1109 | + * |
1110 | + * You could use db_add_{primary_key,unique_key,index}() in all cases |
1111 | + * unless you are converting a field to be type serial. You can use |
1112 | + * the $new_keys argument in all cases. |
1113 | + * |
1114 | + * @param $ret |
1115 | + * Array to which query results will be added. |
1116 | + * @param $table |
1117 | + * Name of the table. |
1118 | + * @param $field |
1119 | + * Name of the field to change. |
1120 | + * @param $field_new |
1121 | + * New name for the field (set to the same as $field if you don't want to change the name). |
1122 | + * @param $spec |
1123 | + * The field specification for the new field. |
1124 | + * @param $new_keys |
1125 | + * Optional keys and indexes specification to be created on the |
1126 | + * table along with changing the field. The format is the same as a |
1127 | + * table specification but without the 'fields' element. |
1128 | + */ |
1129 | +function db_change_field(&$ret, $table, $field, $field_new, $spec, $new_keys = array()) { |
1130 | + $ret[] = update_sql("EXECUTE sp_rename '{$table}.$field', '". $field ."_old'"); |
1131 | + $not_null = isset($spec['not null']) ? $spec['not null'] : FALSE; |
1132 | + unset($spec['not null']); |
1133 | + |
1134 | + db_add_field($ret, $table, "$field_new", $spec); |
1135 | + |
1136 | + //copy values from field_old to field_new |
1137 | + //XXX does this do the right thing with all those ugly quotes? |
1138 | + $ret[] = update_sql('UPDATE "{'.$table.'}" SET "'.$field_new.'" = "'. $field .'_old"'); |
1139 | + |
1140 | + if ($not_null) { |
1141 | + $spec['not null'] = TRUE; |
1142 | + unset($spec['default']); |
1143 | + $ret[] = update_sql('ALTER TABLE "{'.$table.'}" ALTER COLUMN '. _db_create_field_sql($field_new, _db_process_field($spec))); |
1144 | + db_field_set_no_default($ret,$table,$field); |
1145 | + db_field_set_default($ret,$table,$field_new,$default); |
1146 | + } |
1147 | + |
1148 | + db_drop_field($ret, $table, $field .'_old'); |
1149 | + |
1150 | + if (isset($new_keys)) { |
1151 | + _db_create_keys($ret, $table, $new_keys); |
1152 | + } |
1153 | +} |
1154 | + |
1155 | +/** |
1156 | + * @} End of "ingroup schemaapi". |
1157 | + */ |
1158 | + |
1159 | |
1160 | === modified file 'includes/install.inc' |
1161 | --- includes/install.inc 2009-02-25 22:34:15 +0000 |
1162 | +++ includes/install.inc 2009-12-22 23:47:12 +0000 |
1163 | @@ -152,7 +152,7 @@ |
1164 | function drupal_detect_database_types() { |
1165 | $databases = array(); |
1166 | |
1167 | - foreach (array('mysql', 'mysqli', 'pgsql') as $type) { |
1168 | + foreach (array('mysql', 'mysqli', 'pgsql', 'sqlsrv') as $type) { |
1169 | if (file_exists('./includes/install.'. $type .'.inc')) { |
1170 | include_once './includes/install.'. $type .'.inc'; |
1171 | $function = $type .'_is_available'; |
1172 | |
1173 | === added file 'includes/install.sqlsrv.inc' |
1174 | --- includes/install.sqlsrv.inc 1970-01-01 00:00:00 +0000 |
1175 | +++ includes/install.sqlsrv.inc 2009-12-22 23:47:12 +0000 |
1176 | @@ -0,0 +1,158 @@ |
1177 | +<?php |
1178 | +// $Id$ |
1179 | + |
1180 | +// SQL Server specific install functions |
1181 | + |
1182 | +/** |
1183 | + * Check if SQL Server is available. |
1184 | + * |
1185 | + * @return |
1186 | + * TRUE/FALSE |
1187 | + */ |
1188 | +function sqlsrv_is_available() { |
1189 | + return extension_loaded('sqlsrv'); |
1190 | +} |
1191 | + |
1192 | +/** |
1193 | + * Check if we can connect to SQL Server. |
1194 | + * |
1195 | + * @return |
1196 | + * TRUE/FALSE |
1197 | + */ |
1198 | +function drupal_test_sqlsrv($url, &$success) { |
1199 | + if (!sqlsrv_is_available()) { |
1200 | + drupal_set_message(st('PHP SQL Server support not enabled.'), 'error'); |
1201 | + return FALSE; |
1202 | + } |
1203 | + |
1204 | + $url = parse_url($url); |
1205 | + |
1206 | + // Decode url-encoded information in the db connection string. |
1207 | + $url['user'] = urldecode($url['user']); |
1208 | + $url['pass'] = urldecode($url['pass']); |
1209 | + $url['host'] = urldecode($url['host']); |
1210 | + $url['path'] = urldecode($url['path']); |
1211 | + |
1212 | + // Build pgsql connection string and allow for non-standard SQL Server port. |
1213 | + $conn_params = array( |
1214 | + 'UID' => $url['user'], |
1215 | + 'PWD' => $url['pass'], |
1216 | + ); |
1217 | + |
1218 | + //XXX: not entirely sure what to do with path |
1219 | + $host_string = $url['host']; |
1220 | + |
1221 | + // Test connecting to the database. |
1222 | + $connection = @sqlsrv_connect($host_string,$conn_params); |
1223 | + if (!$connection) { |
1224 | + drupal_set_message(st('Failure to connect to your SQL Server database server. SQL Server reports the following message: %error.<ul><li>Are you sure you have the correct username and password?</li><li>Are you sure that you have typed the correct database hostname?</li><li>Are you sure that the database server is running?</li><li>Are you sure you typed the correct database name?</li></ul>For more help, see the <a href="http://drupal.org/node/258">Installation and upgrading handbook</a>. If you are unsure what these terms mean you should probably contact your hosting provider.', array('%error' => 'Connection failed. See log file for failure reason')), 'error'); |
1225 | + return FALSE; |
1226 | + } |
1227 | + |
1228 | + $success = array('CONNECT'); |
1229 | + |
1230 | + // Test CREATE. |
1231 | + $query = 'CREATE TABLE drupal_install_test (id integer NOT NULL)'; |
1232 | + $result = sqlsrv_query($connection, $query); |
1233 | + if ($errors = sqlsrv_errors()) { |
1234 | + $strError = ''; |
1235 | + foreach($errors as $arrError) { |
1236 | + $strError .= "SQLState: ".$arrError[ 'SQLSTATE']."\n"; |
1237 | + $strError .= "Error Code: ".$arrError[ 'code']."\n"; |
1238 | + $strError .= "Message: ".$arrError[ 'message']."\n"; |
1239 | + } |
1240 | + drupal_set_message(st('We were unable to create a test table on your SQL Server database server with the command %query. SQL Server reports the following message: %error.<ul><li>Are you sure the configured username has the necessary SQL Server permissions to create tables in the database?</li></ul>For more help, see the <a href="http://drupal.org/node/258">Installation and upgrading handbook</a>. If you are unsure what these terms mean you should probably contact your hosting provider.', array('%query' => $query, '%error' => $strError)), 'error'); |
1241 | + return FALSE; |
1242 | + } |
1243 | + $err = FALSE; |
1244 | + $success[] = 'SELECT'; |
1245 | + $success[] = 'CREATE'; |
1246 | + |
1247 | + // Test INSERT. |
1248 | + $query = 'INSERT INTO drupal_install_test (id) VALUES (1)'; |
1249 | + $stmt = sqlsrv_query($connection, $query); |
1250 | + if ($errors = sqlsrv_errors()) { |
1251 | + $strError = ''; |
1252 | + foreach($errors as $arrError) { |
1253 | + $strError .= "SQLState: ".$arrError[ 'SQLSTATE']."\n"; |
1254 | + $strError .= "Error Code: ".$arrError[ 'code']."\n"; |
1255 | + $strError .= "Message: ".$arrError[ 'message']."\n"; |
1256 | + } |
1257 | + drupal_set_message(st('We were unable to insert a value into a test table on your SQL Server database server. We tried inserting a value with the command %query and SQL Server reported the following error: %error.', array('%query' => $query, '%error' => $strError)), 'error'); |
1258 | + $err = TRUE; |
1259 | + } |
1260 | + else { |
1261 | + $success[] = 'INSERT'; |
1262 | + } |
1263 | + |
1264 | + // Test UPDATE. |
1265 | + $query = 'UPDATE drupal_install_test SET id = 2'; |
1266 | + $stmt = sqlsrv_query($connection, $query); |
1267 | + if ($errors = sqlsrv_errors()) { |
1268 | + $strError = ''; |
1269 | + foreach($errors as $arrError) { |
1270 | + $strError .= "SQLState: ".$arrError[ 'SQLSTATE']."\n"; |
1271 | + $strError .= "Error Code: ".$arrError[ 'code']."\n"; |
1272 | + $strError .= "Message: ".$arrError[ 'message']."\n"; |
1273 | + } |
1274 | + drupal_set_message(st('We were unable to update a value in a test table on your SQL Server database server. We tried updating a value with the command %query and SQL Server reported the following error: %error.', array('%query' => $query, '%error' => $strError)), 'error'); |
1275 | + $err = TRUE; |
1276 | + } |
1277 | + else { |
1278 | + $success[] = 'UPDATE'; |
1279 | + } |
1280 | + |
1281 | + // Test LOCK. |
1282 | + $query = 'UPDATE drupal_install_test WITH (TABLOCK) SET id = 3';//or ROWLOCK |
1283 | + $result = sqlsrv_query($connection, $query); |
1284 | + if ($errors = sqlsrv_errors(SQLSRV_ERR_ALL)) { |
1285 | + $strError = ''; |
1286 | + foreach($errors as $arrError) { |
1287 | + $strError .= "SQLState: ".$arrError[ 'SQLSTATE']."\n"; |
1288 | + $strError .= "Error Code: ".$arrError[ 'code']."\n"; |
1289 | + $strError .= "Message: ".$arrError[ 'message']."\n"; |
1290 | + } |
1291 | + drupal_set_message(st('We were unable to lock a test table on your SQL Server database server. We tried locking a table with the command %query and SQL Server reported the following error: %error.', array('%query' => $query, '%error' => $strError)), 'error'); |
1292 | + $err = TRUE; |
1293 | + } |
1294 | + else { |
1295 | + $success[] = 'LOCK'; |
1296 | + // Test UNLOCK, which is done automatically upon transaction end in SQL Server |
1297 | + $success[] = 'UNLOCK'; |
1298 | + } |
1299 | + |
1300 | + // Test DELETE. |
1301 | + $query = 'DELETE FROM drupal_install_test'; |
1302 | + $stmt = sqlsrv_query($connection, $query); |
1303 | + if ($errors = sqlsrv_errors()) { |
1304 | + $strError = ''; |
1305 | + foreach($errors as $arrError) { |
1306 | + $strError .= "SQLState: ".$arrError[ 'SQLSTATE']."\n"; |
1307 | + $strError .= "Error Code: ".$arrError[ 'code']."\n"; |
1308 | + $strError .= "Message: ".$arrError[ 'message']."\n"; |
1309 | + } |
1310 | + drupal_set_message(st('We were unable to delete a value from a test table on your SQL Server database server. We tried deleting a value with the command %query and SQL Server reported the following error: %error.', array('%query' => $query, '%error' => $strError)), 'error'); |
1311 | + $err = TRUE; |
1312 | + } |
1313 | + else { |
1314 | + $success[] = 'DELETE'; |
1315 | + } |
1316 | + |
1317 | + // Test DROP. |
1318 | + $query = 'DROP TABLE drupal_install_test'; |
1319 | + $stmt = sqlsrv_query($connection, $query); |
1320 | + if ($errors = sqlsrv_errors()) { |
1321 | + $strError = ''; |
1322 | + foreach($errors as $arrError) { |
1323 | + $strError .= "SQLState: ".$arrError[ 'SQLSTATE']."\n"; |
1324 | + $strError .= "Error Code: ".$arrError[ 'code']."\n"; |
1325 | + $strError .= "Message: ".$arrError[ 'message']."\n"; |
1326 | + } |
1327 | + drupal_set_message(st('We were unable to drop a test table from your SQL Server database server. We tried dropping a table with the command %query and SQL Server reported the following error %error.', array('%query' => $query, '%error' => $strError)), 'error'); |
1328 | + $err = TRUE; |
1329 | + } |
1330 | + else { |
1331 | + $success[] = 'DROP'; |
1332 | + } |
1333 | + return TRUE; |
1334 | +} |
1335 | |
1336 | === modified file 'includes/menu.inc' |
1337 | --- includes/menu.inc 2009-12-16 21:46:50 +0000 |
1338 | +++ includes/menu.inc 2009-12-22 23:47:12 +0000 |
1339 | @@ -1939,7 +1939,7 @@ |
1340 | } |
1341 | } while ($parent === FALSE && $parent_path); |
1342 | } |
1343 | - if ($parent !== FALSE) { |
1344 | + if ($parent !== FALSE && (!empty($parent['menu_name']))) { |
1345 | $item['menu_name'] = $parent['menu_name']; |
1346 | } |
1347 | $menu_name = $item['menu_name']; |
1348 | |
1349 | === modified file 'modules/system/system.admin.inc' |
1350 | --- modules/system/system.admin.inc 2009-11-03 07:19:56 +0000 |
1351 | +++ modules/system/system.admin.inc 2009-12-22 23:47:12 +0000 |
1352 | @@ -1715,6 +1715,7 @@ |
1353 | * If true, only returns a boolean whether there are system status errors. |
1354 | */ |
1355 | function system_status($check = FALSE) { |
1356 | + global $active_db; |
1357 | // Load .install files |
1358 | include_once './includes/install.inc'; |
1359 | drupal_load_updates(); |
1360 | @@ -1728,8 +1729,11 @@ |
1361 | } |
1362 | // MySQL import might have set the uid of the anonymous user to autoincrement |
1363 | // value. Let's try fixing it. See http://drupal.org/node/204411 |
1364 | - db_query("UPDATE {users} SET uid = uid - uid WHERE name = '' AND pass = '' AND status = 0"); |
1365 | - |
1366 | + // exclude query if database type is SQL Server |
1367 | + if(get_resource_type($active_db) != 'SQL Server Connection') { |
1368 | + db_query("UPDATE {users} SET uid = uid - uid WHERE name = '' AND pass = '' AND status = 0"); |
1369 | + } |
1370 | + |
1371 | return theme('status_report', $requirements); |
1372 | } |
1373 | |
1374 | |
1375 | === modified file 'sites/default/default.settings.php' |
1376 | --- sites/default/default.settings.php 2009-09-16 20:14:32 +0000 |
1377 | +++ sites/default/default.settings.php 2009-12-22 23:47:12 +0000 |
1378 | @@ -88,6 +88,7 @@ |
1379 | * $db_url = 'mysql://username:password@localhost/databasename'; |
1380 | * $db_url = 'mysqli://username:password@localhost/databasename'; |
1381 | * $db_url = 'pgsql://username:password@localhost/databasename'; |
1382 | + * $db_url = 'sqlsrv://username:password@instance/databasename'; |
1383 | */ |
1384 | $db_url = 'mysql://username:password@localhost/databasename'; |
1385 | $db_prefix = ''; |