Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#91: Fix integration tests for Exasol v8 #96

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion doc/changes/changes_0.2.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Code name: Upgrade to Exasol 8

## Summary

This release updates integration tests to additionally use Exasol version 8.23.0.
This release updates integration tests to additionally use Exasol version 8.27.0.

When running integration tests with `luarocks test` or `busted`, the environment variable `EXASOL_HOST` now has a default value `localhost` to make running tests more convenient.
If you run local tests, the Exasol DB is most likely set up to forward the default database port to the localhost anyway.
Expand All @@ -17,3 +17,4 @@ Integration test now succeeds with TLS 1.3 in addition to TLS 1.2 (version 8.18.

* #56: Sped up repeated CI tests
* #78: Added tests with Exasol 8
* #91: Fixed failing tests with Exasol 8
38 changes: 38 additions & 0 deletions spec/integration/Issue91_regression_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
-- This is a regression test for exasol/exosol-driver-lua#91
-- https://github.com/exasol/exasol-driver-lua/issues/91
--
-- At the switch from Exasol 7.1 to 8 the test of the drivers broke (first observed in a test
-- against Exasol 8.23.0. Under certain conditions the tests trigger a message timeout after
-- 5 seconds.
--
-- The test below is the minimal implementation that triggers the issue.

require("busted.runner")()
local driver = require("luasql.exasol")
local config = require("config")

describe("Issue91", function()
local connection_params = config.get_connection_params()
local env = driver.exasol()

local function create_connection()
return assert(env:connect(connection_params.source_name, connection_params.user, connection_params.password))
end

it("must not raise message timeout error", function()
local connectionA = create_connection()
-- If you don't use a cursor here and then immediately close the connection, the problem can
-- be triggered with the next new connection.
connectionA:close()
local connectionB = create_connection()
local cursor = assert(connectionB:execute("SELECT 1")) -- ← here is where the error is raised.
finally(function()
if(not connectionB.closed) then
print("Cleaning up remaining connection after failure.")
connectionB:close()
end
end)
cursor:close()
connectionB:close()
end)
end)
18 changes: 9 additions & 9 deletions src/luasql/exasol/ExasolWebsocket.lua
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ end
-- This returns a public RSA key used for encrypting the password before sending it with the
-- @{luasql.exasol.ExasolWebsocket:send_login_credentials} method.
-- @treturn table|nil the public RSA key or `nil` if an error occurred
-- @treturn table|nil `nil` if the operation was successful, otherwise the error that occured
-- @treturn table|nil `nil` if the operation was successful, otherwise the error that occurred
function ExasolWebsocket:send_login_command()
log.debug("Sending login command")
return self:_send_json({command = "login", protocolVersion = 3})
Expand All @@ -50,7 +50,7 @@ end
-- @tparam string encrypted_password the password encrypted with the public key returned by
-- @{luasql.exasol.ExasolWebsocket:send_login_command}
-- @treturn table|nil response data from the database or `nil` if an error occurred
-- @treturn table|nil `nil` if the operation was successful, otherwise the error that occured
-- @treturn table|nil `nil` if the operation was successful, otherwise the error that occurred
function ExasolWebsocket:send_login_credentials(username, encrypted_password)
log.debug("Sending login credentials")
return self:_send_json({username = username, password = encrypted_password, useCompression = false})
Expand All @@ -59,7 +59,7 @@ end
--- Sends the disconnect command.
-- See Exasol API documentation for the
-- [`disconnect` command](https://github.com/exasol/websocket-api/blob/master/docs/commands/disconnectV1.md).
-- @treturn table|nil `nil` if the operation was successful, otherwise the error that occured
-- @treturn table|nil `nil` if the operation was successful, otherwise the error that occurred
function ExasolWebsocket:send_disconnect()
log.debug("Sending disconnect command")
local _, err = self:_send_json({command = "disconnect"}, true)
Expand All @@ -71,7 +71,7 @@ end
-- [`execute` command](https://github.com/exasol/websocket-api/blob/master/docs/commands/executeV1.md).
-- @tparam string statement the SQL statement to execute
-- @treturn table|nil the result set response data from the database or `nil` if an error occurred
-- @treturn table|nil `nil` if the operation was successful, otherwise the error that occured
-- @treturn table|nil `nil` if the operation was successful, otherwise the error that occurred
function ExasolWebsocket:send_execute(statement)
local payload = {command = "execute", sqlText = statement, attributes = {}}
return self:_send_json(payload)
Expand All @@ -84,7 +84,7 @@ end
-- [`setAttributes` command](https://github.com/exasol/websocket-api/blob/master/docs/commands/setAttributesV1.md).
-- @tparam string attribute_name the name of the attribute to set, e.g. `"autocommit"`
-- @tparam string|number|boolean|nil attribute_value the value of the attribute to set, e.g. `false`
-- @treturn table|nil `nil` if the operation was successful, otherwise the error that occured
-- @treturn table|nil `nil` if the operation was successful, otherwise the error that occurred
function ExasolWebsocket:send_set_attribute(attribute_name, attribute_value)
if attribute_value == nil or attribute_value == constants.NULL then
attribute_value = cjson.null
Expand All @@ -100,7 +100,7 @@ end
-- @tparam number start_position row offset (0-based) from which to begin data retrieval
-- @tparam number num_bytes number of bytes to retrieve (max: 64MiB)
-- @treturn table|nil result set from the database or `nil` if an error occurred
-- @treturn table|nil `nil` if the operation was successful, otherwise the error that occured
-- @treturn table|nil `nil` if the operation was successful, otherwise the error that occurred
function ExasolWebsocket:send_fetch(result_set_handle, start_position, num_bytes)
local payload = {
command = "fetch",
Expand All @@ -116,7 +116,7 @@ end
-- See Exasol API documentation for the
-- [`closeResultSet` command](https://github.com/exasol/websocket-api/blob/master/docs/commands/closeResultSetV1.md).
-- @tparam number result_set_handle result set handle to close
-- @treturn table|nil `nil` if the operation was successful, otherwise the error that occured
-- @treturn table|nil `nil` if the operation was successful, otherwise the error that occurred
function ExasolWebsocket:send_close_result_set(result_set_handle)
local payload = {command = "closeResultSet", resultSetHandles = {result_set_handle}, attributes = {}}
local _, err = self:_send_json(payload)
Expand All @@ -125,7 +125,7 @@ end

--- Extract the error from the given database response.
-- @tparam table response the response from the database
-- @treturn nil|table `nil` the error that occured or `nil` if the response was successful
-- @treturn nil|table `nil` the error that occurred or `nil` if the response was successful
local function get_response_error(response)
if response.status == "ok" then
return nil
Expand All @@ -147,7 +147,7 @@ end
-- @tparam boolean|nil ignore_response `false` if we expect a response, else `true`.
-- Default is `false`.
-- @treturn table|nil the received response or nil if ignore_response was `true` or an error occurred
-- @treturn table|nil `nil` if the operation was successful, otherwise the error that occured
-- @treturn table|nil `nil` if the operation was successful, otherwise the error that occurred
function ExasolWebsocket:_send_json(payload, ignore_response)
local raw_payload = cjson.encode(payload)
if self.closed then
Expand Down
15 changes: 9 additions & 6 deletions src/luasql/exasol/luws.lua
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ local STATE_READMASK = "mask"
local MAXMESSAGE = 65535 -- maximum WS message size
local CHUNKSIZE = 2048
local DEFAULTMSGTIMEOUT = 0 -- drop connection if no message in this time (0=no timeout)
local WS_UPGRADE_REQUEST_TIMEOUT <const> = 5
local WS_UPGRADE_RESPONSE_TIMEOUT <const> = 5
local WS_SEND_FRAME_TIMEOUT <const> = 15

local timenow = socket.gettime or os.time -- use hi-res time if available
local unpack = unpack or table.unpack -- luacheck: ignore 143
Expand Down Expand Up @@ -127,16 +130,16 @@ local function wsupgrade( wsconn )

-- Send request.
D("wsupgrade() sending %1", req)
wsconn.socket:settimeout( 5, "b" )
wsconn.socket:settimeout( 5, "r" )
wsconn.socket:settimeout( WS_UPGRADE_REQUEST_TIMEOUT, "b" )
wsconn.socket:settimeout( WS_UPGRADE_REQUEST_TIMEOUT, "r" )
local nb,err = wsconn.socket:send( req )
if nb == nil then
return false, "Failed to send upgrade request: "..tostring(err)
end

-- Read until we get two consecutive linefeeds.
wsconn.socket:settimeout( 5, "b" )
wsconn.socket:settimeout( 5, "r" )
wsconn.socket:settimeout( WS_UPGRADE_RESPONSE_TIMEOUT, "b" )
wsconn.socket:settimeout( WS_UPGRADE_RESPONSE_TIMEOUT, "r" )
local buf = {}
local ntotal = 0
while true do
Expand Down Expand Up @@ -305,8 +308,8 @@ local function send_frame( wsconn, opcode, fin, s )
end
t = nil -- luacheck: ignore 311
D("send_frame() sending frame of %1 bytes for %2", #frame, s)
wsconn.socket:settimeout( 5, "b" )
wsconn.socket:settimeout( 5, "r" )
wsconn.socket:settimeout( WS_SEND_FRAME_TIMEOUT, "b" )
wsconn.socket:settimeout( WS_SEND_FRAME_TIMEOUT, "r" )
-- ??? need retry while nb < payload length
while #frame > 0 do
local nb,err = wsconn.socket:send( frame )
Expand Down
Loading