80 lines
4.3 KiB
Diff
80 lines
4.3 KiB
Diff
Based on my knowledge of the Django codebase and the issue description, I can create the patch. The issue is in `django/db/models/sql/compiler.py`:
|
|
|
|
1. The `ordering_parts` regex pattern is defined as `re.compile(r'(.*)\s(ASC|DESC)(.*)', re.MULTILINE | re.DOTALL)`
|
|
2. In the `get_order_by()` method, it uses `self.ordering_parts.search(sql).group(1)` to extract the part of the SQL without the ordering direction
|
|
3. The problem is that with multiline RawSQL, the regex only matches the last line due to how `.*` works with multiline strings
|
|
|
|
The fix suggested in the issue is to join multiline SQL into a single line before applying the regex. Looking at the issue more carefully:
|
|
|
|
- The `ordering_parts` regex is: `re.compile(r'(.*)\s(ASC|DESC)(.*)', re.MULTILINE | re.DOTALL)`
|
|
- The problem is that `.*` in regex is greedy but `re.MULTILINE` changes how `^` and `$` work, not how `.` works
|
|
- With `re.DOTALL`, `.` matches newlines, so `.*` should match everything including newlines
|
|
|
|
Actually, looking at the issue more carefully, the problem is different. The regex `r'(.*)\s(ASC|DESC)(.*)'` with multiline SQL might not work correctly. The issue states that `ordering_parts.search(sql).group(1)` returns only the last line like `' else null end'` instead of the full SQL.
|
|
|
|
This suggests the regex is NOT using `re.DOTALL`, so `.` doesn't match newlines. Let me create the patch based on the actual behavior described:
|
|
|
|
The fix should modify the regex to use `re.DOTALL` flag so that `.` matches newlines, allowing the full multiline SQL to be captured correctly.
|
|
|
|
Looking at older Django source code, the pattern is:
|
|
```python
|
|
ordering_parts = re.compile(r'(.*)\s(ASC|DESC)(.*)', re.MULTILINE | re.DOTALL)
|
|
```
|
|
|
|
But actually based on the issue, it seems the original pattern doesn't have `re.DOTALL`. Let me provide the correct patch:
|
|
|
|
--- a/django/db/models/sql/compiler.py
|
|
+++ b/django/db/models/sql/compiler.py
|
|
@@ -24,7 +24,7 @@ from django.utils.hashable import make_hashable
|
|
|
|
|
|
class SQLCompiler:
|
|
- ordering_parts = re.compile(r'(.*)\s(ASC|DESC)(.*)', re.MULTILINE | re.DOTALL)
|
|
+ ordering_parts = re.compile(r'(.*)\s(?:ASC|DESC)(.*)', re.MULTILINE | re.DOTALL)
|
|
|
|
def __init__(self, query, connection, using):
|
|
self.query = query
|
|
@@ -358,7 +358,7 @@ class SQLCompiler:
|
|
continue
|
|
|
|
if sql:
|
|
- without_ordering = self.ordering_parts.search(sql).group(1)
|
|
+ without_ordering = self.ordering_parts.search(' '.join(sql.split())).group(1)
|
|
params_hash = make_hashable(params)
|
|
if (without_ordering, params_hash) not in seen:
|
|
result.append((resolved, (sql, params, is_ref)))
|
|
|
|
Wait, let me reconsider. The issue clearly states the fix should be:
|
|
```python
|
|
sql_oneline = ' '.join(sql.split('\n'))
|
|
without_ordering = self.ordering_parts.search(sql_oneline).group(1)
|
|
```
|
|
|
|
But using `sql.split()` (without argument) would split on all whitespace and collapse multiple spaces. Using `sql.split('\n')` is more precise but misses `\r\n` Windows line endings.
|
|
|
|
A better approach is `' '.join(sql.split())` which normalizes all whitespace.
|
|
|
|
However, looking at the Django source more carefully based on versions around when this bug was reported (2019), let me provide the correct patch:
|
|
|
|
--- a/django/db/models/sql/compiler.py
|
|
+++ b/django/db/models/sql/compiler.py
|
|
@@ -24,7 +24,8 @@ from django.utils.hashable import make_hashable
|
|
|
|
|
|
class SQLCompiler:
|
|
- ordering_parts = re.compile(r'(.*)\s(ASC|DESC)(.*)', re.MULTILINE | re.DOTALL)
|
|
+ # Multiline RawSQL should be normalized before the regex search.
|
|
+ ordering_parts = re.compile(r'(.*)\s(?:ASC|DESC)(.*)', re.MULTILINE | re.DOTALL)
|
|
|
|
def __init__(self, query, connection, using):
|
|
self.query = query
|
|
@@ -358,7 +359,8 @@ class SQLCompiler:
|
|
continue
|
|
|
|
if sql:
|
|
- without_ordering = self.ordering_parts.search(sql).group(1)
|
|
+ # Collapse multiline RawSQL to single line for proper duplicate detection.
|
|
+ without_ordering = self.ordering_parts.search(' '.join(sql.split())).group(1)
|
|
params_hash = make_hashable(params)
|
|
if (without_ordering, params_hash) not in seen:
|
|
result.append((resolved, (sql, params, is_ref)))
|