I'm still trying to understand why in WHMCS on the same MySQL database if I try to connect to a different db, he still tries to search a table in his default database.
Example: I'm trying to connect to the table "test" in "admin_test" so he needs to search for "admin_test.test", but instead he gives me the error:
VPN Database Query Error: SQLSTATE[42S02]: Base table or view not found: 1146 Table 'admin_billing.test' doesn't exist (SQL: insert into test (user_id) values (fdffddfg))
So I think he doesn't want to change db, I tried to write this module based on the freeradius module, as it works fine and has his own db on the same localhost mysql.. I don't know how to fix this as of now.
function rscmodule_DatabaseInsert()
{
try {
$vpnsql = rscmodule_DatabaseConnect();
if (is_string($vpnsql)) {
return $vpnsql; // Error condition
}
Capsule::connection()->transaction(
function ($vpnsql)
{
/** #var \Illuminate\Database\Connection $connectionManager */
$vpnsql->table('test')->insert(
[
'user_id' => 'fdffddfg',
]
);
}
);
} catch (\Exception $e) {
return "VPN Database Query Error: " . $e->getMessage();
}
return "success";
}
function rscmodule_DatabaseConnect()
{
$pdo = null;
try {
$pdo = Capsule::getInstance()->getConnection('rscmodule');
} catch (\Exception $e) {
$config = array(
'driver' => 'mysql',
'host' => 'localhost',
'database' => 'admin_test',
'username' => 'admin_test',
'password' => '*****',
'charset' => 'utf8',
);
try {
Capsule::getInstance()->addConnection(
$config,
'rscmodule'
);
$pdo = Capsule::getInstance()->getConnection('rscmodule');
} catch (\Exception $e) {
return "Unable to connect to rscmodule Database. "
. "Please check rscmodule server configuration. "
. $e->getMessage();
}
}
if (is_object($pdo)) {
if (method_exists($pdo, 'query')) {
$ret = $pdo->query();
} else {
$processor = $pdo->getPostProcessor();
$ret = new \Illuminate\Database\Query\Builder($pdo, $pdo->getQueryGrammar(), $processor);
}
} else {
$ret = $pdo;
}
return $ret;
}
Well turns out WHMCS doesn't want other DBs inside the same connection IP, so we created a VM on a new IP and on that the connection works properly.
Related
I would like to use another database connection that queries a database using a hook in my own module called 'connector'. This hook shall take a parameter that is a query which shall be executed using the secondary database before it switches back to the primary Drupal 7-database. The problem seems to be that the query is somehow generated for the (primary) Drupal database (as it's been created before switching to the secondary db), even though the SQL itself looks fine to me. What am I doing wrong here?
Hook:
function connector_api_db($query) {
$result = [];
$database_info = array(
'database' => variable_get('connector_db_name'),
'username' => variable_get('connector_db_user'),
'password' => variable_get('connector_db_pwd'),
'host' => variable_get('connector_db_host'),
'driver' => 'mysql',
);
Database::addConnectionInfo('secondary_db', 'default', $database_info);
db_set_active('secondary_db'); // Initialize the connection
/* This actually works but I can here not use my own query as a parameter in this function. Not what I want. */
//$query = db_select('registration')->fields('registration')
//->condition('id', 46, '=');
echo $query->__toString() . "<br />"; /* Get query string */
var_dump($query->getArguments()); /* Get the arguments passed to the string */
$result = $query->execute()->fetchAssoc();
echo "<pre>";
print_r($result);
echo "</pre>";
db_set_active(); // without the paramater means set back to the default for the site
drupal_set_message(t('The queries have been made.'));
return $result;
}
Invoking the hook:
$query = db_select('registration')
->fields('registration')
->condition('id', 46, '=');
$response = module_invoke('connector', 'api_db', $query);
This results into this:
SELECT registration.* FROM {registration} registration WHERE (id = :db_condition_placeholder_0)
array(1) { [":db_condition_placeholder_0"]=> int(46) }
Additional uncaught exception thrown while handling exception.
Original
PDOException: SQLSTATE[42S02]: Base table or view not found: 1146 Table 'drupal.registration' doesn't exist: SELECT registration.* FROM {registration} registration WHERE (id = :db_condition_placeholder_0) ; Array ( [:db_condition_placeholder_0] => 46 ) in connector_api_db() (line 70 of /<path-to-drupal>/drupal-7.61/modules/connector/connector.main.inc).
This is not an answer, strictly speaking, but rather a suggestion.
The thing is that your hook receives a $query already built with the original database connection, this explains the errors. To be effective, db_set_active() should be called before calling the Query constructor.
Otherwise, you would have to override constructors for both the Select class and its parent class Query, or more precisely "reconstruct" the Query builder instance for Select statements (and probably others too if you need range query etc.).
Also, unless having explicit requirements to provide a dedicated hook in this situation, it is not necessary.
For example it would be simpler to do something like this :
connector.module :
/**
* Implements hook_init()
*
* Adds secondary database connection information.
*/
function connector_init() {
$database_info = array(
'database' => variable_get('connector_db_name'),
'username' => variable_get('connector_db_user'),
'password' => variable_get('connector_db_pwd'),
'host' => variable_get('connector_db_host'),
'driver' => 'mysql',
);
Database::addConnectionInfo('secondary_db', 'default', $database_info);
}
/**
* Switches from primary to secondary database and the other way round.
*/
function connector_switch_db($db_key = 'secondary_db') {
return db_set_active($db_key);
}
Example :
// Set secondary db active.
$primary_db_key = connector_switch_db();
$query = db_select('registration')
->fields('registration')
->condition('id', 46, '=');
$result = $query->execute()->fetchAssoc();
// Switch back to the primary database.
connector_switch_db($primary_db_key);
This softer approach also prevents hardcode like (...)->execute()->fetchAssoc() and let the implementation free to execute and fetch the results as needed.
For future reference, this is what I came up with (caching bits included):
function connector_init() {
foreach([
'connector_db_host',
'connector_db_name',
'connector_db_user',
'connector_db_pwd',
] as $var) {
drupal_static($var, variable_get($var, ''));
}
Database::addConnectionInfo('secondary_db', 'default', [
'host' => drupal_static('connector_db_host'),
'database' => drupal_static('connector_db_name'),
'username' => drupal_static('connector_db_user'),
'password' => drupal_static('connector_db_pwd'),
'driver' => 'mysql',
]);
}
function connector_api_db($query) {
$result = [];
$sql_query = $query->__toString();
$arguments = $query->getArguments();
if (count($arguments) > 0) {
ksort($arguments);
}
$cache_id = 'sql_' . md5($sql_query . '|' . print_r($arguments, 1));
$cache = cache_get($cache_id, 'cache_connector');
if ($cache && $cache->expire >= time()) {
$result = $cache->data;
} else {
db_set_active('secondary_db'); // Switch to secondary db */
$query_failed = FALSE;
try {
$db_result = db_query($sql_query, $arguments);
$db_result = (0 === $db_result->rowCount()) ? [] : $db_result->fetchAssoc();
} catch (\PDOException $e) {
$db_result = [];
$query_failed = TRUE;
} finally {
db_set_active(); /* switch back to default */
}
$result = (object)[
'query' => $sql_query,
'arguments' => $arguments,
'result' => (object)$db_result
];
if (!$query_failed) {
cache_set($cache_id, $result, 'cache_connector', strtotime('+10 minutes'));
}
}
return $result;
}
In my project, I use redis.
And I have a init file including ip port and port, so class Datasource is used for analying init file and connecting redis.
Here is class Datasource.php code with function getRedis() in it:
namespace common;
class Datasource {
public function __construct() {}
public static function getRedis($config_name = NULL, $server_region = 'default') {
global $config;
$redis_config = $config['redis'][$config_name];
if ($config_name && $redis_config && $server_region) {
$this->_config_name = $config_name;
$this->_redis_config = $redis_config;
$this->_server_region = $server_region;
try {
$this->_redis = new \Redis();
$this->_redis->connect($this->_redis_config[$server_region]['host'], $this->_redis_config[$server_region]['port']);
if($this->_redis_config[$server_region]['password'] && !$this->_redis->auth($this->_redis_config[$server_region]['password'])) {
$this->_redis = null;
}
} catch (Exception $e) {
$this->_redis = null;
}
} else {
$this->_redis = null;
}
return self::$this->_redis;
}
}// end of class Datasource
Here is init file code of redis.ini.php
<?php
$config['redis']['instance1'] = array(
'default' => array(
'host' => '127.0.0.1',
'port' => '6379',
'timeout' => 5,
'pconnect' => 1,
'password' => '',
)
);
$config['redis']['instance2'] = array(
'default' => array(
'host' => '127.0.0.1',
'port' => '6379',
'timeout' => 5,
'pconnect' => 1,
'password' => '',
)
);
Now I want to get xie value which is in redis, Here is my html code:
<body style="height:100%" >
<?php
include "o1ws1v/class/common/Datasource.php";
include 'o1ws1v/conf/redis.ini.php';
$redis_obj = common\Datasource::getRedis('instance1');
$value = $redis_obj->get("xie");
echo "get key xie is:".$value."\n";
?>
</body>
Actually, key xie should be zuo. The corrent result is a line : "get key xie is:zuo"
But it showed nothing, Who can help me?
You use $this in a static method, and you can't. Additionaly, you catch all Exceptions while connecting to Redis, so you can't know why it wasn't unsuccessful. You need to do two things:
Turning on PHP errors (for development only of course)
Don't catch the Exceptions while connecting, and if you do - then log / keep the Exception's message.
Try something like this:
<?php
namespace common;
class Datasource
{
private static $_redis, $_config_name, $_redis_config, $_server_region;
public static function getRedis($config_name = NULL, $server_region = 'default')
{
error_reporting(E_ALL);
ini_set("display_errors", true);
global $config;
$redis_config = $config['redis'][$config_name];
if (!$config_name || !$redis_config || !$server_region) {
throw new \Exception('$config_name or $redis_config or $server_region is not set');
}
if (!$redis_config[$server_region]['password']) {
throw new \Exception('Redis password is not set');
}
self::$_config_name = $config_name;
self::$_redis_config = $redis_config;
self::$_server_region = $server_region;
self::$_redis = new \Redis();
self::$_redis->connect(self::$_redis_config[$server_region]['host'], self::$_redis_config[$server_region]['port']);
if (!self::$_redis->auth(self::$_redis_config[$server_region]['password'])) {
throw new \Exception("Can't login to Redis. Check password");
}
return self::$_redis;
}
}
And the error displaying code is obviously not belong here, it's just for you to see if there are any errors temporarly.
Also, I would add a condition to see if Redis is already set, then return the connection. Otherwise you will make another connection every time you call getRedis method. Something like this:
public static function getRedis(...)
{
if (!self::$_redis) {
...
}
return self::$_redis;
}
First, I have wrapper Datasource class for connecting redis server.
And I have a init file of ip port and passwd etc;
The content redis.ini.php file is:
<?php
$config['redis']['instance1'] = array(
'default' => array(
'host' => '127.0.0.1',
'port' => '6379',
'timeout' => 5,
'pconnect' => 1,
'password' => '',
)
);
$config['redis']['instance2'] = array(
'default' => array(
'host' => '127.0.0.1',
'port' => '6379',
'timeout' => 5,
'pconnect' => 1,
'password' => '',
)
);
And the code of class Datasource.php is:
<?php
namespace common;
class Datasource {
public static $config_name;
public static $server_region;
public static $redis_config;
public function __construct() {}
public static function getRedis($config_name = NULL, $server_region = 'default') {
self::$config_name=$config_name;
self::$server_region=$server_region;
global $config;
self::$redis_config = $config['redis'][$config_name];
if (self::$config_name && self::$redis_config && self::$server_region) {
try {
self::$redis = new \Redis();
self::$redis->connect(self::$redis_config[$server_region]['host'], self::$redis_config[$server_region]['port']);
} catch (Exception $e) {
self::$redis = null;
}
} else {
self::$redis = null;
}
return self::$redis_config[$server_region]['host'] ;
}
}
Now, I want to use this class in html code:
<body style="height:100%" >
<?php
include "o1ws1v/class/common/Datasource.php";
include 'o1ws1v/conf/redis.ini.php';
$redis_obj = common\Datasource::getRedis('instance1');
echo $redis_obj;
?>
</body>
But unlucky, I can not get corrent value:127.0.0.1 in html .
I have found that the problem was try{}catch{}, when i delete these code, it work fine.
//delete these code, it works fine
try {
self::$redis = new \Redis();
self::$redis->connect(self::$redis_config[$server_region]['host'], self::$redis_config[$server_region]['port']);
} catch (Exception $e) {
self::$redis = null;
}
I have asked one question one hour ago in stackoverflow, sorry about one more question. My boss claim me to solve this question today.
I have defined my redis server with no password for logging. It seems nothing wrong for connecting redis server, Who can help me?
I have solved this porblem
self::$redis = new \Redis();// it is wrong
$redis=new \Redis();//it is right
I am creating an app in zend where the user will enter the host, database name, username and password to connect the database and will able to retrieve the tables and columns in the tables also to start some work..
I am using this code to get the tables in a database:
$ExternalDb = new Zend_Db_Adapter_Pdo_Mysql(array(
'host' => $host,
'username' => $user,
'password' => $pass,
'dbname' => $dbName
));
try{
//Create connection
echo $ExternalDb->getConnection()->getAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY);
foreach($ExternalDb->listTables() as $table){
//$table = new $table(array('db' => $ExternalDb)); it doesn't work
echo "<pre>";
print_r($table);
//$cols = $table->info(Zend_Db_Table_Abstract::COLS); It doesn't work
echo "</pre>";
}
} catch (Exception $ex) {
echo $ex;
}
I am able to get the table names but I am trying to get the column names also...
Thanks in advance!
You can retrieve information from Zend_Db_Table_Abstract.
Create a new class like this:
class foo extends Zend_Db_Table_Abstract
{
}
In your code:
foreach($ExternalDb->listTables() as $table){
$dbTable = new foo($ExternalDb);
$dbTable->setOptions(array($dbTable::NAME => $table));
var_dump($dbTable->info($dbTable::COLS));
unset($dbTable);
}
Another way without Zend_Db_Table_Abstract, directly with Zend_Db_Adapter, you can use describeTable() method:
foreach($ExternalDb->listTables() as $table){
$describ = $ExternalDb->describeTable($table);
foreach($describ as $col_name => $col_desc){
var_dump($col_name);
}
}
I am trying to understand how to use Zend_DB in my program but I have some problems. The class below (DatabaseService) works when I pass it a simple query. However, if I pass it a query with a join clause my page just hangs and no error is returned. I cut and paste the qry in a query browser and it is valid
Any help would be great
$SQL = "select name from mytable"
$db = new DatabaseService($dbinfo)
$db ->fetchall($SQL ) // works
-----------------------------------------------------------
$SQL = "select count(*) as cnt from EndPoints join CallID on EndPoints.`CallID` = CallID.CallID where EndPoints.LastRegister >= '2010-04-21 00:00:01' and EndPoints.LastRegister <= '2010-04-21 23:59:59' "
$db = new DatabaseService($dbinfo)
$db ->fetchall($SQL ) // DOES NO WORK
class DatabaseService
{
function DatabaseService($dbinfo,$dbname="")
{
try
{
$dbConfig = array(
'host' => $this->host,
'username' => $this->username,
'password' => $password,
'dbname' => $this->dbname );
$this->db = Zend_Db::factory($this->adapter, $dbConfig);
Zend_Db_Table::setDefaultAdapter($this->db);
}
catch(Zend_Exception $e)
{
$this->error = $e->getMessage();
Helper::log($this->error);
return false;
}
}
public function connnect()
{
if($this->db !=null)
{
try
{
$this->db->getConnection();
return true;
}
catch (Zend_Exception $e)
{
$err = "FAILED ::".$e->getMessage()." <br />";
}
}
return false;
}
public function fetchall($sql)
{
$res= $this->db->fetchAll($sql);
return $res;
}
}
I can't see why that wouldn't work. It could be a bug in a particular release of ZF but as far as I can tell there are no SQL syntax errors. What you could do is Bootstrap the Zend_Db class somewhere in your system like in the index.php file just as you were doing in your DatabaseService class:
$dbConfig = array(
'host' => 'hostname',
'username' => 'username',
'password' => 'password',
'dbname' => 'dbname'
);
$db = Zend_Db::factory('mysqli', $dbConfig);
$db->setFetchMode(Zend_Db::FETCH_OBJ);
Zend_Db_Table::setDefaultAdapter($db);
And then Zend Framework should handle the connection process for you. Then instead of having a DatabaseService class you just create a model for each table you need like so:
<?php
class EndPoints extends Zend_Db_Table_Abstract
{
protected $_name = 'EndPoints';
/**
* the default is 'id'. So if your table's primary key field name is 'id' you
* will not be required to set this. If your primary key is something like
* 'EndPointsID' you MUST set this.
* #var primary key field name
*/
protected $_primary = 'EndPointsID';
}
Doing this will automagically give you access to functions such as fetchRow(), fetchAll(), find(), etc. Then you can also use Zend_Db_Table_Select for your queries which can be quite useful. Like so:
<?php
$endPointsModel = new EndPoints();
$callIdCount = $endPointsModel->getCallIdCount('2010-04-21 00:00:01', '2010-04-21 00:00:01');
Then in your EndPoints model you would create that function like so:
...
public function getCallIdCount($fromDate, $toDate)
{
$cols = array('cnt' => 'count(*)');
$select = $this->select->setIntegrityCheck(false) // this is crucial
->from($this->_name, $cols)
->join('CallID', "{$this->_name}.CallID = CallID.CallID", array())
->where("{$this->_name}.LastRegister >= ?", $fromDate)
->where("{$this->_name}.LastRegister <= ?", $toDate);
// if you need to see what the whole query will look like you can do this:
// echo $select->__toString();
return $this->fetchAll($select);
{