CursorData.lua
local log = require("remotelog")
local ExaError = require("ExaError")
local constants = require("luasql.exasol.constants")
local cjson = require("cjson")
local CursorData = {}
function CursorData:create(connection_properties, websocket, result_set)
local object = {
websocket = websocket,
connection_properties = assert(connection_properties, "connection_properties missing"),
result_set_handle = result_set.resultSetHandle,
data = result_set.data,
num_rows_total = assert(result_set.numRows, "numRows missing in result set"),
num_rows_in_message = assert(result_set.numRowsInMessage, "numRowsInMessage missing in result set"),
num_rows_fetched_total = result_set.numRowsInMessage,
current_row = 1,
current_row_in_batch = 1
}
if object.result_set_handle then
log.debug("Creating cursor data for result set %d with %d rows in total and %d rows in message",
object.result_set_handle, object.num_rows_total, object.num_rows_in_message)
else
log.debug("Creating cursor data without result set handle with %d rows", object.num_rows_total)
end
self.__index = self
setmetatable(object, self)
return object
end
function CursorData:next_row()
self.current_row = self.current_row + 1
self.current_row_in_batch = self.current_row_in_batch + 1
end
function CursorData:get_current_row()
return self.current_row
end
function CursorData:has_more_rows()
return self.current_row <= self.num_rows_total
end
local function convert_col_value(col_value)
if col_value == cjson.null then
return constants.NULL
else
return col_value
end
end
function CursorData:get_column_value(column_index)
self:_fetch_data()
log.trace("Fetching row %d of %d (%d of %d in current batch)", self.current_row, self.num_rows_total,
self.current_row_in_batch, self.num_rows_in_message)
if column_index <= 0 or #self.data < column_index then
ExaError:new("E-EDL-29", "Column index {{column_index}} out of bound, must be between 1 and {{column_count}}",
{column_index = column_index, column_count = #self.data}):add_ticket_mitigation():raise()
end
if #self.data[column_index] < self.current_row_in_batch then
local message = "Row {{row_index}} out of bound, must be between 1 and {{row_count}}"
local args = {row_index = self.current_row_in_batch, row_count = #self.data[column_index]}
ExaError:new("E-EDL-30", message, args):add_ticket_mitigation():raise()
end
local value = self.data[column_index][self.current_row_in_batch]
return convert_col_value(value)
end
function CursorData:_fetch_data()
if not self.result_set_handle and not self.data then
ExaError:new("F-EDL-25", "Neither data nor result set handle available"):add_ticket_mitigation():raise()
end
if not self.result_set_handle then
return
end
if self:_end_of_result_set_reached() then
ExaError:new("E-EDL-31", "No more rows available in result set"):add_ticket_mitigation():raise()
end
if not self:_more_data_available() then
self:_fetch_next_data_batch()
end
end
function CursorData:_end_of_result_set_reached()
return self.current_row > self.num_rows_total
end
function CursorData:_more_data_available()
return self.current_row_in_batch <= self.num_rows_in_message
end
function CursorData:_fetch_next_data_batch()
log.trace("Fetching next data batch. Current row in batch: %d, rows in message: %d", self.current_row_in_batch,
self.num_rows_in_message)
local start_position = self.current_row - 1
local fetch_size = self.connection_properties:get_fetchsize_bytes()
local response, err = self.websocket:send_fetch(self.result_set_handle, start_position, fetch_size)
if err then
ExaError:new("E-EDL-26", "Error fetching result data for handle {{result_set_handle}} with start position "
.. "{{start_position}} and fetch size {{fetch_size_bytes}} bytes: {{error}}", {
result_set_handle = self.result_set_handle,
start_position = start_position,
fetch_size_bytes = fetch_size,
error = err
}):raise()
end
self.data = assert(response.data, "missing data")
self.num_rows_in_message = assert(response.numRows, "missing numRows")
self.num_rows_fetched_total = self.num_rows_fetched_total + self.num_rows_in_message
self.current_row_in_batch = 1
log.debug("Received batch with %d rows (#%d..%d of %d) with start pos %d and fetch size %d bytes",
self.num_rows_in_message, self.current_row, self.num_rows_fetched_total, self.num_rows_total,
start_position, fetch_size)
end
return CursorData