06528494bb
driver SQLite. edu
517 lines
18 KiB
Lua
517 lines
18 KiB
Lua
#!/usr/local/bin/lua
|
|
-- See Copyright Notice in license.html
|
|
|
|
TOTAL_FIELDS = 800
|
|
TOTAL_ROWS = 40 --unused
|
|
|
|
---------------------------------------------------------------------
|
|
-- checks for a value and throw an error if it is invalid.
|
|
---------------------------------------------------------------------
|
|
function assert2 (expected, value, msg)
|
|
if not msg then
|
|
msg = ''
|
|
else
|
|
msg = msg..'\n'
|
|
end
|
|
return assert (value == expected,
|
|
msg.."wrong value (["..tostring(value).."] instead of "..
|
|
tostring(expected)..")")
|
|
end
|
|
|
|
---------------------------------------------------------------------
|
|
-- object test.
|
|
---------------------------------------------------------------------
|
|
function test_object (obj, objmethods)
|
|
-- checking object type.
|
|
assert2 ("userdata", type(obj), "incorrect object type")
|
|
-- trying to get metatable.
|
|
assert2 ("LuaSQL: you're not allowed to get this metatable",
|
|
getmetatable(obj), "error permitting access to object's metatable")
|
|
-- trying to set metatable.
|
|
assert2 (false, pcall (setmetatable, ENV, {}))
|
|
-- checking existence of object's methods.
|
|
for i = 1, table.getn (objmethods) do
|
|
local method = obj[objmethods[i]]
|
|
assert2 ("function", type(method))
|
|
assert2 (false, pcall (method), "no 'self' parameter accepted")
|
|
end
|
|
return obj
|
|
end
|
|
|
|
ENV_OK = function (obj)
|
|
return test_object (obj, { "close", "connect", })
|
|
end
|
|
CONN_OK = function (obj)
|
|
return test_object (obj, { "close", "commit", "execute", "rollback", "setautocommit", })
|
|
end
|
|
CUR_OK = function (obj)
|
|
return test_object (obj, { "close", "fetch", "getcolnames", "getcoltypes", })
|
|
end
|
|
|
|
---------------------------------------------------------------------
|
|
-- basic checking test.
|
|
---------------------------------------------------------------------
|
|
function basic_test ()
|
|
-- Check environment object.
|
|
ENV = ENV_OK (luasql[driver] ())
|
|
assert2 (true, ENV:close(), "couldn't close environment")
|
|
-- trying to connect with a closed environment.
|
|
assert2 (false, pcall (ENV.connect, ENV, datasource, username, password),
|
|
"error connecting with a closed environment")
|
|
-- it is ok to close a closed object, but nil is returned instead of 1.
|
|
assert2 (false, ENV:close())
|
|
-- Reopen the environment.
|
|
ENV = ENV_OK (luasql[driver] ())
|
|
-- Check connection object.
|
|
local conn, err = ENV:connect (datasource, username, password)
|
|
assert (conn, (err or '').." ("..datasource..")")
|
|
CONN_OK (conn)
|
|
assert2 (true, conn:close(), "couldn't close connection")
|
|
-- trying to execute a statement with a closed connection.
|
|
assert2 (false, pcall (conn.execute, conn, "create table x (c char)"),
|
|
"error connecting with a closed environment")
|
|
-- it is ok to close a closed object, but nil is returned instead of 1.
|
|
assert2 (false, conn:close())
|
|
-- Check error situation.
|
|
assert2 (nil, ENV:connect ("/unknown-data-base"), "this should be an error")
|
|
|
|
-- force garbage collection
|
|
local a = {}
|
|
setmetatable(a, {__mode="v"})
|
|
a.ENV = ENV_OK (luasql[driver] ())
|
|
a.CONN = a.ENV:connect (datasource, username, password)
|
|
collectgarbage ()
|
|
collectgarbage ()
|
|
assert2(nil, a.ENV, "environment not collected")
|
|
assert2(nil, a.CONN, "connection not collected")
|
|
end
|
|
|
|
---------------------------------------------------------------------
|
|
-- Build SQL command to create the test table.
|
|
---------------------------------------------------------------------
|
|
function define_table (n)
|
|
local s = "create table t ("
|
|
for i = 1, n do
|
|
s = s.."f"..i.." varchar (30), "
|
|
end
|
|
s = string.sub (s, 1, -3)
|
|
if driver == "mysql" then
|
|
return s..") TYPE = InnoDB;"
|
|
else
|
|
return s..")"
|
|
end
|
|
end
|
|
|
|
|
|
---------------------------------------------------------------------
|
|
-- Create a table with TOTAL_FIELDS character fields.
|
|
---------------------------------------------------------------------
|
|
function create_table ()
|
|
-- Check SQL statements.
|
|
CONN = CONN_OK (ENV:connect (datasource, username, password))
|
|
-- Create t.
|
|
local cmd = define_table(TOTAL_FIELDS)
|
|
-- Postgres returns 0, while ODBC returns -1.
|
|
assert (CONN:execute (cmd))
|
|
end
|
|
|
|
---------------------------------------------------------------------
|
|
-- Fetch 2 values.
|
|
---------------------------------------------------------------------
|
|
function fetch2 ()
|
|
-- insert a record.
|
|
assert2 (1, CONN:execute ("insert into t (f1, f2) values ('b', 'c')"))
|
|
-- retrieve data.
|
|
local cur = CUR_OK (CONN:execute ("select f1, f2, f3 from t"))
|
|
-- check data.
|
|
local f1, f2, f3 = cur:fetch()
|
|
assert2 ('b', f1)
|
|
assert2 ('c', f2)
|
|
assert2 (nil, f3)
|
|
assert2 (nil, cur:fetch())
|
|
assert2 (true, cur:close(), "couldn't close cursor")
|
|
assert2 (false, cur:close())
|
|
-- insert a second record.
|
|
assert2 (1, CONN:execute ("insert into t (f1, f2) values ('d', 'e')"))
|
|
cur = CUR_OK (CONN:execute ("select f1, f2, f3 from t order by f1"))
|
|
local f1, f2, f3 = cur:fetch()
|
|
assert2 ('b', f1, f2) -- f2 can be an error message
|
|
assert2 ('c', f2)
|
|
assert2 (nil, f3)
|
|
f1, f2, f3 = cur:fetch()
|
|
assert2 ('d', f1, f2) -- f2 can be an error message
|
|
assert2 ('e', f2)
|
|
assert2 (nil, f3)
|
|
assert2 (nil, cur:fetch())
|
|
assert2 (true, cur:close(), "couldn't close cursor")
|
|
assert2 (false, cur:close())
|
|
-- remove records.
|
|
assert2 (2, CONN:execute ("delete from t where f1 in ('b', 'd')"))
|
|
end
|
|
|
|
---------------------------------------------------------------------
|
|
-- Test fetch with a new table, reusing a table and with different
|
|
-- indexing.
|
|
---------------------------------------------------------------------
|
|
function fetch_new_table ()
|
|
-- insert elements.
|
|
assert2 (1, CONN:execute ("insert into t (f1, f2, f3, f4) values ('a', 'b', 'c', 'd')"))
|
|
assert2 (1, CONN:execute ("insert into t (f1, f2, f3, f4) values ('f', 'g', 'h', 'i')"))
|
|
-- retrieve data using a new table.
|
|
local cur = CUR_OK (CONN:execute ("select f1, f2, f3, f4 from t order by f1"))
|
|
local row, err = cur:fetch{}
|
|
assert2 (type(row), "table", err)
|
|
assert2 ('a', row[1])
|
|
assert2 ('b', row[2])
|
|
assert2 ('c', row[3])
|
|
assert2 ('d', row[4])
|
|
assert2 (nil, row.f1)
|
|
assert2 (nil, row.f2)
|
|
assert2 (nil, row.f3)
|
|
assert2 (nil, row.f4)
|
|
row, err = cur:fetch{}
|
|
assert (type(row), "table", err)
|
|
assert2 ('f', row[1])
|
|
assert2 ('g', row[2])
|
|
assert2 ('h', row[3])
|
|
assert2 ('i', row[4])
|
|
assert2 (nil, row.f1)
|
|
assert2 (nil, row.f2)
|
|
assert2 (nil, row.f3)
|
|
assert2 (nil, row.f4)
|
|
assert2 (nil, cur:fetch())
|
|
assert2 (true, cur:close(), "couldn't close cursor")
|
|
assert2 (false, cur:close())
|
|
|
|
-- retrieve data reusing the same table.
|
|
io.write ("reusing a table...")
|
|
cur = CUR_OK (CONN:execute ("select f1, f2, f3, f4 from t order by f1"))
|
|
local row, err = cur:fetch{}
|
|
assert (type(row), "table", err)
|
|
assert2 ('a', row[1])
|
|
assert2 ('b', row[2])
|
|
assert2 ('c', row[3])
|
|
assert2 ('d', row[4])
|
|
assert2 (nil, row.f1)
|
|
assert2 (nil, row.f2)
|
|
assert2 (nil, row.f3)
|
|
assert2 (nil, row.f4)
|
|
row, err = cur:fetch (row)
|
|
assert (type(row), "table", err)
|
|
assert2 ('f', row[1])
|
|
assert2 ('g', row[2])
|
|
assert2 ('h', row[3])
|
|
assert2 ('i', row[4])
|
|
assert2 (nil, row.f1)
|
|
assert2 (nil, row.f2)
|
|
assert2 (nil, row.f3)
|
|
assert2 (nil, row.f4)
|
|
assert2 (nil, cur:fetch{})
|
|
assert2 (true, cur:close(), "couldn't close cursor")
|
|
assert2 (false, cur:close())
|
|
|
|
-- retrieve data reusing the same table with alphabetic indexes.
|
|
io.write ("with alpha keys...")
|
|
cur = CUR_OK (CONN:execute ("select f1, f2, f3, f4 from t order by f1"))
|
|
local row, err = cur:fetch ({}, "a")
|
|
assert (type(row), "table", err)
|
|
assert2 (nil, row[1])
|
|
assert2 (nil, row[2])
|
|
assert2 (nil, row[3])
|
|
assert2 (nil, row[4])
|
|
assert2 ('a', row.f1)
|
|
assert2 ('b', row.f2)
|
|
assert2 ('c', row.f3)
|
|
assert2 ('d', row.f4)
|
|
row, err = cur:fetch (row, "a")
|
|
assert2 (type(row), "table", err)
|
|
assert2 (nil, row[1])
|
|
assert2 (nil, row[2])
|
|
assert2 (nil, row[3])
|
|
assert2 (nil, row[4])
|
|
assert2 ('f', row.f1)
|
|
assert2 ('g', row.f2)
|
|
assert2 ('h', row.f3)
|
|
assert2 ('i', row.f4)
|
|
assert2 (nil, cur:fetch(row, "a"))
|
|
assert2 (true, cur:close(), "couldn't close cursor")
|
|
assert2 (false, cur:close())
|
|
|
|
-- retrieve data reusing the same table with both indexes.
|
|
io.write ("with both keys...")
|
|
cur = CUR_OK (CONN:execute ("select f1, f2, f3, f4 from t order by f1"))
|
|
local row, err = cur:fetch ({}, "an")
|
|
assert (type(row), "table", err)
|
|
assert2 ('a', row[1])
|
|
assert2 ('b', row[2])
|
|
assert2 ('c', row[3])
|
|
assert2 ('d', row[4])
|
|
assert2 ('a', row.f1)
|
|
assert2 ('b', row.f2)
|
|
assert2 ('c', row.f3)
|
|
assert2 ('d', row.f4)
|
|
row, err = cur:fetch (row, "an")
|
|
assert (type(row), "table", err)
|
|
assert2 ('f', row[1])
|
|
assert2 ('g', row[2])
|
|
assert2 ('h', row[3])
|
|
assert2 ('i', row[4])
|
|
assert2 ('f', row.f1)
|
|
assert2 ('g', row.f2)
|
|
assert2 ('h', row.f3)
|
|
assert2 ('i', row.f4)
|
|
assert2 (nil, cur:fetch(row, "an"))
|
|
assert2 (true, cur:close(), "couldn't close cursor")
|
|
assert2 (false, cur:close())
|
|
-- clean the table.
|
|
assert2 (2, CONN:execute ("delete from t where f1 in ('a', 'f')"))
|
|
end
|
|
|
|
---------------------------------------------------------------------
|
|
-- Fetch many values
|
|
---------------------------------------------------------------------
|
|
function fetch_many ()
|
|
-- insert values.
|
|
local fields, values = "f1", "'v1'"
|
|
for i = 2, TOTAL_FIELDS do
|
|
fields = string.format ("%s,f%d", fields, i)
|
|
values = string.format ("%s,'v%d'", values, i)
|
|
end
|
|
local cmd = string.format ("insert into t (%s) values (%s)",
|
|
fields, values)
|
|
assert2 (1, CONN:execute (cmd))
|
|
-- fetch values (without a table).
|
|
local cur = CUR_OK (CONN:execute ("select * from t where f1 = 'v1'"))
|
|
local row = { cur:fetch () }
|
|
assert2 ("string", type(row[1]), "error while trying to fetch many values (without a table)")
|
|
for i = 1, TOTAL_FIELDS do
|
|
assert2 ('v'..i, row[i])
|
|
end
|
|
assert2 (nil, cur:fetch (row))
|
|
assert2 (true, cur:close(), "couldn't close cursor")
|
|
assert2 (false, cur:close())
|
|
-- fetch values (with a table and default indexing).
|
|
io.write ("with a table...")
|
|
local cur = CUR_OK (CONN:execute ("select * from t where f1 = 'v1'"))
|
|
local row = cur:fetch {}
|
|
assert2 ("string", type(row[1]), "error while trying to fetch many values (default indexing)")
|
|
for i = 1, TOTAL_FIELDS do
|
|
assert2 ('v'..i, row[i])
|
|
end
|
|
assert2 (nil, cur:fetch (row))
|
|
assert2 (true, cur:close(), "couldn't close cursor")
|
|
assert2 (false, cur:close())
|
|
-- fetch values (with numbered indexes on a table).
|
|
io.write ("with numbered keys...")
|
|
local cur = CUR_OK (CONN:execute ("select * from t where f1 = 'v1'"))
|
|
local row = cur:fetch ({}, "n")
|
|
assert2 ("string", type(row[1]), "error while trying to fetch many values (numbered indexes)")
|
|
for i = 1, TOTAL_FIELDS do
|
|
assert2 ('v'..i, row[i])
|
|
end
|
|
assert2 (nil, cur:fetch (row))
|
|
assert2 (true, cur:close(), "couldn't close cursor")
|
|
assert2 (false, cur:close())
|
|
-- fetch values (with alphanumeric indexes on a table).
|
|
io.write ("with alpha keys...")
|
|
local cur = CUR_OK (CONN:execute ("select * from t where f1 = 'v1'"))
|
|
local row = cur:fetch ({}, "a")
|
|
assert2 ("string", type(row.f1), "error while trying to fetch many values (alphanumeric indexes)")
|
|
for i = 1, TOTAL_FIELDS do
|
|
assert2 ('v'..i, row['f'..i])
|
|
end
|
|
assert2 (nil, cur:fetch (row))
|
|
assert2 (true, cur:close(), "couldn't close cursor")
|
|
assert2 (false, cur:close())
|
|
-- fetch values (with both indexes on a table).
|
|
io.write ("with both keys...")
|
|
local cur = CUR_OK (CONN:execute ("select * from t where f1 = 'v1'"))
|
|
local row = cur:fetch ({}, "na")
|
|
assert2 ("string", type(row[1]), "error while trying to fetch many values (both indexes)")
|
|
assert2 ("string", type(row.f1), "error while trying to fetch many values (both indexes)")
|
|
for i = 1, TOTAL_FIELDS do
|
|
assert2 ('v'..i, row[i])
|
|
assert2 ('v'..i, row['f'..i])
|
|
end
|
|
assert2 (nil, cur:fetch (row))
|
|
assert2 (true, cur:close(), "couldn't close cursor")
|
|
assert2 (false, cur:close())
|
|
-- clean the table.
|
|
assert2 (1, CONN:execute ("delete from t where f1 = 'v1'"))
|
|
end
|
|
|
|
---------------------------------------------------------------------
|
|
---------------------------------------------------------------------
|
|
function rollback ()
|
|
CONN:setautocommit (false) -- == begin transaction
|
|
-- insert a record and commit the operation.
|
|
assert2 (1, CONN:execute ("insert into t (f1) values ('a')"))
|
|
local cur = CUR_OK (CONN:execute ("select count(*) from t"))
|
|
assert2 (1, tonumber (cur:fetch ()), "Insert failed")
|
|
assert2 (true, cur:close(), "couldn't close cursor")
|
|
assert2 (false, cur:close())
|
|
CONN:commit ()
|
|
-- insert a record and roll back the operation.
|
|
assert2 (1, CONN:execute ("insert into t (f1) values ('b')"))
|
|
local cur = CUR_OK (CONN:execute ("select count(*) from t"))
|
|
assert2 (2, tonumber (cur:fetch ()), "Insert failed")
|
|
assert2 (true, cur:close(), "couldn't close cursor")
|
|
assert2 (false, cur:close())
|
|
CONN:rollback ()
|
|
-- check resulting table with one record.
|
|
cur = CUR_OK (CONN:execute ("select count(*) from t"))
|
|
assert2 (1, tonumber(cur:fetch()), "Rollback failed")
|
|
assert2 (true, cur:close(), "couldn't close cursor")
|
|
assert2 (false, cur:close())
|
|
-- delete a record and roll back the operation.
|
|
assert2 (1, CONN:execute ("delete from t where f1 = 'a'"))
|
|
cur = CUR_OK (CONN:execute ("select count(*) from t"))
|
|
assert2 (0, tonumber(cur:fetch()))
|
|
assert2 (true, cur:close(), "couldn't close cursor")
|
|
assert2 (false, cur:close())
|
|
CONN:rollback ()
|
|
-- check resulting table with one record.
|
|
cur = CUR_OK (CONN:execute ("select count(*) from t"))
|
|
assert2 (1, tonumber(cur:fetch()), "Rollback failed")
|
|
assert2 (true, cur:close(), "couldn't close cursor")
|
|
assert2 (false, cur:close())
|
|
--[[
|
|
-- insert a second record and turn on the auto-commit mode.
|
|
-- this will produce a rollback on PostgreSQL and a commit on ODBC.
|
|
-- what to do?
|
|
assert2 (1, CONN:execute ("insert into t (f1) values ('b')"))
|
|
cur = CUR_OK (CONN:execute ("select count(*) from t"))
|
|
assert2 (2, tonumber (cur:fetch ()), "Insert failed")
|
|
assert2 (true, cur:close(), "couldn't close cursor")
|
|
assert2 (false, cur:close())
|
|
CONN:setautocommit (true)
|
|
-- check resulting table with one record.
|
|
cur = CUR_OK (CONN:execute ("select count(*) from t"))
|
|
assert2 (1, tonumber(cur:fetch()), "Rollback failed")
|
|
assert2 (true, cur:close(), "couldn't close cursor")
|
|
assert2 (false, cur:close())
|
|
--]]
|
|
-- clean the table.
|
|
if driver == "sqlite" then
|
|
assert2 (1, CONN:execute ("delete from t where 1"))
|
|
else
|
|
assert2 (1, CONN:execute ("delete from t"))
|
|
end
|
|
CONN:commit ()
|
|
CONN:setautocommit (true)
|
|
-- check resulting table with no records.
|
|
cur = CUR_OK (CONN:execute ("select count(*) from t"))
|
|
assert2 (0, tonumber(cur:fetch()), "Rollback failed")
|
|
assert2 (true, cur:close(), "couldn't close cursor")
|
|
assert2 (false, cur:close())
|
|
end
|
|
|
|
---------------------------------------------------------------------
|
|
-- Get column names and types.
|
|
---------------------------------------------------------------------
|
|
function column_info ()
|
|
-- insert elements.
|
|
assert2 (1, CONN:execute ("insert into t (f1, f2, f3, f4) values ('a', 'b', 'c', 'd')"))
|
|
local cur = CUR_OK (CONN:execute ("select f1,f2,f3,f4 from t"))
|
|
-- get column information.
|
|
local names, types = cur:getcolnames(), cur:getcoltypes()
|
|
assert2 ("table", type(names), "getcolnames failed")
|
|
assert2 ("table", type(types), "getcoltypes failed")
|
|
assert2 (4, table.getn(names), "incorrect column names table")
|
|
assert2 (4, table.getn(types), "incorrect column types table")
|
|
for i = 1, table.getn(names) do
|
|
assert2 ("f"..i, names[i], "incorrect column names table")
|
|
local type_i = string.gsub(types[i], "%s+", "")
|
|
assert (type_i == "varchar(30)" or type_i == "string" or type_i == "string(30)", "incorrect column types table")
|
|
end
|
|
-- check if the tables are being reused.
|
|
local n2, t2 = cur:getcolnames(), cur:getcoltypes()
|
|
assert2 (names, n2, "getcolnames is rebuilding the table")
|
|
assert2 (types, t2, "getcoltypes is rebuilding the table")
|
|
assert2 (true, cur:close(), "couldn't close cursor")
|
|
assert2 (false, cur:close())
|
|
-- clean the table.
|
|
assert2 (1, CONN:execute ("delete from t where f1 = 'a'"))
|
|
end
|
|
|
|
---------------------------------------------------------------------
|
|
---------------------------------------------------------------------
|
|
function check_close()
|
|
-- an object with references to it can't be closed
|
|
local cmd = "select * from t"
|
|
local cur = CUR_OK(CONN:execute (cmd))
|
|
--assert2 (false, pcall (CONN.close, CONN), "open cursor could not prevent connection close")
|
|
|
|
assert2 (true, cur:close(), "couldn't close cursor")
|
|
|
|
-- force garbage collection
|
|
local a = {}
|
|
setmetatable(a, {__mode="v"})
|
|
a.CONN = ENV:connect (datasource, username, password)
|
|
cur = CUR_OK(a.CONN:execute (cmd))
|
|
|
|
collectgarbage ()
|
|
collectgarbage ()
|
|
CONN_OK (a.CONN)
|
|
a.cur = cur
|
|
cur = nil
|
|
collectgarbage ()
|
|
assert2(nil, a.cur, "cursor not collected")
|
|
collectgarbage ()
|
|
assert2(nil, a.CONN, "connection not collected")
|
|
end
|
|
|
|
---------------------------------------------------------------------
|
|
---------------------------------------------------------------------
|
|
function drop_table ()
|
|
-- Postgres retorna 0, enquanto ODBC retorna -1.
|
|
assert (CONN:execute ("drop table t"))
|
|
end
|
|
|
|
---------------------------------------------------------------------
|
|
---------------------------------------------------------------------
|
|
function close_conn ()
|
|
assert (true, CONN:close())
|
|
assert (true, ENV:close())
|
|
end
|
|
|
|
|
|
---------------------------------------------------------------------
|
|
tests = {
|
|
{ "basic checking", basic_test },
|
|
{ "create table", create_table },
|
|
{ "fetch two values", fetch2 },
|
|
{ "fetch new table", fetch_new_table },
|
|
{ "fetch many", fetch_many },
|
|
{ "rollback", rollback },
|
|
{ "get column information", column_info },
|
|
{ "close objects", check_close },
|
|
{ "drop table", drop_table },
|
|
{ "close connection", close_conn },
|
|
}
|
|
|
|
---------------------------------------------------------------------
|
|
-- Main
|
|
---------------------------------------------------------------------
|
|
|
|
if type(arg[1]) ~= "string" then
|
|
print (string.format ("Usage %s <driver> [<data source> [, <user> [, <password>]]]", arg[0]))
|
|
os.exit()
|
|
end
|
|
|
|
driver = arg[1]
|
|
datasource = arg[2] or "luasql-test"
|
|
username = arg[3] or nil
|
|
password = arg[4] or nil
|
|
|
|
require (driver)
|
|
assert (luasql, "no luasql table")
|
|
|
|
for i = 1, table.getn (tests) do
|
|
local t = tests[i]
|
|
io.write (t[1].." ...")
|
|
t[2] ()
|
|
io.write (" OK !\n")
|
|
end
|
|
|