I have a MYSQL database that has a table with a column (field1) of type TINYTEXT. The column contains values such as 010101 and 01010" etc… which are actually filenames.
When I query this table using PHP it seems to be deciding that this field is numeric and it thus strips off the leading zero. When I try to use this value as a filename of course it doesn't work.
I am extracting the data like this:
$sSQL = "SELECT * FROM images;";
$rsImageList = RunQuery($sSQL);
$iLoop = 0;
while (($iLoop <= 80) && ($aRow = mysql_fetch_array($rsImageList))) {
extract($aRow);
echo $field1;
$iLoop++;
}
How can I typecast the variable as a string type?
Mea culpa! I had managed to get duplicate data in my table, some entries with the leading zero missing already (thanks to Excel which I used to import the data) and some without. I was looking at the correct data but extracting the incorrect data. Moral of the story - test with only a couple of lines of data not 60!
Sorry for wasting everyone's time.
you can force by casting it to a string:
$var = "" . $aRow["column"];
You can use CAST() function to change it into string
$sSQL = "SELECT CAST(field1 AS CHAR) FROM images;";
Give it a try, I dont think its MySQL fault as you have defined it as TINYTEXT maybe its because extract() method.
Related
I have managed after a struggle to understand what is happening with the prepare placeholders. My only thought is that my table does not have a consistent element in it that I can use as a reference with the place holder.
There is a test column that I have used, but i do not intend on having it in my production plugin. The column is set to 0 for each entry, and I set the $test to 0. Thus my query has now started working. But this doesn't really make sense as a security feature unless it is dynamically calling something in reference to the results on the database. The examples I have seen around all rely on a set constant in their query, but I haven't got this unless I just add a constant entry in the database, but this seems silly.
$test = 0;
$result =
$wpdb->get_results( $wpdb->prepare
( "SELECT * FROM $my_noted_table_name WHERE id_can_view = %d ", $test));
Is there a better way of doing this?
Thanks in advance..
Let me explain what is happening.
The prepare is sanitizing the variable's value, inserting it where you specified the placeholder, and then formatting the SQL query. Then the returned SQL query string is processed by the $wpdb->get_results().
Step 1:
For this line of code:
$wpdb->prepare( "SELECT * FROM $my_noted_table_name WHERE id_can_view = %d", $test );
here's what is happening:
Sanitizes the variable's value $test
Replaces out the placeholder with the sanitized variable's value.
The database table name is extracted from your $my_noted_table_name variable.
Formats the SQL query
For the placeholder, %d means the value will be an integer. If it's a string, then use %s instead. Think about it in terms of using the PHP construct sprintf or printf.
d - the argument is treated as an integer, and presented as a (signed) decimal number.
s - the argument is treated as and presented as a string.
So, let's say your variable $test has a value of 100 assigned to it and the database table's name is countries. Then SQL query string then is:
"SELECT * FROM `countries` WHERE `id_can_view` = 100;"
See how $wpdb->prepare transformed your inputted string into a properly formatted SQL query?
You want to ALWAYS use $wpdb->prepare() to handle this process as it will protect your database.
I actually get very mad about PHP and SQLite3 and the way some of my strings behave there.
I try to save opening hours but in strings instead of numeric to prevent problem with leading zeros (and still have it now haha... -.-).
Hours and minutes have their own column but when I insert '0x' the zero is gone and whatever x is, is left in the database. :/
Im sure im just missing some little damn part somewhere...
I already checked the INSERT-statement but found nothing at all.
Example for an insert string:
INSERT INTO opening INSERT INTO opening (start_day, end_day, start_hour, start_minute, end_hour, end_minute) VALUES('Montag', 'Freitag', '00', '00', '01', '00')
But the output is:
11|Montag|Freitag|0|0|1|0
Part of the Code:
class Database_Opening_Hours extends SQLite3{
function __construct() {
if(!file_exists("../../data/opening_hours/opening_hours.sqlite")){
$this->open("../../data/opening_hours/opening_hours.sqlite");
$this->exec('CREATE TABLE opening (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, start_day STRING, end_day STRING, start_hour STRING, start_minute STRING, end_hour STRING, end_minute STRING)');
}
else{
$this->open("../../data/opening_hours/opening_hours.sqlite");
}
}
}
$db = new Database_Opening_Hours();
$insert = "INSERT INTO opening (start_day, end_day, start_hour, start_minute, end_hour, end_minute) VALUES('".htmlspecialchars($_GET["start_day"])."','".htmlspecialchars($_GET["end_day"])."','".$start_hour."','".$start_minute."','".$end_hour."','".$end_minute."')";
if($db->exec($insert)){
$db->close();
unset($db);
echo "Insert erfolgreich";
}else{
$db->close();
unset($db);
echo "Nicht wirklich...";
}
Fairly sure that the type of your columns is set to an integer (or any other number type) instead of TEXT.
Make sure to double check the column data type and actually dump the table for us to check if it's really set to TEXT.
This is caused by SQLite using dynamic typing. From the FAQ:
This is a feature, not a bug. SQLite uses dynamic typing. It does not enforce data type constraints. Data of any type can (usually) be inserted into any column. You can put arbitrary length strings into integer columns, floating point numbers in boolean columns, or dates in character columns. The datatype you assign to a column in the CREATE TABLE command does not restrict what data can be put into that column. Every column is able to hold an arbitrary length string.
And from the linked page (emphasis mine):
In order to maximize compatibility between SQLite and other database engines, SQLite supports the concept of "type affinity" on columns. The type affinity of a column is the recommended type for data stored in that column. The important idea here is that the type is recommended, not required. Any column can still store any type of data. It is just that some columns, given the choice, will prefer to use one storage class over another. The preferred storage class for a column is called its "affinity".
So SQLite is dynamically casting your values to integer.
I would suggest combining start_hour and start_minute into start_time (the same for the end_ fields) and storing the value in the format 00:00.
SQLite will store this 'as-is' but is smart enough to recognise a time value and allow you to perform date/time operations:
select time(start_time, '+1 hour') from opening
I had this problem with C/C++ because I did not quote the strings:
insert into test values('aa', 'bb');
use varchar instead of string, I had the same problem then I used varchar(length) and it worked fine
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
I am using php and mySQL. I have a select query that is not working. My code is:
$bookquery = "SELECT * FROM my_books WHERE book_title = '$book' OR book_title_short = '$book' OR book_title_long = '$book' OR book_id = '$book'";
The code searches several title types and returns the desired reference most of the time, except when the name of the book starts with a numeral. Though rare, some of my book titles are in the form "2 Book". In such cases, the query only looks at the "2", assumes it is a "book_id" and returns the second entry in the database, instead of the entry for "2 Book". Something like "3 Book" returns the third entry and so forth. I am confused why the select is acting this way, but more importantly, I do not know how to fix it.
If you have a column in your table with a numeric data type (INT, maybe), then your search strategy is going to work strangely for values of $book that start with numbers. You have discovered this.
The following expression always returns true in SQL. It's not intuitive, but it's true.
99 = '99 Luftballon'
That's because, when you compare an integer to a string, MySQL implicitly does this:
CAST(stringvalue AS INT)
And, a cast of a string beginning with the text of an integer always returns the value of the integer. For example, the value of
CAST('99 Luftballon' AS INT)
is 99. So you'll get book id 99 if you look for that search term.
It's pointless to try to compare an INT column to a text string that doesn't start with an integer, because CAST('blah blah blah' AS INT) always returns zero. To make your search strategy work better, you should consider omitting OR book_id = '$book' from your search query unless you know that the entirety of $book is a number.
As others mention, my PHP allowed both numerical enties and text entries from the browser. My query was then having a hard time with this, interpreting some of my text entries as numbers by truncating the end. Thus, my "2 Book" was being interpreted as the number "2" and then being queried to find the second book in the database. To fix this I just created a simple if statement in PHP so that my queries only looked for text or numbers. Thus, in my case, my solution was:
if(is_numeric($book)){
$bookquery = "SELECT * FROM books WHERE book_id = '$book'";
}else{
$bookquery = "SELECT * FROM books WHERE book_title = '$book' OR book_title_short = '$book' OR book_title_long = '$book'";
}
This is working great and I am on my way coding happily again. Thanks #OllieJones and others for your questions and ideas which helped me see I needed to approach the problem differently.
Not sure if this is the correct answer for you but it seems like you are searching for only exact values in your select. Have you thought of trying a more generic search for your criteria? Such as...
$bookquery = "SELECT * FROM my_books WHERE book_title LIKE '".$book."' OR book_title_short LIKE '".$book."' OR book_title_long LIKE '".$book."' OR book_id LIKE '".$book."'"
If you are doing some kind of searching you might even want to ensure the characters before the search key are found as well like so....
$bookquery = "SELECT * FROM my_books WHERE book_title LIKE '%".$book."' OR book_title_short LIKE '%".$book."' OR book_title_long LIKE '%".$book."' OR book_id LIKE '%".$book."'"
The % is a special char that looks for allows you to search for the chars you want to search for PLUS any characters before this that aren't in the search criteri... for example $book = "any" with a % before hand in the query like so, '%".$book."'"`` would return bothcompanyand also the wordany` by itself.
If you need to you can add a % to the end also like so, `'%".$book."%'"`` and it would do the same for the beginning and end of the search key
Question says it all hopefully, if I check a variable returns true for is_numeric(), is it ok to put directly into the MySQL query, or do I need to apply standard escaping? I'm thinking null character, overflow exploits and stuff.
An ambiguous example would be:
if(is_numeric($_GET['user_id'])) {
mysql_query("SELECT * FROM `users` WHERE id = ".$_GET['user_id']);
}
The datatype in MySQL is INT().
The safest way in my opinion is to convert the user_id to an integer, if it's invalid it will return 0.
$user_id = (int) $_GET['user_id'];
if ($user_id > 0) {
mysql_query("SELECT * FROM `users` WHERE `id` = " . $user_id);
}
Considering that "10e3" is_numeric, no.
If you want numbers (as in, only digits), you'll have to check for ctype_digit (which would still break SQL for numbers like 0123) or cast the number to an int or float. If it's acceptable for the number to be something other than all digits, you'll need to apply the SQL safe escaping and quoting.
From http://php.net/manual/en/function.is-numeric.php:
Be careful when using is_numeric() to escape SQL strings. is_numeric('0123') returns true but 0123 without quotes cannot be inserted into SQL. PHP interprets 0123 without quotes as a literal octal number; but SQL just throws a syntax error.
Because the programm must be ready for variant for no-existing id this single row should be enough:
mysql_query(sprintf("SELECT * FROM `users` WHERE `id` = %d LIMIT 1",$_GET['user_id']));
Whatever what will not be a decimal number we pass inside the sprintf will be turned to decimal. The zero (~ bad input) has the same state as no existing id.
Saving condition and declaring extra variable.