From ca4d761059105de620b5af7ddccd29d7f236dd33 Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Mon, 18 Jul 2022 12:52:28 -0400 Subject: [PATCH] implement executemany RETURNING for Oracle 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 | 14 +++++-- lib/sqlalchemy/dialects/oracle/cx_oracle.py | 42 ++++++++++++++------- 2 files changed, 40 insertions(+), 16 deletions(-) diff --git a/doc/build/changelog/unreleased_20/6245.rst b/doc/build/changelog/unreleased_20/6245.rst index 1247544f1c..ddc14ee542 100644 --- a/doc/build/changelog/unreleased_20/6245.rst +++ b/doc/build/changelog/unreleased_20/6245.rst @@ -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:: diff --git a/lib/sqlalchemy/dialects/oracle/cx_oracle.py b/lib/sqlalchemy/dialects/oracle/cx_oracle.py index d2ee0a96ed..d9fb5c8273 100644 --- a/lib/sqlalchemy/dialects/oracle/cx_oracle.py +++ b/lib/sqlalchemy/dialects/oracle/cx_oracle.py @@ -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" -- 2.47.2