local setmetatable, getmetatable = setmetatable, getmetatable;
local ipairs, unpack, select = ipairs, table.unpack or unpack, select; --luacheck: ignore 113
local tonumber, tostring = tonumber, tostring;
-local assert, xpcall, debug_traceback = assert, xpcall, debug.traceback;
+local type = type;
+local assert, pcall, xpcall, debug_traceback = assert, pcall, xpcall, debug.traceback;
local t_concat = table.concat;
local s_char = string.char;
local log = require "util.logger".init("sql");
local function is_index(x) return getmetatable(x)==index_mt; end
local function is_table(x) return getmetatable(x)==table_mt; end
local function is_query(x) return getmetatable(x)==query_mt; end
-local function Integer(n) return "Integer()" end
-local function String(n) return "String()" end
+local function Integer() return "Integer()" end
+local function String() return "String()" end
local function Column(definition)
return setmetatable(definition, column_mt);
local params = self.params;
assert(params.driver, "no driver")
log("debug", "Connecting to [%s] %s...", params.driver, params.database);
- local dbh, err = DBI.Connect(
+ local ok, dbh, err = pcall(DBI.Connect,
params.driver, params.database,
params.username, params.password,
params.host, params.port
);
+ if not ok then return ok, dbh; end
if not dbh then return nil, err; end
dbh:autocommit(false); -- don't commit automatically
self.conn = dbh;
function engine:onconnect()
-- Override from create_engine()
end
+
+function engine:prepquery(sql)
+ if self.params.driver == "PostgreSQL" then
+ sql = sql:gsub("`", "\"");
+ end
+ return sql;
+end
+
function engine:execute(sql, ...)
local success, err = self:connect();
if not success then return success, err; end
end
function engine:execute_query(sql, ...)
- if self.params.driver == "PostgreSQL" then
- sql = sql:gsub("`", "\"");
- end
+ sql = self:prepquery(sql);
local stmt = assert(self.conn:prepare(sql));
assert(stmt:execute(...));
return stmt:rows();
end
function engine:execute_update(sql, ...)
- if self.params.driver == "PostgreSQL" then
- sql = sql:gsub("`", "\"");
- end
+ sql = self:prepquery(sql);
local prepared = self.prepared;
local stmt = prepared[sql];
if not stmt then
engine.update = engine.execute_update;
end
end
+local function handleerr(err)
+ log("error", "Error in SQL transaction: %s", debug_traceback(err, 3));
+ return err;
+end
function engine:_transaction(func, ...)
if not self.conn then
local ok, err = self:connect();
local function f() return func(unpack(args, 1, n_args)); end
log("debug", "SQL transaction begin [%s]", tostring(func));
self.__transaction = true;
- local success, a, b, c = xpcall(f, debug_traceback);
+ local success, a, b, c = xpcall(f, handleerr);
self.__transaction = nil;
if success then
log("debug", "SQL transaction success [%s]", tostring(func));
local driver = self.params.driver;
if driver == "SQLite3" then
return self:transaction(function()
- if self:select"PRAGMA encoding;"()[1] == "UTF-8" then
- self.charset = "utf8";
+ for encoding in self:select"PRAGMA encoding;" do
+ if encoding[1] == "UTF-8" then
+ self.charset = "utf8";
+ end
end
end);
end
local set_names_query = "SET NAMES '%s';"
local charset = "utf8";
if driver == "MySQL" then
- local ok, charsets = self:transaction(function()
- return self:select"SELECT `CHARACTER_SET_NAME` FROM `information_schema`.`CHARACTER_SETS` WHERE `CHARACTER_SET_NAME` LIKE 'utf8%' ORDER BY MAXLEN DESC LIMIT 1;";
+ self:transaction(function()
+ for row in self:select"SELECT `CHARACTER_SET_NAME` FROM `information_schema`.`CHARACTER_SETS` WHERE `CHARACTER_SET_NAME` LIKE 'utf8%' ORDER BY MAXLEN DESC LIMIT 1;" do
+ charset = row and row[1] or charset;
+ end
end);
- local row = ok and charsets();
- charset = row and row[1] or charset;
set_names_query = set_names_query:gsub(";$", (" COLLATE '%s';"):format(charset.."_bin"));
end
self.charset = charset;
local ok, actual_charset = self:transaction(function ()
return self:select"SHOW SESSION VARIABLES LIKE 'character_set_client'";
end);
+ local charset_ok = true;
for row in actual_charset do
if row[2] ~= charset then
log("error", "MySQL %s is actually %q (expected %q)", row[1], row[2], charset);
- return false, "Failed to set connection encoding";
+ charset_ok = false;
end
end
+ if not charset_ok then
+ return false, "Failed to set connection encoding";
+ end
end
return true;