Reply to your further edit (let't hope this is the last one ;)
Prepared statements should afford you a clean conscience because the values never make up part of the SQL query .. unless you are using a library that emulates it, and there are libraries out there that do, so don't assume anything.
Prepared statements do not afford you a "clean conscience". You still need to be careful with your inputs in parameterized queries, because not every input to every query can be bound as a variable. Some operations do require dynamic query construction.
"because not every input to every query can be bound as a variable" Can you give me an example?
var sql = "select * from User where UserType=@ut order by ModificationDate " + sort_order;
In this case, if sort_order directly comes from a 'asc'/'desc' radio button then you have an injection attack.
The correct way to do it would be:
var sql = "select * from User where UserType=@ut order by ModificationDate " + (sort_order=="asc"?"asc":"desc");
The point was that there are some parts of sql that can't be parameterized like sort order or limits on the resultant recordset. Although, ideally support for those things should be coming from your database vendor (if it isn't already there).
For example, SqlServer supports using variables in top expressions: select top(@max) * from ....
Because when you try to use a prepared statement to populate a paginated table, you still force the database to construct a dynamic query based in part on user inputs when you pass the OFFSET and LIMIT to it.
Recommended exercise: implement the MySQL wire protocol. It took me a couple hours a few years ago. It's not hard. Do it, come back, and then see if you still think the same things about SQLI defenses.
Prepared statements should afford you a clean conscience because the values never make up part of the SQL query .. unless you are using a library that emulates it, and there are libraries out there that do, so don't assume anything.