PDO query with parameter [duplicate] - pdo

I'm trying to insert values in the contents table. It works fine if I do not have a PHP variable inside VALUES. When I put the variable $type inside VALUES then this doesn't work. What am I doing wrong?
$type = 'testing';
mysql_query("INSERT INTO contents (type, reporter, description)
VALUES($type, 'john', 'whatever')");

The rules of adding a PHP variable inside of any MySQL statement are plain and simple:
1. Use prepared statements
This rule covers 99% of queries and your query in particular. Any variable that represents an SQL data literal, (or, to put it simply - an SQL string, or a number) MUST be added through a prepared statement. No exceptions.
This approach involves four basic steps
in your SQL statement, replace all variables with placeholders
prepare the resulting query
bind variables to placeholders
execute the query
And here is how to do it with all popular PHP database drivers:
Adding data literals using mysqli
$type = 'testing';
$reporter = "John O'Hara";
$query = "INSERT INTO contents (type, reporter, description)
VALUES(?, ?, 'whatever')";
$stmt = $mysqli->prepare($query);
$stmt->bind_param("ss", $type, $reporter);
$stmt->execute();
The code is a bit complicated but the detailed explanation of all these operators can be found in my article, How to run an INSERT query using Mysqli, as well as a solution that eases the process dramatically.
For a SELECT query you will need to add just a call to get_result() method to get a familiar mysqli_result from which you can fetch the data the usual way:
$reporter = "John O'Hara";
$stmt = $mysqli->prepare("SELECT * FROM users WHERE name=?");
$stmt->bind_param("s", $reporter);
$stmt->execute();
$result = $stmt->get_result();
$row = $result->fetch_assoc(); // or while (...)
Adding data literals using PDO
$type = 'testing';
$reporter = "John O'Hara";
$query = "INSERT INTO contents (type, reporter, description)
VALUES(?, ?, 'whatever')";
$stmt = $pdo->prepare($query);
$stmt->execute([$type, $reporter]);
In PDO, we can have the bind and execute parts combined, which is very convenient. PDO also supports named placeholders which some find extremely convenient.
2. Use white list filtering
Any other query part, such as SQL keyword, table or a field name, or operator - must be filtered through a white list.
Sometimes we have to add a variable that represents another part of a query, such as a keyword or an identifier (a database, table or a field name). It's a rare case but it's better to be prepared.
In this case, your variable must be checked against a list of values explicitly written in your script. This is explained in my other article, Adding a field name in the ORDER BY clause based on the user's choice:
Unfortunately, PDO has no placeholder for identifiers (table and field names), therefore a developer must filter them out manually. Such a filter is often called a "white list" (where we only list allowed values) as opposed to a "black-list" where we list disallowed values.
So we have to explicitly list all possible variants in the PHP code and then choose from them.
Here is an example:
$orderby = $_GET['orderby'] ?: "name"; // set the default value
$allowed = ["name","price","qty"]; // the white list of allowed field names
$key = array_search($orderby, $allowed, true); // see if we have such a name
if ($key === false) {
throw new InvalidArgumentException("Invalid field name");
}
Exactly the same approach should be used for the direction,
$direction = $_GET['direction'] ?: "ASC";
$allowed = ["ASC","DESC"];
$key = array_search($direction, $allowed, true);
if ($key === false) {
throw new InvalidArgumentException("Invalid ORDER BY direction");
}
After such a code, both $direction and $orderby variables can be safely put in the SQL query, as they are either equal to one of the allowed variants or there will be an error thrown.
The last thing to mention about identifiers, they must be also formatted according to the particular database syntax. For MySQL it should be backtick characters around the identifier. So the final query string for our order by example would be
$query = "SELECT * FROM `table` ORDER BY `$orderby` $direction";

To avoid SQL injection the insert statement with be
$type = 'testing';
$name = 'john';
$description = 'whatever';
$con = new mysqli($user, $pass, $db);
$stmt = $con->prepare("INSERT INTO contents (type, reporter, description) VALUES (?, ?, ?)");
$stmt->bind_param("sss", $type , $name, $description);
$stmt->execute();

The best option is prepared statements. Messing around with quotes and escapes is harder work to begin with, and difficult to maintain. Sooner or later you will end up accidentally forgetting to quote something or end up escaping the same string twice, or mess up something like that. Might be years before you find those type of bugs.
http://php.net/manual/en/pdo.prepared-statements.php

The text inside $type is substituted directly into the insert string, therefore MySQL gets this:
... VALUES(testing, 'john', 'whatever')
Notice that there are no quotes around testing, you need to put these in like so:
$type = 'testing';
mysql_query("INSERT INTO contents (type, reporter, description) VALUES('$type', 'john', 'whatever')");
I also recommend you read up on SQL injection, as this sort of parameter passing is prone to hacking attempts if you do not sanitize the data being used:
MySQL - SQL Injection Prevention

That's the easy answer:
$query="SELECT * FROM CountryInfo WHERE Name = '".$name."'";
and you define $name whatever you want.
And another way, the complex way, is like that:
$query = " SELECT '" . $GLOBALS['Name'] . "' .* " .
" FROM CountryInfo " .
" INNER JOIN District " .
" ON District.CountryInfoId = CountryInfo.CountryInfoId " .
" INNER JOIN City " .
" ON City.DistrictId = District.DistrictId " .
" INNER JOIN '" . $GLOBALS['Name'] . "' " .
" ON '" . $GLOBALS['Name'] . "'.CityId = City.CityId " .
" WHERE CountryInfo.Name = '" . $GLOBALS['CountryName'] .
"'";

Related

SqlCommmand parameters not adding to UPDATE statement (C#, MVC)

If you look at the stuff commented out, I can easily get this to work by adding user input directly in to the query, but when I try to parameterize it, none of the values are being added to the parameters...
This code is throwing an error
Must define table variable #formTable
but the issue is none of the values are adding, not just the table variable (verified by replacing table name variable with static text).
I have many insert statements in this project structured exactly like this one which work perfectly. What am I doing wrong here?
string constr = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;
using (SqlConnection con = new SqlConnection(constr))
{
//string query = "UPDATE " + s.formTable + " SET " + s.column + " = '" + s.cellValue + "' WHERE MasterID = '" + s.id + "'";
string query = "UPDATE #formTable SET #column = #cellValue WHERE MasterID = #id;";
using (SqlCommand cmd = new SqlCommand(query))
{
//SqlParameter param = new SqlParameter("#formTable", s.formTable);
//cmd.Parameters.Add(param);
cmd.Parameters.AddWithValue("#formTable", s.formTable);
cmd.Parameters.AddWithValue("#column", s.column);
cmd.Parameters.AddWithValue("#cellValue", s.cellValue.ToString());
cmd.Parameters.AddWithValue("#id", s.id.ToString());
cmd.Connection = con;
con.Open();
cmd.ExecuteNonQuery();
con.Close();
}
}
Parameters are for values, not object identifiers (tables, columns, etc.), so the only valid parameters you have are #cellValue and #id.
If you want to dynamically set table/column names based on user input, you're likely looking at string concatenation. However, that doesn't necessarily mean SQL injection. All you need to do is validate the user input against a set of known values and use the known value in the concatenation.
For example, suppose you have a List<string> with all of your table names. It can be hard-coded if your tables are never going to change, or you can make it more dynamic by querying some system/schema tables in the database to populate it.
When a user inputs a value for a table name, check if it's in the list. If it is, use that matching value from the list. If it isn't, handle the error condition (such as showing a message to the user). So, even though you're using string concatenation, no actual user input is ever entered into the string. You're just concatenating known good values which is no different than the string literals you have now.

double where statement in SQL and ASP

I am a little lost on how to incorporate TWO Where in my sql statement in my asp.
I am trying to get the userID and password entered previously and compare it with what I have in my database created on SQL:
I think my problem comes from my double quotation and single quotation.
UserID is a number in my database and Password is a short text.
var mycon = new ActiveXObject("ADODB.Connection");
var myrec = new ActiveXObject("ADODB.Recordset");
mycon.Open("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\\Users\\Omnivox.mdb");
var txtpassword = Request.QueryString("txtpassword");
var txtuserID = parseInt (Request.QueryString("txtuserID"));
var sql;
sql = "SELECT UserID, UserPassword FROM UserOmnivox WHERE UserID=" +txtuserID+ " AND UserPassword='" + txtpassword + "';";
myrec.Open(sql, mycon);
thank you
UPDATE: It is still not working. The error massage is : no value given for one or more required parameters for the line myrec.Open(sql,mycon)
Change
sql = "SELECT * FROM UserOmnivox WHERE UserID=" +txtuserID "AND UserPassword="'+txtpassword';
to
sql = "SELECT * FROM UserOmnivox WHERE UserID=" +txtuserID + " AND UserPassword='"+txtpassword+"'";
If you'd done any kind of basic debugging, like LOOKING at the query string you're generating, you'd have seen this:
sql = "SELECT [..snip..] UserID=" +txtuserID "AND UserPassword="'+txtpassword
^^--- no space
^--- missing +
which produces
SELECT .... UserID=1234AND userPassword
^^---syntax error, no such field '1234AND'
And then, yes, your quotes are wrong too
sql = "SELECT ... UserID=" +txtuserID "AND UserPassword="'+txtpassword
^------------------^-- one string
^-----------------^-- another string
^---???
It should be
sql = "SELECT * FROM UserOmnivox WHERE UserID=" +txtuserID + " AND UserPassword='" + txtpassword + "';";
I find another more flexible solution is better. Sometimes based on conditions you have one where condition, in others you have zero, and in others you have two. If you go down these paths they don't solve that issue. The following does.....
Some sql query
where 1=1 -- ## A condition that will always be true and does nothing to your query.
and first optional where clause
and second optional where clause
This way if you don't have the first where clause in a given situation but you do have the second you are not missing the words "where". You always have the where and you optionally add any array of "and" parts to your where statement. 100% flexibility in this method works for all challenges. Plus it is easier to follow code once you get past the wtf is this 1=1 nonsense reaction.

Query to retrieve all row data for supplied column name

I am using Eclipse and Oracle SQL Developer. My connections are all set up. I am trying to query my database in SQL Developer by passing in a column name as a variable.
For example, I just want to use something similar to this statement:
select * from CUSTOMERS;
but allow CUSTOMERS to be a variable where I can pass in any table name.
Currently this pulls all column names from given column name and connection:
final String query = "select column_name from all_tab_columns"
+" where owner = ?"
+" and table_name = ?";
try {
headers = DAO.useJNDI(jndi)
.setSQL(query)
.input(1, host)
.input(2, tableName)
.list(String.class);
I want to do the same thing but with rows. Does anyone know how to do this? This is what I am thinking about so far:
final String sql = "select *"
+ " from table_name"
+ " where owner = ? and table_name = ?";
try {
logger.debug(tableName+sourceJNDI);
sourceList = DAO.useJNDI(sourceJNDI)
.setSQL(sql)
.input(1, host)
.input(2, tableName)
.list(DatabaseCompareDto.class);
The main focus is the SQL statements. I know everything else works.
If I'm reading your question correctly, I think what you want is to replace the first table_name in your SQL with ?, then add an additional .input( 1, tableName) :
final String sql = "select *"
+ " from ?"
+ " where owner = ? and table_name = ?";
try {
logger.debug(tableName+sourceJNDI);
sourceList = DAO.useJNDI(sourceJNDI)
.setSQL(sql)
.input(1, tableName)
.input(2, host)
.input(3, tableName)
.list(DatabaseCompareDto.class);
You can't pass the table name as a parameter. Instead of wasting your energy on such an alleged generic solution, use or create a small templating engine which allows you to replace the table name in your query before sending it to the database.

IF-ELSE Alternative for Multiple SQL criteria for use in BIRT

I want to create a report by using BIRT. I have 5 SQL criterias as the parameter for the report. Usually when I have 3 criterias, I am using nested if-else for the WHERE statement with javascript.
Since right now I have more criteria it becomes more difficult to write the code and also check the possibilities, especially for debug purposes.
For example the criteria for table employee, having these 5 criterias : age, city, department, title and education. All criteria will be dynamic, you can leave it blank to show all contents.
Do anyone know the alternative of this method?
There is a magical way to handle this without any script, which makes reports much easier to maintain! We can use this kind of SQL query:
SELECT *
FROM mytable
WHERE (?='' OR city=? )
AND (?=-1 OR age>? )
AND (?='' OR department=? )
AND (?='' OR title=? )
So each criteria has two dataset parameters, with a "OR" clause allowing to ignore a criteria when the parameter gets a specific value, an empty value or a null value as you like. All those "OR" clauses are evaluated with a constant value, therefore performances of queries can't be affected.
In this example we should have 4 report parameters, 8 dataset parameters (each report parameter is bound to 2 dataset parameters) and 0 script. See a live example of a report using this approach here.
If there are many more criteria i would recommend to use a stored procedure, hence we can do the same with just one dataset parameter per criteria.
Integer parameter handling
If we need to handle a "all" value for an integer column such age: we can declare report parameter "age" as a String type and dataset parameters "age" as an integer. Then, in parameters tab of the dataset use a value expression instead of a "linked to report parameters". For example if we like a robust input which handles both "all" "null" and empty values here is the expression to enter:
(params["age"].value=="all" || params["age"].value=="" || params["age"].value==null)?-1:params["age"].value
The sample report can be downloaded here (v 4.3.1)
Depending on the report requirements and audiance you may find this helpful.
Use text box paramaters and make the defualt value % (which is a wild card)
SELECT *
FROM mytable
WHERE city like ?
AND age like ?
AND department like ?
AND title like ?
This also allows your users to search for partial names. if the value in the city text box is %ville% it would return all the cities with "ville" anyplace in the city name.
If report parameters to be included in SQL-WHERE clause would be named according to some naming convention, for instance query_employee_[table column name], you could write Java-Script code in a generic way, so that you will not have to change it when new reporters being added.
for each param in params {
if param.name starts with query_employee_ {
where_clause += " and " + param.name.substring(after query_employee) + " == '" + param.value + "'";
}
}
You will have to check type of a parameter to make a decision whether you have to quote the parameter value.
The event handler could look as follows (implemented in Java, but it should be possible to port it to JavaScript, if you really need it to be in JavaScript):
public class WhereConditionEventHandler extends DataSetEventAdapter {
#Override
public void beforeOpen(IDataSetInstance dataSet,
IReportContext reportContext) throws ScriptException {
super.beforeOpen(dataSet, reportContext);
String whereClause = " where 1 = 1 ";
SlotHandle prms = reportContext.getDesignHandle().getParameters();
for (int i = 0; i < prms.getCount(); i++) {
if (prms.get(i) instanceof ScalarParameterHandle) {
ScalarParameterHandle prm = (ScalarParameterHandle) prms.get(i);
int n = prm.getName().indexOf("sql_customer_");
if (n > -1) {
String prmValue = "" + reportContext.getParameterValue(prm.getName());
if (DesignChoiceConstants.PARAM_TYPE_STRING.equals(prm.getDataType())) {
prmValue = "'" + prmValue + "'";
}
whereClause += " and " + prm.getName().substring("sql_customer_".length()) + " = " + prmValue;
}
}
}
System.out.println("sql: " + whereClause);
dataSet.setQueryText(dataSet.getQueryText() + whereClause);
}
}
By the way, you can pass in parameters that are not registered as report parameters in the BIRT report design. BIRT will nevertheless put them into "params" array.

SQL - Using a prepared statement for the FROM clause?

Is it possible to use a prepared statement for the FROM clause?
I'm trying to do this:
PreparedStatement preStmType = conn.prepareStatement("SELECT * FROM ? WHERE article_id = ?");
preStmType.setString(1, rsetArticle.getString(5));
preStmType.setInt(2, rsetArticle.getInt(1));
It does not seem to work. When I remove the argument for the FROM clause and use it only in the where, it works, but I would like to generate the FROM dynamically too.
Nope, you can't
Prepared statements supports data literals only.
Speaking of this particular case, why do you want to generate fieldlist dynamically? If you don't know what field you need - just select all and then pick one from the returned row
Are you using Java?
How about trying this:
String qryStr = "SELECT * FROM "+rsetArticle.getString(5)+" "
qryStr =qryStr + "WHERE article_id = ?"
PreparedStatement preStmType = conn.prepareStatement(qryStr);
preStmType.setInt(1, rsetArticle.getInt(1));