Merge lp://staging/~drupal-sql-server/pressflow/drupal-sql-server into lp://staging/pressflow

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
Reviewer Review Type Date Requested Status
Pressflow Administrators Pending
Review via email: mp+15629@code.staging.launchpad.net
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 = '';

Subscribers

People subscribed via source and target branches

to status/vote changes: