Is there a way to simulate a failed INSERT query in mysql? Since I'm running PHP/MySQL locally, the return value will always be TRUE. Turning of MySQL in xampp doesn't seem to work.
NOTE: I may be looking at this the wrong way. Feel free to let me know.
Couple of approaches:
revoke INSERT permission for your database user
wrap your DB access code into middle layer and simulate failures there
Or if you just need to test INSERTS, create dumb function like:
function my_query( $link, string $query ) {
if( rand() %1 ) {
return false;
} else {
return mysqli_query( $link, $query );
}
}
and update your code that INSERTs to call my_query instead. Tune your failure conditions and happy testing :)
Related
Problem:
Unable to store data with mySQL stored procedure with mysqli_begin_transaction.
Details:
The below code will do simple insert and select using mysql stored procedure. Code runs fine without select statement. However once the select statement is added, it won't commit any data even the query returns success at PHP side.
Snippets (PHP):
$DB_DRRM_SQLI = mysqli_connect("localhost","root","", "sandbox_db");
mysqli_begin_transaction($DB_DRRM_SQLI);
$SQL_QUERY_CODE = "CALL SANDBOX_TEST()";
$DB_QUERY = mysqli_query($DB_DRRM_SQLI, $SQL_QUERY_CODE);
// ERROR REPORTING
if($DB_QUERY === false)
{
echo mysqli_error($DB_DRRM_SQLI);
mysqli_rollback($DB_DRRM_SQLI);
}
else
{
echo 'success';
mysqli_commit($DB_DRRM_SQLI);
}
exit;
Snippets (mySQL Stored procedure):
BEGIN
INSERT INTO
`sandbox_table`
(
`SOME_STRING`
)
VALUES
(
'ABCDEFGHIJKL...'
);
SELECT
LAST_INSERT_ID() AS INSERTED_ID,
'ABCDE...' AS OTHER_PARAMS;
END
Database (Table sandbox_table):
RECORD_PRIMARY_ID (Int - Auto increment)
SOME_STRING (Varchar - 500 length)
Spec:
PHP version: 5.6.14
10.1.8-MariaDB
Storage Engine: InnoDB
Notes:
If transaction is made at stored procedure works fine, but I need a PHP managed transaction to handle multiple query requests and response depending on the result of query.
(It can be a possible last resort if there's no other solution, where I need to convert whole PHP code to stored procedure and need pass tons of parameter)
Methods Tested:
Tried with other PHP version 7.0.9 with same result (10.1.16-MariaDB)
Tested with new database with no other data except sandbox_tableand above stored procedure.
Tested without additional include libraries (tested with purely on above snippets).
Solution:
It was caused by Commands out of sync error at mysqli_commit. Seems the mysqli won't allow committing transaction while the query is open, which happens if you add select statement to above stored procedure.
So to handle this, it must close the query first or put the query to buffer.
Snippets (PHP):
// SQL Database
$DB_DRRM_SQLI = mysqli_connect("localhost","root","", "sandbox_db");
mysqli_begin_transaction($DB_DRRM_SQLI);
$SQL_QUERY_CODE = "CALL SANDBOX_TEST()";
$DB_QUERY = mysqli_query($DB_DRRM_SQLI, $SQL_QUERY_CODE);
// ERROR REPORTING
if($DB_QUERY === false)
{
echo mysqli_error($DB_DRRM_SQLI);
mysqli_rollback($DB_DRRM_SQLI);
}
else
{
// Must free current query result before committing transaction
#mysqli_free_result($DB_QUERY);
#mysqli_next_result($DB_DRRM_SQLI);
if(mysqli_commit($DB_DRRM_SQLI) === false)
{
echo mysqli_error($DB_DRRM_SQLI);
}
else
{
echo 'success';
}
}
exit;
I am trying my best to object orientate my login scripts and checks for my website. As such, I have created a function that is called named "attempt_login". This function uses a database connection via the "mysqli_connect" function provided by a outside php file that is included and returns the $link variable.
However, when in my "attempt_login" the "mysqli_stmt_prepare" function is reached. It always returns false, and as such, the if statement never runs.
mysql_connect.php
require_once '../conf.php';
$link = mysqli_connect($mysql_server,$mysql_user,$mysql_pass,$mysql_db);
if(mysqli_connect_error())
{
//Fail
echo mysqli_connect_error();
}else{
//Success
}
return $link;
And the code from the "attempt_login" that fails:
$link = require_once 'functions/mysql/mysql_connect.php';
print_r($link);
$stmt = mysqli_stmt_init($link);
echo mysqli_stmt_error($stmt);
echo mysqli_error();
//This if statement always fails.
if(mysqli_stmt_prepare($stmt,"SELECT member_salt FROM members WHERE username=?" ) )
{
mysqli_stmt_bind_param($stmt,'s',$login_details['username']);
mysqli_stmt_execute($stmt);
$member_salt = NULL;
mysqli_stmt_bind_result($stmt,$member_salt);
mysqli_stmt_close($stmt);
}
And calling the function is pretty simple.
attempt_login($login_details);
I have tried many ways to do this, but it seems to only work when it is not within a function. Which defeats the purpose of me trying to have it in a function. If I move the error output to after the if statment, it will print:
No Database Selected
Even though one clearly is set in the link. I also can do a print_r($link); and it does print proper data for the mysql server.
It also has the same issue with procedural and object orientated mysqli styles, and fails as well with "mysqli_prepare".
I do have mysql working on the site, but any attempts to run anything within a function fails.
I have searched for several hours on trying to find a answer, but was unable to locate anything. I look forward to hearing from you! Many thanks in advance.
~Travis
I'd say then you haven't selected a database.
Either $mysql_db isn't set in ../conf.php or there is a typo or $mysql_db isn't visible in mysql_connect.php.
You don't seem to be passing $stmt to your function, I would advise your read this on scopes.
Have an issue with executing routines in CodeIgniter.
I have a routine that just updates couple mysql tables with the data specified in parameters.
Originally I received:
Error Number: 2014
Commands out of sync; you can’t run this command now
Tried the solution specified at http://codeigniter.com/forums/viewthread/96583/
$this->mydb->Query($sql);
with changed driver to mysqli
It works fine with the select statements for me but if the routine has only couple updates - it does not work.
The problem is that there is no error message is returned. It behaves like it is successfully run, but tables are not updated.
NOTE: there should not be an issue with routine itself since if I print the query in question and try to execute this in MySQL Workbench - tables are updated correctly.
Any advises are appreciated.
Try changing the dbdriver in the config file to mysqli:
$db['default']['dbdriver'] = "mysqli";
$this->mydb->Query used in the whole project for each query has solved the issue
function call( $procedure )
{
$result = #$this->db->conn_id->query( $procedure );
while ( $this->db->conn_id->next_result() )
{
//free each result.
$not_used_result = $this->db->conn_id->use_result();
if ( $not_used_result instanceof mysqli_result )
{
$not_used_result->free();
}
}
return $result;
}
I'm developing an update system for a Web Application written in PHP. In the process of the update I might need to execute a bunch of MySQL scripts.
The basic process to run the scripts is:
Search for the Mysql scripts
Begin a transaction
Execute each script with mysqli_multi_query since a script can contain multiple queries
If everything goes ok COMMIT the transaction, otherwise ROLLBACK.
My code looks something like:
$link = mysqli_connect(...);
mysqli_autocommit($link, false);
// open dir and search for scripts in file.
// $file is an array with all the scripts
foreach ($scripts as $file) {
$script = trim(file_get_contents($scriptname));
if (mysqli_multi_query($link, $script)) {
while (mysqli_next_result($link)) {
if ($resSet = mysqli_store_result($link)) { mysqli_free_result($resSet); }
if (mysqli_more_results($link)) { }
}
}
// check for errors in any query of any script
if (mysqli_error($link)) {
mysqli_rollback($link);
return;
}
}
mysqli_commit($link);
Here is an example of the scripts (for demonstration purposes):
script.1.5.0.0.sql:
update `demo` set `alias` = 'test1' where `id` = 1;
update `users` set `alias` = 'user1' where `id` = 1;
script 1.5.1.0.sql:
insert into `users`(id, key, username) values(3, '100', 'column key does not exist');
insert into `users`(id, key, username) values(3, '1', 'column key exists');
In this case, script 1.5.0.0 would execute without errors and script 1.5.1.0 would generate an error (for demonstration purposes, let's say that column key is unique and there is already a row with key = 1).
In this case I want to rollback every query that was executed. But what happens is that the first insert of 1.5.1.0 is not in the database (correctly) but the updates from 1.5.0.0 were executed successfully.
Remarks:
My first option was to split every query from every script with ";" and execute the queries independently. This is not an option since I have to be able to insert HTML code to the database (ex: if I want to insert something like "& nbsp;")
I've already searched StackOverflow and google and came across solutions like this one but I would prefer using a solution like mysqli_multi_query rather than using a function to split every query. It's more understandable and easier for debug purposes
I haven't tested it, but I believe that I could merge all the scripts and execute just a query. However it would be usefull to execute one script at a time so that I can figure out which script has the error.
The tables engine is InnoDB.
Appreciate if you can point some way to make this work.
Edit:mysqli_multi_query() only returns false if the first query fails. If the first query doesn't fail then your code will run mysql_store_result() which if it succeeds will leave mysqli_error() empty. You need to check for errors after every mysqli function that can succeed or fail.
Ok, after spending another day debugging, i've discovered the problem.
Actually, it has nothing to do with the code itself or with mysqli functions. I'm used to MS SQL transactions which supports DDL statements. MySQL does not supports DDL statements and commits data implicitly (Implicit commit). I had one DROP Table in one of the scripts that was auto commiting data.
I'm trying to get into PDO details. So I coded this:
$cn = getConnection();
// get table sequence
$comando = "call p_generate_seq('bitacora')";
$id = getValue($cn, $comando);
//$comando = 'INSERT INTO dsa_bitacora (id, estado, fch_creacion) VALUES (?, ?, ?)';
$comando = 'INSERT INTO dsa_bitacora (id, estado, fch_creacion) VALUES (:id, :estado, :fch_creacion)';
$parametros = array (
':id'=> (int)$id,
':estado'=>1,
':fch_creacion'=>date('Y-m-d H:i:s')
);
execWithParameters($cn, $comando, $parametros);
my getValue function works fine, and I get the next sequence for the table. But when I get into execWithParameters, i get this exception:
PDOException: SQLSTATE[HY000]: General error: 2014 Cannot execute queries while other unbuffered queries are active. Consider using PDOStatement::fetchAll(). Alternatively, if your code is only ever going to run against mysql, you may enable query buffering by setting the PDO::MYSQL_ATTR_USE_BUFFERED_QUERY attribute. in D:\Servidor\xampp_1_7_1\htdocs\bitacora\func_db.php on line 77
I tried to modify the connection attributes but it doesn't work.
These are my core db functions:
function getConnection() {
try {
$cn = new PDO("mysql:host=$host;dbname=$bd", $usuario, $clave, array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
));
$cn->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);
return $cn;
} catch (PDOException $e) {
print "Error!: " . $e->getMessage() . "<br/>";
die();
}
}
function getValue($cn, $comando) {
$resul = $cn->query($comando);
if (!$resul) return null;
while($res = $resul->fetch()) {
$retorno = $res[0][0];
break;
}
return $retorno;
}
function execWithParameters($cn, $comando, $parametros) {
$q = $cn->prepare($comando);
$q->execute($parametros);
if ($q->errorInfo() != null) {
$e = $q->errorInfo();
echo $e[0].':'.$e[1].':'.$e[2];
}
}
Somebody who can shed a light for this? PD. Please do not suggest doing autonumeric id, cause i am porting from another system.
The issue is that mysql only allows for one outstanding cursor at a given time. By using the fetch() method and not consuming all the pending data, you are leaving a cursor open.
The recommended approach is to consume all the data using the fetchAll() method.
An alternative is to use the closeCursor() method.
If you change this function, I think you will be happier:
<?php
function getValue($cn, $comando) {
$resul = $cn->query($comando);
if (!$resul) return null;
foreach ($resul->fetchAll() as $res) {
$retorno = $res[0];
break;
}
return $retorno;
}
?>
I don't think PDOStatement::closeCursor() would work if you're not doing a query that returns data (i.e. an UPDATE, INSERT, etc).
A better solution is to simply unset() your PDOStatement object after calling PDOStatement::execute():
$stmt = $pdo->prepare('UPDATE users SET active = 1');
$stmt->execute();
unset($stmt);
The problem seems to be---I'm not too familiar with PDO--- that after your getValue call returns, the query is still bound to the connection (You only ever ask for the first value, yet the connection returns several, or expects to do so).
Perhaps getValue can be fixed by adding
$resul->closeCursor();
before the return.
Otherwise, if queries to getValue will always return a single (or few enough) value, it seems that using fetchAll will be preferred.
I just spend 15 minutes googling all around the internet, and viewed at least 5 different Stackoverflow questions, some who claimed my bug apparently arose from the wrong version of PHP, wrong version of MySQL library or any other magical black-box stuff...
I changed all my code into using "fetchAll" and I even called closeCursor() and unset() on the query object after each and every query. I was honestly getting desperate! I also tried the MYSQL_ATTR_USE_BUFFERED_QUERY flag, but it did not work.
FINALLY I threw everything out the window and looked at the PHP error, and tracked the line of code where it happened.
SELECT AVG((original_bytes-new_bytes)/original_bytes) as saving
FROM (SELECT original_bytes, new_bytes FROM jobs ORDER BY id DESC LIMIT 100) AS t1
Anyway, the problem happened because my original_bytes and new_bytes both where unsigned bigints, and that meant that if I ever had a job where the new_bytes where actually LARGER than the original_bytes, then I would have a nasty MySQL "out of range" error. And that just happened randomly after running my minification service for a little while.
Why the hell I got this weird MySQL error instead of just giving me the plain error, is beyond me! It actually showed up in SQLBuddy (lightweight PHPMyAdmin) when I ran the raw query.
I had PDO exceptions on, so it should have just given me the MySQL error.
Never mind, the bottom line is:
If you ever get this error, be sure to check that your raw MySQL is actually correct and STILL working!!!
A friend of mine had very much the same problem with the xampp 1.7.1 build. After replacing xampp/php/* by the 5.2.9-2 php.net build and copying all necessary files to xampp/apache/bin it worked fine.
If you're using XAMPP 1.7.1, you just need to upgrade to 1.7.2.