tags:

views:

708

answers:

6

I have a function that I use called sqlf(), it emulates prepared statements. For instance I can do things like:

$sql = sqlf("SELECT * FROM Users WHERE name= :1 AND email= :2",'Big "John"','[email protected]') ;

For various reasons, I cannot use prepared statements, but I would like to emulate them. The problem that I run into is with queries like

$sql = sqlf("SELECT * FROM Users WHERE id IN (:1)",array(1,2,3) );

My code works, but it fails with empty arrays, e.g. the following throws a mysql error:

SELECT * FROM Users WHERE id IN ();

Does anyone have any suggestions? How should I translate and empty array into sql that can be injected into an IN clause? Substituting NULL will not work.

+5  A: 

Null is the only value that you can guarantee is not in the set. How come it is not an option? Anything else can be seen as part of the potential set, they are all values.

neouser99
Well I just tried that, and it infact works. "IN (NULL)" I thought would do the same thing as IS NULL, but they are different. Thanks for the answer.
John C
A: 

Is there a possibility that you could detect empty arrays withing sqlf and change the SQL to not have the IN clause?

Alteratively, you could postprocess the SQL before passing it to the "real" SQL executor so that "IN ()" sections are removed although you'd have to do all sorts of trickery to see what other elements had to be removed so that:

SELECT * FROM Users WHERE id IN ();
SELECT * FROM Users WHERE a = 7 AND id IN ();
SELECT * FROM Users WHERE id IN () OR a = 9;

would become:

SELECT * FROM Users;
SELECT * FROM Users WHERE a = 7;
SELECT * FROM Users WHERE a = 9;

That could get tricky depending on the complexity of your SQL - you'd basically need a full SQL language interpreter.

paxdiablo
A: 

The only way I can think to do it would be to make your sqlf() function scan to see if a particular substitution comes soon after an "IN (" and then if the passed variable is an empty array, put in something which you know for certain won't be in that column: "m,znmzcb~~1", for example. It's a hack, for sure but it would work.

If you wanted to take it even further, could you change your function so that there are different types of substitutions? It looks like your function scans for a colon followed by a number. Why not add another type, like an @ followed by a number, which will be smart to empty arrays (this saves you from having to scan and guess if the variable is supposed to be an array).

nickf
A: 

If your prepare-like function simply replaces :1 with the equivalent argument, you might try having your query contain something like (':1'), so that if :1 is empty, it resolves to (''), which will not cause a parse error (however it may cause undesirable behavior, if that field can have blank values -- although if it's an int, this isn't a problem). It's not a very clean solution, however, and you're better off detecting whether the array is empty and simply using an alternate version of the query that lacks the "IN (:1)" component. (If that's the only logic in the WHERE clause, then presumably you don't want to select everything, so you would simply not execute the query.)

dirtside
+1  A: 

I would say that passing an empty array as argument for an IN() clause is an error. You have control over the syntax of the query when calling this function, so you should also be responsible for the inputs. I suggest checking for emptiness of the argument before calling the function.

Lucas Oman
A: 

I would use zero, assuming your "id" column is a pseudokey that is assigned numbers automatically.

As far as I know, automatic key generators in most brands of database begin at 1. This is a convention, not a requirement (auto-numbered fields are not defined in standard SQL). But this convention is common enough that you can probably rely on it.

Since zero probably never appears in your "id" column, you can use this value in the IN() predicate when your input array is empty, and it'll never match.

Bill Karwin