PHP MySQL Prepared Statement returning no result - php

I have an issue with my prepared statement that is being sent through PHP to my database. Can someone tell me why the below statement does not return the result? I'll just describe what I want it to do, what its doing and the code.
I have data being sent through a form that is ready to be inserted into one of my tables but I need to get a value from another table to insert.
I used to the following SELECT statement to get the tripNo based off the data entered. This query works on MySQL Workbench and returns the correct result, but it doesn't work on my php form. The result is null when I echo $tripNo.
Code:
$destination=$_POST['destination'];
$dateOfSail=$_POST['dateOfSail'];
$db = createConnection();
$sql="select tripNo from TripDB where dateOfSail=? AND destination=?;";
$stmt=$db->prepare($sql);
$stmt->bind_param("ss",$dateOfSail,$destination);
$stmt->execute();
$stmt->store_result();
$stmt->bind_result($tripNo);
echo "tripNo: $tripNo"; //returns null value
There are no errors being reported so I am not sure where to investigate next as I have looked at the prepared statements examples.
Also, I have a side question. Is the method bad practice on getting a value in order to insert to a table?

after the bind_result you have to perform fetch:
while ($stmt->fetch()) {
echo "tripNo: $tripNo<br />";
}

Related

PHP PDO query not inserting - Error HY093

After a lot of searching the web, the times I see this error, it looks really scenario specific. So far, I haven't found one that matched my scenario. I think my issue is coming from a prepared statement with spatial data type params.
The way I'm executing my code is:
$sql = $conn->prepare("INSERT INTO states(`name`, `poly`) VALUES(':name',GeomFromText('GEOMETRYCOLLECTION(:coords)'));");
$res = $sql->execute(['name'=>$name, 'coords'=>$coords]);
if($res){
echo "... Successfully Inserted<br><br>";
}
else{
echo "... Failed<br><br>";
print_r($sql->errorInfo());
echo "<br><br>";
}
The above is failing. The connection to the database has been tested. Since these are rather large geometry sets, instead of pasting my code, I'll show how I verified my SQL:
Dumping a raw SQL file and copy/pasting the SQL into a phpMyAdmin window, everything inserted just fine.
$sqlStr = "INSERT INTO states(`name`, `poly`) VALUES('$name',GeomFromText('GEOMETRYCOLLECTION($coords)'));";
$check = file_put_contents('./states/'.$name.'2.sql', $sqlStr);
So it's because of this, that I believe my sql is correct, but it my problem is likely due to the prepare/execute portion somehow. I'm not sure if spatial data types can't be assigned like this?
Edit
I also want to note that I am on PHP version 5.5.9 and I've executed queries in the original method, with the params in the execute just fine.
There's no way the code at the end could be working. Parameters in the query must not be put inside quotes.
Since GEOMETRYCOLLECTION(:coords) has to be in a string, you need to use CONCAT() to create this string.
$sql = $conn->prepare("
INSERT INTO states(`name`, `poly`)
VALUES(:name,GeomFromText(CONCAT('GEOMETRYCOLLECTION(', :coords, ')')));");

PHP / mysqli: Prepared Statements with num_rows constantly returning nothing

In my test-surroundings there is a database containing some Person Information (Name, E-Mail, Adress etc.). These Informations can be inserted by anyone into the database via a form. In the background they are inserted with a parameterized INSERT into the database after submission.
What I now would like to do is to detect if some person tries to insert the same values into the database again, and if he does, not inserting the new values and instead showing an error message. (So every person name in the database is unique, there are no multiple rows linked to one name).
I had a numerous number of ideas on how to accomplish this. My first one was to use a query like REPLACE or INSERT IGNORE, but this method would not give me feedback so I can display the error message.
My second attempt was to first do a SELECT-query, checking if the row already exists, and if num_rows is greater than 0, exit with the error message (and else do the INSERT-part). For this to work I will have to use parameterized queries for the SELECT too, as I´m putting some user input into it. Figuring that parameterized queries need special functions for everything you could normally do with way less lines of code, I researched in the internet on how to get num_rows from my $statement parameterized-statement-object. This is what I had in the end:
$connection = new mysqli('x', 'x', 'x', 'x');
if (mysqli_connect_error()) {
die("Connect Error");
}
$connection->set_charset("UTF-8");
$statement = $connection->stmt_init();
$statement = $connection->prepare('SELECT Name FROM test WHERE Name LIKE ?');
flags = "s";
$statement->bind_param($flags, $_POST["person_name"]);
$statement->execute();
$statement->store_result();
$result = $statement->get_result(); //Produces error
if ($result->num_rows >= 1) {
$output = "Your already registered";
} else {
$output = "Registering you...";
}
exit($output);
After all, I can´t get why mysqli still won´t give me num_rows from my statement. Any help is appreciated, thanks in advance!
Oh, and if you guys could explain to me what I have to do to get affected_rows,that would be awesome!
EDIT: I know I could to this by using unique constraints. I also found out that I can find out if INSERT IGNORE skipped the INSERT or not. But that won´t answer my complete question: Why does the SELECT num_rows alternative not work?
ANOTHER EDIT: I changed the code snippet to what I now have. Although my mysql(i)-version seems to be 5.6.33 (I echo´d it via $connection->server_info) get_result() produces the following error message:
Fatal error: Call to undefined method mysqli_stmt::get_result() in X on line X (line of get_result)
The behaviour of mysqli_num_rows() depends on whether buffered or unbuffered result sets are being used. For unbuffered result sets, mysqli_num_rows() will not return the correct number of rows until all the rows in the result have been retrieved. Note that if the number of rows is greater than PHP_INT_MAX, the number will be returned as a string.
Also make sure that you declare ->store_result() first. Moreover the function doesn't work with LIMIT used jointly with SQL_CALC_FOUND_ROWS. If you want to obtain the total rows found you must do it manually.
EDIT:
If nothing from the suggestions does not work for you, then I would propose to rewrite your SQL query:
SELECT `Name`, (SELECT COUNT(*) FROM `Persons`) AS `num_rows` FROM `Persons` WHERE `Name` LIKE ?
This query will return the total number from your Persons table, as well as Name, if exist.

Why is INSERT INTO followed by SELECT LAST_INSERT_ID() not outputting anything?

The PHP code I have inserts the HTML form data from the previous page into the database and in the same SQL statement return the PostID back from the inserted data. The PostID column is AUTO_INCREMENTING. I have been researching this problem for a week or two now and have found no significant solutions.
<?php
include("dbconnect.php");
mysql_select_db("astral_database", $con);
session_start();
$username = $_SESSION['username'];
$forumtext = $_POST["forumtext"];
$forumsubject = $_POST["forumsubject"];
$postquery = 'INSERT INTO Forums (Creator, Subject, Content) VALUES ("$username", "$forumsubject", "$forumtext"); SELECT LAST_INSERT_ID()';
$result = mysql_query($postquery, $con);
if (!$con) {
echo "<b>If you are seeing this, please send the information below to astraldevgroup#gmail.com</b><br>Error (331: dbconnect experienced fatal errors while attempting to connect)";
die();
}
if ($username == null) {
echo "<b>If you are seeing this, please send the information below to astraldevgroup#gmail.com</b><br>Error (332: Username was not specified while attempting to send request)";
die();
}
if ($result != null) {
echo "last id: " . $result;
$fhandle = fopen("recentposts.txt", "r+");
$contents = file_get_contents("recentposts.txt");
fwrite($fhandle, json_encode(array("postid" => $result, "creator" => $username, "subject" => $forumsubject, "activity" => time())) . "\n" . $contents);
fclose($fhandle);
mysql_close($con);
header("location: http://astraldevgroup.com/forums");
die();
} else {
die("<b>If you are seeing this, please send the information below to astraldevgroup#gmail.com</b><br>Error (330: Unhandled exception occured while posting forum to website.)<br>");
echo mysql_error();
}
mysql_close($con);
?>
First off, the mysql_query doesn't return anything from the SELECT statement. I haven't found anything that will properly run both the SELECT statement and the INSERT statement in the same query. If I try running them in two different statements, it still doesn't return anything. I tried running the following statement in the SQL console and it ran perfectly fine without errors.
INSERT INTO Forums (Creator, Subject, Content) VALUES ("Admin", "Test forum 15", "This is a forum that should give me the post id."); SELECT LAST_INSERT_ID();
The mysql_query function does not run multiple statements
Reference: http://php.net/manual/en/function.mysql-query.php
mysql_query() sends a unique query (multiple queries are not supported) to the currently active database on the server ...
That's one reason your call to mysql_query isn't returning a resultset.
The most obvious workaround is to not try to run the SELECT in the same query. You could use a call to the mysql_insert_id instead.
Reference: PHP: mysql_insert_id http://php.net/manual/en/function.mysql-insert-id.php
Answers to some of questions you didn't ask:
Yes, your example code is vulnerable to SQL Injection.
Yes, the mysql_ interface has been deprecated for a long time.
Yes, you should being using either PDO or mysqli interfaces instead of the deprecated mysql_ functions.
FOLLOWUP
Re-visiting my answer, looking again at the question, and the example code.
I previously indicated that the code was vulnerable to SQL Injection, because potentially unsafe values are included in the SQL text. And that's what it looked like on a quick review.
But looking at it again, that isn't strictly true, because variable substitution isn't really happening, because the string literal is enclosed in single quotes. Consider what the output from:
$foo = "bar";
echo '$foo';
echo '"$foo"';
Then consider what is assigned to $postquery by this line of code:
$postquery = 'INSERT ... VALUES ("$username", "$forumsubject", "$forumtext")';
Fixing that so that $username is considered to be a reference to a variable, rather than literal characters (to get the value assigned to $username variable incorporated into the SQL text) that would introduce the SQL Injection vulnerability.
Prepared statements with bind placeholders are really not that hard.
$result will never be null. It's either a result handle, or a boolean false. Since you're testing for the wrong value, you'll never see the false that mysql_query() returned to tell you that the query failed.
As others have pointed out, you can NOT issue multiple queries in a single query() call - it's a cheap basic defense against one form of SQL injection attacks in the PHP mysql driver. However, the rest of your code IS vulnerable other forms of injection attacks, so... better start reading: http://bobby-tables.com
Plus, on the logic side, why are you testing for a null username AFTER you try to insert that very same username into the DB? You should be testing/validating those values BEFORE you run the query.

Getting the results of a variable length prepared statement in MySQLi

I writing an accounting website which has quite a few MySQL statements in it. To prevent SQL injection I use prepared statements for any data which is put in by the user.
In order to prevent having to write the steps of preparing and binding statements I have the following function:
function executeSql($mysqli,$query_string,$params=null,$paramtypes=null){
$nr_params=strlen($paramtypes);
$query_type = substr($query_string,0,3);
$stmt = $mysqli->prepare($query_string);
$queryParams[] = $paramtypes;
$counter=1;
if($nr_params>1){
while($counter<=$nr_params){
$queryParams[$counter]=&$params[$counter-1];
$counter++;
}
} else {
$queryParams[1]=&$params;
}
// Actual binding of the statement. Taking into account a variable numbers of '?' in the query string.
call_user_func_array(array($stmt,'bind_param'),$queryParams);
// Execution of the statement
$stmt->execute();
// Part where i'd like to have a substitute for:
$result = $stmt->get_result();
return $result;
}
In the last part I'd like to return the result because then using the result I can treat each row. The problem is that the mysqlnd driver is not installed on the production server so the function $stmt->get_result() cannot be used. I tried to bind the result into variables but then again, every query returns a different number of columns.
Anyone has an idea how to tackle this?
So in summary (in response to the comments):
How can I retrieve a results object of an executed MySQLi statement while I cannot use $stmt->get_result();
Kind regards,
EJG
PS I know the code is not flawless, e.g. if strings are used as variables to bind to the statement but that is easily fixed.
UPDATE:
I came across the function $stmt->result_metadata(); Although supposedly the function name suggests only the meta data the php documentation states that:
"If a statement passed to mysqli_prepare() is one that produces a result set, mysqli_stmt_result_metadata() returns the result object"...

PHP MySQLi Prepare Statement Failing to return rows

I have been converting a lot of my old MySQL stuff to MySQLi in PHP and am getting a problem on the following code:
### FETCH INCLUDES ###
$player=$_POST['player'];
$password=md5($_POST['password']);
#### DB CONNECTION ####
if(!$mysqli=new mysqli(DBHOST,DBUSER,DBPWD,DBNAME)) {$err=$mysqli->error; print($err); }
$sql="SELECT * FROM accounts WHERE name='?' AND passkey='?'";
if($stmt=$mysqli->prepare($sql)) {
//$stmt->bind_param('ss',$player,$password);
$stmt->execute();
$stmt->store_result();
if($stmt->num_rows==1) {
$account=$stmt->fetch_assoc();
// purely for debugging
print_r($account);
if($_SESSION['account']=$account) $account=true;
} else {
echo "Failed. Row count: ";
print($stmt->num_rows);
echo "<br />";
$query=str_replace('?','%s',$sql);
printf($query,$player,$password);
$account=false;
}
$stmt->close();
} else {
$err=$mysqli->error;
print($err);
}
I have narrowed down the fault to the query itself. I am getting 0 rows returned, no errors, so I thought I would output the query (the str_replace thing I have going there) and I can use the query to return a row from the database using the same query from PHPMyAdmin
Where am I going wrong?
EDIT
I tried changing the query to a basic one without binding params - "SELECT * FROM table"
still get no rows returned. So it is isn't the query itself, it would be something in my order/format of the prepare,execute situation
second edit: I have added the $stmt->store_result() to the code and still returns 0 row count.
Third Edit:
I investigated the connection and user settings which seem fine. I can connect via console to the database using the same user and password, and the database name is the same. I am really stumped on this :(
Add a $stmt->store_result(); after $stmt->execute();, as it seem's it must be called once before $stmt->num_rows... At least they do this in the examples (see http://php.net/manual/en/mysqli-stmt.store-result.php). And they meantion a dependency in the documentation of "num_rows".
Other ideas: You check for if($stmt->num_rows==1) {, are you sure num_rows is 0? I don't know your database structure for the table "accounts". Is "name" the primary key (or at least a unique index)? If not, there could be multiple columns that match. That's just a quick idea what could be wrong, and cause you looking hours for the problem in your source code. While the problem is somewhere else.
Ok, I did check your code. First you should fix your error handling when connecting. Don't check for "$mysqli" is true but check mysqli_connect_errno() like this:
$mysqli=new mysqli(DBHOST,DBUSER,DBPWD,DBNAME);
if (mysqli_connect_errno()) {
printf("Connect failed: %s\n", mysqli_connect_error());
exit();
}
On my server the your code worked with correct credentials (e.g. DBHOST,DBUSER,DBPWD,DBNAME needed to be replaced).
And the statement $account=$stmt->fetch_assoc(); doesnt work. There is no fetch_assoc() function in the $stmt object. The fetch_assoc() is for mysqli::result objects obtained by using normal queries (not prepared statements). You need to use $stmt->bind_result(); and then $stmt->fetch(); Also you should to put a list of all column names in the query instead of "*", this defines a field order...
I got it working, I ended up needing the $stmt->store_result();
But I also noticed I had added single quotes around the ? in the prepare statement which was causing problems.
After taking those out and leaving in the store_result method it works!

Categories