diff --git a/ASYNC_APIs_WIKI.md b/ASYNC_APIs_WIKI.md
new file mode 100644
index 00000000..6090d5e9
--- /dev/null
+++ b/ASYNC_APIs_WIKI.md
@@ -0,0 +1,1136 @@
+# Async (asyncio) APIs for ibm_db_dbi
+
+The `ibm_db_dbi` module provides full `asyncio` support through `AsyncConnection`, `AsyncCursor`, and module-level async functions. All async operations use `asyncio.to_thread()` internally, making the synchronous IBM CLI calls non-blocking in an async event loop.
+
+> **Note:** All examples use `await` at the top level for brevity. In a script, wrap your code in `async def main()` and call `asyncio.run(main())`. For interactive testing, use `python -m asyncio` to start the asyncio REPL, which supports top-level `await`. The regular `python` REPL does not support `await` outside a function.
+
+## Table of Contents
+
+- [Module-Level Async Functions](#module-level-async-functions)
+ - [connect_async](#connect_async)
+ - [pconnect_async](#pconnect_async)
+ - [conn_errormsg_async](#conn_errormsg_async)
+ - [conn_error_async](#conn_error_async)
+ - [get_sqlcode_async](#get_sqlcode_async)
+ - [createdb_async](#createdb_async)
+ - [dropdb_async](#dropdb_async)
+ - [recreatedb_async](#recreatedb_async)
+ - [createdbNX_async](#createdbnx_async)
+- [AsyncConnection](#asyncconnection)
+ - [AsyncConnection.connect](#asyncconnectionconnect)
+ - [AsyncConnection.close](#asyncconnectionclose)
+ - [AsyncConnection.commit](#asyncconnectioncommit)
+ - [AsyncConnection.rollback](#asyncconnectionrollback)
+ - [AsyncConnection.cursor](#asyncconnectioncursor)
+ - [AsyncConnection.set_autocommit](#asyncconnectionset_autocommit)
+ - [AsyncConnection.set_option / get_option](#asyncconnectionset_option--get_option)
+ - [AsyncConnection.set_current_schema / get_current_schema](#asyncconnectionset_current_schema--get_current_schema)
+ - [AsyncConnection.server_info](#asyncconnectionserver_info)
+ - [AsyncConnection.set_fix_return_type](#asyncconnectionset_fix_return_type)
+ - [AsyncConnection metadata methods](#asyncconnection-metadata-methods)
+ - [AsyncConnection context manager](#asyncconnection-context-manager)
+- [AsyncCursor](#asynccursor)
+ - [AsyncCursor.execute](#asynccursorexecute)
+ - [AsyncCursor.executemany](#asynccursorexecutemany)
+ - [AsyncCursor.prepare](#asynccursorprepare)
+ - [AsyncCursor.bind_param](#asynccursorbind_param)
+ - [AsyncCursor.fetchone](#asynccursorfetchone)
+ - [AsyncCursor.fetchmany](#asynccursorfetchmany)
+ - [AsyncCursor.fetchall](#asynccursorfetchall)
+ - [AsyncCursor.fetch_tuple](#asynccursorfetch_tuple)
+ - [AsyncCursor.callproc](#asynccursorcallproc)
+ - [AsyncCursor.fetch_callproc](#asynccursorfetch_callproc)
+ - [AsyncCursor.nextset](#asynccursornextset)
+ - [AsyncCursor.close](#asynccursorclose)
+ - [AsyncCursor.stmt_errormsg / stmt_error](#asynccursorstmt_errormsg--stmt_error)
+ - [AsyncCursor.description / rowcount](#asynccursordescription--rowcount)
+ - [AsyncCursor context manager](#asynccursor-context-manager)
+- [Stored Procedure Patterns](#stored-procedure-patterns)
+- [Concurrent Queries with asyncio.gather](#concurrent-queries-with-asynciogather)
+
+## Module-Level Async Functions
+
+### connect_async
+
+`Connection await ibm_db_dbi.connect_async(string dsn, string user, string password [, string host [, string database [, dict conn_options]]])`
+
+**Description**
+
+Async wrapper around `ibm_db_dbi.connect()`. Creates a connection asynchronously using `asyncio.to_thread()` and returns a **sync** `Connection` object.
+
+> **Note:** Use `AsyncConnection.connect()` instead if you want a fully async connection and cursor lifecycle.
+
+**Parameters**
+
+- `dsn` - A connection string (e.g., `"DATABASE=sample;HOSTNAME=host;PORT=50000;PROTOCOL=TCPIP"`)
+- `user` - The username for authentication
+- `password` - The password for authentication
+- `host` - (optional) Hostname
+- `database` - (optional) Database name
+- `conn_options` - (optional) A dict of connection options
+
+> For full details on connection parameters, DSN formats, JWT access tokens, and connection options (`SQL_ATTR_*`), refer to [ibm_db.connect](https://github.com/ibmdb/python-ibmdb/wiki/APIs#ibm_dbconnect).
+
+**Return Values**
+
+- On success, a sync `Connection` object
+- On failure, raises an exception
+
+**Example**
+
+```python
+import asyncio
+import ibm_db_dbi
+
+async def main():
+ dsn = "DATABASE=sample;HOSTNAME=host.example.com;PORT=50000;PROTOCOL=TCPIP"
+ conn = await ibm_db_dbi.connect_async(dsn, "user", "password")
+ # conn is a sync Connection object
+ cursor = conn.cursor()
+ cursor.execute("SELECT 1 FROM SYSIBM.SYSDUMMY1")
+ print(cursor.fetchone())
+ conn.close()
+
+asyncio.run(main())
+```
+
+### pconnect_async
+
+`Connection await ibm_db_dbi.pconnect_async(string dsn, string user, string password [, string host [, string database [, dict conn_options]]])`
+
+**Description**
+
+Async wrapper around `ibm_db_dbi.pconnect()`. Returns a persistent **sync** `Connection`. Persistent connections are reused from a process-wide connection pool when `ibm_db.close()` is called.
+
+**Parameters**
+
+Same as [`connect_async`](#connect_async). For full details on connection parameters, refer to [ibm_db.pconnect](https://github.com/ibmdb/python-ibmdb/wiki/APIs#ibm_dbpconnect).
+
+**Return Values**
+
+- On success, a sync `Connection` object (persistent)
+- On failure, raises an exception
+
+**Example**
+
+```python
+import asyncio
+import ibm_db_dbi
+
+async def main():
+ dsn = "DATABASE=sample;HOSTNAME=host.example.com;PORT=50000;PROTOCOL=TCPIP"
+ conn = await ibm_db_dbi.pconnect_async(dsn, "user", "password")
+ cursor = conn.cursor()
+ cursor.execute("SELECT COUNT(*) FROM STAFF")
+ print(cursor.fetchone())
+ conn.close() # returns to pool, not actually closed
+
+asyncio.run(main())
+```
+
+### conn_errormsg_async
+
+`string await ibm_db_dbi.conn_errormsg_async([Connection connection])`
+
+**Description**
+
+Async wrapper around `ibm_db.conn_errormsg()`. Returns the SQLCODE and error message for the last failed connection operation.
+
+**Parameters**
+
+- `connection` - (optional) A valid connection handler
+
+**Return Values**
+
+Returns a string containing the error message or an empty string if there was no error.
+
+**Example**
+
+```python
+import asyncio
+import ibm_db_dbi
+
+async def main():
+ try:
+ conn = await ibm_db_dbi.connect_async(
+ "DATABASE=sample;HOSTNAME=host;PORT=50000;PROTOCOL=TCPIP",
+ "invalid_user", "invalid_pwd")
+ except Exception:
+ msg = await ibm_db_dbi.conn_errormsg_async()
+ print("Connection error:", msg)
+
+asyncio.run(main())
+```
+
+### conn_error_async
+
+`string await ibm_db_dbi.conn_error_async([Connection connection])`
+
+**Description**
+
+Async wrapper around `ibm_db.conn_error()`. Returns the SQLSTATE (5-character string) for the last failed connection operation.
+
+**Parameters**
+
+- `connection` - (optional) A valid connection handler
+
+**Return Values**
+
+Returns a string containing the SQLSTATE or an empty string if there was no error.
+
+**Example**
+
+```python
+import asyncio
+import ibm_db_dbi
+
+async def main():
+ try:
+ conn = await ibm_db_dbi.connect_async(
+ "DATABASE=sample;HOSTNAME=host;PORT=50000;PROTOCOL=TCPIP",
+ "invalid_user", "invalid_pwd")
+ except Exception:
+ sqlstate = await ibm_db_dbi.conn_error_async()
+ print("SQLSTATE:", sqlstate)
+
+asyncio.run(main())
+```
+
+### get_sqlcode_async
+
+`string await ibm_db_dbi.get_sqlcode_async([Connection connection] / [Cursor cursor])`
+
+**Description**
+
+Async wrapper around `ibm_db.get_sqlcode()`. Returns the SQLCODE for the last failed operation.
+
+**Parameters**
+
+- `handle` - (optional) A valid connection or statement handler
+
+**Return Values**
+
+Returns a string containing the SQLCODE or an empty string if there was no error.
+
+**Example**
+
+```python
+import asyncio
+import ibm_db_dbi
+
+async def main():
+ try:
+ conn = await ibm_db_dbi.connect_async(
+ "DATABASE=sample;HOSTNAME=host;PORT=50000;PROTOCOL=TCPIP",
+ "invalid_user", "invalid_pwd")
+ except Exception:
+ sqlcode = await ibm_db_dbi.get_sqlcode_async()
+ print("SQLCODE:", sqlcode)
+
+asyncio.run(main())
+```
+
+### createdb_async
+
+`bool await ibm_db_dbi.createdb_async(string database, string dsn, string user, string password [, string host [, string codeset [, string mode]]])`
+
+**Description**
+
+Async wrapper around `ibm_db_dbi.createdb()`. Creates a database asynchronously.
+
+**Parameters**
+
+- `database` - Name of the database to create
+- `dsn` - Connection string with `ATTACH=true`
+- `user` - Username
+- `password` - Password
+- `host` - Hostname
+- `codeset` - (optional) Database code set
+- `mode` - (optional) Database logging mode
+
+**Return Values**
+
+Returns `True` on success, `None` on failure.
+
+**Example**
+
+```python
+import asyncio
+import ibm_db_dbi
+
+async def main():
+ dsn = "ATTACH=true;HOSTNAME=host.example.com;PORT=50000;PROTOCOL=TCPIP"
+ rc = await ibm_db_dbi.createdb_async("TESTDB", dsn, "user", "password")
+ print("Created:", rc)
+
+asyncio.run(main())
+```
+
+### dropdb_async
+
+`bool await ibm_db_dbi.dropdb_async(string database, string dsn, string user, string password [, string host])`
+
+**Description**
+
+Async wrapper around `ibm_db_dbi.dropdb()`. Drops a database asynchronously.
+
+**Parameters**
+
+- `database` - Name of the database to drop
+- `dsn` - Connection string with `ATTACH=true`
+- `user` - Username
+- `password` - Password
+- `host` - Hostname
+
+**Return Values**
+
+Returns `True` on success, `None` on failure.
+
+**Example**
+
+```python
+import asyncio
+import ibm_db_dbi
+
+async def main():
+ dsn = "ATTACH=true;HOSTNAME=host.example.com;PORT=50000;PROTOCOL=TCPIP"
+ rc = await ibm_db_dbi.dropdb_async("TESTDB", dsn, "user", "password")
+ print("Dropped:", rc)
+
+asyncio.run(main())
+```
+
+### recreatedb_async
+
+`bool await ibm_db_dbi.recreatedb_async(string database, string dsn, string user, string password [, string host [, string codeset [, string mode]]])`
+
+**Description**
+
+Async wrapper around `ibm_db_dbi.recreatedb()`. Drops and then recreates a database asynchronously.
+
+**Parameters**
+
+Same as `createdb_async`.
+
+**Return Values**
+
+Returns `True` on success, `None` on failure.
+
+### createdbNX_async
+
+`bool await ibm_db_dbi.createdbNX_async(string database, string dsn, string user, string password [, string host [, string codeset [, string mode]]])`
+
+**Description**
+
+Async wrapper around `ibm_db_dbi.createdbNX()`. Creates a database only if it does not already exist.
+
+**Parameters**
+
+Same as `createdb_async`.
+
+**Return Values**
+
+Returns `True` if database already exists or is created successfully, `None` on failure.
+
+## AsyncConnection
+
+`AsyncConnection` is a fully async connection class. All methods are coroutines. Obtain an instance via the `connect()` class method.
+
+### AsyncConnection.connect
+
+`AsyncConnection await AsyncConnection.connect(string dsn, string user, string password [, string host [, string database [, dict conn_options]]])`
+
+**Description**
+
+Class method that creates and returns an `AsyncConnection`. This is the recommended way to establish an async connection.
+
+**Parameters**
+
+- `dsn` - A connection string
+- `user` - Username
+- `password` - Password
+- `host` - (optional) Hostname
+- `database` - (optional) Database name
+- `conn_options` - (optional) A dict of connection options
+
+**Return Values**
+
+Returns an `AsyncConnection` object.
+
+**Example**
+
+```python
+import asyncio
+from ibm_db_dbi import AsyncConnection
+
+async def main():
+ dsn = "DATABASE=sample;HOSTNAME=host.example.com;PORT=50000;PROTOCOL=TCPIP"
+ conn = await AsyncConnection.connect(dsn, "user", "password")
+
+ cursor = await conn.cursor()
+ await cursor.execute("SELECT ID, NAME FROM STAFF FETCH FIRST 3 ROWS ONLY")
+ rows = await cursor.fetchall()
+ for row in rows:
+ print(row)
+
+ await cursor.close()
+ await conn.close()
+
+asyncio.run(main())
+```
+
+### AsyncConnection.close
+
+`await conn.close()`
+
+**Description**
+
+Closes the async connection. Rolls back any uncommitted transactions before closing.
+
+**Return Values**
+
+None.
+
+### AsyncConnection.commit
+
+`await conn.commit()`
+
+**Description**
+
+Commits the current transaction.
+
+**Return Values**
+
+None.
+
+**Example**
+
+```python
+from ibm_db_dbi import AsyncConnection
+
+async def main():
+ conn = await AsyncConnection.connect(dsn, user, password)
+ await conn.set_autocommit(False)
+
+ cursor = await conn.cursor()
+ await cursor.execute("INSERT INTO STAFF (ID, NAME) VALUES (999, 'Test')")
+ await conn.commit()
+
+ await cursor.close()
+ await conn.close()
+```
+
+### AsyncConnection.rollback
+
+`await conn.rollback()`
+
+**Description**
+
+Rolls back an in-progress transaction on the `AsyncConnection` and begins a new transaction. Python applications normally default to AUTOCOMMIT mode, so `rollback()` normally has no effect unless AUTOCOMMIT has been turned off for the connection.
+
+**Note:** If the `AsyncConnection` wraps a persistent connection (created via `pconnect_async`), all transactions in progress for all applications using that persistent connection will be rolled back. For this reason, persistent connections are not recommended for use in applications that require transactions.
+
+**Parameters**
+
+**Parameters**
+
+None.
+
+**Return Values**
+
+None.
+
+**Example**
+
+```python
+from ibm_db_dbi import AsyncConnection
+
+async def main():
+ conn = await AsyncConnection.connect(dsn, user, password)
+ await conn.set_autocommit(False)
+
+ cursor = await conn.cursor()
+ await cursor.execute("DELETE FROM STAFF WHERE ID = 10")
+ await conn.rollback() # undo the delete
+
+ await cursor.close()
+ await conn.close()
+```
+
+### AsyncConnection.cursor
+
+`AsyncCursor await conn.cursor()`
+
+**Description**
+
+Creates and returns an `AsyncCursor` bound to this connection.
+
+**Return Values**
+
+Returns an `AsyncCursor` object.
+
+### AsyncConnection.set_autocommit
+
+`await conn.set_autocommit(bool flag)`
+
+**Description**
+
+Enables or disables autocommit on the connection.
+
+**Parameters**
+
+- `flag` - `True` to enable autocommit, `False` to disable
+
+### AsyncConnection.set_option / get_option
+
+`await conn.set_option(dict options_dict)`
+`mixed await conn.get_option(int option_key)`
+
+**Description**
+
+Sets or gets connection-level attributes.
+
+**Parameters**
+
+- `options_dict` - A dict of `{ibm_db_dbi.SQL_ATTR_*: value}`
+- `option_key` - An `ibm_db_dbi.SQL_ATTR_*` constant
+
+**Example**
+
+```python
+import ibm_db_dbi
+from ibm_db_dbi import AsyncConnection
+
+conn = await AsyncConnection.connect(dsn, user, password)
+await conn.set_option({ibm_db_dbi.SQL_ATTR_AUTOCOMMIT: ibm_db_dbi.SQL_AUTOCOMMIT_OFF})
+val = await conn.get_option(ibm_db_dbi.SQL_ATTR_AUTOCOMMIT)
+print("Autocommit:", val)
+```
+
+### AsyncConnection.set_current_schema / get_current_schema
+
+`await conn.set_current_schema(string schema_name)`
+`string await conn.get_current_schema()`
+
+**Description**
+
+Sets or gets the current schema for the connection.
+
+**Example**
+
+```python
+from ibm_db_dbi import AsyncConnection
+
+conn = await AsyncConnection.connect(dsn, user, password)
+await conn.set_current_schema("MYSCHEMA")
+schema = await conn.get_current_schema()
+print("Current schema:", schema)
+```
+
+### AsyncConnection.server_info
+
+`tuple await conn.server_info()`
+
+**Description**
+
+Returns a tuple of `(DBMS_NAME, DBMS_VER)` for the connected server.
+
+**Return Values**
+
+A tuple: `(dbms_name_string, dbms_ver_string)`
+
+**Example**
+
+```python
+from ibm_db_dbi import AsyncConnection
+
+conn = await AsyncConnection.connect(dsn, user, password)
+info = await conn.server_info()
+print("Server:", info[0], "Version:", info[1])
+```
+
+### AsyncConnection.set_fix_return_type
+
+`await conn.set_fix_return_type(bool flag)`
+
+**Description**
+
+Enables or disables `FIX_RETURN_TYPE`. When enabled, numeric columns return `Decimal` instead of string.
+
+**Parameters**
+
+- `flag` - `True` to enable, `False` to disable
+
+### AsyncConnection metadata methods
+
+`list await conn.tables([string schema_name [, string table_name]])`
+`list await conn.columns([string schema_name [, string table_name [, string column_names]]])`
+`list await conn.primary_keys([bool unique [, string schema_name [, string table_name]]])`
+`list await conn.indexes([bool unique [, string schema_name [, string table_name]]])`
+`list await conn.foreign_keys([bool unique [, string schema_name [, string table_name]]])`
+
+**Description**
+
+Query metadata about tables, columns, primary keys, indexes, and foreign keys. Each returns the result as fetched rows.
+
+**Example**
+
+```python
+from ibm_db_dbi import AsyncConnection
+
+conn = await AsyncConnection.connect(dsn, user, password)
+
+# List tables
+tables = await conn.tables(schema_name="DB2ADMIN", table_name="STAFF")
+print(tables)
+
+# List columns
+columns = await conn.columns(schema_name="DB2ADMIN", table_name="STAFF")
+for col in columns:
+ print(col)
+
+# Primary keys
+pkeys = await conn.primary_keys(schema_name="DB2ADMIN", table_name="STAFF")
+print(pkeys)
+
+await conn.close()
+```
+
+### AsyncConnection context manager
+
+**Description**
+
+`AsyncConnection` supports `async with`. The connection is automatically closed when the block exits.
+
+**Example**
+
+```python
+from ibm_db_dbi import AsyncConnection
+
+async with await AsyncConnection.connect(dsn, user, password) as conn:
+ cursor = await conn.cursor()
+ await cursor.execute("SELECT 1 FROM SYSIBM.SYSDUMMY1")
+ print(await cursor.fetchone())
+# connection auto-closed on exit
+```
+
+## AsyncCursor
+
+`AsyncCursor` is returned by `await conn.cursor()` on an `AsyncConnection`. All I/O methods are coroutines.
+
+### AsyncCursor.execute
+
+`await cursor.execute([string operation [, tuple parameters]])`
+
+**Description**
+
+Executes an SQL statement. Supports three calling patterns:
+- `await cursor.execute(sql)` — execute a SQL string
+- `await cursor.execute(sql, params)` — execute with parameter markers
+- `await cursor.execute()` — execute a previously prepared statement (after `prepare` + `bind_param`)
+
+**Parameters**
+
+- `operation` - SQL statement string, or `None` to execute a prepared statement
+- `parameters` - (optional) A tuple of parameter values for parameter markers (`?`)
+
+**Return Values**
+
+None.
+
+**Example**
+
+```python
+from ibm_db_dbi import AsyncConnection
+
+conn = await AsyncConnection.connect(dsn, user, password)
+cursor = await conn.cursor()
+
+# Simple query
+await cursor.execute("SELECT ID, NAME FROM STAFF FETCH FIRST 5 ROWS ONLY")
+rows = await cursor.fetchall()
+print(rows)
+
+# With parameter markers
+await cursor.execute("SELECT ID, NAME FROM STAFF WHERE ID = ?", (10,))
+row = await cursor.fetchone()
+print(row)
+
+await cursor.close()
+await conn.close()
+```
+
+### AsyncCursor.executemany
+
+`await cursor.executemany(string operation, tuple seq_of_parameters)`
+
+**Description**
+
+Executes an SQL statement once for each set of parameters in the sequence.
+
+**Parameters**
+
+- `operation` - SQL statement string with parameter markers
+- `seq_parameters` - A sequence of tuples, each containing parameter values
+
+**Example**
+
+```python
+cursor = await conn.cursor()
+await cursor.executemany(
+ "INSERT INTO STAFF (ID, NAME) VALUES (?, ?)",
+ ((901, 'Alice'), (902, 'Bob'), (903, 'Carol'))
+)
+print("Rows inserted:", cursor.rowcount)
+```
+
+### AsyncCursor.prepare
+
+`await cursor.prepare(string operation)`
+
+**Description**
+
+Prepares an SQL statement for later execution with `execute()`. Use with `bind_param()` for the prepare/bind/execute workflow.
+
+**Parameters**
+
+- `operation` - SQL statement string, optionally containing parameter markers (`?`)
+
+**Example**
+
+```python
+cursor = await conn.cursor()
+await cursor.prepare("SELECT ID, NAME, SALARY FROM STAFF WHERE ID = ?")
+await cursor.bind_param(1, 20)
+await cursor.execute()
+row = await cursor.fetchone()
+print(row)
+```
+
+### AsyncCursor.bind_param
+
+`await cursor.bind_param(int param_no, mixed value [, int param_type [, int data_type]])`
+
+**Description**
+
+Binds a parameter value to a prepared statement.
+
+**Parameters**
+
+- `index` - 1-based parameter position
+- `value` - The Python value to bind (scalar or list for array parameters)
+- `param_type` - (optional) `ibm_db.SQL_PARAM_INPUT`, `SQL_PARAM_OUTPUT`, or `SQL_PARAM_INPUT_OUTPUT`
+- `data_type` - (optional) SQL data type constant (e.g. `ibm_db.SQL_INTEGER`, `ibm_db.SQL_VARCHAR`)
+
+**Example**
+
+```python
+import ibm_db
+
+cursor = await conn.cursor()
+await cursor.prepare("INSERT INTO STAFF (ID, NAME) VALUES (?, ?)")
+await cursor.bind_param(1, 999)
+await cursor.bind_param(2, "TestName")
+await cursor.execute()
+```
+
+For output parameters:
+
+```python
+await cursor.prepare("CALL MY_DOUBLE_PROC(?, ?)")
+await cursor.bind_param(1, 42, ibm_db.SQL_PARAM_INPUT)
+await cursor.bind_param(2, 0, ibm_db.SQL_PARAM_OUTPUT)
+await cursor.execute()
+result = await cursor.fetch_callproc()
+print(result) # (stmt_handler, 42, 84)
+```
+
+### AsyncCursor.fetchone
+
+`tuple await cursor.fetchone()`
+
+**Description**
+
+Fetches the next row from the result set as a tuple, or `None` if no more rows.
+
+**Return Values**
+
+- A tuple containing the column values, or `None`
+
+**Example**
+
+```python
+await cursor.execute("SELECT ID, NAME FROM STAFF")
+row = await cursor.fetchone()
+while row:
+ print(row)
+ row = await cursor.fetchone()
+```
+
+### AsyncCursor.fetchmany
+
+`list await cursor.fetchmany([int size])`
+
+**Description**
+
+Fetches up to `size` rows from the result set as a list of tuples.
+
+**Parameters**
+
+- `size` - (optional) Number of rows to fetch. Defaults to `cursor.arraysize`.
+
+**Return Values**
+
+A list of tuples. Empty list if no rows remain.
+
+**Example**
+
+```python
+await cursor.execute("SELECT ID, NAME FROM STAFF")
+rows = await cursor.fetchmany(3)
+print(rows)
+```
+
+### AsyncCursor.fetchall
+
+`list await cursor.fetchall()`
+
+**Description**
+
+Fetches all remaining rows from the result set as a list of tuples.
+
+**Return Values**
+
+A list of tuples. Empty list if no rows remain.
+
+**Example**
+
+```python
+await cursor.execute("SELECT ID, NAME FROM STAFF FETCH FIRST 5 ROWS ONLY")
+rows = await cursor.fetchall()
+for row in rows:
+ print(row)
+```
+
+### AsyncCursor.fetch_tuple
+
+`tuple await cursor.fetch_tuple()`
+
+**Description**
+
+Fetches one row as a tuple from the current result set. This is an async equivalent of `ibm_db.fetch_tuple()`.
+
+**Return Values**
+
+- A tuple containing the column values for the next row
+- `None` if there are no more rows
+
+**Example**
+
+```python
+cursor = await conn.cursor()
+await cursor.execute("SELECT ID, NAME FROM STAFF FETCH FIRST 3 ROWS ONLY")
+row = await cursor.fetch_tuple()
+while row:
+ print(row)
+ row = await cursor.fetch_tuple()
+await cursor.close()
+```
+
+### AsyncCursor.callproc
+
+`tuple await cursor.callproc(string procname [, tuple parameters])`
+
+**Description**
+
+Calls a stored procedure. Returns a tuple of the (possibly modified) parameters. OUT and INOUT parameters are updated with values returned by the database.
+
+**Parameters**
+
+- `procname` - Name of the stored procedure
+- `parameters` - (optional) A tuple of IN/OUT/INOUT parameter values
+
+**Return Values**
+
+A tuple of parameter values (IN parameters unchanged, OUT/INOUT parameters updated).
+
+**Example**
+
+```python
+cursor = await conn.cursor()
+
+# Suppose MY_DOUBLE_PROC takes IN int, OUT int and sets OUT = IN * 2
+result = await cursor.callproc("MY_DOUBLE_PROC", (42, 0))
+print(result) # (42, 84)
+```
+
+### AsyncCursor.fetch_callproc
+
+`tuple await cursor.fetch_callproc()`
+
+**Description**
+
+Fetches the output parameters returned by a stored procedure that was executed via the prepare/bind/execute path (not `callproc`).
+
+**Return Values**
+
+A tuple containing the statement handler followed by the parameter values.
+
+**Example**
+
+```python
+import ibm_db
+
+cursor = await conn.cursor()
+await cursor.prepare("CALL MY_DOUBLE_PROC(?, ?)")
+await cursor.bind_param(1, 42, ibm_db.SQL_PARAM_INPUT)
+await cursor.bind_param(2, 0, ibm_db.SQL_PARAM_OUTPUT)
+await cursor.execute()
+
+result = await cursor.fetch_callproc()
+print(result) # (stmt_handler, 42, 84)
+```
+
+For array parameters:
+
+```python
+await cursor.prepare("CALL ARRAY_SUM_PROC(?, ?, ?)")
+await cursor.bind_param(1, [10, 20, 30, 40, 50], ibm_db.SQL_PARAM_INPUT)
+await cursor.bind_param(2, 0, ibm_db.SQL_PARAM_OUTPUT)
+await cursor.bind_param(3, 5, ibm_db.SQL_PARAM_INPUT)
+await cursor.execute()
+
+result = await cursor.fetch_callproc()
+print(result)
+```
+
+### AsyncCursor.nextset
+
+`bool await cursor.nextset()`
+
+**Description**
+
+Advances to the next result set when a stored procedure returns multiple result sets.
+
+**Return Values**
+
+- `True` if there is another result set
+- `None` if there are no more result sets
+
+**Example**
+
+```python
+await cursor.callproc("MULTI_RESULTSET_PROC")
+
+# First result set
+rows1 = await cursor.fetchall()
+print("Result set 1:", rows1)
+
+# Advance to next
+has_next = await cursor.nextset()
+if has_next:
+ rows2 = await cursor.fetchall()
+ print("Result set 2:", rows2)
+```
+
+### AsyncCursor.close
+
+`await cursor.close()`
+
+**Description**
+
+Closes the cursor and releases associated resources.
+
+### AsyncCursor.stmt_errormsg / stmt_error
+
+`string await cursor.stmt_errormsg()`
+`string await cursor.stmt_error()`
+
+**Description**
+
+Returns the error message or SQLSTATE for the last failed statement operation on this cursor.
+
+**Example**
+
+```python
+cursor = await conn.cursor()
+try:
+ await cursor.execute("SELECT * FROM NONEXISTENT_TABLE")
+except Exception:
+ print("Error:", await cursor.stmt_errormsg())
+ print("SQLSTATE:", await cursor.stmt_error())
+```
+
+### AsyncCursor.description / rowcount
+
+**Description**
+
+These are **sync properties** (no `await` needed):
+
+- `cursor.description` — Column metadata after execute (list of 7-tuples per DB-API spec), or `None`
+- `cursor.rowcount` — Number of rows affected by the last DML statement, or `-1`
+
+**Example**
+
+```python
+await cursor.execute("SELECT ID, NAME, SALARY FROM STAFF FETCH FIRST 1 ROW ONLY")
+for col in cursor.description:
+ print(col[0], col[1]) # column name, type object
+
+await cursor.execute("UPDATE STAFF SET SALARY = SALARY + 100 WHERE ID = 10")
+print("Rows updated:", cursor.rowcount)
+```
+
+### AsyncCursor context manager
+
+**Description**
+
+`AsyncCursor` supports `async with`. The cursor is automatically closed when the block exits.
+
+**Example**
+
+```python
+from ibm_db_dbi import AsyncConnection
+
+conn = await AsyncConnection.connect(dsn, user, password)
+cursor = await conn.cursor()
+async with cursor:
+ await cursor.execute("SELECT ID, NAME FROM STAFF FETCH FIRST 3 ROWS ONLY")
+ rows = await cursor.fetchall()
+ for row in rows:
+ print(row)
+# cursor auto-closed on exit
+```
+
+## Stored Procedure Patterns
+
+### callproc (simple IN/OUT)
+
+```python
+cursor = await conn.cursor()
+result = await cursor.callproc("MY_DOUBLE_PROC", (42, 0))
+print(result) # (42, 84)
+```
+
+### prepare + bind_param + fetch_callproc (scalar)
+
+```python
+import ibm_db
+
+cursor = await conn.cursor()
+await cursor.prepare("CALL MY_DOUBLE_PROC(?, ?)")
+await cursor.bind_param(1, 42, ibm_db.SQL_PARAM_INPUT)
+await cursor.bind_param(2, 0, ibm_db.SQL_PARAM_OUTPUT)
+await cursor.execute()
+result = await cursor.fetch_callproc()
+print(result) # (stmt_handler, 42, 84)
+```
+
+### Array bind + fetch_callproc
+
+```python
+await cursor.prepare("CALL ARRAY_SUM_PROC(?, ?, ?)")
+await cursor.bind_param(1, [10, 20, 30, 40, 50], ibm_db.SQL_PARAM_INPUT)
+await cursor.bind_param(2, 0, ibm_db.SQL_PARAM_OUTPUT)
+await cursor.bind_param(3, 5, ibm_db.SQL_PARAM_INPUT)
+await cursor.execute()
+result = await cursor.fetch_callproc()
+```
+
+### INOUT parameter
+
+```python
+await cursor.prepare("CALL ADD_100(?)")
+await cursor.bind_param(1, 42, ibm_db.SQL_PARAM_INPUT_OUTPUT)
+await cursor.execute()
+result = await cursor.fetch_callproc()
+print(result) # (stmt_handler, 142)
+```
+
+### Multiple result sets (nextset)
+
+```python
+await cursor.callproc("MULTI_RESULTSET_PROC")
+
+# First result set
+rows1 = await cursor.fetchall()
+
+# Advance to next
+has_next = await cursor.nextset()
+if has_next:
+ rows2 = await cursor.fetchall()
+```
+
+## Concurrent Queries with asyncio.gather
+
+Since all async methods use `asyncio.to_thread()`, multiple queries can run concurrently using `asyncio.gather()`.
+
+> **Note:** Each concurrent query should use its own cursor.
+
+**Example**
+
+```python
+import asyncio
+from ibm_db_dbi import AsyncConnection
+
+async def query(conn, staff_id):
+ cur = await conn.cursor()
+ await cur.execute("SELECT ID, NAME FROM STAFF WHERE ID = ?", (staff_id,))
+ row = await cur.fetchone()
+ await cur.close()
+ return row
+
+async def main():
+ dsn = "DATABASE=sample;HOSTNAME=host.example.com;PORT=50000;PROTOCOL=TCPIP"
+ conn = await AsyncConnection.connect(dsn, "user", "password")
+
+ results = await asyncio.gather(
+ query(conn, 10),
+ query(conn, 20),
+ query(conn, 30),
+ )
+ for r in results:
+ print(r)
+
+ await conn.close()
+
+asyncio.run(main())
+```
+
+## Complete Example Script
+
+```python
+import asyncio
+from ibm_db_dbi import AsyncConnection
+
+async def main():
+ dsn = "DATABASE=sample;HOSTNAME=host.example.com;PORT=50000;PROTOCOL=TCPIP"
+
+ async with await AsyncConnection.connect(dsn, "user", "password") as conn:
+ # Server info
+ info = await conn.server_info()
+ print("Connected to:", info[0], info[1])
+
+ # Query
+ cursor = await conn.cursor()
+ await cursor.execute("SELECT ID, NAME, SALARY FROM STAFF FETCH FIRST 5 ROWS ONLY")
+ rows = await cursor.fetchall()
+ for row in rows:
+ print(row)
+
+ # Prepare + bind + execute
+ await cursor.prepare("SELECT NAME FROM STAFF WHERE ID = ?")
+ await cursor.bind_param(1, 10)
+ await cursor.execute()
+ print("Staff 10:", await cursor.fetchone())
+
+ # DML with commit
+ await conn.set_autocommit(False)
+ await cursor.execute("UPDATE STAFF SET SALARY = SALARY + 1 WHERE ID = 10")
+ print("Updated rows:", cursor.rowcount)
+ await conn.rollback()
+
+ await cursor.close()
+
+asyncio.run(main())
+```
diff --git a/README.md b/README.md
index bee09f02..47d5cc56 100644
--- a/README.md
+++ b/README.md
@@ -18,6 +18,8 @@ For more information on the APIs supported by ibm_db, please refer to below link
https://github.com/ibmdb/python-ibmdb/wiki/APIs
+For the asyncio APIs, please refer to [ASYNC_APIs_WIKI.md](ASYNC_APIs_WIKI.md).
+
## Pre-requisites
@@ -744,29 +746,38 @@ later of Db2. While Db2 v8.x is fully supported, two of the tests
(test_195.py and test_52949.py) utilize XML functionality. These tests will
fail on version 8.x of Db2.
-## Running the driver testsuite on Linux
+## Running the driver testsuite
-In order to run the entire python driver testsuite on Linux, run this
-command at the command prompt:
+Run the entire testsuite (sync + async):
```
python ibmdb_tests.py
```
-To run a single test, set the environment variable, **SINGLE_PYTHON_TEST**, to
-the test filename you would like to run, followed by the previous command.
+Run only sync or async tests, or a single test file:
-## Running the driver testsuite on Windows
+```
+ python ibmdb_tests.py --async # Run only async tests
+ python ibmdb_tests.py --sync # Run only sync tests
+ python ibmdb_tests.py --async --test test_05_async_fetch.py # Run a single async test
+ python ibmdb_tests.py --sync --test test_006_ConnPassingOpts.py # Run a single sync test
+```
-In order to run the entire python driver testsuite on Windows, run this
-command at the command prompt:
+Legacy: To run a single test, set the environment variable **SINGLE_PYTHON_TEST**:
```
- python ibmdb_tests.py
+ Windows:
+ set SINGLE_PYTHON_TEST=test_006_ConnPassingOpts.py
+
+ Other platforms:
+ export SINGLE_PYTHON_TEST=test_006_ConnPassingOpts.py
```
-To run a single test, set the environment variable, **SINGLE_PYTHON_TEST**, to
-the test filename you would like to run, followed by the previous command.
+Then run:
+
+```
+ python ibmdb_tests.py
+```
## Known Limitations for the Python driver
diff --git a/asyncio_testsuite/test_01_async_connect.py b/asyncio_testsuite/test_01_async_connect.py
new file mode 100644
index 00000000..5036cbb2
--- /dev/null
+++ b/asyncio_testsuite/test_01_async_connect.py
@@ -0,0 +1,50 @@
+from __future__ import print_function
+import asyncio
+import sys
+import unittest
+import ibm_db_dbi
+import config
+from testfunctions import IbmDbTestFunctions
+
+class IbmDbTestCase(unittest.TestCase):
+
+ def test_01_async_connect(self):
+ obj = IbmDbTestFunctions()
+ obj.assert_expect(self.run_test_01)
+
+ def run_test_01(self):
+ async def main():
+ # Cataloged connection
+ conn = await ibm_db_dbi.connect_async(config.database, config.user, config.password)
+ if conn:
+ print("Cataloged connection succeeded.")
+ conn.close()
+ else:
+ print("Cataloged connection failed.")
+
+ # Uncataloged connection
+ conn = await ibm_db_dbi.connect_async(
+ "DATABASE=%s;HOSTNAME=%s;PORT=%d;PROTOCOL=TCPIP;UID=%s;PWD=%s;" % (
+ config.database, config.hostname, config.port,
+ config.user, config.password),
+ '', '')
+ if conn:
+ print("Uncataloged connection succeeded.")
+ conn.close()
+ else:
+ print("Uncataloged connection failed.")
+ asyncio.run(main())
+
+#__END__
+#__LUW_EXPECTED__
+#Cataloged connection succeeded.
+#Uncataloged connection succeeded.
+#__ZOS_EXPECTED__
+#Cataloged connection succeeded.
+#Uncataloged connection succeeded.
+#__SYSTEMI_EXPECTED__
+#Cataloged connection succeeded.
+#Uncataloged connection succeeded.
+#__IDS_EXPECTED__
+#Cataloged connection succeeded.
+#Uncataloged connection succeeded.
diff --git a/asyncio_testsuite/test_02_async_pconnect.py b/asyncio_testsuite/test_02_async_pconnect.py
new file mode 100644
index 00000000..c5bd93d5
--- /dev/null
+++ b/asyncio_testsuite/test_02_async_pconnect.py
@@ -0,0 +1,46 @@
+from __future__ import print_function
+import asyncio
+import sys
+import unittest
+import ibm_db_dbi
+import config
+from testfunctions import IbmDbTestFunctions
+
+
+class IbmDbTestCase(unittest.TestCase):
+
+ def test_02_async_pconnect(self):
+ obj = IbmDbTestFunctions()
+ obj.assert_expectf(self.run_test_02)
+
+ def run_test_02(self):
+ async def main():
+ conn = await ibm_db_dbi.pconnect_async(
+ "DATABASE=%s;HOSTNAME=%s;PORT=%d;PROTOCOL=TCPIP;UID=%s;PWD=%s;" % (
+ config.database, config.hostname, config.port,
+ config.user, config.password),
+ '', '')
+ print("Persistent connection:", conn)
+ info = conn.server_info()
+ print("Server info:", info)
+ conn.close()
+ print("Connection closed.")
+ asyncio.run(main())
+
+#__END__
+#__LUW_EXPECTED__
+#Persistent connection:
+#Server info: (%s)
+#Connection closed.
+#__ZOS_EXPECTED__
+#Persistent connection:
+#Server info: (%s)
+#Connection closed.
+#__SYSTEMI_EXPECTED__
+#Persistent connection:
+#Server info: (%s)
+#Connection closed.
+#__IDS_EXPECTED__
+#Persistent connection:
+#Server info: (%s)
+#Connection closed.
diff --git a/asyncio_testsuite/test_03_async_connection_class.py b/asyncio_testsuite/test_03_async_connection_class.py
new file mode 100644
index 00000000..f6c3f663
--- /dev/null
+++ b/asyncio_testsuite/test_03_async_connection_class.py
@@ -0,0 +1,44 @@
+from __future__ import print_function
+import asyncio
+import sys
+import unittest
+import config
+from ibm_db_dbi import AsyncConnection
+from testfunctions import IbmDbTestFunctions
+
+
+class IbmDbTestCase(unittest.TestCase):
+
+ def test_03_async_connection_class(self):
+ obj = IbmDbTestFunctions()
+ obj.assert_expectf(self.run_test_03)
+
+ def run_test_03(self):
+ async def main():
+ conn = await AsyncConnection.connect(
+ "DATABASE=%s;HOSTNAME=%s;PORT=%d;PROTOCOL=TCPIP;UID=%s;PWD=%s;" % (
+ config.database, config.hostname, config.port,
+ config.user, config.password),
+ '', '')
+ print("Connected via AsyncConnection.connect():", conn)
+
+ cursor = await conn.cursor()
+ print("Cursor created:", cursor)
+
+ await cursor.close()
+ await conn.close()
+ asyncio.run(main())
+
+#__END__
+#__LUW_EXPECTED__
+#Connected via AsyncConnection.connect():
+#Cursor created:
+#__ZOS_EXPECTED__
+#Connected via AsyncConnection.connect():
+#Cursor created:
+#__SYSTEMI_EXPECTED__
+#Connected via AsyncConnection.connect():
+#Cursor created:
+#__IDS_EXPECTED__
+#Connected via AsyncConnection.connect():
+#Cursor created:
diff --git a/asyncio_testsuite/test_04_async_context_manager.py b/asyncio_testsuite/test_04_async_context_manager.py
new file mode 100644
index 00000000..19eca74f
--- /dev/null
+++ b/asyncio_testsuite/test_04_async_context_manager.py
@@ -0,0 +1,59 @@
+from __future__ import print_function
+import asyncio
+import sys
+import unittest
+import config
+from ibm_db_dbi import AsyncConnection
+from testfunctions import IbmDbTestFunctions
+
+
+class IbmDbTestCase(unittest.TestCase):
+
+ def test_04_async_context_manager(self):
+ obj = IbmDbTestFunctions()
+ obj.assert_expect(self.run_test_04)
+
+ def run_test_04(self):
+ async def main():
+ conn = await AsyncConnection.connect(
+ "DATABASE=%s;HOSTNAME=%s;PORT=%d;PROTOCOL=TCPIP;UID=%s;PWD=%s;" % (
+ config.database, config.hostname, config.port,
+ config.user, config.password),
+ '', '')
+
+ async with conn:
+ cursor = await conn.cursor()
+ async with cursor:
+ await cursor.execute("SELECT ID, NAME FROM STAFF FETCH FIRST 3 ROWS ONLY")
+ rows = await cursor.fetchall()
+ for row in rows:
+ print(row)
+ print("Cursor auto-closed via __aexit__")
+ print("Connection auto-closed via __aexit__")
+ asyncio.run(main())
+
+#__END__
+#__LUW_EXPECTED__
+#(10, 'Sanders')
+#(20, 'Pernal')
+#(30, 'Marenghi')
+#Cursor auto-closed via __aexit__
+#Connection auto-closed via __aexit__
+#__ZOS_EXPECTED__
+#(10, 'Sanders')
+#(20, 'Pernal')
+#(30, 'Marenghi')
+#Cursor auto-closed via __aexit__
+#Connection auto-closed via __aexit__
+#__SYSTEMI_EXPECTED__
+#(10, 'Sanders')
+#(20, 'Pernal')
+#(30, 'Marenghi')
+#Cursor auto-closed via __aexit__
+#Connection auto-closed via __aexit__
+#__IDS_EXPECTED__
+#(10, 'Sanders')
+#(20, 'Pernal')
+#(30, 'Marenghi')
+#Cursor auto-closed via __aexit__
+#Connection auto-closed via __aexit__
diff --git a/asyncio_testsuite/test_05_async_fetch.py b/asyncio_testsuite/test_05_async_fetch.py
new file mode 100644
index 00000000..08e3b084
--- /dev/null
+++ b/asyncio_testsuite/test_05_async_fetch.py
@@ -0,0 +1,59 @@
+from __future__ import print_function
+import asyncio
+import sys
+import unittest
+import config
+from ibm_db_dbi import AsyncConnection
+from testfunctions import IbmDbTestFunctions
+
+
+class IbmDbTestCase(unittest.TestCase):
+
+ def test_05_async_fetch(self):
+ obj = IbmDbTestFunctions()
+ obj.assert_expect(self.run_test_05)
+
+ def run_test_05(self):
+ async def main():
+ conn = await AsyncConnection.connect(
+ "DATABASE=%s;HOSTNAME=%s;PORT=%d;PROTOCOL=TCPIP;UID=%s;PWD=%s;" % (
+ config.database, config.hostname, config.port,
+ config.user, config.password),
+ '', '')
+ cursor = await conn.cursor()
+
+ await cursor.execute("SELECT ID, NAME, JOB FROM STAFF FETCH FIRST 10 ROWS ONLY")
+
+ # fetchone
+ row = await cursor.fetchone()
+ print("fetchone:", row)
+
+ # fetchmany
+ rows = await cursor.fetchmany(size=3)
+ print("fetchmany(3):", rows)
+
+ # fetchall (remaining)
+ remaining = await cursor.fetchall()
+ print("fetchall remaining:", remaining)
+
+ await cursor.close()
+ await conn.close()
+ asyncio.run(main())
+
+#__END__
+#__LUW_EXPECTED__
+#fetchone: (10, 'Sanders', 'Mgr ')
+#fetchmany(3): [(20, 'Pernal', 'Sales'), (30, 'Marenghi', 'Mgr '), (40, 'OBrien', 'Sales')]
+#fetchall remaining: [(50, 'Hanes', 'Mgr '), (60, 'Quigley', 'Sales'), (70, 'Rothman', 'Sales'), (80, 'James', 'Clerk'), (90, 'Koonitz', 'Sales'), (100, 'Plotz', 'Mgr ')]
+#__ZOS_EXPECTED__
+#fetchone: (10, 'Sanders', 'Mgr ')
+#fetchmany(3): [(20, 'Pernal', 'Sales'), (30, 'Marenghi', 'Mgr '), (40, 'OBrien', 'Sales')]
+#fetchall remaining: [(50, 'Hanes', 'Mgr '), (60, 'Quigley', 'Sales'), (70, 'Rothman', 'Sales'), (80, 'James', 'Clerk'), (90, 'Koonitz', 'Sales'), (100, 'Plotz', 'Mgr ')]
+#__SYSTEMI_EXPECTED__
+#fetchone: (10, 'Sanders', 'Mgr ')
+#fetchmany(3): [(20, 'Pernal', 'Sales'), (30, 'Marenghi', 'Mgr '), (40, 'OBrien', 'Sales')]
+#fetchall remaining: [(50, 'Hanes', 'Mgr '), (60, 'Quigley', 'Sales'), (70, 'Rothman', 'Sales'), (80, 'James', 'Clerk'), (90, 'Koonitz', 'Sales'), (100, 'Plotz', 'Mgr ')]
+#__IDS_EXPECTED__
+#fetchone: (10, 'Sanders', 'Mgr ')
+#fetchmany(3): [(20, 'Pernal', 'Sales'), (30, 'Marenghi', 'Mgr '), (40, 'OBrien', 'Sales')]
+#fetchall remaining: [(50, 'Hanes', 'Mgr '), (60, 'Quigley', 'Sales'), (70, 'Rothman', 'Sales'), (80, 'James', 'Clerk'), (90, 'Koonitz', 'Sales'), (100, 'Plotz', 'Mgr ')]
diff --git a/asyncio_testsuite/test_06_async_iteration.py b/asyncio_testsuite/test_06_async_iteration.py
new file mode 100644
index 00000000..330a91f2
--- /dev/null
+++ b/asyncio_testsuite/test_06_async_iteration.py
@@ -0,0 +1,65 @@
+from __future__ import print_function
+import asyncio
+import sys
+import unittest
+import config
+from ibm_db_dbi import AsyncConnection
+from testfunctions import IbmDbTestFunctions
+
+
+class IbmDbTestCase(unittest.TestCase):
+
+ def test_06_async_iteration(self):
+ obj = IbmDbTestFunctions()
+ obj.assert_expect(self.run_test_06)
+
+ def run_test_06(self):
+ async def main():
+ conn = await AsyncConnection.connect(
+ "DATABASE=%s;HOSTNAME=%s;PORT=%d;PROTOCOL=TCPIP;UID=%s;PWD=%s;" % (
+ config.database, config.hostname, config.port,
+ config.user, config.password),
+ '', '')
+ cursor = await conn.cursor()
+
+ await cursor.execute("SELECT ID, NAME FROM STAFF FETCH FIRST 5 ROWS ONLY")
+
+ count = 0
+ async for row in cursor:
+ print(row)
+ count += 1
+ print("Iterated over %d rows" % count)
+
+ await cursor.close()
+ await conn.close()
+ asyncio.run(main())
+
+#__END__
+#__LUW_EXPECTED__
+#(10, 'Sanders')
+#(20, 'Pernal')
+#(30, 'Marenghi')
+#(40, 'OBrien')
+#(50, 'Hanes')
+#Iterated over 5 rows
+#__ZOS_EXPECTED__
+#(10, 'Sanders')
+#(20, 'Pernal')
+#(30, 'Marenghi')
+#(40, 'OBrien')
+#(50, 'Hanes')
+#Iterated over 5 rows
+#__SYSTEMI_EXPECTED__
+#(10, 'Sanders')
+#(20, 'Pernal')
+#(30, 'Marenghi')
+#(40, 'OBrien')
+#(50, 'Hanes')
+#Iterated over 5 rows
+#__IDS_EXPECTED__
+#(10, 'Sanders')
+#(20, 'Pernal')
+#(30, 'Marenghi')
+#(40, 'OBrien')
+#(50, 'Hanes')
+#Iterated over 5 rows
diff --git a/asyncio_testsuite/test_07_async_execute_params.py b/asyncio_testsuite/test_07_async_execute_params.py
new file mode 100644
index 00000000..94431b11
--- /dev/null
+++ b/asyncio_testsuite/test_07_async_execute_params.py
@@ -0,0 +1,63 @@
+from __future__ import print_function
+import asyncio
+import sys
+import unittest
+import config
+from ibm_db_dbi import AsyncConnection
+from testfunctions import IbmDbTestFunctions
+
+
+class IbmDbTestCase(unittest.TestCase):
+
+ def test_07_async_execute_params(self):
+ obj = IbmDbTestFunctions()
+ obj.assert_expect(self.run_test_07)
+
+ def run_test_07(self):
+ async def main():
+ conn = await AsyncConnection.connect(
+ "DATABASE=%s;HOSTNAME=%s;PORT=%d;PROTOCOL=TCPIP;UID=%s;PWD=%s;" % (
+ config.database, config.hostname, config.port,
+ config.user, config.password),
+ '', '')
+ cursor = await conn.cursor()
+
+ # SELECT with positional parameter
+ await cursor.execute(
+ "SELECT ID, NAME, JOB, SALARY FROM STAFF WHERE ID = ?",
+ (20,)
+ )
+ row = await cursor.fetchone()
+ print("Staff ID 20:", row)
+
+ # SELECT with multiple parameters
+ await cursor.execute(
+ "SELECT ID, NAME, JOB FROM STAFF WHERE DEPT = ? AND JOB = ?",
+ (20, 'Sales')
+ )
+ rows = await cursor.fetchall()
+ print("Dept 20 Sales (%d rows):" % len(rows))
+ for r in rows:
+ print(" ", r)
+
+ await cursor.close()
+ await conn.close()
+ asyncio.run(main())
+
+#__END__
+#__LUW_EXPECTED__
+#Staff ID 20: (20, 'Pernal', 'Sales', Decimal('18171.25'))
+#Dept 20 Sales (1 rows):
+# (20, 'Pernal', 'Sales')
+#__ZOS_EXPECTED__
+#Staff ID 20: (20, 'Pernal', 'Sales', Decimal('18171.25'))
+#Dept 20 Sales (1 rows):
+# (20, 'Pernal', 'Sales')
+#__SYSTEMI_EXPECTED__
+#Staff ID 20: (20, 'Pernal', 'Sales', Decimal('18171.25'))
+#Dept 20 Sales (1 rows):
+# (20, 'Pernal', 'Sales')
+#__IDS_EXPECTED__
+#Staff ID 20: (20, 'Pernal', 'Sales', Decimal('18171.25'))
+#Dept 20 Sales (1 rows):
+# (20, 'Pernal', 'Sales')
diff --git a/asyncio_testsuite/test_08_async_prepare_bind_execute.py b/asyncio_testsuite/test_08_async_prepare_bind_execute.py
new file mode 100644
index 00000000..ecf615f2
--- /dev/null
+++ b/asyncio_testsuite/test_08_async_prepare_bind_execute.py
@@ -0,0 +1,49 @@
+from __future__ import print_function
+import asyncio
+import sys
+import unittest
+import config
+from ibm_db_dbi import AsyncConnection
+from testfunctions import IbmDbTestFunctions
+
+
+class IbmDbTestCase(unittest.TestCase):
+
+ def test_08_async_prepare_bind_execute(self):
+ obj = IbmDbTestFunctions()
+ obj.assert_expect(self.run_test_08)
+
+ def run_test_08(self):
+ async def main():
+ conn = await AsyncConnection.connect(
+ "DATABASE=%s;HOSTNAME=%s;PORT=%d;PROTOCOL=TCPIP;UID=%s;PWD=%s;" % (
+ config.database, config.hostname, config.port,
+ config.user, config.password),
+ '', '')
+ cursor = await conn.cursor()
+
+ await cursor.prepare("SELECT ID, NAME, JOB, SALARY FROM STAFF WHERE ID = ?")
+ await cursor.bind_param(1, 20)
+ await cursor.execute()
+
+ row = await cursor.fetchone()
+ print("Staff ID 20:", row)
+
+ while row:
+ row = await cursor.fetchone()
+ if row:
+ print(row)
+
+ await cursor.close()
+ await conn.close()
+ asyncio.run(main())
+
+#__END__
+#__LUW_EXPECTED__
+#Staff ID 20: (20, 'Pernal', 'Sales', Decimal('18171.25'))
+#__ZOS_EXPECTED__
+#Staff ID 20: (20, 'Pernal', 'Sales', Decimal('18171.25'))
+#__SYSTEMI_EXPECTED__
+#Staff ID 20: (20, 'Pernal', 'Sales', Decimal('18171.25'))
+#__IDS_EXPECTED__
+#Staff ID 20: (20, 'Pernal', 'Sales', Decimal('18171.25'))
diff --git a/asyncio_testsuite/test_09_async_callproc.py b/asyncio_testsuite/test_09_async_callproc.py
new file mode 100644
index 00000000..801b67a4
--- /dev/null
+++ b/asyncio_testsuite/test_09_async_callproc.py
@@ -0,0 +1,53 @@
+from __future__ import print_function
+import asyncio
+import sys
+import unittest
+import config
+from ibm_db_dbi import AsyncConnection
+from testfunctions import IbmDbTestFunctions
+
+
+class IbmDbTestCase(unittest.TestCase):
+
+ def test_09_async_callproc(self):
+ obj = IbmDbTestFunctions()
+ obj.assert_expect(self.run_test_09)
+
+ def run_test_09(self):
+ async def main():
+ conn = await AsyncConnection.connect(
+ "DATABASE=%s;HOSTNAME=%s;PORT=%d;PROTOCOL=TCPIP;UID=%s;PWD=%s;" % (
+ config.database, config.hostname, config.port,
+ config.user, config.password),
+ '', '')
+ cursor = await conn.cursor()
+
+ # Create stored procedure (ignore error if already exists)
+ try:
+ await cursor.execute("""
+ CREATE PROCEDURE ASYNC_TEST_DOUBLE(IN val BIGINT, OUT result BIGINT)
+ LANGUAGE SQL
+ BEGIN
+ SET result = val * 2;
+ END
+ """)
+ except Exception:
+ pass
+
+ # Call procedure via callproc
+ result = await cursor.callproc("ASYNC_TEST_DOUBLE", (42, 0))
+ print("callproc result:", result)
+
+ await cursor.close()
+ await conn.close()
+ asyncio.run(main())
+
+#__END__
+#__LUW_EXPECTED__
+#callproc result: (42, 84)
+#__ZOS_EXPECTED__
+#callproc result: (42, 84)
+#__SYSTEMI_EXPECTED__
+#callproc result: (42, 84)
+#__IDS_EXPECTED__
+#callproc result: (42, 84)
diff --git a/asyncio_testsuite/test_10_async_bind_array_callproc.py b/asyncio_testsuite/test_10_async_bind_array_callproc.py
new file mode 100644
index 00000000..86cb5535
--- /dev/null
+++ b/asyncio_testsuite/test_10_async_bind_array_callproc.py
@@ -0,0 +1,69 @@
+from __future__ import print_function
+import asyncio
+import sys
+import unittest
+import ibm_db
+import config
+from ibm_db_dbi import AsyncConnection
+from testfunctions import IbmDbTestFunctions
+
+
+class IbmDbTestCase(unittest.TestCase):
+
+ def test_10_async_bind_array_callproc(self):
+ obj = IbmDbTestFunctions()
+ obj.assert_expectf(self.run_test_10)
+
+ def run_test_10(self):
+ async def main():
+ conn = await AsyncConnection.connect(
+ "DATABASE=%s;HOSTNAME=%s;PORT=%d;PROTOCOL=TCPIP;UID=%s;PWD=%s;" % (
+ config.database, config.hostname, config.port,
+ config.user, config.password),
+ '', '')
+ cursor = await conn.cursor()
+
+ # Create stored procedure (ignore error if already exists)
+ try:
+ await cursor.execute("""
+ CREATE PROCEDURE CLIARRAY.ARRAY_BINT12(IN var1 bint_array, OUT var2 BIGINT)
+ LANGUAGE SQL
+ BEGIN
+ SET var2 = CARDINALITY(var1);
+ END
+ """)
+ except Exception:
+ pass
+
+ # Prepare, bind array IN + scalar OUT, execute
+ await cursor.prepare("CALL CLIARRAY.ARRAY_BINT12(?,?)")
+
+ input_array = [10, 20, 30, 40, 50, 60, 70]
+ output = 0
+
+ await cursor.bind_param(1, input_array, ibm_db.SQL_PARAM_INPUT)
+ await cursor.bind_param(2, output, ibm_db.SQL_PARAM_OUTPUT)
+
+ await cursor.execute()
+
+ result = await cursor.fetch_callproc()
+ print("Input array:", input_array)
+ print("fetch_callproc result (cardinality):", result)
+
+ await cursor.close()
+ await conn.close()
+ asyncio.run(main())
+
+#__END__
+#__LUW_EXPECTED__
+#Input array: [10, 20, 30, 40, 50, 60, 70]
+#fetch_callproc result (cardinality): (, [10, 20, 30, 40, 50, 60, 70], 7)
+#__ZOS_EXPECTED__
+#Input array: [10, 20, 30, 40, 50, 60, 70]
+#fetch_callproc result (cardinality): (, [10, 20, 30, 40, 50, 60, 70], 7)
+#__SYSTEMI_EXPECTED__
+#Input array: [10, 20, 30, 40, 50, 60, 70]
+#fetch_callproc result (cardinality): (, [10, 20, 30, 40, 50, 60, 70], 7)
+#__IDS_EXPECTED__
+#Input array: [10, 20, 30, 40, 50, 60, 70]
+#fetch_callproc result (cardinality): (, [10, 20, 30, 40, 50, 60, 70], 7)
diff --git a/asyncio_testsuite/test_11_async_executemany.py b/asyncio_testsuite/test_11_async_executemany.py
new file mode 100644
index 00000000..6bf72694
--- /dev/null
+++ b/asyncio_testsuite/test_11_async_executemany.py
@@ -0,0 +1,62 @@
+from __future__ import print_function
+import asyncio
+import sys
+import unittest
+import config
+from ibm_db_dbi import AsyncConnection
+from testfunctions import IbmDbTestFunctions
+
+
+class IbmDbTestCase(unittest.TestCase):
+
+ def test_11_async_executemany(self):
+ obj = IbmDbTestFunctions()
+ obj.assert_expect(self.run_test_11)
+
+ def run_test_11(self):
+ async def main():
+ conn = await AsyncConnection.connect(
+ "DATABASE=%s;HOSTNAME=%s;PORT=%d;PROTOCOL=TCPIP;UID=%s;PWD=%s;" % (
+ config.database, config.hostname, config.port,
+ config.user, config.password),
+ '', '')
+ await conn.set_autocommit(True)
+ cursor = await conn.cursor()
+
+ # Create temp table
+ try:
+ await cursor.execute("DROP TABLE ASYNC_TEST_BATCH")
+ except Exception:
+ pass
+ await cursor.execute("CREATE TABLE ASYNC_TEST_BATCH (ID INT, NAME VARCHAR(30))")
+
+ # Insert multiple rows
+ data = [(1, 'Alice'), (2, 'Bob'), (3, 'Charlie'), (4, 'Diana')]
+ await cursor.executemany("INSERT INTO ASYNC_TEST_BATCH (ID, NAME) VALUES (?, ?)", data)
+ print("Inserted %d rows" % len(data))
+
+ # Verify
+ await cursor.execute("SELECT * FROM ASYNC_TEST_BATCH ORDER BY ID")
+ rows = await cursor.fetchall()
+ print("Rows:", rows)
+
+ # Cleanup
+ await cursor.execute("DROP TABLE ASYNC_TEST_BATCH")
+
+ await cursor.close()
+ await conn.close()
+ asyncio.run(main())
+
+#__END__
+#__LUW_EXPECTED__
+#Inserted 4 rows
+#Rows: [(1, 'Alice'), (2, 'Bob'), (3, 'Charlie'), (4, 'Diana')]
+#__ZOS_EXPECTED__
+#Inserted 4 rows
+#Rows: [(1, 'Alice'), (2, 'Bob'), (3, 'Charlie'), (4, 'Diana')]
+#__SYSTEMI_EXPECTED__
+#Inserted 4 rows
+#Rows: [(1, 'Alice'), (2, 'Bob'), (3, 'Charlie'), (4, 'Diana')]
+#__IDS_EXPECTED__
+#Inserted 4 rows
+#Rows: [(1, 'Alice'), (2, 'Bob'), (3, 'Charlie'), (4, 'Diana')]
diff --git a/asyncio_testsuite/test_12_async_commit_rollback.py b/asyncio_testsuite/test_12_async_commit_rollback.py
new file mode 100644
index 00000000..e5ea2f3a
--- /dev/null
+++ b/asyncio_testsuite/test_12_async_commit_rollback.py
@@ -0,0 +1,71 @@
+from __future__ import print_function
+import asyncio
+import sys
+import unittest
+import config
+from ibm_db_dbi import AsyncConnection
+from testfunctions import IbmDbTestFunctions
+
+
+class IbmDbTestCase(unittest.TestCase):
+
+ def test_12_async_commit_rollback(self):
+ obj = IbmDbTestFunctions()
+ obj.assert_expect(self.run_test_12)
+
+ def run_test_12(self):
+ async def main():
+ conn = await AsyncConnection.connect(
+ "DATABASE=%s;HOSTNAME=%s;PORT=%d;PROTOCOL=TCPIP;UID=%s;PWD=%s;" % (
+ config.database, config.hostname, config.port,
+ config.user, config.password),
+ '', '')
+ await conn.set_autocommit(False)
+ cursor = await conn.cursor()
+
+ # Create temp table
+ try:
+ await cursor.execute("DROP TABLE ASYNC_TEST_TXN")
+ except Exception:
+ pass
+ await conn.commit()
+ await cursor.execute("CREATE TABLE ASYNC_TEST_TXN (ID INT, VAL VARCHAR(20))")
+ await conn.commit()
+
+ # Insert and rollback
+ await cursor.execute("INSERT INTO ASYNC_TEST_TXN VALUES (1, 'should_disappear')")
+ await conn.rollback()
+ await cursor.execute("SELECT COUNT(*) FROM ASYNC_TEST_TXN")
+ row = await cursor.fetchone()
+ print("After rollback, count:", row[0])
+ assert row[0] == 0, "Rollback failed"
+
+ # Insert and commit
+ await cursor.execute("INSERT INTO ASYNC_TEST_TXN VALUES (2, 'should_stay')")
+ await conn.commit()
+ await cursor.execute("SELECT * FROM ASYNC_TEST_TXN")
+ row = await cursor.fetchone()
+ print("After commit:", row)
+ assert row is not None, "Commit failed"
+
+ # Cleanup
+ await cursor.execute("DROP TABLE ASYNC_TEST_TXN")
+ await conn.commit()
+
+ await cursor.close()
+ await conn.close()
+ asyncio.run(main())
+
+#__END__
+#__LUW_EXPECTED__
+#After rollback, count: 0
+#After commit: (2, 'should_stay')
+#__ZOS_EXPECTED__
+#After rollback, count: 0
+#After commit: (2, 'should_stay')
+#__SYSTEMI_EXPECTED__
+#After rollback, count: 0
+#After commit: (2, 'should_stay')
+#__IDS_EXPECTED__
+#After rollback, count: 0
+#After commit: (2, 'should_stay')
diff --git a/asyncio_testsuite/test_13_async_autocommit.py b/asyncio_testsuite/test_13_async_autocommit.py
new file mode 100644
index 00000000..5ef132a7
--- /dev/null
+++ b/asyncio_testsuite/test_13_async_autocommit.py
@@ -0,0 +1,47 @@
+from __future__ import print_function
+import asyncio
+import sys
+import unittest
+import config
+from ibm_db_dbi import AsyncConnection
+from testfunctions import IbmDbTestFunctions
+
+
+class IbmDbTestCase(unittest.TestCase):
+
+ def test_13_async_autocommit(self):
+ obj = IbmDbTestFunctions()
+ obj.assert_expect(self.run_test_13)
+
+ def run_test_13(self):
+ async def main():
+ conn = await AsyncConnection.connect(
+ "DATABASE=%s;HOSTNAME=%s;PORT=%d;PROTOCOL=TCPIP;UID=%s;PWD=%s;" % (
+ config.database, config.hostname, config.port,
+ config.user, config.password),
+ '', '')
+
+ # Turn autocommit ON
+ result = await conn.set_autocommit(True)
+ print("set_autocommit(True):", result)
+
+ # Turn autocommit OFF
+ result = await conn.set_autocommit(False)
+ print("set_autocommit(False):", result)
+
+ await conn.close()
+ asyncio.run(main())
+
+#__END__
+#__LUW_EXPECTED__
+#set_autocommit(True): True
+#set_autocommit(False): True
+#__ZOS_EXPECTED__
+#set_autocommit(True): True
+#set_autocommit(False): True
+#__SYSTEMI_EXPECTED__
+#set_autocommit(True): True
+#set_autocommit(False): True
+#__IDS_EXPECTED__
+#set_autocommit(True): True
+#set_autocommit(False): True
diff --git a/asyncio_testsuite/test_14_async_set_get_option.py b/asyncio_testsuite/test_14_async_set_get_option.py
new file mode 100644
index 00000000..abb79df3
--- /dev/null
+++ b/asyncio_testsuite/test_14_async_set_get_option.py
@@ -0,0 +1,49 @@
+from __future__ import print_function
+import asyncio
+import sys
+import unittest
+import ibm_db
+import config
+from ibm_db_dbi import AsyncConnection
+from testfunctions import IbmDbTestFunctions
+
+
+class IbmDbTestCase(unittest.TestCase):
+
+ def test_14_async_set_get_option(self):
+ obj = IbmDbTestFunctions()
+ obj.assert_expect(self.run_test_14)
+
+ def run_test_14(self):
+ async def main():
+ conn = await AsyncConnection.connect(
+ "DATABASE=%s;HOSTNAME=%s;PORT=%d;PROTOCOL=TCPIP;UID=%s;PWD=%s;" % (
+ config.database, config.hostname, config.port,
+ config.user, config.password),
+ '', '')
+
+ # Get current autocommit setting
+ val = await conn.get_option(ibm_db.SQL_ATTR_AUTOCOMMIT)
+ print("SQL_ATTR_AUTOCOMMIT:", val)
+
+ # Set autocommit via set_option
+ await conn.set_option({ibm_db.SQL_ATTR_AUTOCOMMIT: ibm_db.SQL_AUTOCOMMIT_ON})
+ val = await conn.get_option(ibm_db.SQL_ATTR_AUTOCOMMIT)
+ print("After set ON, SQL_ATTR_AUTOCOMMIT:", val)
+
+ await conn.close()
+ asyncio.run(main())
+
+#__END__
+#__LUW_EXPECTED__
+#SQL_ATTR_AUTOCOMMIT: 0
+#After set ON, SQL_ATTR_AUTOCOMMIT: 1
+#__ZOS_EXPECTED__
+#SQL_ATTR_AUTOCOMMIT: 0
+#After set ON, SQL_ATTR_AUTOCOMMIT: 1
+#__SYSTEMI_EXPECTED__
+#SQL_ATTR_AUTOCOMMIT: 0
+#After set ON, SQL_ATTR_AUTOCOMMIT: 1
+#__IDS_EXPECTED__
+#SQL_ATTR_AUTOCOMMIT: 0
+#After set ON, SQL_ATTR_AUTOCOMMIT: 1
diff --git a/asyncio_testsuite/test_15_async_schema.py b/asyncio_testsuite/test_15_async_schema.py
new file mode 100644
index 00000000..c4ae637b
--- /dev/null
+++ b/asyncio_testsuite/test_15_async_schema.py
@@ -0,0 +1,55 @@
+from __future__ import print_function
+import asyncio
+import sys
+import unittest
+import config
+from ibm_db_dbi import AsyncConnection
+from testfunctions import IbmDbTestFunctions
+
+
+class IbmDbTestCase(unittest.TestCase):
+
+ def test_15_async_schema(self):
+ obj = IbmDbTestFunctions()
+ obj.assert_expectf(self.run_test_15)
+
+ def run_test_15(self):
+ async def main():
+ conn = await AsyncConnection.connect(
+ "DATABASE=%s;HOSTNAME=%s;PORT=%d;PROTOCOL=TCPIP;UID=%s;PWD=%s;" % (
+ config.database, config.hostname, config.port,
+ config.user, config.password),
+ '', '')
+
+ schema = await conn.get_current_schema()
+ print("Current schema:", schema.upper())
+
+ await conn.set_current_schema("TESTSCHEMA")
+ schema = await conn.get_current_schema()
+ print("After set schema:", schema)
+
+ # Reset back to original
+ await conn.set_current_schema(config.user.upper())
+ schema = await conn.get_current_schema()
+ print("After reset schema:", schema.upper())
+
+ await conn.close()
+ asyncio.run(main())
+
+#__END__
+#__LUW_EXPECTED__
+#Current schema: %s
+#After set schema: TESTSCHEMA
+#After reset schema: %s
+#__ZOS_EXPECTED__
+#Current schema: %s
+#After set schema: TESTSCHEMA
+#After reset schema: %s
+#__SYSTEMI_EXPECTED__
+#Current schema: %s
+#After set schema: TESTSCHEMA
+#After reset schema: %s
+#__IDS_EXPECTED__
+#Current schema: %s
+#After set schema: TESTSCHEMA
+#After reset schema: %s
diff --git a/asyncio_testsuite/test_16_async_server_info.py b/asyncio_testsuite/test_16_async_server_info.py
new file mode 100644
index 00000000..70776b6d
--- /dev/null
+++ b/asyncio_testsuite/test_16_async_server_info.py
@@ -0,0 +1,38 @@
+from __future__ import print_function
+import asyncio
+import sys
+import unittest
+import config
+from ibm_db_dbi import AsyncConnection
+from testfunctions import IbmDbTestFunctions
+
+
+class IbmDbTestCase(unittest.TestCase):
+
+ def test_16_async_server_info(self):
+ obj = IbmDbTestFunctions()
+ obj.assert_expectf(self.run_test_16)
+
+ def run_test_16(self):
+ async def main():
+ conn = await AsyncConnection.connect(
+ "DATABASE=%s;HOSTNAME=%s;PORT=%d;PROTOCOL=TCPIP;UID=%s;PWD=%s;" % (
+ config.database, config.hostname, config.port,
+ config.user, config.password),
+ '', '')
+
+ info = await conn.server_info()
+ print("Server info (DBMS_NAME, DBMS_VER):", info)
+
+ await conn.close()
+ asyncio.run(main())
+
+#__END__
+#__LUW_EXPECTED__
+#Server info (DBMS_NAME, DBMS_VER): (%s)
+#__ZOS_EXPECTED__
+#Server info (DBMS_NAME, DBMS_VER): (%s)
+#__SYSTEMI_EXPECTED__
+#Server info (DBMS_NAME, DBMS_VER): (%s)
+#__IDS_EXPECTED__
+#Server info (DBMS_NAME, DBMS_VER): (%s)
diff --git a/asyncio_testsuite/test_17_async_metadata.py b/asyncio_testsuite/test_17_async_metadata.py
new file mode 100644
index 00000000..5813909f
--- /dev/null
+++ b/asyncio_testsuite/test_17_async_metadata.py
@@ -0,0 +1,107 @@
+from __future__ import print_function
+import asyncio
+import sys
+import unittest
+import config
+from ibm_db_dbi import AsyncConnection
+from testfunctions import IbmDbTestFunctions
+
+
+class IbmDbTestCase(unittest.TestCase):
+
+ def test_17_async_metadata(self):
+ obj = IbmDbTestFunctions()
+ obj.assert_expectf(self.run_test_17)
+
+ def run_test_17(self):
+ async def main():
+ conn = await AsyncConnection.connect(
+ "DATABASE=%s;HOSTNAME=%s;PORT=%d;PROTOCOL=TCPIP;UID=%s;PWD=%s;" % (
+ config.database, config.hostname, config.port,
+ config.user, config.password),
+ '', '')
+
+ schema = config.user.upper()
+
+ # Tables
+ tables = await conn.tables(schema_name=schema, table_name="STAFF")
+ print("Tables (%d found):" % len(tables))
+ for t in tables[:3]:
+ print(" ", t.get("TABLE_NAME", t))
+
+ # Columns
+ columns = await conn.columns(schema_name=schema, table_name="STAFF")
+ print("Columns (%d found):" % len(columns))
+ for c in columns[:5]:
+ print(" ", c.get("COLUMN_NAME", c), "-", c.get("TYPE_NAME", ""))
+
+ # Primary keys
+ pks = await conn.primary_keys(schema_name=schema, table_name="STAFF")
+ print("Primary keys (%d found):" % len(pks))
+ for pk in pks:
+ print(" ", pk.get("COLUMN_NAME", pk))
+
+ # Indexes
+ idxs = await conn.indexes(schema_name=schema, table_name="STAFF")
+ print("Indexes (%d found):" % len(idxs))
+ for idx in idxs[:5]:
+ print(" ", idx.get("INDEX_NAME", idx))
+
+ # Foreign keys
+ fks = await conn.foreign_keys(schema_name=schema, table_name="STAFF")
+ print("Foreign keys (%d found):" % len(fks))
+ for fk in fks[:3]:
+ print(" ", fk.get("FK_NAME", fk))
+
+ await conn.close()
+ asyncio.run(main())
+
+#__END__
+#__LUW_EXPECTED__
+#Tables (%d found):
+# STAFF
+#Columns (%d found):
+# ID - SMALLINT
+# NAME - VARCHAR
+# DEPT - SMALLINT
+# JOB - CHAR
+# YEARS - SMALLINT
+#Primary keys (%d found):
+#Indexes (%d found):
+#Foreign keys (%d found):
+#__ZOS_EXPECTED__
+#Tables (%d found):
+# STAFF
+#Columns (%d found):
+# ID - SMALLINT
+# NAME - VARCHAR
+# DEPT - SMALLINT
+# JOB - CHAR
+# YEARS - SMALLINT
+#Primary keys (%d found):
+#Indexes (%d found):
+#Foreign keys (%d found):
+#__SYSTEMI_EXPECTED__
+#Tables (%d found):
+# STAFF
+#Columns (%d found):
+# ID - SMALLINT
+# NAME - VARCHAR
+# DEPT - SMALLINT
+# JOB - CHAR
+# YEARS - SMALLINT
+#Primary keys (%d found):
+#Indexes (%d found):
+#Foreign keys (%d found):
+#__IDS_EXPECTED__
+#Tables (%d found):
+# STAFF
+#Columns (%d found):
+# ID - SMALLINT
+# NAME - VARCHAR
+# DEPT - SMALLINT
+# JOB - CHAR
+# YEARS - SMALLINT
+#Primary keys (%d found):
+#Indexes (%d found):
+#Foreign keys (%d found):
diff --git a/asyncio_testsuite/test_18_async_fix_return_type.py b/asyncio_testsuite/test_18_async_fix_return_type.py
new file mode 100644
index 00000000..263448f9
--- /dev/null
+++ b/asyncio_testsuite/test_18_async_fix_return_type.py
@@ -0,0 +1,67 @@
+from __future__ import print_function
+import asyncio
+import sys
+import unittest
+import config
+from ibm_db_dbi import AsyncConnection
+from testfunctions import IbmDbTestFunctions
+
+
+class IbmDbTestCase(unittest.TestCase):
+
+ def test_18_async_fix_return_type(self):
+ obj = IbmDbTestFunctions()
+ obj.assert_expect(self.run_test_18)
+
+ def run_test_18(self):
+ async def main():
+ conn = await AsyncConnection.connect(
+ "DATABASE=%s;HOSTNAME=%s;PORT=%d;PROTOCOL=TCPIP;UID=%s;PWD=%s;" % (
+ config.database, config.hostname, config.port,
+ config.user, config.password),
+ '', '')
+ await conn.set_autocommit(True)
+
+ # Enable FIX_RETURN_TYPE
+ await conn.set_fix_return_type(True)
+ cursor = await conn.cursor()
+
+ await cursor.execute("SELECT SALARY, COMM FROM STAFF WHERE ID = 20")
+ row = await cursor.fetchone()
+ print("With FIX_RETURN_TYPE=True:", row)
+ print(" SALARY type:", type(row[0]))
+
+ # Disable FIX_RETURN_TYPE
+ await conn.set_fix_return_type(False)
+ cursor2 = await conn.cursor()
+ await cursor2.execute("SELECT SALARY, COMM FROM STAFF WHERE ID = 20")
+ row2 = await cursor2.fetchone()
+ print("With FIX_RETURN_TYPE=False:", row2)
+ print(" SALARY type:", type(row2[0]))
+
+ await cursor.close()
+ await cursor2.close()
+ await conn.close()
+ asyncio.run(main())
+
+#__END__
+#__LUW_EXPECTED__
+#With FIX_RETURN_TYPE=True: (Decimal('18171.25'), Decimal('612.45'))
+# SALARY type:
+#With FIX_RETURN_TYPE=False: ('18171.25', '612.45')
+# SALARY type:
+#__ZOS_EXPECTED__
+#With FIX_RETURN_TYPE=True: (Decimal('18171.25'), Decimal('612.45'))
+# SALARY type:
+#With FIX_RETURN_TYPE=False: ('18171.25', '612.45')
+# SALARY type:
+#__SYSTEMI_EXPECTED__
+#With FIX_RETURN_TYPE=True: (Decimal('18171.25'), Decimal('612.45'))
+# SALARY type:
+#With FIX_RETURN_TYPE=False: ('18171.25', '612.45')
+# SALARY type:
+#__IDS_EXPECTED__
+#With FIX_RETURN_TYPE=True: (Decimal('18171.25'), Decimal('612.45'))
+# SALARY type:
+#With FIX_RETURN_TYPE=False: ('18171.25', '612.45')
+# SALARY type:
diff --git a/asyncio_testsuite/test_19_async_dml.py b/asyncio_testsuite/test_19_async_dml.py
new file mode 100644
index 00000000..db16613e
--- /dev/null
+++ b/asyncio_testsuite/test_19_async_dml.py
@@ -0,0 +1,80 @@
+from __future__ import print_function
+import asyncio
+import sys
+import unittest
+import config
+from ibm_db_dbi import AsyncConnection
+from testfunctions import IbmDbTestFunctions
+
+
+class IbmDbTestCase(unittest.TestCase):
+
+ def test_19_async_dml(self):
+ obj = IbmDbTestFunctions()
+ obj.assert_expect(self.run_test_19)
+
+ def run_test_19(self):
+ async def main():
+ conn = await AsyncConnection.connect(
+ "DATABASE=%s;HOSTNAME=%s;PORT=%d;PROTOCOL=TCPIP;UID=%s;PWD=%s;" % (
+ config.database, config.hostname, config.port,
+ config.user, config.password),
+ '', '')
+ await conn.set_autocommit(True)
+ cursor = await conn.cursor()
+
+ # Setup
+ try:
+ await cursor.execute("DROP TABLE ASYNC_TEST_DML")
+ except Exception:
+ pass
+ await cursor.execute("CREATE TABLE ASYNC_TEST_DML (ID INT, NAME VARCHAR(30))")
+
+ # INSERT
+ await cursor.execute("INSERT INTO ASYNC_TEST_DML VALUES (1, 'Alice')")
+ print("Insert rowcount:", cursor.rowcount)
+
+ await cursor.execute("INSERT INTO ASYNC_TEST_DML VALUES (2, 'Bob')")
+ await cursor.execute("INSERT INTO ASYNC_TEST_DML VALUES (3, 'Charlie')")
+
+ # UPDATE
+ await cursor.execute("UPDATE ASYNC_TEST_DML SET NAME = 'ALICE_UPDATED' WHERE ID = 1")
+ print("Update rowcount:", cursor.rowcount)
+
+ # DELETE
+ await cursor.execute("DELETE FROM ASYNC_TEST_DML WHERE ID = 2")
+ print("Delete rowcount:", cursor.rowcount)
+
+ # Verify
+ await cursor.execute("SELECT * FROM ASYNC_TEST_DML ORDER BY ID")
+ rows = await cursor.fetchall()
+ print("Remaining rows:", rows)
+
+ # Cleanup
+ await cursor.execute("DROP TABLE ASYNC_TEST_DML")
+
+ await cursor.close()
+ await conn.close()
+ asyncio.run(main())
+
+#__END__
+#__LUW_EXPECTED__
+#Insert rowcount: 1
+#Update rowcount: 1
+#Delete rowcount: 1
+#Remaining rows: [(1, 'ALICE_UPDATED'), (3, 'Charlie')]
+#__ZOS_EXPECTED__
+#Insert rowcount: 1
+#Update rowcount: 1
+#Delete rowcount: 1
+#Remaining rows: [(1, 'ALICE_UPDATED'), (3, 'Charlie')]
+#__SYSTEMI_EXPECTED__
+#Insert rowcount: 1
+#Update rowcount: 1
+#Delete rowcount: 1
+#Remaining rows: [(1, 'ALICE_UPDATED'), (3, 'Charlie')]
+#__IDS_EXPECTED__
+#Insert rowcount: 1
+#Update rowcount: 1
+#Delete rowcount: 1
+#Remaining rows: [(1, 'ALICE_UPDATED'), (3, 'Charlie')]
diff --git a/asyncio_testsuite/test_20_async_nextset.py b/asyncio_testsuite/test_20_async_nextset.py
new file mode 100644
index 00000000..cc571fb0
--- /dev/null
+++ b/asyncio_testsuite/test_20_async_nextset.py
@@ -0,0 +1,107 @@
+from __future__ import print_function
+import asyncio
+import sys
+import unittest
+import config
+from ibm_db_dbi import AsyncConnection
+from testfunctions import IbmDbTestFunctions
+
+
+class IbmDbTestCase(unittest.TestCase):
+
+ def test_20_async_nextset(self):
+ obj = IbmDbTestFunctions()
+ obj.assert_expect(self.run_test_20)
+
+ def run_test_20(self):
+ async def main():
+ conn = await AsyncConnection.connect(
+ "DATABASE=%s;HOSTNAME=%s;PORT=%d;PROTOCOL=TCPIP;UID=%s;PWD=%s;" % (
+ config.database, config.hostname, config.port,
+ config.user, config.password),
+ '', '')
+ cursor = await conn.cursor()
+
+ # Create a procedure that returns two result sets
+ try:
+ await cursor.execute("DROP PROCEDURE ASYNC_TEST_MULTI_RS")
+ except Exception:
+ pass
+ await cursor.execute("""
+ CREATE PROCEDURE ASYNC_TEST_MULTI_RS()
+ LANGUAGE SQL
+ DYNAMIC RESULT SETS 2
+ BEGIN
+ DECLARE c1 CURSOR WITH RETURN FOR
+ SELECT ID, NAME FROM STAFF FETCH FIRST 3 ROWS ONLY;
+ DECLARE c2 CURSOR WITH RETURN FOR
+ SELECT DEPTNO, DEPTNAME FROM DEPARTMENT FETCH FIRST 3 ROWS ONLY;
+ OPEN c1;
+ OPEN c2;
+ END
+ """)
+
+ # Call the stored procedure
+ await cursor.callproc("ASYNC_TEST_MULTI_RS")
+
+ # First result set
+ print("Result set 1:")
+ rows1 = await cursor.fetchall()
+ for r in rows1:
+ print(" ", r)
+
+ # Move to next result set
+ has_next = await cursor.nextset()
+ print("nextset returned:", has_next)
+
+ if has_next:
+ print("Result set 2:")
+ rows2 = await cursor.fetchall()
+ for r in rows2:
+ print(" ", r)
+
+ await cursor.close()
+ await conn.close()
+ asyncio.run(main())
+
+#__END__
+#__LUW_EXPECTED__
+#Result set 1:
+# (10, 'Sanders')
+# (20, 'Pernal')
+# (30, 'Marenghi')
+#nextset returned: True
+#Result set 2:
+# ('A00', 'SPIFFY COMPUTER SERVICE DIV.')
+# ('B01', 'PLANNING')
+# ('C01', 'INFORMATION CENTER')
+#__ZOS_EXPECTED__
+#Result set 1:
+# (10, 'Sanders')
+# (20, 'Pernal')
+# (30, 'Marenghi')
+#nextset returned: True
+#Result set 2:
+# ('A00', 'SPIFFY COMPUTER SERVICE DIV.')
+# ('B01', 'PLANNING')
+# ('C01', 'INFORMATION CENTER')
+#__SYSTEMI_EXPECTED__
+#Result set 1:
+# (10, 'Sanders')
+# (20, 'Pernal')
+# (30, 'Marenghi')
+#nextset returned: True
+#Result set 2:
+# ('A00', 'SPIFFY COMPUTER SERVICE DIV.')
+# ('B01', 'PLANNING')
+# ('C01', 'INFORMATION CENTER')
+#__IDS_EXPECTED__
+#Result set 1:
+# (10, 'Sanders')
+# (20, 'Pernal')
+# (30, 'Marenghi')
+#nextset returned: True
+#Result set 2:
+# ('A00', 'SPIFFY COMPUTER SERVICE DIV.')
+# ('B01', 'PLANNING')
+# ('C01', 'INFORMATION CENTER')
diff --git a/asyncio_testsuite/test_21_async_error_handling.py b/asyncio_testsuite/test_21_async_error_handling.py
new file mode 100644
index 00000000..0b79b68c
--- /dev/null
+++ b/asyncio_testsuite/test_21_async_error_handling.py
@@ -0,0 +1,91 @@
+from __future__ import print_function
+import asyncio
+import sys
+import unittest
+import ibm_db_dbi
+import config
+from testfunctions import IbmDbTestFunctions
+
+
+class IbmDbTestCase(unittest.TestCase):
+
+ def test_21_async_error_handling(self):
+ obj = IbmDbTestFunctions()
+ obj.assert_expectf(self.run_test_21)
+
+ def run_test_21(self):
+ async def main():
+ conn = None
+ try:
+ # Attempt connection
+ conn = await ibm_db_dbi.connect_async(
+ "DATABASE=%s;HOSTNAME=%s;PORT=%d;PROTOCOL=TCPIP;UID=%s;PWD=%s;" % (
+ config.database, config.hostname, config.port,
+ config.user, config.password),
+ '', '')
+ print("Connected:", conn)
+
+ # Run error APIs on a good connection
+ err_msg = await ibm_db_dbi.conn_errormsg_async(conn.conn_handler)
+ print("conn_errormsg_async (good conn):", repr(err_msg))
+
+ err_code = await ibm_db_dbi.conn_error_async(conn.conn_handler)
+ print("conn_error_async (good conn):", repr(err_code))
+
+ sqlcode = await ibm_db_dbi.get_sqlcode_async()
+ print("get_sqlcode_async:", repr(sqlcode))
+
+ except Exception as e:
+ # Connection itself failed
+ print("Connection failed:", e)
+
+ # Run error APIs without a valid conn_handler
+ try:
+ err_msg = await ibm_db_dbi.conn_errormsg_async(None)
+ print("conn_errormsg_async (failed conn):", repr(err_msg))
+ except Exception as e2:
+ print("conn_errormsg_async raised:", e2)
+
+ try:
+ err_code = await ibm_db_dbi.conn_error_async(None)
+ print("conn_error_async (failed conn):", repr(err_code))
+ except Exception as e2:
+ print("conn_error_async raised:", e2)
+
+ try:
+ sqlcode = await ibm_db_dbi.get_sqlcode_async()
+ print("get_sqlcode_async (failed conn):", repr(sqlcode))
+ except Exception as e2:
+ print("get_sqlcode_async raised:", e2)
+
+ finally:
+ if conn:
+ conn.close()
+ print("Connection closed.")
+ asyncio.run(main())
+
+#__END__
+#__LUW_EXPECTED__
+#Connected:
+#conn_errormsg_async (good conn): ''
+#conn_error_async (good conn): ''
+#get_sqlcode_async: ''
+#Connection closed.
+#__ZOS_EXPECTED__
+#Connected:
+#conn_errormsg_async (good conn): ''
+#conn_error_async (good conn): ''
+#get_sqlcode_async: ''
+#Connection closed.
+#__SYSTEMI_EXPECTED__
+#Connected:
+#conn_errormsg_async (good conn): ''
+#conn_error_async (good conn): ''
+#get_sqlcode_async: ''
+#Connection closed.
+#__IDS_EXPECTED__
+#Connected:
+#conn_errormsg_async (good conn): ''
+#conn_error_async (good conn): ''
+#get_sqlcode_async: ''
+#Connection closed.
diff --git a/asyncio_testsuite/test_22_async_scalar_bind_callproc.py b/asyncio_testsuite/test_22_async_scalar_bind_callproc.py
new file mode 100644
index 00000000..3a5df6d7
--- /dev/null
+++ b/asyncio_testsuite/test_22_async_scalar_bind_callproc.py
@@ -0,0 +1,64 @@
+from __future__ import print_function
+import asyncio
+import sys
+import unittest
+import ibm_db
+import config
+from ibm_db_dbi import AsyncConnection
+from testfunctions import IbmDbTestFunctions
+
+
+class IbmDbTestCase(unittest.TestCase):
+
+ def test_22_async_scalar_bind_callproc(self):
+ obj = IbmDbTestFunctions()
+ obj.assert_expectf(self.run_test_22)
+
+ def run_test_22(self):
+ async def main():
+ conn = await AsyncConnection.connect(
+ "DATABASE=%s;HOSTNAME=%s;PORT=%d;PROTOCOL=TCPIP;UID=%s;PWD=%s;" % (
+ config.database, config.hostname, config.port,
+ config.user, config.password),
+ '', '')
+ cursor = await conn.cursor()
+
+ # Create stored procedure
+ try:
+ await cursor.execute("""
+ CREATE PROCEDURE ASYNC_TEST_DOUBLE(IN val BIGINT, OUT result BIGINT)
+ LANGUAGE SQL
+ BEGIN
+ SET result = val * 2;
+ END
+ """)
+ except Exception:
+ pass
+
+ # Prepare + bind + execute
+ await cursor.prepare("CALL ASYNC_TEST_DOUBLE(?,?)")
+ await cursor.bind_param(1, 123456, ibm_db.SQL_PARAM_INPUT)
+ await cursor.bind_param(2, 0, ibm_db.SQL_PARAM_OUTPUT)
+ await cursor.execute()
+
+ result = await cursor.fetch_callproc()
+ print("Input: 123456")
+ print("fetch_callproc result:", result)
+
+ await cursor.close()
+ await conn.close()
+ asyncio.run(main())
+
+#__END__
+#__LUW_EXPECTED__
+#Input: 123456
+#fetch_callproc result: (, 123456, 246912)
+#__ZOS_EXPECTED__
+#Input: 123456
+#fetch_callproc result: (, 123456, 246912)
+#__SYSTEMI_EXPECTED__
+#Input: 123456
+#fetch_callproc result: (, 123456, 246912)
+#__IDS_EXPECTED__
+#Input: 123456
+#fetch_callproc result: (, 123456, 246912)
diff --git a/asyncio_testsuite/test_23_async_concurrent_queries.py b/asyncio_testsuite/test_23_async_concurrent_queries.py
new file mode 100644
index 00000000..15bc857e
--- /dev/null
+++ b/asyncio_testsuite/test_23_async_concurrent_queries.py
@@ -0,0 +1,63 @@
+from __future__ import print_function
+import asyncio
+import sys
+import unittest
+import config
+from ibm_db_dbi import AsyncConnection
+from testfunctions import IbmDbTestFunctions
+
+
+class IbmDbTestCase(unittest.TestCase):
+
+ def test_23_async_concurrent_queries(self):
+ obj = IbmDbTestFunctions()
+ obj.assert_expect(self.run_test_23)
+
+ def run_test_23(self):
+ async def query_staff(conn, staff_id):
+ cursor = await conn.cursor()
+ await cursor.execute(
+ "SELECT ID, NAME, JOB FROM STAFF WHERE ID = ?",
+ (staff_id,)
+ )
+ row = await cursor.fetchone()
+ await cursor.close()
+ return row
+
+ async def main():
+ conn = await AsyncConnection.connect(
+ "DATABASE=%s;HOSTNAME=%s;PORT=%d;PROTOCOL=TCPIP;UID=%s;PWD=%s;" % (
+ config.database, config.hostname, config.port,
+ config.user, config.password),
+ '', '')
+
+ # Run 3 queries concurrently
+ results = await asyncio.gather(
+ query_staff(conn, 10),
+ query_staff(conn, 20),
+ query_staff(conn, 30),
+ )
+
+ for r in results:
+ print(r)
+
+ await conn.close()
+ asyncio.run(main())
+
+#__END__
+#__LUW_EXPECTED__
+#(10, 'Sanders', 'Mgr ')
+#(20, 'Pernal', 'Sales')
+#(30, 'Marenghi', 'Mgr ')
+#__ZOS_EXPECTED__
+#(10, 'Sanders', 'Mgr ')
+#(20, 'Pernal', 'Sales')
+#(30, 'Marenghi', 'Mgr ')
+#__SYSTEMI_EXPECTED__
+#(10, 'Sanders', 'Mgr ')
+#(20, 'Pernal', 'Sales')
+#(30, 'Marenghi', 'Mgr ')
+#__IDS_EXPECTED__
+#(10, 'Sanders', 'Mgr ')
+#(20, 'Pernal', 'Sales')
+#(30, 'Marenghi', 'Mgr ')
diff --git a/asyncio_testsuite/test_24_async_error_functions.py b/asyncio_testsuite/test_24_async_error_functions.py
new file mode 100644
index 00000000..3b61694b
--- /dev/null
+++ b/asyncio_testsuite/test_24_async_error_functions.py
@@ -0,0 +1,54 @@
+from __future__ import print_function
+import asyncio
+import sys
+import unittest
+import ibm_db_dbi
+import config
+from testfunctions import IbmDbTestFunctions
+
+
+class IbmDbTestCase(unittest.TestCase):
+
+ def test_24_async_error_functions(self):
+ obj = IbmDbTestFunctions()
+ obj.assert_expect(self.run_test_24)
+
+ def run_test_24(self):
+ async def main():
+ # Good connection first
+ conn = await ibm_db_dbi.connect_async(
+ "DATABASE=%s;HOSTNAME=%s;PORT=%d;PROTOCOL=TCPIP;UID=%s;PWD=%s;" % (
+ config.database, config.hostname, config.port,
+ config.user, config.password),
+ '', '')
+
+ # Check error functions on a good connection (pass raw conn_handler)
+ err_msg = await ibm_db_dbi.conn_errormsg_async(conn.conn_handler)
+ print("conn_errormsg_async (good conn):", repr(err_msg))
+
+ err_code = await ibm_db_dbi.conn_error_async(conn.conn_handler)
+ print("conn_error_async (good conn):", repr(err_code))
+
+ sqlcode = await ibm_db_dbi.get_sqlcode_async()
+ print("get_sqlcode_async:", repr(sqlcode))
+
+ conn.close()
+ asyncio.run(main())
+
+#__END__
+#__LUW_EXPECTED__
+#conn_errormsg_async (good conn): ''
+#conn_error_async (good conn): ''
+#get_sqlcode_async: ''
+#__ZOS_EXPECTED__
+#conn_errormsg_async (good conn): ''
+#conn_error_async (good conn): ''
+#get_sqlcode_async: ''
+#__SYSTEMI_EXPECTED__
+#conn_errormsg_async (good conn): ''
+#conn_error_async (good conn): ''
+#get_sqlcode_async: ''
+#__IDS_EXPECTED__
+#conn_errormsg_async (good conn): ''
+#conn_error_async (good conn): ''
+#get_sqlcode_async: ''
diff --git a/asyncio_testsuite/test_25_async_createdb_dropdb.py b/asyncio_testsuite/test_25_async_createdb_dropdb.py
new file mode 100644
index 00000000..1410b4c4
--- /dev/null
+++ b/asyncio_testsuite/test_25_async_createdb_dropdb.py
@@ -0,0 +1,104 @@
+from __future__ import print_function
+import asyncio
+import sys
+import os
+import unittest
+import ibm_db
+import ibm_db_dbi
+import config
+from testfunctions import IbmDbTestFunctions
+
+
+class IbmDbTestCase(unittest.TestCase):
+ @unittest.skipIf(os.environ.get("CI", False), "Test fails in CI")
+ def test_25_async_createdb_dropdb(self):
+ obj = IbmDbTestFunctions()
+ if ((obj.server.DBMS_NAME == "DB2") or (obj.server.DBMS_NAME[0:3] != "DB2")):
+ raise unittest.SkipTest("createdb, dropdb not Supported")
+ obj.assert_expect(self.run_test_25)
+
+ def run_test_25(self):
+ async def main():
+ database = 'test001'
+ conn_str = "DATABASE=%s;HOSTNAME=%s;PORT=%d;PROTOCOL=TCPIP;UID=%s;PWD=%s;" % (database, config.hostname, config.port, config.user, config.password)
+ dsn_attach = "attach=true;HOSTNAME=%s;PORT=%d;PROTOCOL=TCPIP;" % (config.hostname, config.port)
+
+ # Clean up if test database already exists
+ conn = None
+ try:
+ conn = ibm_db.connect(conn_str, '', '')
+ except:
+ pass
+ if conn:
+ ibm_db.close(conn)
+ conn = None
+ try:
+ await ibm_db_dbi.dropdb_async(database, dsn_attach, config.user, config.password)
+ except:
+ print('Errors occurred during drop database')
+
+ try:
+ # create database
+ rc = await ibm_db_dbi.createdb_async(database, dsn_attach, config.user, config.password)
+ if rc:
+ conn = ibm_db.connect(conn_str, '', '')
+ if conn:
+ print('database created sucessfully')
+ ibm_db.close(conn)
+ conn = None
+ else:
+ print('database is not created')
+ else:
+ print('Errors occurred during create database')
+
+ # drop database
+ rc = await ibm_db_dbi.dropdb_async(database, dsn_attach, config.user, config.password)
+ if rc:
+ try:
+ conn = ibm_db.connect(conn_str, '', '')
+ except:
+ print('datbase droped sucessfully')
+ if conn:
+ print('Errors occurred during drop database')
+ ibm_db.close(conn)
+ conn = None
+ else:
+ print('Errors occurred during delete database')
+
+ # create database with codeset option
+ rc = await ibm_db_dbi.createdb_async(database, dsn_attach, config.user, config.password, codeset='iso88591')
+ if rc:
+ conn = ibm_db.connect(conn_str, '', '')
+ server_info = ibm_db.server_info(conn)
+ if conn and (server_info.DB_CODEPAGE == 819):
+ print('database with codeset created sucessfully')
+ ibm_db.close(conn)
+ conn = None
+ else:
+ print('database is not created')
+ else:
+ print('Errors occurred during create database')
+
+ # drop database
+ rc = await ibm_db_dbi.dropdb_async(database, dsn_attach, config.user, config.password)
+ if rc:
+ try:
+ conn = ibm_db.connect(conn_str, '', '')
+ except:
+ print('datbase droped sucessfully')
+ if conn:
+ print('Errors occurred during drop database')
+ ibm_db.close(conn)
+ conn = None
+ else:
+ print('Errors occurred during drop database')
+ except:
+ print(ibm_db.conn_errormsg())
+ asyncio.run(main())
+
+#__END__
+#__LUW_EXPECTED__
+#database created sucessfully
+#datbase droped sucessfully
+#database with codeset created sucessfully
+#datbase droped sucessfully
diff --git a/asyncio_testsuite/test_26_async_inout_param.py b/asyncio_testsuite/test_26_async_inout_param.py
new file mode 100644
index 00000000..090b124f
--- /dev/null
+++ b/asyncio_testsuite/test_26_async_inout_param.py
@@ -0,0 +1,62 @@
+from __future__ import print_function
+import asyncio
+import sys
+import unittest
+import ibm_db
+import config
+from ibm_db_dbi import AsyncConnection
+from testfunctions import IbmDbTestFunctions
+
+
+class IbmDbTestCase(unittest.TestCase):
+
+ def test_26_async_inout_param(self):
+ obj = IbmDbTestFunctions()
+ obj.assert_expectf(self.run_test_26)
+
+ def run_test_26(self):
+ async def main():
+ conn = await AsyncConnection.connect(
+ "DATABASE=%s;HOSTNAME=%s;PORT=%d;PROTOCOL=TCPIP;UID=%s;PWD=%s;" % (
+ config.database, config.hostname, config.port,
+ config.user, config.password),
+ '', '')
+ cursor = await conn.cursor()
+
+ # Create stored procedure with INOUT
+ try:
+ await cursor.execute("""
+ CREATE PROCEDURE ASYNC_TEST_INOUT(INOUT val INTEGER)
+ LANGUAGE SQL
+ BEGIN
+ SET val = val + 100;
+ END
+ """)
+ except Exception:
+ pass
+
+ await cursor.prepare("CALL ASYNC_TEST_INOUT(?)")
+ await cursor.bind_param(1, 42, ibm_db.SQL_PARAM_INPUT_OUTPUT)
+ await cursor.execute()
+
+ result = await cursor.fetch_callproc()
+ print("Input: 42, Expected output: 142")
+ print("fetch_callproc result:", result)
+
+ await cursor.close()
+ await conn.close()
+ asyncio.run(main())
+
+#__END__
+#__LUW_EXPECTED__
+#Input: 42, Expected output: 142
+#fetch_callproc result: (, 142)
+#__ZOS_EXPECTED__
+#Input: 42, Expected output: 142
+#fetch_callproc result: (, 142)
+#__SYSTEMI_EXPECTED__
+#Input: 42, Expected output: 142
+#fetch_callproc result: (, 142)
+#__IDS_EXPECTED__
+#Input: 42, Expected output: 142
+#fetch_callproc result: (, 142)
diff --git a/asyncio_testsuite/test_27_async_bind_datatypes.py b/asyncio_testsuite/test_27_async_bind_datatypes.py
new file mode 100644
index 00000000..fae314c2
--- /dev/null
+++ b/asyncio_testsuite/test_27_async_bind_datatypes.py
@@ -0,0 +1,83 @@
+from __future__ import print_function
+import asyncio
+import sys
+import unittest
+import config
+from ibm_db_dbi import AsyncConnection
+from testfunctions import IbmDbTestFunctions
+
+
+class IbmDbTestCase(unittest.TestCase):
+
+ def test_27_async_bind_datatypes(self):
+ obj = IbmDbTestFunctions()
+ obj.assert_expect(self.run_test_27)
+
+ def run_test_27(self):
+ async def main():
+ conn = await AsyncConnection.connect(
+ "DATABASE=%s;HOSTNAME=%s;PORT=%d;PROTOCOL=TCPIP;UID=%s;PWD=%s;" % (
+ config.database, config.hostname, config.port,
+ config.user, config.password),
+ '', '')
+ await conn.set_autocommit(True)
+ cursor = await conn.cursor()
+
+ # Create temp table with multiple types
+ try:
+ await cursor.execute("DROP TABLE ASYNC_TEST_TYPES")
+ except Exception:
+ pass
+ await cursor.execute("""
+ CREATE TABLE ASYNC_TEST_TYPES (
+ ID INTEGER,
+ NAME VARCHAR(50),
+ AMOUNT DECIMAL(10,2),
+ ACTIVE SMALLINT
+ )
+ """)
+
+ # Prepare and bind different types
+ await cursor.prepare("INSERT INTO ASYNC_TEST_TYPES VALUES (?, ?, ?, ?)")
+ await cursor.bind_param(1, 1)
+ await cursor.bind_param(2, 'Test Name')
+ await cursor.bind_param(3, '99.95')
+ await cursor.bind_param(4, 1)
+ await cursor.execute()
+
+ # Verify
+ await cursor.execute("SELECT * FROM ASYNC_TEST_TYPES")
+ row = await cursor.fetchone()
+ print("Inserted row:", row)
+ print(" ID type:", type(row[0]))
+ print(" NAME type:", type(row[1]))
+ print(" AMOUNT type:", type(row[2]))
+
+ # Cleanup
+ await cursor.execute("DROP TABLE ASYNC_TEST_TYPES")
+
+ await cursor.close()
+ await conn.close()
+ asyncio.run(main())
+
+#__END__
+#__LUW_EXPECTED__
+#Inserted row: (1, 'Test Name', Decimal('99.95'), 1)
+# ID type:
+# NAME type:
+# AMOUNT type:
+#__ZOS_EXPECTED__
+#Inserted row: (1, 'Test Name', Decimal('99.95'), 1)
+# ID type:
+# NAME type:
+# AMOUNT type:
+#__SYSTEMI_EXPECTED__
+#Inserted row: (1, 'Test Name', Decimal('99.95'), 1)
+# ID type:
+# NAME type:
+# AMOUNT type:
+#__IDS_EXPECTED__
+#Inserted row: (1, 'Test Name', Decimal('99.95'), 1)
+# ID type:
+# NAME type:
+# AMOUNT type:
diff --git a/asyncio_testsuite/test_28_async_cursor_description.py b/asyncio_testsuite/test_28_async_cursor_description.py
new file mode 100644
index 00000000..5ec01e61
--- /dev/null
+++ b/asyncio_testsuite/test_28_async_cursor_description.py
@@ -0,0 +1,71 @@
+from __future__ import print_function
+import asyncio
+import sys
+import unittest
+import config
+from ibm_db_dbi import AsyncConnection
+from testfunctions import IbmDbTestFunctions
+
+
+class IbmDbTestCase(unittest.TestCase):
+
+ def test_28_async_cursor_description(self):
+ obj = IbmDbTestFunctions()
+ obj.assert_expectf(self.run_test_28)
+
+ def run_test_28(self):
+ async def main():
+ conn = await AsyncConnection.connect(
+ "DATABASE=%s;HOSTNAME=%s;PORT=%d;PROTOCOL=TCPIP;UID=%s;PWD=%s;" % (
+ config.database, config.hostname, config.port,
+ config.user, config.password),
+ '', '')
+ cursor = await conn.cursor()
+
+ await cursor.execute("SELECT ID, NAME, JOB, SALARY FROM STAFF FETCH FIRST 1 ROWS ONLY")
+
+ # Description is populated after execute
+ desc = cursor.description
+ print("cursor.description:")
+ if desc:
+ for col in desc:
+ print(" %s" % (col,))
+ else:
+ print(" description is None (may need async fetch of description)")
+
+ row = await cursor.fetchone()
+ print("First row:", row)
+
+ await cursor.close()
+ await conn.close()
+ asyncio.run(main())
+
+#__END__
+#__LUW_EXPECTED__
+#cursor.description:
+# ['ID', DBAPITypeObject(%s), 6, 6, 5, 0, False]
+# ['NAME', DBAPITypeObject(%s), 9, 9, 9, 0, True]
+# ['JOB', DBAPITypeObject(%s), 5, 5, 5, 0, True]
+# ['SALARY', DBAPITypeObject(%s), 9, 9, 7, 2, True]
+#First row: (10, 'Sanders', 'Mgr ', Decimal('18357.50'))
+#__ZOS_EXPECTED__
+#cursor.description:
+# ['ID', DBAPITypeObject(%s), 6, 6, 5, 0, False]
+# ['NAME', DBAPITypeObject(%s), 9, 9, 9, 0, True]
+# ['JOB', DBAPITypeObject(%s), 5, 5, 5, 0, True]
+# ['SALARY', DBAPITypeObject(%s), 9, 9, 7, 2, True]
+#First row: (10, 'Sanders', 'Mgr ', Decimal('18357.50'))
+#__SYSTEMI_EXPECTED__
+#cursor.description:
+# ['ID', DBAPITypeObject(%s), 6, 6, 5, 0, False]
+# ['NAME', DBAPITypeObject(%s), 9, 9, 9, 0, True]
+# ['JOB', DBAPITypeObject(%s), 5, 5, 5, 0, True]
+# ['SALARY', DBAPITypeObject(%s), 9, 9, 7, 2, True]
+#First row: (10, 'Sanders', 'Mgr ', Decimal('18357.50'))
+#__IDS_EXPECTED__
+#cursor.description:
+# ['ID', DBAPITypeObject(%s), 6, 6, 5, 0, False]
+# ['NAME', DBAPITypeObject(%s), 9, 9, 9, 0, True]
+# ['JOB', DBAPITypeObject(%s), 5, 5, 5, 0, True]
+# ['SALARY', DBAPITypeObject(%s), 9, 9, 7, 2, True]
+#First row: (10, 'Sanders', 'Mgr ', Decimal('18357.50'))
diff --git a/asyncio_testsuite/test_29_async_reprepare.py b/asyncio_testsuite/test_29_async_reprepare.py
new file mode 100644
index 00000000..e12b05c5
--- /dev/null
+++ b/asyncio_testsuite/test_29_async_reprepare.py
@@ -0,0 +1,62 @@
+from __future__ import print_function
+import asyncio
+import sys
+import unittest
+import config
+from ibm_db_dbi import AsyncConnection
+from testfunctions import IbmDbTestFunctions
+
+
+class IbmDbTestCase(unittest.TestCase):
+
+ def test_29_async_reprepare(self):
+ obj = IbmDbTestFunctions()
+ obj.assert_expect(self.run_test_29)
+
+ def run_test_29(self):
+ async def main():
+ conn = await AsyncConnection.connect(
+ "DATABASE=%s;HOSTNAME=%s;PORT=%d;PROTOCOL=TCPIP;UID=%s;PWD=%s;" % (
+ config.database, config.hostname, config.port,
+ config.user, config.password),
+ '', '')
+ cursor = await conn.cursor()
+
+ # First query
+ await cursor.execute("SELECT ID, NAME FROM STAFF FETCH FIRST 2 ROWS ONLY")
+ rows1 = await cursor.fetchall()
+ print("Query 1:", rows1)
+
+ # Re-execute with different SQL on same cursor
+ await cursor.execute("SELECT DEPTNO, DEPTNAME FROM DEPARTMENT FETCH FIRST 2 ROWS ONLY")
+ rows2 = await cursor.fetchall()
+ print("Query 2:", rows2)
+
+ # Re-prepare with bind_param
+ await cursor.prepare("SELECT ID, NAME FROM STAFF WHERE ID = ?")
+ await cursor.bind_param(1, 30)
+ await cursor.execute()
+ row = await cursor.fetchone()
+ print("Query 3 (prepared):", row)
+
+ await cursor.close()
+ await conn.close()
+ asyncio.run(main())
+
+#__END__
+#__LUW_EXPECTED__
+#Query 1: [(10, 'Sanders'), (20, 'Pernal')]
+#Query 2: [('A00', 'SPIFFY COMPUTER SERVICE DIV.'), ('B01', 'PLANNING')]
+#Query 3 (prepared): (30, 'Marenghi')
+#__ZOS_EXPECTED__
+#Query 1: [(10, 'Sanders'), (20, 'Pernal')]
+#Query 2: [('A00', 'SPIFFY COMPUTER SERVICE DIV.'), ('B01', 'PLANNING')]
+#Query 3 (prepared): (30, 'Marenghi')
+#__SYSTEMI_EXPECTED__
+#Query 1: [(10, 'Sanders'), (20, 'Pernal')]
+#Query 2: [('A00', 'SPIFFY COMPUTER SERVICE DIV.'), ('B01', 'PLANNING')]
+#Query 3 (prepared): (30, 'Marenghi')
+#__IDS_EXPECTED__
+#Query 1: [(10, 'Sanders'), (20, 'Pernal')]
+#Query 2: [('A00', 'SPIFFY COMPUTER SERVICE DIV.'), ('B01', 'PLANNING')]
+#Query 3 (prepared): (30, 'Marenghi')
diff --git a/asyncio_testsuite/test_30_sync_vs_async.py b/asyncio_testsuite/test_30_sync_vs_async.py
new file mode 100644
index 00000000..e7917ca0
--- /dev/null
+++ b/asyncio_testsuite/test_30_sync_vs_async.py
@@ -0,0 +1,97 @@
+from __future__ import print_function
+import asyncio
+import sys
+import unittest
+import ibm_db_dbi
+import config
+from ibm_db_dbi import AsyncConnection
+from testfunctions import IbmDbTestFunctions
+
+
+class IbmDbTestCase(unittest.TestCase):
+
+ def test_30_sync_vs_async(self):
+ obj = IbmDbTestFunctions()
+ obj.assert_expect(self.run_test_30)
+
+ def run_test_30(self):
+ def run_sync():
+ print("--- Sync ---")
+ conn = ibm_db_dbi.connect(
+ "DATABASE=%s;HOSTNAME=%s;PORT=%d;PROTOCOL=TCPIP;UID=%s;PWD=%s;" % (
+ config.database, config.hostname, config.port,
+ config.user, config.password),
+ '', '')
+ cursor = conn.cursor()
+
+ # Simple query
+ cursor.execute("SELECT ID, NAME FROM STAFF FETCH FIRST 3 ROWS ONLY")
+ rows = cursor.fetchall()
+ print("Sync fetchall:", rows)
+
+ # Prepare + bind_param + execute
+ cursor.prepare("SELECT ID, NAME FROM STAFF WHERE ID = ?")
+ cursor.bind_param(1, 20)
+ cursor.execute()
+ row = cursor.fetchone()
+ print("Sync bind_param result:", row)
+
+ cursor.close()
+ conn.close()
+
+ async def run_async():
+ print("--- Async ---")
+ conn = await AsyncConnection.connect(
+ "DATABASE=%s;HOSTNAME=%s;PORT=%d;PROTOCOL=TCPIP;UID=%s;PWD=%s;" % (
+ config.database, config.hostname, config.port,
+ config.user, config.password),
+ '', '')
+ cursor = await conn.cursor()
+
+ # Simple query
+ await cursor.execute("SELECT ID, NAME FROM STAFF FETCH FIRST 3 ROWS ONLY")
+ rows = await cursor.fetchall()
+ print("Async fetchall:", rows)
+
+ # Prepare + bind_param + execute
+ await cursor.prepare("SELECT ID, NAME FROM STAFF WHERE ID = ?")
+ await cursor.bind_param(1, 20)
+ await cursor.execute()
+ row = await cursor.fetchone()
+ print("Async bind_param result:", row)
+
+ await cursor.close()
+ await conn.close()
+
+ run_sync()
+ asyncio.run(run_async())
+
+#__END__
+#__LUW_EXPECTED__
+#--- Sync ---
+#Sync fetchall: [(10, 'Sanders'), (20, 'Pernal'), (30, 'Marenghi')]
+#Sync bind_param result: (20, 'Pernal')
+#--- Async ---
+#Async fetchall: [(10, 'Sanders'), (20, 'Pernal'), (30, 'Marenghi')]
+#Async bind_param result: (20, 'Pernal')
+#__ZOS_EXPECTED__
+#--- Sync ---
+#Sync fetchall: [(10, 'Sanders'), (20, 'Pernal'), (30, 'Marenghi')]
+#Sync bind_param result: (20, 'Pernal')
+#--- Async ---
+#Async fetchall: [(10, 'Sanders'), (20, 'Pernal'), (30, 'Marenghi')]
+#Async bind_param result: (20, 'Pernal')
+#__SYSTEMI_EXPECTED__
+#--- Sync ---
+#Sync fetchall: [(10, 'Sanders'), (20, 'Pernal'), (30, 'Marenghi')]
+#Sync bind_param result: (20, 'Pernal')
+#--- Async ---
+#Async fetchall: [(10, 'Sanders'), (20, 'Pernal'), (30, 'Marenghi')]
+#Async bind_param result: (20, 'Pernal')
+#__IDS_EXPECTED__
+#--- Sync ---
+#Sync fetchall: [(10, 'Sanders'), (20, 'Pernal'), (30, 'Marenghi')]
+#Sync bind_param result: (20, 'Pernal')
+#--- Async ---
+#Async fetchall: [(10, 'Sanders'), (20, 'Pernal'), (30, 'Marenghi')]
+#Async bind_param result: (20, 'Pernal')
diff --git a/asyncio_testsuite/test_31_async_sparray_cardinalities.py b/asyncio_testsuite/test_31_async_sparray_cardinalities.py
new file mode 100644
index 00000000..43a8d957
--- /dev/null
+++ b/asyncio_testsuite/test_31_async_sparray_cardinalities.py
@@ -0,0 +1,274 @@
+from __future__ import print_function
+import asyncio
+import sys
+import unittest
+import ibm_db
+import config
+from ibm_db_dbi import AsyncConnection
+from datetime import date, time, datetime
+from testfunctions import IbmDbTestFunctions
+
+
+class IbmDbTestCase(unittest.TestCase):
+
+ def test_31_async_sparray_cardinalities(self):
+ obj = IbmDbTestFunctions()
+ obj.assert_expect(self.run_test_31)
+
+ def run_test_31(self):
+ ARRAY_PROCS = [
+ ("int_array", "array_int12", [1, 2, 3, 4, 5, None]),
+ ("sint_array", "array_sint12", [7, 512, -29000, 32000]),
+ ("bint_array", "array_bint12", [1234567890123, None, -9876543210]),
+ ("float_array", "array_float12", [1.1, 2.2, 3.3]),
+ ("double_array", "array_double12", [10.5, 20.25, 30.75]),
+ ("real_array", "array_real12", [0.5, 1.5, 2.5]),
+ ("decfloat16_array", "array_decfloat1612", [1.23, None, 4.56, None]),
+ ("decfloat34_array", "array_decfloat3412", [1234567890.1234, None]),
+ ("dec_array", "array_dec12", [12.34, None, 56.78]),
+ ("time_array", "array_time12", [time(12, 20, 30), time(13, 30, 45)]),
+ ("date_array", "array_date12", [date(2025, 1, 1), date(2025, 12, 31)]),
+ ("ts_array", "array_ts12", [b'1981-07-08 10:42:34.000010', None,
+ b'1982-07-08 10:42:34.000010']),
+ ("ts_array", "array_ts12", [datetime(1989, 2, 12, 23, 55, 59, 342380),
+ datetime(1990, 2, 12, 23, 55, 59, 342380)]),
+ ("char_array", "array_char12", ["abc", "defg", "jkl"]),
+ ("char_array", "array_char12", [b'abc', b'defg']),
+ ("vc_array", "array_vc12", ["hello", "world"]),
+ ("vc_array", "array_vc12", [b'hello', b'world']),
+ ("vcfbd_array", "array_vcfbd12", [b'abc', b'dog', b'deadbeef', None, b'foobar']),
+ ("clob_array", "array_clob12", [b'long text here', b'another clob']),
+ ("blob_array", "array_blob12", [b"binarydata", b"morebytes", None, b'abc']),
+ ]
+
+ async def main():
+ conn = await AsyncConnection.connect(
+ "DATABASE=%s;HOSTNAME=%s;PORT=%d;PROTOCOL=TCPIP;UID=%s;PWD=%s;" % (
+ config.database, config.hostname, config.port,
+ config.user, config.password),
+ '', '')
+
+ passed = 0
+ failed = 0
+ skipped = 0
+
+ for type_name, proc_name, input_array in ARRAY_PROCS:
+ cursor = await conn.cursor()
+ try:
+ sql = "CALL %s.%s(?,?)" % (config.user.upper(), proc_name.upper())
+ await cursor.prepare(sql)
+
+ await cursor.bind_param(1, input_array, ibm_db.SQL_PARAM_INPUT)
+ await cursor.bind_param(2, 0, ibm_db.SQL_PARAM_OUTPUT, ibm_db.SQL_INTEGER)
+
+ await cursor.execute()
+ result = await cursor.fetch_callproc()
+
+ # result = (stmt, array_out, cardinality_out)
+ cardinality = result[2]
+ non_null = len([x for x in input_array if x is not None])
+ print("%-25s %-20s input=%r" % (proc_name, type_name, input_array))
+ print(" -> array_out=%r cardinality=%s" % (result[1], cardinality))
+
+ if cardinality == non_null or cardinality == len(input_array):
+ passed += 1
+ else:
+ print("*** UNEXPECTED cardinality: got %s, expected ~%s" % (cardinality, non_null))
+ failed += 1
+ except Exception as e:
+ err_str = str(e)
+ if 'SQL0440N' in err_str or 'CLI0102E' in err_str:
+ print(" %-25s %-20s SKIP: procedure not available" % (proc_name, type_name))
+ skipped += 1
+ else:
+ print(" %-25s %-20s ERROR: %s" % (proc_name, type_name, e))
+ failed += 1
+ finally:
+ await cursor.close()
+
+ await conn.close()
+ total = len(ARRAY_PROCS)
+ print("\nResults: %d passed, %d skipped, %d failed out of %d" % (passed, skipped, failed, total))
+ if failed == 0:
+ print("PASSED" if passed > 0 else "ALL SKIPPED")
+ else:
+ print("FAILED")
+ asyncio.run(main())
+
+#__END__
+#__LUW_EXPECTED__
+#array_int12 int_array input=[1, 2, 3, 4, 5, None]
+# -> array_out=[1, 2, 3, 4, 5, None] cardinality=6
+#array_sint12 sint_array input=[7, 512, -29000, 32000]
+# -> array_out=[7, 512, -29000, 32000] cardinality=4
+#array_bint12 bint_array input=[1234567890123, None, -9876543210]
+# -> array_out=[1234567890123, None, -9876543210] cardinality=3
+#array_float12 float_array input=[1.1, 2.2, 3.3]
+# -> array_out=[1.1, 2.2, 3.3] cardinality=3
+#array_double12 double_array input=[10.5, 20.25, 30.75]
+# -> array_out=[10.5, 20.25, 30.75] cardinality=3
+#array_real12 real_array input=[0.5, 1.5, 2.5]
+# -> array_out=[0.5, 1.5, 2.5] cardinality=3
+#array_decfloat1612 decfloat16_array input=[1.23, None, 4.56, None]
+# -> array_out=[1.23, None, 4.56, None] cardinality=4
+#array_decfloat3412 decfloat34_array input=[1234567890.1234, None]
+# -> array_out=[1234567890.1234, None] cardinality=2
+#array_dec12 dec_array input=[12.34, None, 56.78]
+# -> array_out=[12.34, None, 56.78] cardinality=3
+#array_time12 time_array input=[datetime.time(12, 20, 30), datetime.time(13, 30, 45)]
+# -> array_out=[datetime.time(12, 20, 30), datetime.time(13, 30, 45)] cardinality=2
+#array_date12 date_array input=[datetime.date(2025, 1, 1), datetime.date(2025, 12, 31)]
+# -> array_out=[datetime.date(2025, 1, 1), datetime.date(2025, 12, 31)] cardinality=2
+#array_ts12 ts_array input=[b'1981-07-08 10:42:34.000010', None, b'1982-07-08 10:42:34.000010']
+# -> array_out=[b'1981-07-08 10:42:34.000010', None, b'1982-07-08 10:42:34.000010'] cardinality=3
+#array_ts12 ts_array input=[datetime.datetime(1989, 2, 12, 23, 55, 59, 342380), datetime.datetime(1990, 2, 12, 23, 55, 59, 342380)]
+# -> array_out=[datetime.datetime(1989, 2, 12, 23, 55, 59, 342380), datetime.datetime(1990, 2, 12, 23, 55, 59, 342380)] cardinality=2
+#array_char12 char_array input=['abc', 'defg', 'jkl']
+# -> array_out=['abc', 'defg', 'jkl'] cardinality=3
+#array_char12 char_array input=[b'abc', b'defg']
+# -> array_out=[b'abc', b'defg'] cardinality=2
+#array_vc12 vc_array input=['hello', 'world']
+# -> array_out=['hello', 'world'] cardinality=2
+#array_vc12 vc_array input=[b'hello', b'world']
+# -> array_out=[b'hello', b'world'] cardinality=2
+#array_vcfbd12 vcfbd_array input=[b'abc', b'dog', b'deadbeef', None, b'foobar']
+# -> array_out=[b'abc', b'dog', b'deadbeef', None, b'foobar'] cardinality=5
+#array_clob12 clob_array input=[b'long text here', b'another clob']
+# -> array_out=[b'long text here', b'another clob'] cardinality=2
+#array_blob12 blob_array input=[b'binarydata', b'morebytes', None, b'abc']
+# -> array_out=[b'binarydata', b'morebytes', None, b'abc'] cardinality=4
+#
+#Results: 20 passed, 0 skipped, 0 failed out of 20
+#PASSED
+#__ZOS_EXPECTED__
+#array_int12 int_array input=[1, 2, 3, 4, 5, None]
+# -> array_out=[1, 2, 3, 4, 5, None] cardinality=6
+#array_sint12 sint_array input=[7, 512, -29000, 32000]
+# -> array_out=[7, 512, -29000, 32000] cardinality=4
+#array_bint12 bint_array input=[1234567890123, None, -9876543210]
+# -> array_out=[1234567890123, None, -9876543210] cardinality=3
+#array_float12 float_array input=[1.1, 2.2, 3.3]
+# -> array_out=[1.1, 2.2, 3.3] cardinality=3
+#array_double12 double_array input=[10.5, 20.25, 30.75]
+# -> array_out=[10.5, 20.25, 30.75] cardinality=3
+#array_real12 real_array input=[0.5, 1.5, 2.5]
+# -> array_out=[0.5, 1.5, 2.5] cardinality=3
+#array_decfloat1612 decfloat16_array input=[1.23, None, 4.56, None]
+# -> array_out=[1.23, None, 4.56, None] cardinality=4
+#array_decfloat3412 decfloat34_array input=[1234567890.1234, None]
+# -> array_out=[1234567890.1234, None] cardinality=2
+#array_dec12 dec_array input=[12.34, None, 56.78]
+# -> array_out=[12.34, None, 56.78] cardinality=3
+#array_time12 time_array input=[datetime.time(12, 20, 30), datetime.time(13, 30, 45)]
+# -> array_out=[datetime.time(12, 20, 30), datetime.time(13, 30, 45)] cardinality=2
+#array_date12 date_array input=[datetime.date(2025, 1, 1), datetime.date(2025, 12, 31)]
+# -> array_out=[datetime.date(2025, 1, 1), datetime.date(2025, 12, 31)] cardinality=2
+#array_ts12 ts_array input=[b'1981-07-08 10:42:34.000010', None, b'1982-07-08 10:42:34.000010']
+# -> array_out=[b'1981-07-08 10:42:34.000010', None, b'1982-07-08 10:42:34.000010'] cardinality=3
+#array_ts12 ts_array input=[datetime.datetime(1989, 2, 12, 23, 55, 59, 342380), datetime.datetime(1990, 2, 12, 23, 55, 59, 342380)]
+# -> array_out=[datetime.datetime(1989, 2, 12, 23, 55, 59, 342380), datetime.datetime(1990, 2, 12, 23, 55, 59, 342380)] cardinality=2
+#array_char12 char_array input=['abc', 'defg', 'jkl']
+# -> array_out=['abc', 'defg', 'jkl'] cardinality=3
+#array_char12 char_array input=[b'abc', b'defg']
+# -> array_out=[b'abc', b'defg'] cardinality=2
+#array_vc12 vc_array input=['hello', 'world']
+# -> array_out=['hello', 'world'] cardinality=2
+#array_vc12 vc_array input=[b'hello', b'world']
+# -> array_out=[b'hello', b'world'] cardinality=2
+#array_vcfbd12 vcfbd_array input=[b'abc', b'dog', b'deadbeef', None, b'foobar']
+# -> array_out=[b'abc', b'dog', b'deadbeef', None, b'foobar'] cardinality=5
+#array_clob12 clob_array input=[b'long text here', b'another clob']
+# -> array_out=[b'long text here', b'another clob'] cardinality=2
+#array_blob12 blob_array input=[b'binarydata', b'morebytes', None, b'abc']
+# -> array_out=[b'binarydata', b'morebytes', None, b'abc'] cardinality=4
+#
+#Results: 20 passed, 0 skipped, 0 failed out of 20
+#PASSED
+#__SYSTEMI_EXPECTED__
+#array_int12 int_array input=[1, 2, 3, 4, 5, None]
+# -> array_out=[1, 2, 3, 4, 5, None] cardinality=6
+#array_sint12 sint_array input=[7, 512, -29000, 32000]
+# -> array_out=[7, 512, -29000, 32000] cardinality=4
+#array_bint12 bint_array input=[1234567890123, None, -9876543210]
+# -> array_out=[1234567890123, None, -9876543210] cardinality=3
+#array_float12 float_array input=[1.1, 2.2, 3.3]
+# -> array_out=[1.1, 2.2, 3.3] cardinality=3
+#array_double12 double_array input=[10.5, 20.25, 30.75]
+# -> array_out=[10.5, 20.25, 30.75] cardinality=3
+#array_real12 real_array input=[0.5, 1.5, 2.5]
+# -> array_out=[0.5, 1.5, 2.5] cardinality=3
+#array_decfloat1612 decfloat16_array input=[1.23, None, 4.56, None]
+# -> array_out=[1.23, None, 4.56, None] cardinality=4
+#array_decfloat3412 decfloat34_array input=[1234567890.1234, None]
+# -> array_out=[1234567890.1234, None] cardinality=2
+#array_dec12 dec_array input=[12.34, None, 56.78]
+# -> array_out=[12.34, None, 56.78] cardinality=3
+#array_time12 time_array input=[datetime.time(12, 20, 30), datetime.time(13, 30, 45)]
+# -> array_out=[datetime.time(12, 20, 30), datetime.time(13, 30, 45)] cardinality=2
+#array_date12 date_array input=[datetime.date(2025, 1, 1), datetime.date(2025, 12, 31)]
+# -> array_out=[datetime.date(2025, 1, 1), datetime.date(2025, 12, 31)] cardinality=2
+#array_ts12 ts_array input=[b'1981-07-08 10:42:34.000010', None, b'1982-07-08 10:42:34.000010']
+# -> array_out=[b'1981-07-08 10:42:34.000010', None, b'1982-07-08 10:42:34.000010'] cardinality=3
+#array_ts12 ts_array input=[datetime.datetime(1989, 2, 12, 23, 55, 59, 342380), datetime.datetime(1990, 2, 12, 23, 55, 59, 342380)]
+# -> array_out=[datetime.datetime(1989, 2, 12, 23, 55, 59, 342380), datetime.datetime(1990, 2, 12, 23, 55, 59, 342380)] cardinality=2
+#array_char12 char_array input=['abc', 'defg', 'jkl']
+# -> array_out=['abc', 'defg', 'jkl'] cardinality=3
+#array_char12 char_array input=[b'abc', b'defg']
+# -> array_out=[b'abc', b'defg'] cardinality=2
+#array_vc12 vc_array input=['hello', 'world']
+# -> array_out=['hello', 'world'] cardinality=2
+#array_vc12 vc_array input=[b'hello', b'world']
+# -> array_out=[b'hello', b'world'] cardinality=2
+#array_vcfbd12 vcfbd_array input=[b'abc', b'dog', b'deadbeef', None, b'foobar']
+# -> array_out=[b'abc', b'dog', b'deadbeef', None, b'foobar'] cardinality=5
+#array_clob12 clob_array input=[b'long text here', b'another clob']
+# -> array_out=[b'long text here', b'another clob'] cardinality=2
+#array_blob12 blob_array input=[b'binarydata', b'morebytes', None, b'abc']
+# -> array_out=[b'binarydata', b'morebytes', None, b'abc'] cardinality=4
+#
+#Results: 20 passed, 0 skipped, 0 failed out of 20
+#PASSED
+#__IDS_EXPECTED__
+#array_int12 int_array input=[1, 2, 3, 4, 5, None]
+# -> array_out=[1, 2, 3, 4, 5, None] cardinality=6
+#array_sint12 sint_array input=[7, 512, -29000, 32000]
+# -> array_out=[7, 512, -29000, 32000] cardinality=4
+#array_bint12 bint_array input=[1234567890123, None, -9876543210]
+# -> array_out=[1234567890123, None, -9876543210] cardinality=3
+#array_float12 float_array input=[1.1, 2.2, 3.3]
+# -> array_out=[1.1, 2.2, 3.3] cardinality=3
+#array_double12 double_array input=[10.5, 20.25, 30.75]
+# -> array_out=[10.5, 20.25, 30.75] cardinality=3
+#array_real12 real_array input=[0.5, 1.5, 2.5]
+# -> array_out=[0.5, 1.5, 2.5] cardinality=3
+#array_decfloat1612 decfloat16_array input=[1.23, None, 4.56, None]
+# -> array_out=[1.23, None, 4.56, None] cardinality=4
+#array_decfloat3412 decfloat34_array input=[1234567890.1234, None]
+# -> array_out=[1234567890.1234, None] cardinality=2
+#array_dec12 dec_array input=[12.34, None, 56.78]
+# -> array_out=[12.34, None, 56.78] cardinality=3
+#array_time12 time_array input=[datetime.time(12, 20, 30), datetime.time(13, 30, 45)]
+# -> array_out=[datetime.time(12, 20, 30), datetime.time(13, 30, 45)] cardinality=2
+#array_date12 date_array input=[datetime.date(2025, 1, 1), datetime.date(2025, 12, 31)]
+# -> array_out=[datetime.date(2025, 1, 1), datetime.date(2025, 12, 31)] cardinality=2
+#array_ts12 ts_array input=[b'1981-07-08 10:42:34.000010', None, b'1982-07-08 10:42:34.000010']
+# -> array_out=[b'1981-07-08 10:42:34.000010', None, b'1982-07-08 10:42:34.000010'] cardinality=3
+#array_ts12 ts_array input=[datetime.datetime(1989, 2, 12, 23, 55, 59, 342380), datetime.datetime(1990, 2, 12, 23, 55, 59, 342380)]
+# -> array_out=[datetime.datetime(1989, 2, 12, 23, 55, 59, 342380), datetime.datetime(1990, 2, 12, 23, 55, 59, 342380)] cardinality=2
+#array_char12 char_array input=['abc', 'defg', 'jkl']
+# -> array_out=['abc', 'defg', 'jkl'] cardinality=3
+#array_char12 char_array input=[b'abc', b'defg']
+# -> array_out=[b'abc', b'defg'] cardinality=2
+#array_vc12 vc_array input=['hello', 'world']
+# -> array_out=['hello', 'world'] cardinality=2
+#array_vc12 vc_array input=[b'hello', b'world']
+# -> array_out=[b'hello', b'world'] cardinality=2
+#array_vcfbd12 vcfbd_array input=[b'abc', b'dog', b'deadbeef', None, b'foobar']
+# -> array_out=[b'abc', b'dog', b'deadbeef', None, b'foobar'] cardinality=5
+#array_clob12 clob_array input=[b'long text here', b'another clob']
+# -> array_out=[b'long text here', b'another clob'] cardinality=2
+#array_blob12 blob_array input=[b'binarydata', b'morebytes', None, b'abc']
+# -> array_out=[b'binarydata', b'morebytes', None, b'abc'] cardinality=4
+#
+#Results: 20 passed, 0 skipped, 0 failed out of 20
+#PASSED
diff --git a/asyncio_testsuite/test_32_async_sparray_computations.py b/asyncio_testsuite/test_32_async_sparray_computations.py
new file mode 100644
index 00000000..ea10615b
--- /dev/null
+++ b/asyncio_testsuite/test_32_async_sparray_computations.py
@@ -0,0 +1,274 @@
+from __future__ import print_function
+import asyncio
+import sys
+import unittest
+import ibm_db
+import config
+from ibm_db_dbi import AsyncConnection
+from datetime import date, time, datetime
+from testfunctions import IbmDbTestFunctions
+
+
+class IbmDbTestCase(unittest.TestCase):
+
+ def test_32_async_sparray_computations(self):
+ obj = IbmDbTestFunctions()
+ obj.assert_expect(self.run_test_32)
+
+ def run_test_32(self):
+ # (type_name, proc_name, input_val[, output_template])
+ SCALAR_PROCS = [
+ ("int_array", "array_int22", 7),
+ ("sint_array", "array_sint22", 10),
+ ("bint_array", "array_bint22", 12345),
+ ("float_array", "array_float22", 3.14),
+ ("double_array", "array_double22", 20.25),
+ ("real_array", "array_real22", 1.5),
+ ("decfloat16_array", "array_decflt1622", 4.56),
+ ("decfloat34_array", "array_decfloat3422", 123456.1234),
+ ("dec_array", "array_dec22", 56.78),
+ ("time_array", "array_time22", time(12, 20, 30)),
+ ("date_array", "array_date22", date(2025, 1, 1)),
+ ("ts_array", "array_ts22", datetime(1989, 2, 12, 23, 55, 59, 342380)),
+ ("ts_array", "array_ts22", b'1989-02-12 23:55:59.342380'),
+ ("char_array", "array_char22", "HelloWorld"),
+ ("char_array", "array_char22", b'HelloWorld'),
+ ("vc_array", "array_vc22", "basketball"),
+ ("vc_array", "array_vc22", b'basketball'),
+ ("vcfbd_array", "array_vcfbd22", b'foobar'),
+ ("clob_array", "array_clob22", 100, [b'x' * 20] * 4),
+ ("blob_array", "array_blob22", 200, [b'x' * 20] * 4),
+ ]
+
+ async def main():
+ conn = await AsyncConnection.connect(
+ "DATABASE=%s;HOSTNAME=%s;PORT=%d;PROTOCOL=TCPIP;UID=%s;PWD=%s;" % (
+ config.database, config.hostname, config.port,
+ config.user, config.password),
+ '', '')
+
+ passed = 0
+ failed = 0
+ skipped = 0
+
+ for entry in SCALAR_PROCS:
+ type_name, proc_name, input_val = entry[0], entry[1], entry[2]
+ out_template = entry[3] if len(entry) > 3 else None
+ cursor = await conn.cursor()
+ try:
+ sql = "CALL %s.%s(?, ?)" % (config.user.upper(), proc_name.upper())
+ await cursor.prepare(sql)
+
+ output_array = out_template if out_template is not None else [input_val] * 4
+
+ await cursor.bind_param(1, input_val, ibm_db.SQL_PARAM_INPUT)
+ await cursor.bind_param(2, output_array, ibm_db.SQL_PARAM_OUTPUT)
+ await cursor.execute()
+ result = await cursor.fetch_callproc()
+ # result = (stmt, scalar_in, output_array)
+ out_arr = result[2]
+ print("%-25s %-20s input=%r" % (proc_name, type_name, input_val))
+ print("-> output array=%r" % (out_arr,))
+
+ if isinstance(out_arr, list) and len(out_arr) > 0:
+ passed += 1
+ else:
+ print("*** UNEXPECTED output: %r" % (out_arr,))
+ failed += 1
+ except Exception as e:
+ err_str = str(e)
+ if 'SQL0440N' in err_str or 'CLI0102E' in err_str:
+ print("%-25s %-20s SKIP: procedure not available" % (proc_name, type_name))
+ skipped += 1
+ else:
+ print("%-25s %-20s ERROR: %s" % (proc_name, type_name, e))
+ failed += 1
+ finally:
+ await cursor.close()
+
+ await conn.close()
+ total = len(SCALAR_PROCS)
+ print("\nResults: %d passed, %d skipped, %d failed out of %d" % (passed, skipped, failed, total))
+ if failed == 0:
+ print("PASSED" if passed > 0 else "ALL SKIPPED")
+ else:
+ print("FAILED")
+ asyncio.run(main())
+
+#__END__
+#__LUW_EXPECTED__
+#array_int22 int_array input=7
+#-> output array=[49, 343, 28, 2]
+#array_sint22 sint_array input=10
+#-> output array=[100, 1000, 40, 5]
+#array_bint22 bint_array input=12345
+#-> output array=[152399025, 1881365963625, 49380, 12340]
+#array_float22 float_array input=3.14
+#-> output array=[9.8596, 30.959144000000002, 12.56, -1.8599999999999999]
+#array_double22 double_array input=20.25
+#-> output array=[410.0625, 8303.765625, 81.0, 15.25]
+#array_real22 real_array input=1.5
+#-> output array=[2.25, 3.375, 6.0, -3.5]
+#array_decflt1622 decfloat16_array input=4.56
+#-> output array=[20.7936, 94.818816, 18.24, -0.44]
+#array_decfloat3422 decfloat34_array input=123456.1234
+#-> output array=[15241414404.956028, 1881645937568789.0, 493824.4936, 123451.1234]
+#array_dec22 dec_array input=56.78
+#-> output array=[3223.96, 183056.92, 227.12, 51.78]
+#array_time22 time_array input=datetime.time(12, 20, 30)
+#-> output array=[datetime.time(13, 21, 31), datetime.time(11, 19, 29), datetime.time(12, 20, 31), datetime.time(12, 20, 29)]
+#array_date22 date_array input=datetime.date(2025, 1, 1)
+#-> output array=[datetime.date(2026, 2, 2), datetime.date(2023, 11, 30), datetime.date(2025, 1, 2), datetime.date(2024, 12, 31)]
+#array_ts22 ts_array input=datetime.datetime(1989, 2, 12, 23, 55, 59, 342380)
+#-> output array=[datetime.datetime(1990, 3, 14, 0, 57, 0, 342380), datetime.datetime(1988, 1, 11, 22, 54, 58, 342380), datetime.datetime(1989, 2, 13, 23, 56, 0, 342380), datetime.datetime(1989, 2, 11, 23, 55, 58, 342380)]
+#array_ts22 ts_array input=b'1989-02-12 23:55:59.342380'
+#-> output array=[datetime.datetime(1990, 3, 14, 0, 57, 0, 342380), datetime.datetime(1988, 1, 11, 22, 54, 58, 342380), datetime.datetime(1989, 2, 13, 23, 56, 0, 342380), datetime.datetime(1989, 2, 11, 23, 55, 58, 342380)]
+#array_char22 char_array input='HelloWorld'
+#-> output array=['HelloWorld', 'HelloWorld', 'Hell', '']
+#array_char22 char_array input=b'HelloWorld'
+#-> output array=[b'HelloWorld ', b'HelloWorld ', b'Hell ', b' ']
+#array_vc22 vc_array input='basketball'
+#-> output array=['basketballbasketball', 'basketballbasketball', 'bask', 'tball']
+#array_vc22 vc_array input=b'basketball'
+#-> output array=[b'basketballbasketbal', b'basketballbasketbal', b'bask', b'tball']
+#array_vcfbd22 vcfbd_array input=b'foobar'
+#-> output array=[b'foobarfoobar', b'foobarfoobarfoobar', b'foobar', b'oobarr']
+#array_clob22 clob_array input=100
+#-> output array=[b'101 ', b'102 ', b'103 ', b'104 ']
+#array_blob22 blob_array input=200
+#-> output array=[b'201 ', b'202 ', b'203 ', b'204 ']
+#
+#Results: 20 passed, 0 skipped, 0 failed out of 20
+#PASSED
+#__ZOS_EXPECTED__
+#array_int22 int_array input=7
+#-> output array=[49, 343, 28, 2]
+#array_sint22 sint_array input=10
+#-> output array=[100, 1000, 40, 5]
+#array_bint22 bint_array input=12345
+#-> output array=[152399025, 1881365963625, 49380, 12340]
+#array_float22 float_array input=3.14
+#-> output array=[9.8596, 30.959144000000002, 12.56, -1.8599999999999999]
+#array_double22 double_array input=20.25
+#-> output array=[410.0625, 8303.765625, 81.0, 15.25]
+#array_real22 real_array input=1.5
+#-> output array=[2.25, 3.375, 6.0, -3.5]
+#array_decflt1622 decfloat16_array input=4.56
+#-> output array=[20.7936, 94.818816, 18.24, -0.44]
+#array_decfloat3422 decfloat34_array input=123456.1234
+#-> output array=[15241414404.956028, 1881645937568789.0, 493824.4936, 123451.1234]
+#array_dec22 dec_array input=56.78
+#-> output array=[3223.96, 183056.92, 227.12, 51.78]
+#array_time22 time_array input=datetime.time(12, 20, 30)
+#-> output array=[datetime.time(13, 21, 31), datetime.time(11, 19, 29), datetime.time(12, 20, 31), datetime.time(12, 20, 29)]
+#array_date22 date_array input=datetime.date(2025, 1, 1)
+#-> output array=[datetime.date(2026, 2, 2), datetime.date(2023, 11, 30), datetime.date(2025, 1, 2), datetime.date(2024, 12, 31)]
+#array_ts22 ts_array input=datetime.datetime(1989, 2, 12, 23, 55, 59, 342380)
+#-> output array=[datetime.datetime(1990, 3, 14, 0, 57, 0, 342380), datetime.datetime(1988, 1, 11, 22, 54, 58, 342380), datetime.datetime(1989, 2, 13, 23, 56, 0, 342380), datetime.datetime(1989, 2, 11, 23, 55, 58, 342380)]
+#array_ts22 ts_array input=b'1989-02-12 23:55:59.342380'
+#-> output array=[datetime.datetime(1990, 3, 14, 0, 57, 0, 342380), datetime.datetime(1988, 1, 11, 22, 54, 58, 342380), datetime.datetime(1989, 2, 13, 23, 56, 0, 342380), datetime.datetime(1989, 2, 11, 23, 55, 58, 342380)]
+#array_char22 char_array input='HelloWorld'
+#-> output array=['HelloWorld', 'HelloWorld', 'Hell', '']
+#array_char22 char_array input=b'HelloWorld'
+#-> output array=[b'HelloWorld ', b'HelloWorld ', b'Hell ', b' ']
+#array_vc22 vc_array input='basketball'
+#-> output array=['basketballbasketball', 'basketballbasketball', 'bask', 'tball']
+#array_vc22 vc_array input=b'basketball'
+#-> output array=[b'basketballbasketbal', b'basketballbasketbal', b'bask', b'tball']
+#array_vcfbd22 vcfbd_array input=b'foobar'
+#-> output array=[b'foobarfoobar', b'foobarfoobarfoobar', b'foobar', b'oobarr']
+#array_clob22 clob_array input=100
+#-> output array=[b'101 ', b'102 ', b'103 ', b'104 ']
+#array_blob22 blob_array input=200
+#-> output array=[b'201 ', b'202 ', b'203 ', b'204 ']
+#
+#Results: 20 passed, 0 skipped, 0 failed out of 20
+#PASSED
+#__SYSTEMI_EXPECTED__
+#array_int22 int_array input=7
+#-> output array=[49, 343, 28, 2]
+#array_sint22 sint_array input=10
+#-> output array=[100, 1000, 40, 5]
+#array_bint22 bint_array input=12345
+#-> output array=[152399025, 1881365963625, 49380, 12340]
+#array_float22 float_array input=3.14
+#-> output array=[9.8596, 30.959144000000002, 12.56, -1.8599999999999999]
+#array_double22 double_array input=20.25
+#-> output array=[410.0625, 8303.765625, 81.0, 15.25]
+#array_real22 real_array input=1.5
+#-> output array=[2.25, 3.375, 6.0, -3.5]
+#array_decflt1622 decfloat16_array input=4.56
+#-> output array=[20.7936, 94.818816, 18.24, -0.44]
+#array_decfloat3422 decfloat34_array input=123456.1234
+#-> output array=[15241414404.956028, 1881645937568789.0, 493824.4936, 123451.1234]
+#array_dec22 dec_array input=56.78
+#-> output array=[3223.96, 183056.92, 227.12, 51.78]
+#array_time22 time_array input=datetime.time(12, 20, 30)
+#-> output array=[datetime.time(13, 21, 31), datetime.time(11, 19, 29), datetime.time(12, 20, 31), datetime.time(12, 20, 29)]
+#array_date22 date_array input=datetime.date(2025, 1, 1)
+#-> output array=[datetime.date(2026, 2, 2), datetime.date(2023, 11, 30), datetime.date(2025, 1, 2), datetime.date(2024, 12, 31)]
+#array_ts22 ts_array input=datetime.datetime(1989, 2, 12, 23, 55, 59, 342380)
+#-> output array=[datetime.datetime(1990, 3, 14, 0, 57, 0, 342380), datetime.datetime(1988, 1, 11, 22, 54, 58, 342380), datetime.datetime(1989, 2, 13, 23, 56, 0, 342380), datetime.datetime(1989, 2, 11, 23, 55, 58, 342380)]
+#array_ts22 ts_array input=b'1989-02-12 23:55:59.342380'
+#-> output array=[datetime.datetime(1990, 3, 14, 0, 57, 0, 342380), datetime.datetime(1988, 1, 11, 22, 54, 58, 342380), datetime.datetime(1989, 2, 13, 23, 56, 0, 342380), datetime.datetime(1989, 2, 11, 23, 55, 58, 342380)]
+#array_char22 char_array input='HelloWorld'
+#-> output array=['HelloWorld', 'HelloWorld', 'Hell', '']
+#array_char22 char_array input=b'HelloWorld'
+#-> output array=[b'HelloWorld ', b'HelloWorld ', b'Hell ', b' ']
+#array_vc22 vc_array input='basketball'
+#-> output array=['basketballbasketball', 'basketballbasketball', 'bask', 'tball']
+#array_vc22 vc_array input=b'basketball'
+#-> output array=[b'basketballbasketbal', b'basketballbasketbal', b'bask', b'tball']
+#array_vcfbd22 vcfbd_array input=b'foobar'
+#-> output array=[b'foobarfoobar', b'foobarfoobarfoobar', b'foobar', b'oobarr']
+#array_clob22 clob_array input=100
+#-> output array=[b'101 ', b'102 ', b'103 ', b'104 ']
+#array_blob22 blob_array input=200
+#-> output array=[b'201 ', b'202 ', b'203 ', b'204 ']
+#
+#Results: 20 passed, 0 skipped, 0 failed out of 20
+#PASSED
+#__IDS_EXPECTED__
+#array_int22 int_array input=7
+#-> output array=[49, 343, 28, 2]
+#array_sint22 sint_array input=10
+#-> output array=[100, 1000, 40, 5]
+#array_bint22 bint_array input=12345
+#-> output array=[152399025, 1881365963625, 49380, 12340]
+#array_float22 float_array input=3.14
+#-> output array=[9.8596, 30.959144000000002, 12.56, -1.8599999999999999]
+#array_double22 double_array input=20.25
+#-> output array=[410.0625, 8303.765625, 81.0, 15.25]
+#array_real22 real_array input=1.5
+#-> output array=[2.25, 3.375, 6.0, -3.5]
+#array_decflt1622 decfloat16_array input=4.56
+#-> output array=[20.7936, 94.818816, 18.24, -0.44]
+#array_decfloat3422 decfloat34_array input=123456.1234
+#-> output array=[15241414404.956028, 1881645937568789.0, 493824.4936, 123451.1234]
+#array_dec22 dec_array input=56.78
+#-> output array=[3223.96, 183056.92, 227.12, 51.78]
+#array_time22 time_array input=datetime.time(12, 20, 30)
+#-> output array=[datetime.time(13, 21, 31), datetime.time(11, 19, 29), datetime.time(12, 20, 31), datetime.time(12, 20, 29)]
+#array_date22 date_array input=datetime.date(2025, 1, 1)
+#-> output array=[datetime.date(2026, 2, 2), datetime.date(2023, 11, 30), datetime.date(2025, 1, 2), datetime.date(2024, 12, 31)]
+#array_ts22 ts_array input=datetime.datetime(1989, 2, 12, 23, 55, 59, 342380)
+#-> output array=[datetime.datetime(1990, 3, 14, 0, 57, 0, 342380), datetime.datetime(1988, 1, 11, 22, 54, 58, 342380), datetime.datetime(1989, 2, 13, 23, 56, 0, 342380), datetime.datetime(1989, 2, 11, 23, 55, 58, 342380)]
+#array_ts22 ts_array input=b'1989-02-12 23:55:59.342380'
+#-> output array=[datetime.datetime(1990, 3, 14, 0, 57, 0, 342380), datetime.datetime(1988, 1, 11, 22, 54, 58, 342380), datetime.datetime(1989, 2, 13, 23, 56, 0, 342380), datetime.datetime(1989, 2, 11, 23, 55, 58, 342380)]
+#array_char22 char_array input='HelloWorld'
+#-> output array=['HelloWorld', 'HelloWorld', 'Hell', '']
+#array_char22 char_array input=b'HelloWorld'
+#-> output array=[b'HelloWorld ', b'HelloWorld ', b'Hell ', b' ']
+#array_vc22 vc_array input='basketball'
+#-> output array=['basketballbasketball', 'basketballbasketball', 'bask', 'tball']
+#array_vc22 vc_array input=b'basketball'
+#-> output array=[b'basketballbasketbal', b'basketballbasketbal', b'bask', b'tball']
+#array_vcfbd22 vcfbd_array input=b'foobar'
+#-> output array=[b'foobarfoobar', b'foobarfoobarfoobar', b'foobar', b'oobarr']
+#array_clob22 clob_array input=100
+#-> output array=[b'101 ', b'102 ', b'103 ', b'104 ']
+#array_blob22 blob_array input=200
+#-> output array=[b'201 ', b'202 ', b'203 ', b'204 ']
+#
+#Results: 20 passed, 0 skipped, 0 failed out of 20
+#PASSED
diff --git a/asyncio_testsuite/test_33_async_sparray_inout_computations.py b/asyncio_testsuite/test_33_async_sparray_inout_computations.py
new file mode 100644
index 00000000..75fcab6d
--- /dev/null
+++ b/asyncio_testsuite/test_33_async_sparray_inout_computations.py
@@ -0,0 +1,354 @@
+from __future__ import print_function
+import asyncio
+import sys
+import unittest
+import ibm_db
+import config
+from ibm_db_dbi import AsyncConnection
+from datetime import date, time, datetime
+from testfunctions import IbmDbTestFunctions
+
+
+class IbmDbTestCase(unittest.TestCase):
+
+ def test_33_async_sparray_inout_computations(self):
+ obj = IbmDbTestFunctions()
+ obj.assert_expect(self.run_test_33)
+
+ def run_test_33(self):
+ ARRAY_PROCS = [
+ ("int_array", "array_int31", [1, 2, 3, 4, 5]),
+ ("sint_array", "array_sint31", [7, 512, -29000, 32000]),
+ ("bint_array", "array_bint31", [1234567890123, None, -9876543210]),
+ ("float_array", "array_float31", [1.1, 2.2, 3.3]),
+ ("double_array", "array_double31", [10.5, 20.25, 30.75]),
+ ("real_array", "array_real31", [0.5, 1.5, 2.5]),
+ ("decfloat16_array", "array_decflt1631", [1.23, None, 4.56, None]),
+ ("decfloat34_array", "array_decfloat3431", [12345678.1234, None]),
+ ("dec_array", "array_dec31", [12.34, None, 56.78]),
+ ("time_array", "array_time31", [time(12, 20, 30), time(13, 30, 45)]),
+ ("date_array", "array_date31", [date(2025, 1, 1), date(2025, 12, 31)]),
+ ("ts_array", "array_ts31", [datetime(1989, 2, 12, 23, 55, 59, 342380),
+ datetime(1990, 2, 12, 23, 55, 59, 342380)]),
+ ("ts_array", "array_ts31", [b'1981-07-08 10:42:34.000010',
+ b'1982-07-08 10:42:34.000010']),
+ ("char_array", "array_char31", ["abc", "defg"]),
+ ("vc_array", "array_vc31", ["hello", "world"]),
+ ("vc_array", "array_vc31", [b'hello', b'world']),
+ ("vcfbd_array", "array_vcfbd31", [b'basketball', b'baseball', b'football',
+ b'pingpong', b'lacrosse']),
+ ("clob_array", "array_clob31", [b'long text here', b'another clob']),
+ ("blob_array", "array_blob31", [b"binarydata", b"morebytes", b'abc']),
+ ("char_array", "array_char31", [b'abc', None, b'defg']),
+ ]
+
+ async def main():
+ conn = await AsyncConnection.connect(
+ "DATABASE=%s;HOSTNAME=%s;PORT=%d;PROTOCOL=TCPIP;UID=%s;PWD=%s;" % (
+ config.database, config.hostname, config.port,
+ config.user, config.password),
+ '', '')
+
+ passed = 0
+ failed = 0
+ skipped = 0
+
+ for type_name, proc_name, input_array in ARRAY_PROCS:
+ cursor = await conn.cursor()
+ try:
+ sql = "CALL %s.%s(?)" % (config.user.upper(), proc_name.upper())
+ await cursor.prepare(sql)
+
+ await cursor.bind_param(1, input_array, ibm_db.SQL_PARAM_INPUT_OUTPUT)
+
+ await cursor.execute()
+ result = await cursor.fetch_callproc()
+
+ # result = (stmt, inout_array)
+ out_arr = result[1]
+ print("%-25s %-20s" % (proc_name, type_name))
+ print("input =%r" % (input_array,))
+ print("output=%r" % (out_arr,))
+
+ if isinstance(out_arr, list) and len(out_arr) > 0:
+ passed += 1
+ else:
+ print(" *** UNEXPECTED output: %r" % (out_arr,))
+ failed += 1
+ except Exception as e:
+ err_str = str(e)
+ if 'SQL0440N' in err_str or 'CLI0102E' in err_str:
+ print("%-25s %-20s SKIP: procedure not available" % (proc_name, type_name))
+ skipped += 1
+ else:
+ print("%-25s %-20s ERROR: %s" % (proc_name, type_name, e))
+ failed += 1
+ finally:
+ await cursor.close()
+
+ await conn.close()
+ total = len(ARRAY_PROCS)
+ print("\nResults: %d passed, %d skipped, %d failed out of %d" % (passed, skipped, failed, total))
+ if failed == 0:
+ print("PASSED" if passed > 0 else "ALL SKIPPED")
+ else:
+ print("FAILED")
+ asyncio.run(main())
+
+#__END__
+#__LUW_EXPECTED__
+#array_int31 int_array
+#input =[1, 2, 3, 4, 5]
+#output=[2, 3, 4, 5, 6]
+#array_sint31 sint_array
+#input =[7, 512, -29000, 32000]
+#output=[8, 513, -28999, 32001]
+#array_bint31 bint_array
+#input =[1234567890123, None, -9876543210]
+#output=[1234567890124, None, -9876543209]
+#array_float31 float_array
+#input =[1.1, 2.2, 3.3]
+#output=[2.43, 3.5300000000000002, 4.63]
+#array_double31 double_array
+#input =[10.5, 20.25, 30.75]
+#output=[11.83, 21.58, 32.08]
+#array_real31 real_array
+#input =[0.5, 1.5, 2.5]
+#output=[1.8300000429153442, 2.8299999237060547, 3.8299999237060547]
+#array_decflt1631 decfloat16_array
+#input =[1.23, None, 4.56, None]
+#output=[2.56, None, 5.89, None]
+#array_decfloat3431 decfloat34_array
+#input =[12345678.1234, None]
+#output=[12345679.4534, None]
+#array_dec31 dec_array
+#input =[12.34, None, 56.78]
+#output=[13.67, None, 58.11]
+#array_time31 time_array
+#input =[datetime.time(12, 20, 30), datetime.time(13, 30, 45)]
+#output=[datetime.time(11, 21, 29), datetime.time(12, 31, 44)]
+#array_date31 date_array
+#input =[datetime.date(2025, 1, 1), datetime.date(2025, 12, 31)]
+#output=[datetime.date(2025, 12, 2), datetime.date(2026, 12, 1)]
+#array_ts31 ts_array
+#input =[datetime.datetime(1989, 2, 12, 23, 55, 59, 342380), datetime.datetime(1990, 2, 12, 23, 55, 59, 342380)]
+#output=[datetime.datetime(1990, 1, 13, 22, 56, 58, 342380), datetime.datetime(1991, 1, 13, 22, 56, 58, 342380)]
+#array_ts31 ts_array
+#input =[b'1981-07-08 10:42:34.000010', b'1982-07-08 10:42:34.000010']
+#output=[datetime.datetime(1982, 6, 9, 9, 43, 33, 10), datetime.datetime(1983, 6, 9, 9, 43, 33, 10)]
+#array_char31 char_array
+#input =['abc', 'defg']
+#output=['abc - a', 'defg - d']
+#array_vc31 vc_array
+#input =['hello', 'world']
+#output=['hello - h', 'world - w']
+#array_vc31 vc_array
+#input =[b'hello', b'world']
+#output=[b'hello - h', b'world - w']
+#array_vcfbd31 vcfbd_array
+#input =[b'basketball', b'baseball', b'football', b'pingpong', b'lacrosse']
+#output=[b'basketball - b', b'baseball - b', b'football - f', b'pingpong - p', b'lacrosse - l']
+#array_clob31 clob_array
+#input =[b'long text here', b'another clob']
+#output=[b'another clob', b'long text here']
+#array_blob31 blob_array
+#input =[b'binarydata', b'morebytes', b'abc']
+#output=[b'abc', b'morebytes', b'binarydata']
+#array_char31 char_array
+#input =[b'abc', None, b'defg']
+#output=[b'abc - a ', None, b'defg - d ']
+#
+#Results: 20 passed, 0 skipped, 0 failed out of 20
+#PASSED
+#__ZOS_EXPECTED__
+#array_int31 int_array
+#input =[1, 2, 3, 4, 5]
+#output=[2, 3, 4, 5, 6]
+#array_sint31 sint_array
+#input =[7, 512, -29000, 32000]
+#output=[8, 513, -28999, 32001]
+#array_bint31 bint_array
+#input =[1234567890123, None, -9876543210]
+#output=[1234567890124, None, -9876543209]
+#array_float31 float_array
+#input =[1.1, 2.2, 3.3]
+#output=[2.43, 3.5300000000000002, 4.63]
+#array_double31 double_array
+#input =[10.5, 20.25, 30.75]
+#output=[11.83, 21.58, 32.08]
+#array_real31 real_array
+#input =[0.5, 1.5, 2.5]
+#output=[1.8300000429153442, 2.8299999237060547, 3.8299999237060547]
+#array_decflt1631 decfloat16_array
+#input =[1.23, None, 4.56, None]
+#output=[2.56, None, 5.89, None]
+#array_decfloat3431 decfloat34_array
+#input =[12345678.1234, None]
+#output=[12345679.4534, None]
+#array_dec31 dec_array
+#input =[12.34, None, 56.78]
+#output=[13.67, None, 58.11]
+#array_time31 time_array
+#input =[datetime.time(12, 20, 30), datetime.time(13, 30, 45)]
+#output=[datetime.time(11, 21, 29), datetime.time(12, 31, 44)]
+#array_date31 date_array
+#input =[datetime.date(2025, 1, 1), datetime.date(2025, 12, 31)]
+#output=[datetime.date(2025, 12, 2), datetime.date(2026, 12, 1)]
+#array_ts31 ts_array
+#input =[datetime.datetime(1989, 2, 12, 23, 55, 59, 342380), datetime.datetime(1990, 2, 12, 23, 55, 59, 342380)]
+#output=[datetime.datetime(1990, 1, 13, 22, 56, 58, 342380), datetime.datetime(1991, 1, 13, 22, 56, 58, 342380)]
+#array_ts31 ts_array
+#input =[b'1981-07-08 10:42:34.000010', b'1982-07-08 10:42:34.000010']
+#output=[datetime.datetime(1982, 6, 9, 9, 43, 33, 10), datetime.datetime(1983, 6, 9, 9, 43, 33, 10)]
+#array_char31 char_array
+#input =['abc', 'defg']
+#output=['abc - a', 'defg - d']
+#array_vc31 vc_array
+#input =['hello', 'world']
+#output=['hello - h', 'world - w']
+#array_vc31 vc_array
+#input =[b'hello', b'world']
+#output=[b'hello - h', b'world - w']
+#array_vcfbd31 vcfbd_array
+#input =[b'basketball', b'baseball', b'football', b'pingpong', b'lacrosse']
+#output=[b'basketball - b', b'baseball - b', b'football - f', b'pingpong - p', b'lacrosse - l']
+#array_clob31 clob_array
+#input =[b'long text here', b'another clob']
+#output=[b'another clob', b'long text here']
+#array_blob31 blob_array
+#input =[b'binarydata', b'morebytes', b'abc']
+#output=[b'abc', b'morebytes', b'binarydata']
+#array_char31 char_array
+#input =[b'abc', None, b'defg']
+#output=[b'abc - a ', None, b'defg - d ']
+#
+#Results: 20 passed, 0 skipped, 0 failed out of 20
+#PASSED
+#__SYSTEMI_EXPECTED__
+#array_int31 int_array
+#input =[1, 2, 3, 4, 5]
+#output=[2, 3, 4, 5, 6]
+#array_sint31 sint_array
+#input =[7, 512, -29000, 32000]
+#output=[8, 513, -28999, 32001]
+#array_bint31 bint_array
+#input =[1234567890123, None, -9876543210]
+#output=[1234567890124, None, -9876543209]
+#array_float31 float_array
+#input =[1.1, 2.2, 3.3]
+#output=[2.43, 3.5300000000000002, 4.63]
+#array_double31 double_array
+#input =[10.5, 20.25, 30.75]
+#output=[11.83, 21.58, 32.08]
+#array_real31 real_array
+#input =[0.5, 1.5, 2.5]
+#output=[1.8300000429153442, 2.8299999237060547, 3.8299999237060547]
+#array_decflt1631 decfloat16_array
+#input =[1.23, None, 4.56, None]
+#output=[2.56, None, 5.89, None]
+#array_decfloat3431 decfloat34_array
+#input =[12345678.1234, None]
+#output=[12345679.4534, None]
+#array_dec31 dec_array
+#input =[12.34, None, 56.78]
+#output=[13.67, None, 58.11]
+#array_time31 time_array
+#input =[datetime.time(12, 20, 30), datetime.time(13, 30, 45)]
+#output=[datetime.time(11, 21, 29), datetime.time(12, 31, 44)]
+#array_date31 date_array
+#input =[datetime.date(2025, 1, 1), datetime.date(2025, 12, 31)]
+#output=[datetime.date(2025, 12, 2), datetime.date(2026, 12, 1)]
+#array_ts31 ts_array
+#input =[datetime.datetime(1989, 2, 12, 23, 55, 59, 342380), datetime.datetime(1990, 2, 12, 23, 55, 59, 342380)]
+#output=[datetime.datetime(1990, 1, 13, 22, 56, 58, 342380), datetime.datetime(1991, 1, 13, 22, 56, 58, 342380)]
+#array_ts31 ts_array
+#input =[b'1981-07-08 10:42:34.000010', b'1982-07-08 10:42:34.000010']
+#output=[datetime.datetime(1982, 6, 9, 9, 43, 33, 10), datetime.datetime(1983, 6, 9, 9, 43, 33, 10)]
+#array_char31 char_array
+#input =['abc', 'defg']
+#output=['abc - a', 'defg - d']
+#array_vc31 vc_array
+#input =['hello', 'world']
+#output=['hello - h', 'world - w']
+#array_vc31 vc_array
+#input =[b'hello', b'world']
+#output=[b'hello - h', b'world - w']
+#array_vcfbd31 vcfbd_array
+#input =[b'basketball', b'baseball', b'football', b'pingpong', b'lacrosse']
+#output=[b'basketball - b', b'baseball - b', b'football - f', b'pingpong - p', b'lacrosse - l']
+#array_clob31 clob_array
+#input =[b'long text here', b'another clob']
+#output=[b'another clob', b'long text here']
+#array_blob31 blob_array
+#input =[b'binarydata', b'morebytes', b'abc']
+#output=[b'abc', b'morebytes', b'binarydata']
+#array_char31 char_array
+#input =[b'abc', None, b'defg']
+#output=[b'abc - a ', None, b'defg - d ']
+#
+#Results: 20 passed, 0 skipped, 0 failed out of 20
+#PASSED
+#__IDS_EXPECTED__
+#array_int31 int_array
+#input =[1, 2, 3, 4, 5]
+#output=[2, 3, 4, 5, 6]
+#array_sint31 sint_array
+#input =[7, 512, -29000, 32000]
+#output=[8, 513, -28999, 32001]
+#array_bint31 bint_array
+#input =[1234567890123, None, -9876543210]
+#output=[1234567890124, None, -9876543209]
+#array_float31 float_array
+#input =[1.1, 2.2, 3.3]
+#output=[2.43, 3.5300000000000002, 4.63]
+#array_double31 double_array
+#input =[10.5, 20.25, 30.75]
+#output=[11.83, 21.58, 32.08]
+#array_real31 real_array
+#input =[0.5, 1.5, 2.5]
+#output=[1.8300000429153442, 2.8299999237060547, 3.8299999237060547]
+#array_decflt1631 decfloat16_array
+#input =[1.23, None, 4.56, None]
+#output=[2.56, None, 5.89, None]
+#array_decfloat3431 decfloat34_array
+#input =[12345678.1234, None]
+#output=[12345679.4534, None]
+#array_dec31 dec_array
+#input =[12.34, None, 56.78]
+#output=[13.67, None, 58.11]
+#array_time31 time_array
+#input =[datetime.time(12, 20, 30), datetime.time(13, 30, 45)]
+#output=[datetime.time(11, 21, 29), datetime.time(12, 31, 44)]
+#array_date31 date_array
+#input =[datetime.date(2025, 1, 1), datetime.date(2025, 12, 31)]
+#output=[datetime.date(2025, 12, 2), datetime.date(2026, 12, 1)]
+#array_ts31 ts_array
+#input =[datetime.datetime(1989, 2, 12, 23, 55, 59, 342380), datetime.datetime(1990, 2, 12, 23, 55, 59, 342380)]
+#output=[datetime.datetime(1990, 1, 13, 22, 56, 58, 342380), datetime.datetime(1991, 1, 13, 22, 56, 58, 342380)]
+#array_ts31 ts_array
+#input =[b'1981-07-08 10:42:34.000010', b'1982-07-08 10:42:34.000010']
+#output=[datetime.datetime(1982, 6, 9, 9, 43, 33, 10), datetime.datetime(1983, 6, 9, 9, 43, 33, 10)]
+#array_char31 char_array
+#input =['abc', 'defg']
+#output=['abc - a', 'defg - d']
+#array_vc31 vc_array
+#input =['hello', 'world']
+#output=['hello - h', 'world - w']
+#array_vc31 vc_array
+#input =[b'hello', b'world']
+#output=[b'hello - h', b'world - w']
+#array_vcfbd31 vcfbd_array
+#input =[b'basketball', b'baseball', b'football', b'pingpong', b'lacrosse']
+#output=[b'basketball - b', b'baseball - b', b'football - f', b'pingpong - p', b'lacrosse - l']
+#array_clob31 clob_array
+#input =[b'long text here', b'another clob']
+#output=[b'another clob', b'long text here']
+#array_blob31 blob_array
+#input =[b'binarydata', b'morebytes', b'abc']
+#output=[b'abc', b'morebytes', b'binarydata']
+#array_char31 char_array
+#input =[b'abc', None, b'defg']
+#output=[b'abc - a ', None, b'defg - d ']
+#
+#Results: 20 passed, 0 skipped, 0 failed out of 20
+#PASSED
diff --git a/asyncio_testsuite/test_34_async_sparray_input_output_computations.py b/asyncio_testsuite/test_34_async_sparray_input_output_computations.py
new file mode 100644
index 00000000..cb30e197
--- /dev/null
+++ b/asyncio_testsuite/test_34_async_sparray_input_output_computations.py
@@ -0,0 +1,355 @@
+from __future__ import print_function
+import asyncio
+import sys
+import unittest
+import ibm_db
+import config
+from ibm_db_dbi import AsyncConnection
+from datetime import date, time, datetime
+from testfunctions import IbmDbTestFunctions
+
+
+class IbmDbTestCase(unittest.TestCase):
+
+ def test_34_async_sparray_input_output_computations(self):
+ obj = IbmDbTestFunctions()
+ obj.assert_expect(self.run_test_34)
+
+ def run_test_34(self):
+ ARRAY_PROCS = [
+ ("int_array", "array_int41", [1, 2, 3, 4, 5]),
+ ("sint_array", "array_sint41", [7, 512, -29000, 32000]),
+ ("bint_array", "array_bint41", [1234567890123, None, -9876543210]),
+ ("float_array", "array_float41", [1.1, 2.2, 3.3]),
+ ("double_array", "array_double41", [10.5, 20.25, 30.75]),
+ ("real_array", "array_real41", [0.5, 1.5, 2.5]),
+ ("decfloat16_array", "array_decflt1641", [1.23, None, 4.56, None]),
+ ("decfloat34_array", "array_decfloat3441", [12345678.1234, None]),
+ ("dec_array", "array_dec41", [12.34, None, 56.78]),
+ ("time_array", "array_time41", [time(12, 20, 30), time(13, 30, 45)]),
+ ("date_array", "array_date41", [date(2025, 1, 1), date(2025, 12, 31)]),
+ ("ts_array", "array_ts41", [datetime(1989, 2, 12, 23, 55, 59, 342380),
+ datetime(1990, 2, 12, 23, 55, 59, 342380)]),
+ ("ts_array", "array_ts41", [b'1981-07-08 10:42:34.000010',
+ b'1982-07-08 10:42:34.000010']),
+ ("char_array", "array_char41", ["abc", "defg"]),
+ ("char_array", "array_char41", [b'abc', b'defg']),
+ ("vc_array", "array_vc41", ["hello", "world"]),
+ ("vc_array", "array_vc41", [b'hello', b'world']),
+ ("vcfbd_array", "array_vcfbd41", [b'basketball', b'baseball', b'football',
+ b'pingpong', b'lacrosse']),
+ ("blob_array", "array_blob41", [b"binarydata", b"morebytes", None, b'abc']),
+ ("clob_array", "array_clob41", [b'long text here', b'another clob']),
+ ]
+
+ async def main():
+ conn = await AsyncConnection.connect(
+ "DATABASE=%s;HOSTNAME=%s;PORT=%d;PROTOCOL=TCPIP;UID=%s;PWD=%s;" % (
+ config.database, config.hostname, config.port,
+ config.user, config.password),
+ '', '')
+
+ passed = 0
+ failed = 0
+ skipped = 0
+
+ for type_name, proc_name, input_array in ARRAY_PROCS:
+ cursor = await conn.cursor()
+ try:
+ sql = "CALL %s.%s(?, ?)" % (config.user.upper(), proc_name.upper())
+ await cursor.prepare(sql)
+
+ await cursor.bind_param(1, input_array, ibm_db.SQL_PARAM_INPUT)
+ await cursor.bind_param(2, input_array, ibm_db.SQL_PARAM_OUTPUT)
+
+ await cursor.execute()
+ result = await cursor.fetch_callproc()
+
+ # result = (stmt, input_array_echo, output_array)
+ out_arr = result[2]
+ print("%-25s %-20s" % (proc_name, type_name))
+ print("input =%r" % (input_array,))
+ print("output=%r" % (out_arr,))
+
+ if isinstance(out_arr, list) and len(out_arr) > 0:
+ passed += 1
+ else:
+ print(" *** UNEXPECTED output: %r" % (out_arr,))
+ failed += 1
+ except Exception as e:
+ err_str = str(e)
+ if 'SQL0440N' in err_str or 'CLI0102E' in err_str:
+ print("%-25s %-20s SKIP: procedure not available" % (proc_name, type_name))
+ skipped += 1
+ else:
+ print("%-25s %-20s ERROR: %s" % (proc_name, type_name, e))
+ failed += 1
+ finally:
+ await cursor.close()
+
+ await conn.close()
+ total = len(ARRAY_PROCS)
+ print("\nResults: %d passed, %d skipped, %d failed out of %d" % (passed, skipped, failed, total))
+ if failed == 0:
+ print("PASSED" if passed > 0 else "ALL SKIPPED")
+ else:
+ print("FAILED")
+ asyncio.run(main())
+
+#__END__
+#__LUW_EXPECTED__
+#array_int41 int_array
+#input =[1, 2, 3, 4, 5]
+#output=[0, 1, 2, 3, 4]
+#array_sint41 sint_array
+#input =[7, 512, -29000, 32000]
+#output=[6, 511, -29001, 31999]
+#array_bint41 bint_array
+#input =[1234567890123, None, -9876543210]
+#output=[1234567890122, None, -9876543211]
+#array_float41 float_array
+#input =[1.1, 2.2, 3.3]
+#output=[-0.5699999999999998, 0.5300000000000002, 1.63]
+#array_double41 double_array
+#input =[10.5, 20.25, 30.75]
+#output=[8.83, 18.58, 29.08]
+#array_real41 real_array
+#input =[0.5, 1.5, 2.5]
+#output=[-1.1699999570846558, -0.17000000178813934, 0.8299999833106995]
+#array_decflt1641 decfloat16_array
+#input =[1.23, None, 4.56, None]
+#output=[-0.44, None, 2.89, None]
+#array_decfloat3441 decfloat34_array
+#input =[12345678.1234, None]
+#output=[12345676.4534, None]
+#array_dec41 dec_array
+#input =[12.34, None, 56.78]
+#output=[10.67, None, 55.11]
+#array_time41 time_array
+#input =[datetime.time(12, 20, 30), datetime.time(13, 30, 45)]
+#output=[datetime.time(13, 19, 31), datetime.time(14, 29, 46)]
+#array_date41 date_array
+#input =[datetime.date(2025, 1, 1), datetime.date(2025, 12, 31)]
+#output=[datetime.date(2024, 1, 31), datetime.date(2025, 1, 30)]
+#array_ts41 ts_array
+#input =[datetime.datetime(1989, 2, 12, 23, 55, 59, 342380), datetime.datetime(1990, 2, 12, 23, 55, 59, 342380)]
+#output=[datetime.datetime(1988, 3, 12, 0, 55, 0, 342380), datetime.datetime(1989, 3, 12, 0, 55, 0, 342380)]
+#array_ts41 ts_array
+#input =[b'1981-07-08 10:42:34.000010', b'1982-07-08 10:42:34.000010']
+#output=[datetime.datetime(1980, 8, 7, 11, 41, 35, 10), datetime.datetime(1981, 8, 7, 11, 41, 35, 10)]
+#array_char41 char_array
+#input =['abc', 'defg']
+#output=['abc - c', 'defg - g']
+#array_char41 char_array
+#input =[b'abc', b'defg']
+#output=[b'abc - c ', b'defg - g ']
+#array_vc41 vc_array
+#input =['hello', 'world']
+#output=['hello - o', 'world - d']
+#array_vc41 vc_array
+#input =[b'hello', b'world']
+#output=[b'hello - o', b'world - d']
+#array_vcfbd41 vcfbd_array
+#input =[b'basketball', b'baseball', b'football', b'pingpong', b'lacrosse']
+#output=[b'basketball - l', b'baseball - l', b'football - l', b'pingpong - g', b'lacrosse - e']
+#array_blob41 blob_array
+#input =[b'binarydata', b'morebytes', None, b'abc']
+#output=[b'binarydata', b'morebytes', None, b'abc']
+#array_clob41 clob_array
+#input =[b'long text here', b'another clob']
+#output=[b'long text here', b'another clob']
+#
+#Results: 20 passed, 0 skipped, 0 failed out of 20
+#PASSED
+#__ZOS_EXPECTED__
+#array_int41 int_array
+#input =[1, 2, 3, 4, 5]
+#output=[0, 1, 2, 3, 4]
+#array_sint41 sint_array
+#input =[7, 512, -29000, 32000]
+#output=[6, 511, -29001, 31999]
+#array_bint41 bint_array
+#input =[1234567890123, None, -9876543210]
+#output=[1234567890122, None, -9876543211]
+#array_float41 float_array
+#input =[1.1, 2.2, 3.3]
+#output=[-0.5699999999999998, 0.5300000000000002, 1.63]
+#array_double41 double_array
+#input =[10.5, 20.25, 30.75]
+#output=[8.83, 18.58, 29.08]
+#array_real41 real_array
+#input =[0.5, 1.5, 2.5]
+#output=[-1.1699999570846558, -0.17000000178813934, 0.8299999833106995]
+#array_decflt1641 decfloat16_array
+#input =[1.23, None, 4.56, None]
+#output=[-0.44, None, 2.89, None]
+#array_decfloat3441 decfloat34_array
+#input =[12345678.1234, None]
+#output=[12345676.4534, None]
+#array_dec41 dec_array
+#input =[12.34, None, 56.78]
+#output=[10.67, None, 55.11]
+#array_time41 time_array
+#input =[datetime.time(12, 20, 30), datetime.time(13, 30, 45)]
+#output=[datetime.time(13, 19, 31), datetime.time(14, 29, 46)]
+#array_date41 date_array
+#input =[datetime.date(2025, 1, 1), datetime.date(2025, 12, 31)]
+#output=[datetime.date(2024, 1, 31), datetime.date(2025, 1, 30)]
+#array_ts41 ts_array
+#input =[datetime.datetime(1989, 2, 12, 23, 55, 59, 342380), datetime.datetime(1990, 2, 12, 23, 55, 59, 342380)]
+#output=[datetime.datetime(1988, 3, 12, 0, 55, 0, 342380), datetime.datetime(1989, 3, 12, 0, 55, 0, 342380)]
+#array_ts41 ts_array
+#input =[b'1981-07-08 10:42:34.000010', b'1982-07-08 10:42:34.000010']
+#output=[datetime.datetime(1980, 8, 7, 11, 41, 35, 10), datetime.datetime(1981, 8, 7, 11, 41, 35, 10)]
+#array_char41 char_array
+#input =['abc', 'defg']
+#output=['abc - c', 'defg - g']
+#array_char41 char_array
+#input =[b'abc', b'defg']
+#output=[b'abc - c ', b'defg - g ']
+#array_vc41 vc_array
+#input =['hello', 'world']
+#output=['hello - o', 'world - d']
+#array_vc41 vc_array
+#input =[b'hello', b'world']
+#output=[b'hello - o', b'world - d']
+#array_vcfbd41 vcfbd_array
+#input =[b'basketball', b'baseball', b'football', b'pingpong', b'lacrosse']
+#output=[b'basketball - l', b'baseball - l', b'football - l', b'pingpong - g', b'lacrosse - e']
+#array_blob41 blob_array
+#input =[b'binarydata', b'morebytes', None, b'abc']
+#output=[b'binarydata', b'morebytes', None, b'abc']
+#array_clob41 clob_array
+#input =[b'long text here', b'another clob']
+#output=[b'long text here', b'another clob']
+#
+#Results: 20 passed, 0 skipped, 0 failed out of 20
+#PASSED
+#__SYSTEMI_EXPECTED__
+#array_int41 int_array
+#input =[1, 2, 3, 4, 5]
+#output=[0, 1, 2, 3, 4]
+#array_sint41 sint_array
+#input =[7, 512, -29000, 32000]
+#output=[6, 511, -29001, 31999]
+#array_bint41 bint_array
+#input =[1234567890123, None, -9876543210]
+#output=[1234567890122, None, -9876543211]
+#array_float41 float_array
+#input =[1.1, 2.2, 3.3]
+#output=[-0.5699999999999998, 0.5300000000000002, 1.63]
+#array_double41 double_array
+#input =[10.5, 20.25, 30.75]
+#output=[8.83, 18.58, 29.08]
+#array_real41 real_array
+#input =[0.5, 1.5, 2.5]
+#output=[-1.1699999570846558, -0.17000000178813934, 0.8299999833106995]
+#array_decflt1641 decfloat16_array
+#input =[1.23, None, 4.56, None]
+#output=[-0.44, None, 2.89, None]
+#array_decfloat3441 decfloat34_array
+#input =[12345678.1234, None]
+#output=[12345676.4534, None]
+#array_dec41 dec_array
+#input =[12.34, None, 56.78]
+#output=[10.67, None, 55.11]
+#array_time41 time_array
+#input =[datetime.time(12, 20, 30), datetime.time(13, 30, 45)]
+#output=[datetime.time(13, 19, 31), datetime.time(14, 29, 46)]
+#array_date41 date_array
+#input =[datetime.date(2025, 1, 1), datetime.date(2025, 12, 31)]
+#output=[datetime.date(2024, 1, 31), datetime.date(2025, 1, 30)]
+#array_ts41 ts_array
+#input =[datetime.datetime(1989, 2, 12, 23, 55, 59, 342380), datetime.datetime(1990, 2, 12, 23, 55, 59, 342380)]
+#output=[datetime.datetime(1988, 3, 12, 0, 55, 0, 342380), datetime.datetime(1989, 3, 12, 0, 55, 0, 342380)]
+#array_ts41 ts_array
+#input =[b'1981-07-08 10:42:34.000010', b'1982-07-08 10:42:34.000010']
+#output=[datetime.datetime(1980, 8, 7, 11, 41, 35, 10), datetime.datetime(1981, 8, 7, 11, 41, 35, 10)]
+#array_char41 char_array
+#input =['abc', 'defg']
+#output=['abc - c', 'defg - g']
+#array_char41 char_array
+#input =[b'abc', b'defg']
+#output=[b'abc - c ', b'defg - g ']
+#array_vc41 vc_array
+#input =['hello', 'world']
+#output=['hello - o', 'world - d']
+#array_vc41 vc_array
+#input =[b'hello', b'world']
+#output=[b'hello - o', b'world - d']
+#array_vcfbd41 vcfbd_array
+#input =[b'basketball', b'baseball', b'football', b'pingpong', b'lacrosse']
+#output=[b'basketball - l', b'baseball - l', b'football - l', b'pingpong - g', b'lacrosse - e']
+#array_blob41 blob_array
+#input =[b'binarydata', b'morebytes', None, b'abc']
+#output=[b'binarydata', b'morebytes', None, b'abc']
+#array_clob41 clob_array
+#input =[b'long text here', b'another clob']
+#output=[b'long text here', b'another clob']
+#
+#Results: 20 passed, 0 skipped, 0 failed out of 20
+#PASSED
+#__IDS_EXPECTED__
+#array_int41 int_array
+#input =[1, 2, 3, 4, 5]
+#output=[0, 1, 2, 3, 4]
+#array_sint41 sint_array
+#input =[7, 512, -29000, 32000]
+#output=[6, 511, -29001, 31999]
+#array_bint41 bint_array
+#input =[1234567890123, None, -9876543210]
+#output=[1234567890122, None, -9876543211]
+#array_float41 float_array
+#input =[1.1, 2.2, 3.3]
+#output=[-0.5699999999999998, 0.5300000000000002, 1.63]
+#array_double41 double_array
+#input =[10.5, 20.25, 30.75]
+#output=[8.83, 18.58, 29.08]
+#array_real41 real_array
+#input =[0.5, 1.5, 2.5]
+#output=[-1.1699999570846558, -0.17000000178813934, 0.8299999833106995]
+#array_decflt1641 decfloat16_array
+#input =[1.23, None, 4.56, None]
+#output=[-0.44, None, 2.89, None]
+#array_decfloat3441 decfloat34_array
+#input =[12345678.1234, None]
+#output=[12345676.4534, None]
+#array_dec41 dec_array
+#input =[12.34, None, 56.78]
+#output=[10.67, None, 55.11]
+#array_time41 time_array
+#input =[datetime.time(12, 20, 30), datetime.time(13, 30, 45)]
+#output=[datetime.time(13, 19, 31), datetime.time(14, 29, 46)]
+#array_date41 date_array
+#input =[datetime.date(2025, 1, 1), datetime.date(2025, 12, 31)]
+#output=[datetime.date(2024, 1, 31), datetime.date(2025, 1, 30)]
+#array_ts41 ts_array
+#input =[datetime.datetime(1989, 2, 12, 23, 55, 59, 342380), datetime.datetime(1990, 2, 12, 23, 55, 59, 342380)]
+#output=[datetime.datetime(1988, 3, 12, 0, 55, 0, 342380), datetime.datetime(1989, 3, 12, 0, 55, 0, 342380)]
+#array_ts41 ts_array
+#input =[b'1981-07-08 10:42:34.000010', b'1982-07-08 10:42:34.000010']
+#output=[datetime.datetime(1980, 8, 7, 11, 41, 35, 10), datetime.datetime(1981, 8, 7, 11, 41, 35, 10)]
+#array_char41 char_array
+#input =['abc', 'defg']
+#output=['abc - c', 'defg - g']
+#array_char41 char_array
+#input =[b'abc', b'defg']
+#output=[b'abc - c ', b'defg - g ']
+#array_vc41 vc_array
+#input =['hello', 'world']
+#output=['hello - o', 'world - d']
+#array_vc41 vc_array
+#input =[b'hello', b'world']
+#output=[b'hello - o', b'world - d']
+#array_vcfbd41 vcfbd_array
+#input =[b'basketball', b'baseball', b'football', b'pingpong', b'lacrosse']
+#output=[b'basketball - l', b'baseball - l', b'football - l', b'pingpong - g', b'lacrosse - e']
+#array_blob41 blob_array
+#input =[b'binarydata', b'morebytes', None, b'abc']
+#output=[b'binarydata', b'morebytes', None, b'abc']
+#array_clob41 clob_array
+#input =[b'long text here', b'another clob']
+#output=[b'long text here', b'another clob']
+#
+#Results: 20 passed, 0 skipped, 0 failed out of 20
+#PASSED
diff --git a/asyncio_testsuite/test_35_async_scalarsp_execute.py b/asyncio_testsuite/test_35_async_scalarsp_execute.py
new file mode 100644
index 00000000..03279609
--- /dev/null
+++ b/asyncio_testsuite/test_35_async_scalarsp_execute.py
@@ -0,0 +1,195 @@
+from __future__ import print_function
+import asyncio
+import sys
+import unittest
+import ibm_db
+import config
+from ibm_db_dbi import AsyncConnection
+from datetime import date, time, datetime
+from testfunctions import IbmDbTestFunctions
+
+
+class IbmDbTestCase(unittest.TestCase):
+
+ def test_35_async_scalarsp_execute(self):
+ obj = IbmDbTestFunctions()
+ obj.assert_expect(self.run_test_35)
+
+ def run_test_35(self):
+ SCALAR_PROCS = [
+ # Numeric types
+ ("INTEGER", "int_scalar", 42),
+ ("SMALLINT", "sint_scalar", 7),
+ ("BIGINT", "bint_scalar", 1234),
+ ("FLOAT", "float_scalar", 3.14),
+ ("DOUBLE", "double_scalar", 20.25),
+ ("REAL", "real_scalar", 1.5),
+ ("DECFLOAT(16)", "decfloat16_scalar", 4.56),
+ ("DECFLOAT(34)", "decfloat34_scalar", 123456.1234),
+ ("DECIMAL(10,2)", "decimal_scalar", 56.78),
+ # Temporal types
+ ("DATE", "date_scalar", date(2025, 1, 1)),
+ ("TIME", "time_scalar", time(12, 20, 30)),
+ ("TIMESTAMP", "ts_scalar", datetime(1989, 2, 12, 23, 55, 59)),
+ ("TIMESTAMP", "ts_scalar", b'1989-02-12 23:55:59'),
+ # Character/string types
+ ("CHAR(20)", "char_scalar", "hello"),
+ ("CHAR(20)", "char_scalar", b'hello'),
+ ("VARCHAR(20)", "vc_scalar", "testdata"),
+ ("VARCHAR(20)", "vc_scalar", b'testdata'),
+ ("CLOB", "clob_scalar", b'long clob text data'),
+ # Binary types
+ ("BLOB", "blob_scalar", b'binarydata'),
+ ("VARBINARY", "vcfbd_scalar", b'varbindata'),
+ ]
+
+ async def main():
+ conn = await AsyncConnection.connect(
+ "DATABASE=%s;HOSTNAME=%s;PORT=%d;PROTOCOL=TCPIP;UID=%s;PWD=%s;" % (
+ config.database, config.hostname, config.port,
+ config.user, config.password),
+ '', '')
+
+ passed = 0
+ failed = 0
+ skipped = 0
+
+ for base_type, proc_name, input_val in SCALAR_PROCS:
+ cursor = await conn.cursor()
+ try:
+ sql = "CALL %s.%s(?, ?)" % (config.user.upper(), proc_name.upper())
+ await cursor.prepare(sql)
+
+ await cursor.bind_param(1, input_val, ibm_db.SQL_PARAM_INPUT)
+ await cursor.bind_param(2, input_val, ibm_db.SQL_PARAM_OUTPUT)
+
+ await cursor.execute()
+ result = await cursor.fetch_callproc()
+
+ # result = (stmt, input_echo, output_value)
+ in_echo = result[1]
+ out_val = result[2]
+ print("%-22s %-15s input=%r output=%r" % (proc_name, base_type, in_echo, out_val))
+
+ if out_val is not None:
+ passed += 1
+ else:
+ print(" *** UNEXPECTED: output is None")
+ failed += 1
+ except Exception as e:
+ err_str = str(e)
+ if 'SQL0440N' in err_str or 'CLI0102E' in err_str:
+ print("%-22s %-15s SKIP: procedure not available" % (proc_name, base_type))
+ skipped += 1
+ else:
+ print(" %-22s %-15s ERROR: %s" % (proc_name, base_type, e))
+ failed += 1
+ finally:
+ await cursor.close()
+
+ await conn.close()
+ total = len(SCALAR_PROCS)
+ print("\nResults: %d passed, %d skipped, %d failed out of %d" % (passed, skipped, failed, total))
+ if failed == 0:
+ print("PASSED" if passed > 0 else "ALL SKIPPED")
+ else:
+ print("FAILED")
+ asyncio.run(main())
+
+#__END__
+#__LUW_EXPECTED__
+#int_scalar INTEGER input=42 output=43
+#sint_scalar SMALLINT input=7 output=8
+#bint_scalar BIGINT input=1234 output=1235
+#float_scalar FLOAT input=3.14 output=4.140000000000001
+#double_scalar DOUBLE input=20.25 output=21.25
+#real_scalar REAL input=1.5 output=2.5
+#decfloat16_scalar DECFLOAT(16) input=4.56 output=5.56
+#decfloat34_scalar DECFLOAT(34) input=123456.1234 output=123457.1234
+#decimal_scalar DECIMAL(10,2) input=56.78 output=57.78
+#date_scalar DATE input=datetime.date(2025, 1, 1) output=datetime.date(2025, 1, 2)
+#time_scalar TIME input=datetime.time(12, 20, 30) output=datetime.time(12, 21, 30)
+#ts_scalar TIMESTAMP input=datetime.datetime(1989, 2, 12, 23, 55, 59) output=datetime.datetime(1989, 2, 12, 23, 56)
+#ts_scalar TIMESTAMP input=b'1989-02-12 23:55:59' output=datetime.datetime(1989, 2, 12, 23, 56)
+#char_scalar CHAR(20) input='hello' output='hello_OUT'
+#char_scalar CHAR(20) input=b'hello' output=b'hello_OUT '
+#vc_scalar VARCHAR(20) input='testdata' output='testdata_OUT'
+#vc_scalar VARCHAR(20) input=b'testdata' output=b'testdata_OUT'
+#clob_scalar CLOB input=b'long clob text data' output=b'long clob text data'
+#blob_scalar BLOB input=b'binarydata' output=b'binarydata'
+#vcfbd_scalar VARBINARY input=b'varbindata' output=b'varbindata'
+#
+#Results: 20 passed, 0 skipped, 0 failed out of 20
+#PASSED
+#__ZOS_EXPECTED__
+#int_scalar INTEGER input=42 output=43
+#sint_scalar SMALLINT input=7 output=8
+#bint_scalar BIGINT input=1234 output=1235
+#float_scalar FLOAT input=3.14 output=4.140000000000001
+#double_scalar DOUBLE input=20.25 output=21.25
+#real_scalar REAL input=1.5 output=2.5
+#decfloat16_scalar DECFLOAT(16) input=4.56 output=5.56
+#decfloat34_scalar DECFLOAT(34) input=123456.1234 output=123457.1234
+#decimal_scalar DECIMAL(10,2) input=56.78 output=57.78
+#date_scalar DATE input=datetime.date(2025, 1, 1) output=datetime.date(2025, 1, 2)
+#time_scalar TIME input=datetime.time(12, 20, 30) output=datetime.time(12, 21, 30)
+#ts_scalar TIMESTAMP input=datetime.datetime(1989, 2, 12, 23, 55, 59) output=datetime.datetime(1989, 2, 12, 23, 56)
+#ts_scalar TIMESTAMP input=b'1989-02-12 23:55:59' output=datetime.datetime(1989, 2, 12, 23, 56)
+#char_scalar CHAR(20) input='hello' output='hello_OUT'
+#char_scalar CHAR(20) input=b'hello' output=b'hello_OUT '
+#vc_scalar VARCHAR(20) input='testdata' output='testdata_OUT'
+#vc_scalar VARCHAR(20) input=b'testdata' output=b'testdata_OUT'
+#clob_scalar CLOB input=b'long clob text data' output=b'long clob text data'
+#blob_scalar BLOB input=b'binarydata' output=b'binarydata'
+#vcfbd_scalar VARBINARY input=b'varbindata' output=b'varbindata'
+#
+#Results: 20 passed, 0 skipped, 0 failed out of 20
+#PASSED
+#__SYSTEMI_EXPECTED__
+#int_scalar INTEGER input=42 output=43
+#sint_scalar SMALLINT input=7 output=8
+#bint_scalar BIGINT input=1234 output=1235
+#float_scalar FLOAT input=3.14 output=4.140000000000001
+#double_scalar DOUBLE input=20.25 output=21.25
+#real_scalar REAL input=1.5 output=2.5
+#decfloat16_scalar DECFLOAT(16) input=4.56 output=5.56
+#decfloat34_scalar DECFLOAT(34) input=123456.1234 output=123457.1234
+#decimal_scalar DECIMAL(10,2) input=56.78 output=57.78
+#date_scalar DATE input=datetime.date(2025, 1, 1) output=datetime.date(2025, 1, 2)
+#time_scalar TIME input=datetime.time(12, 20, 30) output=datetime.time(12, 21, 30)
+#ts_scalar TIMESTAMP input=datetime.datetime(1989, 2, 12, 23, 55, 59) output=datetime.datetime(1989, 2, 12, 23, 56)
+#ts_scalar TIMESTAMP input=b'1989-02-12 23:55:59' output=datetime.datetime(1989, 2, 12, 23, 56)
+#char_scalar CHAR(20) input='hello' output='hello_OUT'
+#char_scalar CHAR(20) input=b'hello' output=b'hello_OUT '
+#vc_scalar VARCHAR(20) input='testdata' output='testdata_OUT'
+#vc_scalar VARCHAR(20) input=b'testdata' output=b'testdata_OUT'
+#clob_scalar CLOB input=b'long clob text data' output=b'long clob text data'
+#blob_scalar BLOB input=b'binarydata' output=b'binarydata'
+#vcfbd_scalar VARBINARY input=b'varbindata' output=b'varbindata'
+#
+#Results: 20 passed, 0 skipped, 0 failed out of 20
+#PASSED
+#__IDS_EXPECTED__
+#int_scalar INTEGER input=42 output=43
+#sint_scalar SMALLINT input=7 output=8
+#bint_scalar BIGINT input=1234 output=1235
+#float_scalar FLOAT input=3.14 output=4.140000000000001
+#double_scalar DOUBLE input=20.25 output=21.25
+#real_scalar REAL input=1.5 output=2.5
+#decfloat16_scalar DECFLOAT(16) input=4.56 output=5.56
+#decfloat34_scalar DECFLOAT(34) input=123456.1234 output=123457.1234
+#decimal_scalar DECIMAL(10,2) input=56.78 output=57.78
+#date_scalar DATE input=datetime.date(2025, 1, 1) output=datetime.date(2025, 1, 2)
+#time_scalar TIME input=datetime.time(12, 20, 30) output=datetime.time(12, 21, 30)
+#ts_scalar TIMESTAMP input=datetime.datetime(1989, 2, 12, 23, 55, 59) output=datetime.datetime(1989, 2, 12, 23, 56)
+#ts_scalar TIMESTAMP input=b'1989-02-12 23:55:59' output=datetime.datetime(1989, 2, 12, 23, 56)
+#char_scalar CHAR(20) input='hello' output='hello_OUT'
+#char_scalar CHAR(20) input=b'hello' output=b'hello_OUT '
+#vc_scalar VARCHAR(20) input='testdata' output='testdata_OUT'
+#vc_scalar VARCHAR(20) input=b'testdata' output=b'testdata_OUT'
+#clob_scalar CLOB input=b'long clob text data' output=b'long clob text data'
+#blob_scalar BLOB input=b'binarydata' output=b'binarydata'
+#vcfbd_scalar VARBINARY input=b'varbindata' output=b'varbindata'
+#
+#Results: 20 passed, 0 skipped, 0 failed out of 20
+#PASSED
diff --git a/asyncio_testsuite/test_async_concurrent_mixed.py b/asyncio_testsuite/test_async_concurrent_mixed.py
new file mode 100644
index 00000000..db97231d
--- /dev/null
+++ b/asyncio_testsuite/test_async_concurrent_mixed.py
@@ -0,0 +1,101 @@
+from __future__ import print_function
+import asyncio
+import time
+import sys
+import unittest
+import config
+from ibm_db_dbi import AsyncConnection
+from testfunctions import IbmDbTestFunctions
+
+
+class IbmDbTestCase(unittest.TestCase):
+
+ def test_async_concurrent_mixed(self):
+ obj = IbmDbTestFunctions()
+ obj.assert_expectf(self.run_test_misc)
+
+ def run_test_misc(self):
+ async def slow_query(conn):
+ label = "slow_query"
+ print(" [%s] started" % label)
+ t0 = time.perf_counter()
+
+ cursor = await conn.cursor()
+ await cursor.execute("""
+ SELECT COUNT(*) AS cnt, AVG(a.SALARY) AS avg_salary
+ FROM STAFF a, STAFF b, STAFF c
+ """)
+ row = await cursor.fetchone()
+ await cursor.close()
+
+ elapsed = time.perf_counter() - t0
+ print(" [%s] finished in %.3fs => %s" % (label, elapsed, row))
+ return row
+
+ async def fast_query_1(conn):
+ label = "fast_query_1"
+ print(" [%s] started" % label)
+ t0 = time.perf_counter()
+
+ cursor = await conn.cursor()
+ await cursor.execute(
+ "SELECT ID, NAME, DEPT, JOB FROM STAFF WHERE ID = ?",
+ (10,)
+ )
+ row = await cursor.fetchone()
+ await cursor.close()
+
+ elapsed = time.perf_counter() - t0
+ print(" [%s] finished in %.3fs => %s" % (label, elapsed, row))
+ return row
+
+ async def fast_query_2(conn):
+ label = "fast_query_2"
+ print(" [%s] started" % label)
+ t0 = time.perf_counter()
+
+ cursor = await conn.cursor()
+ await cursor.execute("SELECT COUNT(*) AS total FROM STAFF")
+ row = await cursor.fetchone()
+ await cursor.close()
+
+ elapsed = time.perf_counter() - t0
+ print(" [%s] finished in %.3fs => %s" % (label, elapsed, row))
+ return row
+
+ async def main():
+ conn = await AsyncConnection.connect(
+ "DATABASE=%s;HOSTNAME=%s;PORT=%d;PROTOCOL=TCPIP;UID=%s;PWD=%s;" % (
+ config.database, config.hostname, config.port,
+ config.user, config.password),
+ '', '')
+
+ overall_start = time.perf_counter()
+
+ slow_result, fast1_result, fast2_result = await asyncio.gather(
+ slow_query(conn),
+ fast_query_1(conn),
+ fast_query_2(conn),
+ )
+
+ overall_elapsed = time.perf_counter() - overall_start
+
+ print("\n--- Results ---")
+ print(" Slow query : %s" % (slow_result,))
+ print(" Fast query 1: %s" % (fast1_result,))
+ print(" Fast query 2: %s" % (fast2_result,))
+ print("\n Total wall-clock time: %.3fs" % overall_elapsed)
+ print(" (Should be close to the slow query time, not the sum of all three)")
+
+ await conn.close()
+ asyncio.run(main())
+
+#__END__
+#__LUW_EXPECTED__
+#%s
+#__ZOS_EXPECTED__
+#%s
+#__SYSTEMI_EXPECTED__
+#%s
+#__IDS_EXPECTED__
+#%s
diff --git a/ibm_db_ctx.py b/ibm_db_ctx.py
index 18c99d22..9a1840d2 100644
--- a/ibm_db_ctx.py
+++ b/ibm_db_ctx.py
@@ -14,7 +14,7 @@ def __init__(self, dsn: str, username: str, password: str) -> None:
def __enter__(self) -> 'IBM_DBConnection':
"""Connect to DB2."""
- self.__cxn = ibm_db.connect(self.__dsn, '', '')
+ self.__cxn = ibm_db.connect(self.__dsn, self.__username, self.__password)
#print("enter method called")
return self.__cxn
diff --git a/ibm_db_dbi.py b/ibm_db_dbi.py
index ee7e43f6..aae2d79b 100644
--- a/ibm_db_dbi.py
+++ b/ibm_db_dbi.py
@@ -22,6 +22,7 @@
"""
import types, string, time, datetime, decimal, sys
+import asyncio
import weakref
import logging as log_ibmdb_dbi
@@ -1785,33 +1786,41 @@ def stmt_error(self):
LogMsg(INFO, "exit stmt_error()")
return sqlstate
- def execute(self, operation, parameters=None):
+ def execute(self, operation=None, parameters=None):
"""
This method can be used to prepare and execute an SQL
statement. It takes the SQL statement(operation) and a
sequence of values to substitute for the parameter markers in
the SQL statement as arguments.
+
+ If called with no arguments after prepare() + bind_param(),
+ it executes the already-prepared statement.
"""
LogMsg(INFO, "entry execute()")
- LogMsg(DEBUG, f"Executing SQL operation: {operation}")
- if parameters is not None:
- LogMsg(DEBUG, f"SQL parameters: {parameters}")
- self.messages = []
- if not isinstance(operation, string_types):
- err_msg = "execute expects the first argument [%s] to be of type String or Unicode." % operation
- LogMsg(ERROR, err_msg)
- self.messages.append(
- InterfaceError("execute expects the first argument [%s] to be of type String or Unicode." % operation))
- raise self.messages[len(self.messages) - 1]
- if parameters is not None:
- if not isinstance(parameters, (list, tuple, dict)):
- LogMsg(ERROR, "execute parameters argument should be sequence.")
- self.messages.append(InterfaceError("execute parameters argument should be sequence."))
+ if operation is not None:
+ LogMsg(DEBUG, f"Executing SQL operation: {operation}")
+ if parameters is not None:
+ LogMsg(DEBUG, f"SQL parameters: {parameters}")
+ self.messages = []
+ if not isinstance(operation, string_types):
+ err_msg = "execute expects the first argument [%s] to be of type String or Unicode." % operation
+ LogMsg(ERROR, err_msg)
+ self.messages.append(
+ InterfaceError("execute expects the first argument [%s] to be of type String or Unicode." % operation))
raise self.messages[len(self.messages) - 1]
- self.__description = None
- self._all_stmt_handlers = []
- self._prepare_helper(operation)
- self._execute_helper(parameters)
+ if parameters is not None:
+ if not isinstance(parameters, (list, tuple, dict)):
+ LogMsg(ERROR, "execute parameters argument should be sequence.")
+ self.messages.append(InterfaceError("execute parameters argument should be sequence."))
+ raise self.messages[len(self.messages) - 1]
+ self.__description = None
+ self._all_stmt_handlers = []
+ self._prepare_helper(operation)
+ self._execute_helper(parameters)
+ else:
+ # Execute already-prepared statement (after prepare + bind_param)
+ LogMsg(DEBUG, "Executing already-prepared statement")
+ self._execute_helper(parameters)
self._set_cursor_helper()
LogMsg(INFO, "SQL operation executed successfully.")
LogMsg(INFO, "exit execute()")
@@ -2025,6 +2034,62 @@ def nextset(self):
LogMsg(INFO, "exit nextset()")
return True
+ def prepare(self, operation):
+ """Prepare an SQL statement for later execution via bind_param + execute."""
+ LogMsg(INFO, "entry prepare()")
+ self.messages = []
+ self.__description = None
+ self._all_stmt_handlers = []
+ self._prepare_helper(operation)
+ LogMsg(INFO, "exit prepare()")
+
+ def bind_param(self, index, value, param_type=None, data_type=None):
+ """Bind a parameter value for the prepared statement.
+
+ ``index`` is 1-based. ``param_type`` is an optional
+ ``ibm_db.SQL_PARAM_*`` constant (e.g. SQL_PARAM_INPUT).
+ ``data_type`` is an optional SQL data type constant
+ (e.g. ``ibm_db.SQL_INTEGER``).
+ """
+ LogMsg(INFO, "entry bind_param()")
+ try:
+ if param_type is not None and data_type is not None:
+ ibm_db.bind_param(self.stmt_handler, index, value, param_type, data_type)
+ elif param_type is not None:
+ ibm_db.bind_param(self.stmt_handler, index, value, param_type)
+ else:
+ ibm_db.bind_param(self.stmt_handler, index, value)
+ except Exception as inst:
+ LogMsg(ERROR, f"Error binding parameter at index {index}: {_get_exception(inst)}")
+ self.messages.append(_get_exception(inst))
+ raise self.messages[len(self.messages) - 1]
+ LogMsg(INFO, "exit bind_param()")
+
+ def fetch_callproc(self):
+ """Fetch the output parameters returned by a stored procedure
+ executed via the prepare/bind/execute path."""
+ LogMsg(INFO, "entry fetch_callproc()")
+ try:
+ result = ibm_db.fetch_callproc(self.stmt_handler)
+ except Exception as inst:
+ LogMsg(ERROR, f"Error in fetch_callproc: {_get_exception(inst)}")
+ self.messages.append(_get_exception(inst))
+ raise self.messages[len(self.messages) - 1]
+ LogMsg(INFO, "exit fetch_callproc()")
+ return result
+
+ def fetch_tuple(self):
+ """Fetch one row as a tuple from the current result set."""
+ LogMsg(INFO, "entry fetch_tuple()")
+ try:
+ result = ibm_db.fetch_tuple(self.stmt_handler)
+ except Exception as inst:
+ LogMsg(ERROR, f"Error in fetch_tuple: {_get_exception(inst)}")
+ self.messages.append(_get_exception(inst))
+ raise self.messages[len(self.messages) - 1]
+ LogMsg(INFO, "exit fetch_tuple()")
+ return result
+
def setinputsizes(self, sizes):
"""This method currently does nothing."""
pass
@@ -2080,3 +2145,314 @@ def __enter__(self):
def __exit__(self, exc_type, exc_val, exc_tb):
self.close()
+
+
+# Module-level async functions
+
+async def connect_async(dsn, user='', password='', host='', database='',
+ conn_options=None):
+ """Async wrapper around :func:`connect`. Returns a :class:`Connection`."""
+ LogMsg(INFO, "entry connect_async()")
+ conn_obj = await asyncio.to_thread(
+ connect, dsn, user, password, host, database, conn_options
+ )
+ LogMsg(INFO, "exit connect_async()")
+ return conn_obj
+
+
+async def pconnect_async(dsn, user='', password='', host='', database='',
+ conn_options=None):
+ """Async wrapper around :func:`pconnect`. Returns a :class:`Connection`."""
+ LogMsg(INFO, "entry pconnect_async()")
+ conn_obj = await asyncio.to_thread(
+ pconnect, dsn, user, password, host, database, conn_options
+ )
+ LogMsg(INFO, "exit pconnect_async()")
+ return conn_obj
+
+
+async def conn_errormsg_async(connection=None):
+ """Async wrapper around :func:`conn_errormsg`."""
+ return await asyncio.to_thread(conn_errormsg, connection)
+
+
+async def conn_error_async(connection=None):
+ """Async wrapper around :func:`conn_error`."""
+ return await asyncio.to_thread(conn_error, connection)
+
+
+async def get_sqlcode_async(handle=None):
+ """Async wrapper around :func:`get_sqlcode`."""
+ return await asyncio.to_thread(get_sqlcode, handle)
+
+
+async def createdb_async(database, dsn, user='', password='', host='',
+ codeset='', mode=''):
+ """Async wrapper around :func:`createdb`."""
+ return await asyncio.to_thread(
+ createdb, database, dsn, user, password, host, codeset, mode
+ )
+
+
+async def dropdb_async(database, dsn, user='', password='', host=''):
+ """Async wrapper around :func:`dropdb`."""
+ return await asyncio.to_thread(
+ dropdb, database, dsn, user, password, host
+ )
+
+
+async def recreatedb_async(database, dsn, user='', password='', host='',
+ codeset='', mode=''):
+ """Async wrapper around :func:`recreatedb`."""
+ return await asyncio.to_thread(
+ recreatedb, database, dsn, user, password, host, codeset, mode
+ )
+
+
+async def createdbNX_async(database, dsn, user='', password='', host='',
+ codeset='', mode=''):
+ """Async wrapper around :func:`createdbNX`."""
+ return await asyncio.to_thread(
+ createdbNX, database, dsn, user, password, host, codeset, mode
+ )
+
+
+# AsyncCursor
+
+class AsyncCursor:
+ """Async wrapper around :class:`Cursor`.
+
+ Every public method of the sync Cursor that performs I/O is exposed as
+ a coroutine. ``prepare``, ``bind_param`` and ``fetch_callproc`` are
+ added here for the prepare/bind/execute workflow.
+ """
+
+ def __init__(self, sync_cursor):
+ self._cursor = sync_cursor
+
+ #properties (sync, no I/O)
+
+ @property
+ def description(self):
+ return self._cursor.description
+
+ @property
+ def rowcount(self):
+ return self._cursor.rowcount
+
+ @property
+ def arraysize(self):
+ return self._cursor.arraysize
+
+ @arraysize.setter
+ def arraysize(self, value):
+ self._cursor.arraysize = value
+
+ @property
+ def stmt_handler(self):
+ return self._cursor.stmt_handler
+
+ @property
+ def conn_handler(self):
+ return self._cursor.conn_handler
+
+ @property
+ def messages(self):
+ return self._cursor.messages
+
+ # async I/O methods
+
+ async def execute(self, operation=None, parameters=None):
+ """Execute SQL. Supports three calling conventions:
+
+ * ``await cursor.execute(sql)``
+ * ``await cursor.execute(sql, params)``
+ * ``await cursor.execute()`` (after prepare + bind_param)
+ """
+ return await asyncio.to_thread(
+ self._cursor.execute, operation, parameters
+ )
+
+ async def executemany(self, operation, seq_parameters):
+ return await asyncio.to_thread(
+ self._cursor.executemany, operation, seq_parameters
+ )
+
+ async def fetchone(self):
+ return await asyncio.to_thread(self._cursor.fetchone)
+
+ async def fetchmany(self, size=0):
+ return await asyncio.to_thread(self._cursor.fetchmany, size)
+
+ async def fetchall(self):
+ return await asyncio.to_thread(self._cursor.fetchall)
+
+ async def callproc(self, procname, parameters=None):
+ return await asyncio.to_thread(
+ self._cursor.callproc, procname, parameters
+ )
+
+ async def nextset(self):
+ return await asyncio.to_thread(self._cursor.nextset)
+
+ async def close(self):
+ return await asyncio.to_thread(self._cursor.close)
+
+ async def prepare(self, operation):
+ """Prepare an SQL statement for later execution."""
+ return await asyncio.to_thread(self._cursor.prepare, operation)
+
+ async def bind_param(self, index, value, param_type=None, data_type=None):
+ """Bind a parameter value for the prepared statement.
+
+ ``index`` is 1-based. ``param_type`` is an optional
+ ``ibm_db.SQL_PARAM_*`` constant. ``data_type`` is an optional
+ SQL data type constant (e.g. ``ibm_db.SQL_INTEGER``).
+ """
+ return await asyncio.to_thread(
+ self._cursor.bind_param, index, value, param_type, data_type
+ )
+
+ async def fetch_callproc(self):
+ """Fetch the output parameters returned by a CALL statement
+ executed via the prepare/bind/execute path."""
+ return await asyncio.to_thread(self._cursor.fetch_callproc)
+
+ async def fetch_tuple(self):
+ """Fetch one row as a tuple from the current result set."""
+ return await asyncio.to_thread(self._cursor.fetch_tuple)
+
+ async def stmt_errormsg(self):
+ return await asyncio.to_thread(self._cursor.stmt_errormsg)
+
+ async def stmt_error(self):
+ return await asyncio.to_thread(self._cursor.stmt_error)
+
+ #async iteration & context manager
+
+ def __aiter__(self):
+ return self
+
+ async def __anext__(self):
+ row = await self.fetchone()
+ if row is None:
+ raise StopAsyncIteration
+ return row
+
+ async def __aenter__(self):
+ return self
+
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
+ await self.close()
+
+#AsyncConnection
+
+class AsyncConnection:
+ """Async wrapper around :class:`Connection`.
+
+ Obtain an instance via the :meth:`connect` class method::
+
+ conn = await AsyncConnection.connect(dsn, user, password)
+ """
+
+ def __init__(self, sync_conn):
+ self._conn = sync_conn
+
+ #properties (read-only, no I/O)
+
+ @property
+ def conn_handler(self):
+ return self._conn.conn_handler
+
+ @property
+ def dbms_name(self):
+ return self._conn.dbms_name
+
+ @property
+ def dbms_ver(self):
+ return self._conn.dbms_ver
+
+ #class-level factory
+
+ @classmethod
+ async def connect(cls, dsn, user='', password='', host='', database='',
+ conn_options=None):
+ """Create a new async connection. Returns an :class:`AsyncConnection`."""
+ sync_conn = await asyncio.to_thread(
+ connect, dsn, user, password, host, database, conn_options
+ )
+ return cls(sync_conn)
+
+ # async methods wrapping Connection
+
+ async def cursor(self):
+ sync_cursor = self._conn.cursor()
+ return AsyncCursor(sync_cursor)
+
+ async def close(self):
+ return await asyncio.to_thread(self._conn.close)
+
+ async def commit(self):
+ return await asyncio.to_thread(self._conn.commit)
+
+ async def rollback(self):
+ return await asyncio.to_thread(self._conn.rollback)
+
+ async def set_autocommit(self, is_on):
+ return await asyncio.to_thread(self._conn.set_autocommit, is_on)
+
+ async def set_option(self, attr_dict):
+ return await asyncio.to_thread(self._conn.set_option, attr_dict)
+
+ async def get_option(self, attr_key):
+ return await asyncio.to_thread(self._conn.get_option, attr_key)
+
+ async def set_current_schema(self, schema_name):
+ return await asyncio.to_thread(
+ self._conn.set_current_schema, schema_name
+ )
+
+ async def get_current_schema(self):
+ return await asyncio.to_thread(self._conn.get_current_schema)
+
+ async def server_info(self):
+ return await asyncio.to_thread(self._conn.server_info)
+
+ async def tables(self, schema_name=None, table_name=None):
+ return await asyncio.to_thread(
+ self._conn.tables, schema_name, table_name
+ )
+
+ async def columns(self, schema_name=None, table_name=None,
+ column_names=None):
+ return await asyncio.to_thread(
+ self._conn.columns, schema_name, table_name, column_names
+ )
+
+ async def primary_keys(self, unique=True, schema_name=None,
+ table_name=None):
+ return await asyncio.to_thread(
+ self._conn.primary_keys, unique, schema_name, table_name
+ )
+
+ async def indexes(self, unique=True, schema_name=None, table_name=None):
+ return await asyncio.to_thread(
+ self._conn.indexes, unique, schema_name, table_name
+ )
+
+ async def foreign_keys(self, unique=True, schema_name=None,
+ table_name=None):
+ return await asyncio.to_thread(
+ self._conn.foreign_keys, unique, schema_name, table_name
+ )
+
+ async def set_fix_return_type(self, is_on):
+ return await asyncio.to_thread(self._conn.set_fix_return_type, is_on)
+
+ #async context manager
+
+ async def __aenter__(self):
+ return self
+
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
+ await self.close()
diff --git a/ibmdb_tests.py b/ibmdb_tests.py
index 1822330f..80b37949 100644
--- a/ibmdb_tests.py
+++ b/ibmdb_tests.py
@@ -1,5 +1,6 @@
import os
from os.path import basename, join
+import argparse
import random
import sys
import unittest
@@ -21,26 +22,40 @@
except ImportError as e:
print("HtmlTestRunner Package not found .. Running with normal unit test runs")
-# Test runner for ibm_db. Normally one could use something like:
+# Test runner for ibm_db.
#
-# Run all in the tests directory:
-# python -m unittest discover -v -s tests
+# Usage:
+# python ibmdb_tests.py # run both sync + async
+# python ibmdb_tests.py --async # run only async tests
+# python ibmdb_tests.py --sync # run only sync tests
+# python ibmdb_tests.py --async --test test_05_async_fetch.py # single async test
+# python ibmdb_tests.py --sync --test test_006_ConnPassingOpts.py # single sync test
#
-# Run all tests matching specified pattern or specific test:
-# python -m unittest discover -v -s tests -p 'test_*FetchAssocSelect*.py'
-# python -m unittest discover -v -s tests -p test_006_ConnPassingOpts.py
-#
-# However, for running all the tests, we need to ensure that test_000_PrepareDb
-# is run first to set up the database. Possibly this could be moved to a
-# separate task which is run by itself prior to running any tests, which
-# would simplify things a bit.
+# Legacy: SINGLE_PYTHON_TEST env var is also supported for backward compatibility.
+# set SINGLE_PYTHON_TEST=test_006_ConnPassingOpts.py
+# python ibmdb_tests.py
-# Override standard test-loading behavior
-def load_tests(loader, tests, pattern):
- suite = unittest.TestSuite()
+# Parse command-line arguments before unittest sees them
+_parser = argparse.ArgumentParser(add_help=False)
+_parser.add_argument('--async', dest='async_only', action='store_true',
+ help='Run only async tests')
+_parser.add_argument('--sync', dest='sync_only', action='store_true',
+ help='Run only sync tests')
+_parser.add_argument('--test', dest='single_test', default=None,
+ help='Run a single test file (glob pattern)')
+_args, _remaining_argv = _parser.parse_known_args()
+sys.argv = [sys.argv[0]] + _remaining_argv # pass remaining args to unittest
+# Support legacy SINGLE_PYTHON_TEST env var
+if _args.single_test is None:
+ _args.single_test = os.environ.get('SINGLE_PYTHON_TEST')
+
+# Override standard test-loading behavior
+def _load_sync_tests(suite, test_glob=None):
+ """Load sync tests from ibm_db_tests/."""
test_glob_default = "test_*.py"
- test_glob = os.environ.get("SINGLE_PYTHON_TEST", "").strip() or test_glob_default
+ if test_glob is None:
+ test_glob = test_glob_default
# We need files of a given size for some of the test units, so create them
# here.
with open("ibm_db_tests/spook.png", "wb") as f:
@@ -68,9 +83,41 @@ def load_tests(loader, tests, pattern):
mod = importlib.import_module(test)
suite.addTest(mod.IbmDbTestCase(test))
+
+def _load_async_tests(suite, test_glob=None):
+ """Load async tests from asyncio_testsuite/."""
+ if test_glob is None:
+ test_glob = "test_*.py"
+ async_test_dir = 'asyncio_testsuite'
+ if async_test_dir not in sys.path:
+ sys.path.insert(0, async_test_dir)
+ async_files = glob.glob(join(async_test_dir, test_glob))
+ async_tests = [basename(_).replace('.py', '') for _ in async_files]
+ async_tests = [t for t in async_tests if t not in ('test_utils', 'run_all')]
+ async_tests.sort()
+ for test in async_tests:
+ mod = importlib.import_module(test)
+ suite.addTest(mod.IbmDbTestCase(test))
+
+
+def load_tests(loader, tests, pattern):
+ suite = unittest.TestSuite()
+
+ single = _args.single_test
+
+ if _args.async_only:
+ _load_async_tests(suite, single)
+ elif _args.sync_only:
+ _load_sync_tests(suite, single)
+ else:
+ # Default: run both
+ _load_sync_tests(suite, single)
+ _load_async_tests(suite, single)
+
return suite
if __name__ == '__main__':
+ sys.path.insert(0, 'asyncio_testsuite')
if(_HTML_RUNNER):
unittest.main(testRunner=HtmlTestRunner.HTMLTestRunner(report_name='result',combine_reports=True))
else:
diff --git a/run_all_tests b/run_all_tests
index 640dcfbe..53210bf5 100755
--- a/run_all_tests
+++ b/run_all_tests
@@ -57,7 +57,7 @@ AUTOCOMMIT=1
EOF
chtag -b $DSNAOINI
out="${ENCODING}_${ATTACH}_${MULTICONTEXT}"
- python tests.py > $out.out 2>&1
+ python ibmdb_tests.py > $out.out 2>&1
./run_individual_tests $out >> $out.out 2>&1
fgrep -e ' ... ' $out.out | sed -e 's/ .* \.\.\. / /' -e 's/ skipped .*/ skipped/' > $out.summary
done
@@ -82,7 +82,7 @@ rm *.sorted
# export _BPX_SHAREAS=YES
# ./odbc_trace_control ON -I 8000000
-# python tests.py
+# python ibmdb_tests.py
# ./odbc_trace_control DMP $DIAGTRACEFILENAME
# ./odbc_trace_control FMT $DIAGTRACEFILENAME $DIAGTRACEFILENAME.out
# export _BPX_SHAREAS=NO
diff --git a/run_individual_tests b/run_individual_tests
index 0bbdf92a..78a8cd30 100755
--- a/run_individual_tests
+++ b/run_individual_tests
@@ -28,7 +28,7 @@ run_test() {
export SINGLE_PYTHON_TEST=$name
touch $FILEPREFIX$base.out $FILEPREFIX$base.err
chtag -t -c 819 $FILEPREFIX$base.out $FILEPREFIX$base.err
- python tests.py 1> $FILEPREFIX$base.out 2> $FILEPREFIX$base.err <&-
+ python ibmdb_tests.py 1> $FILEPREFIX$base.out 2> $FILEPREFIX$base.err <&-
show_file $FILEPREFIX$base.out
show_file $FILEPREFIX$base.err
}
diff --git a/testfunctions.py b/testfunctions.py
index d2f7a9c4..911a25ba 100644
--- a/testfunctions.py
+++ b/testfunctions.py
@@ -15,7 +15,13 @@
class IbmDbTestFunctions(unittest.TestCase):
- prepconn = ibm_db.connect(config.database, config.user, config.password)
+ if hasattr(config, 'hostname') and config.hostname:
+ _dsn = (f"DATABASE={config.database};HOSTNAME={config.hostname};"
+ f"PORT={config.port};PROTOCOL=TCPIP;"
+ f"UID={config.user};PWD={config.password}")
+ prepconn = ibm_db.connect(_dsn, '', '')
+ else:
+ prepconn = ibm_db.connect(config.database, config.user, config.password)
server = ibm_db.server_info(prepconn)
ibm_db.close(prepconn)
@@ -35,30 +41,31 @@ def capture(self, func):
return var
def testCasesIn(self, fileName):
- if (fileName.startswith('ibm_db_tests/test_017') or \
- fileName.startswith('ibm_db_tests/test_005') or \
- fileName.startswith('ibm_db_tests/test_018') or \
- fileName.startswith('ibm_db_tests/test_019') or \
- fileName.startswith('ibm_db_tests/test_024') or \
- fileName.startswith('ibm_db_tests/test_053') or \
- fileName.startswith('ibm_db_tests/test_054') or \
- fileName.startswith('ibm_db_tests/test_080') or \
- fileName.startswith('ibm_db_tests/test_081') or \
- fileName.startswith('ibm_db_tests/test_082') or \
- fileName.startswith('ibm_db_tests/test_090') or \
- fileName.startswith('ibm_db_tests/test_091') or \
- fileName.startswith('ibm_db_tests/test_092') or \
- fileName.startswith('ibm_db_tests/test_103') or \
- fileName.startswith('ibm_db_tests/test_116') or \
- fileName.startswith('ibm_db_tests/test_133') or \
- fileName.startswith('ibm_db_tests/test_147') or \
- fileName.startswith('ibm_db_tests/test_157a') or \
- fileName.startswith('ibm_db_tests/test_240') or \
- fileName.startswith('ibm_db_tests/test_241') or \
- fileName.startswith('ibm_db_tests/test_cursortype') or \
- fileName.startswith('ibm_db_tests/test_decfloat') or \
- fileName.startswith('ibm_db_tests/test_setgetOption') or \
- fileName.startswith('ibm_db_tests/test_warn') \
+ basename = os.path.basename(fileName)
+ if (basename.startswith('test_017') or \
+ basename.startswith('test_005') or \
+ basename.startswith('test_018') or \
+ basename.startswith('test_019') or \
+ basename.startswith('test_024') or \
+ basename.startswith('test_053') or \
+ basename.startswith('test_054') or \
+ basename.startswith('test_080') or \
+ basename.startswith('test_081') or \
+ basename.startswith('test_082') or \
+ basename.startswith('test_090') or \
+ basename.startswith('test_091') or \
+ basename.startswith('test_092') or \
+ basename.startswith('test_103') or \
+ basename.startswith('test_116') or \
+ basename.startswith('test_133') or \
+ basename.startswith('test_147') or \
+ basename.startswith('test_157a') or \
+ basename.startswith('test_240') or \
+ basename.startswith('test_241') or \
+ basename.startswith('test_cursortype') or \
+ basename.startswith('test_decfloat') or \
+ basename.startswith('test_setgetOption') or \
+ basename.startswith('test_warn') \
):
return True
else:
@@ -98,10 +105,15 @@ def expected_AS(self, fileName):
# This function grabs the expected output of the current test function for z/OS ODBC driver,
# located at the bottom of the current test file.
+ # Falls back to expected_ZOS if no #__ZOS_ODBC_EXPECTED__ section exists.
def expected_ZOS_ODBC(self, fileName):
- fileHandle = open(fileName,'r')
- fileInput = fileHandle.read().split('#__ZOS_ODBC_EXPECTED__')[-1].replace('\n', "").replace('#', '')
+ fileHandle = open(fileName, 'r')
+ fileContent = fileHandle.read()
fileHandle.close()
+ if '#__ZOS_ODBC_EXPECTED__' in fileContent:
+ fileInput = fileContent.split('#__ZOS_ODBC_EXPECTED__')[-1].replace('\n', '').replace('#', '')
+ else:
+ fileInput = fileContent.split('#__ZOS_EXPECTED__')[-1].split('#__SYSTEMI_EXPECTED__')[0].replace('\n', '').replace('#', '')
return fileInput
# This function compares the captured outout with the expected out of
@@ -111,7 +123,7 @@ def assert_expect(self, testFuncName):
try:
if (self.server.DBMS_NAME[0:2] == "AS"):
self.assertEqual(self.capture(testFuncName), self.expected_AS(callstack[1][1]))
- elif (platform.system() == 'z/OS' or platform.system() == 'OS/390' and self.testCasesIn(callstack[1][1])):
+ elif ((platform.system() == 'z/OS' or platform.system() == 'OS/390') and self.testCasesIn(callstack[1][1])):
self.assertEqual(self.capture(testFuncName), self.expected_ZOS_ODBC(callstack[1][1]))
elif (self.server.DBMS_NAME == "DB2" or "DSN" in self.server.DBMS_NAME):
self.assertEqual(self.capture(testFuncName), self.expected_ZOS(callstack[1][1]))
@@ -130,7 +142,7 @@ def assert_expectf(self, testFuncName):
try:
if (self.server.DBMS_NAME[0:2] == "AS"):
pattern = self.expected_AS(callstack[1][1])
- elif (platform.system() == 'z/OS' or platform.system() == 'OS/390' and self.testCasesIn(callstack[1][1])):
+ elif ((platform.system() == 'z/OS' or platform.system() == 'OS/390') and self.testCasesIn(callstack[1][1])):
pattern = self.expected_ZOS_ODBC(callstack[1][1])
elif (self.server.DBMS_NAME == "DB2" or "DSN" in self.server.DBMS_NAME):
pattern = self.expected_ZOS(callstack[1][1])
@@ -139,15 +151,11 @@ def assert_expectf(self, testFuncName):
else:
pattern = self.expected_LUW(callstack[1][1])
- sym = [r'\[',r'\]',r'\(',r'\)']
- for chr in sym:
- pattern = re.sub(chr, '\\' + chr, pattern)
-
- pattern = re.sub('%s', '.*?', pattern)
- if sys.version_info >=(3,7 ):
- pattern = re.sub('%d', r'\\d+', pattern)
- else:
- pattern = re.sub('%d', '\\d+', pattern)
+ pattern = pattern.replace('%s', '\x00WILDCARD_S\x00')
+ pattern = pattern.replace('%d', '\x00WILDCARD_D\x00')
+ pattern = re.escape(pattern)
+ pattern = pattern.replace('\x00WILDCARD_S\x00', '.*?')
+ pattern = pattern.replace('\x00WILDCARD_D\x00', r'\d+')
result = re.match(pattern, self.capture(testFuncName))
self.assertNotEqual(result, None)