Squashed 'public/legacy/' content from commit 817a12dc0

git-subtree-dir: public/legacy
git-subtree-split: 817a12dc0c30c189f56d5cb1f7dc37a9631bdbe3
This commit is contained in:
Dillon-Brown 2021-03-31 15:37:32 +01:00
commit 8e4cc94994
9769 changed files with 1617695 additions and 0 deletions

4117
include/database/DBManager.php Executable file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,283 @@
<?php
if (!defined('sugarEntry') || !sugarEntry) {
die('Not A Valid Entry Point');
}
/**
*
* SugarCRM Community Edition is a customer relationship management program developed by
* SugarCRM, Inc. Copyright (C) 2004-2013 SugarCRM Inc.
*
* SuiteCRM is an extension to SugarCRM Community Edition developed by SalesAgility Ltd.
* Copyright (C) 2011 - 2018 SalesAgility Ltd.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by the
* Free Software Foundation with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
* OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along with
* this program; if not, see http://www.gnu.org/licenses or write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
* You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
* SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "Powered by
* SugarCRM" logo and "Supercharged by SuiteCRM" logo. If the display of the logos is not
* reasonably feasible for technical reasons, the Appropriate Legal Notices must
* display the words "Powered by SugarCRM" and "Supercharged by SuiteCRM".
*/
/**
* Description: This file generates the appropriate manager for the database
*
* Portions created by SugarCRM are Copyright (C) SugarCRM, Inc.
* All Rights Reserved.
* Contributor(s): ______________________________________..
********************************************************************************/
require_once dirname(__DIR__) . '/../include/database/DBManager.php';
/**
* Database driver factory
* @api
* Instantiates and configures appropriate DB drivers
*/
class DBManagerFactory
{
public static $instances = array();
/**
* Returns a reference to the DB object of specific type
*
* @param string $type DB type
* @param array $config DB configuration
* @return object DBManager instance
*/
public static function getTypeInstance($type, $config = array())
{
global $sugar_config;
if (empty($config['db_manager'])) {
// standard types
switch ($type) {
case "mysql":
if (empty($sugar_config['mysqli_disabled']) && function_exists('mysqli_connect')) {
$my_db_manager = 'MysqliManager';
} else {
$my_db_manager = "MysqlManager";
}
break;
case "mssql":
if (function_exists('sqlsrv_connect')
&& (empty($config['db_mssql_force_driver']) || $config['db_mssql_force_driver'] == 'sqlsrv')) {
$my_db_manager = 'SqlsrvManager';
} elseif (self::isFreeTDS()
&& (empty($config['db_mssql_force_driver']) || $config['db_mssql_force_driver'] == 'freetds')) {
$my_db_manager = 'FreeTDSManager';
} else {
$my_db_manager = 'MssqlManager';
}
break;
default:
$my_db_manager = self::getManagerByType($type, false);
if (empty($my_db_manager)) {
$GLOBALS['log']->fatal("unable to load DB manager for: $type");
sugar_die("Cannot load DB manager");
}
}
} else {
$my_db_manager = $config['db_manager'];
}
// sanitize the name
$my_db_manager = preg_replace("/[^A-Za-z0-9_-]/", "", $my_db_manager);
if (!empty($config['db_manager_class'])) {
$my_db_manager = $config['db_manager_class'];
} else {
if (file_exists("custom/include/database/{$my_db_manager}.php")) {
require_once("custom/include/database/{$my_db_manager}.php");
} else {
require_once("include/database/{$my_db_manager}.php");
}
}
if (class_exists($my_db_manager)) {
return new $my_db_manager();
} else {
return null;
}
}
/**
* Returns a reference to the DB object for instance $instanceName, or the default
* instance if one is not specified
*
* @param string $instanceName optional, name of the instance
* @return DBManager instance
*/
public static function getInstance($instanceName = '')
{
global $sugar_config;
static $count = 0, $old_count = 0;
//fall back to the default instance name
if (empty($sugar_config['db'][$instanceName])) {
$instanceName = '';
}
if (!isset(self::$instances[$instanceName])) {
$config = $sugar_config['dbconfig'];
$count++;
self::$instances[$instanceName] = self::getTypeInstance($config['db_type'], $config);
if (!empty($sugar_config['dbconfigoption'])) {
self::$instances[$instanceName]->setOptions($sugar_config['dbconfigoption']);
}
self::$instances[$instanceName]->connect($config, true);
self::$instances[$instanceName]->count_id = $count;
self::$instances[$instanceName]->references = 0;
} else {
$old_count++;
self::$instances[$instanceName]->references = $old_count;
}
return self::$instances[$instanceName];
}
/**
* Disconnect all DB connections in the system
*/
public static function disconnectAll()
{
foreach (self::$instances as $instance) {
$instance->disconnect();
}
self::$instances = array();
}
/**
* Get DB manager class name by type name
*
* For use in install
* @param string $type
* @param bool $validate Return only valid drivers or any?
* @return string
*/
public static function getManagerByType($type, $validate = true)
{
$drivers = self::getDbDrivers($validate);
if (!empty($drivers[$type])) {
return get_class($drivers[$type]);
}
return false;
}
/**
* Scan directory for valid DB drivers
* @param string $dir
* @param array $drivers
* @param bool $validate Return only valid drivers or all of them?
*/
protected static function scanDriverDir($dir, &$drivers, $validate = true)
{
if (!is_dir($dir)) {
return;
}
$scandir = opendir($dir);
if ($scandir === false) {
return;
}
while (($name = readdir($scandir)) !== false) {
if (substr($name, -11) != "Manager.php") {
continue;
}
if ($name == "DBManager.php") {
continue;
}
require_once("$dir/$name");
$classname = substr($name, 0, -4);
if (!class_exists($classname)) {
continue;
}
$driver = new $classname;
if (!$validate || $driver->valid()) {
if (empty($drivers[$driver->dbType])) {
$drivers[$driver->dbType] = array();
}
$drivers[$driver->dbType][] = $driver;
}
}
}
/**
* Compares two drivers by priority
* @internal
* @param object $a
* @param object $b
* @return int
*/
public static function _compareDrivers($a, $b)
{
return $b->priority - $a->priority;
}
/**
* Get list of all available DB drivers
* @param bool $validate Return only valid drivers or all of them?
* @return array List of Db drivers, key - variant (mysql, mysqli), value - driver type (mysql, mssql)
*/
public static function getDbDrivers($validate = true)
{
$drivers = array();
self::scanDriverDir("include/database", $drivers, $validate);
self::scanDriverDir("custom/include/database", $drivers, $validate);
$result = array();
foreach ($drivers as $type => $tdrivers) {
if (empty($tdrivers)) {
continue;
}
if (count($tdrivers) > 1) {
usort($tdrivers, array(__CLASS__, "_compareDrivers"));
}
$result[$type] = $tdrivers[0];
}
return $result;
}
/**
* Check if we have freeTDS driver installed
* Invoked when connected to mssql. checks if we have freetds version of mssql library.
* the response is put into a global variable.
* @return bool
*/
public static function isFreeTDS()
{
static $is_freetds = null;
if ($is_freetds === null) {
ob_start();
phpinfo(INFO_MODULES);
$info=ob_get_contents();
ob_end_clean();
$is_freetds = (strpos($info, 'FreeTDS') !== false);
}
return $is_freetds;
}
}

View file

@ -0,0 +1,117 @@
<?php
/**
*
* SugarCRM Community Edition is a customer relationship management program developed by
* SugarCRM, Inc. Copyright (C) 2004-2013 SugarCRM Inc.
*
* SuiteCRM is an extension to SugarCRM Community Edition developed by SalesAgility Ltd.
* Copyright (C) 2011 - 2018 SalesAgility Ltd.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by the
* Free Software Foundation with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
* OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along with
* this program; if not, see http://www.gnu.org/licenses or write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
* You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
* SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "Powered by
* SugarCRM" logo and "Supercharged by SuiteCRM" logo. If the display of the logos is not
* reasonably feasible for technical reasons, the Appropriate Legal Notices must
* display the words "Powered by SugarCRM" and "Supercharged by SuiteCRM".
*/
if (!defined('sugarEntry') || !sugarEntry) {
die('Not A Valid Entry Point');
}
include_once('include/database/MssqlManager.php');
/**
* SQL Server driver for FreeTDS
*/
class FreeTDSManager extends MssqlManager
{
public $dbName = 'FreeTDS SQL Server';
public $variant = 'freetds';
public $label = 'LBL_MSSQL2';
protected $capabilities = array(
"affected_rows" => true,
'fulltext' => true,
'limit_subquery' => true,
);
protected $type_map = array(
'int' => 'int',
'double' => 'float',
'float' => 'float',
'uint' => 'int',
'ulong' => 'int',
'long' => 'bigint',
'short' => 'smallint',
'varchar' => 'nvarchar',
'text' => 'nvarchar(max)',
'longtext' => 'nvarchar(max)',
'date' => 'datetime',
'enum' => 'nvarchar',
'relate' => 'nvarchar',
'multienum'=> 'nvarchar(max)',
'html' => 'nvarchar(max)',
'longhtml' => 'text',
'emailbody' => 'nvarchar(max)',
'datetime' => 'datetime',
'datetimecombo' => 'datetime',
'time' => 'datetime',
'bool' => 'bit',
'tinyint' => 'tinyint',
'char' => 'char',
'blob' => 'nvarchar(max)',
'longblob' => 'nvarchar(max)',
'currency' => 'decimal(26,6)',
'decimal' => 'decimal',
'decimal2' => 'decimal',
'id' => 'varchar(36)',
'url' => 'nvarchar',
'encrypt' => 'nvarchar',
'file' => 'nvarchar',
'decimal_tpl' => 'decimal(%d, %d)',
);
public function query($sql, $dieOnError = false, $msg = '', $suppress = false, $keepResult = false)
{
global $app_strings;
if (is_array($sql)) {
return $this->queryArray($sql, $dieOnError, $msg, $suppress);
}
$sql = $this->_appendN($sql);
return parent::query($sql, $dieOnError, $msg, $suppress, $keepResult);
}
/**
* Check if this driver can be used
* @return bool
*/
public function valid()
{
return function_exists("mssql_connect") && DBManagerFactory::isFreeTDS();
}
}

2132
include/database/MssqlManager.php Executable file

File diff suppressed because it is too large Load diff

1672
include/database/MysqlManager.php Executable file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,430 @@
<?php
if (!defined('sugarEntry') || !sugarEntry) {
die('Not A Valid Entry Point');
}
/**
*
* SugarCRM Community Edition is a customer relationship management program developed by
* SugarCRM, Inc. Copyright (C) 2004-2013 SugarCRM Inc.
*
* SuiteCRM is an extension to SugarCRM Community Edition developed by SalesAgility Ltd.
* Copyright (C) 2011 - 2018 SalesAgility Ltd.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by the
* Free Software Foundation with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
* OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along with
* this program; if not, see http://www.gnu.org/licenses or write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
* You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
* SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "Powered by
* SugarCRM" logo and "Supercharged by SuiteCRM" logo. If the display of the logos is not
* reasonably feasible for technical reasons, the Appropriate Legal Notices must
* display the words "Powered by SugarCRM" and "Supercharged by SuiteCRM".
*/
/*********************************************************************************
* Description: This file handles the Data base functionality for the application.
* It acts as the DB abstraction layer for the application. It depends on helper classes
* which generate the necessary SQL. This sql is then passed to PEAR DB classes.
* The helper class is chosen in DBManagerFactory, which is driven by 'db_type' in 'dbconfig' under config.php.
*
* All the functions in this class will work with any bean which implements the meta interface.
* The passed bean is passed to helper class which uses these functions to generate correct sql.
*
* The meta interface has the following functions:
* getTableName() Returns table name of the object.
* getFieldDefinitions() Returns a collection of field definitions in order.
* getFieldDefintion(name) Return field definition for the field.
* getFieldValue(name) Returns the value of the field identified by name.
* If the field is not set, the function will return boolean FALSE.
* getPrimaryFieldDefinition() Returns the field definition for primary key
*
* The field definition is an array with the following keys:
*
* name This represents name of the field. This is a required field.
* type This represents type of the field. This is a required field and valid values are:
* <EFBFBD> int
* <EFBFBD> long
* <EFBFBD> varchar
* <EFBFBD> text
* <EFBFBD> date
* <EFBFBD> datetime
* <EFBFBD> double
* <EFBFBD> float
* <EFBFBD> uint
* <EFBFBD> ulong
* <EFBFBD> time
* <EFBFBD> short
* <EFBFBD> enum
* length This is used only when the type is varchar and denotes the length of the string.
* The max value is 255.
* enumvals This is a list of valid values for an enum separated by "|".
* It is used only if the type is <EFBFBD>enum<EFBFBD>;
* required This field dictates whether it is a required value.
* The default value is <EFBFBD>FALSE<EFBFBD>.
* isPrimary This field identifies the primary key of the table.
* If none of the fields have this flag set to <EFBFBD>TRUE<EFBFBD>,
* the first field definition is assume to be the primary key.
* Default value for this field is <EFBFBD>FALSE<EFBFBD>.
* default This field sets the default value for the field definition.
*
*
* Portions created by SugarCRM are Copyright (C) SugarCRM, Inc.
* All Rights Reserved.
* Contributor(s): ______________________________________..
********************************************************************************/
require_once('include/database/MysqlManager.php');
/**
* MySQL manager implementation for mysqli extension
*/
class MysqliManager extends MysqlManager
{
/**
* @see DBManager::$dbType
*/
public $dbType = 'mysql';
public $variant = 'mysqli';
public $priority = 10;
public $label = 'LBL_MYSQLI';
/**
* @see DBManager::$backendFunctions
*/
protected $backendFunctions = array(
'free_result' => 'mysqli_free_result',
'close' => 'mysqli_close',
'row_count' => 'mysqli_num_rows',
'affected_row_count' => 'mysqli_affected_rows',
);
/**
* @see MysqlManager::query()
*/
public function query($sql, $dieOnError = false, $msg = '', $suppress = false, $keepResult = false)
{
if (is_array($sql)) {
return $this->queryArray($sql, $dieOnError, $msg, $suppress);
}
static $queryMD5 = array();
parent::countQuery($sql);
$GLOBALS['log']->info('Query:' . $sql);
$this->checkConnection();
$this->query_time = microtime(true);
$this->lastsql = $sql;
if (!empty($sql)) {
if ($this->database instanceof mysqli) {
$result = $suppress ? @mysqli_query($this->database, $sql) : mysqli_query($this->database, $sql);
if ($result === false && !$suppress) {
if (inDeveloperMode()) {
LoggerManager::getLogger()->debug('Mysqli_query failed, error was: ' . $this->lastDbError() . ', query was: ');
}
LoggerManager::getLogger()->fatal('Mysqli_query failed.');
}
} else {
LoggerManager::getLogger()->fatal('Database error: Incorrect link');
}
} else {
$GLOBALS['log']->fatal('MysqliManager: Empty query');
$result = null;
}
$md5 = md5($sql);
if (empty($queryMD5[$md5])) {
$queryMD5[$md5] = true;
}
$this->query_time = microtime(true) - $this->query_time;
$GLOBALS['log']->info('Query Execution Time:' . $this->query_time);
$this->dump_slow_queries($sql);
// This is some heavy duty debugging, leave commented out unless you need this:
/*
$bt = debug_backtrace();
for ( $i = count($bt) ; $i-- ; $i > 0 ) {
if ( strpos('MysqliManager.php',$bt[$i]['file']) === false ) {
$line = $bt[$i];
}
}
$GLOBALS['log']->fatal("${line['file']}:${line['line']} ${line['function']} \nQuery: $sql\n");
*/
if ($keepResult) {
$this->lastResult = $result;
}
$this->checkError($msg . ' Query Failed: ' . $sql, $dieOnError);
return $result;
}
/**
* Returns the number of rows affected by the last query
*
* @return int
*/
public function getAffectedRowCount($result)
{
return mysqli_affected_rows($this->getDatabase());
}
/**
* Returns the number of rows returned by the result
*
* This function can't be reliably implemented on most DB, do not use it.
* @abstract
* @deprecated
* @param resource $result
* @return int
*/
public function getRowCount($result)
{
return mysqli_num_rows($result);
}
/**
* Disconnects from the database
*
* Also handles any cleanup needed
*/
public function disconnect()
{
if (isset($GLOBALS['log']) && !is_null($GLOBALS['log'])) {
$GLOBALS['log']->debug('Calling MySQLi::disconnect()');
}
if (!empty($this->database)) {
$this->freeResult();
if (!@mysqli_close($this->database)) {
$GLOBALS['log']->fatal('mysqli_close() failed');
}
$this->database = null;
}
}
/**
* @see DBManager::freeDbResult()
*/
protected function freeDbResult($dbResult)
{
if (!empty($dbResult)) {
mysqli_free_result($dbResult);
}
}
/**
* @see DBManager::getFieldsArray()
*/
public function getFieldsArray($result, $make_lower_case = false)
{
$field_array = array();
if (!isset($result) || empty($result)) {
return 0;
}
$i = 0;
while ($i < mysqli_num_fields($result)) {
$meta = mysqli_fetch_field_direct($result, $i);
if (!$meta) {
return 0;
}
if ($make_lower_case == true) {
$meta->name = strtolower($meta->name);
}
$field_array[] = $meta->name;
$i++;
}
return $field_array;
}
/**
* @see DBManager::fetchRow()
*/
public function fetchRow($result)
{
if (empty($result)) {
return false;
}
$row = mysqli_fetch_assoc($result);
if ($row == null) {
$row = false;
} //Make sure MySQLi driver results are consistent with other database drivers
return $row;
}
/**
* @see DBManager::quote()
*/
public function quote($string)
{
return mysqli_real_escape_string($this->getDatabase(), $this->quoteInternal($string));
}
/**
* @see DBManager::connect()
*/
public function connect(array $configOptions = null, $dieOnError = false)
{
global $sugar_config;
if (is_null($configOptions)) {
$configOptions = $sugar_config['dbconfig'];
}
if (!isset($this->database)) {
//mysqli connector has a separate parameter for port.. We need to separate it out from the host name
$dbhost = $configOptions['db_host_name'];
$dbport = isset($configOptions['db_port']) ? ($configOptions['db_port'] == '' ? null : $configOptions['db_port']) : null;
$pos = strpos($configOptions['db_host_name'], ':');
if ($pos !== false) {
$dbhost = substr($configOptions['db_host_name'], 0, $pos);
$dbport = substr($configOptions['db_host_name'], $pos + 1);
}
$this->database = @mysqli_connect(
$dbhost,
$configOptions['db_user_name'],
$configOptions['db_password'],
isset($configOptions['db_name']) ? $configOptions['db_name'] : '',
$dbport
);
if (empty($this->database)) {
$GLOBALS['log']->fatal("Could not connect to DB server " . $dbhost . " as " . $configOptions['db_user_name'] . ". port " . $dbport . ": " . mysqli_connect_error());
if ($dieOnError) {
if (isset($GLOBALS['app_strings']['ERR_NO_DB'])) {
sugar_die($GLOBALS['app_strings']['ERR_NO_DB']);
} else {
sugar_die("Could not connect to the database. Please refer to suitecrm.log for details (2).");
}
} else {
return false;
}
}
}
if (!empty($configOptions['db_name']) && !@mysqli_select_db($this->database, $configOptions['db_name'])) {
$GLOBALS['log']->fatal("Unable to select database {$configOptions['db_name']}: " . mysqli_connect_error());
if ($dieOnError) {
if (isset($GLOBALS['app_strings']['ERR_NO_DB'])) {
sugar_die($GLOBALS['app_strings']['ERR_NO_DB']);
} else {
sugar_die("Could not connect to the database. Please refer to suitecrm.log for details (2).");
}
} else {
return false;
}
}
$collation = $this->getOption('collation');
$charset = $this->getCharset();
if (!empty($collation) && !empty($charset)) {
$names = 'SET NAMES ' . $this->quoted($charset) . ' COLLATE ' . $this->quoted($collation);
mysqli_query($this->database, $names);
}
if (!empty($charset)) {
mysqli_set_charset($this->database, $charset);
}
// https://github.com/salesagility/SuiteCRM/issues/7107
// MySQL 5.7 is stricter regarding missing values in SQL statements and makes some tests fail.
// Remove STRICT_TRANS_TABLES from sql_mode so we get the old behaviour again.
mysqli_query($this->database, "SET SESSION sql_mode=(SELECT REPLACE(@@sql_mode, 'STRICT_TRANS_TABLES', ''))");
if ($this->checkError('Could Not Connect', $dieOnError)) {
$GLOBALS['log']->info("connected to db");
}
$this->connectOptions = $configOptions;
return true;
}
/**
* (non-PHPdoc)
* @see MysqlManager::lastDbError()
*/
public function lastDbError()
{
if ($this->database) {
if (mysqli_errno($this->database)) {
return "MySQL error " . mysqli_errno($this->database) . ": " . mysqli_error($this->database);
}
} else {
$err = mysqli_connect_error();
if ($err) {
return $err;
}
}
return false;
}
public function getDbInfo()
{
$charsets = $this->getCharsetInfo();
$charset_str = array();
foreach ($charsets as $name => $value) {
$charset_str[] = "$name = $value";
}
return array(
"MySQLi Version" => @mysqli_get_client_info(),
"MySQLi Host Info" => @mysqli_get_host_info($this->database),
"MySQLi Server Info" => @mysqli_get_server_info($this->database),
"MySQLi Client Encoding" => @mysqli_character_set_name($this->database),
"MySQL Character Set Settings" => implode(", ", $charset_str),
);
}
/**
* Select database
* @param string $dbname
*/
protected function selectDb($dbname)
{
return mysqli_select_db($this->getDatabase(), $dbname);
}
/**
* Check if this driver can be used
* @return bool
*/
public function valid()
{
return function_exists("mysqli_connect") && empty($GLOBALS['sugar_config']['mysqli_disabled']);
}
}

View file

@ -0,0 +1,636 @@
<?php
/**
*
* SugarCRM Community Edition is a customer relationship management program developed by
* SugarCRM, Inc. Copyright (C) 2004-2013 SugarCRM Inc.
*
* SuiteCRM is an extension to SugarCRM Community Edition developed by SalesAgility Ltd.
* Copyright (C) 2011 - 2018 SalesAgility Ltd.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by the
* Free Software Foundation with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
* OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along with
* this program; if not, see http://www.gnu.org/licenses or write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
* You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
* SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "Powered by
* SugarCRM" logo and "Supercharged by SuiteCRM" logo. If the display of the logos is not
* reasonably feasible for technical reasons, the Appropriate Legal Notices must
* display the words "Powered by SugarCRM" and "Supercharged by SuiteCRM".
*/
if (!defined('sugarEntry') || !sugarEntry) {
die('Not A Valid Entry Point');
}
/*********************************************************************************
* Description: This file handles the Data base functionality for the application.
* It acts as the DB abstraction layer for the application. It depends on helper classes
* which generate the necessary SQL. This sql is then passed to PEAR DB classes.
* The helper class is chosen in DBManagerFactory, which is driven by 'db_type' in 'dbconfig' under config.php.
*
* All the functions in this class will work with any bean which implements the meta interface.
* The passed bean is passed to helper class which uses these functions to generate correct sql.
*
* The meta interface has the following functions:
* getTableName() Returns table name of the object.
* getFieldDefinitions() Returns a collection of field definitions in order.
* getFieldDefintion(name) Return field definition for the field.
* getFieldValue(name) Returns the value of the field identified by name.
* If the field is not set, the function will return boolean FALSE.
* getPrimaryFieldDefinition() Returns the field definition for primary key
*
* The field definition is an array with the following keys:
*
* name This represents name of the field. This is a required field.
* type This represents type of the field. This is a required field and valid values are:
* int
* long
* varchar
* text
* date
* datetime
* double
* float
* uint
* ulong
* time
* short
* enum
* length This is used only when the type is varchar and denotes the length of the string.
* The max value is 255.
* enumvals This is a list of valid values for an enum separated by "|".
* It is used only if the type is ?enum?;
* required This field dictates whether it is a required value.
* The default value is ?FALSE?.
* isPrimary This field identifies the primary key of the table.
* If none of the fields have this flag set to ?TRUE?,
* the first field definition is assume to be the primary key.
* Default value for this field is ?FALSE?.
* default This field sets the default value for the field definition.
*
*
* Portions created by SugarCRM are Copyright (C) SugarCRM, Inc.
* All Rights Reserved.
* Contributor(s): ______________________________________..
********************************************************************************/
include_once('include/database/MssqlManager.php');
/**
* SQL Server (sqlsrv) manager
*/
class SqlsrvManager extends MssqlManager
{
public $dbName = 'SQL Server';
public $variant = 'sqlsrv';
public $priority = 10;
public $label = 'LBL_MSSQL_SQLSRV';
protected $capabilities = array(
"affected_rows" => true,
'fulltext' => true,
'limit_subquery' => true,
'create_user' => true,
"create_db" => true,
);
protected $type_map = array(
'int' => 'int',
'double' => 'float',
'float' => 'float',
'uint' => 'int',
'ulong' => 'int',
'long' => 'bigint',
'short' => 'smallint',
'varchar' => 'nvarchar',
'text' => 'nvarchar(max)',
'longtext' => 'nvarchar(max)',
'date' => 'datetime',
'enum' => 'nvarchar',
'relate' => 'nvarchar',
'multienum' => 'nvarchar(max)',
'html' => 'nvarchar(max)',
'emailbody' => 'nvarchar(max)',
'longhtml' => 'nvarchar(max)',
'datetime' => 'datetime',
'datetimecombo' => 'datetime',
'time' => 'datetime',
'bool' => 'bit',
'tinyint' => 'tinyint',
'char' => 'char',
'blob' => 'nvarchar(max)',
'longblob' => 'nvarchar(max)',
'currency' => 'decimal(26,6)',
'decimal' => 'decimal',
'decimal2' => 'decimal',
'id' => 'varchar(36)',
'url' => 'nvarchar',
'encrypt' => 'nvarchar',
'file' => 'nvarchar',
'decimal_tpl' => 'decimal(%d, %d)',
);
/**
* @see DBManager::connect()
*/
public function connect(array $configOptions = null, $dieOnError = false)
{
global $sugar_config;
if (is_null($configOptions)) {
$configOptions = $sugar_config['dbconfig'];
}
//set the connections parameters
$connect_param = '';
$configOptions['db_host_instance'] = trim($configOptions['db_host_instance']);
if (empty($configOptions['db_host_instance'])) {
$connect_param = $configOptions['db_host_name'];
} else {
$connect_param = $configOptions['db_host_name'] . "\\" . $configOptions['db_host_instance'];
}
/*
* Don't try to specifically use a persistent connection
* since the driver will handle that for us
*/
$options = array(
"UID" => $configOptions['db_user_name'],
"PWD" => $configOptions['db_password'],
"CharacterSet" => "UTF-8",
"ReturnDatesAsStrings" => true,
"MultipleActiveResultSets" => true,
);
if (!empty($configOptions['db_name'])) {
$options["Database"] = $configOptions['db_name'];
}
$this->database = sqlsrv_connect($connect_param, $options);
if (empty($this->database)) {
$GLOBALS['log']->fatal("Could not connect to server " . $configOptions['db_host_name'] . " as " . $configOptions['db_user_name'] . ".");
if ($dieOnError) {
if (isset($GLOBALS['app_strings']['ERR_NO_DB'])) {
sugar_die($GLOBALS['app_strings']['ERR_NO_DB']);
} else {
sugar_die("Could not connect to the database. Please refer to suitecrm.log for details (4).");
}
} else {
return false;
}
}
if ($this->checkError('Could Not Connect:', $dieOnError)) {
$GLOBALS['log']->info("connected to db");
}
sqlsrv_query($this->database, 'SET DATEFORMAT mdy');
$this->connectOptions = $configOptions;
$GLOBALS['log']->info("Connect:" . $this->database);
return true;
}
/**
* @see DBManager::query()
*/
public function query($sql, $dieOnError = false, $msg = '', $suppress = false, $keepResult = false)
{
if (is_array($sql)) {
return $this->queryArray($sql, $dieOnError, $msg, $suppress);
}
$sql = $this->_appendN($sql);
$this->countQuery($sql);
$GLOBALS['log']->info('Query:' . $sql);
$this->checkConnection();
$this->query_time = microtime(true);
$result = $suppress ? @sqlsrv_query($this->database, $sql) : sqlsrv_query($this->database, $sql);
$this->query_time = microtime(true) - $this->query_time;
$GLOBALS['log']->info('Query Execution Time:' . $this->query_time);
$this->checkError($msg . ' Query Failed:' . $sql . '::', $dieOnError);
//suppress non error messages
sqlsrv_configure('WarningsReturnAsErrors', false);
return $result;
}
/**
* @see DBManager::getFieldsArray()
*/
public function getFieldsArray($result, $make_lower_case = false)
{
$field_array = array();
if (!$result) {
return false;
}
foreach (sqlsrv_field_metadata($result) as $fieldMetadata) {
$key = $fieldMetadata['Name'];
if ($make_lower_case == true) {
$key = strtolower($key);
}
$field_array[] = $key;
}
return $field_array;
}
/**
* @see DBManager::fetchRow()
*/
public function fetchRow($result)
{
if (empty($result)) {
return false;
}
$row = sqlsrv_fetch_array($result, SQLSRV_FETCH_ASSOC);
if (empty($row)) {
return false;
}
foreach ($row as $key => $column) {
// MSSQL returns a space " " when a varchar column is empty ("") and not null.
// We need to strip empty spaces
// notice we only strip if one space is returned. we do not want to strip
// strings with intentional spaces (" foo ")
if (!empty($column) && $column == " ") {
$row[$key] = '';
}
}
return $row;
}
/**
* @see DBManager::convert()
*/
public function convert($string, $type, array $additional_parameters = array())
{
if ($type == 'datetime') { // see http://msdn.microsoft.com/en-us/library/ms187928.aspx for details
return "CONVERT(datetime,$string,120)";
} else {
return parent::convert($string, $type, $additional_parameters);
}
}
/**
* Compares two vardefs. Overriding 39098 due to bug: 39098 . IN 6.0 we changed the id columns to dbType = 'id'
* for example emails_beans. In 554 the field email_id was nvarchar but in 6.0 since it id dbType = 'id' we would want to alter
* it to varchar. This code will prevent it.
*
* @param array $fielddef1
* @param array $fielddef2
* @return bool true if they match, false if they don't
*/
public function compareVarDefs($fielddef1, $fielddef2, $ignoreName = false)
{
if ((isset($fielddef2['dbType']) && $fielddef2['dbType'] == 'id') || preg_match(
'/(_id$|^id$)/',
$fielddef2['name']
)
) {
if (isset($fielddef1['type']) && isset($fielddef2['type'])) {
$fielddef2['type'] = $fielddef1['type'];
}
}
return parent::compareVarDefs($fielddef1, $fielddef2);
}
/**
* Disconnects from the database
*
* Also handles any cleanup needed
*/
public function disconnect()
{
$GLOBALS['log']->debug('Calling Mssql::disconnect()');
if (!empty($this->database)) {
$this->freeResult();
sqlsrv_close($this->database);
$this->database = null;
}
}
/**
* @see DBManager::freeDbResult()
*/
protected function freeDbResult($dbResult)
{
if (!empty($dbResult)) {
sqlsrv_free_stmt($dbResult);
}
}
/**
* Detect if no clustered index has been created for a table; if none created then just pick the first index and make it that
*/
public function getConstraintSql($indices, $table)
{
if ($this->doesTableHaveAClusteredIndexDefined($table)) {
return parent::getConstraintSql($indices, $table);
}
// check to see if one of the passed in indices is a primary one; if so we can bail as well
foreach ($indices as $index) {
if ($index['type'] == 'primary') {
return parent::getConstraintSql($indices, $table);
}
}
// Change the first index listed to be a clustered one instead ( so we have at least one for the table )
if (isset($indices[0])) {
$indices[0]['type'] = 'clustered';
}
return parent::getConstraintSql($indices, $table);
}
/**
* @see DBManager::get_columns()
*/
public function get_columns($tablename)
{
//find all unique indexes and primary keys.
$result = $this->query("sp_columns_90 $tablename");
$columns = array();
while (($row = $this->fetchByAssoc($result)) != null) {
$column_name = strtolower($row['COLUMN_NAME']);
$columns[$column_name]['name'] = $column_name;
$columns[$column_name]['type'] = strtolower($row['TYPE_NAME']);
if ($row['TYPE_NAME'] == 'decimal') {
$columns[$column_name]['len'] = strtolower($row['PRECISION']);
$columns[$column_name]['len'] .= ',' . strtolower($row['SCALE']);
} elseif (in_array($row['TYPE_NAME'], array('nchar', 'nvarchar'))) {
$columns[$column_name]['len'] = strtolower($row['PRECISION']);
if ($row['TYPE_NAME'] == 'nvarchar' && $row['PRECISION'] == '0') {
$columns[$column_name]['len'] = 'max';
}
} elseif (!in_array($row['TYPE_NAME'], array('datetime', 'text'))) {
$columns[$column_name]['len'] = strtolower($row['LENGTH']);
}
if (stristr($row['TYPE_NAME'], 'identity')) {
$columns[$column_name]['auto_increment'] = '1';
$columns[$column_name]['type'] = str_replace(' identity', '', strtolower($row['TYPE_NAME']));
}
if (!empty($row['IS_NULLABLE']) && $row['IS_NULLABLE'] == 'NO' && (empty($row['KEY']) || !stristr(
$row['KEY'],
'PRI'
))
) {
$columns[strtolower($row['COLUMN_NAME'])]['required'] = 'true';
}
$column_def = 1;
if (strtolower($tablename) == 'relationships') {
$column_def = $this->getOne("select cdefault from syscolumns where id = object_id('relationships') and name = '$column_name'");
}
if ($column_def != 0 && ($row['COLUMN_DEF'] != null)) { // NOTE Not using !empty as an empty string may be a viable default value.
$matches = array();
$row['COLUMN_DEF'] = html_entity_decode($row['COLUMN_DEF'], ENT_QUOTES);
if (preg_match('/\([\(|\'](.*)[\)|\']\)/i', $row['COLUMN_DEF'], $matches)) {
$columns[$column_name]['default'] = $matches[1];
} elseif (preg_match('/\(N\'(.*)\'\)/i', $row['COLUMN_DEF'], $matches)) {
$columns[$column_name]['default'] = $matches[1];
} else {
$columns[$column_name]['default'] = $row['COLUMN_DEF'];
}
}
}
return $columns;
}
/**
* protected function to return true if the given tablename has any clustered indexes defined.
*
* @param string $tableName
* @return bool
*/
protected function doesTableHaveAClusteredIndexDefined($tableName)
{
$query = <<<EOSQL
SELECT IST.TABLE_NAME
FROM INFORMATION_SCHEMA.TABLES IST
WHERE objectProperty(object_id(IST.TABLE_NAME), 'IsUserTable') = 1
AND objectProperty(object_id(IST.TABLE_NAME), 'TableHasClustIndex') = 1
AND IST.TABLE_NAME = '{$tableName}'
EOSQL;
$result = $this->getOne($query);
if (!$result) {
return false;
}
return true;
}
/**
* protected function to return true if the given tablename has any fulltext indexes defined.
*
* @param string $tableName
* @return bool
*/
protected function doesTableHaveAFulltextIndexDefined($tableName)
{
$query = <<<EOSQL
SELECT 1
FROM sys.fulltext_indexes i
JOIN sys.objects o ON i.object_id = o.object_id
WHERE o.name = '{$tableName}'
EOSQL;
$result = $this->getOne($query);
if (!$result) {
return false;
}
return true;
}
/**
* Override method to add support for detecting and dropping fulltext indices.
*
* @see DBManager::changeColumnSQL()
* @see MssqlHelper::changeColumnSQL()
*/
protected function changeColumnSQL($tablename, $fieldDefs, $action, $ignoreRequired = false)
{
$sql = '';
if ($action == 'drop' && $this->doesTableHaveAFulltextIndexDefined($tablename)) {
$sql .= "DROP FULLTEXT INDEX ON {$tablename}";
}
$sql .= parent::changeColumnSQL($tablename, $fieldDefs, $action, $ignoreRequired);
return $sql;
}
/**
* Truncate table
* @param $name
* @return string
*/
public function truncateTableSQL($name)
{
return "TRUNCATE TABLE $name";
}
/**
* (non-PHPdoc)
* @see DBManager::lastDbError()
*/
public function lastDbError()
{
$errors = sqlsrv_errors(SQLSRV_ERR_ERRORS);
if (empty($errors)) {
return false;
}
global $app_strings;
if (empty($app_strings)
or !isset($app_strings['ERR_MSSQL_DB_CONTEXT'])
or !isset($app_strings['ERR_MSSQL_WARNING'])
) {
//ignore the message from sql-server if $app_strings array is empty. This will happen
//only if connection if made before languge is set.
return false;
}
$messages = array();
foreach ($errors as $error) {
$sqlmsg = $error['message'];
$sqlpos = strpos($sqlmsg, 'Changed database context to');
$sqlpos2 = strpos($sqlmsg, 'Warning:');
$sqlpos3 = strpos($sqlmsg, 'Checking identity information:');
if ($sqlpos !== false || $sqlpos2 !== false || $sqlpos3 !== false) {
continue;
}
$sqlpos = strpos($sqlmsg, $app_strings['ERR_MSSQL_DB_CONTEXT']);
$sqlpos2 = strpos($sqlmsg, $app_strings['ERR_MSSQL_WARNING']);
if ($sqlpos !== false || $sqlpos2 !== false) {
continue;
}
$messages[] = $sqlmsg;
}
if (!empty($messages)) {
return implode("\n", $messages);
}
return false;
}
/**
* (non-PHPdoc)
* @see DBManager::getDbInfo()
* @return array
*/
public function getDbInfo()
{
$info = array_merge(sqlsrv_client_info(), sqlsrv_server_info());
return $info;
}
/**
* Execute data manipulation statement, then roll it back
* @param $type
* @param $table
* @param $query
* @return string
*/
protected function verifyGenericQueryRollback($type, $table, $query)
{
$this->log->debug("verifying $type statement");
if (!sqlsrv_begin_transaction($this->database)) {
return "Failed to create transaction";
}
$this->query($query, false);
$error = $this->lastError();
sqlsrv_rollback($this->database);
return $error;
}
/**
* Tests an INSERT INTO query
* @param string table The table name to get DDL
* @param string query The query to test.
* @return string Non-empty if error found
*/
public function verifyInsertInto($table, $query)
{
return $this->verifyGenericQueryRollback("INSERT", $table, $query);
}
/**
* Tests an UPDATE query
* @param string table The table name to get DDL
* @param string query The query to test.
* @return string Non-empty if error found
*/
public function verifyUpdate($table, $query)
{
return $this->verifyGenericQueryRollback("UPDATE", $table, $query);
}
/**
* Tests an DELETE FROM query
* @param string table The table name to get DDL
* @param string query The query to test.
* @return string Non-empty if error found
*/
public function verifyDeleteFrom($table, $query)
{
return $this->verifyGenericQueryRollback("DELETE", $table, $query);
}
/**
* Select database
* @param string $dbname
*/
protected function selectDb($dbname)
{
return $this->query("USE " . $this->quoted($dbname));
}
/**
* Check if this driver can be used
* @return bool
*/
public function valid()
{
return function_exists("sqlsrv_connect");
}
}