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