Fixed #36795 -- Enforced quoting of all database object names.#20587
Fixed #36795 -- Enforced quoting of all database object names.#20587charettes wants to merge 2 commits intodjango:mainfrom
Conversation
|
This is promising. So from my Git archeology session it seems that the behaviour was introduced to support backends that care about the case of aliases such as Postgres. If we are to move forward with this patch it means that mixing Running tests on Oracle now to see if this can help with ticket-20226 |
|
The Oracle failures were very telling and explain ticket-20226. As Shai pointed out on the ticket we'd have to break backward compatiblity to change that so this ticket will continue to focus solely on the quoting aspect. It's kind of weird that we decided to default to always upper casing on Oracle to deal with SQL92 delimited (quoted) names handling of case while we decided not to do the same thing on Postgres which also follows the same standard. |
charettes
left a comment
There was a problem hiding this comment.
@jacobtylerwalls let me know what you think of the approach so far. The commit history obvisouly needs some rework.
We'll want to keep disallowing some characters in users provided aliases until we adjust the quote_alias method of each backend to transform them (e.g. handle " as """, strip \x00) according to their respective docs) but the current patch should at least provide a fondation to ensure we don't have ever skip calling quote_name on aliases.
jacobtylerwalls
left a comment
There was a problem hiding this comment.
This sparks a lot of joy. Makes total sense for a first step.
My understanding is that the rest of the plan is:
- make
quote_name_unless_alias()a no-op forconnection.ops.quote_name()and deprecate it - document the need to possibly adjust casing of RawSQL expressions
- [in follow ups:] begin undoing
FORBIDDEN_ALIAS_PATTERNby auditing thequote_name()methods for each backend to see what needs special handling
Do you anticipate any issues with these .lower() comparisons?
django/django/db/models/expressions.py
Lines 1254 to 1265 in 2831eae
| return r | ||
| if (quoted := self.quote_cache.get(name)) is not None: | ||
| return quoted | ||
| quoted = self.connection.ops.quote_name(name) |
There was a problem hiding this comment.
I bet we could just make this a @cached_property now, and still seed it with * up front.
There was a problem hiding this comment.
I could be missing someting but the since we cache is a value of the name I don't think a cached_property would work here?
There was a problem hiding this comment.
Right, I guess it would have to be @functools.cache. Seemed like we're maintaining our own version of that. But it's not something we have to do now.
Yep that would be the plan, I'll spend some time re-organizing the commits to do the following
Adjusting Hashing the moment a disallowed character is provided is safe even if it results in a different name as long as it's deterministic. We don't have to worry about whether it's a user provided alias or not as the moment it includes disallowed characters it means an object could not have been created with this name in the first place and thus it must be a user provided alias.
django/django/db/models/expressions.py Lines 1254 to 1265 in 2831eae Not that I can think of as even if the name is quoted it will stil be idenifiable through a containment check. In other words
|
This ensures all database identifiers are quoted independently of their orign and most importantly that user provided aliases through annotate() and alias() which paves the way for dropping the allow list of characters such aliases can contain. This will require adjustments to raw SQL interfaces such as RawSQL that might make reference to ORM managed annotations as these will now be quoted. The `SQLCompiler.quote_name_unless_alias` method is kept for now as an alias for the newly introduced `.quote_name` method but will be duly deprecated in a follow up commit.
…iases feature flag. Now that user provided aliases are systematically quoted there is no need to disallow the usage of the dollar sign on Postgres.
acaa3f6 to
aff2860
Compare
📊 Coverage Report for Changed FilesNote: Missing lines are warnings only. Some lines may not be covered by SQLite tests as they are database-specific. For more information about code coverage on pull requests, see the contributing documentation. |
To flesh out my thought, based on your hint I was anticipating that a user would need to change: .annotate(bAr=...).filter(foo=RawSQL("bar = %s", (...)))to: .annotate(bAr=...).filter(foo=RawSQL("bAr = %s", (...)))And then we would hit this line, where (Pdb) self.sql
'bAr = %s'But still maps to the same # sortorder is a field on parent model
# Raw query on the proxy model using a uniquely cased alias SORTORDER should not cause parent field resolving?
>>> Proxy.objects.annotate(SORTORDER=F("pk")).annotate(foo=RawSQL("SORTORDER = %s", (1,))).query
# str()
SELECT ..., "tiles"."tileid" AS "SORTORDER", (SORTORDER = 1) AS "foo" FROM "tiles"This still resolves |
Trac ticket number
ticket-36795
Branch description
Currenly trying to learn more about why we started not quoting users probided aliases as 9c52d56 which introduced the change has very little details about its origin.
AI Assistance Disclosure (REQUIRED)