←back to thread

620 points tambourine_man | 4 comments | | HN request time: 0s | source
Show context
TekMol ◴[] No.43749608[source]
Will this allow neat SQL syntax like the following?

    city = 'London'
    min_age = 21
    # Find all users in London who are 21 or older:
    users = db.get(t'
        SELECT * FROM users
        WHERE city={city} AND age>{min_age}
    ')
If the db.get() function accepts a template, it should, right?

This would be the nicest way to use SQL I have seen yet.

replies(8): >>43749674 #>>43749734 #>>43749906 #>>43749926 #>>43749979 #>>43750037 #>>43751845 #>>43756963 #
jbaiter ◴[] No.43749674[source]
Thanks, I hate it. While it's nice syntactic sugar, the difference between an SQL injection vulnerability and a properly parametrized query is now a single letter that's easily missed
replies(5): >>43749680 #>>43749683 #>>43749690 #>>43749804 #>>43750217 #
JimDabell ◴[] No.43749804[source]
The t-string produces a Template object without a __str__() method. You can’t mistakenly use an f-string in its place. Either the code expects a string, in which case passing it a Template would blow it up, or the code expects a Template, in which case passing it a string would blow it up.
replies(5): >>43749994 #>>43750148 #>>43750347 #>>43751082 #>>43752420 #
crazygringo ◴[] No.43752420[source]
> or the code expects a Template, in which case passing it a string would blow it up.

That's where the problem is though -- in most cases it probably won't blow up.

Plenty of SQL queries don't have any parameters at all. You're just getting the number of rows in a table or something. A raw string is perfectly fine.

Will sqlite3 really disallow strings? Will it force you to use templates, even when the template doesn't contain any parameters?

You can argue it should, but that's not being very friendly with inputs, and will break backwards compatibility. Maybe if there's a flag you can set in the module to enable that strict behavior though, with the idea that in a decade it will become the default?

replies(1): >>43753002 #
WorldMaker ◴[] No.43753002[source]

    db.execute(t"Select Count(1) from someTable")
It's one extra letter to "force" for an unparameterized query over a "raw string". The t-string itself works just fine without parameters.

There's definitely a backwards compatibility hurdle of switching to a template-only API, but a template-only API doesn't look that much "less friendly" with inputs, when the only difference is a `t` before every string, regardless of number of parameters.

replies(1): >>43753116 #
crazygringo ◴[] No.43753116[source]
Sure, but it's just I don't have to do that anywhere else.

I never put an f in front of a string if I'm not putting variables within it.

And I'm generally used to Python inputs being liberal. I can usually pass a list if it expects a tuple; I can pass an int if it expects a float; often I can pass an item directly instead of a tuple with a single item. Regex functions take regular strings or regex strings, they don't force regex strings.

Being forced to use a single specific type of string in all cases is just very different from how Python has traditionally operated.

It's safer, I get that. But it's definitely less friendly, so I'll be curious to see how module maintainers decide to handle this.

replies(3): >>43753580 #>>43753590 #>>43756570 #
0cf8612b2e1e ◴[] No.43753590{4}[source]

  I never put an f in front of a string if I'm not putting variables within it.
Linters will even complain if you have a f string without variables. I assume it will be the same for t strings.
replies(2): >>43754334 #>>43754729 #
davepeck ◴[] No.43754334{5}[source]
For the reasons discussed above, I'm not sure that it will be the case for t-strings. I think it'll take a little while for frameworks/libraries to adapt (while still maintaining backward compatibility) and a while for best practices to find their way into our linting and other tools.
replies(1): >>43756057 #
0cf8612b2e1e ◴[] No.43756057{6}[source]
If you can use a string anywhere you can use a t-string, then a non parametrized t-string is a code smell (lining error). If there is a dedicated template-string API, then there is the implicit threat you are breaking backwards compatibility to stop using regular strings.
replies(1): >>43756219 #
1. davepeck ◴[] No.43756219{7}[source]
> If you can use a string anywhere you can use a t-string

You can't; they're different types. t-strings are not `str`

It's up to good framework/API design to take advantage of this.

replies(2): >>43756583 #>>43756834 #
2. WorldMaker ◴[] No.43756583[source]
Yeah, "t-string" is possibly a misnomer, because they are in fact at runtime a Template object (from string.templatelib).
3. 0cf8612b2e1e ◴[] No.43756834[source]
A library writer ultimately has to decide if they accept both types. For a database cursor, do you take regular strings + parameter arguments and template strings? Or dedicate a new API to the idea?

  cursor.execute(“select * from x where foo=?”, {foo=1})
  # while also allowing
  cursor.execute(t“select * from x where foo={foo}”)
  #Vs 
  cursor.executetemplate(“select * from x where foo={foo}”)
If ‘execute’ takes string and t-string, then I would consider it a problem to use a t-string without parameters. If there is a novel API just for t-strings, then you are implying widespread breaking changes as you have a schism between the two ways of providing parameters.
replies(1): >>43757595 #
4. davepeck ◴[] No.43757595[source]
My point was that library authors will need to consider this carefully. If you're writing a library where injection attacks matter, then -- long term -- you almost certainly do not want a single method that accepts `Union[str, Template]`. You probably either want to avoid accepting `str` entirely, or perhaps provide two separate methods. Some period of deprecation seems inevitable.