feat(django-google-spanner): support Django 6.0 - #18128
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces support for Django 6.0, including dependency updates, tuple-casting for lookup parameters, an asynchronous autocommit setter, and a Spanner-specific JSON path compiler. The review feedback highlights critical improvements: resolving syntax, type, and SQL injection issues in compile_json_path by utilizing json.dumps; wrapping the async autocommit operation in self.execute_wrapper to align with Django standards; and fixing invalid shell syntax and compatibility issues in the new test suite script.
| def compile_json_path(self, key_transforms, fallback_to_null=False): | ||
| # Spanner specific JSON path formatting | ||
| path = "['%s']" % "']['".join(key_transforms) | ||
| return path |
There was a problem hiding this comment.
The current implementation of compile_json_path has several critical issues:
- Missing
$Prefix: Spanner's JSON path syntax requires the path to start with a$character (e.g.,$.pathor$["path"]). Without it, Spanner will raise anInvalid JSON patherror at runtime. - SQL Injection / Syntax Errors: Single quotes in
key_transformsare not escaped, which can lead to SQL syntax errors or potential SQL injection vulnerabilities. - TypeError on Integer Keys: If
key_transformscontains integer indices (for array lookups),"']['".join(key_transforms)will raise aTypeErrorbecausejoinexpects strings.
Using json.dumps (similar to Django's default implementation) solves all of these issues robustly by correctly prepending $, escaping special characters, and handling both string keys and integer indices.
| def compile_json_path(self, key_transforms, fallback_to_null=False): | |
| # Spanner specific JSON path formatting | |
| path = "['%s']" % "']['".join(key_transforms) | |
| return path | |
| def compile_json_path(self, key_transforms, fallback_to_null=False): | |
| import json | |
| return "$" + "".join(f"[{json.dumps(key)}]" for key in key_transforms) |
| async def _a_set_autocommit(self, autocommit): | ||
| return await sync_to_async(self._set_autocommit)(autocommit) |
There was a problem hiding this comment.
In Django, database operations executed asynchronously should be wrapped in self.execute_wrapper to ensure that any custom connection wrappers, instrumentation, or middleware hooks are correctly executed. Wrapping the call in async with self.execute_wrapper: aligns with Django's standard implementation of _a_set_autocommit.
| async def _a_set_autocommit(self, autocommit): | |
| return await sync_to_async(self._set_autocommit)(autocommit) | |
| async def _a_set_autocommit(self, autocommit): | |
| async with self.execute_wrapper: | |
| return await sync_to_async(self._set_autocommit)(autocommit) |
| #!/bin/sh | ||
|
|
||
| # Copyright (c) 2020 Google LLC. All rights reserved. | ||
| # Use of this source code is governed by a BSD-style | ||
| # license that can be found in the LICENSE file. | ||
|
|
||
| set -x pipefail |
There was a problem hiding this comment.
There are two issues with the shell configuration here:
set -x pipefailis invalid shell syntax. To enable thepipefailoption, you must useset -o pipefail.- The
pipefailoption is abash/zshextension and is not supported by POSIX-compliant/bin/sh(such asdashon Debian/Ubuntu). To guarantee support, the shebang should be changed to#!/bin/bash. - It is highly recommended to enable
set -e(errexit) so that the script exits immediately if any setup command (likepip3 installorgit clone) fails, preventing silent failures in CI.
| #!/bin/sh | |
| # Copyright (c) 2020 Google LLC. All rights reserved. | |
| # Use of this source code is governed by a BSD-style | |
| # license that can be found in the LICENSE file. | |
| set -x pipefail | |
| #!/bin/bash | |
| # Copyright (c) 2020 Google LLC. All rights reserved. | |
| # Use of this source code is governed by a BSD-style | |
| # license that can be found in the LICENSE file. | |
| set -xe | |
| set -o pipefail |
Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly:
Fixes #<issue_number_goes_here> 🦕