ODBC provides the possibility to provide data for input parameters at execution time, and this feature is especially used for transferring large objects into the database.
Here, the indicator of a host variable signals that the data is available at execution time.
If at least one of these parameters is found, instead of consisting of a single call to execute()
or executeBatch
, the execution of a prepared statement proceeds according to the following sequence:
execute()
, it will return IFR_NEED_DATA
. nextParameter(index, address)
. Here, index and address will be filled with the next parameter's details. The address here is set according to the parameter binding, the binding type, and the position in the row array, if batch execution is done. If the method returns IFR_OK
, you are finished, if it returns IFR_NEED_DATA
, putData(data, lengthindicator)
to actually provide the data. Take care that for integral data types, and the speciall NULL and DEFAULT values the data must be delivered in one call, otherwise subsequent calls of putData
append to the data. Continue with this step until all data for that parameter has been transferred.
The calling sequence described above is translated into one EXECUTE message, and if necessary, one or more PUTVAL messages.
For doing this, the following measures are taken:
The following example illustrates the behaviour:
Consider a table TESTDATA defined as follows:
CREATE TABLE TESTDATA(A INTEGER, B CHAR(200), C LONG BYTE, D CHAR(30), E LONG BYTE)
Assume an INSERT statement on this table, where the parameters for column C and D are bound having an indicator value DATA AT EXECUTE, and E is bound to an array of 1000 bytes. We assume also that the order in the data part is A,B,C,D,E, for the ease of description.
The packet is filled as follows:
(1) execute
The execute step does check whether there is DATA AT EXECUTE, and so returns almost immediately.
(2) nextParameter
The execute step puts the parse id part into the segment, and then all available data. The segment will then look as follows:
+----------------+ | ParseID Part | +----------------+ | Data Part | | A | | B | | C (Descriptor) | <== Descriptor is set to 'no data' for now. | | <== Space for D is kept, but not filled with data. | E (Descriptor) | <== Points to the data, has flag vm_alldata | E (Data) | to show there is no more data. +----------------+
Furthermore, the internal structures are laid out, and all DATA AT EXECUTE parameters are recorded.
Finally, the function returns pointing to D, as it is not a LONG column, and non-LONGs are preferred.
(3) putData
The application fills up D using putData, which fills up the value, and eventually returns IFR_DATA_TRUNC when too much data is inserted.
Now the segment looks like this:
+----------------+ | ParseID Part | +----------------+ | Data Part | | A | | B | | C (Descriptor) | | D | | E (Descriptor) | | E (Data) | +----------------+
(4) nextParameter
The next call to this method returns parameter C, and does not send any packet - or change anything, as it is not filled up.
(5) putData
Subsequently, C will be filled with data. At some point, the segment will look like:
+----------------+ | ParseID Part | +----------------+ | Data Part | | A | | B | | C (Descriptor) | | D | | E (Descriptor) | | E (Data) | | D (Data) | +----------------+
and the packet is filled up completely. Then the packet is sent, and normal PUTVAL packets for this LONG are created, which are sent when they are filled up (which may happen in extreme even several times with one putData.
When DATA AT EXECUTE is spotted during an executeBatch()
operation, the control flow is similar to the case when this happen during execute()
.
The address returned by the nextParameter
call will indicate the row, as it would point to the place where the data would be normally expected, if no DATA AT EXECUTE would be present.
In contrast to a single execute, the data from the rows accumulates in the packet until the packet is full.