Verzeichnisstruktur phpBB-3.2.0


Veröffentlicht
06.01.2017

So funktioniert es


Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück

Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

(Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

mssql.php

Zuletzt modifiziert: 09.10.2024, 12:54 - Dateigröße: 21.20 KiB


001  <?php
002  /**
003   *
004   * This file is part of the phpBB Forum Software package.
005   *
006   * @copyright (c) phpBB Limited <https://www.phpbb.com>
007   * @license GNU General Public License, version 2 (GPL-2.0)
008   *
009   * For full copyright and license information, please see
010   * the docs/CREDITS.txt file.
011   *
012   */
013   
014  namespace phpbb\db\tools;
015   
016  /**
017   * Database Tools for handling cross-db actions such as altering columns, etc.
018   * Currently not supported is returning SQL for creating tables.
019   */
020  class mssql extends tools
021  {
022      /**
023       * Is the used MS SQL Server a SQL Server 2000?
024       * @var bool
025       */
026      protected $is_sql_server_2000;
027   
028      /**
029       * Get the column types for mssql based databases
030       *
031       * @return array
032       */
033      public static function get_dbms_type_map()
034      {
035          return array(
036              'mssql'        => array(
037                  'INT:'        => '[int]',
038                  'BINT'        => '[float]',
039                  'ULINT'        => '[int]',
040                  'UINT'        => '[int]',
041                  'UINT:'        => '[int]',
042                  'TINT:'        => '[int]',
043                  'USINT'        => '[int]',
044                  'BOOL'        => '[int]',
045                  'VCHAR'        => '[varchar] (255)',
046                  'VCHAR:'    => '[varchar] (%d)',
047                  'CHAR:'        => '[char] (%d)',
048                  'XSTEXT'    => '[varchar] (1000)',
049                  'STEXT'        => '[varchar] (3000)',
050                  'TEXT'        => '[varchar] (8000)',
051                  'MTEXT'        => '[text]',
052                  'XSTEXT_UNI'=> '[varchar] (100)',
053                  'STEXT_UNI'    => '[varchar] (255)',
054                  'TEXT_UNI'    => '[varchar] (4000)',
055                  'MTEXT_UNI'    => '[text]',
056                  'TIMESTAMP'    => '[int]',
057                  'DECIMAL'    => '[float]',
058                  'DECIMAL:'    => '[float]',
059                  'PDECIMAL'    => '[float]',
060                  'PDECIMAL:'    => '[float]',
061                  'VCHAR_UNI'    => '[varchar] (255)',
062                  'VCHAR_UNI:'=> '[varchar] (%d)',
063                  'VCHAR_CI'    => '[varchar] (255)',
064                  'VARBINARY'    => '[varchar] (255)',
065              ),
066   
067              'mssqlnative'    => array(
068                  'INT:'        => '[int]',
069                  'BINT'        => '[float]',
070                  'ULINT'        => '[int]',
071                  'UINT'        => '[int]',
072                  'UINT:'        => '[int]',
073                  'TINT:'        => '[int]',
074                  'USINT'        => '[int]',
075                  'BOOL'        => '[int]',
076                  'VCHAR'        => '[varchar] (255)',
077                  'VCHAR:'    => '[varchar] (%d)',
078                  'CHAR:'        => '[char] (%d)',
079                  'XSTEXT'    => '[varchar] (1000)',
080                  'STEXT'        => '[varchar] (3000)',
081                  'TEXT'        => '[varchar] (8000)',
082                  'MTEXT'        => '[text]',
083                  'XSTEXT_UNI'=> '[varchar] (100)',
084                  'STEXT_UNI'    => '[varchar] (255)',
085                  'TEXT_UNI'    => '[varchar] (4000)',
086                  'MTEXT_UNI'    => '[text]',
087                  'TIMESTAMP'    => '[int]',
088                  'DECIMAL'    => '[float]',
089                  'DECIMAL:'    => '[float]',
090                  'PDECIMAL'    => '[float]',
091                  'PDECIMAL:'    => '[float]',
092                  'VCHAR_UNI'    => '[varchar] (255)',
093                  'VCHAR_UNI:'=> '[varchar] (%d)',
094                  'VCHAR_CI'    => '[varchar] (255)',
095                  'VARBINARY'    => '[varchar] (255)',
096              ),
097          );
098      }
099   
100      /**
101      * Constructor. Set DB Object and set {@link $return_statements return_statements}.
102      *
103      * @param \phpbb\db\driver\driver_interface    $db                    Database connection
104      * @param bool        $return_statements    True if only statements should be returned and no SQL being executed
105      */
106      public function __construct(\phpbb\db\driver\driver_interface $db, $return_statements = false)
107      {
108          parent::__construct($db, $return_statements);
109   
110          // Determine mapping database type
111          switch ($this->db->get_sql_layer())
112          {
113              case 'mssql_odbc':
114                  $this->sql_layer = 'mssql';
115              break;
116   
117              case 'mssqlnative':
118                  $this->sql_layer = 'mssqlnative';
119              break;
120          }
121   
122          $this->dbms_type_map = self::get_dbms_type_map();
123      }
124   
125      /**
126       * {@inheritDoc}
127       */
128      function sql_list_tables()
129      {
130          $sql = "SELECT name
131              FROM sysobjects
132              WHERE type='U'";
133          $result = $this->db->sql_query($sql);
134   
135          $tables = array();
136          while ($row = $this->db->sql_fetchrow($result))
137          {
138              $name = current($row);
139              $tables[$name] = $name;
140          }
141          $this->db->sql_freeresult($result);
142   
143          return $tables;
144      }
145   
146      /**
147       * {@inheritDoc}
148       */
149      function sql_create_table($table_name, $table_data)
150      {
151          // holds the DDL for a column
152          $columns = $statements = array();
153   
154          if ($this->sql_table_exists($table_name))
155          {
156              return $this->_sql_run_sql($statements);
157          }
158   
159          // Begin transaction
160          $statements[] = 'begin';
161   
162          // Determine if we have created a PRIMARY KEY in the earliest
163          $primary_key_gen = false;
164   
165          // Determine if the table requires a sequence
166          $create_sequence = false;
167   
168          // Begin table sql statement
169          $table_sql = 'CREATE TABLE [' . $table_name . '] (' . "\n";
170   
171          if (!isset($table_data['PRIMARY_KEY']))
172          {
173              $table_data['COLUMNS']['mssqlindex'] = array('UINT', null, 'auto_increment');
174              $table_data['PRIMARY_KEY'] = 'mssqlindex';
175          }
176   
177          // Iterate through the columns to create a table
178          foreach ($table_data['COLUMNS'] as $column_name => $column_data)
179          {
180              // here lies an array, filled with information compiled on the column's data
181              $prepared_column = $this->sql_prepare_column_data($table_name, $column_name, $column_data);
182   
183              if (isset($prepared_column['auto_increment']) && $prepared_column['auto_increment'] && strlen($column_name) > 26) // "${column_name}_gen"
184              {
185                  trigger_error("Index name '${column_name}_gen' on table '$table_name' is too long. The maximum auto increment column length is 26 characters.", E_USER_ERROR);
186              }
187   
188              // here we add the definition of the new column to the list of columns
189              $columns[] = "\t [{$column_name}" . $prepared_column['column_type_sql_default'];
190   
191              // see if we have found a primary key set due to a column definition if we have found it, we can stop looking
192              if (!$primary_key_gen)
193              {
194                  $primary_key_gen = isset($prepared_column['primary_key_set']) && $prepared_column['primary_key_set'];
195              }
196   
197              // create sequence DDL based off of the existance of auto incrementing columns
198              if (!$create_sequence && isset($prepared_column['auto_increment']) && $prepared_column['auto_increment'])
199              {
200                  $create_sequence = $column_name;
201              }
202          }
203   
204          // this makes up all the columns in the create table statement
205          $table_sql .= implode(",\n", $columns);
206   
207          // Close the table for two DBMS and add to the statements
208          $table_sql .= "\n);";
209          $statements[] = $table_sql;
210   
211          // we have yet to create a primary key for this table,
212          // this means that we can add the one we really wanted instead
213          if (!$primary_key_gen)
214          {
215              // Write primary key
216              if (isset($table_data['PRIMARY_KEY']))
217              {
218                  if (!is_array($table_data['PRIMARY_KEY']))
219                  {
220                      $table_data['PRIMARY_KEY'] = array($table_data['PRIMARY_KEY']);
221                  }
222   
223                  // We need the data here
224                  $old_return_statements = $this->return_statements;
225                  $this->return_statements = true;
226   
227                  $primary_key_stmts = $this->sql_create_primary_key($table_name, $table_data['PRIMARY_KEY']);
228                  foreach ($primary_key_stmts as $pk_stmt)
229                  {
230                      $statements[] = $pk_stmt;
231                  }
232   
233                  $this->return_statements = $old_return_statements;
234              }
235          }
236   
237          // Write Keys
238          if (isset($table_data['KEYS']))
239          {
240              foreach ($table_data['KEYS'] as $key_name => $key_data)
241              {
242                  if (!is_array($key_data[1]))
243                  {
244                      $key_data[1] = array($key_data[1]);
245                  }
246   
247                  $old_return_statements = $this->return_statements;
248                  $this->return_statements = true;
249   
250                  $key_stmts = ($key_data[0] == 'UNIQUE') ? $this->sql_create_unique_index($table_name, $key_name, $key_data[1]) : $this->sql_create_index($table_name, $key_name, $key_data[1]);
251   
252                  foreach ($key_stmts as $key_stmt)
253                  {
254                      $statements[] = $key_stmt;
255                  }
256   
257                  $this->return_statements = $old_return_statements;
258              }
259          }
260   
261          // Commit Transaction
262          $statements[] = 'commit';
263   
264          return $this->_sql_run_sql($statements);
265      }
266   
267      /**
268       * {@inheritDoc}
269       */
270      function sql_list_columns($table_name)
271      {
272          $columns = array();
273   
274          $sql = "SELECT c.name
275              FROM syscolumns c
276              LEFT JOIN sysobjects o ON c.id = o.id
277              WHERE o.name = '{$table_name}'";
278          $result = $this->db->sql_query($sql);
279   
280          while ($row = $this->db->sql_fetchrow($result))
281          {
282              $column = strtolower(current($row));
283              $columns[$column] = $column;
284          }
285          $this->db->sql_freeresult($result);
286   
287          return $columns;
288      }
289   
290      /**
291       * {@inheritDoc}
292       */
293      function sql_index_exists($table_name, $index_name)
294      {
295          $sql = "EXEC sp_statistics '$table_name'";
296          $result = $this->db->sql_query($sql);
297   
298          while ($row = $this->db->sql_fetchrow($result))
299          {
300              if ($row['TYPE'] == 3)
301              {
302                  if (strtolower($row['INDEX_NAME']) == strtolower($index_name))
303                  {
304                      $this->db->sql_freeresult($result);
305                      return true;
306                  }
307              }
308          }
309          $this->db->sql_freeresult($result);
310   
311          return false;
312      }
313   
314      /**
315       * {@inheritDoc}
316       */
317      function sql_unique_index_exists($table_name, $index_name)
318      {
319          $sql = "EXEC sp_statistics '$table_name'";
320          $result = $this->db->sql_query($sql);
321   
322          while ($row = $this->db->sql_fetchrow($result))
323          {
324              // Usually NON_UNIQUE is the column we want to check, but we allow for both
325              if ($row['TYPE'] == 3)
326              {
327                  if (strtolower($row['INDEX_NAME']) == strtolower($index_name))
328                  {
329                      $this->db->sql_freeresult($result);
330                      return true;
331                  }
332              }
333          }
334          $this->db->sql_freeresult($result);
335   
336          return false;
337      }
338   
339      /**
340       * {@inheritDoc}
341      */
342      function sql_prepare_column_data($table_name, $column_name, $column_data)
343      {
344          if (strlen($column_name) > 30)
345          {
346              trigger_error("Column name '$column_name' on table '$table_name' is too long. The maximum is 30 characters.", E_USER_ERROR);
347          }
348   
349          // Get type
350          list($column_type, ) = $this->get_column_type($column_data[0]);
351   
352          // Adjust default value if db-dependent specified
353          if (is_array($column_data[1]))
354          {
355              $column_data[1] = (isset($column_data[1][$this->sql_layer])) ? $column_data[1][$this->sql_layer] : $column_data[1]['default'];
356          }
357   
358          $sql = '';
359   
360          $return_array = array();
361   
362          $sql .= " {$column_type} ";
363          $sql_default = " {$column_type} ";
364   
365          // For adding columns we need the default definition
366          if (!is_null($column_data[1]))
367          {
368              // For hexadecimal values do not use single quotes
369              if (strpos($column_data[1], '0x') === 0)
370              {
371                  $return_array['default'] = 'DEFAULT (' . $column_data[1] . ') ';
372                  $sql_default .= $return_array['default'];
373              }
374              else
375              {
376                  $return_array['default'] = 'DEFAULT (' . ((is_numeric($column_data[1])) ? $column_data[1] : "'{$column_data[1]}'") . ') ';
377                  $sql_default .= $return_array['default'];
378              }
379          }
380   
381          if (isset($column_data[2]) && $column_data[2] == 'auto_increment')
382          {
383              // $sql .= 'IDENTITY (1, 1) ';
384              $sql_default .= 'IDENTITY (1, 1) ';
385          }
386   
387          $return_array['textimage'] = $column_type === '[text]';
388   
389          if (!is_null($column_data[1]) || (isset($column_data[2]) && $column_data[2] == 'auto_increment'))
390          {
391              $sql .= 'NOT NULL';
392              $sql_default .= 'NOT NULL';
393          }
394          else
395          {
396              $sql .= 'NULL';
397              $sql_default .= 'NULL';
398          }
399   
400          $return_array['column_type_sql_default'] = $sql_default;
401   
402          $return_array['column_type_sql'] = $sql;
403   
404          return $return_array;
405      }
406   
407      /**
408       * {@inheritDoc}
409       */
410      function sql_column_add($table_name, $column_name, $column_data, $inline = false)
411      {
412          $column_data = $this->sql_prepare_column_data($table_name, $column_name, $column_data);
413          $statements = array();
414   
415          // Does not support AFTER, only through temporary table
416          $statements[] = 'ALTER TABLE [' . $table_name . '] ADD [' . $column_name . '] ' . $column_data['column_type_sql_default'];
417   
418          return $this->_sql_run_sql($statements);
419      }
420   
421      /**
422       * {@inheritDoc}
423       */
424      function sql_column_remove($table_name, $column_name, $inline = false)
425      {
426          $statements = array();
427   
428          // We need the data here
429          $old_return_statements = $this->return_statements;
430          $this->return_statements = true;
431   
432          $indexes = $this->get_existing_indexes($table_name, $column_name);
433          $indexes = array_merge($indexes, $this->get_existing_indexes($table_name, $column_name, true));
434   
435          // Drop any indexes
436          $recreate_indexes = array();
437          if (!empty($indexes))
438          {
439              foreach ($indexes as $index_name => $index_data)
440              {
441                  $result = $this->sql_index_drop($table_name, $index_name);
442                  $statements = array_merge($statements, $result);
443                  if (sizeof($index_data) > 1)
444                  {
445                      // Remove this column from the index and recreate it
446                      $recreate_indexes[$index_name] = array_diff($index_data, array($column_name));
447                  }
448              }
449          }
450   
451          // Drop default value constraint
452          $result = $this->mssql_get_drop_default_constraints_queries($table_name, $column_name);
453          $statements = array_merge($statements, $result);
454   
455          // Remove the column
456          $statements[] = 'ALTER TABLE [' . $table_name . '] DROP COLUMN [' . $column_name . ']';
457   
458          if (!empty($recreate_indexes))
459          {
460              // Recreate indexes after we removed the column
461              foreach ($recreate_indexes as $index_name => $index_data)
462              {
463                  $result = $this->sql_create_index($table_name, $index_name, $index_data);
464                  $statements = array_merge($statements, $result);
465              }
466          }
467   
468          $this->return_statements = $old_return_statements;
469   
470          return $this->_sql_run_sql($statements);
471      }
472   
473      /**
474       * {@inheritDoc}
475       */
476      function sql_index_drop($table_name, $index_name)
477      {
478          $statements = array();
479   
480          $statements[] = 'DROP INDEX ' . $table_name . '.' . $index_name;
481   
482          return $this->_sql_run_sql($statements);
483      }
484   
485      /**
486       * {@inheritDoc}
487       */
488      function sql_table_drop($table_name)
489      {
490          $statements = array();
491   
492          if (!$this->sql_table_exists($table_name))
493          {
494              return $this->_sql_run_sql($statements);
495          }
496   
497          // the most basic operation, get rid of the table
498          $statements[] = 'DROP TABLE ' . $table_name;
499   
500          return $this->_sql_run_sql($statements);
501      }
502   
503      /**
504       * {@inheritDoc}
505       */
506      function sql_create_primary_key($table_name, $column, $inline = false)
507      {
508          $statements = array();
509   
510          $sql = "ALTER TABLE [{$table_name}] WITH NOCHECK ADD ";
511          $sql .= "CONSTRAINT [PK_{$table_name}] PRIMARY KEY  CLUSTERED (";
512          $sql .= '[' . implode("],\n\t\t[", $column) . ']';
513          $sql .= ')';
514   
515          $statements[] = $sql;
516   
517          return $this->_sql_run_sql($statements);
518      }
519   
520      /**
521       * {@inheritDoc}
522       */
523      function sql_create_unique_index($table_name, $index_name, $column)
524      {
525          $statements = array();
526   
527          $this->check_index_name_length($table_name, $index_name);
528   
529          $statements[] = 'CREATE UNIQUE INDEX [' . $index_name . '] ON [' . $table_name . ']([' . implode('], [', $column) . '])';
530   
531          return $this->_sql_run_sql($statements);
532      }
533   
534      /**
535       * {@inheritDoc}
536       */
537      function sql_create_index($table_name, $index_name, $column)
538      {
539          $statements = array();
540   
541          $this->check_index_name_length($table_name, $index_name);
542   
543          // remove index length
544          $column = preg_replace('#:.*$#', '', $column);
545   
546          $statements[] = 'CREATE INDEX [' . $index_name . '] ON [' . $table_name . ']([' . implode('], [', $column) . '])';
547   
548          return $this->_sql_run_sql($statements);
549      }
550   
551      /**
552       * {@inheritDoc}
553       */
554      function sql_list_index($table_name)
555      {
556          $index_array = array();
557          $sql = "EXEC sp_statistics '$table_name'";
558          $result = $this->db->sql_query($sql);
559          while ($row = $this->db->sql_fetchrow($result))
560          {
561              if ($row['TYPE'] == 3)
562              {
563                  $index_array[] = strtolower($row['INDEX_NAME']);
564              }
565          }
566          $this->db->sql_freeresult($result);
567   
568          return $index_array;
569      }
570   
571      /**
572       * {@inheritDoc}
573       */
574      function sql_column_change($table_name, $column_name, $column_data, $inline = false)
575      {
576          $column_data = $this->sql_prepare_column_data($table_name, $column_name, $column_data);
577          $statements = array();
578   
579          // We need the data here
580          $old_return_statements = $this->return_statements;
581          $this->return_statements = true;
582   
583          $indexes = $this->get_existing_indexes($table_name, $column_name);
584          $unique_indexes = $this->get_existing_indexes($table_name, $column_name, true);
585   
586          // Drop any indexes
587          if (!empty($indexes) || !empty($unique_indexes))
588          {
589              $drop_indexes = array_merge(array_keys($indexes), array_keys($unique_indexes));
590              foreach ($drop_indexes as $index_name)
591              {
592                  $result = $this->sql_index_drop($table_name, $index_name);
593                  $statements = array_merge($statements, $result);
594              }
595          }
596   
597          // Drop default value constraint
598          $result = $this->mssql_get_drop_default_constraints_queries($table_name, $column_name);
599          $statements = array_merge($statements, $result);
600   
601          // Change the column
602          $statements[] = 'ALTER TABLE [' . $table_name . '] ALTER COLUMN [' . $column_name . '] ' . $column_data['column_type_sql'];
603   
604          if (!empty($column_data['default']))
605          {
606              // Add new default value constraint
607              $statements[] = 'ALTER TABLE [' . $table_name . '] ADD CONSTRAINT [DF_' . $table_name . '_' . $column_name . '_1] ' . $column_data['default'] . ' FOR [' . $column_name . ']';
608          }
609   
610          if (!empty($indexes))
611          {
612              // Recreate indexes after we changed the column
613              foreach ($indexes as $index_name => $index_data)
614              {
615                  $result = $this->sql_create_index($table_name, $index_name, $index_data);
616                  $statements = array_merge($statements, $result);
617              }
618          }
619   
620          if (!empty($unique_indexes))
621          {
622              // Recreate unique indexes after we changed the column
623              foreach ($unique_indexes as $index_name => $index_data)
624              {
625                  $result = $this->sql_create_unique_index($table_name, $index_name, $index_data);
626                  $statements = array_merge($statements, $result);
627              }
628          }
629   
630          $this->return_statements = $old_return_statements;
631   
632          return $this->_sql_run_sql($statements);
633      }
634   
635      /**
636      * Get queries to drop the default constraints of a column
637      *
638      * We need to drop the default constraints of a column,
639      * before being able to change their type or deleting them.
640      *
641      * @param string $table_name
642      * @param string $column_name
643      * @return array        Array with SQL statements
644      */
645      protected function mssql_get_drop_default_constraints_queries($table_name, $column_name)
646      {
647          $statements = array();
648          if ($this->mssql_is_sql_server_2000())
649          {
650              // http://msdn.microsoft.com/en-us/library/aa175912%28v=sql.80%29.aspx
651              // Deprecated in SQL Server 2005
652              $sql = "SELECT so.name AS def_name
653                  FROM sysobjects so
654                  JOIN sysconstraints sc ON so.id = sc.constid
655                  WHERE object_name(so.parent_obj) = '{$table_name}'
656                      AND so.xtype = 'D'
657                      AND sc.colid = (SELECT colid FROM syscolumns
658                          WHERE id = object_id('{$table_name}')
659                              AND name = '{$column_name}')";
660          }
661          else
662          {
663              $sql = "SELECT dobj.name AS def_name
664                  FROM sys.columns col
665                      LEFT OUTER JOIN sys.objects dobj ON (dobj.object_id = col.default_object_id AND dobj.type = 'D')
666                  WHERE col.object_id = object_id('{$table_name}')
667                      AND col.name = '{$column_name}'
668                      AND dobj.name IS NOT NULL";
669          }
670   
671          $result = $this->db->sql_query($sql);
672          while ($row = $this->db->sql_fetchrow($result))
673          {
674              $statements[] = 'ALTER TABLE [' . $table_name . '] DROP CONSTRAINT [' . $row['def_name'] . ']';
675          }
676          $this->db->sql_freeresult($result);
677   
678          return $statements;
679      }
680   
681      /**
682      * Get a list with existing indexes for the column
683      *
684      * @param string $table_name
685      * @param string $column_name
686      * @param bool $unique Should we get unique indexes or normal ones
687      * @return array        Array with Index name => columns
688      */
689      public function get_existing_indexes($table_name, $column_name, $unique = false)
690      {
691          $existing_indexes = array();
692          if ($this->mssql_is_sql_server_2000())
693          {
694              // http://msdn.microsoft.com/en-us/library/aa175912%28v=sql.80%29.aspx
695              // Deprecated in SQL Server 2005
696              $sql = "SELECT DISTINCT ix.name AS phpbb_index_name
697                  FROM sysindexes ix
698                  INNER JOIN sysindexkeys ixc
699                      ON ixc.id = ix.id
700                          AND ixc.indid = ix.indid
701                  INNER JOIN syscolumns cols
702                      ON cols.colid = ixc.colid
703                          AND cols.id = ix.id
704                  WHERE ix.id = object_id('{$table_name}')
705                      AND cols.name = '{$column_name}'
706                      AND INDEXPROPERTY(ix.id, ix.name, 'IsUnique') = " . ($unique ? '1' : '0');
707          }
708          else
709          {
710              $sql = "SELECT DISTINCT ix.name AS phpbb_index_name
711                  FROM sys.indexes ix
712                  INNER JOIN sys.index_columns ixc
713                      ON ixc.object_id = ix.object_id
714                          AND ixc.index_id = ix.index_id
715                  INNER JOIN sys.columns cols
716                      ON cols.column_id = ixc.column_id
717                          AND cols.object_id = ix.object_id
718                  WHERE ix.object_id = object_id('{$table_name}')
719                      AND cols.name = '{$column_name}'
720                      AND ix.is_unique = " . ($unique ? '1' : '0');
721          }
722   
723          $result = $this->db->sql_query($sql);
724          while ($row = $this->db->sql_fetchrow($result))
725          {
726              if (!isset($row['is_unique']) || ($unique && $row['is_unique'] == 'UNIQUE') || (!$unique && $row['is_unique'] == 'NONUNIQUE'))
727              {
728                  $existing_indexes[$row['phpbb_index_name']] = array();
729              }
730          }
731          $this->db->sql_freeresult($result);
732   
733          if (empty($existing_indexes))
734          {
735              return array();
736          }
737   
738          if ($this->mssql_is_sql_server_2000())
739          {
740              $sql = "SELECT DISTINCT ix.name AS phpbb_index_name, cols.name AS phpbb_column_name
741                  FROM sysindexes ix
742                  INNER JOIN sysindexkeys ixc
743                      ON ixc.id = ix.id
744                          AND ixc.indid = ix.indid
745                  INNER JOIN syscolumns cols
746                      ON cols.colid = ixc.colid
747                          AND cols.id = ix.id
748                  WHERE ix.id = object_id('{$table_name}')
749                      AND " . $this->db->sql_in_set('ix.name', array_keys($existing_indexes));
750          }
751          else
752          {
753              $sql = "SELECT DISTINCT ix.name AS phpbb_index_name, cols.name AS phpbb_column_name
754                  FROM sys.indexes ix
755                  INNER JOIN sys.index_columns ixc
756                      ON ixc.object_id = ix.object_id
757                          AND ixc.index_id = ix.index_id
758                  INNER JOIN sys.columns cols
759                      ON cols.column_id = ixc.column_id
760                          AND cols.object_id = ix.object_id
761                  WHERE ix.object_id = object_id('{$table_name}')
762                      AND " . $this->db->sql_in_set('ix.name', array_keys($existing_indexes));
763          }
764   
765          $result = $this->db->sql_query($sql);
766          while ($row = $this->db->sql_fetchrow($result))
767          {
768              $existing_indexes[$row['phpbb_index_name']][] = $row['phpbb_column_name'];
769          }
770          $this->db->sql_freeresult($result);
771   
772          return $existing_indexes;
773      }
774   
775      /**
776      * Is the used MS SQL Server a SQL Server 2000?
777      *
778      * @return bool
779      */
780      protected function mssql_is_sql_server_2000()
781      {
782          if ($this->is_sql_server_2000 === null)
783          {
784              $sql = "SELECT CAST(SERVERPROPERTY('productversion') AS VARCHAR(25)) AS mssql_version";
785              $result = $this->db->sql_query($sql);
786              $properties = $this->db->sql_fetchrow($result);
787              $this->db->sql_freeresult($result);
788              $this->is_sql_server_2000 = $properties['mssql_version'][0] == '8';
789          }
790   
791          return $this->is_sql_server_2000;
792      }
793   
794  }
795