]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - include/database/SqlsrvHelper.php
Release 6.3.1
[Github/sugarcrm.git] / include / database / SqlsrvHelper.php
1 <?php
2 if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
3 /*********************************************************************************
4  * SugarCRM Community Edition is a customer relationship management program developed by
5  * SugarCRM, Inc. Copyright (C) 2004-2011 SugarCRM Inc.
6  * 
7  * This program is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU Affero General Public License version 3 as published by the
9  * Free Software Foundation with the addition of the following permission added
10  * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
11  * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
12  * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
13  * 
14  * This program is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16  * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
17  * details.
18  * 
19  * You should have received a copy of the GNU Affero General Public License along with
20  * this program; if not, see http://www.gnu.org/licenses or write to the Free
21  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22  * 02110-1301 USA.
23  * 
24  * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
25  * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
26  * 
27  * The interactive user interfaces in modified source and object code versions
28  * of this program must display Appropriate Legal Notices, as required under
29  * Section 5 of the GNU Affero General Public License version 3.
30  * 
31  * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
32  * these Appropriate Legal Notices must retain the display of the "Powered by
33  * SugarCRM" logo. If the display of the logo is not reasonably feasible for
34  * technical reasons, the Appropriate Legal Notices must display the words
35  * "Powered by SugarCRM".
36  ********************************************************************************/
37
38 /*********************************************************************************
39
40 * Description: This file handles the Data base functionality for the application specific
41 * to SQL Server database using the php_sqlsrv extension. It is called by the DBManager class to generate various sql statements.
42 *
43 * All the functions in this class will work with any bean which implements the meta interface.
44 * Please refer the DBManager documentation for the details.
45 *
46 * Portions created by SugarCRM are Copyright (C) SugarCRM, Inc.
47 * All Rights Reserved.
48 * Contributor(s): ______________________________________..
49 ********************************************************************************/
50 require_once('include/database/MssqlHelper.php');
51
52 class SqlsrvHelper extends MssqlHelper
53 {
54         /**
55      * @see DBHelper::getColumnType()
56      */
57     public function getColumnType(
58         $type,
59         $name = '',
60         $table = ''
61         )
62     {
63                 $columnType = parent::getColumnType($type,$name,$table);
64
65                 if ( in_array($columnType,array('char','varchar')) && !preg_match('/(_id$|^id$)/', $name))
66                         $columnType = 'n'.$columnType;
67
68                 if ( in_array($columnType,array('text','ntext','image')) ) {
69                     $columnType = 'nvarchar(max)';
70         }
71
72         return $columnType;
73     }
74
75         /**
76          * @see DBHelper::massageValue()
77          */
78         public function massageValue(
79         $val,
80         $fieldDef
81         )
82     {
83         $type = $this->getFieldType($fieldDef);
84
85                 switch ($type) {
86                 case 'int':
87                 case 'double':
88                 case 'float':
89                 case 'uint':
90                 case 'ulong':
91                 case 'long':
92                 case 'short':
93                 case 'tinyint':
94             return $val;
95             break;
96         }
97
98         $qval = $this->quote($val);
99
100         switch ($type) {
101         case 'varchar':
102         case 'nvarchar':
103         case 'char':
104         case 'nchar':
105         case 'enum':
106         case 'multienum':
107         case 'id':
108             return $qval;
109             break;
110         case 'date':
111             return "$qval";
112             break;
113         case 'datetime':
114             return $qval;
115             break;
116         case 'time':
117             return "$qval";
118             break;
119         case 'text':
120         case 'ntext':
121         case 'blob':
122         case 'longblob':
123         case 'clob':
124         case 'longtext':
125         case 'image':
126             return $qval;
127             break;
128                 }
129
130         return $val;
131         }
132
133         /**
134          * Detect if no clustered index has been created for a table; if none created then just pick the first index and make it that
135          *
136          * @see MssqlHelper::indexSQL()
137      */
138     public function indexSQL(
139         $tableName,
140         $fieldDefs,
141         $indices
142         )
143     {
144         if ( $this->doesTableHaveAClusteredIndexDefined($tableName) ) {
145             return parent::indexSQL($tableName, $fieldDefs, $indices);
146         }
147
148         // check to see if one of the passed in indices is a primary one; if so we can bail as well
149         foreach ( $indices as $index ) {
150             if ( $index['type'] == 'primary' ) {
151                 return parent::indexSQL($tableName, $fieldDefs, $indices);
152             }
153         }
154
155         // Change the first index listed to be a clustered one instead ( so we have at least one for the table )
156         if ( isset($indices[0]) ) {
157             $indices[0]['type'] = 'clustered';
158         }
159
160         return parent::indexSQL($tableName, $fieldDefs, $indices);
161     }
162
163     /**
164      * @see DBHelper::get_columns()
165      */
166     public function get_columns(
167         $tablename
168         )
169     {
170         //find all unique indexes and primary keys.
171         $result = $this->db->query("sp_columns_90 $tablename");
172
173         $columns = array();
174         while (($row=$this->db->fetchByAssoc($result)) !=null) {
175             $column_name = strtolower($row['COLUMN_NAME']);
176             $columns[$column_name]['name']=$column_name;
177             $columns[$column_name]['type']=strtolower($row['TYPE_NAME']);
178             if ( $row['TYPE_NAME'] == 'decimal' ) {
179                 $columns[$column_name]['len']=strtolower($row['PRECISION']);
180                 $columns[$column_name]['len'].=','.strtolower($row['SCALE']);
181             }
182                         elseif ( in_array($row['TYPE_NAME'],array('nchar','nvarchar')) ) {
183                                 $columns[$column_name]['len']=strtolower($row['PRECISION']);
184                                 if ( $row['TYPE_NAME'] == 'nvarchar' && $row['PRECISION'] == '0' ) {
185                                     $columns[$column_name]['len']='255';
186                                 }
187                         }
188             elseif ( !in_array($row['TYPE_NAME'],array('datetime','text')) ) {
189                 $columns[$column_name]['len']=strtolower($row['LENGTH']);
190             }
191             if ( stristr($row['TYPE_NAME'],'identity') ) {
192                 $columns[$column_name]['auto_increment'] = '1';
193                 $columns[$column_name]['type']=str_replace(' identity','',strtolower($row['TYPE_NAME']));
194             }
195
196             if (!empty($row['IS_NULLABLE']) && $row['IS_NULLABLE'] == 'NO' && (empty($row['KEY']) || !stristr($row['KEY'],'PRI')))
197                 $columns[strtolower($row['COLUMN_NAME'])]['required'] = 'true';
198
199             $column_def = 0;
200             if ( strtolower($tablename) == 'relationships' ) {
201                 $column_def = $this->db->getOne("select cdefault from syscolumns where id = object_id('relationships') and name = '$column_name'");
202             }
203             if ( $column_def != 0 ) {
204                 $matches = array();
205                 $row['COLUMN_DEF'] = html_entity_decode($row['COLUMN_DEF'],ENT_QUOTES);
206                 if ( preg_match("/\([\(|'](.*)[\)|']\)/i",$row['COLUMN_DEF'],$matches) )
207                     $columns[$column_name]['default'] = $matches[1];
208                 elseif ( preg_match("/\(N'(.*)'\)/i",$row['COLUMN_DEF'],$matches) )
209                     $columns[$column_name]['default'] = $matches[1];
210                 else
211                     $columns[$column_name]['default'] = $row['COLUMN_DEF'];
212             }
213         }
214         return $columns;
215     }
216
217     /**
218      * @see DBHelper::get_indices()
219      */
220     public function get_indices(
221         $tableName
222         ) 
223     {
224         //find all unique indexes and primary keys.
225         $query = <<<EOSQL
226 SELECT sys.tables.object_id, sys.tables.name as table_name, sys.columns.name as column_name, 
227         sys.indexes.name as index_name, sys.indexes.is_unique, sys.indexes.is_primary_key
228     FROM sys.tables, sys.indexes, sys.index_columns, sys.columns 
229     WHERE (sys.tables.object_id = sys.indexes.object_id 
230             AND sys.tables.object_id = sys.index_columns.object_id 
231             AND sys.tables.object_id = sys.columns.object_id
232             AND sys.indexes.index_id = sys.index_columns.index_id 
233             AND sys.index_columns.column_id = sys.columns.column_id) 
234         AND sys.tables.name = '$tableName'
235 EOSQL;
236         $result = $this->db->query($query);
237
238         $indices = array();
239         while (($row=$this->db->fetchByAssoc($result)) != null) {
240             $index_type = 'index';
241             if ($row['is_primary_key'] == '1')
242                 $index_type = 'primary';
243             elseif ($row['is_unique'] == 1 )
244                 $index_type = 'unique';
245             $name = strtolower($row['index_name']);
246             $indices[$name]['name']     = $name;
247             $indices[$name]['type']     = $index_type;
248             $indices[$name]['fields'][] = strtolower($row['column_name']);
249         }
250         return $indices;
251     }
252
253     /**
254      * protected function to return true if the given tablename has any clustered indexes defined.
255      *
256      * @param  string $tableName
257      * @return bool
258      */
259     protected function doesTableHaveAClusteredIndexDefined($tableName)
260     {
261         $query = <<<EOSQL
262 SELECT IST.TABLE_NAME
263     FROM INFORMATION_SCHEMA.TABLES IST
264     WHERE objectProperty(object_id(IST.TABLE_NAME), 'IsUserTable') = 1
265         AND objectProperty(object_id(IST.TABLE_NAME), 'TableHasClustIndex') = 1
266         AND IST.TABLE_NAME = '{$tableName}'
267 EOSQL;
268
269         $result = $this->db->getOne($query);
270         if ( !$result ) {
271             return false;
272         }
273
274         return true;
275     }
276
277     /**
278      * protected function to return true if the given tablename has any fulltext indexes defined.
279      *
280      * @param  string $tableName
281      * @return bool
282      */
283     protected function doesTableHaveAFulltextIndexDefined($tableName)
284     {
285         $query = <<<EOSQL
286 SELECT 1
287     FROM sys.fulltext_indexes i
288         JOIN sys.objects o ON i.object_id = o.object_id
289     WHERE o.name = '{$tableName}'
290 EOSQL;
291
292         $result = $this->db->getOne($query);
293         if ( !$result ) {
294             return false;
295         }
296
297         return true;
298     }
299
300     /**
301      * Override method to add support for detecting and dropping fulltext indices.
302      *
303      * @see DBHelper::changeColumnSQL()
304      * @see MssqlHelper::changeColumnSQL()
305      */
306     protected function changeColumnSQL(
307         $tablename,
308         $fieldDefs,
309         $action,
310         $ignoreRequired = false
311         )
312     {
313         $sql = '';
314         if ( $this->doesTableHaveAFulltextIndexDefined($tablename) ) {
315             $sql .= "DROP FULLTEXT INDEX ON {$tablename}";
316         }
317
318         $sql .= parent::changeColumnSQL($tablename, $fieldDefs, $action, $ignoreRequired);
319
320         return $sql;
321     }
322     
323     /**
324      * @see DBHelper::massageFieldDef()
325      */
326     public function massageFieldDef(
327         &$fieldDef,
328         $tablename
329         )
330     {
331         parent::massageFieldDef($fieldDef,$tablename);
332         
333         if ($fieldDef['type'] == 'bit')
334             $fieldDef['len'] = '1';
335     }
336
337     protected function getDefault($fieldDef, $type) {
338         if (isset($fieldDef['default']) && strlen($fieldDef['default']) > 0) {
339             $default = " DEFAULT '".$fieldDef['default']."'";
340         }
341         else {
342             $default = '';
343         }
344
345         return $default;
346     }
347 }
348 ?>