This question already has an answer here:
What to do with mysqli problems? Errors like mysqli_fetch_array(): Argument #1 must be of type mysqli_result and such
(1 answer)
Closed 6 years ago.
I have a problem using following PHP function:
function saveCommentOnDB($arg_textComment, $arg_score, $arg_userEmail)
{
$result_tmp = null;
$this->conn->autocommit(false);
echo "saving\n";
echo "text comment: \n";
var_dump($arg_textComment); // OKAY
echo "comment score: \n";
var_dump($arg_score); // OKAY
echo "user mail: \n";
var_dump($arg_userEmail); // OKAY
try {
//[tag1] $query_1 = "INSERT INTO commenti (userFirstname, userEmail, textComment, score, feedback) VALUES ( (SELECT firstname FROM utente u WHERE u.userEmail = 'asd#asd.asd') ,'asd#asd.asd', 'This is an example comment.', 5, 0);";
$query_1 = "INSERT INTO commenti (userFirstname, userEmail, textComment, score, feedback) VALUES ( (SELECT firstname FROM utente u WHERE u.userEmail = ?) ,?,?, ?, 0);";
$query_2 = "UPDATE utente SET commentID=(SELECT c.commentID FROM commenti c WHERE c.userEmail = ?) WHERE userEmail = ?;";
$query_3 = "SELECT commentID, textComment FROM commenti WHERE userEmail = ?;";
$stmt1 = $this->conn->prepare($query_1);
$stmt2 = $this->conn->prepare($query_2);
$stmt3 = $this->conn->prepare($query_3);
$stmt1->bind_param("sssd", $arg_userEmail, $arg_userEmail, $arg_textComment, $arg_score);
$stmt2->bind_param("ss", $arg_userEmail, $arg_userEmail);
$stmt3->bind_param("s", $arg_userEmail);
$stmt1->execute();
$stmt2->execute();
$stmt3->execute();
$stmt3->bind_result($col1, $col2);
$stmt3->fetch();
echo "result:\n";
var_dump($col1); // OKAY
var_dump($col2); // OKAY
$result_tmp = array(
'commentID' => $col1,
'textComment' => $col2
);
$this->conn->commit();
} catch (Exception $e) {
$this->conn->rollback();
}
return $result_tmp;
}
Please, ignore the echo and var_dump, I put them only for debugging.
The problem is that in this function these three prepared statement seems to work not correctly. In particular the statement $stmt1: the result of $stmt3 is correct (as if $stmt1 and $stmt2 are executed correctly), but I don't see anything on my Database. In other words: the statements works correctly 'temporarily' during the execution, but in MyPHP Admin there's nothing on the table commenti.
For example, we assume having this on the DB:
Now I launch the function with following parameters:
$arg_textComment = 'This is an example comment'
$arg_score = '5'
$arg_userEmail = 'asd#asd.asd'
and we have on my browser console:
ie: the commentID (28) is right and the comment text (commentcomment) was "saved", then I recheck the DB but I have still this:
and var_dump($stmt1) after the execution is:
stmt1:
object(mysqli_stmt)#4 (10) {
["affected_rows"]=>
int(1)
["insert_id"]=>
int(41)
["num_rows"]=>
int(0)
["param_count"]=>
int(4)
["field_count"]=>
int(0)
["errno"]=>
int(0)
["error"]=>
string(0) ""
["error_list"]=>
array(0) {
}
["sqlstate"]=>
string(5) "00000"
["id"]=>
int(4)
}
The var_dump seems to be ok, but DB nope.
So I try to execute the query 'manually' by this (it will be executed only the code into the green lined box):
and I have what I expected:
sql> INSERT INTO commenti (userFirstname, userEmail, textComment, score, feedback) VALUES ( (SELECT firstname FROM utente u WHERE u.userEmail = 'asd#asd.asd') ,'asd#asd.asd', 'commentcomment', '5', 0) [2017-01-21 17:38:28] 1 row affected in 11ms
Keep in mind score value is store on DB as float.
The SQL query of the $stmt1 is the same I inserted manually (INSERT INTO... via PHPStorm).
Why the first doesn't works and instead the second yes?
Hope this screencast may help:
https://youtu.be/UsYK93jYVqA
Problem solved, change from this:
$stmt1->execute();
$stmt2->execute();
$stmt3->execute();
to this:
$stmt1->execute();
$this->conn->commit();
$stmt2->execute();
$this->conn->commit();
$stmt3->execute();
$this->conn->commit();
Have no idea of why... 😐 but it works after many tests.
Related
Here I am using sqlsrv:
$conn = sqlsrv_connect("192.168.1.102,1433", array("Database"=>"RF_User", "UID"=>"rfo-gcp", "PWD" => ""));
$tsql = "SELECT birthdate FROM tbl_rfaccount WHERE id = ?";
$stmt = sqlsrv_query($conn, $tsql, array("test"));
$result = sqlsrv_fetch_array($stmt);
var_dump($result);
Result: array(2) { [0]=> object(DateTime)#1 (3) { ["date"]=> string(26) "2020-04-19 20:40:00.000000" ["timezone_type"]=> int(3) ["timezone"]=> string(3) "UTC" } ["birthdate"]=> object(DateTime)#1 (3) { ["date"]=> string(26) "2020-04-19 20:40:00.000000" ["timezone_type"]=> int(3) ["timezone"]=> string(3) "UTC" } }
Here I am using PDO:
$conn = new PDO("sqlsrv:Server=192.168.1.102,1433; Database=RF_User;", "rfo-gcp", "");
$tsql = "SELECT birthdate FROM tbl_rfaccount WHERE id = cast(? as varchar(13))";
$stmt = $conn->prepare($tsql);
$stmt->execute(array("test"));
$result = $stmt->fetch(PDO::FETCH_ASSOC);
var_dump($result);
Result: array(1) { ["birthdate"]=> string(19) "2020-04-19 20:40:00" }
If you notice, I had to use cast(? as varchar(13)) on the PDO code. Without it would not return any row. On the sqlsrv I didn't have to use the CAST() function. Why is this? Also, the id column on the database is a BINARY(13), so why do I have to cast the id to varchar and not to binary (with binary cast it also doesn't find the row)?
Why date and time values are returned differently?
In fact, this is only a setting.
When you use PDO_SQLSRV (as is mentioned in the documentation), date and time types (smalldatetime, datetime, date, time, datetime2, and datetimeoffset) are by default returned as strings. Neither the PDO::ATTR_STRINGIFY_FETCHES nor the PDO::SQLSRV_ATTR_FETCHES_NUMERIC_TYPE attribute has any effect. In order to retrieve date and time types as PHP DateTime objects, set the connection or statement attribute PDO::SQLSRV_ATTR_FETCHES_DATETIME_TYPE to true (it is false by default).
When you use SQLSRV driver (again from the documentation), smalldatetime, datetime, date, time, datetime2, and datetimeoffset types will be returned as PHP DateTime objects. This behaviour can be changed by setting the 'ReturnDatesAsStrings' option in the connection string or at the statement level.
$conn = sqlsrv_connect(
"192.168.1.102,1433",
array(
"ReturnDatesAsStrings"=>true,
"Database"=>"RF_User",
"UID"=>"rfo-gcp",
"PWD" => ""
)
);
Note that some of the features depend on the version of PHP Driver for SQL Server.
How to cast parameters values?
Using CAST() and CONVERT() functions in the statement and binding parameter value with string value should work. Of course, you can specify the parameter data type, when you bind a parameter.
For PDO_SQLSRV you should extended syntax for PDOStatement::bindParam().
For SQLSRV you may use the extended $params syntax to specify the SQL Server data type, when you make a call to sqlsrv_query()\sqlsrv_execute().
I'm able to reproduce this issue (PHP 7.1.12, PHP Driver for SQL Server 4.3.0+9904, SQL Server 2012) and the solution is to use:
$params = array($id, SQLSRV_PARAM_IN, null, SQLSRV_SQLTYPE_BINARY); // SQLSRV
$stmt->bindParam(1, $id, PDO::PARAM_LOB, null, PDO::SQLSRV_ENCODING_BINARY); // PDO_SQLSRV
Table:
CREATE TABLE tbl_rfaccount (id binary(13), birthdate datetime)
INSERT INTO tbl_rfaccount (id, birthdate) VALUES (CONVERT(binary(13), 'Test'), GETDATE())
PHP:
<?php
...
//
$id = "Test";
// SQLSRV
$tsql = "SELECT birthdate FROM tbl_rfaccount WHERE id = ?";
$params = array($id, SQLSRV_PARAM_IN, null, SQLSRV_SQLTYPE_BINARY);
$stmt = sqlsrv_query($conn, $tsql, $params);
if ($stmt === false) {
echo "Error (sqlsrv_query): ".print_r(sqlsrv_errors(), true);
exit;
}
$result = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
var_dump($result);
// PDO_SQLSRV
$tsql = "SELECT birthdate FROM tbl_rfaccount WHERE id = ?";
$stmt = $conn->prepare($tsql);
$stmt->bindParam(1, $id, PDO::PARAM_LOB, null, PDO::SQLSRV_ENCODING_BINARY);
$stmt->execute();
$result = $stmt->fetch(PDO::FETCH_ASSOC);
var_dump($result);
...
?>
I have created a temporary table with the following PHP script
$query = "CREATE TEMPORARY TABLE people(id int(11) not null auto_increment, first_name varchar(25), second_name varchar(25), primary key(id))";
$q=$pdo->prepare($query);
$q->execute();
I then tried to insert data to this table with the following script
$query2 = "INSERT INTO people(first_name, second_name) VALUES(:firstname,:secondname)";
$q2=$pdo->prepare($query2);
$q2->bindValue(':firstname', $firstname,PDO::PARAM_STR);
$q2->bindValue(':secondname', $secondname,PDO::PARAM_STR);
$q2->execute;
The data I am trying to insert comes from another table. At first this seemed as a problem caused by an elementary error but I cannot figure out where the problem is because when I run this script (see below), I see that all fields were created
$query3 = "DESCRIBE people";
$q3 = $con->prepare($query3);
$q3->execute();
$row = $q3->fetchAll(PDO::FETCH_COLUMN);
print_r($row);
$query2 = "INSERT INTO people(first_name, second_name) VALUES(:firstname,:secondname)";
$q2=$pdo->prepare($query2);
$q2->bindValue(':firstname', $firstname,PDO::PARAM_STR);
$q2->bindValue(':secondname', $secondname,PDO::PARAM_STR);
$q2->execute;
The () for the execute() method are missing.
Additionaly:
If you are trying to fill the table from another table, without other steps in between, you could do this directly via mysql:
$query2 = "INSERT INTO people(first_name, second_name)
select firstname, secondname from otherTABLE where something = 'xy'";
$pdo->exec($query2);
Doing it directly in the database would reduce the chance for errors in php trying to do the same.
The rest of your script is correct, I tested it locally:
$pdo = new PDO('mysql:dbname=test;host=127.0.0.1', 'root', '', array(PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC));
$query = "CREATE TEMPORARY TABLE people(id int(11) not null auto_increment, first_name varchar(25), second_name varchar(25), primary key(id))";
$q = $pdo->prepare($query);
$q->execute();
$firstname = 'test';
$secondname = 'what';
$query2 = "INSERT INTO people(first_name, second_name) VALUES(:firstname,:secondname)";
$q2 = $pdo->prepare($query2);
$q2->bindValue(':firstname', $firstname, PDO::PARAM_STR);
$q2->bindValue(':secondname', $secondname, PDO::PARAM_STR);
$q2->execute();
$pdo->exec("INSERT INTO people(first_name, second_name) SELECT 'prename', 'surname'");
$result = $pdo->query('SELECT * FROM people');
echo '<pre>';
var_dump($result->fetchAll());
exit;
displays:
array(2) {
[0]=>
array(3) {
["id"]=>
string(1) "1"
["first_name"]=>
string(4) "test"
["second_name"]=>
string(4) "what"
}
[1]=>
array(3) {
["id"]=>
string(1) "2"
["first_name"]=>
string(7) "prename"
["second_name"]=>
string(7) "surname"
}
}
I have tried making a few posts about this problem, but have decided to collect everything in this final one to hopefully somehow solve it.
I am building a site where users can vote on questions from a database. There's no login and so, to make sure everyone can only vote once per question, I am using their IP together with the ID of the question.
First, I get the ID and IP address and store both, making sure they are integers:
if(isset($_GET['id']))
{
//Get IP address
//Test if it is a shared client
if (!empty($_SERVER['HTTP_CLIENT_IP'])){
$ip=$_SERVER['HTTP_CLIENT_IP'];
//Is it a proxy address
}elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])){
$ip=$_SERVER['HTTP_X_FORWARDED_FOR'];
}else{
$ip=$_SERVER['REMOTE_ADDR'];
}
//Save id and IP address as variables
$id = $_GET['id'];
$ip_long = ip2long($ip);
I then check to see if the user has already votes, using the two variables. This is where I expect the problem arises. I get a:
Notice: Trying to get property of non-object
from line 116 which is: $row_cnt = $result->num_rows.
Furthermore var_dump ($result) returns bool(false) and var_dump ($row_cnt) returns Null. Adding quotes around the two variables in the query, $ip_long and $id fixes the problem while localhost, but not on my server.
A local var_dump($result) with quotes around the variables returns the following:
object(mysqli_result)#2 (5) { ["current_field"]=> int(0) ["field_count"]=> int(1) ["lengths"]=> NULL ["num_rows"]=> int(1) ["type"]=> int(0) }
I would like to add 1 to the QuestionVotes for the specific question and then remove the option to vote on that same question for the specific IP Address.
//Save id and IP address as variables
$id = $_GET['id'];
$ip_long = ip2long($ip);
///Check to see if user already voted
$stmt = $conn->prepare("SELECT * FROM User_Votes where UserID = ? and QuestionID = ?");
mysqli_stmt_bind_param($stmt, 'ss', $ip_long, $id);
$stmt->execute();
$result = $stmt->get_result();
if($result->num_rows){
//The user has already voted
echo "Already voted";
}else{
//Add IP Address and ID to the User_Votes table
$stmt = $conn->prepare("INSERT INTO User_Votes (UserID, QuestionID) VALUES (?, ?)");
mysqli_stmt_bind_param($stmt, 'ss', $ip_long, $id);
$stmt->execute();
$stmt = $conn->prepare("UPDATE Question SET QuestionVotes = QuestionVotes + 1 where QuestionID = ?");
mysqli_stmt_bind_param($stmt, 's', $id);
$stmt->execute();
}
}
And lastly, here is the code I use to build the html boxes containing database question information, add a voting button that displays the current votes and append, what is used as QuestionID, to the url:
// Build 4 question boxes from database Question table, including voting button
$stmt = $conn->prepare("SELECT * FROM question ORDER BY QuestionVotes DESC LIMIT 4");
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows > 0) {
// output data of each row
while($row = $result->fetch_assoc()) {
//$row["QuestionID"] to add id to url
echo "<div class=\"col-md-3\"><h2>". $row["QuestionHeader"]. "</h2><p>". $row["QuestionText"]. "</p><p> " . $row["QuestionVotes"] . "</p></div>";
}
}
else
{
echo "0 results";
}
My tables are as follows:
Question: QuestionID(int11)(pk), QuestionHeader(varchar(20)), QuestionText(text), QuestionVotes(int(5))
User_Votes: UserID(unsigned, int(39)), QuestionID(int(11))
There are couple of things I would like to point out. First, your error:
I get a 'Notice: Trying to get property of non-object' from line 116 which is: $row_cnt = $result->num_rows;.
When you call mysqli->query() with a select query that finds no results then returned object is not an object but instead false.
Second, instead of COUNT(*), just use *.
So to maintain your logic, you should do something like this:
//Check to see if user already voted
$result = $conn->query("SELECT * FROM User_Votes where UserID = '$ip_long' and QuestionID = '$id'");
if ($result === false) {
//Add IP Address and ID to the User_Votes table
$result = $conn->query("INSERT INTO `User_Votes` (`UserID`, `QuestionID`) VALUES ('$ip_long', '$id')");
}elseif($result && $result->num_rows) {
//The user has already voted
echo "Already voted";
}
Edited:
//Check to see if user already voted
$result = $conn->query("SELECT * FROM User_Votes where UserID = '$ip_long' and QuestionID = '$id'");
if($result->num_rows){
//The user has already voted
echo "Already voted";
}else{
//Add IP Address and ID to the User_Votes table
$result = $conn->query("INSERT INTO User_Votes (UserID, QuestionID) VALUES ('$ip_long', '$id')");
}
Re-edited:
You have to call $stmt->store_result() after $stmt->execute(). And your $stmt->get_result() is unnecessary here because you're not using the selected data.
Part of a comment from the documentation:
If you do not use mysqli_stmt_store_result( ), and immediatley call this function after executing a prepared statement, this function will usually return 0 as it has no way to know how many rows are in the result set as the result set is not saved in memory yet.
So your code should be like this:
if(isset($_GET['id']) && !empty($_GET['id'])){
$id = $_GET['id'];
$ip_long = ip2long($ip);
//Check to see if user already voted
$stmt = $conn->prepare("SELECT * FROM User_Votes where UserID = ? and QuestionID = ?");
$stmt->bind_param('ss', $ip_long, $id);
$stmt->execute();
$stmt->store_result();
if($stmt->num_rows){
//The user has already voted
echo "Already voted";
}else{
//Add IP Address and ID to the User_Votes table
$stmt = $conn->prepare("INSERT INTO User_Votes (UserID, QuestionID) VALUES (?, ?)");
$stmt->bind_param('ss', $ip_long, $id);
$stmt->execute();
$stmt = $conn->prepare("UPDATE Question SET QuestionVotes = QuestionVotes + 1 where QuestionID = ?");
$stmt->bind_param('s', $id);
$stmt->execute();
}
}
Sidenote: Please don't mix the procedural and object oriented style of mysqli.
You should check the name of your table.
You use this in one of the queries User_Votes and this user_votes in another one. It might work on your development server, if it's powered by Windows, that are case insensitive, but Linux, which probably powers your production server case-sensitive is.
Check this question for more informations: Are table names in MySQL case sensitive?
Also note, that from the code above, your app looks insecure to SQL injection. You should cast the variables to int, or what do you expect them to be.
Your insert statement is using single quotes to enclose your variables. Those should be double quotes so PHP will interpret your variables to be the values instead of the literal string.
This looks to be the root cause of what's going on. Were you able to verify everything was being written to your database tables correctly before pulling them to work on? Then verify your select statement was pulling the data correctly and what the form the data took?
And jcaran's comment is correct ... some verification of the variables you grab will need to be considered.
I have the following query in CI 2.2:
$query = $this->db->select($this->identity_column . ', username, email, id, password, active, last_login')
->where($this->identity_column, $this->db->escape_str($identity))
->limit(1)
->get($this->tables['users']);
I then run this:
if ($query->num_rows() === 1)
{
$user = $query->row();
}
num_rows() returns 1 so I get inside the if statement but $query->row() returns an empty array. I have run the produced SQL and it in fact grabs the correct row.
The query object looks like this:
object(CI_DB_pdo_result)#381 (8) { ["num_rows"]=> int(1) ["conn_id"]=> object(PDO)#380 (0) { } ["result_id"]=> object(PDOStatement)#387 (1) { ["queryString"]=> string(115) "SELECT username, username, email, id, password, active, last_login FROM users WHERE username = 'CORAIR4JK' LIMIT 1" } ["result_array"]=> array(0) { } ["result_object"]=> array(0) { } ["custom_result_object"]=> array(0) { } ["current_row"]=> int(0) ["row_data"]=> NULL }
This ain't no driver issue and do not go from PDO to OLD MySQL as PDO is much more secure, to get results you must use below,
if ($query->num_rows() === 1)
{
$user = $query->result_array();
}
This will load all the database results into $user and will make it an array.
enjoy!!
I have an array like this which contains a lot of row and I need to insert it into a MySQL database using PDO
array(3067) {
[0]=>
array(2) {
["order"]=>
string(7) "2854811"
["consignment"]=>
string(0) ""
}
[1]=>
array(2) {
["blah"]=>
string(7) "2854811"
["whatever"]=>
string(2) "someval"
}
[4]=>
array(2) {
["blah"]=>
string(7) "2864412"
["whatever"]=>
string(0) ""
}
I have tried various combinations of suggestions made on here but each suggestion gets a different error message
php PDO insert batch multiple rows with placeholders
PDO MySQL: Insert multiple rows in one query
I have tried this
$db->beginTransaction();
$stmt = $db->prepare("INSERT INTO mytable (column1, column2) VALUES (:blah, :whatever)");
foreach($test as $insertRow){
// now loop through each inner array to match binded values
foreach($insertRow as $column => $value){
$stmt->bindParam(":{$column}", $value);
$stmt->execute();
}
}
$db->commit();
but i get this error message
Uncaught exception 'PDOException' with message 'SQLSTATE[HY093]:
Invalid parameter number: number of bound variables does not match
number of tokens
and I've also tried
$sql = 'INSERT INTO mytable (blah, whatever) VALUES ';
$insertQuery = array();
$insertData = array();
$n = 0;
foreach ($input as $row) {
$insertQuery[] = '(:blah' . $n . ', :whatever' . $n . ')';
$insertData['blah' . $n] = $row['blah'];
$insertData['whatever' . $n] = $row['whatever'];
$n++;
}
if (!empty($insertQuery)) {
$sql .= implode(', ', $insertQuery);
$stmt = $db->prepare($sql);
$stmt->execute($insertData);
}
but i get this error message which makes no sense as each length of 'blah' are the same
Uncaught exception 'PDOException' with message 'SQLSTATE[22001]:
String data, right truncated: 1406 Data too long for column
'order_number' at row 1625'
How can i get my array to insert into the database? I'm not fussy if i have to execute a load of times or just once as long as I can get it to insert.
EDIT
What I am trying to do is read in a text file and insert it into an array which works perfectly so all i'm left with is an associative array with about 3000 rows and they each contain a field called 'blah' and 'whatever'.
After I get my array, i need to insert it into a MySQL database
CREATE TABLE IF NOT EXISTS `tracker` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`blah` varchar(8) NOT NULL,
`whatever` varchar(25) NOT NULL,
`input_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB
I should end up with about 3000 rows that was inserted from my array.
I hope this makes sense. If not I'll add some more
I believe you almost had it with this example:
$db->beginTransaction();
$stmt = $db->prepare("INSERT INTO mytable (column1, column2) VALUES (:blah, :whatever)");
foreach($test as $insertRow){
// now loop through each inner array to match binded values
foreach($insertRow as $column => $value){
$stmt->bindParam(":{$column}", $value);
$stmt->execute();
}
}
$db->commit();
The problem you are running into is you are calling execute() before you have bound the proper number of parameters. Instead, you need to bind all of your parameters first, then call execute().
$db->beginTransaction();
$stmt = $db->prepare("INSERT INTO mytable (column1, column2) VALUES (:blah, :whatever)");
foreach($test as $insertRow){
// now loop through each inner array to match binded values
foreach($insertRow as $column => $value){
$stmt->bindParam(":{$column}", $value);
}
}
// NOW DO EXECUTE
$stmt->execute();
$db->commit();
EDIT
In response to your comment, it's hard to tell exactly what you are trying to accomplish, but if you are only receiving one record, then it is because of what Gerald brought up, these are separate queries to all be transacted at once. Take a look at this revision:
// Start Transaction
$db->beginTransaction();
// Insert each record
foreach($test as $insertRow){
// Prepare statement
$stmt = $db->prepare("INSERT INTO mytable (column1, column2) VALUES (:blah, :whatever)");
// now loop through each inner array to match binded values
foreach($insertRow as $column => $value){
$stmt->bindValue(":{$column}", $value);
}
// Execute statement to add to transaction
$stmt->execute();
// Clear statement for next record (not necessary, but good practice)
$stmt = null;
}
// Commit all inserts
$db->commit();