dbo_mssql.php

Go to the documentation of this file.
00001 <?php
00002 /* SVN FILE: $Id: dbo__mssql_8php-source.html 580 2008-07-01 14:45:49Z gwoo $ */
00003 /**
00004  * MS SQL layer for DBO
00005  *
00006  * Long description for file
00007  *
00008  * PHP versions 4 and 5
00009  *
00010  * CakePHP(tm) :  Rapid Development Framework <http://www.cakephp.org/>
00011  * Copyright 2005-2008, Cake Software Foundation, Inc.
00012  *                              1785 E. Sahara Avenue, Suite 490-204
00013  *                              Las Vegas, Nevada 89104
00014  *
00015  * Licensed under The MIT License
00016  * Redistributions of files must retain the above copyright notice.
00017  *
00018  * @filesource
00019  * @copyright       Copyright 2005-2008, Cake Software Foundation, Inc.
00020  * @link                http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
00021  * @package         cake
00022  * @subpackage      cake.cake.libs.model.datasources.dbo
00023  * @since           CakePHP(tm) v 0.10.5.1790
00024  * @version         $Revision: 580 $
00025  * @modifiedby      $LastChangedBy: gwoo $
00026  * @lastmodified    $Date: 2008-07-01 09:45:49 -0500 (Tue, 01 Jul 2008) $
00027  * @license         http://www.opensource.org/licenses/mit-license.php The MIT License
00028  */
00029 
00030 /**
00031  * Short description for class.
00032  *
00033  * Long description for class
00034  *
00035  * @package     cake
00036  * @subpackage  cake.cake.libs.model.datasources.dbo
00037  */
00038 class DboMssql extends DboSource {
00039 /**
00040  * Driver description
00041  *
00042  * @var string
00043  */
00044     var $description = "MS SQL DBO Driver";
00045 /**
00046  * Starting quote character for quoted identifiers
00047  *
00048  * @var string
00049  */
00050     var $startQuote = "[";
00051 /**
00052  * Ending quote character for quoted identifiers
00053  *
00054  * @var string
00055  */
00056     var $endQuote = "]";
00057 /**
00058  * Creates a map between field aliases and numeric indexes.  Workaround for the
00059  * SQL Server driver's 30-character column name limitation.
00060  *
00061  * @var array
00062  */
00063     var $__fieldMappings = array();
00064 /**
00065  * Base configuration settings for MS SQL driver
00066  *
00067  * @var array
00068  */
00069     var $_baseConfig = array(
00070         'persistent' => true,
00071         'host' => 'localhost',
00072         'login' => 'root',
00073         'password' => '',
00074         'database' => 'cake',
00075         'port' => '1433',
00076     );
00077 /**
00078  * MS SQL column definition
00079  *
00080  * @var array
00081  */
00082     var $columns = array(
00083         'primary_key' => array('name' => 'IDENTITY (1, 1) NOT NULL'),
00084         'string'    => array('name' => 'varchar', 'limit' => '255'),
00085         'text'      => array('name' => 'text'),
00086         'integer'   => array('name' => 'int', 'formatter' => 'intval'),
00087         'float'     => array('name' => 'numeric', 'formatter' => 'floatval'),
00088         'datetime'  => array('name' => 'datetime', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
00089         'timestamp' => array('name' => 'timestamp', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
00090         'time'      => array('name' => 'datetime', 'format' => 'H:i:s', 'formatter' => 'date'),
00091         'date'      => array('name' => 'datetime', 'format' => 'Y-m-d', 'formatter' => 'date'),
00092         'binary'    => array('name' => 'image'),
00093         'boolean'   => array('name' => 'bit')
00094     );
00095 /**
00096  * Index of basic SQL commands
00097  *
00098  * @var array
00099  * @access protected
00100  */
00101     var $_commands = array(
00102         'begin'    => 'BEGIN TRANSACTION',
00103         'commit'   => 'COMMIT',
00104         'rollback' => 'ROLLBACK'
00105     );
00106 /**
00107  * MS SQL DBO driver constructor; sets SQL Server error reporting defaults
00108  *
00109  * @param array $config Configuration data from app/config/databases.php
00110  * @return boolean True if connected successfully, false on error
00111  */
00112     function __construct($config, $autoConnect = true) {
00113         if ($autoConnect) {
00114             if (!function_exists('mssql_min_message_severity')) {
00115                 trigger_error("PHP SQL Server interface is not installed, cannot continue.  For troubleshooting information, see http://php.net/mssql/", E_USER_WARNING);
00116             }
00117             mssql_min_message_severity(15);
00118             mssql_min_error_severity(2);
00119         }
00120         return parent::__construct($config, $autoConnect);
00121     }
00122 /**
00123  * Connects to the database using options in the given configuration array.
00124  *
00125  * @return boolean True if the database could be connected, else false
00126  */
00127     function connect() {
00128         $config = $this->config;
00129 
00130         $os = env('OS');
00131         if (!empty($os) && strpos($os, 'Windows') !== false) {
00132             $sep = ',';
00133         } else {
00134             $sep = ':';
00135         }
00136         $this->connected = false;
00137 
00138         if (is_numeric($config['port'])) {
00139             $port = $sep . $config['port']; // Port number
00140         } elseif ($config['port'] === null) {
00141             $port = '';                     // No port - SQL Server 2005
00142         } else {
00143             $port = '\\' . $config['port']; // Named pipe
00144         }
00145 
00146         if (!$config['persistent']) {
00147             $this->connection = mssql_connect($config['host'] . $port, $config['login'], $config['password'], true);
00148         } else {
00149             $this->connection = mssql_pconnect($config['host'] . $port, $config['login'], $config['password']);
00150         }
00151 
00152         if (mssql_select_db($config['database'], $this->connection)) {
00153             $this->_execute("SET DATEFORMAT ymd");
00154             $this->connected = true;
00155         }
00156         return $this->connected;
00157     }
00158 /**
00159  * Disconnects from database.
00160  *
00161  * @return boolean True if the database could be disconnected, else false
00162  */
00163     function disconnect() {
00164         @mssql_free_result($this->results);
00165         $this->connected = !@mssql_close($this->connection);
00166         return !$this->connected;
00167     }
00168 /**
00169  * Executes given SQL statement.
00170  *
00171  * @param string $sql SQL statement
00172  * @return resource Result resource identifier
00173  * @access protected
00174  */
00175     function _execute($sql) {
00176         return mssql_query($sql, $this->connection);
00177     }
00178 /**
00179  * Returns an array of sources (tables) in the database.
00180  *
00181  * @return array Array of tablenames in the database
00182  */
00183     function listSources() {
00184         $cache = parent::listSources();
00185 
00186         if ($cache != null) {
00187             return $cache;
00188         }
00189         $result = $this->fetchAll('SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES');
00190 
00191         if (!$result || empty($result)) {
00192             return array();
00193         } else {
00194             $tables = array();
00195 
00196             foreach ($result as $table) {
00197                 $tables[] = $table[0]['TABLE_NAME'];
00198             }
00199 
00200             parent::listSources($tables);
00201             return $tables;
00202         }
00203     }
00204 /**
00205  * Returns an array of the fields in given table name.
00206  *
00207  * @param Model $model Model object to describe
00208  * @return array Fields in table. Keys are name and type
00209  */
00210     function describe(&$model) {
00211         $cache = parent::describe($model);
00212 
00213         if ($cache != null) {
00214             return $cache;
00215         }
00216 
00217         $fields = false;
00218         $cols = $this->fetchAll("SELECT COLUMN_NAME as Field, DATA_TYPE as Type, COL_LENGTH('" . $this->fullTableName($model, false) . "', COLUMN_NAME) as Length, IS_NULLABLE As [Null], COLUMN_DEFAULT as [Default], COLUMNPROPERTY(OBJECT_ID('" . $this->fullTableName($model, false) . "'), COLUMN_NAME, 'IsIdentity') as [Key], NUMERIC_SCALE as Size FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '" . $this->fullTableName($model, false) . "'", false);
00219 
00220         foreach ($cols as $column) {
00221             $field = $column[0]['Field'];
00222             $fields[$field] = array(
00223                 'type' => $this->column($column[0]['Type']),
00224                 'null' => (strtoupper($column[0]['Null']) == 'YES'),
00225                 'default' => preg_replace("/^\('?([^']*)?'?\)$/", "$1", $column[0]['Default']),
00226                 'length' => intval($column[0]['Length']),
00227                 'key'   => ($column[0]['Key'] == '1')
00228             );
00229             if ($fields[$field]['default'] === 'null') {
00230                 $fields[$field]['default'] = null;
00231             } else {
00232                 $this->value($fields[$field]['default'], $fields[$field]['type']);
00233             }
00234 
00235             if ($fields[$field]['key'] && $fields[$field]['type'] == 'integer') {
00236                 $fields[$field]['length'] = 11;
00237             } elseif (!$fields[$field]['key']) {
00238                 unset($fields[$field]['key']);
00239             }
00240             if (in_array($fields[$field]['type'], array('date', 'time', 'datetime', 'timestamp'))) {
00241                 $fields[$field]['length'] = null;
00242             }
00243         }
00244         $this->__cacheDescription($this->fullTableName($model, false), $fields);
00245         return $fields;
00246     }
00247 /**
00248  * Returns a quoted and escaped string of $data for use in an SQL statement.
00249  *
00250  * @param string $data String to be prepared for use in an SQL statement
00251  * @param string $column The column into which this data will be inserted
00252  * @param boolean $safe Whether or not numeric data should be handled automagically if no column data is provided
00253  * @return string Quoted and escaped data
00254  */
00255     function value($data, $column = null, $safe = false) {
00256         $parent = parent::value($data, $column, $safe);
00257 
00258         if ($parent != null) {
00259             return $parent;
00260         }
00261         if ($data === null) {
00262             return 'NULL';
00263         }
00264         if ($data === '') {
00265             return "''";
00266         }
00267 
00268         switch($column) {
00269             case 'boolean':
00270                 $data = $this->boolean((bool)$data);
00271             break;
00272             default:
00273                 if (get_magic_quotes_gpc()) {
00274                     $data = stripslashes(str_replace("'", "''", $data));
00275                 } else {
00276                     $data = str_replace("'", "''", $data);
00277                 }
00278             break;
00279         }
00280 
00281         if (in_array($column, array('integer', 'float')) && is_numeric($data)) {
00282             return $data;
00283         }
00284         return "'" . $data . "'";
00285     }
00286 /**
00287  * Generates the fields list of an SQL query.
00288  *
00289  * @param Model $model
00290  * @param string $alias Alias tablename
00291  * @param mixed $fields
00292  * @return array
00293  */
00294     function fields(&$model, $alias = null, $fields = array(), $quote = true) {
00295         if (empty($alias)) {
00296             $alias = $model->alias;
00297         }
00298         $fields = parent::fields($model, $alias, $fields, false);
00299         $count = count($fields);
00300 
00301         if ($count >= 1 && $fields[0] != '*' && strpos($fields[0], 'COUNT(*)') === false) {
00302             for ($i = 0; $i < $count; $i++) {
00303                 $prepend = '';
00304 
00305                 if (strpos($fields[$i], 'DISTINCT') !== false) {
00306                     $prepend = 'DISTINCT ';
00307                     $fields[$i] = trim(str_replace('DISTINCT', '', $fields[$i]));
00308                 }
00309                 $fieldAlias = count($this->__fieldMappings);
00310 
00311                 if (!preg_match('/\s+AS\s+/i', $fields[$i])) {
00312                     if (strpos($fields[$i], '.') === false) {
00313                         $this->__fieldMappings[$alias . '__' . $fieldAlias] = $alias . '.' . $fields[$i];
00314                         $fieldName  = $this->name($alias . '.' . $fields[$i]);
00315                         $fieldAlias = $this->name($alias . '__' . $fieldAlias);
00316                     } else {
00317                         $build = explode('.', $fields[$i]);
00318                         $this->__fieldMappings[$build[0] . '__' . $fieldAlias] = $fields[$i];
00319                         $fieldName  = $this->name($build[0] . '.' . $build[1]);
00320                         $fieldAlias = $this->name(preg_replace("/^\[(.+)\]$/", "$1", $build[0]) . '__' . $fieldAlias);
00321                     }
00322                     if ($model->getColumnType($fields[$i]) == 'datetime') {
00323                         $fieldName = "CONVERT(VARCHAR(20), {$fieldName}, 20)";
00324                     }
00325                     $fields[$i] =  "{$fieldName} AS {$fieldAlias}";
00326                 }
00327                 $fields[$i] = $prepend . $fields[$i];
00328             }
00329         }
00330         return $fields;
00331     }
00332 /**
00333  * Generates and executes an SQL INSERT statement for given model, fields, and values.
00334  * Removes Identity (primary key) column from update data before returning to parent, if
00335  * value is empty.
00336  *
00337  * @param Model $model
00338  * @param array $fields
00339  * @param array $values
00340  * @param mixed $conditions
00341  * @return array
00342  */
00343     function create(&$model, $fields = null, $values = null) {
00344         if (!empty($values)) {
00345             $fields = array_combine($fields, $values);
00346         }
00347 
00348         if (array_key_exists($model->primaryKey, $fields)) {
00349             if (empty($fields[$model->primaryKey])) {
00350                 unset($fields[$model->primaryKey]);
00351             } else {
00352                 $this->_execute("SET IDENTITY_INSERT " . $this->fullTableName($model) . " ON");
00353             }
00354         }
00355         $result = parent::create($model, array_keys($fields), array_values($fields));
00356         if (array_key_exists($model->primaryKey, $fields) && !empty($fields[$model->primaryKey])) {
00357             $this->_execute("SET IDENTITY_INSERT " . $this->fullTableName($model) . " OFF");
00358         }
00359         return $result;
00360     }
00361 
00362 /**
00363  * Generates and executes an SQL UPDATE statement for given model, fields, and values.
00364  * Removes Identity (primary key) column from update data before returning to parent.
00365  *
00366  * @param Model $model
00367  * @param array $fields
00368  * @param array $values
00369  * @param mixed $conditions
00370  * @return array
00371  */
00372     function update(&$model, $fields = array(), $values = null, $conditions = null) {
00373         if (!empty($values)) {
00374             $fields = array_combine($fields, $values);
00375         }
00376         if (isset($fields[$model->primaryKey])) {
00377             unset($fields[$model->primaryKey]);
00378         }
00379         return parent::update($model, array_keys($fields), array_values($fields), $conditions);
00380     }
00381 /**
00382  * Returns a formatted error message from previous database operation.
00383  *
00384  * @return string Error message with error number
00385  */
00386     function lastError() {
00387         $error = mssql_get_last_message($this->connection);
00388 
00389         if ($error) {
00390             if (!preg_match('/contesto di database|changed database/i', $error)) {
00391                 return $error;
00392             }
00393         }
00394         return null;
00395     }
00396 /**
00397  * Returns number of affected rows in previous database operation. If no previous operation exists,
00398  * this returns false.
00399  *
00400  * @return integer Number of affected rows
00401  */
00402     function lastAffected() {
00403         if ($this->_result) {
00404             return mssql_rows_affected($this->connection);
00405         }
00406         return null;
00407     }
00408 /**
00409  * Returns number of rows in previous resultset. If no previous resultset exists,
00410  * this returns false.
00411  *
00412  * @return integer Number of rows in resultset
00413  */
00414     function lastNumRows() {
00415         if ($this->_result) {
00416             return @mssql_num_rows($this->_result);
00417         }
00418         return null;
00419     }
00420 /**
00421  * Returns the ID generated from the previous INSERT operation.
00422  *
00423  * @param unknown_type $source
00424  * @return in
00425  */
00426     function lastInsertId($source = null) {
00427         $id = $this->fetchRow('SELECT SCOPE_IDENTITY() AS insertID', false);
00428         return $id[0]['insertID'];
00429     }
00430 /**
00431  * Returns a limit statement in the correct format for the particular database.
00432  *
00433  * @param integer $limit Limit of results returned
00434  * @param integer $offset Offset from which to start results
00435  * @return string SQL limit/offset statement
00436  */
00437     function limit($limit, $offset = null) {
00438         if ($limit) {
00439             $rt = '';
00440             if (!strpos(strtolower($limit), 'top') || strpos(strtolower($limit), 'top') === 0) {
00441                 $rt = ' TOP';
00442             }
00443             $rt .= ' ' . $limit;
00444             if (is_int($offset) && $offset > 0) {
00445                 $rt .= ' OFFSET ' . $offset;
00446             }
00447             return $rt;
00448         }
00449         return null;
00450     }
00451 /**
00452  * Converts database-layer column types to basic types
00453  *
00454  * @param string $real Real database-layer column type (i.e. "varchar(255)")
00455  * @return string Abstract column type (i.e. "string")
00456  */
00457     function column($real) {
00458         if (is_array($real)) {
00459             $col = $real['name'];
00460 
00461             if (isset($real['limit'])) {
00462                 $col .= '(' . $real['limit'] . ')';
00463             }
00464             return $col;
00465         }
00466         $col                = str_replace(')', '', $real);
00467         $limit              = null;
00468         if (strpos($col, '(') !== false) {
00469             list($col, $limit) = explode('(', $col);
00470         }
00471 
00472         if (in_array($col, array('date', 'time', 'datetime', 'timestamp'))) {
00473             return $col;
00474         }
00475         if ($col == 'bit') {
00476             return 'boolean';
00477         }
00478         if (strpos($col, 'int') !== false) {
00479             return 'integer';
00480         }
00481         if (strpos($col, 'char') !== false) {
00482             return 'string';
00483         }
00484         if (strpos($col, 'text') !== false) {
00485             return 'text';
00486         }
00487         if (strpos($col, 'binary') !== false || $col == 'image') {
00488             return 'binary';
00489         }
00490         if (in_array($col, array('float', 'real', 'decimal', 'numeric'))) {
00491             return 'float';
00492         }
00493         return 'text';
00494     }
00495 /**
00496  * Enter description here...
00497  *
00498  * @param unknown_type $results
00499  */
00500     function resultSet(&$results) {
00501         $this->results =& $results;
00502         $this->map = array();
00503         $num_fields = mssql_num_fields($results);
00504         $index = 0;
00505         $j = 0;
00506 
00507         while ($j < $num_fields) {
00508             $column = mssql_field_name($results, $j);
00509 
00510             if (strpos($column, '__')) {
00511                 if (isset($this->__fieldMappings[$column]) && strpos($this->__fieldMappings[$column], '.')) {
00512                     $map = explode('.', $this->__fieldMappings[$column]);
00513                 } elseif (isset($this->__fieldMappings[$column])) {
00514                     $map = array(0, $this->__fieldMappings[$column]);
00515                 } else {
00516                     $map = array(0, $column);
00517                 }
00518                 $this->map[$index++] = $map;
00519             } else {
00520                 $this->map[$index++] = array(0, $column);
00521             }
00522             $j++;
00523         }
00524     }
00525 /**
00526  * Builds final SQL statement
00527  *
00528  * @param string $type Query type
00529  * @param array $data Query data
00530  * @return string
00531  */
00532     function renderStatement($type, $data) {
00533         switch (strtolower($type)) {
00534             case 'select':
00535                 extract($data);
00536                 $fields = trim($fields);
00537 
00538                 if (strpos($limit, 'TOP') !== false && strpos($fields, 'DISTINCT ') === 0) {
00539                     $limit = 'DISTINCT ' . trim($limit);
00540                     $fields = substr($fields, 9);
00541                 }
00542 
00543                 if (preg_match('/offset\s+([0-9]+)/i', $limit, $offset)) {
00544                     $limit = preg_replace('/\s*offset.*$/i', '', $limit);
00545                     preg_match('/top\s+([0-9]+)/i', $limit, $limitVal);
00546                     $offset = intval($offset[1]) + intval($limitVal[1]);
00547                     $rOrder = $this->__switchSort($order);
00548                     list($order2, $rOrder) = array($this->__mapFields($order), $this->__mapFields($rOrder));
00549                     return "SELECT * FROM (SELECT {$limit} * FROM (SELECT TOP {$offset} {$fields} FROM {$table} {$alias} {$joins} {$conditions} {$order}) AS Set1 {$rOrder}) AS Set2 {$order2}";
00550                 } else {
00551                     return "SELECT {$limit} {$fields} FROM {$table} {$alias} {$joins} {$conditions} {$order}";
00552                 }
00553             break;
00554             case "schema":
00555                 extract($data);
00556 
00557                 foreach ($indexes as $i => $index) {
00558                     if (preg_match('/PRIMARY KEY/', $index)) {
00559                         unset($indexes[$i]);
00560                         break;
00561                     }
00562                 }
00563 
00564                 foreach (array('columns', 'indexes') as $var) {
00565                     if (is_array(${$var})) {
00566                         ${$var} = "\t" . join(",\n\t", array_filter(${$var}));
00567                     }
00568                 }
00569                 return "CREATE TABLE {$table} (\n{$columns});\n{$indexes}";
00570             break;
00571             default:
00572                 return parent::renderStatement($type, $data);
00573             break;
00574         }
00575     }
00576 /**
00577  * Reverses the sort direction of ORDER statements to get paging offsets to work correctly
00578  *
00579  * @param string $order
00580  * @return string
00581  * @access private
00582  */
00583     function __switchSort($order) {
00584         $order = preg_replace('/\s+ASC/i', '__tmp_asc__', $order);
00585         $order = preg_replace('/\s+DESC/i', ' ASC', $order);
00586         return preg_replace('/__tmp_asc__/', ' DESC', $order);
00587     }
00588 /**
00589  * Translates field names used for filtering and sorting to shortened names using the field map
00590  *
00591  * @param string $sql A snippet of SQL representing an ORDER or WHERE statement
00592  * @return string The value of $sql with field names replaced
00593  * @access private
00594  */
00595     function __mapFields($sql) {
00596         if (empty($sql) || empty($this->__fieldMappings)) {
00597             return $sql;
00598         }
00599         foreach ($this->__fieldMappings as $key => $val) {
00600             $sql = preg_replace('/' . preg_quote($val) . '/', $this->name($key), $sql);
00601             $sql = preg_replace('/' . preg_quote($this->name($val)) . '/', $this->name($key), $sql);
00602         }
00603         return $sql;
00604     }
00605 /**
00606  * Returns an array of all result rows for a given SQL query.
00607  * Returns false if no rows matched.
00608  *
00609  * @param string $sql SQL statement
00610  * @param boolean $cache Enables returning/storing cached query results
00611  * @return array Array of resultset rows, or false if no rows matched
00612  */
00613     function read(&$model, $queryData = array(), $recursive = null) {
00614         $results = parent::read($model, $queryData, $recursive);
00615         $this->__fieldMappings = array();
00616         $this->__fieldMapBase = null;
00617         return $results;
00618     }
00619 /**
00620  * Fetches the next row from the current result set
00621  *
00622  * @return unknown
00623  */
00624     function fetchResult() {
00625         if ($row = mssql_fetch_row($this->results)) {
00626             $resultRow = array();
00627             $i = 0;
00628 
00629             foreach ($row as $index => $field) {
00630                 list($table, $column) = $this->map[$index];
00631                 $resultRow[$table][$column] = $row[$index];
00632                 $i++;
00633             }
00634             return $resultRow;
00635         } else {
00636             return false;
00637         }
00638     }
00639 /**
00640  * Generate a database-native column schema string
00641  *
00642  * @param array $column An array structured like the following: array('name'=>'value', 'type'=>'value'[, options]),
00643  *                      where options can be 'default', 'length', or 'key'.
00644  * @return string
00645  */
00646     function buildColumn($column) {
00647         $result = preg_replace('/(int|integer)\([0-9]+\)/i', '$1', parent::buildColumn($column));
00648         $null = (
00649             (isset($column['null']) && $column['null'] == true) ||
00650             (array_key_exists('default', $column) && $column['default'] === null) ||
00651             (array_keys($column) == array('type', 'name'))
00652         );
00653         $stringKey = (isset($column['key']) && $column['key'] == 'primary' && $column['type'] != 'integer');
00654         if ($null) {
00655             $result .= " NULL";
00656         }
00657         return $result;
00658     }
00659 /**
00660  * Format indexes for create table
00661  *
00662  * @param array $indexes
00663  * @param string $table
00664  * @return string
00665  */
00666     function buildIndex($indexes, $table = null) {
00667         $join = array();
00668 
00669         foreach ($indexes as $name => $value) {
00670             if ($name == 'PRIMARY') {
00671                 $out = 'PRIMARY KEY  (' . $this->name($value['column']) . ')';
00672             } else {
00673                 $out = "ALTER TABLE {$table} ADD CONSTRAINT {$name} UNIQUE";
00674 
00675                 if (is_array($value['column'])) {
00676                     $value['column'] = join(', ', array_map(array(&$this, 'name'), $value['column']));
00677                 } else {
00678                     $value['column'] = $this->name($value['column']);
00679                 }
00680                 $out .= "({$value['column']});";
00681             }
00682             $join[] = $out;
00683         }
00684         return $join;
00685     }
00686 }
00687 
00688 ?>