]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - include/database/MysqlHelper.php
Release 6.3.0
[Github/sugarcrm.git] / include / database / MysqlHelper.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 oracle 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): ______________________________________..
49 ********************************************************************************/
50 require_once('include/database/DBHelper.php');
51
52 class MysqlHelper extends DBHelper
53 {
54     /**
55      * Maximum length of identifiers
56      */
57     protected $maxNameLengths = array(
58         'table' => 64,
59         'column' => 64,
60         'index' => 64,
61         'alias' => 256
62     );
63     
64     /**
65      * @see DBHelper::createTableSQL()
66      */
67     public function createTableSQL(
68         SugarBean $bean
69         )
70     {
71         $tablename = $bean->getTableName();
72         $fieldDefs = $bean->getFieldDefinitions();
73         $indices   = $bean->getIndices();
74         $engine    = $this->getEngine($bean);
75         return $this->createTableSQLParams($tablename, $fieldDefs, $indices, $engine);
76         }
77
78     /**
79      * Generates sql for create table statement for a bean.
80      *
81      * @param  string $tablename
82      * @param  array  $fieldDefs
83      * @param  array  $indices
84      * @param  string $engine optional, MySQL engine to use
85      * @return string SQL Create Table statement
86     */
87     public function createTableSQLParams(
88         $tablename,
89         $fieldDefs,
90         $indices,
91         $engine = null
92         )
93     {
94                 if ( empty($engine) && isset($fieldDefs['engine']))
95             $engine = $fieldDefs['engine'];
96         if ( !$this->isEngineEnabled($engine) )
97             $engine = '';
98
99         $sql = parent::createTableSQLParams($tablename,$fieldDefs,$indices);
100         if (!empty($engine))
101             $sql.= " ENGINE=$engine";
102
103         return $sql;
104         }
105
106     /**
107      * Returns the name of the engine to use or null if we are to use the default
108      *
109      * @param  object $bean SugarBean instance
110      * @return string
111      */
112     private function getEngine($bean)
113     {
114         global $dictionary;
115         $engine = null;
116         if (isset($dictionary[$bean->getObjectName()]['engine'])) {
117                         $engine = $dictionary[$bean->getObjectName()]['engine'];
118                 }
119         return $engine;
120     }
121
122     /**
123      * Returns true if the engine given is enabled in the backend
124      *
125      * @param  string $engine
126      * @return bool
127      */
128     private function isEngineEnabled(
129         $engine
130         )
131     {
132         $engine = strtoupper($engine);
133
134         $r = $this->db->query("SHOW ENGINES");
135
136         while ( $row = $this->db->fetchByAssoc($r) )
137             if ( strtoupper($row['Engine']) == $engine )
138                 return ($row['Support']=='YES' || $row['Support']=='DEFAULT');
139
140         return false;
141     }
142
143     /**
144      * @see DBHelper::getColumnType()
145      */
146     public function getColumnType(
147         $type,
148         $name = '',
149         $table = ''
150         )
151     {
152         $map = array(
153             'int'      => 'int',
154             'double'   => 'double',
155             'float'    => 'float',
156             'uint'     => 'int unsigned',
157             'ulong'    => 'bigint unsigned',
158             'long'     => 'bigint',
159             'short'    => 'smallint',
160             'varchar'  => 'varchar',
161             'text'     => 'text',
162             'longtext' => 'longtext',
163             'date'     => 'date',
164             'enum'     => 'varchar',
165             'relate'   => 'varchar',
166             'multienum'=> 'text',
167             'html'     => 'text',
168             'datetime' => 'datetime',
169             'datetimecombo' => 'datetime',
170             'time'     => 'time',
171             'bool'     => 'bool',
172             'tinyint'  => 'tinyint',
173             'char'     => 'char',
174             'blob'     => 'blob',
175             'longblob' => 'longblob',
176             'currency' => 'decimal(26,6)',
177             'decimal'  => 'decimal',
178             'decimal2' => 'decimal',
179             'id'       => 'char(36)',
180            'url'=>'varchar',
181            'encrypt'=>'varchar',
182            'file'      => 'varchar',
183             );
184
185         // Bug 44291 - If requested type is in array, returns it. Otherwise return requested type, so devs could see exactly what went wrong in log.
186         if (isset($map[$type]))
187             return $map[$type];
188         else
189             return $type;
190     }
191
192     /**
193      * @see DBHelper::oneColumnSQLRep()
194      */
195         protected function oneColumnSQLRep(
196         $fieldDef,
197         $ignoreRequired = false,
198         $table = '',
199         $return_as_array = false
200         )
201     {
202         $ref = parent::oneColumnSQLRep($fieldDef, $ignoreRequired, $table, true);
203
204         if ( $ref['colType'] == 'int'
205                 && !empty($fieldDef['len']) )
206             $ref['colType'] .= "(".$fieldDef['len'].")";
207
208         // bug 22338 - don't set a default value on text or blob fields
209         if ( isset($ref['default']) &&
210             ($ref['colType'] == 'text' || $ref['colType'] == 'blob'
211                 || $ref['colType'] == 'longtext' || $ref['colType'] == 'longblob' ))
212             $ref['default'] = '';
213             
214         if ( $return_as_array )
215             return $ref;
216         else
217             return "{$ref['name']} {$ref['colType']} {$ref['default']} {$ref['required']} {$ref['auto_increment']}";
218     }
219
220     /**
221      * @see DBHelper::changeColumnSQL()
222      */
223     protected function changeColumnSQL(
224         $tablename,
225         $fieldDefs,
226         $action,
227         $ignoreRequired = false
228         )
229     {
230         if ($this->isFieldArray($fieldDefs)){
231             foreach ($fieldDefs as $def){
232                 if ($action == 'drop')
233                     $columns[] = $def['name'];
234                 else
235                 $columns[] = $this->oneColumnSQLRep($def, $ignoreRequired);
236             }
237         }else{
238             if ($action == 'drop')
239                 $columns[] = $fieldDefs['name'];
240         else
241             $columns[] = $this->oneColumnSQLRep($fieldDefs);
242         }
243
244         return "alter table $tablename $action column ".implode(",$action column ", $columns);
245     }
246
247     /**
248      * @see DBHelper::deleteColumnSQL()
249      */
250     public function deleteColumnSQL(
251         SugarBean $bean,
252         $fieldDefs
253         )
254     {
255         if ($this->isFieldArray($fieldDefs))
256             foreach ($fieldDefs as $fieldDef)
257                 $columns[] = $fieldDef['name'];
258         else
259             $columns[] = $fieldDefs['name'];
260
261         return "alter table ".$bean->getTableName()." drop column ".implode(", drop column ", $columns);
262     }
263
264     /**
265      * @see DBHelper::keysSQL
266      */
267     public function keysSQL(
268         $indices,
269         $alter_table = false,
270         $alter_action = ''
271         )
272         {
273        // check if the passed value is an array of fields.
274        // if not, convert it into an array
275        if (!$this->isFieldArray($indices))
276            $indices[] = $indices;
277
278        $columns = array();
279        foreach ($indices as $index) {
280            if(!empty($index['db']) && $index['db'] != 'mysql')
281                continue;
282            if (isset($index['source']) && $index['source'] != 'db')
283                continue;
284            
285            $type = $index['type'];
286            $name = $index['name'];
287
288            if (is_array($index['fields']))
289                $fields = implode(", ", $index['fields']);
290            else
291                $fields = $index['fields'];
292
293            switch ($type) {
294            case 'unique':
295                $columns[] = " UNIQUE $name ($fields)";
296                break;
297            case 'primary':
298                $columns[] = " PRIMARY KEY ($fields)";
299                break;
300            case 'index':
301            case 'foreign':
302            case 'clustered':
303            case 'alternate_key':
304                /**
305                 * @todo here it is assumed that the primary key of the foreign
306                 * table will always be named 'id'. It must be noted though
307                 * that this can easily be fixed by referring to db dictionary
308                 * to find the correct primary field name
309                 */
310                if ( $alter_table )
311                    $columns[] = " INDEX $name ($fields)";
312                else
313                    $columns[] = " KEY $name ($fields)";
314                break;
315            case 'fulltext':
316                if ($this->full_text_indexing_enabled())
317                    $columns[] = " FULLTEXT ($fields)";
318                else
319                    $GLOBALS['log']->debug('MYISAM engine is not available/enabled, full-text indexes will be skipped. Skipping:',$name);
320                break;
321           }
322        }
323        $columns = implode(", $alter_action ", $columns);
324        if(!empty($alter_action)){
325            $columns = $alter_action . ' '. $columns;
326        }
327        return $columns;
328     }
329
330     /**
331      * @see DBHelper::setAutoIncrement()
332      */
333         protected function setAutoIncrement(
334         $table,
335         $field_name
336         )
337     {
338                 return "auto_increment";
339         }
340
341         /**
342      * Sets the next auto-increment value of a column to a specific value.
343      *
344      * @param  string $table tablename
345      * @param  string $field_name
346      */
347     public function setAutoIncrementStart(
348         $table,
349         $field_name,
350         $start_value
351         )
352     {
353         $this->db->query( "ALTER TABLE $table AUTO_INCREMENT = $start_value;");
354
355         return true;
356     }
357
358     /**
359      * Returns the next value for an auto increment
360      *
361      * @param  string $table tablename
362      * @param  string $field_name
363      * @return string
364      */
365     public function getAutoIncrement(
366         $table,
367         $field_name
368         )
369     {
370
371         $result = $this->db->query("SHOW TABLE STATUS LIKE '$table'");
372         $row = $this->db->fetchByAssoc($result);
373         if (!empty($row['Auto_increment']))
374             return $row['Auto_increment'];
375
376         return "";
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         $result = $this->db->query("SHOW INDEX FROM $tablename");
388
389         $indices = array();
390         while (($row=$this->db->fetchByAssoc($result)) !=null) {
391             $index_type='index';
392             if ($row['Key_name'] =='PRIMARY') {
393                 $index_type='primary';
394             }
395             elseif ( $row['Non_unique'] == '0' ) {
396                 $index_type='unique';
397             }
398             $name = strtolower($row['Key_name']);
399             $indices[$name]['name']=$name;
400             $indices[$name]['type']=$index_type;
401             $indices[$name]['fields'][]=strtolower($row['Column_name']);
402         }
403         return $indices;
404     }
405
406         /**
407      * @see DBHelper::get_columns()
408      */
409     public function get_columns(
410         $tablename
411         )
412     {
413         //find all unique indexes and primary keys.
414         $result = $this->db->query("DESCRIBE $tablename");
415
416         $columns = array();
417         while (($row=$this->db->fetchByAssoc($result)) !=null) {
418             $name = strtolower($row['Field']);
419             $columns[$name]['name']=$name;
420             $matches = array();
421             preg_match_all("/(\w+)(?:\(([0-9]+,?[0-9]*)\)|)( unsigned)?/i", $row['Type'], $matches);
422             $columns[$name]['type']=strtolower($matches[1][0]);
423             if ( isset($matches[2][0]) && in_array(strtolower($matches[1][0]),array('varchar','char','varchar2','int','decimal','float')) )
424                 $columns[$name]['len']=strtolower($matches[2][0]);
425             if ( stristr($row['Extra'],'auto_increment') )
426                 $columns[$name]['auto_increment'] = '1';
427             if ($row['Null'] == 'NO' && !stristr($row['Key'],'PRI'))
428                 $columns[$name]['required'] = 'true';
429             if (!empty($row['Default']) )
430                 $columns[$name]['default'] = $row['Default'];
431         }
432         return $columns;
433     }
434
435     /**
436      * @see DBHelper::add_drop_constraint()
437      */
438     public function add_drop_constraint(
439         $table,
440         $definition,
441         $drop = false
442         )
443     {
444         $type         = $definition['type'];
445         $fields       = implode(',',$definition['fields']);
446         $name         = $definition['name'];
447         $foreignTable = isset($definition['foreignTable']) ? $definition['foreignTable'] : array();
448         $sql          = '';
449
450         switch ($type){
451         // generic indices
452         case 'index':
453         case 'alternate_key':
454             if ($drop)
455                 $sql = "DROP INDEX {$name} ";
456             else
457                 $sql = "CREATE INDEX {$name} ON {$table} ({$fields})";
458             break;
459         // constraints as indices
460         case 'unique':
461             if ($drop)
462                 $sql = "ALTER TABLE {$table} DROP INDEX $name";
463             else
464                 $sql = "ALTER TABLE {$table} ADD CONSTRAINT UNIQUE {$name} ({$fields})";
465             break;
466         case 'primary':
467             if ($drop)
468                 $sql = "ALTER TABLE {$table} DROP PRIMARY KEY";
469             else
470                 $sql = "ALTER TABLE {$table} ADD CONSTRAINT PRIMARY KEY ({$fields})";
471             break;
472         case 'foreign':
473             if ($drop)
474                 $sql = "ALTER TABLE {$table} DROP FOREIGN KEY ({$fields})";
475             else
476                 $sql = "ALTER TABLE {$table} ADD CONSTRAINT FOREIGN KEY {$name} ({$fields}) REFERENCES {$foreignTable}({$foreignfields})";
477             break;
478         }
479         return $sql;
480     }
481
482     /**
483      * @see DBHelper::number_of_columns()
484      */
485     public function number_of_columns(
486         $table_name
487         )
488     {
489         $result = $this->db->query("DESCRIBE $table_name");
490
491         return ($this->db->getRowCount($result));
492     }
493
494         /**
495      * @see DBHelper::full_text_indexing_enabled()
496      */
497     protected function full_text_indexing_enabled(
498         $dbname = null
499         )
500     {
501                 return $this->isEngineEnabled('MyISAM');
502         }
503
504     /**
505      * @see DBHelper::massageFieldDef()
506      */
507     public function massageFieldDef(
508         &$fieldDef,
509         $tablename
510         )
511     {
512         DBHelper::massageFieldDef($fieldDef,$tablename);
513
514         if ( isset($fieldDef['default']) &&
515             ($fieldDef['dbType'] == 'text'
516                 || $fieldDef['dbType'] == 'blob'
517                 || $fieldDef['dbType'] == 'longtext'
518                 || $fieldDef['dbType'] == 'longblob' ))
519             unset($fieldDef['default']);
520         if ($fieldDef['dbType'] == 'uint')
521             $fieldDef['len'] = '10';
522         if ($fieldDef['dbType'] == 'ulong')
523             $fieldDef['len'] = '20';
524         if ($fieldDef['dbType'] == 'bool')
525             $fieldDef['type'] = 'tinyint';
526         if ($fieldDef['dbType'] == 'bool' && empty($fieldDef['default']) )
527             $fieldDef['default'] = '0';
528         if (($fieldDef['dbType'] == 'varchar' || $fieldDef['dbType'] == 'enum') && empty($fieldDef['len']) )
529             $fieldDef['len'] = '255';
530         if ($fieldDef['dbType'] == 'uint')
531             $fieldDef['len'] = '10';
532         if ($fieldDef['dbType'] == 'int' && empty($fieldDef['len']) )
533             $fieldDef['len'] = '11';
534     }
535 }
536 ?>