Using SilverStripe's SQLQuery object without selecting * fields - orm

Looking at the documentation, there are fairly good instructions on how to build up a SQL query.
My code looks like this:
$sqlQuery = new SQLQuery();
$sqlQuery->setFrom('PropertyPage')->selectField('PropertyType')->setDistinct(true);
I'm aiming to get the following SQL:
SELECT DISTINCT PropertyType FROM PropertyPage
but instead I'm getting back this:
SELECT DISTINCT *, PropertyType FROM PropertyPage
Even their own example seems to give back a 'SELECT DISTINCT *. How can I avoid this?

Why do you use SQLQuery directly?
With pure ORM it should go like this:
$result = PropertyPage::get()->setQueriedColumns(array('PropertyType'))->distinct();
which returns a DataList you can loop over.
See setQueriedColumns() and distinct()
What version of SS framework are you using? Distinct was added in 3.1.7 iirc.

Just to add to #wmk's answer as well as directly address how to do it with SQLQuery, your call to selectField is the reason why that query happened. The documentation to selectField shows that it adds an additional field to select, not restrict the list to just that field.
The reason why their examples (A and B) also had the issue is that the first parameter for the constructor for SQLQuery is a default value of "*" for select statement.
To still use your code as a base, replace your use of selectField with setSelect. Your query will then look like this:
SELECT DISTINCT PropertyType FROM PropertyPage
It isn't bad to directly query the database using SQLQuery especially if you really just want the raw data of a particular column or the result itself cannot be expressed against a DataObject (eg. using column aliases). You would also have a small performance improvement that PHP would not have to instantiate a DataObject.
While saying that, it can be far more useful having a DataObject and the various functions that it exposes per record.

Related

Filtering Harmful SQL Select Statements in ASP.NET

I am exposing a web service that constructs a SQL SELECT statement and accepts parameters from a controller. The generic functions looks like this:
DataTable SqlSelect (string select, string from, string Where, string orderby = "", string groupBy = "")
{
string sql = "SELECT " + select + " FROM " + from + " WHERE " + where
(orderby =="") ? "" : "ORDER BY " + orderby ...
//do other stuff
}
Now what makes me worry is the fact that base on given function above, the user may now inject harmful commands like:
SqlSelect("DROP TABLE 'TABLENAME'", "INFORMATION_SCHEMA.TABLES", "TABLE_NAME like %%'");
Which I want to prevent.
Now my question is: what is the best thing I can do to prevent user to UPDATE, MODIFY, DELETE, TRUNCATE tables and only allow SELECT statement (can use something like READ oNLY?)
Note: this is similar to this question but the user was working on PHP, while I'm in ASP.NET MVC, also what I want to achieve here is only allow SELECT or 'GET' statement.
Do not do it this way. You need to parameterize your queries which means that you cannot accept SQL text as an input. I have found many attempts by developers to detect SQL injection attacks and I almost always find a way of getting past their logic.
If you need to be able to dynamically construct any SELECT query based on any table in your database then you could easily create a class that indicates the table, select columns and where predicate columns as enums, and the values of the where predicates. Concatenate the SQL text based on this class and include the predicate values using SqlParameters.
It is just one example, but for sure you do not want to accept SQL text.
Make use of a data reader https://msdn.microsoft.com/en-us/library/haa3afyz(v=vs.110).aspx. You can catch any exceptions when you call ExecuteReader().
However I would advise against exposing this kind of generic functionality to client side code. You should rather only provide controlled access to your data via an appropriate data layer using something like the repository pattern.

Spring JDBCDaoSupport - dealing with multiple selects

Does Spring have any features which allow for a batch select? I basically have n number of selects to execute depending on the number of records in a list. At the moment because the size of the list is always changing i have to dynamically build the SQL which will be executed. The end product looks something like this
select * from record_details t WHERE id IN ((?),(?),(?))
However the code to generate this SQL on the fly is messy and I'm wondering if there is a nicer approach for this type of problem?
The NamedParameterJdbcTemplate (and according support class) does have that support.
public void someRepoMethod(List ids) {
String query = "select * from record_details where id in (:ids)";
getNamedParameterJdbcTemplate().query(query, Collections.singletonMap("ids", ids), new YourRowMapper());
}
If you don't want to generate SQL yourself you have to use some existing framework. From what i know myBatis, is more lightweight than hibernate so it can suit you more, but there may be other more suitable.

VB.NET - Having some trouble with a parametized queries

I built a prototype system with some database queries. Since it was just a prototype and I was very new to databases, I just used a direct string. This is the string I used and it works fine:
command = New OleDbCommand("SELECT * FROM " + prefix + "CanonicForms WHERE Type=1 AND Canonic_Form='" + item + "'", dictionary_connection)
Now, in putting it in a real system, I wanted to use the more secure parametized method, so after some googling I came up with this:
command = New OleDbCommand("SELECT * FROM #prefix WHERE Type=1 AND Canonic_Form=#form", dictionary_connection)
command.Parameters.AddWithValue("#prefix", prefix + "CanonicForms")
command.Parameters.AddWithValue("#form", item)
But all I get is an error for an incomplete query clause. What have I done differently between the two?
Your table name can't be a parameter. You might have to do some form of concatenation. It's not really a parameter in the formal sense of the word.
As ek_ny states, your table name can not be a parameter.
If you are really paranoid about injection then check the passed in table name against a white list of allowable values prior to buidling up the SQL string.
#cost, I agree that it would be nice but its just not a feature that exists. Generally its assumed that your WHERE clauses are the dynamic portion of the query and everything else, including the SELECT list and the table name, are static. In fact, most of the WHERE clause isn't even dynamic, only the search values themselves. You can't dynamically add column names this way either. This could definitely be built but it would require that the query engine be more aware of the database engine which is a can of worms that Microsoft didn't feel like opening.
Imagine a drop down menu with table names such as 'Jobs', 'People', 'Employers' that some naive developer built. On postback that value is used to SELECT * FROM. A simple SQL injection would be all that it takes to jump to another table not listed in that menu. And passing something really weird could very easily throw an error that could reveal something about the database. Value-only parametrized queries can still be broken but the surface area is much smaller.
Some people with multiple table prefixes use schemas, is that an option for you?

NHibernate Projection using SqlQuery

I'm trying to reproduce the HqlQuery style 'select new ObjectToProjectOut' functionality. i.e. take a list of columns returned from a query and return as a list of ObjectToProjectOut types that are instantiated using a Constructor with as many parameters as the columns in the query.
This is in effect what 'select new ObjectToProjectOut' achieves in Hql.... but clearly that's not available in SqlQuery. I think I need to set a result transform and use either PassThroughResultTransformer, DistinctRootEntityResultTransformer etc to get it to work.
Anyone know what I should use ?
ok.... after looking at the NHibernate code it seems that I was looking for AliasToBeanConstructorResultTransformer.... of course!
However I may have found an nHibernate bug. If you have the same column name returned twice from two different tables (market.name and account.name, say) then by the time nHibernate returns the array from the db to the transformer, the first occurance of 'Name' will be used for both. Nasty.
Work around is to uniquely alias. With Hql, the sql generated is heavily aliased, so this is only a bug with SqlQuery.
Grrrr. Today must be my day, also found another nHibernate bug/issue I've posted to StackOverflow for comment.
You could use the AddEntity method to fill entities from a SQL query.
Here are two examples from the NHibernate docs:
sess.CreateSQLQuery("SELECT * FROM CATS")
.AddEntity(typeof(Cat));
sess.CreateSQLQuery("SELECT ID, NAME, BIRTHDATE FROM CATS")
.AddEntity(typeof(Cat));

How to search for matches with optional special characters in SQL?

I have a problem with a search query I am using my data contains names and information that has apostrophes in various forms (HTML encoded and actual).
So for example I would like an alternative to this:
SELECT * FROM Customers WHERE REPLACE(LastName,'''','')
LIKE Replace('O''Brien,'''','')
This is just an example, what I want is a way where if someone types OBrien or O'Brien this will still work, I need to replace three versions of the character and the data is feed sourced and cannot be changed - what can be done to a query to allow for this kind of search to work.
I have Items with names which work this way which currently have many nested REPLACE functions and cannot seem to find something that will work this way, which is more efficient.
I am using MS SQL 2000 with ASP if that helps.
Edit
Here is the query that needs to match O'Brien or OBrien, this query does this but is too inefficient - it is joined by another for Item Names and FirstName (optional) for matching.
SELECT * FROM Customers
WHERE
REPLACE(REPLACE(REPLACE(LastName,''',''),''',''),'''','')
LIKE
REPLACE(REPLACE(REPLACE('%O'Brien%',''',''),''',''),'''','')
If you want to stay correct and do this in SQL this is probably the best you can do
SELECT * FROM Customers WHERE
LastName LIKE 'O%Brien' AND
REPLACE(LastName,'''','') LIKE 'O''Brien'
You will still get table scans sometimes, due to poor selectivity.
The reason for the first where is to try to use an existing index.
The reason for the second match is to ensure that last names like ObbBrien do not match.
Of course the best thing to do would be not to need the ugly replace. This could be achieved in the app by storing an additional clean lastname column. Or in a trigger. Or in an indexed view.
You could try this:
SELECT *
FROM Customers
WHERE LastName LIKE Replace('O''Brien,'''','%')
This should allow it to use an index as you are not modifying the original column.
For pure SQL, the escaping is entirely unnecessary.
SELECT * FROM Customers WHERE LastName = 'O''Brien'
Use parameters instead of building the queries in code.
If you are using ADO you can use a syntax like this:
Dim cmd, rs, connect, intNumber
Set cmd = Server.CreateObject("ADODB.Command")
cmd.ActiveConnection = "your connectionstring"
cmd.CommandText = "SELECT * FROM Customers WHERE LastName LIKE #LastName"
cmd.Parameters.Append cmd.CreateParameter("#LastName",,,,"O'Brien")
Set rs = cmd.Execute
This should perform the query and insert the string O'Brien properly formatted for your database.
Using parameters ensures that all values are properly formatted and it also protects you against sql injection attacks.