]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - include/database/MssqlHelper.php
Release 6.2.0
[Github/sugarcrm.git] / include / database / MssqlHelper.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 Mssql database. 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): ___RPS___________________________________..
49 ********************************************************************************/
50
51 include_once('include/database/DBHelper.php');
52
53 class MssqlHelper extends DBHelper
54 {
55     /**
56      * @see DBHelper::getColumnType()
57      */
58     public function getColumnType(
59         $type, 
60         $name = '', 
61         $table = ''
62         )
63     {
64         $map = array( 
65             'int'      => 'int',
66             'double'   => 'float',
67             'float'    => 'float',
68             'uint'     => 'int',
69             'ulong'    => 'int',
70             'long'     => 'bigint',
71             'short'    => 'smallint',
72             'varchar'  => 'varchar',
73             'text'     => 'text',
74             'longtext' => 'text',
75             'date'     => 'datetime',
76             'enum'     => 'varchar',
77             'relate'   => 'varchar',
78             'multienum'=> 'text',
79             'html'     => 'text',
80             'datetime' => 'datetime',
81             'datetimecombo' => 'datetime',
82             'time'     => 'datetime',
83             'bool'     => 'bit',
84             'tinyint'  => 'tinyint',
85             'char'     => 'char',
86             'blob'     => 'image',
87             'longblob' => 'image',
88             'currency' => 'decimal(26,6)',
89             'decimal'  => 'decimal',
90             'decimal2' => 'decimal',
91             'id'       => 'varchar(36)',
92             'url'=>'varchar',
93             'encrypt'=>'varchar',
94             'file'     => 'varchar',
95             );
96         
97         return $map[$type];
98     }
99     
100     /**
101      * @see DBHelper::dropTableNameSQL()
102      */
103     public function dropTableNameSQL(
104         $name
105         )
106     {
107                 return "DROP TABLE ".$name;
108     }
109
110     /**
111      * Returns the SQL Alter table statment
112      *
113      * MSSQL has a quirky T-SQL alter table syntax. Pay special attention to the
114      * modify operation
115      * @param string $action
116      * @param array  $def
117      * @param bool   $ignorRequired
118      * @param string $tablename
119      */
120     private function alterSQLRep(
121         $action, 
122         array $def, 
123         $ignoreRequired, 
124         $tablename = ''
125         ) 
126     {
127         switch($action){
128         case 'add':
129              $f_def=$this->oneColumnSQLRep($def, $ignoreRequired,$tablename,false);
130             return "ADD " . $f_def;
131             break;
132         case 'drop':
133             return "DROP COLUMN " . $def['name'];
134             break;
135         case 'modify':
136             //You cannot specify a default value for a column for MSSQL
137             $f_def  = $this->oneColumnSQLRep($def, $ignoreRequired,$tablename, true);
138             $f_stmt = "ALTER COLUMN ".$f_def['name'].' '.$f_def['colType'].' '.
139                         $f_def['required'].' '.$f_def['auto_increment']."\n";
140             if (!empty( $f_def['default']))
141                 $f_stmt .= " ALTER TABLE " . $tablename .  " ADD  ". $f_def['default'] . " FOR " . $def['name'];
142             return $f_stmt;
143             break;
144         default:
145             return '';
146         }
147     }
148
149     /**
150      * @see DBHelper::changeColumnSQL()
151      *
152      * MSSQL uses a different syntax than MySQL for table altering that is
153      * not quite as simplistic to implement...
154      */
155     protected function changeColumnSQL(
156         $tablename, 
157         $fieldDefs, 
158         $action, 
159         $ignoreRequired = false
160         )
161     {
162         $sql=$sql2='';
163         $constraints = $this->get_field_default_constraint_name($tablename);
164         if ($this->isFieldArray($fieldDefs)) {
165             foreach ($fieldDefs as $def)
166                 {
167                         //if the column is being modified drop the default value
168                         //constraint if it exists. alterSQLRep will add the constraint back
169                         if (!empty($constraints[$def['name']])) {
170                                 $sql.=" ALTER TABLE " . $tablename . " DROP CONSTRAINT " . $constraints[$def['name']];
171                         }
172                         //check to see if we need to drop related indexes before the alter
173                         $indices = $this->get_indices($tablename);
174                 foreach ( $indices as $index ) {
175                     if ( in_array($def['name'],$index['fields']) ) {
176                         $sql  .= ' ' . $this->add_drop_constraint($tablename,$index,true).' ';
177                         $sql2 .= ' ' . $this->add_drop_constraint($tablename,$index,false).' ';
178                     }
179                 }
180             
181                         $columns[] = $this->alterSQLRep($action, $def, $ignoreRequired,$tablename);
182                 }
183         }
184         else {
185             //if the column is being modified drop the default value
186                 //constraint if it exists. alterSQLRep will add the constraint back
187                 if (!empty($constraints[$fieldDefs['name']])) {
188                         $sql.=" ALTER TABLE " . $tablename . " DROP CONSTRAINT " . $constraints[$fieldDefs['name']];
189                 }
190                 //check to see if we need to drop related indexes before the alter
191             $indices = $this->get_indices($tablename);
192             foreach ( $indices as $index ) {
193                 if ( in_array($fieldDefs['name'],$index['fields']) ) {
194                     $sql  .= ' ' . $this->add_drop_constraint($tablename,$index,true).' ';
195                     $sql2 .= ' ' . $this->add_drop_constraint($tablename,$index,false).' ';
196                 }
197             }
198             
199
200                 $columns[] = $this->alterSQLRep($action, $fieldDefs, $ignoreRequired,$tablename);
201         }
202
203         $columns = implode(", ", $columns);
204         $sql .= " ALTER TABLE $tablename $columns " . $sql2;
205         
206         return $sql;
207     }
208
209     /**
210      * @see DBHelper::deleteColumnSQL()
211      */
212     public function deleteColumnSQL(
213         SugarBean $bean, 
214         $fieldDefs
215         )
216     {
217         if ($this->isFieldArray($fieldDefs)) 
218             foreach ($fieldDefs as $fieldDef) 
219                 $columns[] = $fieldDef['name'];
220         else 
221             $columns[] = $fieldDefs['name'];
222         
223         return "ALTER TABLE ".$bean->getTableName()." DROP COLUMN ".implode(", DROP COLUMN ", $columns);
224     }
225     
226     /**
227      * returns an alter table statement to build the list of indices
228      *
229      * @param  string $tableName
230      * @param  array  $fieldDefs
231      * @param  array  $indices
232      * @return string SQL statement
233      */
234     public function indexSQL( 
235         $tableName, 
236         $fieldDefs, 
237         $indices
238         ) 
239     {
240        // check if the passed value is an array of fields.
241        // if not, convert it into an array
242        if (!$this->isFieldArray($indices)) 
243            $indices[] = $indices;
244
245        $columns = array();
246        foreach ($indices as $index) {
247            if(!empty($index['db']) && $index['db'] != 'mssql')
248                continue;
249            if (isset($index['source']) && $index['source'] != 'db')
250                continue;
251
252            $type = $index['type'];
253            $name = $index['name'];
254
255            if (is_array($index['fields']))
256                $fields = implode(", ", $index['fields']);
257            else
258                $fields = $index['fields'];
259
260            switch ($type) {
261            case 'primary':
262                // SQL server requires primary key constraints to be created with
263                // key word "PRIMARY KEY".  Cannot default to index as synonym
264                $columns[] = "ALTER TABLE $tableName ADD CONSTRAINT pk_$tableName PRIMARY KEY ($fields)";
265                break;
266            case 'unique':
267                $columns[] = "ALTER TABLE $tableName ADD CONSTRAINT " . $index['name'] . " UNIQUE ($fields)";
268                break;
269            case 'clustered':
270                $columns[] = "CREATE CLUSTERED INDEX $name ON $tableName ( $fields )";
271                break;
272            case 'index':
273            case 'alternate_key':
274            case 'foreign':
275                $columns[] = "CREATE INDEX $name ON $tableName ( $fields )";
276                break;
277            case 'fulltext':
278                if ($this->full_text_indexing_enabled()) {
279                    $catalog_name="sugar_fts_catalog";
280                    if ( isset($index['catalog_name']) 
281                             && $index['catalog_name'] != 'default')
282                                 $catalog_name = $index['catalog_name'];
283
284                         $language = "Language 1033";
285                         if (isset($index['language']) && !empty($index['language']))
286                                 $language = "Language " . $index['language'];
287                         
288                         $key_index = $index['key_index'];;
289
290                         $change_tracking = "auto";
291                         if (isset($index['change_tracking']) 
292                             && !empty($index['change_tracking']))
293                                 $change_tracking = $index['change_tracking'];
294                         
295                         $columns[] = " CREATE FULLTEXT INDEX ON $tableName($fields $language) KEY INDEX $key_index ON $catalog_name WITH CHANGE_TRACKING $change_tracking" ;
296                }
297                break;
298            }
299        }
300
301        $columns = implode(" ", $columns);
302        
303        return $columns;
304     }
305
306         protected function setAutoIncrement(
307         $table, 
308         $field_name
309         )
310     {
311                 return "identity(1,1)";
312         }
313
314     /**
315      * @see DBHelper::setAutoIncrementStart()
316      */
317     public function setAutoIncrementStart(
318         $table,
319         $field_name,
320         $start_value
321         )
322     {
323         if($start_value > 1)
324             $start_value -= 1;
325                 $this->db->query("DBCC CHECKIDENT ('$table', RESEED, $start_value)");
326         return true;
327     }
328         
329         /**
330      * @see DBHelper::getAutoIncrement()
331      */
332     public function getAutoIncrement(
333         $table,
334         $field_name
335         )
336     {
337         
338         
339                 $result = $this->db->query("select IDENT_CURRENT('$table') + IDENT_INCR ( '$table' ) as 'Auto_increment'");
340         $row = $this->db->fetchByAssoc($result);
341                 if (!empty($row['Auto_increment']))
342             return $row['Auto_increment'];
343         
344         return "";
345     }
346
347     /**
348      * @see DBHelper::createTableSQLParams()
349      */
350         public function createTableSQLParams(
351         $tablename, 
352         $fieldDefs, 
353         $indices,
354         $engine = null
355         )
356     {
357         if (empty($tablename) || empty($fieldDefs)) 
358             return '';
359
360         $sql ='';
361         $columns = $this->columnSQLRep($fieldDefs, false, $tablename);
362         if (empty($columns))
363             return false;
364         
365         return "CREATE TABLE $tablename ($columns ) " .
366             $this->indexSQL($tablename, $fieldDefs, $indices);
367     }
368
369         /**
370      * @see DBHelper::get_indices()
371      */
372     public function get_indices(
373         $tablename
374         ) 
375     {
376         //find all unique indexes and primary keys.
377         $query = <<<EOSQL
378 SELECT LEFT(so.[name], 30) TableName, 
379         LEFT(si.[name], 50) 'Key_name',
380         LEFT(sik.[keyno], 30) Sequence, 
381         LEFT(sc.[name], 30) Column_name,
382                 isunique = CASE
383             WHEN si.status & 2 = 2 AND so.xtype != 'PK' THEN 1
384             ELSE 0
385         END
386     FROM sysindexes si
387         INNER JOIN sysindexkeys sik 
388             ON (si.[id] = sik.[id] AND si.indid = sik.indid)
389         INNER JOIN sysobjects so 
390             ON si.[id] = so.[id]
391         INNER JOIN syscolumns sc 
392             ON (so.[id] = sc.[id] AND sik.colid = sc.colid)
393         INNER JOIN sysfilegroups sfg 
394             ON si.groupid = sfg.groupid
395     WHERE so.[name] = '$tablename'
396     ORDER BY Key_name, Sequence, Column_name
397 EOSQL;
398         $result = $this->db->query($query);
399         
400         $indices = array();
401         while (($row=$this->db->fetchByAssoc($result)) != null) {
402             $index_type = 'index';
403             if ($row['Key_name'] == 'PRIMARY')
404                 $index_type = 'primary';
405             elseif ($row['isunique'] == 1 )
406                 $index_type = 'unique';
407             $name = strtolower($row['Key_name']);
408             $indices[$name]['name']     = $name;
409             $indices[$name]['type']     = $index_type;
410             $indices[$name]['fields'][] = strtolower($row['Column_name']);
411         }
412         return $indices;
413     }
414
415     /**
416      * @see DBHelper::get_columns()
417      */
418     public function get_columns(
419         $tablename
420         ) 
421     {
422         //find all unique indexes and primary keys.
423         $result = $this->db->query("sp_columns $tablename");
424         
425         $columns = array();
426         while (($row=$this->db->fetchByAssoc($result)) !=null) {
427             $column_name = strtolower($row['COLUMN_NAME']);
428             $columns[$column_name]['name']=$column_name;
429             $columns[$column_name]['type']=strtolower($row['TYPE_NAME']);
430             if ( $row['TYPE_NAME'] == 'decimal' ) {
431                 $columns[$column_name]['len']=strtolower($row['PRECISION']);
432                 $columns[$column_name]['len'].=','.strtolower($row['SCALE']);
433             }
434                         elseif ( in_array($row['TYPE_NAME'],array('nchar','nvarchar')) )
435                                 $columns[$column_name]['len']=strtolower($row['PRECISION']);
436             elseif ( !in_array($row['TYPE_NAME'],array('datetime','text','bit')) )
437                 $columns[$column_name]['len']=strtolower($row['LENGTH']);
438             if ( stristr($row['TYPE_NAME'],'identity') ) {
439                 $columns[$column_name]['auto_increment'] = '1';
440                 $columns[$column_name]['type']=str_replace(' identity','',strtolower($row['TYPE_NAME']));
441             }
442             
443             if (!empty($row['IS_NULLABLE']) && $row['IS_NULLABLE'] == 'NO' && (empty($row['KEY']) || !stristr($row['KEY'],'PRI')))
444                 $columns[strtolower($row['COLUMN_NAME'])]['required'] = 'true';
445             
446             $column_def = 0;
447             if ( strtolower($tablename) == 'relationships' ) {
448                 $column_def = $this->db->getOne("select cdefault from syscolumns where id = object_id('relationships') and name = '$column_name'");
449             }
450             if ( $column_def != 0 ) {
451                 $matches = array();
452                 $row['COLUMN_DEF'] = html_entity_decode($row['COLUMN_DEF'],ENT_QUOTES);
453                 if ( preg_match("/\([\(|'](.*)[\)|']\)/i",$row['COLUMN_DEF'],$matches) )
454                     $columns[$column_name]['default'] = $matches[1];
455                 elseif ( preg_match("/\(N'(.*)'\)/i",$row['COLUMN_DEF'],$matches) )
456                     $columns[$column_name]['default'] = $matches[1];
457                 else
458                     $columns[$column_name]['default'] = $row['COLUMN_DEF'];
459             }
460         }
461         return $columns;
462     }
463     
464     /**
465      * @see DBHelper::add_drop_constraint()
466      */
467     public function add_drop_constraint(
468         $table,
469         $definition, 
470         $drop = false
471         ) 
472     {
473         $type         = $definition['type'];
474         $fields       = implode(',',$definition['fields']);
475         $name         = $definition['name'];
476         $foreignTable = isset($definition['foreignTable']) ? $definition['foreignTable'] : array();
477         $sql          = '';
478         
479         switch ($type){
480         // generic indices
481         case 'index':
482         case 'alternate_key':
483             if ($drop)
484                 $sql = "DROP INDEX {$name} ON {$table}";
485             else
486                 $sql = "CREATE INDEX {$name} ON {$table} ({$fields})";
487             break;
488         // constraints as indices
489         case 'unique':
490             if ($drop)
491                 $sql = "ALTER TABLE {$table} DROP CONSTRAINT $name";
492             else
493                 $sql = "ALTER TABLE {$table} ADD CONSTRAINT {$name} UNIQUE ({$fields})";
494             break;
495         case 'primary':
496             if ($drop)
497                 $sql = "ALTER TABLE {$table} DROP PRIMARY KEY";
498             else
499                 $sql = "ALTER TABLE {$table} ADD CONSTRAINT {$name} PRIMARY KEY ({$fields})";
500             break;
501         case 'foreign':
502             if ($drop)
503                 $sql = "ALTER TABLE {$table} DROP FOREIGN KEY ({$fields})";
504             else
505                 $sql = "ALTER TABLE {$table} ADD CONSTRAINT {$name}  FOREIGN KEY ({$fields}) REFERENCES {$foreignTable}({$foreignfields})";
506             break;
507         case 'fulltext':
508             if ($this->full_text_indexing_enabled() && $drop)
509                 $sql = "DROP FULLTEXT INDEX ON {$table}";
510             elseif ($this->full_text_indexing_enabled()) {
511                 $catalog_name="sugar_fts_catalog";
512                 if ( isset($index['catalog_name']) && $index['catalog_name'] != 'default')
513                     $catalog_name = $index['catalog_name'];
514
515                 $language = "Language 1033";
516                 if (isset($index['language']) && !empty($index['language']))
517                     $language = "Language " . $index['language'];
518                 
519                 $key_index = $index['key_index'];
520
521                 $change_tracking = "auto";
522                 if (isset($index['change_tracking']) && !empty($index['change_tracking']))
523                     $change_tracking = $index['change_tracking'];
524                 
525                 $columns[] = " CREATE FULLTEXT INDEX ON $table ($fields $language) KEY INDEX $key_index ON $catalog_name WITH CHANGE_TRACKING $change_tracking" ;
526             }
527             break;
528         }
529         return $sql;
530     }
531
532     /**
533      * @see DBHelper::number_of_columns()
534      */
535     public function number_of_columns(
536         $table_name
537         ) 
538     {
539         $def_query = <<<EOSQL
540 SELECT count(*) as cols 
541     FROM sys.columns col join sys.types col_type 
542         on col.user_type_id=col_type.user_type_id 
543     where col.object_id = (
544         select object_id(sys.schemas.name + '.' + sys.tables.name)
545             from sys.tables join sys.schemas 
546                 on sys.schemas.schema_id = sys.tables.schema_id
547             where sys.tables.name='$table_name'
548         )
549 EOSQL;
550         /**
551          * @TODO test the similarities of the above the query against all system tables vs the query below against
552          * the information_schema view in terms of results and efficiency. suspician is provided the two produce
553          * the same information the latter will be slightly faster.
554          * <code>
555          * <?php
556          * $def_query = "SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='$table_name'";
557          * ?>
558          * </code>
559          */
560
561         $result = $this->db->query($def_query);
562         $row    = $this->db->fetchByAssoc($result);
563         if (!empty($row)) {
564             return $row['cols'];
565         }
566         return 0;
567     }
568
569     /**
570      * Returns true if Full Text Search is installed
571      *
572      * @return bool
573      */
574     protected function full_text_indexing_installed() 
575     {
576         $ftsChckRes = $this->db->query(
577             "SELECT FULLTEXTSERVICEPROPERTY('IsFulltextInstalled') as fts");
578         $row = $this->db->fetchByAssoc($ftsChckRes);
579
580         return (isset($row) && isset($row['fts']) && ($row['fts'] == 1 || $row['fts'] == '1'));
581     }
582
583     /**
584      * @see DBHelper::full_text_indexing_enabled()
585      */
586     protected function full_text_indexing_enabled(
587         $dbname = null
588         ) 
589     {
590         // check to see if we already have install setting in session
591         if(!isset($_SESSION['IsFulltextInstalled']))
592             $_SESSION['IsFulltextInstalled'] = $this->full_text_indexing_installed();
593         
594         // check to see if FTS Indexing service is installed
595         if(empty($_SESSION['IsFulltextInstalled']) 
596                 || $_SESSION['IsFulltextInstalled'] === false)
597             return false;
598
599         // grab the dbname if it was not passed through
600                 if (empty($dbname)) {
601                         global $sugar_config;
602                         $dbname = $sugar_config['dbconfig']['db_name'];
603                 }
604         //we already know that Indexing service is installed, now check
605         //to see if it is enabled
606                 $res = $this->db->query(
607             "SELECT DATABASEPROPERTY('$dbname', 'IsFulltextEnabled') ftext");
608                 $row = $GLOBALS['db']->fetchByAssoc($res);
609         
610         return (isset($row['ftext']) && $row['ftext'] == 1);
611         }
612
613     /**
614      * Creates default full text catalog
615      */
616         public function create_default_full_text_catalog() 
617     {
618                 if ($this->full_text_indexing_enabled()) {
619             $GLOBALS['log']->debug('Creating the default catalog for full-text indexing, sugar_fts_catalog');
620                         
621             //drop catalog if exists.
622                         $ret = $this->db->query("
623                 if not exists(
624                     select * 
625                         from sys.fulltext_catalogs 
626                         where name ='sugar_fts_catalog'
627                         ) 
628                 CREATE FULLTEXT CATALOG sugar_fts_catalog");
629
630                         if (empty($ret)) {
631                                 $GLOBALS['log']->error('Error creating default full-text catalog, sugar_fts_catalog');
632                         }
633                 }
634         }
635
636     /**
637      * Function returns name of the constraint automatically generated by sql-server.
638      * We request this for default, primary key, required
639      *
640      * @param  string $table
641      * @param  string $column
642      * @return string 
643      */
644         private function get_field_default_constraint_name(
645         $table, 
646         $column = null
647         ) 
648     {
649         static $results = array();
650         
651         if ( empty($column) && isset($results[$table]) )
652             return $results[$table];
653         
654         $query = <<<EOQ
655 select s.name, o.name, c.name dtrt, d.name ctrt
656     from sys.default_constraints as d
657         join sys.objects as o
658             on o.object_id = d.parent_object_id
659         join sys.columns as c
660             on c.object_id = o.object_id and c.column_id = d.parent_column_id
661         join sys.schemas as s
662             on s.schema_id = o.schema_id
663     where o.name = '$table'
664 EOQ;
665         if ( !empty($column) )
666             $query .= " and c.name = '$column'";
667         $res = $this->db->query($query);
668         if ( !empty($column) ) {
669             $row = $this->db->fetchByAssoc($res);
670             if (!empty($row)) 
671                 return $row['ctrt'];
672         }
673         else {
674             $returnResult = array();
675             while ( $row = $this->db->fetchByAssoc($res) )
676                 $returnResult[$row['dtrt']] = $row['ctrt'];
677             $results[$table] = $returnResult;
678             return $returnResult;
679         }
680                 
681         return null;
682         }
683     
684     /**
685      * @see DBHelper::massageFieldDef()
686      */
687     public function massageFieldDef(
688         &$fieldDef,
689         $tablename
690         )
691     {
692         parent::massageFieldDef($fieldDef,$tablename);
693         
694         if ($fieldDef['type'] == 'int')
695             $fieldDef['len'] = '4';
696         if ($fieldDef['type'] == 'bit' && empty($fieldDef['len']) )
697             $fieldDef['len'] = '1';
698                 if ($fieldDef['type'] == 'bool' && empty($fieldDef['len']) )
699             $fieldDef['len'] = '1';
700         if ($fieldDef['type'] == 'float' && empty($fieldDef['len']) )
701             $fieldDef['len'] = '8';
702         if ($fieldDef['type'] == 'varchar' && empty($fieldDef['len']) )
703             $fieldDef['len'] = '255';
704                 if ($fieldDef['type'] == 'nvarchar' && empty($fieldDef['len']) )
705             $fieldDef['len'] = '255';
706         if ($fieldDef['type'] == 'bit' && empty($fieldDef['default']) )
707             $fieldDef['default'] = '0';
708                 if ($fieldDef['type'] == 'bool' && empty($fieldDef['default']) )
709             $fieldDef['default'] = '0';
710         if ($fieldDef['type'] == 'image' && empty($fieldDef['len']) )
711             $fieldDef['len'] = '2147483647';
712         if ($fieldDef['type'] == 'ntext' && empty($fieldDef['len']) )
713             $fieldDef['len'] = '2147483646';
714         if ($fieldDef['type'] == 'smallint' && empty($fieldDef['len']) )
715             $fieldDef['len'] = '2';
716                 if (isset($fieldDef['required']) && $fieldDef['required'] && !isset($fieldDef['default']) )
717                         $fieldDef['default'] = '';
718     }
719     
720     /**
721      * @see DBHelper::oneColumnSQLRep()
722      */
723     protected function oneColumnSQLRep(
724         $fieldDef,
725         $ignoreRequired = false,
726         $table = '',
727         $return_as_array = false
728         )
729     {
730         //Bug 25814
731                 if(isset($fieldDef['name'])){
732                         $name = $fieldDef['name'];
733                 $type = $this->getFieldType($fieldDef);
734                 $colType = $this->getColumnType($type, $name, $table);
735                 if(stristr($colType, 'decimal')){
736                                 $fieldDef['len'] = isset($fieldDef['len'])? min($fieldDef['len'],38) : 38;
737                         }
738                         //bug: 39690 float(8) is interpreted as real and this generates a diff when doing repair
739                         if(stristr($colType, 'float')){
740                                 if(isset($fieldDef['len']) && $fieldDef['len'] == 8){
741                                         unset($fieldDef['len']);
742                                 }
743                         }
744                 }
745                 
746                 $ref = parent::oneColumnSQLRep($fieldDef, $ignoreRequired, $table, true);
747         
748                 // Bug 24307 - Don't add precision for float fields.
749                 if ( stristr($ref['colType'],'float') )
750                         $ref['colType'] = preg_replace('/(,\d+)/','',$ref['colType']);
751             
752         if ( $return_as_array )
753             return $ref;
754         else
755             return "{$ref['name']} {$ref['colType']} {$ref['default']} {$ref['required']} {$ref['auto_increment']}";
756         }
757         
758     /**
759      * Saves changes to module's audit table
760      *
761      * @param object $bean    Sugarbean instance
762      * @param array  $changes changes
763      * @see DBHelper::getDataChanges()
764      */
765     public function save_audit_records(
766         SugarBean &$bean,
767         &$changes
768         )
769         {
770                 //Bug 25078 fixed by Martin Hu: sqlserver haven't 'date' type, trim extra "00:00:00"
771                 if($changes['data_type'] == 'date'){
772                         $changes['before'] = str_replace(' 00:00:00','',$changes['before']);
773                 }
774                 parent::save_audit_records($bean,$changes);
775         }
776 }
777 ?>