Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #10473: Added Oracle support for "RETURNING" ids from insert st…

…atements.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@10044 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit c3dc837950966013ca5a4a235e85ed85595f7161 1 parent 6d17020
Ian Kelly authored March 12, 2009
16  django/db/backends/__init__.py
@@ -162,6 +162,14 @@ def drop_sequence_sql(self, table):
162 162
         """
163 163
         return None
164 164
 
  165
+    def fetch_returned_insert_id(self, cursor):
  166
+        """
  167
+        Given a cursor object that has just performed an INSERT...RETURNING
  168
+        statement into a table that has an auto-incrementing ID, returns the
  169
+        newly created ID.
  170
+        """
  171
+        return cursor.fetchone()[0]
  172
+
165 173
     def field_cast_sql(self, db_type):
166 174
         """
167 175
         Given a column type (e.g. 'BLOB', 'VARCHAR'), returns the SQL necessary
@@ -249,10 +257,10 @@ def pk_default_value(self):
249 257
 
250 258
     def return_insert_id(self):
251 259
         """
252  
-        For backends that support returning the last insert ID as part of an
253  
-        insert query, this method returns the SQL to append to the INSERT
254  
-        query. The returned fragment should contain a format string to hold
255  
-        hold the appropriate column.
  260
+        For backends that support returning the last insert ID as part
  261
+        of an insert query, this method returns the SQL and params to
  262
+        append to the INSERT query. The returned fragment should
  263
+        contain a format string to hold the appropriate column.
256 264
         """
257 265
         pass
258 266
 
29  django/db/backends/oracle/base.py
@@ -37,6 +37,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
37 37
     uses_custom_query_class = True
38 38
     interprets_empty_strings_as_nulls = True
39 39
     uses_savepoints = True
  40
+    can_return_id_from_insert = True
40 41
 
41 42
 
42 43
 class DatabaseOperations(BaseDatabaseOperations):
@@ -97,6 +98,9 @@ def deferrable_sql(self):
97 98
     def drop_sequence_sql(self, table):
98 99
         return "DROP SEQUENCE %s;" % self.quote_name(get_sequence_name(table))
99 100
 
  101
+    def fetch_returned_insert_id(self, cursor):
  102
+        return long(cursor._insert_id_var.getvalue())
  103
+
100 104
     def field_cast_sql(self, db_type):
101 105
         if db_type and db_type.endswith('LOB'):
102 106
             return "DBMS_LOB.SUBSTR(%s)"
@@ -152,6 +156,9 @@ def regex_lookup(self, lookup_type):
152 156
         connection.cursor()
153 157
         return connection.ops.regex_lookup(lookup_type)
154 158
 
  159
+    def return_insert_id(self):
  160
+        return "RETURNING %s INTO %%s", (InsertIdVar(),)
  161
+
155 162
     def savepoint_create_sql(self, sid):
156 163
         return "SAVEPOINT " + self.quote_name(sid)
157 164
 
@@ -332,8 +339,11 @@ class OracleParam(object):
332 339
     parameter when executing the query.
333 340
     """
334 341
 
335  
-    def __init__(self, param, charset, strings_only=False):
336  
-        self.smart_str = smart_str(param, charset, strings_only)
  342
+    def __init__(self, param, cursor, strings_only=False):
  343
+        if hasattr(param, 'bind_parameter'):
  344
+            self.smart_str = param.bind_parameter(cursor)
  345
+        else:
  346
+            self.smart_str = smart_str(param, cursor.charset, strings_only)
337 347
         if hasattr(param, 'input_size'):
338 348
             # If parameter has `input_size` attribute, use that.
339 349
             self.input_size = param.input_size
@@ -344,6 +354,19 @@ def __init__(self, param, charset, strings_only=False):
344 354
             self.input_size = None
345 355
 
346 356
 
  357
+class InsertIdVar(object):
  358
+    """
  359
+    A late-binding cursor variable that can be passed to Cursor.execute
  360
+    as a parameter, in order to receive the id of the row created by an
  361
+    insert statement.
  362
+    """
  363
+
  364
+    def bind_parameter(self, cursor):
  365
+        param = cursor.var(Database.NUMBER)
  366
+        cursor._insert_id_var = param
  367
+        return param
  368
+
  369
+
347 370
 class FormatStylePlaceholderCursor(object):
348 371
     """
349 372
     Django uses "format" (e.g. '%s') style placeholders, but Oracle uses ":var"
@@ -363,7 +386,7 @@ def __init__(self, connection):
363 386
         self.cursor.arraysize = 100
364 387
 
365 388
     def _format_params(self, params):
366  
-        return tuple([OracleParam(p, self.charset, True) for p in params])
  389
+        return tuple([OracleParam(p, self, True) for p in params])
367 390
 
368 391
     def _guess_input_sizes(self, params_list):
369 392
         sizes = [None] * len(params_list[0])
2  django/db/backends/postgresql_psycopg2/base.py
@@ -39,7 +39,7 @@ def last_executed_query(self, cursor, sql, params):
39 39
         return cursor.query
40 40
 
41 41
     def return_insert_id(self):
42  
-        return "RETURNING %s"
  42
+        return "RETURNING %s", ()
43 43
 
44 44
 class DatabaseWrapper(BaseDatabaseWrapper):
45 45
     operators = {
9  django/db/models/sql/subqueries.py
@@ -306,17 +306,20 @@ def as_sql(self):
306 306
         result = ['INSERT INTO %s' % qn(opts.db_table)]
307 307
         result.append('(%s)' % ', '.join([qn(c) for c in self.columns]))
308 308
         result.append('VALUES (%s)' % ', '.join(self.values))
  309
+        params = self.params
309 310
         if self.connection.features.can_return_id_from_insert:
310 311
             col = "%s.%s" % (qn(opts.db_table), qn(opts.pk.column))
311  
-            result.append(self.connection.ops.return_insert_id() % col)
312  
-        return ' '.join(result), self.params
  312
+            r_fmt, r_params = self.connection.ops.return_insert_id()
  313
+            result.append(r_fmt % col)
  314
+            params = params + r_params
  315
+        return ' '.join(result), params
313 316
 
314 317
     def execute_sql(self, return_id=False):
315 318
         cursor = super(InsertQuery, self).execute_sql(None)
316 319
         if not (return_id and cursor):
317 320
             return
318 321
         if self.connection.features.can_return_id_from_insert:
319  
-            return cursor.fetchone()[0]
  322
+            return self.connection.ops.fetch_returned_insert_id(cursor)
320 323
         return self.connection.ops.last_insert_id(cursor,
321 324
                 self.model._meta.db_table, self.model._meta.pk.column)
322 325
 

0 notes on commit c3dc837

Please sign in to comment.
Something went wrong with that request. Please try again.