Most active commenters
  • Izkata(5)

←back to thread

620 points tambourine_man | 16 comments | | HN request time: 1.931s | source | bottom
Show context
serbuvlad ◴[] No.43750075[source]
All things considered, this is pretty cool. Basically, this replaces

    db.execute("QUERY WHERE name = ?", (name,))
with

    db.execute(t"QUERY WHERE name = {name}")
Does the benefit from this syntactic sugar outweigh the added complexity of a new language feature? I think it does in this case for two reasons:

1. Allowing library developers to do whatever they want with {} expansions is a good thing, and will probably spawn some good uses.

2. Generalizing template syntax across a language, so that all libraries solve this problem in the same way, is probably a good thing.

replies(12): >>43750226 #>>43750250 #>>43750260 #>>43750279 #>>43750513 #>>43750750 #>>43752117 #>>43752173 #>>43752293 #>>43754738 #>>43756560 #>>43763190 #
benwilber0 ◴[] No.43752173[source]
Aren't there other benefits to server-side parameter binding besides just SQL-injection safety? For instance, using PG's extended protocol (binary) instead of just raw SQL strings. Caching parameterized prepared statements, etc.

Also:

    db.execute(t"QUERY WHERE name = {name}")
Is dangerously close to:

    db.execute(f"QUERY WHERE name = {name}")

A single character difference and now you've just made yourself trivially injectible.

I don't think this new format specifier is in any way applicable to SQL queries.

replies(12): >>43752236 #>>43752283 #>>43752331 #>>43752336 #>>43752358 #>>43752859 #>>43753280 #>>43753699 #>>43754372 #>>43754646 #>>43755330 #>>43756720 #
MR4D ◴[] No.43752336[source]
Dang! Thanks for pointing this out.

I had to look SEVERAL times at your comment before I noticed one is an F and the other is a T.

This won’t end well. Although I like it conceptually, this few pixel difference in a letter is going to cause major problems down the road.

replies(1): >>43752552 #
1. pphysch ◴[] No.43752552[source]
How? tstrings and fstrings are literals for completely different types.

CS has survived for decades with 1 and 1.0 being completely different types.

replies(3): >>43753130 #>>43753177 #>>43754544 #
2. Izkata ◴[] No.43753130[source]
Because they're both passed to "execute", which can't tell between the f-string and a non-interpolated query, so it just has to trust you did the right thing. Typoing the "t" as an "f" introduces SQL injection that's hard to spot.
replies(2): >>43753238 #>>43756782 #
3. Certhas ◴[] No.43753177[source]
I had an extended debugging session last week that centered on 1 and 1. confusion in a library I have to use...
replies(1): >>43756918 #
4. vlovich123 ◴[] No.43753238[source]
Assuming `execute` takes both. You could have `execute(template)` and `execute_interpolated(str, ...args)` but yeah if it takes both you'll have challenges discouraging plain-text interpolation.
replies(1): >>43753703 #
5. Izkata ◴[] No.43753703{3}[source]
It would have to be the other way around or be a (possibly major) breaking change. Just execute() with strings is already standard python that all the frameworks build on top of, not to mention tutorials:

https://docs.python.org/3/library/sqlite3.html

https://www.psycopg.org/docs/cursor.html

https://dev.mysql.com/doc/connector-python/en/connector-pyth...

replies(1): >>43769181 #
6. MR4D ◴[] No.43754544[source]
Reread my comment. It’s about noticing you have an “f” or a “t” and both are very similar characters.
replies(1): >>43754644 #
7. rocha ◴[] No.43754644[source]
Yes, but you will get an error since string and templates are different types and have different interfaces.
replies(1): >>43755714 #
8. Izkata ◴[] No.43755714{3}[source]
Click "parent" a few times and look at the code example that started this thread. It's using the same function in a way that can't distinguish whether the user intentionally used a string (including an f-string) and a t-string.
replies(1): >>43756771 #
9. zahlman ◴[] No.43756771{4}[source]
Yes, and the parent is misguided. As was pointed out in multiple replies, the library can distinguish whether an ordinary string or a t-string is passed because the t-string is not a string instance, but instead creates a separate library type. A user who mistakenly uses an f prefix instead of a t prefix will, with a properly designed library, encounter a `TypeError` at runtime (or a warning earlier, given type annotations and a checker), not SQL injection.
replies(1): >>43758123 #
10. zahlman ◴[] No.43756782[source]
`execute` can tell the difference, because `t"..."` does not create the same type of object that `f"..."` does.
11. pphysch ◴[] No.43756918[source]
Yeah, it's a real bummer when that happens. I wish JSON never tried to do types.
12. Izkata ◴[] No.43758123{5}[source]
In this particular instance it can't, because there are 3 ways in question here, and it can't distinguish between correct intentional usage and accidental usage of an f-string instead of a t-string:

  db.execute("SELECT foo FROM bar;")
  db.execute(f"SELECT foo FROM bar WHERE id = {foo_id};")
  db.execute(t"SELECT foo FROM bar WHERE id = {foo_id};")
The first and second look identical to execute() because all it sees is a string. But the second one is wrong, a hard-to-see typo of the third.

If f-strings didn't exist there'd be no issue because it could distinguish by type as you say. But we have an incorrect SQL-injection-prone usage here that can't be distinguished by type from the correct plain string usage.

replies(1): >>43764565 #
13. Timon3 ◴[] No.43764565{6}[source]
There is no reason to support the first or second usage. It's totally fine to always require a t-string:

    db.execute(t"SELECT foo FROM bar;")
See? No reason to accept strings, it's absolutely fine to always error if a string is passed.
replies(1): >>43765327 #
14. Izkata ◴[] No.43765327{7}[source]
My (and their) point is that's the already existing API. You're proposing a big breaking change, with how many frameworks and tutorials are built on top of that.
replies(1): >>43765451 #
15. Timon3 ◴[] No.43765451{8}[source]
It's not like this is the first time APIs have been improved. There are many tools (e.g. deprecation warnings & hints in editors, linter rules) that can help bridge the gap - even if t-strings are only used for new or refactored code, it's still a big improvement!

There's also simply no hard requirement to overload an `execute` function. We have options beyond "no templates at all" and "execute takes templates and strings", for example by introducing a separate function. Why does perfect have to be the enemy of good here?

16. dragonwriter ◴[] No.43769181{4}[source]
> It would have to be the other way around or be a (possibly major) breaking change.

If it is going to reject the currently-accepted unsafe usage, its going to be a major breaking change in any case, so I don't see the problem. I mean, if you are lamenting it can't reject the currently-accepted SQL-interpolated-via-f-string because it can't distinguish it by type from plain strings with no interpolation, you are already saying that you want a major breaking change but are upset because the particular implementation you want is not possible. So you can't turn around and dismiss an alternative solution because it would be a major breaking change, that's what was asked for!