PHP 5.4.14
SQL Server 2012/SQL Client 2012
Windows 2008 R2
I have a function (simplified version follows) that I call to run a SQL query. It works correctly: connects to DB; runs query and obtains a valid resource. The problem is that the resource that gets returned is null...
function squeal($query) {
$serverName = "XXXXXXXX\SQLEXPRESS";
$uid = "private";
$pwd = "private";
$connectionInfo = array( "UID"=>$uid, "PWD"=>$pwd, "Database"=>"DBname");
/* Connect using SQL Server Authentication. */
$conn = sqlsrv_connect( $serverName, $connectionInfo);
if( $conn === false ) {
echo "Unable to connect.</br>";
die( print_r(sqlsrv_errors(), true));
}
/* Run query */
$result = sqlsrv_query( $conn, $query, array(), array("Scrollable"=>"buffered"));
if( $result === false ) {
echo "Error in executing query.</br>";
die( print_r(sqlsrv_errors(), true));
}
/* check resource exists for debug (still fails without these lines) */
echo("Resource=".intval($result)."</br>");
echo("Has rows=".sqlsrv_has_rows($result)."</br>");
return $result;
}
$tsql = "SELECT id FROM mytable";
$fred = squeal($tsql);
echo("Resource=".intval($fred)."</br>");
echo("Has rows=".sqlsrv_has_rows($fred)."</br>");
It gives the following output...
Resource=8
Has rows=1
Resource=8
Warning: sqlsrv_has_rows(): 8 is not a valid ss_sqlsrv_stmt resource in <path> on line 85
Has rows=
SQL is working correctly and returns a valid resource. On return from the function it knows it has been passed resource #8 (in this instance) but it is empty. I use a similar method for MySQL that works perfectly. My whole intranet app relies on being able to call a function to run a query and get a resource back.
Does the resource 'die' on leaving the function in sqlsvr/ODBC? surely not.
I have spent a couple of days now scouring google for answers but can only get SQL server to work outside of a function. I would appreciate any suggestions
Cheers
I found this a fascinating problem, and while searching for a possible cause I found this topic which actually sounds plausible.
In short: you open connection resource $conn within function scope. As such it makes sense that it is destroyed when leaving that scope. All resources stemming from that connection resource as such are automatically no longer valid, so that explains why the query resource also dies upon exiting the scope. If you look at it this way it makes sense too - how can a derived resource continue to exist without its logical parent resource. We're just spoilt by PHP that it usually doesn't care about this, but in C#/Java etc. it's plausible behaviour.
If this assumption is correct, the simplest solution is to put global $conn; at the beginning of your squeal function so it cannot get out of scope. Depending on the rest of the code you could implement a more elegant (and less potentially conflicting) solution, the implementation isn't really kosher anyway that it connects within a function and then expects followups to survive outside.
Related
I have installed the latest version of CI 2.1.3
Now after running a query, I am getting a very slow response time for something very simple such as:
function Bash(){
$sql = “SELECT * FROM Contacts”;
$stmt = sqlsrv_query( $conn, $sql );
if( $stmt === false) {
die( print_r( sqlsrv_errors(), true) );
}
after querying a remote database. (Sql server 2008)
When I run this same query in a simple PHP script against the same remote database. I get results instantly.
a) Has anyone else experienced this problem with the sqlsrv drivers in codeigniter?
If so, how did you solve it?
Here is my connection string:
$db['default']['hostname'] = "xxxxx,1433";
$db['default']['username'] = "xx";
$db['default']['password'] = "xxxxxx-xx";
$db['default']['database'] = "xxxxxxxxx";
$db['default']['dbdriver'] = "sqlsrv";
$db['default']['dbprefix'] = '';
$db['default']['pconnect'] = TRUE;
$db['default']['db_debug'] = TRUE;
$db['default']['cache_on'] = TRUE;
$db['default']['cachedir'] = '';
$db['default']['char_set'] = 'utf8';
$db['default']['dbcollat'] = 'utf8_general_ci';
$db['default']['swap_pre'] = '';
$db['default']['autoinit'] = TRUE;
$db['default']['stricton'] = FALSE;
UPDATE:
I have found the following from running the profiler.
DATABASE: database QUERIES: 1 (Hide)
0.0659 select * from Contacts
Loading Time: Base Classes 0.0428
Controller Execution Time ( Welcome / AzureBash ) 58.2173
Total Execution Time 58.2602
It seems as though the query is executing in 0.06 secs but the controller is taking a minute to load.
No idea why this is happening.
Solution
The active records interface for the latest SQLSRV drivers are buggy.
So, download and overwrite the existing interface with these (overwrite your sqlsrv folder in the database folder in CI):
http://www.kaweb.co.uk/blog/mssql-server-2005-and-codeigniter/
Note: These have been tested with SQL Azure and works.
$query->num_rows(); does not work with these drivers, so I suggest you use count instead. Or create your own wrapper.
In addition date is now a date object type in your result set.
I hope this helps.
Solution 2
If for whatever reason you find a bug that makes this completely unusable. Revert back to the sqlsrv interface originally provided. You will find what is causing the problem is the way the original interface are executing the query, thus, create a database helper class; use $sql = $this->db->last_query(); to get the query you was about to execute and then within the database_helper class execute it yourself:
function MakeDbCall ($sql)
{
$serverName = "xxxxx-xxxx-xxx,1433"; //serverName\instanceName
$connectionInfo = array( "Database"=>"xxx", "UID"=>"xx", "PWD"=>"xxxxx","ConnectionPooling" => "1");
$conn = sqlsrv_connect($serverName,$connectionInfo);
$stmt = sqlsrv_query($conn, $sql);
while( $row = sqlsrv_fetch_array( $stmt, SQLSRV_FETCH_ASSOC) ) {
$result_array[] = $row;
}
return $result_array;
}
Create one for row_array.
You should be able to call this function directly, from anywhere in your app. Whilst taking advantage of the way active_records constructs your query.
Not an ideal solution, but until codeigniter sort their SQLSRV class, there is not a lot we can do.
Adding an answer to this after the answer has already been accepted because I found a different solution. I was having the same problem ... looping through the result set was very very slow. i opened system/database/drivers/sqlsrv/sqlsrv_driver.php and found the connection function. i noticed that is was using the SQLSRV_CURSOR_STATIC option. i changed this to SQLSRV_CURSOR_CLIENT_BUFFERED and my slowness problems went away. See documentation for this here:
http://msdn.microsoft.com/en-us/library/hh487160(v=sql.105).aspx
I honestly have no idea what the sql server driver for php is doing, however, given the speed up, etc i can guess that the driver might be using a cursor by default. this seems like an awful idea. i also am assuming that by choosing client_buffered the data for the query would b e read without a cursor and accessed in memory on the client as if it were a cursor. If this is the case, bad things might happen if you try to execute a query that has many many rows to read. Perhaps another option (SQLSRV_CURSOR_FORWARD?) can be used to read data without a cursor - but i'm sure the methods used to access the query will be more limited (e.g. not using result_array())
-Don
Solution
The active records interface for the latest SQLSRV drivers are buggy.
So, download and overwrite the existing interface with these (overwrite your sqlsrv folder in the database folder in CI):
http://www.kaweb.co.uk/blog/mssql-server-2005-and-codeigniter/
Note: These have been tested with SQL Azure and works.
$query->num_rows(); does not work with these drivers, so I suggest you use count instead. Or create your own wrapper.
In addition date is now a date object type in your result set.
Solution 2
If for whatever reason you find a bug that makes this completely unusable. Revert back to the sqlsrv interface originally provided. You will find what is causing the problem is the way the original interface are executing the query, thus, create a database helper class; use $sql = $this->db->last_query(); to get the query you was about to execute and then within the database_helper class execute it yourself:
function MakeDbCall ($sql)
{
$serverName = "xxxxx-xxxx-xxx,1433"; //serverName\instanceName
$connectionInfo = array( "Database"=>"xxx", "UID"=>"xx", "PWD"=>"xxxxx","ConnectionPooling" => "1");
$conn = sqlsrv_connect($serverName,$connectionInfo);
$stmt = sqlsrv_query($conn, $sql);
while( $row = sqlsrv_fetch_array( $stmt, SQLSRV_FETCH_ASSOC) ) {
$result_array[] = $row;
}
return $result_array;
}
Create one for row_array.
You should be able to call this function directly, from anywhere in your app. Whilst taking advantage of the way active_records constructs your query.
Not an ideal solution, but until codeigniter sort their SQLSRV class, there is not a lot we can do.
What is your connection string? You can specify the "network protocol" explicitly, which somtimes can affect speed.
http://www.connectionstrings.com/articles/show/define-sql-server-network-protocol
"Provider=sqloledb;Data Source=190.190.200.100,1433;Network Library=DBMSSOCN;Initial Catalog=pubs;User ID=myUsername;Password=myPassword;"
By specifying the IP address, the port number (1433) and the Network Library, you are providing a very granular connection string.
Your details may vary of course.
Alot of times, you don't need this. But I've been on a few client trips where this was the magic dust.
You might want to turn db_debug to FALSE which should save time debugging the database.
Also, would suggest to turn cache_on to FALSE and specify cachedir and use $this->db->cache_on(); for queries that are less dynamic, i.e. does not change frequently.
For speed up fetch up to 3 times please use "MultipleActiveResultSets"=>'0' in your sqlsrv_connect connection options.
Ex:
$db = sqlsrv_connect('127.0.0.1', array('Database'=>'dbname','UID'=> 'sa','PWD'=> 'pass',"CharacterSet" =>"UTF-8","ConnectionPooling" => "1"
,"MultipleActiveResultSets"=>'0'
));
I'm kinda going crazy about this problem. I can't do it myself so I need the community to help me get this thing solved. I've been spending hours on this because I didn't know where to look. I now know a possible fix but it's just messy (read on). I just need someone who knows more about this than I do.
This is my situation:
I want to use 2 or more mysql connections.
I use OOP
I have a class called dbase, it has two functions, setConnection and getConnection and two class variables called $connection and $dbaseName.
In my main project file I include dbase and create two objects:
dbase
maindbase
then I do:
$this->dbase->setConnection($server, $uname, $pword);
$this->maindbase->setConnection($server, $uname, $pword);
the setConnection function looks like this:
function setConnection ($server, $serv_Username, $serv_Password) {
$this->connection = mysql_connect($server, $serv_Username, $serv_Password, true);
// echo $this->connection . "<BR>";
}
I echo it to see the resourcenumber and added true to mysql_connect (and I know it's deprecated since 5.5, I'm not here for that).
Now, as I understand OOP, the class variables are set per object. So $connection from dbase will never be the same as maindbase (unless, of course, I use the same credentials, but even then it will create a new link because of the $new_link option I enabled). They both have different resource ID's.
My problem:
In class dbase I also have a function which runs a query like this:
$connection = $this->getConnection();
$dbase_name = $this->getDbaseName();
mysql_select_db($dbase_name, $connection);
$q = "SELECT * FROM {$table_name} WHERE {$column} LIKE '{$value}'";
$result = mysql_query($q);
Now, when I use it like this, it will ALWAYS use the FIRST $connection that has been set in class dbase and it doesn't matter which object this is, either object dbase or maindbase.
I get this error:
Table 'testdbase1.skye_domains' doesn't exist
object dbase is connected to testdbase1
object maindbase is connected to testdbase2
the above error I get when trying to select results using the maindbase object.
When I remove the $connection string from mysql_select_db it works perfectly because it will try to open a new connection as if using mysql_connect.
Why is this? This is impossible right? How can objectmaindbase have the same $connection as object dbase? They are in NO WAY connected to eachother... Is PHP somehow using a global mysql_connect variable or buffer which I'm not aware about?
I would like to keep using connectionstrings as this is just handy now and then. Leaving the $connection string out seems messy.
Does anybody have any suggestions I can try to make PHP (or my head) sane again?
Try to put echo $this->connection EVERYWHERE you use it. Also, create an "id" member and fill it with a unique value upon constructing a dbase-object and echo it along the value of $this->connection. This way you can track where what happens to your connection.
And check if there's maybe some place outside of the class that assigns $foo->connection. If you're not using "private" on the members, you're bound to have such problems when you e.g. forget to remove a hack or an experiment from unrelated parts of your code.
I've read a LOT of things about this problem, however I still cannot fix it.
In my functions file I declare a variable with a value like so:
$px_host = "localhost";
And I have a database query function like so:
function dbQuery($database, $reqquery){
if(!$connect = mysql_connect($px_host, $px_dbuser, $px_dbpass)){
exit("Error - cannot connect to MySQL server - " . mysql_error());
}
if(!$database = mysql_select_db($database)){
exit("Error - cannot select database - " . mysql_error());
}
if(!$query = mysql_query($reqquery)){
exit("Error - query error.");
}
return $query;
}
And whenever I try and run the function, I get an 'Undefined Variable' error. I've tried setting the variable to global, however it still says it's undefined. They are in the same file and the variable is defined and set before the function.
(I'm bad at explaining this so try and work round it)
The file which contains dbQuery() is included in another file. In the other file, there is a function which uses dbQuery() to get certain information. Is it possible that because it's being initially run from the first file, the variable is outside the scope?
In your specific case you should declare global within function all variables outside the function. So
function dbQuery($database, $reqquery){
global $px_host,$px_dbuser,$px_dbpass;
// rest of function
}
But your code can be improved: You should define constants at the beginning of your script
define('DB_HOST','localhost');
define('DB_USER','user');
define('DB_PASS','pass');
and use constants inside the function (so no need to declare global, and it's more logic because host, user and pass aren't variable but constants).
You should connect at database only once at the beginning of your flow so the function dbQuery execute only the query (according with the function name).
EDIT for completeness of answer:
As some users say you in other comments, I invite you to read the php doc for mysql_connect and see the red advise:
Use of this extension is discouraged. Instead, the MySQLi or PDO_MySQL extension should be used. See also MySQL: choosing an API guide and related FAQ for more information. Alternatives to this function include:
I'm not here for say you what you MUST do in your project, but read the doc and follow the tipps/suggestions is essential for the success of your project. :)
It's getting late, so this is only a partial answer.
Another approach you could take is to pass the database instance into your helper function, thus resolving the credentials issue.
function dbQuery($database, $reqquery)
{
if (false !== ($query = mysql_query($reqquery, $database))) {
exit("Error - query error.");
}
return $query;
}
Now this function receives its dependency via the arguments and is also a lot shorter and doesn't connect / query / disconnect every time.
The remaining code has to be written elsewhere; if you require a database at every page, you can write this pretty high up the chain:
if (false === ($connect = mysql_connect($px_host, $px_dbuser, $px_dbpass))) {
exit("Error - cannot connect to MySQL server - " . mysql_error());
}
if (false === mysql_select_db($database)) {
exit("Error - cannot select database - " . mysql_error());
}
Then you pass $connect around wherever it's required.
$res = dbQuery($connect, 'SELECT "hello world"');
Of course, mysql_connect and friends use implied connections, so you technically don't need to pass it around anyway; it's a good pattern nonetheless.
Last but not least
Learn how to use PDO and revel in the magical world of OOP ;-)
If you set the variable global, you will need to set it global in the function as well. In this case:
$px_host = "localhost";
function dbQuery($database, $reqquery){
global $px_host;
if(!$connect = mysql_connect($px_host, $px_dbuser, $px_dbpass)){
exit("Error - cannot connect to MySQL server - " . mysql_error());
}
if(!$database = mysql_select_db($database)){
exit("Error - cannot select database - " . mysql_error());
}
if(!$query = mysql_query($reqquery)){
exit("Error - query error.");
}
return $query;
}
This should fix this problem.
How can I check if a db-connection is open in PHP?
How can I get a reference to this connection?
How can I determine if it is MySQL, Postgres or something else?
Ideally you would know the variable the connection is saved into. Without this it becomes a hassle to find it. It means you would have to loop through every variable and test each one, but doing this for any reason can be a pain because it wont find variables outside of the current scope. You can loop the current variables using:
foreach(get_defined_vars() as $x){
// Code
}
To check if a variable is a valid connection you can use the below code, it should return an integer:
if(mysql_thread_id($connection){
// Connection exists
}
Once you know its a connection you can use the following to return the type of connection, eg, mysql.
echo get_resource_type($connection);
You'd have to iterate over every defined variable and figure out if it's a resource handle or not. Try get_defined_vars().
Probably the best solution to the problem is to not lose track of the DB connection in the first place.
Consider integrating an ORM into your project such as Doctrine or Propel, or if you want something a little more lightweight, consider a DBAL such as Zend_Db. All of these solutions encapsulate the database connections in class instances, ensuring that they are always available to your code if necessary.
In general terms, I don't think it's really possible, not at least with some sort of complicate workaround. You can, for instance, loop through all defined variables:
foreach(get_defined_vars() as $variable){
if( is_resource($variable) && get_resource_type($variable)=='mysql link' ){
// Found a connection!
var_dump($variable);
}
}
Though, in fact, a connection remains available even if assigned to a local variable out of current scope:
function foo(){
$conn = mysql_connect('localhost', 'test', 'test');
}
foo();
$res = mysql_query('SELECT NOW() AS what_is_the_time');
if( $row = mysql_fetch_assoc($res) ){
var_dump($row);
}
The problem is that PHP doesn't really care about variables when talking about connections:
// One connection and zero variables!
mysql_connect('localhost', 'test', 'test');
$res = mysql_query('SELECT NOW() AS what_is_the_time');
if( $row = mysql_fetch_assoc($res) ){
var_dump($row);
}
You can try and find information about current connection:
var_dump(mysql_thread_id());
// int(2696)
... though many of the functions will just attempt to stablish a new connection if there isn't one so must take care of warnings...:
Warning: mysql_thread_id(): Access denied for user ''#'localhost' (using password: NO)
bool(false)
... unless your configuration actually allows a succesful credential-less connnection attempt.
I'm pretty sure that, whatever you want to do, can be done in an entirely different way :)
My question is I am using the variable $db in my general script code and within one of my functions. It's purpose is to be the variable that is used for MySQL connections. I have a need inside a function to write some data to the database. In my script I cannot assume that an existing db connection will be open so I open a new one and close it before the function exits. Ever since doing this I am getting an error after the script runs saying the MySQL reference is bad / doesn't exist.
The only thing I can pin it to is in my core code I use the variable $db as the variable name for database connection. I also use the same variable in the function. I did not imagine this would be a problem because I do not use global in front of $db in the function. This should mean the $db I reference in my function is in the functions private scope but it seems to be closing the public $db's connection.
Any thoughts?
Fragments of my code are:
database.php
db_connect()
{
// open mysql db connection and return it;
}
db_close( &$db )
{
// close the passed by reference db connection
}
api.php
api_verify( $keyid, $userid, $key )
{
// open a new db connection
$db = db_connect();
// check for errors. if any errors are found note them in the db
// close the db
db_close($db);
}
main.php
include api.php;
include database.php;
// open a connection to the db
$db = db_connect();
// pull a list of things to process from the db and move through them one at a time
// call api_verify() on each key before working through it's data.
db_close($db)
To manage DB connections, you can create a class rather than a pair of functions. If where you say "MySQL reference", the exact error refers to a "MySQL resource", then you are using the outdated mysql extension and should switch to a more modern extension, such as PDO.
class DBConnection {
protected static $_connections = array(),
static connect($dsn) {
if (!isset(self::$_connections[$dsn])) {
$credentials = self::getCredentials();
/* Create connection. For example: */
try {
self::$_connections[$dsn][0] = new PDO($dsn, $credentials['username'], $credentials['password']);
} catch (PDOException $exc) {
// erase the frame w/ password from call trace to prevent leak.
throw new PDOException($exc->getMessage(), $exc->getCode());
}
/* End create connection example */
self::$_connections[$dsn][0]->dsn = $dsn;
}
++self::$_connections[$dsn]['count'];
return self::$_connections[$dsn][0];
}
static close($db) {
if (isset(self::$_connections[$db->dsn])) {
if (--(self::$_connections[$db->dsn]['count']) < 1) {
unset(self::$_connections[$db->dsn]);
}
}
}
static getCredentials() {
/* credentials can be stored in configuration file or script, in this method, or some other approach of your own devising */
}
}
Note that this isn't exactly OOP (it is, but only in a technical sense). The above doesn't lend itself well to unit testing. If you want a more OO approach (which will be more amenable to unit testing), extend or wrap PDO. Using dependency injection can also help with the coupling issues of the above.
I assume you are opening a connection to the same database with the same username/password at each of the places you call db_connect. When doing so,unless your db_connect explicitly specifies, that you are creating a new link, it will return an already opened link.If that link is then closed using db_close(), it will also close the other connection, since the link is the same. If you are using mysql_connect to connect to the database, it takes an argument called new link
new_link
If a second call is made to mysql_connect() with the same arguments, no new link will be established, but instead, the link identifier of the already opened link will be returned. The new_link parameter modifies this behavior and makes mysql_connect() always open a new link, even if mysql_connect() was called before with the same parameters. In SQL safe mode, this parameter is ignored.
Refer to http://php.net/manual/en/function.mysql-connect.php
I'm not sure if this is the issue you are facing. Hope it helps.
I would assume what is happening is the connect cancels out because there already is a connection, and then the close ends the current connection.
I would recommend either A) start a connection at the beginning of the file, and just know it's always there (what I do); or B) check the to see if the $db variable is set, if not then create the connection, and always end the connection at the end of the file.