Sometimes you have to concatenate, e.g. to specify order by clauses. This is something database vendors need to fix. There need to be ways to tell the database to modify a prepared statement without changing it's SQL. You might be able to do it with a crazy IIF, but then it's likely not to use indexes well.
I have hit this problem in just about any system using prepared statements. The performance overhead of query planning is minimal compared to the headaches it prevents.
> Sometimes you have to concatenate, e.g. to specify order by clauses.
So, should I concatenate or not?
> This is something database vendors need to fix.
How long do you propose I wait before giving up and using a library that allows me to safely and sanely compose queries?
deleted_at TIMESTAMP
created_at TIMESTAMP
user_id INTEGER
body TEXT
all _optionally_ filterable (greater than, less than, like, equal, etc.) where applicable without an ORM, without string concatenation?That you view the situation as ORM or concatenation terrifies me. No wonder SQL injection remains so prevalent.
Prepared statements are what you use here. You provide a query with placeholders for the values you want to fill in, and then you call that prepared statement with the arguments you need. These are typically type-checked and hardened against SQL injection, but of course you should verify with your particular language/library combination.
With an ORM you can construct the prepared statements programmatically.
You're still working with SQL as a language, but you're manipulating it directly instead of trying to go via string formatting.
A quick search for "query builder golang" (We are on HN after all) provides the following library:
https://github.com/fragmenta/query
An example from the README:
// In your handlers, construct queries and ask your models for the data
// Find a simple model by id
page, err := pages.Find(1)
// Start querying the database using chained finders
q := page.Query().Where("id IN (?,?)",4,5).Order("id desc").Limit(30)
// Build up chains depending on other app logic, still no db requests
if shouldRestrict {
q.Where("id > ?",3).OrWhere("keywords ~* ?","Page")
}Absolutely, prepared statements would not be appropriate for that.
> With an ORM you can construct the prepared statements programmatically.
That's not limited to ORMs though. You can also just use a query builder. In fact I would argue that an ORM that provides that functionality is a query builder with additional ORM features on top.
You can use functions to create basic "getters" for application data structures. Layering these through further functions for limits/offsets/etc is simple (and you can write those layers in a generic way).
You just have to get used to the mental mindshift that all your data manipulation code lives in the database, and your application simply consumes data from single function calls.
I don't think others mean concatenation of values where placeholders should be, but the fact that the query itself is a big string. Unless some sort of query builder is used to literally build this big string for you.
They explicitly contrast using an ORM to the practice of concatenating strings here:
> > SQL is backward to a programming language. Creating statements by concatenating strings?
> Unless some sort of query builder is used to literally build this big string for you.
I'm not entirely sure they're aware of the existence of query builders outside of an ORM given the dichotomy presented.
What would be great to have first class support in the language (or perhaps IDE) that would understand database schema and database types so you could have error highlighting and autocompletion.
Something like JOOQ for Java seems to do that. Seems like a good idea at the surface, though I don't currently program in Java and don't see anything similar in other languages, so I'm wondering what's wrong with it