]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - include/database/DBManager.php
Release 6.5.0
[Github/sugarcrm.git] / include / database / DBManager.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-2012 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.
41 * It acts as the DB abstraction layer for the application. It depends on helper classes
42 * which generate the necessary SQL. This sql is then passed to PEAR DB classes.
43 * The helper class is chosen in DBManagerFactory, which is driven by 'db_type' in 'dbconfig' under config.php.
44 *
45 * All the functions in this class will work with any bean which implements the meta interface.
46 * The passed bean is passed to helper class which uses these functions to generate correct sql.
47 *
48 * The meta interface has the following functions:
49 * getTableName()                        Returns table name of the object.
50 * getFieldDefinitions()         Returns a collection of field definitions in order.
51 * getFieldDefintion(name)               Return field definition for the field.
52 * getFieldValue(name)           Returns the value of the field identified by name.
53 *                               If the field is not set, the function will return boolean FALSE.
54 * getPrimaryFieldDefinition()   Returns the field definition for primary key
55 *
56 * The field definition is an array with the following keys:
57 *
58 * name          This represents name of the field. This is a required field.
59 * type          This represents type of the field. This is a required field and valid values are:
60 *           �   int
61 *           �   long
62 *           �   varchar
63 *           �   text
64 *           �   date
65 *           �   datetime
66 *           �   double
67 *           �   float
68 *           �   uint
69 *           �   ulong
70 *           �   time
71 *           �   short
72 *           �   enum
73 * length    This is used only when the type is varchar and denotes the length of the string.
74 *           The max value is 255.
75 * enumvals  This is a list of valid values for an enum separated by "|".
76 *           It is used only if the type is �enum�;
77 * required  This field dictates whether it is a required value.
78 *           The default value is �FALSE�.
79 * isPrimary This field identifies the primary key of the table.
80 *           If none of the fields have this flag set to �TRUE�,
81 *           the first field definition is assume to be the primary key.
82 *           Default value for this field is �FALSE�.
83 * default   This field sets the default value for the field definition.
84 *
85 *
86 * Portions created by SugarCRM are Copyright (C) SugarCRM, Inc.
87 * All Rights Reserved.
88 * Contributor(s): ______________________________________..
89 ********************************************************************************/
90
91 /**
92  * Base database driver implementation
93  * @api
94  */
95 abstract class DBManager
96 {
97         /**
98          * Name of database
99          * @var resource
100          */
101         public $database = null;
102
103         /**
104          * Indicates whether we should die when we get an error from the DB
105          */
106         protected $dieOnError = false;
107
108         /**
109          * Indicates whether we should html encode the results from a query by default
110          */
111         protected $encode = true;
112
113         /**
114          * Records the execution time of the last query
115          */
116         protected $query_time = 0;
117
118         /**
119          * Last error message from the DB backend
120          */
121         protected $last_error = false;
122
123         /**
124          * Registry of available result sets
125          */
126         protected $lastResult;
127
128         /**
129          * Current query count
130          */
131         private static $queryCount = 0;
132
133         /**
134          * Query threshold limit
135          */
136         private static $queryLimit = 0;
137
138         /**
139          * Array of prepared statements and their correspoding parsed tokens
140          */
141         protected $preparedTokens = array();
142
143         /**
144          * TimeDate instance
145          * @var TimeDate
146          */
147         protected $timedate;
148
149         /**
150          * PHP Logger
151          * @var Logger
152          */
153         protected $log;
154
155         /**
156          * Table descriptions
157          * @var array
158          */
159         protected static $table_descriptions = array();
160
161         /**
162          * Index descriptions
163          * @var array
164          */
165         protected static $index_descriptions = array();
166
167         /**
168          * Maximum length of identifiers
169          * @abstract
170          * @var array
171          */
172         protected $maxNameLengths = array(
173                 'table' => 64,
174                 'column' => 64,
175                 'index' => 64,
176                 'alias' => 64
177         );
178
179         /**
180          * DB driver priority
181          * Higher priority drivers override lower priority ones
182          * @var int
183          */
184         public $priority = 0;
185
186         /**
187          * Driver name label, for install
188          * @absrtact
189          * @var string
190          */
191         public $label = '';
192
193         /**
194          * Type names map
195          * @abstract
196          * @var array
197          */
198         protected $type_map = array();
199
200         /**
201          * Type classification into:
202          * - int
203          * - bool
204          * - float
205          * - date
206          * @abstract
207          * @var array
208          */
209         protected $type_class = array(
210                         'int'      => 'int',
211                         'double'   => 'float',
212                         'float'    => 'float',
213                         'uint'     => 'int',
214                         'ulong'    => 'bigint',
215                         'long'     => 'bigint',
216                         'short'    => 'int',
217                         'date'     => 'date',
218                         'datetime' => 'date',
219                         'datetimecombo' => 'date',
220                         'time'     => 'time',
221                         'bool'     => 'bool',
222                         'tinyint'  => 'int',
223                         'currency' => 'float',
224                         'decimal'  => 'float',
225                         'decimal2' => 'float',
226         );
227
228         /**
229          * Capabilities this DB supports. Supported list:
230          * affected_rows        Can report query affected rows for UPDATE/DELETE
231          *                                      implement getAffectedRowCount()
232          * select_rows          Can report row count for SELECT
233          *                                      implement getRowCount()
234          * case_sensitive       Supports case-sensitive text columns
235          * fulltext                     Supports fulltext search indexes
236          * inline_keys          Supports defining keys together with the table
237          * auto_increment_sequence Autoincrement support implemented as sequence
238          * limit_subquery   Supports LIMIT clauses in subqueries
239          * create_user          Can create users for Sugar
240          * create_db            Can create databases
241          * collation            Supports setting collations
242          * disable_keys     Supports temporarily disabling keys (for upgrades, etc.)
243          *
244          * @abstract
245          * Special cases:
246          * fix:expandDatabase - needs expandDatabase fix, see expandDatabase.php
247          * TODO: verify if we need these cases
248          */
249         protected $capabilities = array();
250
251         /**
252          * Database options
253          * @var array
254          */
255         protected $options = array();
256
257     /**
258      * Create DB Driver
259      */
260         public function __construct()
261         {
262                 $this->timedate = TimeDate::getInstance();
263                 $this->log = $GLOBALS['log'];
264                 $this->helper = $this; // compatibility
265         }
266
267     /**
268      * Wrapper for those trying to access the private and protected class members directly
269      * @param string $p var name
270      * @return mixed
271      */
272         public function __get($p)
273         {
274                 $this->log->info('Call to DBManager::$'.$p.' is deprecated');
275                 return $this->$p;
276         }
277
278         /**
279          * Returns the current database handle
280          * @return resource
281          */
282         public function getDatabase()
283         {
284                 $this->checkConnection();
285                 return $this->database;
286         }
287
288         /**
289          * Returns this instance's DBHelper
290          * Actually now returns $this
291          * @deprecated
292          * @return DBManager
293          */
294         public function getHelper()
295         {
296                 return $this;
297         }
298
299         /**
300          * Checks for error happening in the database
301          *
302          * @param  string $msg        message to prepend to the error message
303          * @param  bool   $dieOnError true if we want to die immediately on error
304          * @return bool True if there was an error
305          */
306         public function checkError($msg = '', $dieOnError = false)
307         {
308                 if (empty($this->database)) {
309                         $this->registerError("$msg: Database Is Not Connected", $dieOnError);
310                         return true;
311                 }
312
313                 $dberror = $this->lastDbError();
314                 if($dberror === false) {
315                 $this->last_error = false;
316                 return false;
317                 }
318                 $this->registerError($msg, $dberror, $dieOnError);
319         return true;
320         }
321
322         /**
323          * Register database error
324          * If die-on-error flag is set, logs the message and dies,
325          * otherwise sets last_error to the message
326          * @param string $userMessage Message from function user
327          * @param string $message Message from SQL driver
328          * @param bool $dieOnError
329          */
330         protected function registerError($userMessage, $message, $dieOnError = false)
331         {
332                 if(!empty($message)) {
333                         if(!empty($userMessage)) {
334                                 $message = "$userMessage: $message";
335                         }
336                         if(empty($message)) {
337                             $message = "Database error";
338                         }
339                         $this->log->fatal($message);
340                         if ($dieOnError || $this->dieOnError) {
341                                 if(isset($GLOBALS['app_strings']['ERR_DB_FAIL'])) {
342                                         sugar_die($GLOBALS['app_strings']['ERR_DB_FAIL']);
343                                 } else {
344                                         sugar_die("Database error. Please check sugarcrm.log for details.");
345                                 }
346                         } else {
347                                 $this->last_error = $message;
348                         }
349                 }
350         }
351
352         /**
353          * Return DB error message for the last query executed
354          * @return string Last error message
355          */
356         public function lastError()
357         {
358                 return $this->last_error;
359         }
360
361         /**
362          * This method is called by every method that runs a query.
363          * If slow query dumping is turned on and the query time is beyond
364          * the time limit, we will log the query. This function may do
365          * additional reporting or log in a different area in the future.
366          *
367          * @param  string  $query query to log
368          * @return boolean true if the query was logged, false otherwise
369          */
370         protected function dump_slow_queries($query)
371         {
372                 global $sugar_config;
373
374                 $do_the_dump = isset($sugar_config['dump_slow_queries'])
375                         ? $sugar_config['dump_slow_queries'] : false;
376                 $slow_query_time_msec = isset($sugar_config['slow_query_time_msec'])
377                         ? $sugar_config['slow_query_time_msec'] : 5000;
378
379                 if($do_the_dump) {
380                         if($slow_query_time_msec < ($this->query_time * 1000)) {
381                                 // Then log both the query and the query time
382                                 $this->log->fatal('Slow Query (time:'.$this->query_time."\n".$query);
383                                 return true;
384                         }
385                 }
386                 return false;
387         }
388
389 /**
390         * Scans order by to ensure that any field being ordered by is.
391         *
392         * It will throw a warning error to the log file - fatal if slow query logging is enabled
393         *
394         * @param  string $sql         query to be run
395         * @param  bool   $object_name optional, object to look up indices in
396         * @return bool   true if an index is found false otherwise
397         */
398 protected function checkQuery($sql, $object_name = false)
399 {
400         $match = array();
401         preg_match_all("'.* FROM ([^ ]*).* ORDER BY (.*)'is", $sql, $match);
402         $indices = false;
403         if (!empty($match[1][0]))
404                 $table = $match[1][0];
405         else
406                 return false;
407
408         if (!empty($object_name) && !empty($GLOBALS['dictionary'][$object_name]))
409                 $indices = $GLOBALS['dictionary'][$object_name]['indices'];
410
411         if (empty($indices)) {
412                 foreach ( $GLOBALS['dictionary'] as $current ) {
413                         if ($current['table'] == $table){
414                                 $indices = $current['indices'];
415                                 break;
416                         }
417                 }
418         }
419         if (empty($indices)) {
420                 $this->log->warn('CHECK QUERY: Could not find index definitions for table ' . $table);
421                 return false;
422         }
423         if (!empty($match[2][0])) {
424                 $orderBys = explode(' ', $match[2][0]);
425                 foreach ($orderBys as $orderBy){
426                         $orderBy = trim($orderBy);
427                         if (empty($orderBy))
428                                 continue;
429                         $orderBy = strtolower($orderBy);
430                         if ($orderBy == 'asc' || $orderBy == 'desc')
431                                 continue;
432
433                         $orderBy = str_replace(array($table . '.', ','), '', $orderBy);
434
435                         foreach ($indices as $index)
436                                 if (empty($index['db']) || $index['db'] == $this->dbType)
437                                         foreach ($index['fields'] as $field)
438                                                 if ($field == $orderBy)
439                                                         return true;
440
441                         $warning = 'Missing Index For Order By Table: ' . $table . ' Order By:' . $orderBy ;
442                         if (!empty($GLOBALS['sugar_config']['dump_slow_queries']))
443                                 $this->log->fatal('CHECK QUERY:' .$warning);
444                         else
445                                 $this->log->warn('CHECK QUERY:' .$warning);
446                 }
447         }
448         return false;
449         }
450
451         /**
452          * Returns the time the last query took to execute
453          *
454          * @return int
455          */
456         public function getQueryTime()
457         {
458                 return $this->query_time;
459         }
460
461         /**
462          * Checks the current connection; if it is not connected then reconnect
463          */
464         public function checkConnection()
465         {
466                 $this->last_error = '';
467                 if (!isset($this->database))
468                         $this->connect();
469         }
470
471         /**
472          * Sets the dieOnError value
473          *
474          * @param bool $value
475          */
476         public function setDieOnError($value)
477         {
478                 $this->dieOnError = $value;
479         }
480
481     /**
482      * Implements a generic insert for any bean.
483      *
484      * @param SugarBean $bean SugarBean instance
485      * @return bool query result
486      *
487      */
488         public function insert(SugarBean $bean)
489         {
490                 $sql = $this->insertSQL($bean);
491                 $tablename =  $bean->getTableName();
492                 $msg = "Error inserting into table: $tablename:";
493                 return $this->query($sql,true,$msg);
494         }
495
496         /**
497          * Insert data into table by parameter definition
498          * @param string $table Table name
499          * @param array $field_defs Definitions in vardef-like format
500          * @param array $data Key/value to insert
501          * @param array $field_map Fields map from SugarBean
502          * @param bool $execute Execute or return query?
503      * @return bool query result
504      */
505         public function insertParams($table, $field_defs, $data, $field_map = null, $execute = true)
506         {
507                 $values = array();
508                 foreach ($field_defs as $field => $fieldDef)
509                 {
510                         if (isset($fieldDef['source']) && $fieldDef['source'] != 'db')  continue;
511                         //custom fields handle there save seperatley
512                         if(!empty($field_map) && !empty($field_map[$field]['custom_type'])) continue;
513
514                         if(isset($data[$field])) {
515                                 // clean the incoming value..
516                                 $val = from_html($data[$field]);
517                         } else {
518                                 if(isset($fieldDef['default']) && strlen($fieldDef['default']) > 0) {
519                                         $val = $fieldDef['default'];
520                                 } else {
521                                         $val = null;
522                                 }
523                         }
524
525                         //handle auto increment values here - we may have to do something like nextval for oracle
526                         if (!empty($fieldDef['auto_increment'])) {
527                                 $auto = $this->getAutoIncrementSQL($table, $fieldDef['name']);
528                                 if(!empty($auto)) {
529                                         $values[$field] = $auto;
530                                 }
531                         } elseif ($fieldDef['name'] == 'deleted') {
532                                 $values['deleted'] = (int)$val;
533                         } else {
534                                 // need to do some thing about types of values
535                                 if(!is_null($val) || !empty($fieldDef['required'])) {
536                                         $values[$field] = $this->massageValue($val, $fieldDef);
537                                 }
538                         }
539                 }
540
541                 if (empty($values))
542                         return $execute?true:''; // no columns set
543
544                 // get the entire sql
545                 $query = "INSERT INTO $table (".implode(",", array_keys($values)).")
546                                         VALUES (".implode(",", $values).")";
547                 return $execute?$this->query($query):$query;
548         }
549
550     /**
551      * Implements a generic update for any bean
552      *
553      * @param SugarBean $bean Sugarbean instance
554      * @param array $where values with the keys as names of fields.
555      * If we want to pass multiple values for a name, pass it as an array
556      * If where is not passed, it defaults to id of table
557      * @return bool query result
558      *
559      */
560         public function update(SugarBean $bean, array $where = array())
561         {
562                 $sql = $this->updateSQL($bean, $where);
563                 $tablename = $bean->getTableName();
564                 $msg = "Error updating table: $tablename:";
565                 return $this->query($sql,true,$msg);
566         }
567
568     /**
569      * Implements a generic delete for any bean identified by id
570      *
571      * @param SugarBean $bean Sugarbean instance
572      * @param array  $where values with the keys as names of fields.
573      * If we want to pass multiple values for a name, pass it as an array
574      * If where is not passed, it defaults to id of table
575      * @return bool query result
576      */
577         public function delete(SugarBean $bean, array $where = array())
578         {
579                 $sql = $this->deleteSQL($bean, $where);
580                 $tableName = $bean->getTableName();
581                 $msg = "Error deleting from table: ".$tableName. ":";
582                 return $this->query($sql,true,$msg);
583         }
584
585         /**
586          * Implements a generic retrieve for any bean identified by id
587          *
588          * If we want to pass multiple values for a name, pass it as an array
589          * If where is not passed, it defaults to id of table
590          *
591          * @param  SugarBean   $bean  Sugarbean instance
592          * @param  array    $where values with the keys as names of fields.
593          * @return resource result from the query
594          */
595         public function retrieve(SugarBean $bean, array $where = array())
596         {
597                 $sql = $this->retrieveSQL($bean, $where);
598                 $tableName = $bean->getTableName();
599                 $msg = "Error retriving values from table:".$tableName. ":";
600                 return $this->query($sql,true,$msg);
601         }
602
603         /**
604          * Implements a generic retrieve for a collection of beans.
605          *
606          * These beans will be joined in the sql by the key attribute of field defs.
607          * Currently, this function does support outer joins.
608          *
609          * @param  array $beans Sugarbean instance(s)
610          * @param  array $cols  columns to be returned with the keys as names of bean as identified by
611          * get_class of bean. Values of this array is the array of fieldDefs to be returned for a bean.
612          * If an empty array is passed, all columns are selected.
613          * @param  array $where  values with the keys as names of bean as identified by get_class of bean
614          * Each value at the first level is an array of values for that bean identified by name of fields.
615          * If we want to pass multiple values for a name, pass it as an array
616          * If where is not passed, all the rows will be returned.
617          * @return resource
618          */
619         public function retrieveView(array $beans, array $cols = array(), array $where = array())
620         {
621                 $sql = $this->retrieveViewSQL($beans, $cols, $where);
622                 $msg = "Error retriving values from View Collection:";
623                 return $this->query($sql,true,$msg);
624         }
625
626
627         /**
628          * Implements creation of a db table for a bean.
629          *
630          * NOTE: does not handle out-of-table constraints, use createConstraintSQL for that
631          * @param SugarBean $bean  Sugarbean instance
632          */
633         public function createTable(SugarBean $bean)
634         {
635                 $sql = $this->createTableSQL($bean);
636                 $tablename = $bean->getTableName();
637                 $msg = "Error creating table: $tablename:";
638                 $this->query($sql,true,$msg);
639                 if(!$this->supports("inline_keys")) {
640                 // handle constraints and indices
641                         $indicesArr = $this->createConstraintSql($bean);
642                         if (count($indicesArr) > 0)
643                                 foreach ($indicesArr as $indexSql)
644                                         $this->query($indexSql, true, $msg);
645                 }
646         }
647
648         /**
649          * returns SQL to create constraints or indices
650          *
651          * @param  SugarBean $bean SugarBean instance
652          * @return array list of SQL statements
653          */
654         protected function createConstraintSql(SugarBean $bean)
655         {
656                 return $this->getConstraintSql($bean->getIndices(), $bean->getTableName());
657         }
658
659         /**
660          * Implements creation of a db table
661          *
662          * @param string $tablename
663          * @param array  $fieldDefs  Field definitions, in vardef format
664          * @param array  $indices    Index definitions, in vardef format
665          * @param string $engine    Engine parameter, used for MySQL engine so far
666      * @todo: refactor engine param to be more generic
667      * @return bool success value
668      */
669         public function createTableParams($tablename, $fieldDefs, $indices, $engine = null)
670         {
671                 if (!empty($fieldDefs)) {
672                         $sql = $this->createTableSQLParams($tablename, $fieldDefs, $indices, $engine);
673                         $res = true;
674                         if ($sql) {
675                                 $msg = "Error creating table: $tablename";
676                                 $res = ($res and $this->query($sql,true,$msg));
677                         }
678                         if(!$this->supports("inline_keys")) {
679                                 // handle constraints and indices
680                                 $indicesArr = $this->getConstraintSql($indices, $tablename);
681                                 if (count($indicesArr) > 0)
682                                         foreach ($indicesArr as $indexSql)
683                                                 $res = ($res and $this->query($indexSql, true, "Error creating indexes"));
684                         }
685                         return $res;
686                 }
687                 return false;
688         }
689
690         /**
691          * Implements repair of a db table for a bean.
692          *
693          * @param  SugarBean $bean    SugarBean instance
694          * @param  bool   $execute true if we want the action to take place, false if we just want the sql returned
695          * @return string SQL statement or empty string, depending upon $execute
696          */
697         public function repairTable(SugarBean $bean, $execute = true)
698         {
699                 $indices   = $bean->getIndices();
700                 $fielddefs = $bean->getFieldDefinitions();
701                 $tablename = $bean->getTableName();
702
703                 //Clean the indexes to prevent duplicate definitions
704                 $new_index = array();
705                 foreach($indices as $ind_def){
706                         $new_index[$ind_def['name']] = $ind_def;
707                 }
708                 //jc: added this for beans that do not actually have a table, namely
709                 //ForecastOpportunities
710                 if($tablename == 'does_not_exist' || $tablename == '')
711                         return '';
712
713                 global $dictionary;
714                 $engine=null;
715                 if (isset($dictionary[$bean->getObjectName()]['engine']) && !empty($dictionary[$bean->getObjectName()]['engine']) )
716                         $engine = $dictionary[$bean->getObjectName()]['engine'];
717
718                 return $this->repairTableParams($tablename, $fielddefs,$new_index,$execute,$engine);
719         }
720
721         /**
722          * Can this field be null?
723          * Auto-increment and ID fields can not be null
724          * @param array $vardef
725      * @return bool
726      */
727         protected function isNullable($vardef)
728         {
729
730                 if(isset($vardef['isnull']) && (strtolower($vardef['isnull']) == 'false' || $vardef['isnull'] === false)
731                         && !empty($vardef['required'])) {
732                                 /* required + is_null=false => not null */
733                         return false;
734                 }
735                 if(empty($vardef['auto_increment']) && (empty($vardef['type']) || $vardef['type'] != 'id')
736                                         && (empty($vardef['dbType']) || $vardef['dbType'] != 'id')
737                                         && (empty($vardef['name']) || ($vardef['name'] != 'id' && $vardef['name'] != 'deleted'))
738                 ) {
739                         return true;
740                 }
741                 return false;
742         }
743
744
745         /**
746          * Builds the SQL commands that repair a table structure
747          *
748          * @param  string $tablename
749          * @param  array  $fielddefs Field definitions, in vardef format
750          * @param  array  $indices   Index definitions, in vardef format
751          * @param  bool   $execute   optional, true if we want the queries executed instead of returned
752          * @param  string $engine    optional, MySQL engine
753      * @todo: refactor engine param to be more generic
754      * @return string
755      */
756         public function repairTableParams($tablename, $fielddefs,  $indices, $execute = true, $engine = null)
757         {
758                 //jc: had a bug when running the repair if the tablename is blank the repair will
759                 //fail when it tries to create a repair table
760                 if ($tablename == '' || empty($fielddefs))
761                         return '';
762
763                 //if the table does not exist create it and we are done
764                 $sql = "/* Table : $tablename */\n";
765                 if (!$this->tableExists($tablename)) {
766                         $createtablesql = $this->createTableSQLParams($tablename,$fielddefs,$indices,$engine);
767                         if($execute && $createtablesql){
768                                 $this->createTableParams($tablename,$fielddefs,$indices,$engine);
769                         }
770
771                         $sql .= "/* MISSING TABLE: {$tablename} */\n";
772                         $sql .= $createtablesql . "\n";
773                         return $sql;
774                 }
775
776                 $compareFieldDefs = $this->get_columns($tablename);
777                 $compareIndices = $this->get_indices($tablename);
778
779                 $take_action = false;
780
781                 // do column comparisons
782                 $sql .= "/*COLUMNS*/\n";
783                 foreach ($fielddefs as $name => $value) {
784                         if (isset($value['source']) && $value['source'] != 'db')
785                                 continue;
786
787             // Bug #42406. Skipping breaked vardef without type or name
788             if (isset($value['name']) == false || $value['name'] == false)
789             {
790                 $sql .= "/* NAME IS MISSING IN VARDEF $tablename::$name */\n";
791                 continue;
792             }
793             else if (isset($value['type']) == false || $value['type'] == false)
794             {
795                 $sql .= "/* TYPE IS MISSING IN VARDEF $tablename::$name */\n";
796                 continue;
797             }
798
799                         $name = strtolower($value['name']);
800                         // add or fix the field defs per what the DB is expected to give us back
801                         $this->massageFieldDef($value,$tablename);
802
803                         $ignorerequired=false;
804
805                         //Do not track requiredness in the DB, auto_increment, ID,
806                         // and deleted fields are always required in the DB, so don't force those
807                         if ($this->isNullable($value)) {
808                                 $value['required'] = false;
809                         }
810                         //Should match the conditions in DBManager::oneColumnSQLRep for DB required fields, type='id' fields will sometimes
811
812                         //come into this function as 'type' = 'char', 'dbType' = 'id' without required set in $value. Assume they are correct and leave them alone.
813                         else if (($name == 'id' || $value['type'] == 'id' || (isset($value['dbType']) && $value['dbType'] == 'id'))
814                                 && (!isset($value['required']) && isset($compareFieldDefs[$name]['required'])))
815                         {
816                                 $value['required'] = $compareFieldDefs[$name]['required'];
817                         }
818
819                         if ( !isset($compareFieldDefs[$name]) ) {
820                                 // ok we need this field lets create it
821                                 $sql .= "/*MISSING IN DATABASE - $name -  ROW*/\n";
822                                 $sql .= $this->addColumnSQL($tablename, $value) .  "\n";
823                                 if ($execute)
824                                         $this->addColumn($tablename, $value);
825                                 $take_action = true;
826                         } elseif ( !$this->compareVarDefs($compareFieldDefs[$name],$value)) {
827                                 //fields are different lets alter it
828                                 $sql .= "/*MISMATCH WITH DATABASE - $name -  ROW ";
829                                 foreach($compareFieldDefs[$name] as $rKey => $rValue) {
830                                         $sql .= "[$rKey] => '$rValue'  ";
831                                 }
832                                 $sql .= "*/\n";
833                                 $sql .= "/* VARDEF - $name -  ROW";
834                                 foreach($value as $rKey => $rValue) {
835                                         $sql .= "[$rKey] => '$rValue'  ";
836                                 }
837                                 $sql .= "*/\n";
838
839                                 //jc: oracle will complain if you try to execute a statement that sets a column to (not) null
840                                 //when it is already (not) null
841                                 if ( isset($value['isnull']) && isset($compareFieldDefs[$name]['isnull']) &&
842                                         $value['isnull'] === $compareFieldDefs[$name]['isnull']) {
843                                         unset($value['required']);
844                                         $ignorerequired=true;
845                                 }
846
847                                 //dwheeler: Once a column has been defined as null, we cannot try to force it back to !null
848                                 if ((isset($value['required']) && ($value['required'] === true || $value['required'] == 'true' || $value['required'] === 1))
849                                         && (empty($compareFieldDefs[$name]['required']) || $compareFieldDefs[$name]['required'] != 'true'))
850                                 {
851                                         $ignorerequired = true;
852                                 }
853                                 $altersql = $this->alterColumnSQL($tablename, $value,$ignorerequired);
854                                 if(is_array($altersql)) {
855                                         $altersql = join("\n", $altersql);
856                                 }
857                                 $sql .= $altersql .  "\n";
858                                 if($execute){
859                                         $this->alterColumn($tablename, $value, $ignorerequired);
860                                 }
861                                 $take_action = true;
862                         }
863                 }
864
865                 // do index comparisons
866                 $sql .= "/* INDEXES */\n";
867                 $correctedIndexs = array();
868
869         $compareIndices_case_insensitive = array();
870
871                 // do indices comparisons case-insensitive
872                 foreach($compareIndices as $k => $value){
873                         $value['name'] = strtolower($value['name']);
874                         $compareIndices_case_insensitive[strtolower($k)] = $value;
875                 }
876                 $compareIndices = $compareIndices_case_insensitive;
877                 unset($compareIndices_case_insensitive);
878
879                 foreach ($indices as $value) {
880                         if (isset($value['source']) && $value['source'] != 'db')
881                                 continue;
882
883
884                         $validDBName = $this->getValidDBName($value['name'], true, 'index', true);
885                         if (isset($compareIndices[$validDBName])) {
886                                 $value['name'] = $validDBName;
887                         }
888                     $name = strtolower($value['name']);
889
890                         //Don't attempt to fix the same index twice in one pass;
891                         if (isset($correctedIndexs[$name]))
892                                 continue;
893
894                         //don't bother checking primary nothing we can do about them
895                         if (isset($value['type']) && $value['type'] == 'primary')
896                                 continue;
897
898                         //database helpers do not know how to handle full text indices
899                         if ($value['type']=='fulltext')
900                                 continue;
901
902                         if ( in_array($value['type'],array('alternate_key','foreign')) )
903                                 $value['type'] = 'index';
904
905                         if ( !isset($compareIndices[$name]) ) {
906                                 //First check if an index exists that doesn't match our name, if so, try to rename it
907                                 $found = false;
908                                 foreach ($compareIndices as $ex_name => $ex_value) {
909                                         if($this->compareVarDefs($ex_value, $value, true)) {
910                                                 $found = $ex_name;
911                                                 break;
912                                         }
913                                 }
914                                 if ($found) {
915                                         $sql .=  "/*MISSNAMED INDEX IN DATABASE - $name - $ex_name */\n";
916                                         $rename = $this->renameIndexDefs($ex_value, $value, $tablename);
917                                         if($execute) {
918                                                 $this->query($rename, true, "Cannot rename index");
919                                         }
920                                         $sql .= is_array($rename)?join("\n", $rename). "\n":$rename."\n";
921
922                                 } else {
923                                         // ok we need this field lets create it
924                                         $sql .=  "/*MISSING INDEX IN DATABASE - $name -{$value['type']}  ROW */\n";
925                                         $sql .= $this->addIndexes($tablename,array($value), $execute) .  "\n";
926                                 }
927                                 $take_action = true;
928                                 $correctedIndexs[$name] = true;
929                         } elseif ( !$this->compareVarDefs($compareIndices[$name],$value) ) {
930                                 // fields are different lets alter it
931                                 $sql .= "/*INDEX MISMATCH WITH DATABASE - $name -  ROW ";
932                                 foreach ($compareIndices[$name] as $n1 => $t1) {
933                                         $sql .=  "<$n1>";
934                                         if ( $n1 == 'fields' )
935                                                 foreach($t1 as $rKey => $rValue)
936                                                         $sql .= "[$rKey] => '$rValue'  ";
937                                         else
938                                                 $sql .= " $t1 ";
939                                 }
940                                 $sql .= "*/\n";
941                                 $sql .= "/* VARDEF - $name -  ROW";
942                                 foreach ($value as $n1 => $t1) {
943                                         $sql .= "<$n1>";
944                                         if ( $n1 == 'fields' )
945                                                 foreach ($t1 as $rKey => $rValue)
946                                                         $sql .= "[$rKey] => '$rValue'  ";
947                                         else
948                                                 $sql .= " $t1 ";
949                                 }
950                                 $sql .= "*/\n";
951                                 $sql .= $this->modifyIndexes($tablename,array($value), $execute) .  "\n";
952                                 $take_action = true;
953                                 $correctedIndexs[$name] = true;
954                         }
955                 }
956
957                 return ($take_action === true) ? $sql : '';
958         }
959
960     /**
961      * Compares two vardefs
962      *
963      * @param  array  $fielddef1 This is from the database
964      * @param  array  $fielddef2 This is from the vardef
965      * @param bool $ignoreName Ignore name-only differences?
966      * @return bool   true if they match, false if they don't
967      */
968         public function compareVarDefs($fielddef1, $fielddef2, $ignoreName = false)
969         {
970                 foreach ( $fielddef1 as $key => $value ) {
971                         if ( $key == 'name' && ( strtolower($fielddef1[$key]) == strtolower($fielddef2[$key]) || $ignoreName) )
972                                 continue;
973                         if ( isset($fielddef2[$key]) && $fielddef1[$key] == $fielddef2[$key] )
974                                 continue;
975                         //Ignore len if its not set in the vardef
976                         if ($key == 'len' && empty($fielddef2[$key]))
977                                 continue;
978             // if the length in db is greather than the vardef, ignore it
979             if ($key == 'len' && ($fielddef1[$key] >= $fielddef2[$key])) {
980                 continue;
981             }
982                         return false;
983                 }
984
985                 return true;
986         }
987
988         /**
989          * Compare a field in two tables
990          * @deprecated
991          * @param  string $name   field name
992          * @param  string $table1
993          * @param  string $table2
994          * @return array  array with keys 'msg','table1','table2'
995          */
996         public function compareFieldInTables($name, $table1, $table2)
997         {
998                 $row1 = $this->describeField($name, $table1);
999                 $row2 = $this->describeField($name, $table2);
1000                 $returnArray = array(
1001                         'table1' => $row1,
1002                         'table2' => $row2,
1003                         'msg'    => 'error',
1004                         );
1005
1006                 $ignore_filter = array('Key'=>1);
1007                 if ($row1) {
1008                         if (!$row2) {
1009                                 // Exists on table1 but not table2
1010                                 $returnArray['msg'] = 'not_exists_table2';
1011                         }
1012                         else {
1013                                 if (sizeof($row1) != sizeof($row2)) {
1014                                         $returnArray['msg'] = 'no_match';
1015                                 }
1016                                 else {
1017                                         $returnArray['msg'] = 'match';
1018                                         foreach($row1 as $key => $value){
1019                                                 //ignore keys when checking we will check them when we do the index check
1020                                                 if( !isset($ignore_filter[$key]) && (!isset($row2[$key]) || $row1[$key] !== $row2[$key])){
1021                                                         $returnArray['msg'] = 'no_match';
1022                                                 }
1023                                         }
1024                                 }
1025                         }
1026                 }
1027                 else {
1028                         $returnArray['msg'] = 'not_exists_table1';
1029                 }
1030
1031                 return $returnArray;
1032         }
1033 //
1034 //    /**
1035 //     * Compare an index in two different tables
1036 //     * @deprecated
1037 //     * @param  string $name   index name
1038 //     * @param  string $table1
1039 //     * @param  string $table2
1040 //     * @return array  array with keys 'msg','table1','table2'
1041 //     */
1042 //    public function compareIndexInTables($name, $table1, $table2)
1043 //    {
1044 //        $row1 = $this->describeIndex($name, $table1);
1045 //        $row2 = $this->describeIndex($name, $table2);
1046 //        $returnArray = array(
1047 //            'table1' => $row1,
1048 //            'table2' => $row2,
1049 //            'msg'    => 'error',
1050 //            );
1051 //        $ignore_filter = array('Table'=>1, 'Seq_in_index'=>1,'Cardinality'=>1, 'Sub_part'=>1, 'Packed'=>1, 'Comment'=>1);
1052 //
1053 //        if ($row1) {
1054 //            if (!$row2) {
1055 //                //Exists on table1 but not table2
1056 //                $returnArray['msg'] = 'not_exists_table2';
1057 //            }
1058 //            else {
1059 //                if (sizeof($row1) != sizeof($row2)) {
1060 //                    $returnArray['msg'] = 'no_match';
1061 //                }
1062 //                else {
1063 //                    $returnArray['msg'] = 'match';
1064 //                    foreach ($row1 as $fname => $fvalue) {
1065 //                        if (!isset($row2[$fname])) {
1066 //                            $returnArray['msg'] = 'no_match';
1067 //                        }
1068 //                        if(!isset($ignore_filter[$fname]) && $row1[$fname] != $row2[$fname]){
1069 //                            $returnArray['msg'] = 'no_match';
1070 //                        }
1071 //                    }
1072 //                }
1073 //            }
1074 //        } else {
1075 //            $returnArray['msg'] = 'not_exists_table1';
1076 //        }
1077 //
1078 //        return $returnArray;
1079 //    }
1080
1081
1082         /**
1083          * Creates an index identified by name on the given fields.
1084          *
1085          * @param SugarBean $bean      SugarBean instance
1086          * @param array  $fieldDefs Field definitions, in vardef format
1087          * @param string $name      index name
1088          * @param bool   $unique    optional, true if we want to create an unique index
1089      * @return bool query result
1090      */
1091         public function createIndex(SugarBean $bean, $fieldDefs, $name, $unique = true)
1092         {
1093                 $sql = $this->createIndexSQL($bean, $fieldDefs, $name, $unique);
1094                 $tablename = $bean->getTableName();
1095                 $msg = "Error creating index $name on table: $tablename:";
1096                 return $this->query($sql,true,$msg);
1097         }
1098
1099         /**
1100          * returns a SQL query that creates the indices as defined in metadata
1101          * @param  array  $indices Assoc array with index definitions from vardefs
1102          * @param  string $table Focus table
1103          * @return array  Array of SQL queries to generate indices
1104          */
1105         public function getConstraintSql($indices, $table)
1106         {
1107                 if (!$this->isFieldArray($indices))
1108                         $indices = array($indices);
1109
1110                 $columns = array();
1111
1112                 foreach ($indices as $index) {
1113                         if(!empty($index['db']) && $index['db'] != $this->dbType)
1114                                 continue;
1115                         if (isset($index['source']) && $index['source'] != 'db')
1116                         continue;
1117
1118                         $sql = $this->add_drop_constraint($table, $index);
1119
1120                         if(!empty($sql)) {
1121                                 $columns[] = $sql;
1122                         }
1123                 }
1124
1125                 return $columns;
1126         }
1127
1128         /**
1129          * Adds a new indexes
1130          *
1131          * @param  string $tablename
1132          * @param  array  $indexes   indexes to add
1133          * @param  bool   $execute   true if we want to execute the returned sql statement
1134          * @return string SQL statement
1135          */
1136         public function addIndexes($tablename, $indexes, $execute = true)
1137         {
1138                 $alters = $this->getConstraintSql($indexes, $tablename);
1139                 if ($execute) {
1140                         foreach($alters as $sql) {
1141                                 $this->query($sql, true, "Error adding index: ");
1142                         }
1143                 }
1144                 if(!empty($alters)) {
1145                         $sql = join(";\n", $alters).";\n";
1146                 } else {
1147                         $sql = '';
1148                 }
1149                 return $sql;
1150         }
1151
1152         /**
1153          * Drops indexes
1154          *
1155          * @param  string $tablename
1156          * @param  array  $indexes   indexes to drop
1157          * @param  bool   $execute   true if we want to execute the returned sql statement
1158          * @return string SQL statement
1159          */
1160         public function dropIndexes($tablename, $indexes, $execute = true)
1161         {
1162                 $sqls = array();
1163                 foreach ($indexes as $index) {
1164                         $name =$index['name'];
1165                         $sqls[$name] = $this->add_drop_constraint($tablename,$index,true);
1166                 }
1167                 if (!empty($sqls) && $execute) {
1168                         foreach($sqls as $name => $sql) {
1169                                 unset(self::$index_descriptions[$tablename][$name]);
1170                                 $this->query($sql);
1171                         }
1172                 }
1173                 if(!empty($sqls)) {
1174                         return join(";\n",$sqls).";";
1175                 } else {
1176                         return '';
1177                 }
1178         }
1179
1180         /**
1181          * Modifies indexes
1182          *
1183          * @param  string $tablename
1184          * @param  array  $indexes   indexes to modify
1185          * @param  bool   $execute   true if we want to execute the returned sql statement
1186          * @return string SQL statement
1187          */
1188         public function modifyIndexes($tablename, $indexes, $execute = true)
1189         {
1190                 return $this->dropIndexes($tablename, $indexes, $execute)."\n".
1191                         $this->addIndexes($tablename, $indexes, $execute);
1192         }
1193
1194         /**
1195          * Adds a column to table identified by field def.
1196          *
1197          * @param string $tablename
1198          * @param array  $fieldDefs
1199      * @return bool query result
1200      */
1201         public function addColumn($tablename, $fieldDefs)
1202         {
1203                 $sql = $this->addColumnSQL($tablename, $fieldDefs);
1204                 if ($this->isFieldArray($fieldDefs)){
1205                         $columns = array();
1206                         foreach ($fieldDefs as $fieldDef)
1207                                 $columns[] = $fieldDef['name'];
1208                         $columns = implode(",", $columns);
1209                 }
1210                 else {
1211                         $columns = $fieldDefs['name'];
1212                 }
1213                 $msg = "Error adding column(s) $columns on table: $tablename:";
1214                 return $this->query($sql,true,$msg);
1215         }
1216
1217         /**
1218          * Alters old column identified by oldFieldDef to new fieldDef.
1219          *
1220          * @param string $tablename
1221          * @param array  $newFieldDef
1222          * @param bool   $ignoreRequired optional, true if we are ignoring this being a required field
1223      * @return bool query result
1224      */
1225         public function alterColumn($tablename, $newFieldDef, $ignoreRequired = false)
1226         {
1227                 $sql = $this->alterColumnSQL($tablename, $newFieldDef,$ignoreRequired);
1228                 if ($this->isFieldArray($newFieldDef)){
1229                         $columns = array();
1230                         foreach ($newFieldDef as $fieldDef) {
1231                                 $columns[] = $fieldDef['name'];
1232                         }
1233                         $columns = implode(",", $columns);
1234                 }
1235                 else {
1236                         $columns = $newFieldDef['name'];
1237                 }
1238
1239                 $msg = "Error altering column(s) $columns on table: $tablename:";
1240                 $res = $this->query($sql,true,$msg);
1241                 if($res) {
1242                         $this->getTableDescription($tablename, true); // reload table description after altering
1243                 }
1244                 return $res;
1245         }
1246
1247         /**
1248          * Drops the table associated with a bean
1249          *
1250          * @param SugarBean $bean SugarBean instance
1251      * @return bool query result
1252          */
1253         public function dropTable(SugarBean $bean)
1254         {
1255                 return $this->dropTableName($bean->getTableName());
1256         }
1257
1258         /**
1259          * Drops the table by name
1260          *
1261          * @param string $name Table name
1262      * @return bool query result
1263          */
1264         public function dropTableName($name)
1265         {
1266                 $sql = $this->dropTableNameSQL($name);
1267                 return $this->query($sql,true,"Error dropping table $name:");
1268         }
1269
1270     /**
1271      * Deletes a column identified by fieldDef.
1272      *
1273      * @param SugarBean $bean   SugarBean containing the field
1274      * @param array  $fieldDefs Vardef definition of the field
1275      * @return bool query result
1276      */
1277         public function deleteColumn(SugarBean $bean, $fieldDefs)
1278         {
1279                 $tablename = $bean->getTableName();
1280                 $sql = $this->dropColumnSQL($tablename, $fieldDefs);
1281                 $msg = "Error deleting column(s) on table: $tablename:";
1282                 return $this->query($sql,true,$msg);
1283         }
1284
1285     /**
1286      * Generate a set of Insert statements based on the bean given
1287      *
1288      * @deprecated
1289      *
1290      * @param  SugarBean $bean         the bean from which table we will generate insert stmts
1291      * @param  string $select_query the query which will give us the set of objects we want to place into our insert statement
1292      * @param  int    $start        the first row to query
1293      * @param  int    $count        the number of rows to query
1294      * @param  string $table        the table to query from
1295      * @param bool $is_related_query
1296      * @return string SQL insert statement
1297      */
1298         public function generateInsertSQL(SugarBean $bean, $select_query, $start, $count = -1, $table, $is_related_query = false)
1299         {
1300                 $this->log->info('call to DBManager::generateInsertSQL() is deprecated');
1301                 global $sugar_config;
1302
1303                 $rows_found = 0;
1304                 $count_query = $bean->create_list_count_query($select_query);
1305                 if(!empty($count_query))
1306                 {
1307                         // We have a count query.  Run it and get the results.
1308                         $result = $this->query($count_query, true, "Error running count query for $this->object_name List: ");
1309                         $assoc = $this->fetchByAssoc($result);
1310                         if(!empty($assoc['c']))
1311                         {
1312                                 $rows_found = $assoc['c'];
1313                         }
1314                 }
1315                 if($count == -1){
1316                         $count  = $sugar_config['list_max_entries_per_page'];
1317                 }
1318                 $next_offset = $start + $count;
1319
1320                 $result = $this->limitQuery($select_query, $start, $count);
1321                 // get basic insert
1322                 $sql = "INSERT INTO ".$table;
1323                 $custom_sql = "INSERT INTO ".$table."_cstm";
1324
1325                 // get field definitions
1326                 $fields = $bean->getFieldDefinitions();
1327                 $custom_fields = array();
1328
1329                 if($bean->hasCustomFields()){
1330                         foreach ($fields as $fieldDef){
1331                                 if($fieldDef['source'] == 'custom_fields'){
1332                                         $custom_fields[$fieldDef['name']] = $fieldDef['name'];
1333                                 }
1334                         }
1335                         if(!empty($custom_fields)){
1336                                 $custom_fields['id_c'] = 'id_c';
1337                                 $id_field = array('name' => 'id_c', 'custom_type' => 'id',);
1338                                 $fields[] = $id_field;
1339                         }
1340                 }
1341
1342                 // get column names and values
1343                 $row_array = array();
1344                 $columns = array();
1345                 $cstm_row_array = array();
1346                 $cstm_columns = array();
1347                 $built_columns = false;
1348                 while(($row = $this->fetchByAssoc($result)) != null)
1349                 {
1350                         $values = array();
1351                         $cstm_values = array();
1352                         if(!$is_related_query){
1353                                 foreach ($fields as $fieldDef)
1354                                 {
1355                                         if(isset($fieldDef['source']) && $fieldDef['source'] != 'db' && $fieldDef['source'] != 'custom_fields') continue;
1356                                         $val = $row[$fieldDef['name']];
1357
1358                                         //handle auto increment values here only need to do this on insert not create
1359                                         if ($fieldDef['name'] == 'deleted'){
1360                                                         $values['deleted'] = $val;
1361                                                         if(!$built_columns){
1362                                                         $columns[] = 'deleted';
1363                                                 }
1364                                         }
1365                                         else
1366                                         {
1367                                                 $type = $fieldDef['type'];
1368                                                 if(!empty($fieldDef['custom_type'])){
1369                                                         $type = $fieldDef['custom_type'];
1370                                                 }
1371                                                 // need to do some thing about types of values
1372                                                 if($this->dbType == 'mysql' && $val == '' && ($type == 'datetime' ||  $type == 'date' || $type == 'int' || $type == 'currency' || $type == 'decimal')){
1373                                                         if(!empty($custom_fields[$fieldDef['name']]))
1374                                                                 $cstm_values[$fieldDef['name']] = 'null';
1375                                                         else
1376                                                                 $values[$fieldDef['name']] = 'null';
1377                                                 }else{
1378                                                         if(isset($type) && $type=='int') {
1379                                                                 if(!empty($custom_fields[$fieldDef['name']]))
1380                                                                         $cstm_values[$fieldDef['name']] = $GLOBALS['db']->quote(from_html($val));
1381                                                                 else
1382                                                                         $values[$fieldDef['name']] = $GLOBALS['db']->quote(from_html($val));
1383                                                         } else {
1384                                                                 if(!empty($custom_fields[$fieldDef['name']]))
1385                                                                         $cstm_values[$fieldDef['name']] = "'".$GLOBALS['db']->quote(from_html($val))."'";
1386                                                                 else
1387                                                                         $values[$fieldDef['name']] = "'".$GLOBALS['db']->quote(from_html($val))."'";
1388                                                         }
1389                                                 }
1390                                                 if(!$built_columns){
1391                                                         if(!empty($custom_fields[$fieldDef['name']]))
1392                                                                 $cstm_columns[] = $fieldDef['name'];
1393                                                         else
1394                                                                 $columns[] = $fieldDef['name'];
1395                                                 }
1396                                         }
1397
1398                                 }
1399                         } else {
1400                         foreach ($row as $key=>$val)
1401                         {
1402                                         if($key != 'orc_row'){
1403                                                 $values[$key] = "'$val'";
1404                                                 if(!$built_columns){
1405                                                         $columns[] = $key;
1406                                                 }
1407                                         }
1408                         }
1409                         }
1410                         $built_columns = true;
1411                         if(!empty($values)){
1412                                 $row_array[] = $values;
1413                         }
1414                         if(!empty($cstm_values) && !empty($cstm_values['id_c']) && (strlen($cstm_values['id_c']) > 7)){
1415                                 $cstm_row_array[] = $cstm_values;
1416                         }
1417                 }
1418
1419                 //if (sizeof ($values) == 0) return ""; // no columns set
1420
1421                 // get the entire sql
1422                 $sql .= "(".implode(",", $columns).") ";
1423                 $sql .= "VALUES";
1424                 for($i = 0; $i < count($row_array); $i++){
1425                         $sql .= " (".implode(",", $row_array[$i]).")";
1426                         if($i < (count($row_array) - 1)){
1427                                 $sql .= ", ";
1428                         }
1429                 }
1430                 //custom
1431                 // get the entire sql
1432                 $custom_sql .= "(".implode(",", $cstm_columns).") ";
1433                 $custom_sql .= "VALUES";
1434
1435                 for($i = 0; $i < count($cstm_row_array); $i++){
1436                         $custom_sql .= " (".implode(",", $cstm_row_array[$i]).")";
1437                         if($i < (count($cstm_row_array) - 1)){
1438                                 $custom_sql .= ", ";
1439                         }
1440                 }
1441                 return array('data' => $sql, 'cstm_sql' => $custom_sql, /*'result_count' => $row_count, */ 'total_count' => $rows_found, 'next_offset' => $next_offset);
1442         }
1443
1444         /**
1445          * @deprecated
1446          * Disconnects all instances
1447          */
1448         public function disconnectAll()
1449         {
1450                 DBManagerFactory::disconnectAll();
1451         }
1452
1453         /**
1454          * This function sets the query threshold limit
1455          *
1456          * @param int $limit value of query threshold limit
1457          */
1458         public static function setQueryLimit($limit)
1459         {
1460                 //reset the queryCount
1461                 self::$queryCount = 0;
1462                 self::$queryLimit = $limit;
1463         }
1464
1465         /**
1466          * Returns the static queryCount value
1467          *
1468          * @return int value of the queryCount static variable
1469          */
1470         public static function getQueryCount()
1471         {
1472                 return self::$queryCount;
1473         }
1474
1475
1476         /**
1477          * Resets the queryCount value to 0
1478          *
1479          */
1480         public static function resetQueryCount()
1481         {
1482                 self::$queryCount = 0;
1483         }
1484
1485         /**
1486          * This function increments the global $sql_queries variable
1487          */
1488         public function countQuery()
1489         {
1490                 if (self::$queryLimit != 0 && ++self::$queryCount > self::$queryLimit
1491                         &&(empty($GLOBALS['current_user']) || !is_admin($GLOBALS['current_user']))) {
1492             require_once('include/resource/ResourceManager.php');
1493             $resourceManager = ResourceManager::getInstance();
1494             $resourceManager->notifyObservers('ERR_QUERY_LIMIT');
1495                 }
1496         }
1497
1498         /**
1499          * Pre-process string for quoting
1500          * @internal
1501          * @param string $string
1502      * @return string
1503      */
1504         protected function quoteInternal($string)
1505         {
1506                 return from_html($string);
1507         }
1508
1509         /**
1510          * Return string properly quoted with ''
1511          * @param string $string
1512          * @return string
1513          */
1514         public function quoted($string)
1515         {
1516                 return "'".$this->quote($string)."'";
1517         }
1518
1519         /**
1520      * Quote value according to type
1521      * Numerics aren't quoted
1522      * Dates are converted and quoted
1523      * Rest is just quoted
1524      * @param string $type
1525      * @param string $value
1526      * @return string Quoted value
1527      */
1528     public function quoteType($type, $value)
1529         {
1530             if($type == 'date') {
1531                 return $this->convert($this->quoted($value), "date");
1532             }
1533             if($type == 'time') {
1534                 return $this->convert($this->quoted($value), "time");
1535             }
1536         if(isset($this->type_class[$type]) &&  $this->type_class[$type] == "date") {
1537             return $this->convert($this->quoted($value), "datetime");
1538         }
1539         if($this->isNumericType($type)) {
1540             return 0+$value; // ensure it's numeric
1541         }
1542
1543         return $this->quoted($value);
1544         }
1545
1546     /**
1547      * Quote the strings of the passed in array
1548      *
1549      * The array must only contain strings
1550      *
1551      * @param array $array
1552      * @return array Quoted strings
1553      */
1554         public function arrayQuote(array &$array)
1555         {
1556                 foreach($array as &$val) {
1557                         $val = $this->quote($val);
1558                 }
1559                 return $array;
1560         }
1561
1562     /**
1563      * Frees out previous results
1564      *
1565      * @param resource|bool $result optional, pass if you want to free a single result instead of all results
1566      */
1567         protected function freeResult($result = false)
1568         {
1569                 if($result) {
1570                         $this->freeDbResult($result);
1571                 }
1572                 if($this->lastResult) {
1573                         $this->freeDbResult($this->lastResult);
1574                         $this->lastResult = null;
1575                 }
1576         }
1577
1578         /**
1579          * @abstract
1580          * Check if query has LIMIT clause
1581          * Relevant for now only for Mysql
1582          * @param string $sql
1583          * @return bool
1584          */
1585         protected function hasLimit($sql)
1586         {
1587             return false;
1588         }
1589
1590         /**
1591          * Runs a query and returns a single row containing single value
1592          *
1593          * @param  string   $sql        SQL Statement to execute
1594          * @param  bool     $dieOnError True if we want to call die if the query returns errors
1595          * @param  string   $msg        Message to log if error occurs
1596          * @return array    single value from the query
1597          */
1598         public function getOne($sql, $dieOnError = false, $msg = '')
1599         {
1600                 $this->log->info("Get One: |$sql|");
1601                 if(!$this->hasLimit($sql)) {
1602                     $queryresult = $this->limitQuery($sql, 0, 1, $dieOnError, $msg);
1603                 } else {
1604                     // support old code that passes LIMIT to sql
1605                     // works only for mysql, so do not rely on this
1606                     $queryresult = $this->query($sql, $dieOnError, $msg);
1607                 }
1608                 $this->checkError($msg.' Get One Failed:' . $sql, $dieOnError);
1609                 if (!$queryresult) return false;
1610                 $row = $this->fetchByAssoc($queryresult);
1611                 if(!empty($row)) {
1612                         return array_shift($row);
1613                 }
1614                 return false;
1615         }
1616
1617         /**
1618          * Runs a query and returns a single row
1619          *
1620          * @param  string   $sql        SQL Statement to execute
1621          * @param  bool     $dieOnError True if we want to call die if the query returns errors
1622          * @param  string   $msg        Message to log if error occurs
1623          * @param  bool     $suppress   Message to log if error occurs
1624          * @return array    single row from the query
1625          */
1626         public function fetchOne($sql, $dieOnError = false, $msg = '', $suppress = false)
1627         {
1628                 $this->log->info("Fetch One: |$sql|");
1629                 $this->checkConnection();
1630                 $queryresult = $this->query($sql, $dieOnError, $msg);
1631                 $this->checkError($msg.' Fetch One Failed:' . $sql, $dieOnError);
1632
1633                 if (!$queryresult) return false;
1634
1635                 $row = $this->fetchByAssoc($queryresult);
1636                 if ( !$row ) return false;
1637
1638                 $this->freeResult($queryresult);
1639                 return $row;
1640         }
1641
1642     /**
1643      * Returns the number of rows affected by the last query
1644      * @abstract
1645          * See also affected_rows capability, will return 0 unless the DB supports it
1646      * @param resource $result query result resource
1647      * @return int
1648      */
1649         public function getAffectedRowCount($result)
1650         {
1651                 return 0;
1652         }
1653
1654         /**
1655          * Returns the number of rows returned by the result
1656          *
1657          * This function can't be reliably implemented on most DB, do not use it.
1658          * @abstract
1659          * @deprecated
1660          * @param  resource $result
1661          * @return int
1662          */
1663         public function getRowCount($result)
1664         {
1665             return 0;
1666         }
1667
1668         /**
1669      * Get table description
1670      * @param string $tablename
1671      * @param bool $reload true means load from DB, false allows using cache
1672      * @return array Vardef-format table description
1673      *
1674      */
1675         public function getTableDescription($tablename, $reload = false)
1676         {
1677                 if($reload || empty(self::$table_descriptions[$tablename])) {
1678                         self::$table_descriptions[$tablename] = $this->get_columns($tablename);
1679                 }
1680                 return self::$table_descriptions[$tablename];
1681         }
1682
1683         /**
1684          * Returns the field description for a given field in table
1685          *
1686          * @param  string $name
1687          * @param  string $tablename
1688          * @return array
1689          */
1690         protected function describeField($name, $tablename)
1691         {
1692                 $table = $this->getTableDescription($tablename);
1693                 if(!empty($table) && isset($table[$name]))
1694                         return  $table[$name];
1695
1696                 $table = $this->getTableDescription($tablename, true);
1697
1698                 if(isset($table[$name]))
1699                 return $table[$name];
1700
1701                 return array();
1702         }
1703
1704         /**
1705          * Returns the index description for a given index in table
1706          *
1707          * @param  string $name
1708          * @param  string $tablename
1709          * @return array
1710          */
1711         protected function describeIndex($name, $tablename)
1712         {
1713                 if(isset(self::$index_descriptions[$tablename]) && isset(self::$index_descriptions[$tablename]) && isset(self::$index_descriptions[$tablename][$name])){
1714                         return  self::$index_descriptions[$tablename][$name];
1715                 }
1716
1717                 self::$index_descriptions[$tablename] = $this->get_indices($tablename);
1718
1719                 if(isset(self::$index_descriptions[$tablename][$name])){
1720                         return  self::$index_descriptions[$tablename][$name];
1721                 }
1722
1723                 return array();
1724         }
1725
1726     /**
1727      * Truncates a string to a given length
1728      *
1729      * @param string $string
1730      * @param int    $len    length to trim to
1731      * @return string
1732      *
1733      */
1734         public function truncate($string, $len)
1735         {
1736                 if ( is_numeric($len) && $len > 0)
1737                 {
1738                         $string = mb_substr($string,0,(int) $len, "UTF-8");
1739                 }
1740                 return $string;
1741         }
1742
1743     /**
1744      * Returns the database string needed for concatinating multiple database strings together
1745      *
1746      * @param string $table table name of the database fields to concat
1747      * @param array $fields fields in the table to concat together
1748      * @param string $space Separator between strings, default is single space
1749      * @return string
1750      */
1751         public function concat($table, array $fields, $space = ' ')
1752         {
1753                 if(empty($fields)) return '';
1754                 $elems = array();
1755                 $space = $this->quoted($space);
1756                 foreach ( $fields as $field ) {
1757                         if(!empty($elems)) $elems[] = $space;
1758                         $elems[] = $this->convert("$table.$field", 'IFNULL', array("''"));
1759                 }
1760                 $first = array_shift($elems);
1761                 return "LTRIM(RTRIM(".$this->convert($first, 'CONCAT', $elems)."))";
1762         }
1763
1764         /**
1765          * Given a sql stmt attempt to parse it into the sql and the tokens. Then return the index of this prepared statement
1766          * Tokens can come in the following forms:
1767          * ? - a scalar which will be quoted
1768          * ! - a literal which will not be quoted
1769          * & - binary data to read from a file
1770          *
1771          * @param  string       $sql        The sql to parse
1772          * @return int index of the prepared statement to be used with execute
1773          */
1774         public function prepareQuery($sql)
1775         {
1776                 //parse out the tokens
1777                 $tokens = preg_split('/((?<!\\\)[&?!])/', $sql, -1, PREG_SPLIT_DELIM_CAPTURE);
1778
1779                 //maintain a count of the actual tokens for quick reference in execute
1780                 $count = 0;
1781
1782                 $sqlStr = '';
1783                 foreach ($tokens as $key => $val) {
1784                         switch ($val) {
1785                                 case '?' :
1786                                 case '!' :
1787                                 case '&' :
1788                                         $count++;
1789                                         $sqlStr .= '?';
1790                                         break;
1791
1792                                 default :
1793                                         //escape any special characters
1794                                         $tokens[$key] = preg_replace('/\\\([&?!])/', "\\1", $val);
1795                                         $sqlStr .= $tokens[$key];
1796                                         break;
1797                         } // switch
1798                 } // foreach
1799
1800                 $this->preparedTokens[] = array('tokens' => $tokens, 'tokenCount' => $count, 'sqlString' => $sqlStr);
1801                 end($this->preparedTokens);
1802                 return key($this->preparedTokens);
1803         }
1804
1805         /**
1806          * Takes a prepared stmt index and the data to replace and creates the query and runs it.
1807          *
1808          * @param  int          $stmt       The index of the prepared statement from preparedTokens
1809          * @param  array    $data               The array of data to replace the tokens with.
1810          * @return resource result set or false on error
1811          */
1812         public function executePreparedQuery($stmt, $data = array())
1813         {
1814                 if(!empty($this->preparedTokens[$stmt])){
1815                         if(!is_array($data)){
1816                                 $data = array($data);
1817                         }
1818
1819                         $pTokens = $this->preparedTokens[$stmt];
1820
1821                         //ensure that the number of data elements matches the number of replacement tokens
1822                         //we found in prepare().
1823                         if(count($data) != $pTokens['tokenCount']){
1824                                 //error the data count did not match the token count
1825                                 return false;
1826                         }
1827
1828                         $query = '';
1829                         $dataIndex = 0;
1830                         $tokens = $pTokens['tokens'];
1831                         foreach ($tokens as $val) {
1832                                 switch ($val) {
1833                                         case '?':
1834                                                 $query .= $this->quote($data[$dataIndex++]);
1835                                                 break;
1836                                         case '&':
1837                                                 $filename = $data[$dataIndex++];
1838                                                 $query .= file_get_contents($filename);
1839                                                 break;
1840                                         case '!':
1841                                                 $query .= $data[$dataIndex++];
1842                                                 break;
1843                                         default:
1844                                                 $query .= $val;
1845                                                 break;
1846                                 }//switch
1847                         }//foreach
1848                         return $this->query($query);
1849                 }else{
1850                         return false;
1851                 }
1852         }
1853
1854         /**
1855          * Run both prepare and execute without the client having to run both individually.
1856          *
1857          * @param  string       $sql        The sql to parse
1858          * @param  array    $data               The array of data to replace the tokens with.
1859          * @return resource result set or false on error
1860          */
1861         public function pQuery($sql, $data = array())
1862         {
1863                 $stmt = $this->prepareQuery($sql);
1864                 return $this->executePreparedQuery($stmt, $data);
1865         }
1866
1867 /********************** SQL FUNCTIONS ****************************/
1868     /**
1869      * Generates sql for create table statement for a bean.
1870      *
1871      * NOTE: does not handle out-of-table constraints, use createConstraintSQL for that
1872      * @param SugarBean $bean SugarBean instance
1873      * @return string SQL Create Table statement
1874      */
1875         public function createTableSQL(SugarBean $bean)
1876         {
1877                 $tablename = $bean->getTableName();
1878                 $fieldDefs = $bean->getFieldDefinitions();
1879                 $indices = $bean->getIndices();
1880                 return $this->createTableSQLParams($tablename, $fieldDefs, $indices);
1881         }
1882
1883         /**
1884          * Generates SQL for insert statement.
1885          *
1886          * @param  SugarBean $bean SugarBean instance
1887          * @return string SQL Create Table statement
1888          */
1889         public function insertSQL(SugarBean $bean)
1890         {
1891                 // get column names and values
1892                 $sql = $this->insertParams($bean->getTableName(), $bean->getFieldDefinitions(), get_object_vars($bean),
1893                         isset($bean->field_name_map)?$bean->field_name_map:null, false);
1894                 return $sql;
1895         }
1896
1897         /**
1898          * Generates SQL for update statement.
1899          *
1900          * @param  SugarBean $bean SugarBean instance
1901          * @param  array  $where Optional, where conditions in an array
1902          * @return string SQL Create Table statement
1903          */
1904         public function updateSQL(SugarBean $bean, array $where = array())
1905         {
1906                 $primaryField = $bean->getPrimaryFieldDefinition();
1907                 $columns = array();
1908         $fields = $bean->getFieldDefinitions();
1909                 // get column names and values
1910                 foreach ($fields as $field => $fieldDef) {
1911                         if (isset($fieldDef['source']) && $fieldDef['source'] != 'db')  continue;
1912                         // Do not write out the id field on the update statement.
1913                 // We are not allowed to change ids.
1914                 if ($fieldDef['name'] == $primaryField['name']) continue;
1915
1916                 // If the field is an auto_increment field, then we shouldn't be setting it.  This was added
1917                 // specially for Bugs and Cases which have a number associated with them.
1918                 if (!empty($bean->field_name_map[$field]['auto_increment'])) continue;
1919
1920                 //custom fields handle their save separately
1921                 if(isset($bean->field_name_map) && !empty($bean->field_name_map[$field]['custom_type']))  continue;
1922
1923                 // no need to clear deleted since we only update not deleted records anyway
1924                 if($fieldDef['name'] == 'deleted' && empty($bean->deleted)) continue;
1925
1926                 if(isset($bean->$field)) {
1927                         $val = from_html($bean->$field);
1928                 } else {
1929                         continue;
1930                 }
1931
1932                 if(!empty($fieldDef['type']) && $fieldDef['type'] == 'bool'){
1933                         $val = $bean->getFieldValue($field);
1934                 }
1935
1936                 if(strlen($val) == 0) {
1937                         if(isset($fieldDef['default']) && strlen($fieldDef['default']) > 0) {
1938                                 $val = $fieldDef['default'];
1939                         } else {
1940                                 $val = null;
1941                         }
1942                 }
1943
1944                 if(!empty($val) && !empty($fieldDef['len']) && strlen($val) > $fieldDef['len']) {
1945                             $val = $this->truncate($val, $fieldDef['len']);
1946                         }
1947
1948                 if(!is_null($val) || !empty($fieldDef['required'])) {
1949                         $columns[] = "{$fieldDef['name']}=".$this->massageValue($val, $fieldDef);
1950                 } elseif($this->isNullable($fieldDef)) {
1951                         $columns[] = "{$fieldDef['name']}=NULL";
1952                 } else {
1953                     $columns[] = "{$fieldDef['name']}=".$this->emptyValue($fieldDef['type']);
1954                 }
1955                 }
1956
1957                 if ( sizeof($columns) == 0 )
1958                         return ""; // no columns set
1959
1960                 // build where clause
1961                 $where = $this->getWhereClause($bean, $this->updateWhereArray($bean, $where));
1962                 if(isset($fields['deleted'])) {
1963                     $where .= " AND deleted=0";
1964                 }
1965
1966                 return "UPDATE ".$bean->getTableName()."
1967                                         SET ".implode(",", $columns)."
1968                                         $where";
1969         }
1970
1971         /**
1972          * This method returns a where array so that it has id entry if
1973          * where is not an array or is empty
1974          *
1975          * @param  SugarBean $bean SugarBean instance
1976          * @param  array  $where Optional, where conditions in an array
1977          * @return array
1978          */
1979         protected function updateWhereArray(SugarBean $bean, array $where = array())
1980         {
1981                 if (count($where) == 0) {
1982                         $fieldDef = $bean->getPrimaryFieldDefinition();
1983                         $primaryColumn = $fieldDef['name'];
1984
1985                         $val = $bean->getFieldValue($fieldDef['name']);
1986                         if ($val != FALSE){
1987                                 $where[$primaryColumn] = $val;
1988                         }
1989                 }
1990
1991                 return $where;
1992         }
1993
1994         /**
1995          * Returns a where clause without the 'where' key word
1996          *
1997          * The clause returned does not have an 'and' at the beginning and the columns
1998          * are joined by 'and'.
1999          *
2000          * @param  string $table table name
2001          * @param  array  $whereArray Optional, where conditions in an array
2002          * @return string
2003          */
2004         protected function getColumnWhereClause($table, array $whereArray = array())
2005         {
2006                 $where = array();
2007                 foreach ($whereArray as $name => $val) {
2008                         $op = "=";
2009                         if (is_array($val)) {
2010                                 $op = "IN";
2011                                 $temp = array();
2012                                 foreach ($val as $tval){
2013                                         $temp[] = $this->quoted($tval);
2014                                 }
2015                                 $val = implode(",", $temp);
2016                                 $val = "($val)";
2017                         } else {
2018                                 $val = $this->quoted($val);
2019                         }
2020
2021                         $where[] = " $table.$name $op $val";
2022                 }
2023
2024                 if (!empty($where))
2025                         return implode(" AND ", $where);
2026
2027                 return '';
2028         }
2029
2030         /**
2031          * This method returns a complete where clause built from the
2032          * where values specified.
2033          *
2034          * @param  SugarBean $bean SugarBean that describes the table
2035          * @param  array  $whereArray Optional, where conditions in an array
2036          * @return string
2037          */
2038         protected function getWhereClause(SugarBean $bean, array $whereArray=array())
2039         {
2040             return " WHERE " . $this->getColumnWhereClause($bean->getTableName(), $whereArray);
2041         }
2042
2043         /**
2044          * Outputs a correct string for the sql statement according to value
2045          *
2046          * @param  mixed $val
2047          * @param  array $fieldDef field definition
2048          * @return mixed
2049          */
2050         public function massageValue($val, $fieldDef)
2051         {
2052                 $type = $this->getFieldType($fieldDef);
2053
2054                 if(isset($this->type_class[$type])) {
2055                         // handle some known types
2056                         switch($this->type_class[$type]) {
2057                                 case 'bool':
2058                                 case 'int':
2059                                         if (!empty($fieldDef['required']) && $val == ''){
2060                                                 if (isset($fieldDef['default'])){
2061                                                         return $fieldDef['default'];
2062                                                 }
2063                                                 return 0;
2064                                         }
2065                                         return intval($val);
2066                 case 'bigint' :
2067                     $val = (float)$val;
2068                                         if (!empty($fieldDef['required']) && $val == false){
2069                                                 if (isset($fieldDef['default'])){
2070                                                         return $fieldDef['default'];
2071                                                 }
2072                                                 return 0;
2073                                         }
2074                     return $val;
2075                                 case 'float':
2076                                         if (!empty($fieldDef['required'])  && $val == ''){
2077                                                 if (isset($fieldDef['default'])){
2078                                                         return $fieldDef['default'];
2079                                                 }
2080                                                 return 0;
2081                                         }
2082                                         return floatval($val);
2083                                 case 'time':
2084                                 case 'date':
2085                                         // empty date can't be '', so convert it to either NULL or empty date value
2086                                         if($val == '') {
2087                                                 if (!empty($fieldDef['required'])) {
2088                                                         if (isset($fieldDef['default'])) {
2089                                                                 return $fieldDef['default'];
2090                                                         }
2091                                                         return $this->emptyValue($type);
2092                                                 }
2093                                                 return "NULL";
2094                                         }
2095                                         break;
2096                         }
2097                 } else {
2098                     if(!empty($val) && !empty($fieldDef['len']) && strlen($val) > $fieldDef['len']) {
2099                             $val = $this->truncate($val, $fieldDef['len']);
2100                         }
2101                 }
2102
2103                 if ( is_null($val) ) {
2104                         if(!empty($fieldDef['required'])) {
2105                                 if (isset($fieldDef['default'])){
2106                                         return $fieldDef['default'];
2107                                 }
2108                                 return $this->emptyValue($type);
2109                         } else {
2110                                 return "NULL";
2111                         }
2112                 }
2113         if($type == "datetimecombo") {
2114             $type = "datetime";
2115         }
2116                 return $this->convert($this->quoted($val), $type);
2117         }
2118
2119         /**
2120          * Massages the field defintions to fill in anything else the DB backend may add
2121          *
2122          * @param  array  $fieldDef
2123          * @param  string $tablename
2124          * @return array
2125          */
2126         public function massageFieldDef(&$fieldDef, $tablename)
2127         {
2128                 if ( !isset($fieldDef['dbType']) ) {
2129                         if ( isset($fieldDef['dbtype']) )
2130                                 $fieldDef['dbType'] = $fieldDef['dbtype'];
2131                         else
2132                                 $fieldDef['dbType'] = $fieldDef['type'];
2133                 }
2134                 $type = $this->getColumnType($fieldDef['dbType'],$fieldDef['name'],$tablename);
2135                 $matches = array();
2136         // len can be a number or a string like 'max', for example, nvarchar(max)
2137         preg_match_all('/(\w+)(?:\(([0-9]+,?[0-9]*|\w+)\)|)/i', $type, $matches);
2138                 if ( isset($matches[1][0]) )
2139                         $fieldDef['type'] = $matches[1][0];
2140                 if ( isset($matches[2][0]) && empty($fieldDef['len']) )
2141                         $fieldDef['len'] = $matches[2][0];
2142                 if ( !empty($fieldDef['precision']) && is_numeric($fieldDef['precision']) && !strstr($fieldDef['len'],',') )
2143                         $fieldDef['len'] .= ",{$fieldDef['precision']}";
2144                 if (!empty($fieldDef['required']) || ($fieldDef['name'] == 'id' && !isset($fieldDef['required'])) ) {
2145                         $fieldDef['required'] = 'true';
2146                 }
2147         }
2148
2149         /**
2150          * Take an SQL statement and produce a list of fields used in that select
2151          * @param string $selectStatement
2152          * @return array
2153          */
2154         public function getSelectFieldsFromQuery($selectStatement)
2155         {
2156                 $selectStatement = trim($selectStatement);
2157                 if (strtoupper(substr($selectStatement, 0, 6)) == "SELECT")
2158                         $selectStatement = trim(substr($selectStatement, 6));
2159
2160                 //Due to sql functions existing in many selects, we can't use php explode
2161                 $fields = array();
2162                 $level = 0;
2163                 $selectField = "";
2164                 $strLen = strlen($selectStatement);
2165                 for($i = 0; $i < $strLen; $i++)
2166                 {
2167                         $char = $selectStatement[$i];
2168
2169                         if ($char == "," && $level == 0)
2170                         {
2171                                 $field = $this->getFieldNameFromSelect(trim($selectField));
2172                                 $fields[$field] = $selectField;
2173                                 $selectField = "";
2174                         }
2175                         else if ($char == "("){
2176                                 $level++;
2177                                 $selectField .= $char;
2178                         }
2179                         else if($char == ")"){
2180                                 $level--;
2181                                 $selectField .= $char;
2182
2183
2184                         }else{
2185                                 $selectField .= $char;
2186                         }
2187
2188                 }
2189                 $fields[$this->getFieldNameFromSelect($selectField)] = $selectField;
2190                 return $fields;
2191         }
2192
2193         /**
2194          * returns the field name used in a select
2195          * @param string $string SELECT query
2196      * @return string
2197      */
2198         protected function getFieldNameFromSelect($string)
2199         {
2200                 if(strncasecmp($string, "DISTINCT ", 9) == 0) {
2201                         $string = substr($string, 9);
2202                 }
2203                 if (stripos($string, " as ") !== false)
2204                         //"as" used for an alias
2205                         return trim(substr($string, strripos($string, " as ") + 4));
2206                 else if (strrpos($string, " ") != 0)
2207                         //Space used as a delimiter for an alias
2208                         return trim(substr($string, strrpos($string, " ")));
2209                 else if (strpos($string, ".") !== false)
2210                         //No alias, but a table.field format was used
2211                         return substr($string, strpos($string, ".") + 1);
2212                 else
2213                         //Give up and assume the whole thing is the field name
2214                         return $string;
2215         }
2216
2217         /**
2218          * Generates SQL for delete statement identified by id.
2219          *
2220          * @param  SugarBean $bean SugarBean instance
2221          * @param  array  $where where conditions in an array
2222          * @return string SQL Update Statement
2223          */
2224         public function deleteSQL(SugarBean $bean, array $where)
2225         {
2226                 $where = $this->getWhereClause($bean, $this->updateWhereArray($bean, $where));
2227                 return "UPDATE ".$bean->getTableName()." SET deleted=1 $where";
2228         }
2229
2230     /**
2231      * Generates SQL for select statement for any bean identified by id.
2232      *
2233      * @param  SugarBean $bean SugarBean instance
2234      * @param  array  $where where conditions in an array
2235      * @return string SQL Select Statement
2236      */
2237         public function retrieveSQL(SugarBean $bean, array $where)
2238         {
2239                 $where = $this->getWhereClause($bean, $this->updateWhereArray($bean, $where));
2240                 return "SELECT * FROM ".$bean->getTableName()." $where AND deleted=0";
2241         }
2242
2243     /**
2244      * This method implements a generic sql for a collection of beans.
2245      *
2246      * Currently, this function does not support outer joins.
2247      *
2248      * @param array $beans Array of values returned by get_class method as the keys and a bean as
2249      *      the value for that key. These beans will be joined in the sql by the key
2250      *      attribute of field defs.
2251      * @param  array $cols Optional, columns to be returned with the keys as names of bean
2252      *      as identified by get_class of bean. Values of this array is the array of fieldDefs
2253      *      to be returned for a bean. If an empty array is passed, all columns are selected.
2254      * @param  array $whereClause Optional, values with the keys as names of bean as identified
2255      *      by get_class of bean. Each value at the first level is an array of values for that
2256      *      bean identified by name of fields. If we want to pass multiple values for a name,
2257      *      pass it as an array. If where is not passed, all the rows will be returned.
2258      *
2259      * @return string SQL Select Statement
2260      */
2261         public function retrieveViewSQL(array $beans, array $cols = array(), array $whereClause = array())
2262         {
2263                 $relations = array(); // stores relations between tables as they are discovered
2264                 $where = $select = array();
2265                 foreach ($beans as $beanID => $bean) {
2266                         $tableName = $bean->getTableName();
2267                         $beanTables[$beanID] = $tableName;
2268
2269                         $table = $beanID;
2270                         $tables[$table] = $tableName;
2271                         $aliases[$tableName][] = $table;
2272
2273                         // build part of select for this table
2274                         if (is_array($cols[$beanID]))
2275                                 foreach ($cols[$beanID] as $def) $select[] = $table.".".$def['name'];
2276
2277                         // build part of where clause
2278                         if (is_array($whereClause[$beanID])){
2279                                 $where[] = $this->getColumnWhereClause($table, $whereClause[$beanID]);
2280                         }
2281                         // initialize so that it can be used properly in form clause generation
2282                         $table_used_in_from[$table] = false;
2283
2284                         $indices = $bean->getIndices();
2285                         foreach ($indices as $index){
2286                                 if ($index['type'] == 'foreign') {
2287                                         $relationship[$table][] = array('foreignTable'=> $index['foreignTable']
2288                                                                                                 ,'foreignColumn'=>$index['foreignField']
2289                                                                                                 ,'localColumn'=> $index['fields']
2290                                                                                                 );
2291                                 }
2292                         }
2293                         $where[] = " $table.deleted = 0";
2294                 }
2295
2296                 // join these clauses
2297                 $select = !empty($select) ? implode(",", $select) : "*";
2298                 $where = implode(" AND ", $where);
2299
2300                 // generate the from clause. Use relations array to generate outer joins
2301                 // all the rest of the tables will be used as a simple from
2302                 // relations table define relations between table1 and table2 through column on table 1
2303                 // table2 is assumed to joining through primary key called id
2304                 $separator = "";
2305                 $from = ''; $table_used_in_from = array();
2306                 foreach ($relations as $table1 => $rightsidearray){
2307                         if ($table_used_in_from[$table1]) continue; // table has been joined
2308
2309                         $from .= $separator." ".$table1;
2310                         $table_used_in_from[$table1] = true;
2311                         foreach ($rightsidearray as $tablearray){
2312                                 $table2 = $tablearray['foreignTable']; // get foreign table
2313                                 $tableAlias = $aliases[$table2]; // get a list of aliases for this table
2314                                 foreach ($tableAlias as $table2) {
2315                                         //choose first alias that does not match
2316                                         // we are doing this because of self joins.
2317                                         // in case of self joins, the same table will have many aliases.
2318                                         if ($table2 != $table1) break;
2319                                 }
2320
2321                                 $col = $tablearray['foreingColumn'];
2322                                 $name = $tablearray['localColumn'];
2323                                 $from .= " LEFT JOIN $table on ($table1.$name = $table2.$col)";
2324                                 $table_used_in_from[$table2] = true;
2325                         }
2326                         $separator = ",";
2327                 }
2328
2329                 return "SELECT $select FROM $from WHERE $where";
2330         }
2331
2332         /**
2333          * Generates SQL for create index statement for a bean.
2334          *
2335          * @param  SugarBean $bean SugarBean instance
2336          * @param  array  $fields fields used in the index
2337          * @param  string $name index name
2338          * @param  bool   $unique Optional, set to true if this is an unique index
2339          * @return string SQL Select Statement
2340          */
2341         public function createIndexSQL(SugarBean $bean, array $fields, $name, $unique = true)
2342         {
2343                 $unique = ($unique) ? "unique" : "";
2344                 $tablename = $bean->getTableName();
2345                 $columns = array();
2346                 // get column names
2347                 foreach ($fields as $fieldDef)
2348                         $columns[] = $fieldDef['name'];
2349
2350                 if (empty($columns))
2351                         return "";
2352
2353                 $columns = implode(",", $columns);
2354
2355                 return "CREATE $unique INDEX $name ON $tablename ($columns)";
2356         }
2357
2358         /**
2359          * Returns the type of the variable in the field
2360          *
2361          * @param  array $fieldDef Vardef-format field def
2362          * @return string
2363          */
2364         public function getFieldType($fieldDef)
2365         {
2366                 // get the type for db type. if that is not set,
2367                 // get it from type. This is done so that
2368                 // we do not have change a lot of existing code
2369                 // and add dbtype where type is being used for some special
2370                 // purposes like referring to foreign table etc.
2371                 if(!empty($fieldDef['dbType']))
2372                         return  $fieldDef['dbType'];
2373                 if(!empty($fieldDef['dbtype']))
2374                         return  $fieldDef['dbtype'];
2375                 if (!empty($fieldDef['type']))
2376                         return  $fieldDef['type'];
2377                 if (!empty($fieldDef['Type']))
2378                         return  $fieldDef['Type'];
2379                 if (!empty($fieldDef['data_type']))
2380                         return  $fieldDef['data_type'];
2381
2382                 return null;
2383         }
2384
2385     /**
2386      * retrieves the different components from the passed column type as it is used in the type mapping and vardefs
2387      * type format: <baseType>[(<len>[,<scale>])]
2388      * @param string $type Column type
2389      * @return array|bool array containing the different components of the passed in type or false in case the type contains illegal characters
2390      */
2391     public function getTypeParts($type) {
2392         if(preg_match("((?'type'\w+)\s*(?'arg'\((?'len'\w+)\s*(,\s*(?'scale'\d+))*\))*)", $type, $matches))
2393         {
2394             $return = array();  // Not returning matches array as such as we don't want to expose the regex make up on the interface
2395             $return['baseType'] = $matches['type'];
2396             if( isset($matches['arg'])) {
2397                 $return['arg'] = $matches['arg'];
2398             }
2399             if( isset($matches['len'])) {
2400                 $return['len'] = $matches['len'];
2401             }
2402             if( isset($matches['scale'])) {
2403                 $return['scale'] = $matches['scale'];
2404             }
2405             return $return;
2406         } else {
2407             return false;
2408         }
2409     }
2410
2411         /**
2412          * Returns the defintion for a single column
2413          *
2414          * @param  array  $fieldDef Vardef-format field def
2415          * @param  bool   $ignoreRequired  Optional, true if we should ignore this being a required field
2416          * @param  string $table           Optional, table name
2417          * @param  bool   $return_as_array Optional, true if we should return the result as an array instead of sql
2418          * @return string or array if $return_as_array is true
2419          */
2420         protected function oneColumnSQLRep($fieldDef, $ignoreRequired = false, $table = '', $return_as_array = false)
2421         {
2422                 $name = $fieldDef['name'];
2423                 $type = $this->getFieldType($fieldDef);
2424         $colType = $this->getColumnType($type);
2425
2426         if($parts = $this->getTypeParts($colType))
2427         {
2428             $colBaseType = $parts['baseType'];
2429             $defLen =  isset($parts['len']) ? $parts['len'] : '255'; // Use the mappings length (precision) as default if it exists
2430         }
2431
2432         if(!empty($fieldDef['len'])) {
2433             if (in_array($colBaseType, array( 'nvarchar', 'nchar', 'varchar', 'varchar2', 'char',
2434                                           'clob', 'blob', 'text'))) {
2435                     $colType = "$colBaseType(${fieldDef['len']})";
2436             } elseif(($colBaseType == 'decimal' || $colBaseType == 'float')){
2437                   if(!empty($fieldDef['precision']) && is_numeric($fieldDef['precision']))
2438                       if(strpos($fieldDef['len'],',') === false){
2439                           $colType = $colBaseType . "(".$fieldDef['len'].",".$fieldDef['precision'].")";
2440                       }else{
2441                           $colType = $colBaseType . "(".$fieldDef['len'].")";
2442                       }
2443                   else
2444                           $colType = $colBaseType . "(".$fieldDef['len'].")";
2445               }
2446         } else {
2447             if (in_array($colBaseType, array( 'nvarchar', 'nchar', 'varchar', 'varchar2', 'char'))) {
2448                 $colType = "$colBaseType($defLen)";
2449             }
2450         }
2451
2452         $default = '';
2453                 if (isset($fieldDef['default']) && strlen($fieldDef['default']) > 0)
2454                         $default = " DEFAULT ".$this->quoted($fieldDef['default']);
2455                 elseif (!isset($fieldDef['default']) && $type == 'bool')
2456                         $default = " DEFAULT 0 ";
2457
2458                 $auto_increment = '';
2459                 if(!empty($fieldDef['auto_increment']) && $fieldDef['auto_increment'])
2460                         $auto_increment = $this->setAutoIncrement($table , $fieldDef['name']);
2461
2462                 $required = 'NULL';  // MySQL defaults to NULL, SQL Server defaults to NOT NULL -- must specify
2463                 //Starting in 6.0, only ID and auto_increment fields will be NOT NULL in the DB.
2464                 if ((empty($fieldDef['isnull']) || strtolower($fieldDef['isnull']) == 'false') &&
2465                         (!empty($auto_increment) || $name == 'id' || ($fieldDef['type'] == 'id' && !empty($fieldDef['required'])))) {
2466                         $required =  "NOT NULL";
2467                 }
2468                 // If the field is marked both required & isnull=>false - alwqys make it not null
2469                 // Use this to ensure primary key fields never defined as null
2470                 if(isset($fieldDef['isnull']) && (strtolower($fieldDef['isnull']) == 'false' || $fieldDef['isnull'] === false)
2471                         && !empty($fieldDef['required'])) {
2472                         $required =  "NOT NULL";
2473                 }
2474                 if ($ignoreRequired)
2475                         $required = "";
2476
2477                 if ( $return_as_array ) {
2478                         return array(
2479                                 'name' => $name,
2480                                 'colType' => $colType,
2481                 'colBaseType' => $colBaseType,  // Adding base type for easier processing in derived classes
2482                                 'default' => $default,
2483                                 'required' => $required,
2484                                 'auto_increment' => $auto_increment,
2485                                 'full' => "$name $colType $default $required $auto_increment",
2486                                 );
2487                 } else {
2488                         return "$name $colType $default $required $auto_increment";
2489                 }
2490         }
2491
2492         /**
2493          * Returns SQL defintions for all columns in a table
2494          *
2495          * @param  array  $fieldDefs  Vardef-format field def
2496          * @param  bool   $ignoreRequired Optional, true if we should ignor this being a required field
2497          * @param  string $tablename      Optional, table name
2498          * @return string SQL column definitions
2499          */
2500         protected function columnSQLRep($fieldDefs, $ignoreRequired = false, $tablename)
2501         {
2502                 $columns = array();
2503
2504                 if ($this->isFieldArray($fieldDefs)) {
2505                         foreach ($fieldDefs as $fieldDef) {
2506                                 if(!isset($fieldDef['source']) || $fieldDef['source'] == 'db') {
2507                                         $columns[] = $this->oneColumnSQLRep($fieldDef,false, $tablename);
2508                                 }
2509                         }
2510                         $columns = implode(",", $columns);
2511                 }
2512                 else {
2513                         $columns = $this->oneColumnSQLRep($fieldDefs,$ignoreRequired, $tablename);
2514                 }
2515
2516                 return $columns;
2517         }
2518
2519         /**
2520          * Returns the next value for an auto increment
2521          * @abstract
2522          * @param  string $table Table name
2523          * @param  string $field_name Field name
2524          * @return string
2525          */
2526         public function getAutoIncrement($table, $field_name)
2527         {
2528                 return "";
2529         }
2530
2531         /**
2532          * Returns the sql for the next value in a sequence
2533          * @abstract
2534          * @param  string $table  Table name
2535          * @param  string $field_name  Field name
2536          * @return string
2537          */
2538         public function getAutoIncrementSQL($table, $field_name)
2539         {
2540                 return "";
2541         }
2542
2543         /**
2544          * Either creates an auto increment through queries or returns sql for auto increment
2545          * that can be appended to the end of column defination (mysql)
2546          * @abstract
2547          * @param  string $table Table name
2548          * @param  string $field_name Field name
2549          * @return string
2550          */
2551         protected function setAutoIncrement($table, $field_name)
2552         {
2553                 $this->deleteAutoIncrement($table, $field_name);
2554                 return "";
2555         }
2556
2557     /**
2558      * Sets the next auto-increment value of a column to a specific value.
2559      * @abstract
2560      * @param  string $table Table name
2561      * @param  string $field_name Field name
2562      * @param  int $start_value  Starting autoincrement value
2563      * @return string
2564      *
2565      */
2566         public function setAutoIncrementStart($table, $field_name, $start_value)
2567         {
2568                 return "";
2569         }
2570
2571         /**
2572          * Deletes an auto increment
2573          * @abstract
2574          * @param string $table tablename
2575          * @param string $field_name
2576          */
2577         public function deleteAutoIncrement($table, $field_name)
2578         {
2579                 return;
2580         }
2581
2582         /**
2583          * This method generates sql for adding a column to table identified by field def.
2584          *
2585          * @param  string $tablename
2586          * @param  array  $fieldDefs
2587          * @return string SQL statement
2588          */
2589         public function addColumnSQL($tablename, $fieldDefs)
2590         {
2591             return $this->changeColumnSQL($tablename, $fieldDefs, 'add');
2592         }
2593
2594         /**
2595          * This method genrates sql for altering old column identified by oldFieldDef to new fieldDef.
2596          *
2597          * @param  string $tablename
2598          * @param  array  $newFieldDefs
2599          * @param  bool  $ignorerequired Optional, true if we should ignor this being a required field
2600          * @return string|array SQL statement(s)
2601          */
2602         public function alterColumnSQL($tablename, $newFieldDefs, $ignorerequired = false)
2603         {
2604                 return $this->changeColumnSQL($tablename, $newFieldDefs, 'modify', $ignorerequired);
2605         }
2606
2607         /**
2608          * Generates SQL for dropping a table.
2609          *
2610          * @param  SugarBean $bean Sugarbean instance
2611          * @return string SQL statement
2612          */
2613         public function dropTableSQL(SugarBean $bean)
2614         {
2615                 return $this->dropTableNameSQL($bean->getTableName());
2616         }
2617
2618         /**
2619          * Generates SQL for dropping a table.
2620          *
2621          * @param  string $name table name
2622          * @return string SQL statement
2623          */
2624         public function dropTableNameSQL($name)
2625         {
2626                 return "DROP TABLE ".$name;
2627         }
2628
2629         /**
2630          * Generates SQL for truncating a table.
2631          * @param  string $name  table name
2632          * @return string
2633          */
2634         public function truncateTableSQL($name)
2635         {
2636                 return "TRUNCATE $name";
2637         }
2638
2639         /**
2640          * This method generates sql that deletes a column identified by fieldDef.
2641          *
2642          * @param  SugarBean $bean      Sugarbean instance
2643          * @param  array  $fieldDefs
2644          * @return string SQL statement
2645          */
2646         public function deleteColumnSQL(SugarBean $bean, $fieldDefs)
2647         {
2648                 return $this->dropColumnSQL($bean->getTableName(), $fieldDefs);
2649         }
2650
2651         /**
2652          * This method generates sql that drops a column identified by fieldDef.
2653          * Designed to work like the other addColumnSQL() and alterColumnSQL() functions
2654          *
2655          * @param  string $tablename
2656          * @param  array  $fieldDefs
2657          * @return string SQL statement
2658          */
2659         public function dropColumnSQL($tablename, $fieldDefs)
2660         {
2661                 return $this->changeColumnSQL($tablename, $fieldDefs, 'drop');
2662         }
2663
2664     /**
2665      * Return a version of $proposed that can be used as a column name in any of our supported databases
2666      * Practically this means no longer than 25 characters as the smallest identifier length for our supported DBs is 30 chars for Oracle plus we add on at least four characters in some places (for indicies for example)
2667      * @param string|array $name Proposed name for the column
2668      * @param bool|string $ensureUnique Ensure the name is unique
2669      * @param string $type Name type (table, column)
2670      * @param bool $force Force new name
2671      * @return string|array Valid column name trimmed to right length and with invalid characters removed
2672      */
2673         public function getValidDBName($name, $ensureUnique = false, $type = 'column', $force = false)
2674         {
2675                 if(is_array($name)) {
2676                         $result = array();
2677                         foreach($name as $field) {
2678                                 $result[] = $this->getValidDBName($field, $ensureUnique, $type);
2679                         }
2680                         return $result;
2681                 } else {
2682                     if(strchr($name, ".")) {
2683                         // this is a compound name with dots, handle separately
2684                         $parts = explode(".", $name);
2685                         if(count($parts) > 2) {
2686                             // some weird name, cut to table.name
2687                             array_splice($parts, 0, count($parts)-2);
2688                         }
2689                         $parts = $this->getValidDBName($parts, $ensureUnique, $type, $force);
2690                 return join(".", $parts);
2691                     }
2692                         // first strip any invalid characters - all but word chars (which is alphanumeric and _)
2693                         $name = preg_replace( '/[^\w]+/i', '', $name ) ;
2694                         $len = strlen( $name ) ;
2695                         $maxLen = empty($this->maxNameLengths[$type]) ? $this->maxNameLengths[$type]['column'] : $this->maxNameLengths[$type];
2696                         if ($len <= $maxLen && !$force) {
2697                                 return strtolower($name);
2698                         }
2699                         if ($ensureUnique) {
2700                                 $md5str = md5($name);
2701                                 $tail = substr ( $name, -11) ;
2702                                 $temp = substr($md5str , strlen($md5str)-4 );
2703                                 $result = substr( $name, 0, 10) . $temp . $tail ;
2704                         } else {
2705                                 $result = substr( $name, 0, 11) . substr( $name, 11 - $maxLen);
2706                         }
2707
2708                         return strtolower( $result ) ;
2709                 }
2710         }
2711
2712         /**
2713          * Returns the valid type for a column given the type in fieldDef
2714          *
2715          * @param  string $type field type
2716          * @return string valid type for the given field
2717          */
2718         public function getColumnType($type)
2719         {
2720                 return isset($this->type_map[$type])?$this->type_map[$type]:$type;
2721         }
2722
2723         /**
2724          * Checks to see if passed array is truely an array of defitions
2725          *
2726          * Such an array may have type as a key but it will point to an array
2727          * for a true array of definitions an to a col type for a definition only
2728          *
2729          * @param  mixed $defArray
2730          * @return bool
2731          */
2732         public function isFieldArray($defArray)
2733         {
2734                 if ( !is_array($defArray) )
2735                         return false;
2736
2737                 if ( isset($defArray['type']) ){
2738                         // type key exists. May be an array of defs or a simple definition
2739                         return is_array($defArray['type']); // type is not an array => definition else array
2740                 }
2741
2742                 // type does not exist. Must be array of definitions
2743                 return true;
2744         }
2745
2746         /**
2747          * returns true if the type can be mapped to a valid column type
2748          *
2749          * @param  string $type
2750          * @return bool
2751          */
2752         protected function validColumnType($type)
2753         {
2754                 $type = $this->getColumnType($type);
2755                 return !empty($type);
2756         }
2757
2758         /**
2759          * Generate query for audit table
2760          * @param SugarBean $bean SugarBean that was changed
2761          * @param array $changes List of changes, contains 'before' and 'after'
2762      * @return string  Audit table INSERT query
2763      */
2764         protected function auditSQL(SugarBean $bean, $changes)
2765         {
2766                 global $current_user;
2767                 $sql = "INSERT INTO ".$bean->get_audit_table_name();
2768                 //get field defs for the audit table.
2769                 require('metadata/audit_templateMetaData.php');
2770                 $fieldDefs = $dictionary['audit']['fields'];
2771
2772                 $values=array();
2773                 $values['id'] = $this->massageValue(create_guid(), $fieldDefs['id']);
2774                 $values['parent_id']= $this->massageValue($bean->id, $fieldDefs['parent_id']);
2775                 $values['field_name']= $this->massageValue($changes['field_name'], $fieldDefs['field_name']);
2776                 $values['data_type'] = $this->massageValue($changes['data_type'], $fieldDefs['data_type']);
2777                 if ($changes['data_type']=='text') {
2778                         $bean->fetched_row[$changes['field_name']]=$changes['after'];;
2779                         $values['before_value_text'] = $this->massageValue($changes['before'], $fieldDefs['before_value_text']);
2780                         $values['after_value_text'] = $this->massageValue($changes['after'], $fieldDefs['after_value_text']);
2781                 } else {
2782                         $bean->fetched_row[$changes['field_name']]=$changes['after'];;
2783                         $values['before_value_string'] = $this->massageValue($changes['before'], $fieldDefs['before_value_string']);
2784                         $values['after_value_string'] = $this->massageValue($changes['after'], $fieldDefs['after_value_string']);
2785                 }
2786                 $values['date_created'] = $this->massageValue(TimeDate::getInstance()->nowDb(), $fieldDefs['date_created'] );
2787                 $values['created_by'] = $this->massageValue($current_user->id, $fieldDefs['created_by']);
2788
2789                 $sql .= "(".implode(",", array_keys($values)).") ";
2790                 $sql .= "VALUES(".implode(",", $values).")";
2791                 return $sql;
2792         }
2793
2794     /**
2795      * Saves changes to module's audit table
2796      *
2797      * @param SugarBean $bean Sugarbean instance that was changed
2798      * @param array $changes List of changes, contains 'before' and 'after'
2799      * @return bool query result
2800      *
2801      */
2802         public function save_audit_records(SugarBean $bean, $changes)
2803         {
2804                 return $this->query($this->auditSQL($bean, $changes));
2805         }
2806
2807     /**
2808      * Uses the audit enabled fields array to find fields whose value has changed.
2809      * The before and after values are stored in the bean.
2810      * Uses $bean->fetched_row to compare
2811      *
2812      * @param SugarBean $bean Sugarbean instance that was changed
2813      * @return array
2814      */
2815         public function getDataChanges(SugarBean &$bean)
2816         {
2817                 $changed_values=array();
2818                 $audit_fields=$bean->getAuditEnabledFieldDefinitions();
2819
2820                 if (is_array($audit_fields) and count($audit_fields) > 0) {
2821                         foreach ($audit_fields as $field=>$properties) {
2822                                 if (!empty($bean->fetched_row) && array_key_exists($field, $bean->fetched_row)) {
2823                                         $before_value=$bean->fetched_row[$field];
2824                                         $after_value=$bean->$field;
2825                                         if (isset($properties['type'])) {
2826                                                 $field_type=$properties['type'];
2827                                         } else {
2828                                                 if (isset($properties['dbType']))
2829                                                         $field_type=$properties['dbType'];
2830                                                 else if(isset($properties['data_type']))
2831                                                         $field_type=$properties['data_type'];
2832                                                 else
2833                                                         $field_type=$properties['dbtype'];
2834                                         }
2835
2836                                         //Because of bug #25078(sqlserver haven't 'date' type, trim extra "00:00:00" when insert into *_cstm table).
2837                                         // so when we read the audit datetime field from sqlserver, we have to replace the extra "00:00:00" again.
2838                                         if(!empty($field_type) && $field_type == 'date'){
2839                                                 $before_value = $this->fromConvert($before_value , $field_type);
2840                                         }
2841                                         //if the type and values match, do nothing.
2842                                         if (!($this->_emptyValue($before_value,$field_type) && $this->_emptyValue($after_value,$field_type))) {
2843                                                 if (trim($before_value) !== trim($after_value)) {
2844                             // Bug #42475: Don't directly compare numeric values, instead do the subtract and see if the comparison comes out to be "close enough", it is necessary for floating point numbers.
2845                             // Manual merge of fix 95727f2eed44852f1b6bce9a9eccbe065fe6249f from DBHelper
2846                             // This fix also fixes Bug #44624 in a more generic way and therefore eliminates the need for fix 0a55125b281c4bee87eb347709af462715f33d2d in DBHelper
2847                                                         if (!($this->isNumericType($field_type) &&
2848                                   abs(
2849                                       2*((trim($before_value)+0)-(trim($after_value)+0))/((trim($before_value)+0)+(trim($after_value)+0)) // Using relative difference so that it also works for other numerical types besides currencies
2850                                   )<0.0000000001)) {    // Smaller than 10E-10
2851                                                                 if (!($this->isBooleanType($field_type) && ($this->_getBooleanValue($before_value)== $this->_getBooleanValue($after_value)))) {
2852                                                                         $changed_values[$field]=array('field_name'=>$field,
2853                                                                                 'data_type'=>$field_type,
2854                                                                                 'before'=>$before_value,
2855                                                                                 'after'=>$after_value);
2856                                                                 }
2857                                                         }
2858                                                 }
2859                                         }
2860                                 }
2861                         }
2862                 }
2863                 return $changed_values;
2864         }
2865
2866         /**
2867          * Setup FT indexing
2868          * @abstract
2869          */
2870         public function full_text_indexing_setup()
2871         {
2872                 // Most DBs have nothing to setup, so provide default empty function
2873         }
2874
2875         /**
2876          * Quotes a string for storing in the database
2877          * @deprecated
2878          * Return value will be not surrounded by quotes
2879          *
2880          * @param  string $string
2881          * @return string
2882          */
2883         public function escape_quote($string)
2884         {
2885                 return $this->quote($string);
2886         }
2887
2888         /**
2889          * Quotes a string for storing in the database
2890          * @deprecated
2891          * Return value will be not surrounded by quotes
2892          *
2893          * @param  string $string
2894          * @return string
2895          */
2896         public function quoteFormEmail($string)
2897         {
2898                 return $this->quote($string);
2899         }
2900
2901     /**
2902      * Renames an index using fields definition
2903      *
2904      * @param  array  $old_definition
2905      * @param  array  $new_definition
2906      * @param  string $table_name
2907      * @return string SQL statement
2908      */
2909         public function renameIndexDefs($old_definition, $new_definition, $table_name)
2910         {
2911                 return array($this->add_drop_constraint($table_name,$old_definition,true),
2912                                 $this->add_drop_constraint($table_name,$new_definition), false);
2913         }
2914
2915         /**
2916          * Check if type is boolean
2917          * @param string $type
2918      * @return bool
2919      */
2920         public function isBooleanType($type)
2921         {
2922                 return 'bool' == $type;
2923         }
2924
2925         /**
2926          * Get truth value for boolean type
2927          * Allows 'off' to mean false, along with all 'empty' values
2928          * @param mixed $val
2929      * @return bool
2930          */
2931         protected function _getBooleanValue($val)
2932         {
2933                 //need to put the === sign here otherwise true == 'non empty string'
2934                 if (empty($val) or $val==='off')
2935                         return false;
2936
2937                 return true;
2938         }
2939
2940         /**
2941          * Check if type is a number
2942          * @param string $type
2943      * @return bool
2944          */
2945         public function isNumericType($type)
2946         {
2947             if(isset($this->type_class[$type]) && ($this->type_class[$type] == 'int' || $this->type_class[$type] == 'float')) {
2948                 return true;
2949             }
2950                 return false;
2951         }
2952
2953     /**
2954      * Check if the value is empty value for this type
2955      * @param mixed $val Value
2956      * @param string $type Type (one of vardef types)
2957      * @return bool true if the value if empty
2958      */
2959         protected function _emptyValue($val, $type)
2960         {
2961                 if (empty($val))
2962                         return true;
2963
2964                 if($this->emptyValue($type) == $val) {
2965                         return true;
2966                 }
2967                 switch ($type) {
2968                         case 'decimal':
2969                         case 'decimal2':
2970                         case 'int':
2971                         case 'double':
2972                         case 'float':
2973                         case 'uint':
2974                         case 'ulong':
2975                         case 'long':
2976                         case 'short':
2977                                 return ($val == 0);
2978                         case 'date':
2979                                 if ($val == '0000-00-00')
2980                                         return true;
2981                                 if ($val == 'NULL')
2982                                         return true;
2983                                 return false;
2984                 }
2985
2986                 return false;
2987         }
2988
2989         /**
2990      * @abstract
2991          * Does this type represent text (i.e., non-varchar) value?
2992          * @param string $type
2993      * @return bool
2994          */
2995         public function isTextType($type)
2996         {
2997                 return false;
2998         }
2999
3000         /**
3001          * Check if this DB supports certain capability
3002          * See $this->capabilities for the list
3003          * @param string $cap
3004      * @return bool
3005          */
3006         public function supports($cap)
3007         {
3008                 return !empty($this->capabilities[$cap]);
3009         }
3010
3011         /**
3012          * Create ORDER BY clause for ENUM type field
3013          * @param string $order_by Field name
3014          * @param array $values Possible enum value
3015          * @param string $order_dir Order direction, ASC or DESC
3016      * @return string
3017      */
3018         public function orderByEnum($order_by, $values, $order_dir)
3019         {
3020                 $i = 0;
3021                 $order_by_arr = array();
3022                 foreach ($values as $key => $value) {
3023                         if($key == '') {
3024                                 $order_by_arr[] = "WHEN ($order_by='' OR $order_by IS NULL) THEN $i";
3025                         } else {
3026                                 $order_by_arr[] = "WHEN $order_by=".$this->quoted($key)." THEN $i";
3027                         }
3028                         $i++;
3029                 }
3030                 return "CASE ".implode("\n", $order_by_arr)." ELSE $i END $order_dir\n";
3031         }
3032
3033         /**
3034          * Return representation of an empty value depending on type
3035          * The value is fully quoted, converted, etc.
3036          * @param string $type
3037      * @return mixed Empty value
3038      */
3039         public function emptyValue($type)
3040         {
3041                 if(isset($this->type_class[$type]) && ($this->type_class[$type] == 'bool' || $this->type_class[$type] == 'int' || $this->type_class[$type] == 'float')) {
3042                         return 0;
3043                 }
3044
3045                 return "''";
3046         }
3047
3048         /**
3049          * List of available collation settings
3050      * @abstract
3051          * @return string
3052          */
3053         public function getDefaultCollation()
3054         {
3055                 return null;
3056         }
3057
3058         /**
3059          * List of available collation settings
3060      * @abstract
3061          * @return array
3062          */
3063         public function getCollationList()
3064         {
3065                 return null;
3066         }
3067
3068         /**
3069          * Returns the number of columns in a table
3070          *
3071          * @param  string $table_name
3072          * @return int
3073          */
3074         public function number_of_columns($table_name)
3075         {
3076                 $table = $this->getTableDescription($table_name);
3077                 return count($table);
3078         }
3079
3080         /**
3081          * Return limit query based on given query
3082          * @param string $sql
3083          * @param int $start
3084          * @param int $count
3085          * @param bool $dieOnError
3086          * @param string $msg
3087      * @return resource|bool query result
3088      * @see DBManager::limitQuery()
3089          */
3090         public function limitQuerySql($sql, $start, $count, $dieOnError=false, $msg='')
3091         {
3092                 return $this->limitQuery($sql,$start,$count,$dieOnError,$msg,false);
3093         }
3094
3095         /**
3096          * Return current time in format fit for insertion into DB (with quotes)
3097          * @return string
3098          */
3099         public function now()
3100         {
3101                 return $this->convert($this->quoted(TimeDate::getInstance()->nowDb()), "datetime");
3102         }
3103
3104         /**
3105          * Check if connecting user has certain privilege
3106          * @param string $privilege
3107      * @return bool Privilege allowed?
3108      */
3109         public function checkPrivilege($privilege)
3110         {
3111                 switch($privilege) {
3112                         case "CREATE TABLE":
3113                                 $this->query("CREATE TABLE temp (id varchar(36))");
3114                                 break;
3115                         case "DROP TABLE":
3116                                 $sql = $this->dropTableNameSQL("temp");
3117                                 $this->query($sql);
3118                                 break;
3119                         case "INSERT":
3120                                 $this->query("INSERT INTO temp (id) VALUES ('abcdef0123456789abcdef0123456789abcd')");
3121                                 break;
3122                         case "UPDATE":
3123                                 $this->query("UPDATE temp SET id = '100000000000000000000000000000000000' WHERE id = 'abcdef0123456789abcdef0123456789abcd'");
3124                                 break;
3125                         case 'SELECT':
3126                                 return $this->getOne('SELECT id FROM temp WHERE id=\'100000000000000000000000000000000000\'', false);
3127                         case 'DELETE':
3128                                 $this->query("DELETE FROM temp WHERE id = '100000000000000000000000000000000000'");
3129                                 break;
3130                         case "ADD COLUMN":
3131                                 $test = array("test" => array("name" => "test", "type" => "varchar", "len" => 50));
3132                                 $sql =  $this->changeColumnSQL("temp", $test, "add");
3133                                 $this->query($sql);
3134                                 break;
3135                         case "CHANGE COLUMN":
3136                                 $test = array("test" => array("name" => "test", "type" => "varchar", "len" => 100));
3137                                 $sql =  $this->changeColumnSQL("temp", $test, "modify");
3138                                 $this->query($sql);
3139                                 break;
3140                         case "DROP COLUMN":
3141                                 $test = array("test" => array("name" => "test", "type" => "varchar", "len" => 100));
3142                                 $sql =  $this->changeColumnSQL("temp", $test, "drop");
3143                                 $this->query($sql);
3144                                 break;
3145                         default:
3146                                 return false;
3147                 }
3148                 if($this->checkError("Checking privileges")) {
3149                         return false;
3150                 }
3151                 return true;
3152         }
3153
3154         /**
3155          * Check if the query is a select query
3156          * @param string $query
3157      * @return bool  Is query SELECT?
3158      */
3159         protected function isSelect($query)
3160         {
3161                 $query = trim($query);
3162                 $select_check = strpos(strtolower($query), strtolower("SELECT"));
3163                 //Checks to see if there is union select which is valid
3164                 $select_check2 = strpos(strtolower($query), strtolower("(SELECT"));
3165                 if($select_check==0 || $select_check2==0){
3166                         //Returning false means query is ok!
3167                         return true;
3168                 }
3169                 return false;
3170         }
3171
3172         /**
3173          * Parse fulltext search query with mysql syntax:
3174          *  terms quoted by ""
3175          *  + means the term must be included
3176          *  - means the term must be excluded
3177          *  * or % at the end means wildcard
3178          * @param string $query
3179          * @return array of 3 elements - query terms, mandatory terms and excluded terms
3180          */
3181         public function parseFulltextQuery($query)
3182         {
3183                 /* split on space or comma, double quotes with \ for escape */
3184                 if(strpbrk($query, " ,")) {
3185                         // ("([^"]*?)"|[^" ,]+)((, )+)?
3186                         // '/([^" ,]+|".*?[^\\\\]")(,|\s)\s*/'
3187                         if(!preg_match_all('/("([^"]*?)"|[^"\s,]+)((,\s)+)?/', $query, $m)) {
3188                                 return false;
3189                         }
3190                         $qterms = $m[1];
3191                 } else {
3192                         $qterms = array($query);
3193                 }
3194                 $terms = $must_terms = $not_terms = array();
3195                 foreach($qterms as $item) {
3196                         if($item[0] == '"') {
3197                                 $item = trim($item, '"');
3198                         }
3199                         if($item[0] == '+') {
3200                 if (strlen($item) > 1) {
3201                     $must_terms[] = substr($item, 1);
3202                 }
3203                 continue;
3204                         }
3205                         if($item[0] == '-') {
3206                 if (strlen($item) > 1) {
3207                                     $not_terms[] = substr($item, 1);
3208                 }
3209                 continue;
3210                         }
3211                         $terms[] = $item;
3212                 }
3213                 return array($terms, $must_terms, $not_terms);
3214         }
3215
3216     // Methods to check respective queries
3217         protected $standardQueries = array(
3218                 'ALTER TABLE' => 'verifyAlterTable',
3219                 'DROP TABLE' => 'verifyDropTable',
3220                 'CREATE TABLE' => 'verifyCreateTable',
3221                 'INSERT INTO' => 'verifyInsertInto',
3222                 'UPDATE' => 'verifyUpdate',
3223                 'DELETE FROM' => 'verifyDeleteFrom',
3224         );
3225
3226
3227     /**
3228      * Extract table name from a query
3229      * @param string $query SQL query
3230      * @return string
3231      */
3232         protected function extractTableName($query)
3233         {
3234         $query = preg_replace('/[^A-Za-z0-9_\s]/', "", $query);
3235         $query = trim(str_replace(array_keys($this->standardQueries), '', $query));
3236
3237         $firstSpc = strpos($query, " ");
3238         $end = ($firstSpc > 0) ? $firstSpc : strlen($query);
3239         $table = substr($query, 0, $end);
3240
3241         return $table;
3242         }
3243
3244     /**
3245      * Verify SQl statement using per-DB verification function
3246      * provided the function exists
3247      * @param string $query Query to verify
3248      * @param array $skipTables List of blacklisted tables that aren't checked
3249      * @return string
3250      */
3251         public function verifySQLStatement($query, $skipTables)
3252         {
3253                 $query = trim($query);
3254                 foreach($this->standardQueries as $qstart => $check) {
3255                         if(strncasecmp($qstart, $query, strlen($qstart)) == 0) {
3256                                 if(is_callable(array($this, $check))) {
3257                                         $table = $this->extractTableName($query);
3258                                         if(!in_array($table, $skipTables)) {
3259                                                 return call_user_func(array($this, $check), $table, $query);
3260                                         } else {
3261                                                 $this->log->debug("Skipping table $table as blacklisted");
3262                                         }
3263                                 } else {
3264                                         $this->log->debug("No verification for $qstart on {$this->dbType}");
3265                                 }
3266                                 break;
3267                         }
3268                 }
3269                 return "";
3270         }
3271
3272         /**
3273          * Tests an CREATE TABLE query
3274          * @param string $table The table name to get DDL
3275          * @param string $query The query to test.
3276          * @return string Non-empty if error found
3277          */
3278         protected function verifyCreateTable($table, $query)
3279         {
3280                 $this->log->debug('verifying CREATE statement...');
3281
3282                 // rewrite DDL with _temp name
3283                 $this->log->debug('testing query: ['.$query.']');
3284                 $tempname = $table."__uw_temp";
3285                 $tempTableQuery = str_replace("CREATE TABLE {$table}", "CREATE TABLE $tempname", $query);
3286
3287                 if(strpos($tempTableQuery, '__uw_temp') === false) {
3288                         return 'Could not use a temp table to test query!';
3289                 }
3290
3291                 $this->query($tempTableQuery, false, "Preflight Failed for: {$query}");
3292
3293                 $error = $this->lastError(); // empty on no-errors
3294                 if(!empty($error)) {
3295                         return $error;
3296                 }
3297
3298                 // check if table exists
3299                 $this->log->debug('testing for table: '.$table);
3300                 if(!$this->tableExists($tempname)) {
3301                         return "Failed to create temp table!";
3302                 }
3303
3304                 $this->dropTableName($tempname);
3305                 return '';
3306         }
3307
3308         /**
3309          * Execute multiple queries one after another
3310          * @param array $sqls Queries
3311          * @param bool $dieOnError Die on error, passed to query()
3312          * @param string $msg Error message, passed to query()
3313          * @param bool $suppress Supress errors, passed to query()
3314          * @return resource|bool result set or success/failure bool
3315          */
3316         public function queryArray(array $sqls, $dieOnError = false, $msg = '', $suppress = false)
3317         {
3318                 $last = true;
3319                 foreach($sqls as $sql) {
3320                         if(!($last = $this->query($sql, $dieOnError, $msg, $suppress))) {
3321                                 break;
3322                         }
3323                 }
3324                 return $last;
3325         }
3326
3327         /**
3328          * Fetches the next row in the query result into an associative array
3329          *
3330          * @param  resource $result
3331          * @param  bool $encode Need to HTML-encode the result?
3332          * @return array    returns false if there are no more rows available to fetch
3333          */
3334         public function fetchByAssoc($result, $encode = true)
3335         {
3336             if (empty($result)) return false;
3337
3338             if(is_int($encode) && func_num_args() == 3) {
3339                 // old API: $result, $rowNum, $encode
3340                 $GLOBALS['log']->deprecated("Using row number in fetchByAssoc is not portable and no longer supported. Please fix your code.");
3341                 $encode = func_get_arg(2);
3342             }
3343             $row = $this->fetchRow($result);
3344             if (!empty($row) && $encode && $this->encode) {
3345                 return array_map('to_html', $row);
3346             } else {
3347                return $row;
3348             }
3349         }
3350
3351         /**
3352          * Get DB driver name used for install/upgrade scripts
3353          * @return string
3354          */
3355         public function getScriptName()
3356         {
3357                 // Usually the same name as dbType
3358                 return $this->dbType;
3359         }
3360
3361         /**
3362          * Set database options
3363          * Options are usually db-dependant and derive from $config['dbconfigoption']
3364          * @param array $options
3365          * @return DBManager
3366          */
3367         public function setOptions($options)
3368         {
3369             $this->options = $options;
3370             return $this;
3371         }
3372
3373         /**
3374          * Get DB options
3375          * @return array
3376          */
3377         public function getOptions()
3378         {
3379             return $this->options;
3380         }
3381
3382         /**
3383          * Get DB option by name
3384          * @param string $option Option name
3385          * @return mixed Option value or null if doesn't exist
3386          */
3387         public function getOption($option)
3388         {
3389             if(isset($this->options[$option])) {
3390                 return $this->options[$option];
3391             }
3392             return null;
3393         }
3394
3395         /**
3396          * Commits pending changes to the database when the driver is setup to support transactions.
3397          * Note that the default implementation is applicable for transaction-less or auto commit scenarios.
3398          * @abstract
3399          * @return bool true if commit succeeded, false if it failed
3400          */
3401         public function commit()
3402         {
3403                 $this->log->info("DBManager.commit() stub");
3404                 return true;
3405         }
3406
3407         /**
3408          * Rollsback pending changes to the database when the driver is setup to support transactions.
3409          * Note that the default implementation is applicable for transaction-less or auto commit scenarios.
3410          * Since rollbacks cannot be done, this implementation always returns false.
3411          * @abstract
3412          * @return bool true if rollback succeeded, false if it failed
3413          */
3414         public function rollback()
3415         {
3416                 $this->log->info("DBManager.rollback() stub");
3417                 return false;
3418         }
3419
3420         /**
3421          * Check if this DB name is valid
3422          *
3423          * @param string $name
3424          * @return bool
3425          */
3426         public function isDatabaseNameValid($name)
3427         {
3428                 // Generic case - no slashes, no dots
3429                 return preg_match('#[/.\\\\]#', $name)==0;
3430         }
3431
3432         /**
3433          * Check special requirements for DB installation.
3434          * @abstract
3435          * If everything is OK, return true.
3436          * If something's wrong, return array of error code and parameters
3437          * @return mixed
3438          */
3439         public function canInstall()
3440         {
3441                 return true;
3442         }
3443
3444         /**
3445          * @abstract
3446      * Code run on new database before installing
3447          */
3448         public function preInstall()
3449         {
3450         }
3451
3452         /**
3453      * @abstract
3454          * Code run on new database after installing
3455          */
3456         public function postInstall()
3457         {
3458         }
3459
3460         /**
3461          * Disable keys on the table
3462          * @abstract
3463          * @param string $tableName
3464          */
3465         public function disableKeys($tableName)
3466         {
3467         }
3468
3469         /**
3470          * Re-enable keys on the table
3471          * @abstract
3472          * @param string $tableName
3473          */
3474         public function enableKeys($tableName)
3475         {
3476         }
3477
3478         /**
3479          * Quote string in DB-specific manner
3480          * @param string $string
3481          * @return string
3482          */
3483         abstract public function quote($string);
3484
3485         /**
3486          * Use when you need to convert a database string to a different value; this function does it in a
3487          * database-backend aware way
3488          * Supported conversions:
3489          *      today           return current date
3490          *      left            Take substring from the left
3491          *      date_format     Format date as string, supports %Y-%m-%d, %Y-%m, %Y
3492      *      time_format Format time as string
3493      *      date        Convert date string to datetime value
3494      *      time        Convert time string to datetime value
3495          *      datetime        Convert datetime string to datetime value
3496          *      ifnull          If var is null, use default value
3497          *      concat          Concatenate strings
3498          *      quarter         Quarter number of the date
3499          *      length          Length of string
3500          *      month           Month number of the date
3501          *      add_date        Add specified interval to a date
3502      *      add_time    Add time interval to a date
3503      *      text2char   Convert text field to varchar
3504          *
3505          * @param string $string database string to convert
3506          * @param string $type type of conversion to do
3507          * @param array  $additional_parameters optional, additional parameters to pass to the db function
3508          * @return string
3509          */
3510         abstract public function convert($string, $type, array $additional_parameters = array());
3511
3512         /**
3513          * Converts from Database data to app data
3514          *
3515          * Supported types
3516          * - date
3517          * - time
3518          * - datetime
3519      * - datetimecombo
3520      * - timestamp
3521          *
3522          * @param string $string database string to convert
3523          * @param string $type type of conversion to do
3524          * @return string
3525          */
3526         abstract public function fromConvert($string, $type);
3527
3528     /**
3529      * Parses and runs queries
3530      *
3531      * @param  string   $sql        SQL Statement to execute
3532      * @param  bool     $dieOnError True if we want to call die if the query returns errors
3533      * @param  string   $msg        Message to log if error occurs
3534      * @param  bool     $suppress   Flag to suppress all error output unless in debug logging mode.
3535      * @param  bool     $keepResult Keep query result in the object?
3536      * @return resource|bool result set or success/failure bool
3537      */
3538         abstract public function query($sql, $dieOnError = false, $msg = '', $suppress = false, $keepResult = false);
3539
3540     /**
3541      * Runs a limit query: one where we specify where to start getting records and how many to get
3542      *
3543      * @param  string   $sql     SELECT query
3544      * @param  int      $start   Starting row
3545      * @param  int      $count   How many rows
3546      * @param  boolean  $dieOnError  True if we want to call die if the query returns errors
3547      * @param  string   $msg     Message to log if error occurs
3548      * @param  bool     $execute Execute or return SQL?
3549      * @return resource query result
3550      */
3551         abstract function limitQuery($sql, $start, $count, $dieOnError = false, $msg = '', $execute = true);
3552
3553
3554         /**
3555          * Free Database result
3556          * @param resource $dbResult
3557          */
3558         abstract protected function freeDbResult($dbResult);
3559
3560         /**
3561          * Rename column in the DB
3562          * @param string $tablename
3563          * @param string $column
3564          * @param string $newname
3565          */
3566         abstract function renameColumnSQL($tablename, $column, $newname);
3567
3568         /**
3569          * Returns definitions of all indies for passed table.
3570          *
3571          * return will is a multi-dimensional array that
3572          * categorizes the index definition by types, unique, primary and index.
3573          * <code>
3574          * <?php
3575          * array(                                                              O
3576          *       'index1'=> array (
3577          *           'name'   => 'index1',
3578          *           'type'   => 'primary',
3579          *           'fields' => array('field1','field2')
3580          *           )
3581          *       )
3582          * ?>
3583          * </code>
3584          * This format is similar to how indicies are defined in vardef file.
3585          *
3586          * @param  string $tablename
3587          * @return array
3588          */
3589         abstract public function get_indices($tablename);
3590
3591         /**
3592          * Returns definitions of all indies for passed table.
3593          *
3594          * return will is a multi-dimensional array that
3595          * categorizes the index definition by types, unique, primary and index.
3596          * <code>
3597          * <?php
3598          * array(
3599          *       'field1'=> array (
3600          *           'name'   => 'field1',
3601          *           'type'   => 'varchar',
3602          *           'len' => '200'
3603          *           )
3604          *       )
3605          * ?>
3606          * </code>
3607          * This format is similar to how indicies are defined in vardef file.
3608          *
3609          * @param  string $tablename
3610          * @return array
3611          */
3612         abstract public function get_columns($tablename);
3613
3614         /**
3615          * Generates alter constraint statement given a table name and vardef definition.
3616          *
3617          * Supports both adding and droping a constraint.
3618          *
3619          * @param  string $table      tablename
3620          * @param  array  $definition field definition
3621          * @param  bool   $drop       true if we are dropping the constraint, false if we are adding it
3622          * @return string SQL statement
3623          */
3624         abstract public function add_drop_constraint($table, $definition, $drop = false);
3625
3626         /**
3627          * Returns the description of fields based on the result
3628          *
3629          * @param  resource $result
3630          * @param  boolean  $make_lower_case
3631          * @return array field array
3632          */
3633         abstract public function getFieldsArray($result, $make_lower_case = false);
3634
3635         /**
3636          * Returns an array of tables for this database
3637          *
3638          * @return      array|false     an array of with table names, false if no tables found
3639          */
3640         abstract public function getTablesArray();
3641
3642         /**
3643          * Return's the version of the database
3644          *
3645          * @return string
3646          */
3647         abstract public function version();
3648
3649         /**
3650          * Checks if a table with the name $tableName exists
3651          * and returns true if it does or false otherwise
3652          *
3653          * @param  string $tableName
3654          * @return bool
3655          */
3656         abstract public function tableExists($tableName);
3657
3658         /**
3659          * Fetches the next row in the query result into an associative array
3660          *
3661          * @param  resource $result
3662          * @return array    returns false if there are no more rows available to fetch
3663          */
3664         abstract public function fetchRow($result);
3665
3666         /**
3667          * Connects to the database backend
3668          *
3669          * Takes in the database settings and opens a database connection based on those
3670          * will open either a persistent or non-persistent connection.
3671          * If a persistent connection is desired but not available it will defualt to non-persistent
3672          *
3673          * configOptions must include
3674          * db_host_name - server ip
3675          * db_user_name - database user name
3676          * db_password - database password
3677          *
3678          * @param array   $configOptions
3679          * @param boolean $dieOnError
3680          */
3681         abstract public function connect(array $configOptions = null, $dieOnError = false);
3682
3683         /**
3684          * Generates sql for create table statement for a bean.
3685          *
3686          * @param  string $tablename
3687          * @param  array  $fieldDefs
3688          * @param  array  $indices
3689          * @return string SQL Create Table statement
3690          */
3691         abstract public function createTableSQLParams($tablename, $fieldDefs, $indices);
3692
3693         /**
3694          * Generates the SQL for changing columns
3695          *
3696          * @param string $tablename
3697          * @param array  $fieldDefs
3698          * @param string $action
3699          * @param bool   $ignoreRequired Optional, true if we should ignor this being a required field
3700          * @return string|array
3701          */
3702         abstract protected function changeColumnSQL($tablename, $fieldDefs, $action, $ignoreRequired = false);
3703
3704         /**
3705          * Disconnects from the database
3706          *
3707          * Also handles any cleanup needed
3708          */
3709         abstract public function disconnect();
3710
3711         /**
3712          * Get last database error
3713          * This function should return last error as reported by DB driver
3714          * and should return false if no error condition happened
3715          * @return string|false Error message or false if no error happened
3716          */
3717         abstract public function lastDbError();
3718
3719     /**
3720      * Check if this query is valid
3721      * Validates only SELECT queries
3722      * @param string $query
3723      * @return bool
3724      */
3725         abstract public function validateQuery($query);
3726
3727         /**
3728          * Check if this driver can be used
3729          * @return bool
3730          */
3731         abstract public function valid();
3732
3733         /**
3734          * Check if certain database exists
3735          * @param string $dbname
3736          */
3737         abstract public function dbExists($dbname);
3738
3739         /**
3740          * Get tables like expression
3741          * @param string $like Expression describing tables
3742          * @return array
3743          */
3744         abstract public function tablesLike($like);
3745
3746         /**
3747          * Create a database
3748          * @param string $dbname
3749          */
3750         abstract public function createDatabase($dbname);
3751
3752         /**
3753          * Drop a database
3754          * @param string $dbname
3755          */
3756         abstract public function dropDatabase($dbname);
3757
3758         /**
3759          * Get database configuration information (DB-dependent)
3760          * @return array|null
3761          */
3762         abstract public function getDbInfo();
3763
3764         /**
3765          * Check if certain DB user exists
3766          * @param string $username
3767          */
3768         abstract public function userExists($username);
3769
3770         /**
3771          * Create DB user
3772          * @param string $database_name
3773          * @param string $host_name
3774          * @param string $user
3775          * @param string $password
3776          */
3777         abstract public function createDbUser($database_name, $host_name, $user, $password);
3778
3779         /**
3780          * Check if the database supports fulltext indexing
3781          * Note that database driver can be capable of supporting FT (see supports('fulltext))
3782          * but particular instance can still have it disabled
3783          * @return bool
3784          */
3785         abstract public function full_text_indexing_installed();
3786
3787         /**
3788          * Generate fulltext query from set of terms
3789          * @param string $field Field to search against
3790          * @param array $terms Search terms that may be or not be in the result
3791          * @param array $must_terms Search terms that have to be in the result
3792          * @param array $exclude_terms Search terms that have to be not in the result
3793          */
3794         abstract public function getFulltextQuery($field, $terms, $must_terms = array(), $exclude_terms = array());
3795
3796         /**
3797          * Get install configuration for this DB
3798          * @return array
3799          */
3800         abstract public function installConfig();
3801
3802     /**
3803      * Returns a DB specific FROM clause which can be used to select against functions.
3804      * Note that depending on the database that this may also be an empty string.
3805      * @abstract
3806      * @return string
3807      */
3808     abstract public function getFromDummyTable();
3809
3810     /**
3811      * Returns a DB specific piece of SQL which will generate GUID (UUID)
3812      * This string can be used in dynamic SQL to do multiple inserts with a single query.
3813      * I.e. generate a unique Sugar id in a sub select of an insert statement.
3814      * @abstract
3815      * @return string
3816      */
3817     abstract public function getGuidSQL();
3818 }