]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
implement executemany RETURNING for Oracle
authorMike Bayer <mike_mp@zzzcomputing.com>
Mon, 18 Jul 2022 16:52:28 +0000 (12:52 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 18 Jul 2022 17:00:56 +0000 (13:00 -0400)
this works straight out of the box as we can expand
upon what we did for #6245 to also receive for multiple
statements.    Oracle "fast ORM insert" then is basically done.

Fixes: #6245
Change-Id: I32902d199d473bc38cd03d14fec7482e1b37cd5b

doc/build/changelog/unreleased_20/6245.rst
lib/sqlalchemy/dialects/oracle/cx_oracle.py

index 1247544f1cebd93980f3f499f9fe3bd2baad93bb..ddc14ee542f5ceecd1f23c41df5b9ea3e057ae78 100644 (file)
@@ -2,9 +2,17 @@
     :tags: oracle, feature
     :tickets: 6245
 
-    Full "RETURNING" support is implemented for the cx_Oracle dialect, meaning
-    multiple RETURNING rows are now recived for DML statements that produce
-    more than one row for RETURNING.
+    Full "RETURNING" support is implemented for the cx_Oracle dialect, covering
+    two individual types of functionality:
+
+    * multi-row RETURNING is implemented, meaning multiple RETURNING rows are
+      now received for DML statements that produce more than one row for
+      RETURNING.
+    * "executemany RETURNING" is also implemented - this allows RETURNING to
+      yield row-per statement when ``cursor.executemany()`` is used.
+      The implementation of this part of the feature delivers dramatic
+      performance improvements to ORM inserts, in the same way as was
+      added for psycopg2 in the SQLAlchemy 1.4 change :ref:`change_5263`.
 
 
 .. change::
index d2ee0a96ed7bbde62aa6f0a7884fa8890f8a1ee8..d9fb5c8273f8ccbf4a21c82024a8a1d3733a7bca 100644 (file)
@@ -718,6 +718,8 @@ class OracleExecutionContext_cx_oracle(OracleExecutionContext):
             out_parameters = self.out_parameters
             assert out_parameters is not None
 
+            len_params = len(self.parameters)
+
             quoted_bind_names = self.compiled.escaped_bind_names
             for bindparam in self.compiled.binds.values():
                 if bindparam.isoutparam:
@@ -726,7 +728,7 @@ class OracleExecutionContext_cx_oracle(OracleExecutionContext):
 
                     if hasattr(type_impl, "_cx_oracle_var"):
                         out_parameters[name] = type_impl._cx_oracle_var(
-                            self.dialect, self.cursor, arraysize=1
+                            self.dialect, self.cursor, arraysize=len_params
                         )
                     else:
                         dbtype = type_impl.get_dbapi_type(self.dialect.dbapi)
@@ -765,15 +767,17 @@ class OracleExecutionContext_cx_oracle(OracleExecutionContext):
                             out_parameters[name] = self.cursor.var(
                                 dbtype,
                                 outconverter=lambda value: value.read(),
-                                arraysize=1,
+                                arraysize=len_params,
                             )
                         else:
                             out_parameters[name] = self.cursor.var(
-                                dbtype, arraysize=1
+                                dbtype, arraysize=len_params
                             )
-                    self.parameters[0][
-                        quoted_bind_names.get(name, name)
-                    ] = out_parameters[name]
+
+                    for param in self.parameters:
+                        param[
+                            quoted_bind_names.get(name, name)
+                        ] = out_parameters[name]
 
     def _generate_cursor_outputtype_handler(self):
         output_handlers = {}
@@ -833,16 +837,26 @@ class OracleExecutionContext_cx_oracle(OracleExecutionContext):
             # get_out_parameter_values(), the result-row handlers here will be
             # applied at the Result level
 
-            numrows = len(self.out_parameters["ret_0"].values[0])
             numcols = len(self.out_parameters)
 
-            initial_buffer = [
-                tuple(
-                    self.out_parameters[f"ret_{j}"].values[0][i]
-                    for j in range(numcols)
+            # [stmt_result for stmt_result in outparam.values] == each
+            # statement in executemany
+            # [val for val in stmt_result] == each row for a particular
+            # statement
+            initial_buffer = list(
+                zip(
+                    *[
+                        [
+                            val
+                            for stmt_result in self.out_parameters[
+                                f"ret_{j}"
+                            ].values
+                            for val in stmt_result
+                        ]
+                        for j in range(numcols)
+                    ]
                 )
-                for i in range(numrows)
-            ]
+            )
 
             fetch_strategy = _cursor.FullyBufferedCursorFetchStrategy(
                 self.cursor,
@@ -882,6 +896,8 @@ class OracleDialect_cx_oracle(OracleDialect):
     supports_sane_rowcount = True
     supports_sane_multi_rowcount = True
 
+    insert_executemany_returning = True
+
     bind_typing = interfaces.BindTyping.SETINPUTSIZES
 
     driver = "cx_oracle"