I have some issues with an update script. I bind my values but it returns false and I cannot see what I am doing wrong.
I am running this:
$row = $db->query('
UPDATE '. $config->db_prefix .'_adverts
SET ad_type = ?,
title = ?,
text = ?,
price = ?,
category = ?,
condition = ?
WHERE aid = ?')
->bind(1, $ad_type)
->bind(2, $title)
->bind(3, $text)
->bind(4, $price)
->bind(5, $category)
->bind(6, $condition)
->bind(7, $aid)->execute();
}
The bind function is this:
public function bind($pos, $value, $type = null) {
if( is_null($type) ) {
switch( true ) {
case is_int($value):
$type = PDO::PARAM_INT;
break;
case is_bool($value):
$type = PDO::PARAM_BOOL;
break;
case is_null($value):
$type = PDO::PARAM_NULL;
break;
default:
$type = PDO::PARAM_STR;
}
}
$this->stmt->bindValue($pos, $value, $type);
return $this;
}
a var_dump($this) gives me:
object(DB)#1 (2) { ["dbh":protected]=> object(PDO)#2 (0) { } ["stmt":protected]=> object(PDOStatement)#15 (1) { ["queryString"]=> string(211) " UPDATE rno_adverts SET ad_type = ?, title = ?, text = ?, price = ?, category = ?, condition = ? WHERE aid = ?" } }
but I can't see what is wrong.
EDIT:
query function is this:
public function query($query) {
$this->stmt = $this->dbh->prepare($query);
return $this;
}
and execute is this:
public function execute($var = null) {
return $this->stmt->execute($var);
}
ERROR:
Uncaught exception 'PDOException' with message 'SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'condition = 3 WHERE aid = 1'
The output of the query:
UPDATE rno_adverts SET ad_type = 3, title = "Gul bil", text = "En flot gul bil med hvide striber", price = 500, category = 4, condition = 3 WHERE aid = 1
I have gone blind on this query so I can't see what the problem is. If I remove category and condition, it works without problems. Both fields are INT NOT NULL in the database.
You can only use the bind functions with prepared statements. ->query() is for generating queries where you already know all the values/variables of a query beforehand.
Just get rid of this bind function and be all right
$sql = 'UPDATE '. $config->db_prefix .'_adverts SET
ad_type = ?, title = ?, text = ?, price = ?, category = ?, condition = ?
WHERE aid = ?';
$db->query($sql);
$db->execute(array($ad_type, $title, $text, $price, $category, $condition, $aid));
From your question one can not say specifically where the error is located. I therefore suggest you add more error checks first, e.g. with bindvalue(), you don't check the return value:
$this->stmt->bindValue($pos, $value, $type);
Instead throw an exception if this failed:
$bind = $this->stmt->bindValue($pos, $value, $type);
if (!$bind) {
throw new Exception(sprintf("Unable to bind parameter %s with a value of type #%d", $pos, $type));
}
This will prevent you have problems binding a value but it got unnoticed.
Similar with the execution. If you want to provide information why it fails, you need to read out the error information and throw an exception.
Related
I need to know how to get the result of a select statement that is executed after an insert statement as one execute in PDO.
My PDO connection parameters are as follows:
$opt = [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES => true];
$conn = new PDO($dsn, $user, $pass, $opt);
I have the following helper function that i use for my PDO statement Execution:
function databaseExecute($SQL, $BIND_P, &$BIND_R) {
global $conn;
$stmt = $conn->prepare($SQL);
if ($stmt->execute($BIND_P)) {
if ($BIND_R !== false) {
//Type testing is important here
$tmp = $stmt->fetchAll(PDO::FETCH_ASSOC);
$stmt->closeCursor();
if (!$tmp || count($tmp) == 0) {
return false;
}
$BIND_R = $tmp;
} else {
$stmt->closeCursor();
}
return true;
}
$stmt->closeCursor();
return false;
}
My function itself is:
/**
* Adds the current purchase object to the database table
* #return true if success
*/
public function pushToDB() {
global $tbl_purchases;
//We don't push purchaseID since that field is auto handled by the database
$sql = "INSERT INTO " . $tbl_purchases . " (ProductID, UID, TID, GenDate, KeyIDs, Total, Assigned) VALUES (?, ?, ?, ?, ?, ?, ?); SELECT LAST_INSERT_ID();";
$result = array();
if (databaseExecute($sql, array(
$this->getProductID(),
$this->getUID(),
$this->getTID(),
$this->getGenDate(),
$this->getKeyIDsJSON(),
$this->getTotal(),
$this->getAssigned(),
), $r)) {
var_dump($result);
$this->_setPurchaseID($result[0]);
return true;
}
trigger_error("Purchase::pushToDB - Could not push purchase to database", E_USER_ERROR);
return false;
}
But this throws a general error
Fatal error: Uncaught PDOException: SQLSTATE[HY000]: General error when i attempt to fetchAll
In this situation, how do i get the result of the SQL execution?
PS: Using Two executes is not acceptable here.
Using Two executes is not acceptable here.
This is but a delusion.
Use either second query or - better - a dedicated function PDO::LastInsertId(). But with your rather poorly designed function it could be a problem. So be it 2 queries.
So change your functions to
function databaseExecute($SQL, $BIND_P = array();) {
global $conn;
if (!$BIND_P)
{
return $conn->query($SQL);
}
$stmt = $conn->prepare($SQL);
$stmt->execute($BIND_P);
return $stmt;
}
and
public function pushToDB() {
global $tbl_purchases;
//We don't push purchaseID since that field is auto handled by the database
$sql = "INSERT INTO $tbl_purchases
(ProductID, UID, TID, GenDate, KeyIDs, Total, Assigned)
VALUES (?, ?, ?, ?, ?, ?, ?)";
databaseExecute($sql, array(
$this->getProductID(),
$this->getUID(),
$this->getTID(),
$this->getGenDate(),
$this->getKeyIDsJSON(),
$this->getTotal(),
$this->getAssigned(),
));
$id = databaseExecute("SELECT LAST_INSERT_ID()")->fetchColumn();
$this->_setPurchaseID($db);
return true;
}
}
You can alter your databaseExectute function to take an extra parameter of 'SecondResult' (for example), then change it to something like...
function databaseExecute($SQL, $BIND_P, &$BIND_R,$SecondResult) {
global $conn;
$stmt = $conn->prepare($SQL);
if ($stmt->execute($BIND_P)) {
if ($BIND_R !== false) {
//Type testing is important here
if ($SecondResult) $stmt->nextRowSet(); // this will ensure that the fetchAll will return the data from the 2nd query
$tmp = $stmt->fetchAll(PDO::FETCH_ASSOC);
$stmt->closeCursor();
if (!$tmp || count($tmp) == 0) {
return false;
}
$BIND_R = $tmp;
} else {
$stmt->closeCursor();
}
return true;
}
$stmt->closeCursor();
return false;
}
I just typed this in to here directly, I haven't tested it, but it should work.
Also, I'm not saying that the other comments are wrong, and there might be a better way of doing this, but you CAN run two queries within the same 'statement'.
I insert data into a table called 'roster'. The first column (id_roster) is an id using mysql auto-increment.
I run a SELECT to find the id_roster
I use this id_roster to insert it into a table 'roster_par_membre' along with other data
if ($insert_stmt = $mysqli->prepare("INSERT INTO `roster`(`nom_roster`, `description_roster`, `id_organisation`, `created_by`, `creation_date`,`modified_by`) VALUES (?, ?, ?, ?, ?, ?)")) {
$insert_stmt->bind_param('ssiisi', $roster_name, $description_roster, $organisation_id, $user_id, $creation_date, $user_id);
if (!$insert_stmt->execute()) {
$reponse = 'Sorry, a database error occurred; please try later';
} else {
// if INSERT OK -> create a new line in roster_membre table
//1. get the roster_id
$sql = "SELECT r.id_roster
FROM roster r
WHERE r.nom_roster = ?
LIMIT 1";
$stmt = $mysqli->prepare($sql);
if ($stmt) {
$stmt->bind_param('s', $roster_name);
$stmt->execute(); // Execute the prepared query.
$stmt->store_result();
$stmt->bind_result($id_roster);
$stmt->fetch();
$level = 1;
//2. create a line with the roster_id and insert the membre as level 1
$insert_stmt = $mysqli->prepare("INSERT INTO `roster_par_membre`(`id_membre`, `id_roster`, `level`, `modified_by`) VALUES (?,?,?,?)");
$insert_stmt->bind_param('iiii', $user_id, $id_roster, $level, $user_id);
$insert_stmt->execute();
$reponse = 'success';
}
So far the code is working but it is not very nice.
Is there a way when we create a new line in a table to directly return a value (id with auto-increment) to be used in a sql query (to insert data into a second table)? or maybe to merge the two query (the two INSERT) in one statment?
short edit: it is an AJAX $response the return value (JSON)
Ok,solution:
//1. get the roster_id
$sql = "SELECT r.id_roster
FROM roster r
WHERE r.nom_roster = ?
LIMIT 1";
$stmt = $mysqli->prepare($sql);
if ($stmt) {
$stmt->bind_param('s', $roster_name);
$stmt->execute(); // Execute the prepared query.
$stmt->store_result();
$stmt->bind_result($id_roster);
$stmt->fetch();
Just need to replace all this part by
$id_roster = $mysqli->insert_id;
nice and easy. THANKS to albanx
these are the functions I used for query on projects that I do not want to use any framework (just php):
/**
*
* Executes query methods
* #param string $query the query string
* #param array $vals array of values
* #param bool $show show the query
* #return int/array/false
*/
function q($query, $vals=array(), $show_query=false)
{
$conn = new mysqli(...)
$offset = 0;
foreach ($vals as $v)
{
$cv = $conn->real_escape_string($v);//escape the value for avoiding sql injection
$fv = ($v===NULL) ? 'NULL':"'".$cv."'"; //if value is null then insert NULL in db
$qpos = strpos($query, '?', $offset);//replace the ? with the valeue
$query = substr($query, 0, $qpos).$fv.substr($query, $qpos+1);
$offset = $qpos+strlen($cv)+1;
}
$result = $conn->query($query);
if($show || $result===false) echo $query."<br>";
$rows = array();
if($result===true)
{
return $conn->affected_rows;
}
else if($result===false)
{
return false;
}
else
{
while ($row = $result->fetch_array(MYSQLI_ASSOC) )
{
$rows[]=$row;
}
}
return $rows;
}
function lastid()
{
return $this->qval("SELECT LAST_INSERT_ID()");
}
Usage example:
q('INSERT INTO USER(name, email) VALUES(?,?)', array('admin','admin#admin.com'));
$id = lastid();
i need somehow to check if field exist in db, and then if exist, just update price, if not, insert fields. I am using simplxml for parsing data to db from xml.
here is my code without if statement, just insert into two databese.
I need to check from db products if ident exist, so if not exist, do all that code down, if exist just update price in db products
foreach ($lib->product as $data) {
$manufacturer = (string) $data->manufacturer;
$ident = (string) $data->id;
$name = (string) $data->name;
$category = (string) $data->category;
$subcategory = (string) $data->subcategory;
$price = (int) ($data->price * 1.2 * 1.4 * 1.1);
$image = (string) $data->images->image[0];
$insert = $db->prepare('INSERT INTO products (ident, manufacturer,name,category,subcategory,price,image) VALUES (?, ?, ?, ?, ?, ?, ?)');
$insert->bind_param('sssssss', $ident, $manufacturer, $name, $category, $subcategory, $price, $image);
$insert->execute();
foreach($data->specifications->attribute_group as $group) {
$attribute_group = (string) $group->attributes()['name'];
foreach($group as $attr) {
$attribute = (string) $attr->attributes()['name'];
$value = (string) $attr->value;
$insert = $db->prepare('INSERT INTO spec (attr_group,attr_name, attr_value, product_id) VALUES (?, ?, ?, ?)');
$insert->bind_param('ssss', $attribute_group, $attribute, $value, $ident);
$insert->execute();
}
}
}
To do in one query, look up MySQL's ON DUPLICATE KEY UPDATE functionality for INSERT.
Then use $insert->rowCount() if you're using PDO or $insert->affected_rows for mysqli.
If the first insert tried to already insert a key that existed and updates a value, then rowCount()/affected_rows will be 2; if it just inserted a record then rowCount()/affected_rows will be 1. It will be 0 if the INSERT was unsuccessful.
e.g. for PDO:
switch($insert->rowCount()) {
case 2: // UPDATE occurred thanks to 'ON DUPLICATE UPDATE KEY'
// SOME CODE HERE IF YOU LIKE
break;
case 1: // INSERT occurred as no duplicate
// CODE TO INSERT INTO SECOND TABLE
break;
case 0:
default:
// NEITHER THE ABOVE OCCURRED SO CODE TO HANDLE ERROR
}
$query = "SELECT * name FROM yourtable";
$bool = true;
if($r = #mysql_query($query))
{
while($row = #mysql_fetch_array($r))
{
if( ($row['id'] == $id))
{
$bool = false;
break;
}
}
}
if($bool) {
UPDATE
}
else
{
INSERT
}
I'm starting with mysql prepared statements and I can't advance due to an error that I don't understand. Here's my function to update de db:
public function updateUserData($user_label,$user_alliance, $score, $rank, $timestamp, $user_id, $db_object){
$sql='UPDATE users SET label = ?, alliance = ?, points = ?, position = ?, modified = ?, WHERE user_id = ?';
$label = $user_label;
$alliance = $user_alliance;
$points = $score;
$position = $rank;
$modified = $timestamp;
$user_id_q = $user_id;
$stmt = $db_object->prepare($sql);
if($stmt === false) {
trigger_error('Wrong SQL: ' . $sql . ' Error: ' . $db_object->error, E_USER_ERROR);
}
$stmt->bind_param('ssiiii',$label,$alliance,$points,$position,$modified,$user_id_q);
/* Execute statement */
$stmt->execute();
echo $stmt->affected_rows;
$stmt->close();
}
Here's how I use it:
//Get user Data
$user = new user();
$page_clasif = $user->getPagClasif($ch,$url_clasif);
$user_label = $user->findPlayerName($page_clasif);
$user_alliance = $user->findAllianceName($page_clasif);
$rank = $user->findRank($page_clasif);
$score = $user->findScore($page_clasif);
$user_id = $user->findPlayerId($page_clasif);
$version = $user->findVersion($page_clasif);
$user_universe = $user->findUniverse($page_clasif);
//Get install date as timestamp
$core = new core();
$timestamp = $core->dateAsTimestamp();
//Update User
$user->updateUserData($user_label,$user_alliance,$score,$rank,$timestamp,$user_id,$conn);
Here's the error:
PHP Fatal error: Wrong SQL: UPDATE users SET label = ?, alliance = ?, points = ?, position = ?, modified = ?, WHERE user_id = ? Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'WHERE user_id = ?'
Any ideas?
Thanks in advance.
You have a superfluous comma before the WHERE keyword:
$sql='UPDATE users SET label = ?, alliance = ?, points = ?, position = ?, modified = ?, WHERE user_id = ?';
// remove this comma --^
I'm trying to convert a code I write to use the php function mysqli_stmt_bind_param() instead of replacing tag in prewritten statement which I believe is not a good pratice.
So here one of the function I have today:
$idTag1="###";
$requestReplaceArray = array("%PRODUCT_ID%","%PLATFORM_ID%","%STATUS_ID%","%DATE%","%COMMENT%",$idTag1);
$updateRequest="UPDATE REQUEST
SET
id_product = %PRODUCT_ID%,
id_platform = %PLATFORM_ID%,
id_status = %STATUS_ID%,
last_modifier = '".$_SERVER['PHP_AUTH_USER']."',
last_modification_date='%DATE%',
last_comment = '%COMMENT%'
WHERE id =".$idTag1;
function updateRequest($id, $productID, $platformID, $statusID, $date, $comment){
global $requestReplaceArray, $updateRequest;
escapeArguments($id, $productID, $platformID, $statusID, $date,$comment);
/*if number value is empty replace by 'null'*/
$productID=replaceEmptyValueByNull($productID);
$platformID=replaceEmptyValueByNull($platformID);
$statusID=replaceEmptyValueByNull($statusID);
$dbConnection = getDbConnection();
$replacement = array($productID, $platformID, $statusID,$date ,$comment, $id);
$updateRequest = str_replace($requestReplaceArray, $replacement, $updateRequest);
if(! $result = mysqli_query( $dbConnection, $updateRequest ) ) {
mysqli_free_result($result);
$dbConnection->close();
return "Error MyU02, can't update the request #".$id;
}else{
mysqli_free_result($result);
$dbConnection->close();
return $id;
}
}
This code isn't perfect but it works except when a $date is NULL.
My objectives is to now use that synthax :
$requestReplaceString = "iiissi";
$updateRequest="UPDATE REQUEST
SET
id_product = ?,
id_platform = ?,
id_status = ?,
last_modifier = '".$_SERVER['PHP_AUTH_USER']."',
last_modification_date=?,
last_comment = ?
WHERE id =?";
function updateRequest($id, $productID, $platformID, $statusID, $date, $comment){
global $requestReplaceString, $updateRequest;
$dbConnection = getDbConnection();
$stmt = mysqli_prepare( $dbConnection, $updateRequest);
mysqli_stmt_bind_param($stmt, $requestReplaceString, $productID, $platformID, $statusID,$date ,$comment, $id);
if(mysqli_stmt_execute($stmt) ) {
mysqli_stmt_close($stmt);
$dbConnection->close();
return $id;
}else{
mysqli_stmt_close($stmt);
$dbConnection->close();
return "Error MyU02, can't update the request #".$id;
}
}
My main issue here is to set some value to null because trying to bind a php NULL is not working at all. So my question is how can I bind NULL with mysqli if it's possible ?
Edit: it does work and my mistake comes from my bad code, the code is now corrected.
Just bind it. It works perfectly. Any null value will be sent to server as mysql's NULL.
Also note that there is a fatal issue with your code: you are connecting to database for the every query. Which will just kill your server.