Opened 79 minutes ago
#37376 assigned Bug
UUID4() function on Oracle persists data in uppercase hex but queries in lowercase hex
| Reported by: | Jacob Walls | Owned by: | Jacob Walls |
|---|---|---|---|
| Component: | Database layer (models, ORM) | Version: | 6.1 |
| Severity: | Release blocker | Keywords: | UUID |
| Cc: | Lily, Mariusz Felisiak | Triage Stage: | Unreviewed |
| Has patch: | no | Needs documentation: | no |
| Needs tests: | no | Patch needs improvement: | no |
| Easy pickings: | no | UI/UX: | no |
Description
field_defaults.tests.DefaultTests.test_foreign_key_to_parent_with_expression_pk(), fails on Oracle 26ai (i.e. 23.26.2.0):
django.db.utils.IntegrityError: ORA-02291: integrity constraint (DJANGO_TESTS_DEFAULT_22.FIELD_DEF_PARENT_ID_42B58311_F) violated - parent key not found Help: https://docs.oracle.com/error-help/db/ora-02291/
The queries in this test are:
1. INSERT INTO "FIELD_DEFAULTS_DBDEFAULTSF0214" ("UUID") VALUES (UUID()) RETURNING "FIELD_DEFAULTS_DBDEFAULTSF0214"."UUID" INTO <django.db.backends.oracle.utils.BoundVar object at 0xffff7d1da360> 2. INSERT INTO "FIELD_DEFAULTS_DBDEFAULTSF22F2" ("PARENT_ID") VALUES (b41dd9331e764f988fc47e139ef0251d) RETURNING "FIELD_DEFAULTS_DBDEFAULTSF22F2"."ID" INTO <django.db.backends.oracle.utils.BoundVar object at 0xffff7d1da360>
The UUID value returned by query 1 differs in case from the value bound in query 2. Check by skipping Django's deserialization and reading it from a raw cursor:
def test_foreign_key_to_parent_with_expression_pk(self): parent = DBDefaultsFunctionPK(pk=UUID4()) obj = DBDefaultsFunctionFK(parent=parent) parent.save() with connection.cursor() as cursor: cursor.execute('SELECT "UUID" FROM "FIELD_DEFAULTS_DBDEFAULTSF0214"') print(cursor.fetchall()) obj.save() self.assertEqual(obj.parent_id, parent.pk)
[('B41DD9331E764F988FC47E139EF0251D',)]
The Oracle docs show the uppercase form being persisted. My understanding is that the FK fails integrity when a lowercase value is used in the second query, via Django/Python-side normalization, and fails a case-sensitive check on the varchar column in the db.
Bug in accceec9493d08e19d59fa1a59f69c0fdf23bb13.
The test passes after adjusting UUID4.as_oracle() to lowercase like this. (RAWTOHEX makes explicit the implicit conversion done today when inserting raw UUID into varchar columns.)
-
django/db/models/functions/uuid.py
diff --git a/django/db/models/functions/uuid.py b/django/db/models/functions/uuid.py index 7059798ff3..17754a48c1 100644
a b class UUID4(Func): 32 32 raise NotSupportedError( 33 33 "UUID4 requires Oracle version 23ai/26ai (23.9) or later." 34 34 ) 35 return self.as_sql(compiler, connection, function="UUID", **extra_context) 35 return self.as_sql( 36 compiler, 37 connection, 38 function="UUID", 39 template=f"LOWER(RAWTOHEX({self.template}))", 40 **extra_context, 41 ) 36 42 37 43 38 44 class UUID7(Func):
I presume if we go through with this we will need to write a release note advising Oracle users already on Django 6.1 to migrate their data created via UUID4 to lowercase (including foreign keys).