I am updating an application to use PDO, and it's fine apart from the following, I Have a database of darts League Team Names. With a prepared Select I cannot retrieve records when there is an & in the Team Name. This was not a problem with mysql and I can retrieve teams with any other character including '. How do I sanitise my select?
// Get the Team Record for display.
$stmt = $dbc->prepare('SELECT * FROM Teams WHERE Season = ? AND TeamName =?');
$stmt->bindValue(1, $Season, PDO::PARAM_INT);
$stmt->bindValue(2, $teamname, PDO::PARAM_STR);
$stmt->execute();
$trow = $stmt->fetch(PDO::FETCH_ASSOC);
if ( isset ($_GET['teamName'])) {
$teamName = ($_GET['teamName']);
$teamname = urldecode($teamName);
}
Related
New to R shiny and SQL
I have made some reactive dashboards but none yet using SQL database connection.
Here is my toy:
The database is the MySQL world database.
I want to join various tables and show some columns from each, but I want to be able to filter by Language found in the CountryLanguage table.
My WHERE statement doesn't work.
Current code:
ui <- fluidPage(
numericInput("nrows", "Enter the number of rows to display:", 5),
selectizeInput("inputlang", label = "Language", choices = NULL, selected = NULL, options = list(placeholder = "Please type a language")),
tableOutput("tbl")
)
server <- function(input, output, session) {
output$tbl <- renderTable({
conn <- dbConnect(
drv = RMySQL::MySQL(),
dbname = "shinydemo",
host = "shiny-demo.csa7qlmguqrf.us-east-1.rds.amazonaws.com",
username = "guest",
password = "guest")
on.exit(dbDisconnect(conn), add = TRUE)
dbGetQuery(conn, paste0(
"SELECT City.Name, City.Population, Country.Name, Country.Continent, CountryLanguage.Language, CountryLanguage.Percentage
FROM City
INNER JOIN Country on City.CountryCode = Country.Code
INNER JOIN CountryLanguage on Country.Code = CountryLanguage.CountryCode
WHERE CountryLanguage.Language = reactive({get(input$Selectize)})
LIMIT ", input$nrows, ";"))
})
}
shinyApp(ui, server)
I did not expect that code to work, but tried anyway. I suspect I can't pass an R command from within a dbGetQuery because it is expecting SQL syntax only. Is that correct?
So... what is the best way to set something like this up? I imagine I could make the joined selected stuff into a dataframe like
df <-dbGetQuery ( SELECT & JOIN)
dffilter <- df %>% filter ()
But is that going to make things super slow if the dataset is still quite large?
What would be the best practice here?
Having reactive(...) in a string is not evaluated, it's just a string. Further, DBI is not using glue on the query, so {get(...)} will do nothing.
You define the input as input$inputlang but in your reactive, you reference input$Selectize, I think that's a mistake.
You may want to consider parameterized queries vice constructing query strings manually. While there are security concerns about malicious SQL injection (e.g., XKCD's Exploits of a Mom aka "Little Bobby Tables"), it is also a concern for malformed strings or Unicode-vs-ANSI mistakes, even if it's a single data analyst running the query. Both DBI (with odbc) and RODBC support parameterized queries, either natively or via add-ons.
While this does not work for the LIMIT portion, it is useful for most other portions of a query. For that limit part, the req(is.numeric(input$nrows)) should be a reasonable check to ensure inadvertent injection problems.
Try this:
output$tbl <- renderTable({
req(is.numeric(input$nrows), input$inputlang)
conn <- dbConnect(
drv = RMySQL::MySQL(),
dbname = "shinydemo",
host = "shiny-demo.csa7qlmguqrf.us-east-1.rds.amazonaws.com",
username = "guest",
password = "guest")
on.exit(dbDisconnect(conn), add = TRUE)
dbGetQuery(conn, paste("
SELECT City.Name, City.Population, Country.Name, Country.Continent, CountryLanguage.Language, CountryLanguage.Percentage
FROM City
INNER JOIN Country on City.CountryCode = Country.Code
INNER JOIN CountryLanguage on Country.Code = CountryLanguage.CountryCode
WHERE CountryLanguage.Language = ?
LIMIT ", input$nrows),
params = list(input$inputlang))
})
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'] .
"'";
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.
I have a raw query in Symfony2
$sql = "SELECT * FROM Content
JOIN ContentLearningAreas ON Content.id = ContentLearningAreas.content_id
JOIN LearningArea ON ContentLearningAreas.learning_area_id = LearningArea.id
WHERE ContentLearningAreas.learning_area_id = {$id} AND Content.active = 1";
$stmt = $this->getEntityManager()->getConnection()->prepare($sql);
$stmt->execute();
return $stmt->fetchAll();
This works fine, but I wish to cast the results to Content Entities so that their functions still work.
Jake\NameOfBundle\Entity\Content
How can this be done?
EDIT
It appears that this is using PDO and not Doctrine or Symfony2.
Please read about Native Query in doctrine 2.
With NativeQuery you can execute native SELECT SQL statements and map
the results to Doctrine entities or any other result format supported
by Doctrine.
This works;
$stmt = $this->getEntityManager()->getConnection()->prepare($sql);
$stmt->execute();
$items = $stmt->fetchAll(\PDO::FETCH_CLASS, "Jake\NameOfBundle\Entity\Content");
function get_result_professions($key_word)
{
$sql = "SELECT users.name FROM users
inner join users_has_professions on users_has_professions.users_id = users.id
inner join professions on users_has_professions.professions_id = professions.id
where professions.key_word = ? ";
return $this->db->get()->query($sql, $key_word);
}
When I execute this code I receive the following error:
A Database Error Occurred
Error Number: 1096
No tables used
SELECT *
Filename: /var/www/expertt/models/search_model.php
Line Number: 31
How can I solve this problem? Thanks in advance.
$this->db->get() must contain an table name. in your case you want to remove it sindse you have an custom query so your function wil look like this:
function get_result_professions($key_word)
{
$sql = "SELECT users.name FROM users
inner join users_has_professions on users_has_professions.users_id = users.id
inner join professions on users_has_professions.professions_id = professions.id
where professions.key_word = '$key_word' ";
return $this->db->query($sql);
}
The $this->db->get() method in CodeIgniter's Active record requires a table name parameter (see the Active Record Documentation for more info) when used to query a table, unless you have previously build up the query using one of the other provided methods.
Usually when building up joins like you are doing you would use the select/ join methods provided by Active Record, like so
$this->db->select('users.name')->from('users')
->join('users_has_professions', 'users_has_professions.users_id = users.id')
->join('professions', 'users_has_professions.professions_id = professions.id')
->where('professions.key_word', $key_word);
(untested as I don't have your database to run it against)
You can then use the $this->db->get() method to retrieve the results like so
$results = $this->db->get();
foreach($query->result() as $row) {
//code here
}