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