SQL/PHP - REPLACE statement not working as intended - php

I'm trying to 'remove' part of a string in a DB entry after a delete command is made.
Say a PDF file gets deleted, i want to remove the reference of it by replacing it with '' (but keep the other references). That works fine, but now i also want to do it with another column, and the exact same setup doesn't seem to work and i can't figure out why.
This works:
$stmt_2 = $db->prepare("UPDATE data_videothek SET pdfAttachment = REPLACE(pdfAttachment, $id, '') WHERE pdfAttachment LIKE ?");
$stmt_2->execute(array( $like_id_string ));
This doesn't:
$stmt_2 = $db->prepare("UPDATE data_videothek SET speaker_img = REPLACE(speaker_img, $thefile, '' ) WHERE speaker_img LIKE ?");
$stmt_2->execute(array( $like_filename_string ));
Both columns are defined as 'varchar' and all input in the query is as 'string'
I can't wrap my head around why the pdf one works, but the speaker_img doesn't.
The problem must be in REPLACE(speaker_img, $thefile, '' ) because if i replace that with a simple = '', the query performs fine and clears the whole cell if a match is made.
This is how i define the 2 input variables:
$thefile = (string)basename($row['filename'], ".png");
$like_filename_string = "%".$thefile."%";
For the curious why i save the info like that: There's only 1 field for the filenames, seperated by a "-", which when loaded gets 'exploded' into an array. It allows me to haven an open-ended number of attachments. Maybe not the best way of doing things?

If you use parameters for $id and $thefile instead of variable interpolation, you won't need to worry about escaping chars, which is probably the reason the sql is malformed when you try to just use the value of $thefile inside a sql string.

Related

MySQL SELECT and then save or update to a different table when special characters are in the first table. leave_my_text_alone();

I have text correctly saved into a mariahDB 10.2 database. The text, to complicate matters, is in fact a combination of Regular Expressions and a hybrid code invented by someone else.It can be used unchanged in another application as a text file - not PHP. But it just text at the end of the day. I want to grab data from this table, change it a small amount, and save it in a new table.
The problem is less so about changing the original data much, but more about SELECTING and saving data that is full of backslashes, single quotes, and double quotes to a new table without it being changed when it is saved. Is there a simple way in PHP and MySQL to take text from a table and resave it exactly as it is so the second table is not different from the the first?
For example the first table has the following in it.
add list to list(%section,$plugin function("XpathPlugin.dll", "$Generic Xpath Parser", $document text, "//p[1]/a[#class=\'result-title hdrlnk\' and 1]", "outerhtml", "False"),"Don\'t Delete","Global")
But if I put this into a variable and then INSERT or UPDATE that to another table, MySQL seems to strip out the backslashes, or add backslashes and throw errors for incorrectly formatted SQL.
For instance Don\'t Delete becomes Don't Delete and in other examples \\ become \
In another case ?=\")" loses the a backslash and becomes ?=")"
I have tried dozens of combinations of PHP function to leave the text alone, such as addslashes(), htmlentities(), preg_replace(), various string substitution and nothing get the data back into the table the same way as it came out.
Does anyone have the trick to do this? I would call the function leave_my_text_alone(); !!
EDIT
To add a few things that did not do the trick to get a variable I could update into the database I tried
$omnibotScript = addcslashes($omnibotScript,"'");
I then found I need to do this twice to consider the backslash being removed from before the apostrophe in Don't Delete....or it would throw a MySQL parsing error..Doing it again fixed that. So then I had to put two backslashes back to have one removed. I then added this to consider a double backslash being reduced to single backslash.
$pattern = '/\\\\"/';
$replacement = '\\\\\\\"';
$omnibotScript = preg_replace($pattern, $replacement, $omnibotScript);
But the list went on.
Use prepared statements.
If you use a prepared statement, MySQL will take care of all the escaping you need to get the string back into the table exactly as it came out of it. For example, using MySQLi:
$query = "SELECT s1 FROM t1";
if (!$result = $con->query($query)) {
exit($con->error);
}
$row = $result->fetch_assoc();
$value = $row['s1'];
$query = "INSERT INTO t2(s1) VALUES (?)";
$stmt = $con->prepare($query);
$stmt->bind_param('s', $value);
$stmt->execute();
The value of s1 in t2 will be exactly the same as the value in t1.

Can't retrieve row containing '%' from database using PHP PDO

I am making a site that has an autocomplete product search bar. When a user types in a few letters, I compare them to the database with the LIKE statement and get products back that look similar. A user can see a max of 4 products that the user was thinking of in a drop down under the search bar. Now once the user clicks on a result, I pass that back to the PHP PDO statement.
If the product doesn't contain a %, it works fine. But I have products such as '100% Product A', and I can't seem to get around it. I know % is used in the LIKE statement. I used like, but the results weren't great. I was getting a different variation of the product I wanted. How do I get passed this?
$pdo = new PDO($dsn, $user, $passwd);
//Retrieving Product Name
$prodName = $_GET['name'];
$stm = $pdo->prepare("SELECT * FROM products WHERE productName = ?");
$stm->bindValue(1, $prodName);
$stm->execute();
$row = $stm->fetch(PDO::FETCH_ASSOC);
$results[0] = $row["productName"];
$results[1] = $row["price"];
$results[2] = $row["quantity"];
$result=implode("','", $results);
echo $result;
Since % has special meaning, you simply need to escape it to make it used literally, using usual \:
\% A % character; see note following the table
So you just need to str_replace() all % with \%.
See: https://dev.mysql.com/doc/refman/8.0/en/string-literals.html
There is not a single problem with retrieving a row containing % character with LIKE operator, let alone with a comparison operator used in your code.
Even with like it could only give you extra results, but never would make a problem with getting the existing result.
To claim such a problem exists, you you must prove your words by posting a Minimal Complete Verifiable example so everyone would be able to run your code and confirm the problem exists indeed.
However, in such a simple case, instead of a proof, you will just discover some simple mistake, completely irrelevant to % sign. For example, your search string gets severely encoded/escaped by some cargo cult code and naturally becomes a different string that doesn't have any matches in the database
Well, i would just remove % from the search string using str_replace
//Retrieving Product Name
$prodName = str_replace('%', '', $_GET['name']);
You could also escape the percentage by putting it in brackets or using backslash:
//Retrieving Product Name
$prodName = str_replace('%', '[%]', $_GET['name']); // \% would work too instead of [%]

Running a MySQL query using a string in php

Answer found (syntax): The column name of my string had to be encased in backticks " ` " as they contained spaces. Note that this means that the majority of this post has no relevance to the issue. The code has been corrected in case someone wants to do something similar.
So, I am doing a foreach loop to assign a value (1/0) to non-static columns in my database (it needs to support addition/deletion/editing of columns). I am using $connectionvar->query($queryvar); to do my queries which worked fine up until now when I'm trying to use a custom built string as $queryvar in order to change the column name to a variable within the loop. I've been outputting this string through echo and it looks exactly like my functional queries but somehow doesn't run. I've attempted to use eval() to solve this but to no avail (I feel safe using eval() as the user input is radio buttons).
Here's the loop as well as my thought processes behind the code. If something seems incoherent or just plain stupid, refer to my username.
foreach($rdb as $x) { //$rdb is a variable retrieved from $_POST earlier in the code.
$pieces = explode("qqqppp", $x); //Splits the string in two (column name and value) (this is a workaround to radio buttons only sending 1 value)
$qualname = $pieces[0]; //Column name from exploded string
$qualbool = $pieces[1]; //desired row value from exploded string
$sql = 'UPDATE users SET '; //building the query string
$sql .= '`$qualname`';
$sql .= '=\'$qualbool\' WHERE username=\'$profilename\''; //$profilename is retrieved earlier to keep track of the profile I am editing.
eval("\$sql = \"$sql\";"); //This fills out the variables in the above string.
$conn->query($sql); //Runs the query (works)
echo ' '.$sql.' <br>'; //echoes the query strings on my page, they have the exact same output format as my regular queries have.
}
}}
Here's an example of what the echo of the string looks like:
UPDATE users SET Example Qualification 3='1' WHERE username='Admin2'
For comparison, echoing a similar (working) query variable outside of this loop (for static columns) looks like this:
UPDATE users SET profiletext='qqq' WHERE username='Admin2'
As you can see the string format is definitely as planned, yet somehow doesn't execute. What am I doing wrong?
PS. Yes I did research this to death before posting it, as I have hundreds of other issues since I started web developing a month ago. Somehow this one has left me stumped though, perhaps due to it being a god awful hack that nobody would even consider in the first place.
You need to use backticks when referring to column names which have spaces in them. So your first query from the loop is outputting as this:
UPDATE users SET Example Qualification 3='1' WHERE username='Admin2'
But it should be this:
UPDATE users SET `Example Qualification 3`='1' WHERE username='Admin2'
Change your PHP code to this:
$sql = 'UPDATE users SET `'; // I added an opening backtick around the column name
$sql .= '$qualname`'; // I added a closing backtick around the column name
$sql .= '=\'$qualbool\' WHERE username=\'$profilename\'';
Example Qualification 3 : Is that the name of your Mysql Column name ?
You shouldnt use spaces nor upper / lower case in your columnname.
Prefere : example_qualification_3
EDIT :
To get column name and Comment
SHOW FULL COLUMNS FROM users

foreach loop returns nothing

I am trying to pull user data from a Cart66 table I have and put it into a shortcode in wordpress. $account is an integer pulled from session data. The code below returns nothing.
$account =Cart66Session::get(Cart66AccountId);
global $wpdb;
$fname=$wpdb->get_results("SELECT * FROM 'vfp_cart66_accounts' WHERE id = '$account', ARRAY_N");
foreach ($fname AS $row)
{
echo $row;
}
This returns "Array"
return $fname;
Ok firstly, maybe I am the only one who saw this, and it could be the source of your entire problem, but you have a misplaced double quote, at the end of your SQL line, which should live at the end of the actual SQL string, not after the requested return type:
// at the end of this line you have: '$account', ARRAY_N");
// this should be changed to: '$account'", ARRAY_N);
$fname=$wpdb->get_results("SELECT * FROM 'vfp_cart66_accounts' WHERE id = '$account', ARRAY_N");
Even the first person who answered the question did not correct you, so I am assuming he didn't see it either. Secondly, using single quotes (') to escape a table name is invalid. If it is quoted at all, use backticks (`). Single quotes indicate a string, not an database, table, or field, all three of which should only be quoted with backticks (except on utility queries like SHOW). Use this instead:
select * from `vfp_cart66_accounts` where id = '$account'
Thirdly, as your commenters point out, you could be vulnerable to SQL Injection. Make sure to use the tools that WP gives you, and do this, or similar, instead:
$fname = $wpdb->get_results(
$wpdb->prepare(
'select * from `vfp_cart66_accounts` where id = %d',
$account
),
ARRAY_N
);
Lastly, you are requesting an array from the DB, but you are trying to echo it as if it were a scalar value. This explains why printing the value of $row yields "Array". When you convert an array() to a string, by default, you get "Array", since arrays can be complex data that may not be beautifully converted to a string. As a correction of this, you can do one of two things.
First, if you need the entire resulting array that represents the entire row of the table, then you can simply change your echo code to this:
foreach ($fname as $row) {
// print the fname of the row
echo $row['fname'];
// do the other stuff you need to do with $row
...
}
OR, if you simply need the fname field out of that table, for the given id, you could use a different $wpdb function, called $wpdb->get_var(), which gets one specific field from the first entry of the resulting data from the database, coupled with some minor SQL changes:
// use the get_var() function instead
$fname = $wpdb->get_var(
$wpdb->prepare(
// 1) change the 'fields' of your sql to only get the `fname` field
// 2) also add limit 1, to reduce load by only asking for one row
// NOTE: #2 is optional really, because WP does this for you when using get_var,
// but is good practice to only ask for what you need. so do it
'select fname from `vfp_cart66_accounts` where id = %d limit 1',
$account
),
ARRAY_N
);
echo $fname; // print the value of field fname from vfp_cart66_accounts for id $account
Now. I don't have specific knowledge of Cart66. That being said, if the above changes to PHP, WordPress, and SQL syntax do not yield results, then you are probably having one of the following other problems instead:
there is a different PHP error somewhere in the code, causing this to never run
this code is never called, and thus it is never executed
you misspelled the table name, which is causing an SQL error
the table exists, but does not have a field named id
both table and field exist, but there are no entries in the table
some other random thing that is not coming to mind
DEBUG #1
For #1, you could try turning on error_reporting() and display_errors early in the code execution. In a normal, run of the mill PHP script you could add the following two lines somewhere early in the code:
error_reporting(E_ALL);
ini_set('display_errors', 1);
However, you are using WordPress, so you will need to do something like this in your wp-config.php file:
// find the line that looks like this and comment it out
// define('WP_DEBUG', false);
// add these two lines directly below it
define('WP_DEBUG', true);
ini_set('display_errors', 1);
DEBUG #2
Make sure your code is running. Don't be afraid to throw a die() statement directly above it, to make sure it is running. Something like this:
// add a die() before everything
die('I am running. Awesome!');
// revised code
$account = Cart66Session::get(Cart66AccountId);
global $wpdb;
$fname = $wpdb->get_var(
$wpdb->prepare(
'select fname from `vfp_cart66_accounts` where id = %d limit 1',
$account
),
ARRAY_N
);
echo $fname;
DEBUG #3
To debug #3, you need either access to a commandline tool for MySQL or some type of GUI interface like phpMyAdmin, so that you can run a query directly from the database. Here is the query you should run:
show tables like 'vfp_cart66_%';
This is an example of one of the only places in SQL that you should ever quote a table name in single quotes. Running this will yield a list of all the tables that start with vfp_cart66_. If you get no results, then your table name is wrong. If your results do not include vfp_cart66_accounts, then your table name is wrong. If you see vfp_cart66_accounts, you are good to go.
DEBUG #4
This one will need to be run directly from the DB or through something like phpMyAdmin also. You are trying to make sure you have the correct field name. The way you do that is:
show create table `vfp_cart66_accounts`;
Assumedly, the field you are calling id would be the auto_incremented field in the table. Thus you are looking for a line, similar to this one:
`id` bigint(20) NOT NULL AUTO_INCREMENT,
Make sure that the line that has AUTO_INCREMENT on it, begins with:
`id`
If it does not, and the name is something else other than id, then you probably have the wrong field name.
DEBUG #5
Make sure you actually have data to display. From your mysql console or phpMyAdmin, run:
select * from `vfp_cart66_accounts` limit 1;
If you bet any results, then you have data, and you are good.
DEBUG #3 - #5 (alternate methods)
Another option you have is to dump the $wpdb object, directly after you run the query, because it contains the last error you received from MySQL. You can do this like so:
$fname = $wpdb->get_var(
$wpdb->prepare(
'select fname from `vfp_cart66_accounts` where id = %d limit 1',
$account
),
ARRAY_N
);
// dump a readable version of the $wpdb object
echo '<pre>';
print_r($wpdb);
die('</pre>');
Often times, reading the MySQL error message helps narrow down the problem in your SQL syntax.
DEBUG #6
If none of this has helped at all, then you will need to use your experience to trackdown a random bug in either your plugins or theme, what could literally be anything. You may as well not even dig in core WP code because, while it does have a couple minor bugs unrelated to your problem, which are getting repaired as we speak, it is one of the most stable CMS platforms out there. It is used by more of the top 10 million sites on the internet than any other CMS, for a good reason. It works, it is up-to-date, and most of all, it is stable.
I really hope you found this helpful or at least learned something from it. Hopefully others find it useful as well.
$fname=$wpdb->get_results(
"SELECT * FROM `vfp_cart66_accounts` WHERE id = '$account'",
ARRAY_N"
);

MySQL SELECT statement using PHP GET variable

I have a PHP script that is generating a MySQL select statement:
select * from words where word = 'Classic'
There is exactly one word in the words table with the variable word equal to Classic.
When my PHP page executes, I get no results from the query. If I echo the string that is being used to execute the query, cut and paste that into the SQL window in PHPMyAdmin in the database, I also get no results. However, if I re-type that EXACT string into the SQL window in PHPMyAdmin (with the same quote characters), I get the proper result of one row.
The word Classic from the select statement is gotten from a PHP GET (see code below). I can echo the $word variable, and get the correct result of 'Classic'. What am I doing wrong?
Here is my code:
<?php
require ('dbconnect.php');
$word = $_GET["word"];
$selectStr = "SELECT * FROM words WHERE word = '" . $word . "'";
if ($results = MySQL($dbName, $selectStr))
{
$rowCount = MySQL_NUMROWS($results);
}
$resultRow = MYSQL_FETCH_ROW($results);
$wordID = $resultRow[0];
?>
Please, please, please sanitize that word. mysql_real_escape_string() should do the trick.
$selectStr = "SELECT * FROM words WHERE word LIKE '" . $sanitized_word_i_promise . "'"; should work :)
Just to explain: "=" should work for exact matches. This includes uppercase / lowercase, spaces etc. You should probably trim that result first too, before using it in the query.
If you have foo stored in the database (note the space at the end) - it won't match foo, without a space. You'll want to use LIKE 'foo%' - probably.
Either way, Sourabh is right, although performance wise, this isn't a big hit when trying to match exact strings, you should look for the problem in other places first (such as, is the item in the database an exact match?).
First off you should not take any user input and directly input it into a query without sanitizing it, or using a prepared statement.
Now that we've gotten that out of the way: have you tried doing a strcmp() with the variable and your string written in? Such as
echo strcmp($_GET['word'], "Classic")
If you get a result other than 0 it means they are not the same, most likely there will be a whitespace of some sort in the $_GET variable. use trim() on it to take out whitespace. Also could be a case sensitivity issue as well.

Categories