mirror of
https://github.com/SuiteCRM/SuiteCRM-Core.git
synced 2025-08-29 08:17:18 +08:00
Squashed 'public/legacy/' content from commit 817a12dc0
git-subtree-dir: public/legacy git-subtree-split: 817a12dc0c30c189f56d5cb1f7dc37a9631bdbe3
This commit is contained in:
commit
8e4cc94994
9769 changed files with 1617695 additions and 0 deletions
4117
include/database/DBManager.php
Executable file
4117
include/database/DBManager.php
Executable file
File diff suppressed because it is too large
Load diff
283
include/database/DBManagerFactory.php
Executable file
283
include/database/DBManagerFactory.php
Executable 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;
|
||||
}
|
||||
}
|
117
include/database/FreeTDSManager.php
Executable file
117
include/database/FreeTDSManager.php
Executable 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
2132
include/database/MssqlManager.php
Executable file
File diff suppressed because it is too large
Load diff
1672
include/database/MysqlManager.php
Executable file
1672
include/database/MysqlManager.php
Executable file
File diff suppressed because it is too large
Load diff
430
include/database/MysqliManager.php
Executable file
430
include/database/MysqliManager.php
Executable 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']);
|
||||
}
|
||||
}
|
636
include/database/SqlsrvManager.php
Executable file
636
include/database/SqlsrvManager.php
Executable 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");
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue