I'm getting a strange result, where an update method (http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/data-retrieval-and-manipulation.html#update) call that activates/deactivates site users accounts in my mySQL database sometimes are not updated and returns a 0. Here is the code:
$user_id = '198';
$sqlSetStmnt= [ 'activation_code' => NULL, 'active' => 0 ];
$conn = $this->get( 'database_connection' );
try {
$result = $conn->update( 'users', $sqlSetStmnt,[ 'id' => (int)$user_id ] );
}
catch( Exception $e ) {
$error = $e->getMessage();
throw new \Exception( 'update user account data function -- ' .
'error: ' . $error . ' - ' .
'unable to update your account!' );
} // End of catch( Exception ) block.
if( ( $result === FALSE ) || ( $result !== 1 ) ) {
throw new \Exception( 'update user account data function -- ' .
'update return value: ' . $result . ' - ' .
'unable to update your account!' );
} // if( ( $result === FALSE ) || ( $result !== 1 ) ) ...
The users table has an id int(11) column which is the primary key, an active tinyint(1) column, and activation_code varchar(40) NULL column.
Please note the $user_id variable contains a string value of '198', but it is cast to an int when creating the $sqlSetStmnt value.
Inspecting the users table confirms that there was a row in the users table with the id column value of 198 at the time of the update call.
The account used when running the update call has enough privileges to change the row active and activation_code column values.
There are no other users in the system or accessing the database, so there aren't any locks on the row.
I inspected the code using x-debug, and the values of the $user_id and $sqlSetStmnt variables were properly set to the values that I expected, that $result was set to 0 by the update method, and that no exceptions were thrown by the update method call.
By the way, there is no need to using variable binding because the values in the $user_id and $sqlSetStmnt variables are not input by an user, so no possibility of SQL-Injection, or Buffer-Overrun.
Is there some way to get information from DBAL about why the update method returned 0?
Thank you.
Before solving this issue I switched from:
$result = $conn->update( 'users', $sqlSetStmnt, [ 'id' => (int)$user_id ] );
to:
$sqlUpdateStmnt = 'UPDATE `users` SET field = value ' .
'WHERE `id` = ' . $user_id;
$result = $conn->executeUpdate( $sqlUpdateStmnt );
and got exactly the same result, where some updates would return 0 rows when there definitely was a row in the users table with an id matching the value of $user_id.
I got around this problem by fetching the existing row and then only updating the row when the fields in the set-clause were different than the same columns from the table.
This tells me that the 0 return value wasn't that there were no matching rows, but that the updated didn't have any effect on any rows.
So this issue isn't a bug, so much as a misunderstanding of the result. However, the problem still exists when using the update() method of how to determine when the update failed due to no matching rows and when no changes were made.
The solution that I ended up solves this at the cost of a pre-fetch to verify that the update would affect a row. In my case, with a multi-user database application, pre-fetching isn't actually a problem because the row that is to be updated could have been deleted by another user before the update tries to make its change. But, it would be nice if both update methods explained this more clearly, and returned different values: 0 for no affected rows and FALSE for no rows found.
Related
I constantly need to "reset" the AUTO_INCREMENT value of my tables, after I delete a part of my rows. Let me explain with an actual example :
I have a table called CLIENT. Let us say before removing some rows, the auto_increment was set to 11. Then I delete the 4 lasts rows. The auto_increment is still set to 11. So when I will insert some clients again, it will make a hole of id.
I always need to "clean" the auto_increment, e.g. using this function below :
function cleanAutoIncrement($tableName, $columnAutoIncrement, $pdo)
{
$r = false;
try {
$p = $pdo->prepare("SELECT IFNULL(MAX($columnAutoIncrement) + 1, 1) AS 'max' FROM $tableName LIMIT 1;");
$p->execute();
$max = $p->fetch(PDO::FETCH_ASSOC)['max'];
$p = $pdo->prepare("ALTER TABLE $tableName AUTO_INCREMENT = $max;"):
$p->execute();
$r = true;
}
catch(Exception $e) {
$r = false;
}
return $r;
}
What the function do is to get the maximum id in the table, then increments it of 1, and return its value (if there was no rows in table, it return 1). Then I alter the table to reset a "clean" id in order not to let any hole of id.
QUESTION
Is there any MySQL command to perform this task without having to do this manually ?
To close this question, no shortcut exists in MySQL and it is not recommended to perform this task.
Low level question but, I understand that you can select elements from a table using:
$sql = "SELECT blah FROM TABLE WHERE this = 'something' ";
But when I try to select a specific value from my table, where let's say a user has no tries left so if I try to grab how many tries they have left with:
$sql = "SELECT tries FROM table WHERE user = 'something'";
How would I grab that value specifically if it was 5 or 9? I tried setting a variable equal to something I $sql off my table but it doesn't grab the value.
Edit
I have a database that has a table called Item which contains: id, name, value, and stock of a particular item. If a user wants to order that item I will first check it if's in stock with a function, to see if it is not in stock then a error message is printed, otherwise accept the order.
Extremely primitive since I'm just trying to get grab the stock value first.
$query = $_GET['query']; //id I get from the specified item
echo 'the id is: ' .$query.''; //test purposes
$mysql_handle = mysql_connect($dbhost, $dbuser, $dbpass)
or die("Error connecting to database server");
mysql_select_db($dbname, $mysql_handle)
or die("Error selecting database: $dbname");
$sql1 = "SELECT item_stock FROM chat-db.Item WHERE id = '".$query."'";
echo '' .$sql2. ''; //test purposes
whats the correct way to assign the value from that specific stock to a variable?
If you want to grab rows with a set of possible values you can use 'IN' such as:
Get all columns from users table where users have 5 or 9 tries:
SELECT * FROM users WHERE tries IN('5', '9'); or
If you want to select where the user has no tries left, assuming the tries column is a numeric type you can look for rows with 0 tries:
Get all columns from Item table where stock is 0:
SELECT * FROM db_inv.Item WHERE stock = '0';
Get all columns from users table where tries is 0:
SELECT * FROM users WHERE tries = '0';
As for your php code you should be able to do the following:
$query = $_GET['query']; //id I get from the specified item
echo 'the id is: ' . $query; //test purposes
$mysql_handle = mysqli_connect($dbhost, $dbuser, $dbpass) or die("Error connecting to database server");
$sql1 = "SELECT item_stock FROM chat-db.Item WHERE id = '".$query."'";
$results = mysqli_query($mysql_handle, $sql1);
if (!empty($results) && mysqli_num_rows($results) > 0) {
while($rec = mysqli_fetch_array($results)) {
echo $rec['item_stock'];
}
}
I created a php function to fetch records from a sql table subscriptions, and I want to add a condition to mysql_query to ignore the records in table subscriptions that exists in table removed_items, here is my code;
function subscriptions_func($user_id, $limit){
$subs = array();
$sub_query = mysql_query("
SELECT `subscriptions`.`fo_id`, `subscriptions`.`for_id`, `picture`.`since`, `picture`.`user_id`, `picture`.`pic_id`
FROM `subscriptions`
LEFT JOIN `picture`
ON `subscriptions`.`fo_id` = `picture`.`user_id`
WHERE `subscriptions`.`for_id` = $user_id
AND `picture`.`since` > `subscriptions`.`timmp`
GROUP BY `subscriptions`.`fo_id`
ORDER BY MAX(`picture`.`since_id`) DESC
$limit
");
while ($sub_row = mysql_fetch_assoc($sub_query)) {
$subs [] = array(
'fo_id' => $sub_row['fo_id'],
'for_id' => $sub_row['for_id'],
'user_id' => $sub_row['user_id'],
'pic_id' => $sub_row['pic_id'],
'since' => $sub_row['since']
);
}
return $subs ;
}
My solution is to create another function to fetch the records from table removed_items and set a php condition where I call subscriptions_func() to skip/unset the records that resemble the records in subscriptions_func(), as the following
$sub = subscriptions_func($user_id);
foreach($sub as $sub){
$rmv_sub = rmv_items_func($sub[‘pic_id’]);
If($rmv_sub[‘pic_id’] != $sub[‘pic_id’]){
echo $sub[‘pic_id’];
}
}
This solution succeeded to skip the items in the table removed_items however this solution makes gaps in the array stored in the variable $sub which makes plank spots in the echoed items.
Is there a condition I can add to the function subscriptions_func() to cut all the additional conditions and checks?
Assuming id is the primary key of subscriptions and subs_id is the foreign key in removed_items, then you just have to add a condition to the WHERE clause. Something like this should work :
...
AND `subscriptions`.id NOT IN (SELECT `removed_items`.subs_id FROM `removed_items`)
...
Not related to your problem :
Your code seems vulnerable to SQL injection : use prepared statement to prevent this.
The original Mysql API is deprecated, it is highly recommended to switch to Mysqli instead.
I have a table structure with META_ID | KEY | VALUE | USER_ID where META_ID is auto-increment. Now in my php logic
1 get the result key-value pairs per user
2 delete the key value row per user
3 update or insert the key value pair for a already known USER_ID
4 insert key value pair for a new user
But the META_ID keeps growing, so i was wondering if i could just delete the META_ID column?
Case logic
An registered user or returning registered user can update their form over time if they haven't submit it yet. So overtime an user can select and deselect certain form options and update, insert or delete is triggered.
Now the logic behind "returning user deselects a key (and the row needs to be deleted)" gives me a problem. That's why i just delete all users key-value pairs. But what would be the right way?
So if the key-value exists in the db table but not in $params i need to delete it!
btw here's my function
function user_shopping_meta_data($params) {
global $wpdb;
$shopping_meta_table = 'wp_shopping_metavalues';
$wp_user_id = $params['wp_user_id'];
//1 CHECK IF USER HAS KEY VALUE PAIRS
$checkKeyValues = $wpdb->get_results("SELECT meta_shopping_key FROM $shopping_meta_table WHERE wp_user_id = '$wp_user_id'");
//2 WE DELETE
$qdel = $wpdb->delete($shopping_meta_table, array('wp_user_id' => $wp_user_id));
//3 UPDATE OR INSERT
foreach ($params as $key => $val) {
//variables
if (is_array($val)) {
$val = json_encode($val);
}
$shopping_meta_values = array(
'wp_user_id' => $wp_user_id,
'meta_shopping_key' => $key,
'meta_shopping_value' => $val
);
if (count($checkKeyValues) > 0) {//3 USER IS KNOWN SO UPDATE and/or INSERT new key-value
foreach ($checkKeyValues as $check) {
//UPDATE OR INSERT
if (($key != "wp_user_id")) {
//FOR UPDATE where
$shopping_meta_where = array('meta_shopping_key' => $key, 'wp_user_id' => $wp_user_id);
$result = $wpdb->get_results("SELECT * FROM $shopping_meta_table WHERE meta_shopping_key = '" . $key . "' AND wp_user_id = '$wp_user_id'");
if (count($result) > 0) {//KEY ALREADY EXISTS FOR USER
$return .= $wpdb->update($shopping_meta_table, array('meta_shopping_key' => $key, 'meta_shopping_value' => $val), $shopping_meta_where) . '<br/>';
//$return .= 'UDPATE<br/>';
} else {//KEY IS NEW
$return .= $wpdb->insert($shopping_meta_table, $shopping_meta_values) . '<br/>';
// $return .= 'INSERT for old';
}
}//.end $key
}//.end foreach checkKeyValue
}//.end count
else {//4 INSERT KEY VALUE PAIR FOR NEW USER
if (($key != "wp_user_id")) {
$return .= $wpdb->insert($shopping_meta_table, $shopping_meta_values) . '<br/>';
// $return .= 'INSERT NEW';
}
}
}//.end each
echo 'Test return: ' . $return;
}
You won't gain much by deleting it. You might think that you save some space, but in fact you don't. An auto_increment column is always also (part of) the primary key. If you delete it, MySQL will create an "implicit" primary key, which is not visible but necessary for MySQL to identify rows. Also you will lose some comfort like not being able to use LAST_INSERT_ID().
You can very well delete it. It is just a unique ID. If you can distinguish different rows without the META_ID or you do not need to distinguish rows, then META_ID is redundant.
If i can give you a suggest is better to leave that field as a history.
If you need to want to know what is the last action done for that user you can order by META_ID.
Is usefull to have a primary key in a table. But this is just a suggest
I suggest you have a primary key that you are sure of that it is unique. It is a good idea to use a auto-increment column for this because you will always be sure that it is unique.
I am having an issue with inserting an array of information into a mysql database. Basically I built a sortable gallery similar to Facebook's photo albums that can be arranged by moving the div to a new spot with jquery's sortable function.
I am using Ajax to call a php file which will inser the new order of the div's into the DB. The information is being passed correctly, it is just not being inserted correctly.
The error I am receiving is:
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 'Array' at line 1
The Php code is:
foreach ($_GET['listItem'] as $position => $item) {
if ($item >= 1) {
$sql[] = "UPDATE table SET order = '{$position}' WHERE id = '{$item}'";
mysql_query($sql) or die(mysql_error());
}
}
If I remove the mysql_query function and just do a print_r, I get:
Array
(
[0] => UPDATE table SET order = '0' WHERE id = '2'
[1] => UPDATE table SET order = '1' WHERE id = '4'
[2] => UPDATE table SET order = '2' WHERE id = '3'
[3] => UPDATE table SET order = '3' WHERE id = '1'
[4] => UPDATE table SET order = '4' WHERE id = '5'
[5] => UPDATE table SET order = '5' WHERE id = '6'
)
This is the first time I have tried to do something like this. Any help would be great.
Thank you in advance for the help!
In mysql_query($sql) $sql is an array, therefore it's value is simply Array. When you assign $sql[] = "UPDATE table SET order = '{$position}' WHERE id = '{$item}'"; simply make this line $sql = "UPDATE table SET order = '{$position}' WHERE id = '{$item}'";. That should solve your problem.
EDIT:
You can leave the [] and simply remove the mysql_query from where it is. After your foreach list item, add this:
foreach($sql as $query) {
mysql_query($query);
}
Sounds like there is some confusion about what the [] operator does. You use [] when you want to append an element to the end of an existing array.
For example:
$sql = array();
$sql[] = 'UPDATE table SET order = "0" WHERE id = "2"';
mysql_query($sql); // this will produce the error you are seeing
Versus:
$sql = 'UPDATE table SET order = "0" WHERE id = "2"';
mysql_query($sql); // this will work
You should rewrite your code as such:
foreach ($_GET['listItem'] as $position => $item) {
if ($item >= 1) {
$sql = "UPDATE table SET order = '{$position}' WHERE id = '{$item}'";
mysql_query($sql) or die(mysql_error());
}
}
That will do what you are intending. However, this is still not a good idea, since you are passing untrusted $_GET data directly to the database. I could, for example, call your script with a string like:
http://yoursite.com/yourscript.php?listItem=1'%3B%20DROP%20TABLE%20yourtable%3B
Since the value of listItem is going directly to the database -- and the $item >= 1 check is insufficient, since PHP will evaluate a string as an integer if it begins with numeric data -- all I have to do is add a single quote to terminate the previous query, and I am then free to inject whatever SQL command I'd like; this is a basic SQL injection attack. Whenever you write database-touching code, you should cleanse any input that might be going to the database. A final version of your code might look like:
foreach ($_GET['listItem'] as $position => $item) {
if ($item >= 1) { // this check may or may not be needed depending on its purpose
$sql = 'UPDATE table SET order = "' . mysql_real_escape_string($position) . '" WHERE id = "' . mysql_real_escape_string($item) . '"';
mysql_query($sql) or die(mysql_error());
}
}
There are other ways to cleanse input data as well, that is just one of them. Hope that helps.