import asyncio
import pyngres.asyncio as py
...

# assume a session was started and connHandle is set. Also assume
# a transaction is already in progress and tranHandle has a value, or
# tranHandle is set to None, indicating the start of a new transaction

async def exec_SQL():
    '''execute an SQL statement with parameters but returns no rows'''

    # encode the query so the DBMS can understand it; note the 
    # blank before AND after every ~V marker
    query = "INSERT INTO foo VALUES ( ~V , ~V , ~V )" 
    queryText = query.encode()

    # set up the query parameter block; set connHandle and tranHandle
    qyp = py.IIAPI_QUERYPARM()
    qyp.qy_connHandle = connHandle
    qyp.qy_queryType = py.IIAPI_QT_QUERY    # indicate query execution
    qyp.qy_queryText = queryText
    qyp.qy_parameters = True                # indicate arguments will be sent

    # send the SQL to the DBMS
    await py.IIapi_query(qyp)
    # errors sending the SQL to the server are reported here
    status = qyp.qy_genParm.gp_status
    if not status == py.IIAPI_ST_SUCCESS:
        print(f'IIapi_query() error, {status=}')
        quit()

    tranHandle = qyp.qy_tranHandle
    stmtHandle = qyp.qy_stmtHandle

    # set up the data buffers for the arguments
    arg_count = 3
    name = ctypes.c_buffer(25)
    name.value = b"Treefrog Brewing Co"
    ordernr = ctypes.c_int(71025)
    weight = ctypes.c_double(16.18)

    # set up the array of argument descriptors; note that ds_columnType
    # is set to py.IIAPI_COL_QPARM to indicate a query parameter
    descriptors = (py.IIAPI_DESCRIPTOR * arg_count)()
    descriptors[0].ds_dataType = py.IIAPI_CHA_TYPE
    descriptors[0].ds_length = len(name)
    descriptors[0].ds_nullable = True
    descriptors[0].ds_columnType = py.IIAPI_COL_QPARM
    descriptors[1].ds_dataType = py.IIAPI_INT_TYPE
    descriptors[1].ds_length = py.IIAPI_I4_LEN
    descriptors[1].ds_nullable = True
    descriptors[1].ds_columnType = py.IIAPI_COL_QPARM
    descriptors[2].ds_dataType = py.IIAPI_FLT_TYPE
    descriptors[2].ds_length = py.IIAPI_F8_LEN
    descriptors[2].ds_nullable = True
    descriptors[2].ds_columnType = py.IIAPI_COL_QPARM

    # send the description of the arguments
    sdp = py.IIAPI_SETDESCRPARM()
    sdp.sd_stmtHandle = stmtHandle
    sdp.sd_descriptorCount = arg_count
    sdp.sd_descriptor = descriptors
    await py.IIapi_setDescriptor(sdp)
    status = sdp.sd_genParm.gp_status
    if not status == py.IIAPI_ST_SUCCESS:
        print(f'IIapi_setDescriptor() error, {status=}')
        quit()

    # set up the array of argument values
    arguments = (py.IIAPI_DATAVALUE * arg_count)()
    arguments[0].dv_length = len(name.value)
    arguments[0].dv_value = ctypes.addressof(name)
    arguments[1].dv_length = py.IIAPI_I4_LEN
    arguments[1].dv_value = ctypes.addressof(ordernr)
    arguments[2].dv_length = py.IIAPI_F8_LEN
    arguments[2].dv_value = ctypes.addressof(weight)

    # send the arguments to substitute for the placeholders
    ppp = py.IIAPI_PUTPARMPARM()
    ppp.pp_stmtHandle = stmtHandle
    ppp.pp_parmCount = arg_count
    ppp.pp_parmData = arguments
    await py.IIapi_putParms(ppp)
    status = ppp.pp_genParm.gp_status
    if not status == py.IIAPI_ST_SUCCESS:
        print(f'IIapi_putParms() error, {status=}')
        quit()

    # get the query status; SQL syntax errors and references to
    # non-existent database assets are reported here
    gqp = py.IIAPI_GETQINFOPARM()
    gqp.gq_stmtHandle = stmtHandle
    await py.IIapi_getQueryInfo(gqp)
    status = gqp.gq_genParm.gp_status
    if not status == py.IIAPI_ST_SUCCESS:
        print(f'IIapi_getQueryInfo() error, {status=}')
        quit()

    # close the query to free the statement handle
    clp = py.IIAPI_CLOSEPARM()
    clp.cl_stmtHandle = stmtHandle
    await py.IIapi_close(clp)
    status = clp.cl_genParm.gp_status
    if not status == py.IIAPI_ST_SUCCESS:
        print(f'IIapi_close() error, {status=}')
        quit()

...
