How to use prepare() with dynamic column names? - php

I have a function that takes an sql table column name string as a parameter, returns 1 string result:
function myFunction($column_name) {
return $wpdb->get_var($wpdb->prepare("SELECT %s FROM myTable WHERE user_id=%s", $column_name, $current_user->user_login));
}
However, this code does NOT work, since with the nature of prepare, I can't use a variable for column names (and table names).
This works, but I think it poses a security issue:
return $wpdb->get_var('SELECT ' . $column_name . ' FROM myTable WHERE user_id=' . $current_user->user_login);
What do I need to do in order to to use dynamic column names in my prepare statement?

You could use a list of "approved" values instead, that way you're not really using user data inside a query. Something like this:
$Approved = array ('firstname', 'lastname', 'birthdate') ;
$Location = array_search($ColumnName, $Approved) // Returns approved column location as int
if($Location !== FALSE) {
// Use the value from Approved using $Location as a key
$Query = $wpdb->Prepare('SELECT ' . $Approved[$Location] . ' FROM myTable WHERE user_id=:userid');
$Query->Execute(array(
:userid => $current_user->user_login
));
return $Query;
} else {
return false;
}
Maybe it might be easier to just get all (SELECT * or SELECT a,b,c,d) of the user data and save it to session to use later?

It only took 8 years! But it looks like a fix is finally coming to WordPress in milestone 6.2, where all you'll have to do is use %i instead of %s as a placeholder inside $wpdb-prepare() statements for table and column names. Here's a dev-note about it with examples.
Will update this answer once milestone 6.2 is live (looks like it's currently slated for Mar 28).

Related

How to prefix / suffix column names in a MySQL query?

I'm storing the user type (seller or buyer) in one of my session variables as $_SESSION['user_type'].
Some of my select / insert / update queries require the columns by the name of seller_id or buyer_id.
I want to know if there's a way by which I can add the _id suffix to $_SESSION['user_type'] in my queries.
For example: If I want to select all the columns from my order table where buyer_id is equal to 7, my query should look something like this:
SELECT *
FROM `order`
WHERE ( $_SESSION['user_type'] + "_id" ) = '7'
Note: I know I can use a variable and generate the corresponding column name, but was wondering whether this is possible without any extra variable.
Just concatenate query as string and use it then.
$query = "SELECT *
FROM `order`
WHERE " . $_SESSION['user_type'] . "_id = '7'";
But make sure that you'll not include anything from user input in such way.
If i see old post with possible SQL-Injections i have post ...
If you have to use the value of a variable in a query then
use a whitelist - Please!
Example:
// even if its a session stored on your server and the values set by your self ...
// do NOT trust it and handle it as any other value from any other varible.
// example input:
// $_SESSION['user_type'] = 'seller';
// define the map of valid session user types and column relations (hardcoded)
$columnMap = [
// user_type => column name
'buyer' => 'buyer_id',
'seller' => 'seller_id',
];
// check if you got a valid session type (if you find it on the white list)
if (!isset($columnMap[$_SESSION['user_type']])) {
throw new \InvalidArgumentException("Invalid session[user_type].");
}
// use the value from the white list
$columnName = $columnMap[$_SESSION['user_type']];
// create proper query
$query = "SELECT * FROM `order` WHERE `{$columnName}` = :id;";
// Note:
// "SELECT * FROM `order` WHERE
// `{$columnName}` -- use ` to enclose the column name
// = :id -- use placeholder (prepared statements f.e. PHP PDO)
// ; -- finish statement with semicolon
// "
PHP PDO: https://www.php.net/manual/de/pdo.prepared-statements.php
Why?
Because code may change over years and you get the (i.e.) user_type from a POST or GET request.
Still - why?
Go search for "SQL-Injections".

PHP MySQL Select * From Where Date < today

So I am building a function that prints out a league schedule. I've run into a little snag when trying to pull the last 5 matches. Here is my code:
$league ID, $direction and $limit are set by the functions parameters.
$matches = $wpdb->get_results('SELECT * FROM ' . $wpdb->prefix . 'leagueDesigner_season_matches WHERE leagueID = ' . $leagueID . ' and date < CURRENT_DATE() ORDER BY date ' . $direction . ' LIMIT 0, ' . $limit);
Appologies, I forgot to finish the post. The code is returning all dates and not just the dates before today.
The most likely explanation for the behavior you observe is that the column named date is defined as a datatype other than DATE, DATETIME, or TIMESTAMP. (Likely, you've defined it as an integer or varchar), and MySQL is not doing a "date" comparison, it's comparing strings or integers.
But absent the definition of the date column, that's just conjecture.
If the "snag" you've hit is an error being returned from MySQL, my recommendation for debugging issues with SQL statements is to you build your SQL text into a string variable, as a separate step, and then echo (or printf or vardump) the contents of the variable containing the SQL text, before you try to execute it.
$sql = "SELECT foo FROM bar WHERE fee = " . $val . " ORDER BY foo DESC LIMIT 1 ";
echo $sql;
And verify that the string echoed out is the SQL text you intend to send to the database; taking that string and attempting to execute it through another MySQL client is an effective way of verifying the statement executes and returns the resultset you expect.
If you use any reserved words as column names, you may need to qualify those column names with a tablename, rowsource alias, etc., or enclose it in backticks. (EDIT: DATE is not a reserved word in MySQL 5.5)
... FROM mytable t WHERE t.date = ...
or
... FROM mytable t WHERE `date` = ...
Also note that including "unsafe" variables in SQL text can lead to SQL injection vulnerabilities.
For example,
$val = '1 OR 1=1';
$sql = "SELECT * FROM mytable WHERE id = ' . $val ;

PDO MySQL get records with previous date

I've got a table with some columns. I want to filter some records using two of them, the one with INT type and second with DATETIME type. I'm using PHP PDO extension to connect with database and make some queries.
I'm trying to get the records from my table where datetime field is lower then given date, f.e.
<?php
$date = date("Y-m-d");
$this->db->query("SELECT * FROM `" . DB_PREFIX . "fanpage` WHERE `flag_warning` = ? AND DATE(`update_date`) < ?", array(1, $date));
?>
This returns NULL, but when I paste the same query into the phpMyAdmin window it shows me proper records. What is the problem?
Edit:
Fragment from query function:
public function query($sql, $params = array())
{
$result = array();
$result['query'] = $this->pdo->prepare($sql);
$result['query']->execute($params);
$this->lastResult = $result['query'];
unset($result['query']);
}
No need for the prepared statements at all
WHERE flag_warning = 1 AND update_date < CURDATE()
Use
$sqlStatement = $this->db->prepare("SELECT * FROM `" . DB_PREFIX . "fanpage` WHERE `flag_warning` = ? AND `update_date` < CURDATE()");
$this->db->execute(array(1));
$result = $sqlStatement->fetchAll(PDO::FETCH_ASSOC);
Now $result has what you need.
I've changed the column name to date_time_upd and it works right now, I think it's bug or something, maybe someone can explain that?
Edit:
Okay, I've figured it out. There was a fragment of code that checked for occurrence of the "UPDATE, DELETE OR INSERT" word in the query, and if there was a word like that the query result was not fetched. I've changed that to search for SELECT word, now everything is okay.

Trying to sanitize user input to dynamically set column name in PearDB prepared statement

I'm trying to write a simple method to sort some DB results. We are stuck using PEAR DB, which is an old OO data object class.
I need to dynamically set which column we sort by:
$query = $db->prepare('SELECT * FROM ' . $this->table . ' WHERE ? IS NOT NULL');
The problem occurs because when the statement is executed, I wind up with the column name in regular quotes instead of slanted ones, so in the example, the column is never NULL.
try something like this, it will bump the requested column up against a list of known good column.
$columns = array("id", "name", "zipcode");
if (!in_array($requestedCol, $columns)){
// either reset $requestedCol to a default or error
}
$query = $db->prepare('SELECT * FROM ' . $this->table . ' WHERE '.$requestedColumn.' IS NOT NULL');

disable null value in mysql search

My database table has many columns.
I want to do a search based on multiple columns.
Sometimes it may not be the value of some columns.
How do these fields in sql query to be ineffective?
Thank you.
for examle:
$C1=$_POST[c1];
$C2=$_POST[c2];
SELECT * FROM mytable WHERE column1='$c1' AND column2='$c2'
i want if C2 be nulled, disable it from sql query.
One way is:
if(!$_POST[C2]){
SELECT * FROM mytable WHERE column1='$c1'
}
...
I want do it through sql query to do because My table has many columns.
First, you should never write queries with variables inside like that. Learn about PDO / mysqli and prepared statements.
Second, key references for an array should either be a string or integer; the expression $_POST[c1] will most likely cause a notice and implicit conversion to a string. It's better to write $_POST['c1'].
Third, and to answer your question, you can use isset() and strlen() to determine whether a value is "empty", i.e. empty string.
$params = array($_POST['c1']); // you should also check whether $_POST['c1'] is defined too
$sql = 'SELECT * FROM `table_name` WHERE column1 = ?';
if (isset($_POST['c2']) && strlen($_POST['c2'])) {
$sql .= ' AND column2 = ?';
$params[] = $_POST['c2'];
}
$stmt = $db->prepare($sql);
$stmt->execute($params);
Build an array of conditions by iterating through the POST values, adding a condition if the respective POST parameter is not empty:
$conditions = array();
foreach ($_POST as $key => $value) {
if (!empty($value)) {
$conditions[] =
$dbcolumn[$key] . " = '" . mysql_real_escape_string($value) . "'";
}
}
You will need an array $dbcolumn that matches POST variables to the database columns (or you have to provide some other means of translating between the two).
Now create a SQL query for the given conditions:
$query = 'SELECT * FROM mytable';
if (!empty($conditions)) {
$query .= ' WHERE ' . join(' AND ', $conditions);
}
Note that the extension that provides mysql_real_escape_string() is deprectaded. You should probably use some other extension to comunicate with the MySQL server and would than have to use the repsective call of the other extension.
This code not recomended, but if you realy want to do it on MySQL, you can use LIKE syntax like this:
SELECT * FROM mytable WHERE column1="$c1" AND column2="$c2%"
Add % character before or after $c2
Please don't do it!!

Categories