I have a SQL statement that has to pull information from two databases, one is a constant and known database, and the other is dynamically found in the first database. The databases structure looks like this:
(database) lookup
(table) test
(fields) key, database_name
(row sample) "foo", "database_foo"
(database) database_foo
(table) bar
(fields) important1, important2
(row sample) "silly", "test"
So my SQL statement looks like this:
SELECT
test.key as key,
test.database_name as database_name,
bar.important1 as important1,
bar.importnat2 as important2,
FROM
lookup.test as test,
(database_name).bar as bar, # this, obviously, doesn't work
WHERE
key = 'foo'
LIMIT 1;
Is there a way I can make this work, or is it better for me to just do two separate SQL statements, one for the lookup, and one for the database?
If you must do it this way then you need to use dynamic sql and a two statements.
You have your query built as a string and then you run an EXEC on the query once it's constructed.
In this case you would have a string variable for the db name, then you would create a query from this variable and your literal query, then you would simply execute it.
Be aware, though, this makes you vulnerable to SQL Injection if you don't control the input parameters.
Erland Sommarskog has a great primer on using dynamic SQL:
http://www.sommarskog.se/dynamic_sql.html
EDIT: From #BryanMoyle comment below, you will likely need to do both a separate query and dynamic sql. You need to extract the value in order to determine the other DB name... Since you cannot use the DB name as a variable otherwise, you'll need to SELECT this information first, then stick it into the subsequent query.
I personally go for 2 separate statements; it would make it easier to control for errors such as the the lookup provides a row, that the row provides a valid database, etc.
As Matthew pointed out, beware of SQLIA and sanitize all user input. I like to MD5 hash inputs and compare to the hash of the value looked up.
I'd go for two separate queries, mediated by php: It's simpler, more robust, and will keep you sane and productive. Write one query to find out which database you should be talking to. Use php (not SQL) to direct your query to that database:
$row = $lookup->prepare("SELECT database_name WHERE key = 'foo'")->execute()->fetch();
$db = $row[0];
Then you contact $db and ask for the row with key foo. Use PHP code to select the right open connection, or switch databases inside the connection with USE:
$query2 = "USE " . $db . "; SELECT * FROM bar where key == 'foo'"
Related
I have been using prepared statements with PDO and I have found that table names cannot be bound. While I can use something like $sql = "select * from $table_name where something = :something"
If the $table_name variable is assigned internally by a method by running a query and getting the value from the database, is this method safe? Or is it bad practice to have variable table names? If it is opinion based, I'd like to know your opinions.
There are situations when you must use dynamic table names, so this is an acceptable practice. Personally, I would double check if this requirement is a must and if not, then hard code the table names in the queries.
If the table name comes from a user input, then I would use white listing as a security measure: get list of table names from mysql's information_schema and compare them to the input in the application code to avoid any aql injection.
If the table name comes from mysql's information_schema, then that's it, you can use it without any further checks because it is a valid table name. Just put backticks around the name.
If the table name comes from a table created by the application, then I would again simply retrieve the list of tables via the information_schema and check if it existed in the application code. This ensures that the table name is valid.
I'm using the PDO class to connect to mysql becuase I noticed that it's the best and surest way to do it. I noticed also that i can't use PDO parameter in tables name so I saw this question: Can PHP PDO Statements accept the table or column name as parameter?
I use in my application a GET parameter as a table name and now my question is:
It's better to use
function buildQuery( $get_var )
{
switch($get_var)
{
case 1:
$tbl = 'users';
break;
}
$sql = "SELECT * FROM $tbl";
}
or just give a "show tables" in MySql and compare every single table resulted from this command to the table passed via GET?
Thank you all!
Although "Just give a "show tables" in MySql and compare every single table resulted from this command to the table passed via GET" approach is flawed, it is not the main problem.
You are no the first one who, having no basic knowledge on database design, inventing yet another square wheel and then come to the community for help to make it move.
You need to rewind your ideas back some.
And reconsider database design, which should never involve dynamical tables, each of them used as a some sort of plain text file, queried with just SELECT * FROM table.
Please learn some relational database design basics first, then create a sensible design, then query your tables the way everyone do it: keep all the data in one table and use WHERE clause to get specific data.
Youre first solution is fine.
But don't pass the tablename via get. just pass a number which you can lookup via switch the table name.
This question already has answers here:
Get table column names in MySQL?
(19 answers)
Closed 9 years ago.
As I am still learning PHP and MySQL, I would like to know if it is possible to query a table without knowing it's name. Knowing the table I am querying and the column I would like to retrieve, I can write something like
$book_title=$row['book_title'];
Then I can use the resulting variable later in the script. In each case, each book category table will have a column of interest with a different name. I have several tables on which I am running queries. I am able to query any table by using a variable that always evaluates to the correct table name, because all the input from users corresponds to the tables in the database, so the $_POST super global will always carry a correct table name. The problem is for me to have a
$variable=$row['column'];
in cases where I do not know a column name before hand even though I know the table name.
My queries are simple, and look like
query="select * FROM $book_categories WHERE id=$id";
$row = mysqli_fetch_array ($result);
$variable=$row['?'];
The question mark say, I do not know what column to expect, as it's name could be anything from the tables in the database!
Since I have several tables, the query will zero on a table, but the column names in each table varies so I would like to be able to use one query that can give me an entry from such a column.
I hope my question is clear and that I am not asking for the impossible. If it's ambiguous, I care to elucidate (hope so).
I'm not sure what you mean, but it is possible to reference specifc columns by typing index (starting with 0) something like this: $row[0], $row[1] where 0 indicates the first column, and 1 indicates the second column from the returned recordset.
Example:
If you have a select-statement like this:
SELECT title, author FROM books
You could reference these two columns with $row[0], $row[1]
If you try to get the value of $row[2] you will get an unassigned value because there are only two columns (0 and 1) from the recordset.
If you have a select-statement like this:
SELECT * FROM book_categories
and the recordset returns three columns, then you could access these with $row[0], $row[1] and $row[2]. $row[3] does not exist because there are only three columns (0,1 and 2)
Since you are learning maybe we could take some time to explain why this is possible but many people (including myself) would say this is bad -- or at least dangerous
Why you can
Your SQL query is basically a text string you send to the DB server, which decode that string trying to interpret it as SQL in order to execute the query.
Since all you send to the DB server is text string, you could build that string however you want. Such as using string interpolation as you did:
select * FROM $book_categories WHERE id=$id
That way, you could replace any part of your query by the content of a variable. You could even go further:
$query FROM $book_categories WHERE id=$id
Where $query could by SELECT * or DELETE.
And, why not initializing all those variables from a form:
$book_categories = $_POST['book_categories'];
$id = $_POST['id'];
$query = $_POST['query'];
Great, no? Well, no...
Why you shouldn't
The problem here is "could you trust those variables to only contain acceptable values?". That is, what would append if $book_categories somehow resolve to one table you didn't want to (say myTableContainigSecretData)? And what if $id resolve to some specially crafted value like 1; DELETE * FROM myImportantTable;?
In these conditions, your query:
select * FROM $book_categories WHERE id=$id
Will become as received by the DB server:
select * FROM myTableContainigSecretData WHERE id=1; DELETE * FROM myImportantTable;
Probably not what you want.
What I've tried to demonstrate here is called SQL injection. This is a very common bug in web application.
How to prevent that?
The best way to prevent SQL injection is to use prepared statement to replace some placeholders in your query by values properly shielded against SQL injection. There was an example posted a few minutes ago as a response to an other question: https://stackoverflow.com/a/18035404/2363712
The "problem" regarding your initial question is that will replace values not table or columns identifiers.
If you really want to replace table/columns identifiers (or other non-value part of your query) by variables contents, you will have to check yourself the content of each of these variables in order to prevent SQL injection. This is quite feasible. But that's some work...
Im writing PHP scripts for using with my mySQL database. The only problem i have is binding variables for drop table/ create table and so on.
$stmt = $link->prepare("DROP TABLE ?");
$stmt->bind_param('s','testing');
$stmt->execute();
is not working. I tried also:
SELECT * FROM (SELECT MAX(name) from profiles where name='testing') <- is working
DROP TABLE (SELECT MAX(name) from profiles where name='testing') <- dont work
Binding a parameter is not the same as just replacing a portion of the string : you cannot just bind anything you want.
In this case : you cannot use a bound parameter for a table name -- you'll have to use string concatenations to build your query, instead of using a prepared statement.
As a reference, quoting PREPARE Syntax :
Parameter markers can be used only
where data values should appear, not
for SQL keywords, identifiers, and so
forth.
As far as I know, you can only bind to a parameter, not to any part of a query you want. You're essentially telling the database "hey, I'm going to pass you a value here, and I want you to do your magic to make sure it doesn't overstep its bounds". Things like table names or field names aren't values, they're part of the table structure itself.
In this case, you'll have to just use a use a simple $query = "DROP TABLE " . $table;. It should be easy enough to check against a list of known tables to ensure you're not injecting anything harmful. Anything that makes DDL changes shouldn't be taking input from the user anyway, as far as I'm concerned. These sorts of changes can be based on user input, but the actual construction of the query should be really well known and shouldn't need outside data to construct.
Also, I'm not really sure what you're trying to do with this query:
DROP TABLE (SELECT MAX(name) from profiles where name='testing');
It looks like you might be trying to delete a record, but that's entirely the wrong syntax for that. If you're trying to drop a table whose name comes from the result of another query, I really don't think you can do that either. I'm 99% sure that DROP TABLE expects only a literal table name value.
Are you sure you want to drop tables dynamically?
It is extremely unusual.
It seems you have wrong database design.
And now you faced a consequence.
It seems you should have one table users and delete rows from it, not tables.
Instead of hard coding sql queries like Select * from users where user_id =220202 can these be made dynamic like Select * from $users where $user_id = $input.
Reason i ask is when changes are needed to table/column names i can just update it in one place and don't have to ask developers to go line by line to find all references to update. It is very time consuming. And I do not like the idea of exposing database stuff in the code.
My major concern is load time. Like with dynamic pages, the database has to fetch the page content, same way if queries are dynamic first system has to lookup the references then execute the queries, so does it impact load times?
I am using codeignitor PHP.
If it is possible then the next question is where to store all the references? In the app, in a file, in the DB, and how?
---EDIT:
Even better: Can the SQL query itself be made dynamic? I can just reference $sqlA instead of the whole query? This way if I have to re-write the query I can just update 1 file.
Because you are using Codeigniter, I would reccomend utilizing the Active Record Class to accomplish what you are trying to do.
The active record class enables you to build queries dynamically in steps allowing you to build them logically. So to take your example using active record...
( this could be accomplished with less code, I'm just trying to illustrate Active Record )
$this->db->select('*');
$this->db->from($table);
$this->db->where($user_id, $input);
and so to show what I mean about building the query logically, you can build whatever logic you want INTO the query building process. Lets say you have a $limit variable that you set if you want to limit the number of results you get. BUT if it isn't set (or NULL) you don't want to set the limit clause.
if ( $isset($limit) ) {
$this->db->limit($limit);
}
and now to execute your query now that it has been built
$query = $this->db->get();
Then just deal with $query with your database class just like you would any other CodeIgniter query object.
Of course you can, if that's what you wish. I'd rather recommend you taking more time to design you database but changes in the schema are inevitable in the long run.
I don't think load time would be an issue with this because ussually the bottleneck in this applications is in the database.
Finally my recommendation is to save this in a file just by declaring the column names as php variables
It depends on the database driver(s) you are using. The old PHP database drivers did not support placeholders (PHP 3.x). The modern (PDO) ones do. You write the SQL with question marks:
SELECT * FROM Users WHERE User_ID = ?
You then provide the value of the user ID when you execute the query.
However, you cannot provide the column name like this - only values. But you could prepare a statement from a string such as:
SELECT * FROM Users WHERE $user_id = ?
Then you provide the value at execute time.
mysql_query() takes a string and it doesn't need to be a constant string, it can be a variable.
$SQL = "SELECT foo FROM bar b";
SQLSet = mysql_query($SQL);
Aa you can see, you can use ordinary string manipulation to build your whole SQL query.
$SQL="SELECT * FROM MyTable";
$BuzID = 5;
$Filter = "Buz=".$BuzID;
if (is_numeric($BuzID)) SQL .= " WHERE ".$Filter;
SQLSet = mysql_query($SQL);
This will expand to "SELECT * FROM MyTable WHERE Buz=5" if $BuzID is set to any number.
If not the statement will just be "SELECT * FROM MyTable"
As you can see, you can build very complex SQL statements on the fly without need of variable support in the SQL server.
IF you want constants such as database name, user login, you can but them in a separate include located outside the public directory.
SecretStuff.inc.php
$__DatabaseName = "localhost";
$__UserName = "DatabaseAccess";
$__Password = "E19A4F72B4AA091C6D2";
Or have the whole PHP database connection code in the same file.