PHP PDO syntax issue? - php

I'm having trouble with this snippet of code, and can't find any errors:
$query = "CREATE TABLE ? (? INT NOT NULL AUTO_INCREMENT, PRIMARY KEY(?), ? VARCHAR(30), ? VARCHAR(50), ? TIMESTAMP, ? VARCHAR(50), ? DECIMAL(15, 2), ? DECIMAL(3, 2), ? VARCHAR(255))";
$array = array($table_name, $id, $id, $a_title, $c_title, $date_updated_title, $s_title, $ds_title, $ps_title, $u_title);
try {
$results = db_query($db, $query, $array); // db_query() is my PDO function to query the database. This function works fine elsewhere.
echo($table_name . " create successfully!");
} catch (PDOException $e) {
echo('<br />Could not create table "' . $table_name . '".');
return false;
error($e); //error() is my function to write errors to my log, and works fine elsewhere.
}
When I run this in my browser, it returns my caught exception 'Could not create table "name".' However, I don't see any error in my log, so I don't know if it's a syntax issue, or what.
When I take the query itself, and replace the question marks with the actual values, and dump it in PHPMyAdmin, it creates the table fine. I'm not really sure what the issue is here. I've had reasonable success with PDO on another site, but I'm still relatively new. Any ideas?
Thanks for the help!
[Edit] I've since tried using this query:
"CREATE TABLE $a_title (? INT NOT NULL AUTO_INCREMENT, PRIMARY KEY(?), ? VARCHAR(30), ? VARCHAR(50), ? TIMESTAMP, ? VARCHAR(50), ? DECIMAL(15, 2), ? DECIMAL(3, 2), ? VARCHAR(255))";
I've tried with both single and double quotes. I also removed the $table_name variable from the array. Still getting a syntax error, and not sure why.

Comment from #DCoder is correct. You can use a query parameter only in place where you could normally put a single string literal, date literal, or numeric literal.
You can't use a query parameter for:
Table names
WRONG: SELECT * FROM ?
Column names
WRONG: SELECT * FROM table WHERE ? = 1234
Lists of values
WRONG: SELECT * FROM table WHERE column IN (?)
Though you could use IN() with a list of parameter placeholders, one for each scalar value.
SQL operators, expressions, or keywords
WRONG: SELECT * FROM table WHERE column ? 'value' AND ? ORDER BY column ?
For those cases, if you want dynamic content to become part of your query, the content must be part of the query before you call prepare().
But this means that you're back to interpolating variables into SQL query strings, which we are told is a no-no for its SQL injection risk.
The solution is to use filtering and whitelisting to make sure that the content doesn't contain some unsafe content. For example, if it's a dynamic table name, strip out anything but characters you know you want to keep, and then also delimit the table name just in case someone names their table a reserved word like "table" or "order" or something.
$table = preg_replace("/[^\w]/", "", $table);
$sql = "CREATE TABLE `{$table}` ( ... )";
Re your comment:
Yes, column names are off limits as well. As I said at the top, parameters are only for scalar values.
You also need to learn the appropriate usage of the three different types of quote marks.
What is the difference between single and double quotes in SQL?
Do different databases use different name quote?

Related

How to force int as string in prepared statement

I'm working on syncing two PostgreSQL databases using a PHP script. I am not able to query the entire table so I have to use an id column to grab records in batches.
The id column is a string column, not numeric. However, there are numerical ids in the column. This is where I'm having an issue.
When I prepare the SQL statement in PHP, when I happen to get an id that is numeric, when I bind it to the statement, it doesn't put quotes around the value because it thinks its an int, not a string.
How do I force it to be a string and always put single quotes around the id??
If I put the quotes around the ? in the query it treats it as text and the parameter doesn't get bound to the statement.
As you can see in the code I also tried casting the $start variable as a string. $start contains the starting id.
Here is the code:
$sql = "select id from properties where id > ? order by id limit ?";
$params = [(string) $start, 50000];
$rows = $this->wolfnet->select($sql, $params);

PHP MYSQL returning string field as numeric

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.

Postgresql custom function returning table

I am using PostgreSQL 9.1.11.
I need to return result of SELECT to my php script. The invocation in php is like this:
$res = $pdb->getAssoc("SELECT * FROM my_profile();");
The class code to illustrate what is going on in php
public function getAssoc($in_query) {
$res = pg_query($this->_Link, $in_query);
if($res == FALSE) {
return array("dberror", iconv("utf-8", "windows-1251", pg_last_error($this->_Link)));
}
return pg_fetch_all($res);
}
Next comes my function in Postgres. I fully re-create database by dropping in a script when I update any function. (The project is in the early stage of development.) I have little to no experience doing stored procedures.
I get this error:
structure of query does not match function result type
CONTEXT: PL/pgSQL function "my_profile" line 3 at RETURN QUERY )
Trying to write:
CREATE FUNCTION my_profile()
RETURNS TABLE (_nick text, _email text) AS $$
BEGIN
RETURN QUERY SELECT (nick, email) FROM my_users WHERE id = 1;
END;
$$
LANGUAGE 'plpgsql' SECURITY DEFINER;
Table structure is:
CREATE TABLE my_users(
id integer NOT NULL,
nick text,
email text,
pwd_salt varchar(32),
pwd_hash character(128),
CONSTRAINT users_pk PRIMARY KEY (id)
);
When I return 1 column in a table the query works. Tried to rewrite procedure in LANGUAGE sql instead of plpgsql with some success, but I want to stick to plpgsql.
The Postgres 9.1.11, php-fpm I am using is latest for fully updated amd64 Debian wheezy.
What I want to do is to return a recordset containing from 0 to n rows from proc to php in an associative array.
This part is incorrect:
RETURN QUERY SELECT (nick, email) FROM my_users WHERE id = 1;
You should remove the parentheses around nick,email otherwise they form a unique column with a ROW type.
This is why it doesn't match the result type.
#Daniel already pointed out your immediate problem (incorrect use of parentheses). But there is more:
Never quote the language name plpgsql in this context. It's an identifier, not a string literal. It's tolerated for now since it's a wide-spread anti-pattern. But it may be considered a syntax error in future releases.
The SECURITY DEFINER clause should be accompanied by a local setting for search_path. Be sure to read the according chapter in the manual.
Everything put together, it could look like this:
CREATE FUNCTION my_profile()
RETURNS TABLE (nick text, email text) AS
$func$
BEGIN
RETURN QUERY
SELECT m.nick, m.email FROM my_users m WHERE m.id = 1;
END
$func$
LANGUAGE plpgsql SECURITY DEFINER SET search_path = public, pg_temp;
Replace public whit the actual schema of your table.
To avoid possible naming conflicts between OUT parameters in RETURNS TABLE ... and table columns in the SELECT statement I table-qualified column names with the given alias m.

Check if string includes phrases from database

I am using the following query (simplified for here) to check if a string contains a "watch-word" where the watch words are contained in a MySQL table:
$sql = "SELECT ww_id FROM watch_words WHERE ww_word IN (" . $string . ")";
This works perfectly for single words, but now I need to make it work for phrases (i.e. the field ww_word may contain more than one word). All I can think of are things like reading the whole table into an array and then doing multiple loops to compare against combinations of the words in the string, but I'm sure (hoping) there's a better way.
EDIT: Thanks for the suggestions, but as pointed out by Mike Brant, the needle is in MySQL and the haystack in PHP - not the "usual" way around (like a search form for instance). I need to check if a string (actually a message) contains one or more "watch phrases" - like a bad-language filter (but not that).
Sample table thus:
CREATE TABLE `watch_words` (
`ww_id` int(11) NOT NULL AUTO_INCREMENT,
`ww_word` varchar(250) NOT NULL,
PRIMARY KEY (`ww_id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=latin1 AUTO_INCREMENT=6 ;
INSERT INTO `watch_words` VALUES (1, 'foo bar');
INSERT INTO `watch_words` VALUES (2, 'nice sunny day');
INSERT INTO `watch_words` VALUES (3, 'whatever');
INSERT INTO `watch_words` VALUES (4, 'my full name here');
INSERT INTO `watch_words` VALUES (5, 'keyword');
So string "What a nice sunny day we're having" should return a match, whereas "What a lovely sunny day..." wouldn't. TIA.
use LIKE for pattern matching
$sql = "SELECT ww_id FROM watch_words WHERE ww_word LIKE '%" . $string . "%'";
or maybe interchange the two,
$sql = "SELECT ww_id FROM watch_words WHERE " . $string . " LIKE CONCAT('%', ww_word,'%')";
As a sidenote, the query is vulnerable with SQL Injection if the value(s) came from the outside. Please take a look at the article below to learn how to prevent from it. By using PreparedStatements you can get rid of using single quotes around values.
How to prevent SQL injection in PHP?
You will likely need to take a different approach here. You have the needle in MySQL and the haystack in PHP. Using things like LIKE (which you use for string matches not IN), MySQL can work fine with the haystack being in MySQL table and the needle in the application (in the LIKE).
There is no convenient reverse matching to pass MySQL the haystack and have it apply a needle from a field in a table against it.
You will likely need to select your needles out of the database and compare it to the haystack in your application.

Assigning a default value to a blank recalled database field

I'm working on an attempted social networking site with a relative of mine as a side project. I'm writing most of it bymyself. I have made it so each user is stored into a table with their data (mysql) and i want to do a single compare statement so that if a field is blank to change the recieved variable to "No data".
For example:
persons data is
name=john
age=34
mobile number=(empty because it hasnt been entered)
so when i receive the resource id with the mysql_func_assoc function
$result= mysql_query($sql);
$db_field = mysql_fetch_assoc($result);
$szUser_name=$db_field['user_name'];
$szAge=$db_field['user_age'];
$szNumber=$db_field['user_number'];
now i want an if statement to recognise a blank field (something like if($szUser=="" ||$szAge=="" ||$szNumber=="")) and then automatically change the respective field to "No data". i could just set all the values to ="No data" above and have them reassigned by the assoc function but im looking for a neat simple option. Thanks in advance
Maybe put this into a function so you can reuse the code?
function getField($fieldName, $fields, $default="No data") {
return empty($fields[$fieldName]) ? $default : $fields[$fieldName];
}
So
$szNumber=$db_field['user_number'];
becomes
$szNumber=getField('user_number', $db_fields);
EDIT: Yeah you're right
//Nitpick :
Add an 's' to $db_field as well since it contains multiple fields. Or consider renaming it $db_row
If your "empty" fields are actually set to NULL:
For a pure MySQL solution you can use COALESCE to achieve what you want assuming the "empty" fields are set to NULL. For example:
SELECT COALESCE(`user_name`, 'No data') AS `username` FROM `users`
IFNULL works in a similar way and is probably better suited to this particular job than COALESCE:
SELECT IFNULL(`username`, 'No data') AS `username` FROM `users`
COALESCE can have many arguments and simply returns the first NOT NULL argument whereas IFNULL only accepts two arguments and it takes the first NOT NULL argument.
If your "empty" fields are actually empty strings:
If your values are empty strings rather than NULL then you can use:
SELECT IF('' = `username`, 'No data', `username`) AS `username` FROM `users`
For more information on the MySQL IF function see the manual.
If you have a combination of both empty strings and NULL:
You can use:
SELECT IF('' = `username` OR `username` IS NULL, 'No data', `username`) AS `username` FROM `users`
This is usually the kind of thing where you use the ternary operator :
$szNumber = (!empty($db_field['user_number'])) ? $db_field['user_number'] : 'No data';
Or, you can use the full if/else block.
You could also a CASE statement in your query :
SELECT CASE WHEN user_number = '' THEN 'No data' ELSE user_number END AS user_number
FROM yourtable
But it can make queries hard to read.
The best choice is to choose MVC-based architecture for your application, transfer raw sql-data from controller into view, and to decide what data to output in view part. For example, Zend Framework allows to use classes called "view helpers", which will display "No data" instead of empty strings.

Categories