Pyngres Reference
Because pyngres is only a wrapper around the Actian OpenAPI
the primary documentation for understanding the OpenAPI will always be
the OpenAPI User Guide.
As of this writing the OpenAPI is at version 11, which supports all versions of Ingres up to Ingres 12.x.
Tip
On-line documentation doesn’t suite everyone. You may prefer to download the PDF documentation set from the Actian Electronic Software Distribution site. (If you are not already a community member you will have to register.)
The following is the additional information relevant to using Pyngres to invoke OpenAPI functions from Python.
There are three versions of pyngres. The first is a base version in
which all the OpenAPI functions are used exactly as they are in C code.
Using the base version means all the asynchronous functions must be
waited using IIapi_wait() in a busy-wait loop.
The pyngres.blocking version makes
the functions look and behave as if they are actually synchronous. For many
simple applications
this will be perfectly suitable, reducing visual clutter and eliminating
the risk of forgetting a necessary busy-wait loop.
Finally the pyngres.asyncio
version presents the OpenAPI functions as awaitable coroutines. This unlocks
the power of Python to run concurrent Ingres tasks without the complexity of
multi-threading, and to mix Ingres database operations freely with
awaitable functions from other asyncio packages, such as GUI frameworks.
Working with Asynchronous Functions
Because an Ingres or Vector client is communicating with the database server over a network, possibly even a wide area network, there will be delays between invoking OpenAPI functions and the server completing even the simplest requests such as making a database connection or returning a row from a cursor. Far worse, the request may execute a very long-running query.
Many small delays or one very long one would interfere with the responsiveness of a GUI framework. To allow applications to be written that give the end user the best experience the OpenAPI operates asynchronously. OpenAPI functions return immediately whether the requested action has been completed or not. The application has to somehow determine, later, that the request has been completed.
Using pyngres and Busy-Wait Loops
pyngres supports two mechanisms for
determining that a request is completed. Either the OpenAPI function
is passed a callback function to execute on completion, or the
application runs a so-called busy-wait loop to repeatedly examine a
completion flag until it is set.
Callbacks are described elsewhere. This section concentrates on busy-wait loops.
Assume pyngres has been imported as shown below, defining py as a
shorthand for its namespace:
import pyngres as py
inp = py.IIAPI_INITPARM()
inp.in_timeout = -1
inp.in_version = py.IIAPI_VERSION
py.IIapi_initialize(inp)
envHandle = inp.in_envHandle
All OpenAPI busy-wait loops invoke IIapi_wait().
It returns control
to the OpenAPI so that it can set the completion flag if the operation is in
fact complete. To get ready to call it, first allocate an
IIAPI_WAITPARM
control block then set its wt_timeout to 0, which means don’t wait,
return immediately, no matter what. Functions that allocate OpenAPI control
blocks complete before returning. There is no need to check for completion.
This control block can be set up at any time before it is needed.
Note
A wt_timeout of 0 means the application will spin in
a “tight” loop. It may be preferable to allow a non-zero wait
if there is nothing else to be done in the busy-wait loop.
wtp = py.IIAPI_WAITPARM()
wtp.wt_timeout = 0
Later, allocate a control block for the function to be invoked. In this example the
function is IIapi_connect(), which takes an
IIAPI_CONNPARM
control block. The only important value to notice here is co_timeout
which is set to -1, meaning no timeout.
dbname = 'sandbox'
target = dbname.encode()
cop = py.IIAPI_CONNPARM()
cop.co_target = target
cop.co_connHandle = envHandle
cop.co_type = py.IIAPI_CT_SQL
cop.co_timeout = -1
With both control blocks allocated and initialized the OpenAPI function can be called to initiate the request. Because it is asynchronous it returns immediately even though the request may take several seconds to complete over a slow channnel.
The application then loops to repeatedly
check the co_genParm.gp_completed flag. If the flag is not
set control is passed back to the OpenAPI by calling IIapi_wait().
Control returns immediately to the application
because wt_timeout is set to 0. The loop is iterated as
often as necessary until the OpenAPI marks the request as completed.
py.IIapi_connect(cop)
while not cop.co_genParm.gp_completed:
py.IIapi_wait(wtp)
In this description of the process no useful work is shown being done in the busy-wait loop, but the application is free to do anything, like update a display or advance a spinner, before doing the next check for completion.
The approach shown above is faithful to the way OpenAPI functions are
called in C programs. If one is using pyngres to learn to use the
OpenAPI, or to prototype
something and the ultimate intention is a C implementation, it is the
recommended approach.
But if the ultimate objective is to develop an application with Python
rather than C, it is better to use pyngres.blocking, and even better to use
pyngres.asyncio.
Using pyngres.blocking
Using pyngres.blocking is almost identical to what is shown above,
except the OpenAPI functions behave as if they are synchronous so no busy-wait
loop is required. In fact the loop is still executed but it is done
automatically. The code is more concise but the application does not get
the opportunity to do useful work while waiting for completion. It will
will be less responsive. There are many applications where
that trade-off is acceptable: batch scripts being one example.
The code would resemble this:
import pyngres.blocking as py
inp = py.IIAPI_INITPARM()
inp.in_timeout = -1
inp.in_version = py.IIAPI_VERSION
py.IIapi_initialize(inp)
envHandle = inp.in_envHandle
dbname = 'sandbox'
target = dbname.encode()
cop = py.IIAPI_CONNPARM()
cop.co_target = target
cop.co_connHandle = envHandle
cop.co_type = py.IIAPI_CT_SQL
cop.co_timeout = -1
py.IIapi_connect(cop)
...
Using pyngres.asyncio
pyngres.asyncio is the most powerful way to harness the OpenAPI. It
seamlessly presents the OpenAPI functions as asyncio awaitable
coroutines.
Tip
The usefulness of asyncio is hard to overstate. Every Python
programmer would benefit from familiarity with it. One excellent resource
is Python Asyncio: The Complete Guide, maintained
by Jason Brownlee. The content is also available as a book entitled
Python Asyncio Jump-Start, ISBN-13: 979-8361197620.
This example initiates a connection to a database. While it is waiting
for the connection to be completed it displays a rotating “spinner”.
The database connection
is requested by the connect() coroutine/task and the spinner is
animated by the spin() coroutine/task.
The spinner runs
until the connection is established, at which point the connect() task
sets the connected event. The spin() task terminates its
loop when the connected event is set and then displays
“connected”.
import asyncio
import pyngres.asyncio as py
async def spin(connected):
'''display a spinning rotor until connected'''
rotor = '-\\|/'
i = 0
while not connected.is_set():
i = (i+1) % 4
output = rotor[i] + ' ' + '\b\b'
print(output, end='', flush=True)
await asyncio.sleep(0.10)
print('connected')
async def connect(connected):
'''connect to an Actian database'''
print('connecting...')
inp = py.IIAPI_INITPARM()
inp.in_timeout = -1
inp.in_version = py.IIAPI_VERSION
py.IIapi_initialize(inp)
envHandle = inp.in_envHandle
dbname = 'loathing::sandbox'
target = dbname.encode()
cop = py.IIAPI_CONNPARM()
cop.co_target = target
cop.co_connHandle = envHandle
cop.co_type = py.IIAPI_CT_SQL
cop.co_timeout = -1
await py.IIapi_connect(cop)
connected.set()
async def main():
'''start the tasks to run concurrently'''
connected = asyncio.Event()
spinner = spin(connected)
connector = connect(connected)
await asyncio.gather(spinner,connector)
asyncio.run(main())
quit()
Note
This is a whimsical example just to show that Python is capable
of doing other things while waiting for the OpenAPI to complete.
The real utility of cooperative multitasking using
asyncio is being able to run multiple Ingres sessions
simultaneously, or interacting with a message queueing service
such as PyZMQ,
or reacting instantly to mouse movments and button-clicks in a GUI,
all while interacting with the Actian DBMS.
OpenAPI Status Codes
Pyngres Constant |
Value |
|---|---|
IIAPI_ST_SUCCESS |
0 |
IIAPI_ST_MESSAGE |
1 |
IIAPI_ST_WARNING |
2 |
IIAPI_ST_NO_DATA |
3 |
IIAPI_ST_ERROR |
4 |
IIAPI_ST_FAILURE |
5 |
IIAPI_ST_NOT_INITIALIZED |
6 |
IIAPI_ST_INVALID_HANDLE |
7 |
IIAPI_ST_OUT_OF_MEMORY |
8 |
Note
IIAPI_ST_ERROR indicates an error such as an
SQL syntax error or a reference to a non-existent database asset.
The DBMS session remains usable and the application can continue.
IIAPI_ST_FAILURE indicates the session has failed.
It could indicate an OpenAPI protocol error or it could indicate the
connection to the DBMS is no longer be usable.
Function Reference
Most but not all OpenAPI functions are asynchronous.
If using pyngres.asyncio, most but not all
of the asynchronous functions are awaitable.
Note
IIapi_catchEvent() is asynchronous but not awaitable.
Use asyncio or multuthreading to
run it in a busy-wait loop in a concurrent task.
Refer to the Catching DBEvents example.
Note
IIapi_wait() is not present in pyngres.asyncio.
It would serve no purpose. If for some reason it is
needed, import both pyngres.asyncio and pyngres then invoke it
using pyngres.IIapi_wait()
For complete information on the OpenAPI functions refer to OpenAPI Functions in the OpenAPI User Guide.
Function |
Behaviour |
|---|---|
|
asynchronous, awaitable |
|
asynchronous, awaitable |
|
asynchronous, awaitable |
|
asynchronous, awaitable |
|
asynchronous, awaitable |
|
asynchronous, not awaitable |
|
asynchronous, awaitable |
|
asynchronous, awaitable |
|
asynchronous, awaitable |
|
synchronous |
|
asynchronous, awaitable |
|
synchronous |
|
synchronous |
|
asynchronous, awaitable |
|
asynchronous, awaitable |
|
asynchronous, awaitable |
|
synchronous |
|
asynchronous, awaitable |
|
asynchronous, awaitable |
|
synchronous |
|
asynchronous, awaitable |
|
asynchronous, awaitable |
|
asynchronous, awaitable |
|
asynchronous, awaitable |
|
asynchronous, awaitable |
|
asynchronous, awaitable |
|
synchronous |
|
synchronous |
|
synchronous |
|
asynchronous, awaitable |
|
asynchronous, awaitable |
|
asynchronous, awaitable |
|
asynchronous, awaitable |
|
asynchronous, awaitable |
|
synchronous |
|
synchronous |
|
asynchronous, not awaitable |
|
asynchronous, awaitable |
|
asynchronous, awaitable |
|
asynchronous, awaitable |
|
asynchronous, awaitable |
|
asynchronous, awaitable |
Pyngres Callback Helper Functions
Pyngres provides additional Python-specific helper functions to simplify the use of OpenAPI callbacks to Python functions.
Note
The helper functions are pyngres extensions. They are
not part of the Actian OpenAPI.
Callbacks are required in order to handle DBEvent notifications and trace messages sent by the DBMS (e.g. I/O traces, transaction log traces, query execution plans, etc.). They can also be used to notify the application when asynchronous OpenAPI functions have completed a requested operation.
See Using Callbacks.
IIapi_callback()
A decorator factory used to declare Python callback functions for use by the OpenAPI. An OpenAPI-compliant callback is expected to take two argument; a pointer to a closure and a pointer to an optional OpenAPI parmBlock. The closure can be any Python object.
@IIapi_callback
def my_callback(closure,parmBlock=None):
'''callback function'''
...
IIapi_getCallbackPtr()
A function to return a c_void_p pointer to an OpenAPI-compliant
Python callback function defined using the IIapi_callback() decorator.
callback = IIapi_getCallbackPtr(my_callback)
...
IIapi_getClosurePtr()
A function to return a c_void_p pointer to Python object to use as
a callback closure.
closure = IIapi_getClosurePtr(my_closure)
...
IIapi_getClosure()
Return the Python object pointed to by a ctypes.c_void_p pointer.
closure = IIapi_getClosure(ptr,closureClass)
ptr is expected to be a ctypes.c_void_p pointer.
closureClass is the class of the instance to which the pointer
refers. The returned object will be an instance of that class.