PDO Sqlite error 5: database locked - php

I've been trying to use a sqlite database (php with PDO), but have been running into a problem. Generally the commands work, and everything is fine (including storing files), but for some reason when I run these two commands (which have been simplified), I get the error
SQLSTATE[HY000]: General error: 5 database is locked
I've tried for a while, but have been unable to fix whatever is wrong. The code is below.
Things I've done:
Tried to put sleep(2) between commands
Found out that commenting either of the commands out will cause the error not to happen (which doesn't really help, as both commands must run)
Note that (unlike other problems I saw while looking at similar questions) the database operates correctly in other cases.
$db = new MyDB();
$STH = $db->catchMistakes('SELECT PASSWORD FROM USERS WHERE USERNAME = ?', "test");
$STH->fetchColumn();
$db->catchMistakes("UPDATE ISSUES SET NAME = ? WHERE NUM = ?", ["test", "1"]);
And here's the code for MyDB
public function catchMistakes($cmd, $params = []) {
if (!is_array($params)) {
$params = [$params];
}
try {
$DBH = new PDO("sqlite:" . DB);
$DBH->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
$DBH->beginTransaction();
$query = $DBH->prepare($cmd);
$toReturn = $query->execute($params);
$DBH->commit();
return $query;
}
catch(PDOException $e) {
$DBH->rollback();
$error = $e->getMessage();
exit;
}
}
Sorry if there's a simple fix, I'm pretty new at this. Any help would be greatly appreciated.

You can use closeCursor() method on a PDOStatement object to free the connection to the database so the statement can be executed. You can refer to the PHP manual.

Related

PHP - Is this way I update and select from my database secure?

Recently, I switched to PDO and wanted to ask if is as safe as I do it or not?
(I filter the data before with much filter methods provided by php)
QUERY db:
include 'path_to_config_file_with_login_creds_for_db.php';
$options = array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_SILENT,
PDO::ATTR_PERSISTENT => false,
);
try {
$pdo = new PDO('mysql:host=' . $database_host . ';dbname=' . $database_name . ';charset=utf8mb4', $database_user, $database_pass, $options);
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
} catch (PDOException $exception) {
die("some error message");
}
try {
$statement = $pdo->prepare($sql);
$statement->execute($bindings);
$statement->closeCursor();
return $output;
} catch (PDOException $stmEx) {
die("again some error message");
}
UPDATE db:
include 'again_path_to_config_file_where_creds_to_db.php';
$options = array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_SILENT,
PDO::ATTR_PERSISTENT => false,
);
try {
$pdo = new PDO('mysql:host=' . $database_host . ';dbname=' . $database_name . ';charset=utf8mb4', $database_user, $database_pass, $options);
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
} catch (PDOException $exception) {
die("...");
}
try {
$statement = $pdo->prepare($sql);
$statement->execute($bindings);
$statement->closeCursor();
} catch (PDOException $stmEx) {
die("...");
}
Is this way safe or not?
It's all working fine but I want to know it its safe too
From the security point of view it should be safe as long as the query in $sql uses param binding properly. If the $sql variable is build like this then your code won't help you.
//DO NOT TRY THIS AT HOME
$sql = "SELECT * FROM `users` WHERE user='" . $_POST['user'] . "'";
But i can see several other issues with your code.
1) Your try ... catch blocks are useless. When you use PDO::ERRMODE_SILENT the PDO won't raise any exception. Only the $pdo->errorCode or $statement->errorCode properties will be set when error is encountered.
2) You are opening too many connections. I assume that you have the code you've shared in some function that you plan to call like queryDb($sql, $params); That means that every time you will call that function you will create new instance of PDO and open new connection. You might want to move the part that creates PDO instance into the db-config file then use that single instance everytime you are going to create new statement using $pdo->prepare().
3) The use of die. It's common practice to use die or exit when the query goes wrong in examples. But in actual application it would mean the user will see ugly empty page with single sentence saying something went wrong. It's better to throw an exception that will be handled higher in your app. That would let the application to display the page layout with menu and other things even if the requested action failed.
4) You do not set the value to $output variable in your first "Query DB" part of code. Although i'm not sure if you just left it out when copying or if you have it like this in your actual code.
In regards to SQL injection, as long as you parameterize all inputs and white list all dynamic SQL parts, you should be safe.
However, your code has another serious problem. You are silencing errors. PDO::ATTR_ERRMODE should be set to PDO::ERRMODE_EXCEPTION. But, that would leave your code even in a worse state, as you have die all over the place. Don't catch the exceptions unless you have extremely good reason to do so. Read this article https://phpdelusions.net/pdo#errors
Silencing or displaying errors to the user opens up new vectors for exploitation. That is why the best course of action is to leave them alone. In production system you should have the configuration set to never display errors. They will be securely logged on the server.

ZF2: How to get error info from Zend\Db

I am connecting to MySQL through PDO with Zend\Db from ZF2. How can I report the last errorInfo()?
Here's what I have:
$sqlWriter = new Sql($this->getAdapter());
$insert = $sqlWriter->insert('table_name')->columns(array_keys($data))->values($data);
$stmt = $sqlWriter->prepareStatementForSqlObject($insert);
try {
$stmt->execute();
$object->id = $this->getAdapter()->driver->getLastGeneratedValue();
} catch (\Exception $e) {
//
// HOW CAN I display errorInfo() here?
//
throw new Exception\Exception('Unable to insert record...');
}
I have tried calling methods on the adapter, driver, statement, platform, result, etc... But all to no avail...
EDIT: I found that I can get the info I am looking for by posting the following at the top of the catch block:
$pdoException = $e->getPrevious();
var_dump($pdoException);
I'll leave the question open however since it would be good to know how to execute PDO::errorInfo() directly.

Vertica and PDO prepared statements

For the last two days, I've been struggling with a very strange bug while I'm connecting with Vertica using PDO. You see, the following script works:
$c = new PDO("odbc:Driver=Vertica;Server=x.x.x.x;Port=5433;Database=db;", "MyUser", "MyPassword");
$c->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $c->prepare("SELECT * FROM myClients WHERE ClientNum = 88");
$stmt->execute();
After that, I loop through the results and display them no problem. This basically means my connection is correct otherwise I wouldn't get anything out of the database. On the other hand, the following makes the Apache server reset the connection completely (when run in Windows, I get a message that Apache crashed):
$c = new PDO("odbc:Driver=Vertica;Server=x.x.x.x;Port=5433;Database=db;", "MyUser", "MyPassword");
$c->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$c->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
//$c->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
try
{
$stmt = $c->prepare("SELECT * FROM myClients WHERE ClientNum = :cl");
$stmt->bindValue(":cl", 88);
$stmt->execute();
while($res = $stmt->fetch(PDO::FETCH_ASSOC))
{
echo $res['noClient'] . "<br>";
}
}
catch(Exception $e)
{
echo $e->getMessage();
}
The problem is present both in Linux and Windows and I'm using Vertica version 7.0.2-1 along with the corresponding ODBC driver. The problem was also present in Vertica 6.1. Can anyone give me a hand with this?
Thanks in advance.
EDIT: I tried to set PDO::ATTR_EMULATE_PREPARES to both true and false without any change.
EDIT: This being a test script, I didn't bother with any error handling. Also, given that the server actually crashes, I doubt it would change anything.
EDIT: Updated the code above to include some basic error handling. Apologies to Kermit for sounding condescending in my earlier comment. Anyway, even with this addition to my code, I still didn't get any message, the server would just silently crash and I'd get a "Connection reset" page. Upon seeing this, I tried querying different tables in my database and on one, instead of a crash, I got the following:
SQLSTATE[HY000]: General error: 50310 [Vertica][Support] (50310) Unrecognized ICU conversion error. (SQLExecute[50310] at ext\pdo_odbc\odbc_stmt.c:254)
EDIT: Went to my ODBC DSN, clicked Configure, went on the Server Setting tab and found that the locale was set to: en_US#collation=binary (which is the default for Vertica, I believe). Should I check somewhere else?
EDIT: I was curious to see what the bindValue() was doing to my query and so opened the vertica.log file. Here's what I saw:
2014-10-02 11:38:42.100 Init Session:0x5ef3030 [Session] <INFO> [Query] TX:0(vertica-1756:0xbc42) set session autocommit to on
2014-10-02 11:38:42.104 Init Session:0x5ef3030 [Session] <INFO> [PQuery] TX:0(vertica-1756:0xbc42) SELECT * FROM myClients WHERE ClientNum = ?
2014-10-02 11:38:42.105 Init Session:0x5ef3030-a00000000aac68 [Txn] <INFO> Begin Txn: a00000000aac68 'SELECT * FROM myClients WHERE ClientNum = ?'
2014-10-02 11:38:42.915 Init Session:0x5ef3030-a00000000aac68 <LOG> #v_flexgroup_node0001: 08006/2895: Could not receive data from client: No such file or directory
2014-10-02 11:38:42.915 Init Session:0x5ef3030-a00000000aac68 <LOG> #v_flexgroup_node0001: 08006/5167: Unexpected EOF on client connection
2014-10-02 11:38:42.915 Init Session:0x5ef3030-a00000000aac68 <LOG> #v_flexgroup_node0001: 00000/4719: Session vertica-1756:0xbc42 ended; closing connection (connCnt 2)
2014-10-02 11:38:42.916 Init Session:0x5ef3030-a00000000aac68 [Txn] <INFO> Rollback Txn: a00000000aac68 'SELECT * FROM myClients WHERE ClientNum = ?'
Apparently, it seems PDO is replacing the placeholders by question marks in the final query. Not all that unexpected, but for some reason, the actual value of the parameter seems to get lost along the way.
EDIT: Following a suggestion, I tried:
$stmt = $c->prepare("SELECT * FROM myClients WHERE ClientNum = :cl");
$stmt->execute(array(":cl" => 88));
But the problem remains the same.
Okay, so after going halfway crazy trying to figure out what was wrong with PDO, I discovered that using PHP odbc module directly worked.
Since all my modules are actually written using PDO and rewriting them was not an option, I ended up writing the following wrapper classes:
class PDOVertica
{
protected $conn;
public function __construct($dsn, $user, $password)
{
$this->conn = odbc_connect($dsn, $user, $password);
}
public function prepare($qry)
{
return new PDOVerticaStatement($this->conn, $qry);
}
public function lastInsertId()
{
$stmt = odbc_prepare($this->conn, "SELECT LAST_INSERT_ID()");
odbc_execute($stmt);
$res = odbc_fetch_array($stmt);
return $res['LAST_INSERT_ID'];
}
}
class PDOVerticaStatement
{
protected $qry;
protected $param;
protected $stmt;
public function __construct($conn, $qry)
{
$this->qry = preg_replace('/(?<=\s|^):[^\s:]++/um', '?', $qry);
$this->param = null;
$this->extractParam($qry);
$this->stmt = odbc_prepare($conn, $this->qry);
}
public function bindValue($param, $val)
{
$this->param[$param] = $val;
}
public function execute()
{
if($this->param == null)
odbc_execute($this->stmt);
else
odbc_execute($this->stmt, $this->param);
$this->clearParam();
}
public function fetch($option)
{
return odbc_fetch_array($this->stmt);
}
protected function extractParam($qry)
{
$qryArray = explode(" ", $qry);
$ind = 0;
while(isset($qryArray[$ind]))
{
if(preg_match("/^:/", $qryArray[$ind]))
$this->param[$qryArray[$ind]] = null;
++$ind;
}
}
protected function clearParam()
{
$ind = 0;
while(isset($this->param[$ind]))
{
$this->param[$ind] = null;
++$ind;
}
}
}
I was pleasantly surprised to find that this works without me having to rewrite hundreds of modules. I do need to rework some of the SQL since there are differences between MySQL and Vertica, but those are just minor touch ups.
Anyway, should anyone choose to use these classes, keep in mind I only implemented what I needed in terms of functionalities and they only work with queries using placeholders for parameters (:someParameter). Use them and modify them at your own discretion.
Thanks for anyone who helped me.

PHP function to open database using PDO

I realize this is probably super simple but i just started taking peoples advice and im converting a small program from mysql to PDO as an attempt to learn and switch to PDO.
The script is a script that shows you how to build a shopping cart, so keep in mind its focused on a learning audience like myself. Anyway i converted the old script here:
function db_connect()
{
$connection = mysql_pconnect('localhost', 'database_1', 'password');
if(!$connection)
{
return false;
}
if(!mysql_select_db('database_1'))
{
return false;
}
return $connection;
}
to this which does connect fine:
function db_connect() {
//Hostname
$hostname = 'xxx.com';
//username
$username = 'xxx';
//password
$password = 'xxx';
try {
$connection = new PDO("mysql:host=$hostname;dbname=database_1", $username, $password);
}
catch(PDOException $e){
echo $e->getMessage();
}
}
Now in other parts of the script before accessing the database it does this:
$connection = db_connect();
Now i have 2 questions. First is to help me understand better what is going on.
I understand in the original mysql function we connect to the database, if the connection is unsuccessful or the database doesnt exist it returns false. If it does connect to the database then it returns true.
With that i mind i dont understand this:
$connection = db_connect();
Isnt that just assigning true or false to the $connection variable, if so then whats going on in this part of the code.
$price = 0.00;
$connection = db_connect();
if (is_array($cart))
{
foreach($cart as $id => $qty)
{
$query = "SELECT price
FROM products
WHERE products.id = '$id' ";
$result = mysql_query($query);
if($result)
{
$item_price = mysql_result($result, 0, 'price');
$price += $item_price * $qty;
}
}
}
Instead couldn't i just create an include file with the PDO connection and no function and include that at the top of each page i run scripts on. I just don't understand where the $connection = db_connect comes in.
So the 2nd question if my above suggestion is not the answer is how do i return a boolean value from the connection function to return true or false (If i even need to)
There is one essential difference between old mysql and PDO: both these libraries require a resource variable to connect with. If you take a look at mysql_query() function definition, you will notice the second parameter, represents such a resource.
$connection variable returned by your old function by no means contain boolean value but such a resource variable. Which can be used in every mysql_query call.
But while for mysql ext this resource parameter being optional, and used automatically when not set, with PDO you have to address this resource variable explicitly. Means you cannot just call any PDO function anywhere in the code, but only as a method of existing PDO object. Means you have to make this variable available wherever you need PDO.
Thus, you need not a boolean but PDO object.
Here is the right code for the function:
function db_connect()
{
$dsn = "mysql:host=localhost;dbname=test;charset=utf8";
$opt = array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
);
return new PDO($dsn,'root','', $opt);
}
now you can use it this way
$pdo = db_connect();
but note again - unlike with mysql_query(), you have to always use this $pdo variable for your queries.
Further reading is PDO tag wiki
As you guessed from the context, db_connect() is supposed to return the connection object. Your converted version doesn't return anything, which is a problem.
With the mysql module, you can run queries without using the connection object - this is not the case with PDO. You'll need to use the connection object to run any queries -
$result = $connection->query('SELECT * FROM foo');
First off, let me congratulate you for making the effort to learn PDO over mysql_*. You're ahead of the curve!
Now, a few things to understand:
PDO is OO, meaning the connection to the database is represented by a PDO Object.
Your db_connect() function should return the object that gets created.
Passing in the parameters required by PDO will give you more flexibility!
So what we have is:
function db_connect($dsn, $username, $password)
{
$conn = new PDO($dsn, $username, $password);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); //This makes sure that PDO will throw PDOException objects on errors, which makes it much easier enter code hereto debug.
$conn->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); //This disables emulated prepared statements by PHP, and switches to *true* prepared statements in MySQL.
return $conn; //Returns the connection object so that it may be used from the outside.
}
Now, you may have noticed we aren't checking for PDOExceptions inside of the function! That's because you can't handle the error from inside of the function correctly (becuase you don't know what you would want to do? Would you terminate the page? Redirect to an error message?). So you can only know it when you call the function.
So usage:
try {
$connection = db_connect("mysql:host=$hostname;dbname=database", "user", "pass");
}
catch (PDOException $e) {
echo "Database error! " . $e->getMessage();
}
Further Reading!
The PDO Manual entry - is super easy and super useful. I recommend you read all of it.

Using $dsn string variable as parameter in database PDO

Goal: to simply fetch array from a MySQL database.
Issue: I am using the $dsn "string variable" as a parameter in the PDO Statement but there appears to be an uncaught exception and it has something to do with invoking the driver or the (PDO->__construct) -- I am not sure. Do you have any ideas on how to fix the $dsn string variable or other areas of this code that would cause it to fail? Thanks in advance.
// Define Database Parameters
$dbhost = "localhost";
$dbname = "x";
$dbuser = "y";
$dbpass = "z";
// Invoke Driver (as a variable string)
$dsn = "mysql:host=$dbhost;dbname=$dbname";
// Connect to newly created db object
$dbh = new PDO($dsn, $dbuser, $dbpass);
// Set the PDO error mode to enable exceptions
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
// Execute query to the database
$sql = "SELECT * FROM a_aif_remaining";
$sth = $dbh->prepare($sql);
$sth->execute();
// Present results from query
print("PDO::FETCH_ASSOC: ");
print("Return next row as an array indexed by column name");
$result = $sth->fetch(PDO::FETCH_ASSOC);
print_r($result);
print("");
return $results;
// Close db connection
$dbh = NULL;
?>
Put your code in a try catch block. And see the error message reported. An example of this below:
try
{
if ( !class_exists( 'PDO' ) )
throw new Exception( 'PHP without PDO' );
if ( array_search( PDO::getAvailableDrivers(), 'mysql' ) === false )
throw new Exception( 'PHP without PDO mysql driver' );
$dbh = new PDO( ... );
...
}
catch ( PDOException $e )
{
print $e->getMessage();
}
catch ( Exception $e )
{
print $e->getMessage();
}
There can't be any problem where you're looking for it. Substituting a string with a variable is okay and cannot cause any issues. So, it is somewhere else. Luckily PHP reported of it, but strangely, you paid not much attention to that report.
there appears to be an uncaught exception
Uncaught exceptions often contains the error message.
Most of PHP error messages are quite informative. One have to read them through to get the explanation of the problem. After all, it's the only your source of information. No one can tell you what is wrong on your server but server itself. If you have difficulties with interpreting the error message - it is essential to post it along with your question, to let people interpret it for you. So, every error message you get, should be posted in your question, whole and intact. Simple but very important rule.

Categories