X-Git-Url: https://git.enpas.org/?a=blobdiff_plain;f=plugins%2Fmod_storage_sql2.lua;h=e5357416913f6137525780eb4fe734095ea57e7c;hb=163d2b9489196e5c9a532ecd4494afaddc414df6;hp=90e9a2f60911f3fec505abf156d44e25badcd174;hpb=e698bae417a8cb50160038560633ca1bc2e50a83;p=prosody.git diff --git a/plugins/mod_storage_sql2.lua b/plugins/mod_storage_sql2.lua index 90e9a2f6..e5357416 100644 --- a/plugins/mod_storage_sql2.lua +++ b/plugins/mod_storage_sql2.lua @@ -2,7 +2,7 @@ local json = require "util.json"; local xml_parse = require "util.xml".parse; local uuid = require "util.uuid"; -local resolve_relative_path = require "core.configmanager".resolve_relative_path; +local resolve_relative_path = require "util.paths".resolve_relative_path; local stanza_mt = require"util.stanza".stanza_mt; local getmetatable = getmetatable; @@ -27,7 +27,7 @@ local engine; -- TODO create engine local function create_table() local Table,Column,Index = mod_sql.Table,mod_sql.Column,mod_sql.Index; - --[[ + local ProsodyTable = Table { name="prosody"; Column { name="host", type="TEXT", nullable=false }; @@ -35,39 +35,16 @@ local function create_table() Column { name="store", type="TEXT", nullable=false }; Column { name="key", type="TEXT", nullable=false }; Column { name="type", type="TEXT", nullable=false }; - Column { name="value", type="TEXT", nullable=false }; + Column { name="value", type="MEDIUMTEXT", nullable=false }; Index { name="prosody_index", "host", "user", "store", "key" }; }; engine:transaction(function() ProsodyTable:create(engine); - end);]] - if not module:get_option("sql_manage_tables", true) then - return; - end - - local create_sql = "CREATE TABLE `prosody` (`host` TEXT, `user` TEXT, `store` TEXT, `key` TEXT, `type` TEXT, `value` TEXT);"; - if params.driver == "PostgreSQL" then - create_sql = create_sql:gsub("`", "\""); - elseif params.driver == "MySQL" then - create_sql = create_sql:gsub("`value` TEXT", "`value` MEDIUMTEXT") - :gsub(";$", " CHARACTER SET 'utf8' COLLATE 'utf8_bin';"); - end - - local index_sql = "CREATE INDEX `prosody_index` ON `prosody` (`host`, `user`, `store`, `key`)"; - if params.driver == "PostgreSQL" then - index_sql = index_sql:gsub("`", "\""); - elseif params.driver == "MySQL" then - index_sql = index_sql:gsub("`([,)])", "`(20)%1"); - end - - local success,err = engine:transaction(function() - engine:execute(create_sql); - engine:execute(index_sql); end); local ProsodyArchiveTable = Table { name="prosodyarchive"; - Column { name="sort_id", type="INTEGER PRIMARY KEY AUTOINCREMENT", nullable=false }; + Column { name="sort_id", type="INTEGER", primary_key=true, auto_increment=true }; Column { name="host", type="TEXT", nullable=false }; Column { name="user", type="TEXT", nullable=false }; Column { name="store", type="TEXT", nullable=false }; @@ -75,24 +52,14 @@ local function create_table() Column { name="when", type="INTEGER", nullable=false }; -- timestamp Column { name="with", type="TEXT", nullable=false }; -- related id Column { name="type", type="TEXT", nullable=false }; - Column { name="value", type=params.driver == "MySQL" and "MEDIUMTEXT" or "TEXT", nullable=false }; - Index { name="prosodyarchive_index", "host", "user", "store", "key" }; + Column { name="value", type="MEDIUMTEXT", nullable=false }; + Index { name="prosodyarchive_index", unique = true, "host", "user", "store", "key" }; }; engine:transaction(function() ProsodyArchiveTable:create(engine); end); end -local function set_encoding() - if params.driver == "SQLite3" then return end - local set_names_query = "SET NAMES 'utf8';"; - if params.driver == "MySQL" then - set_names_query = set_names_query:gsub(";$", " COLLATE 'utf8_bin';"); - end - local success,err = engine:transaction(function() return engine:execute(set_names_query); end); - if not success then - module:log("error", "Failed to set database connection encoding to UTF8: %s", err); - end -end + local function upgrade_table() if params.driver == "MySQL" then local success,err = engine:transaction(function() @@ -146,12 +113,14 @@ do -- process options to get a db connection --local dburi = db2uri(params); engine = mod_sql:create_engine(params); - set_encoding(); + engine:set_encoding(); - -- Automatically create table, ignore failure (table probably already exists) - create_table(); - -- Encoding mess - upgrade_table(); + if module:get_option("sql_manage_tables", true) then + -- Automatically create table, ignore failure (table probably already exists) + create_table(); + -- Encoding mess + upgrade_table(); + end end local function serialize(value) @@ -229,7 +198,9 @@ local keyval_store = {}; keyval_store.__index = keyval_store; function keyval_store:get(username) user,store = username,self.store; - return select(2, engine:transaction(keyval_store_get)); + local ok, result = engine:transaction(keyval_store_get); + if not ok then return ok, result; end + return result; end function keyval_store:set(username, data) user,store = username,self.store; @@ -245,12 +216,52 @@ function keyval_store:users() return iterator(result); end +local map_store = {}; +map_store.__index = map_store; +function map_store:get(username, key) + local ok, result = engine:transaction(function() + if type(key) == "string" and key ~= "" then + for row in engine:select("SELECT `type`, `value` FROM `prosody` WHERE `host`=? AND `user`=? AND `store`=? AND `key`=?", host, username or "", self.store, key) do + return deserialize(row[1], row[2]); + end + else + error("TODO: non-string keys"); + end + end); + if not ok then return nil, result; end + return result; +end +function map_store:set(username, key, data) + local ok, result = engine:transaction(function() + if type(key) == "string" and key ~= "" then + engine:delete("DELETE FROM `prosody` WHERE `host`=? AND `user`=? AND `store`=? AND `key`=?", + host, username or "", self.store, key); + if data ~= nil then + local t, value = assert(serialize(data)); + engine:insert("INSERT INTO `prosody` (`host`,`user`,`store`,`key`,`type`,`value`) VALUES (?,?,?,?,?,?)", host, username or "", self.store, key, t, value); + end + else + error("TODO: non-string keys"); + end + return true; + end); + if not ok then return nil, result; end + return result; +end + local archive_store = {} archive_store.__index = archive_store -function archive_store:append(username, when, with, value) +function archive_store:append(username, key, when, with, value) + if value == nil then -- COMPAT early versions + when, with, value, key = key, when, with, value + end local user,store = username,self.store; return engine:transaction(function() - local key = uuid.generate(); + if key then + engine:delete("DELETE FROM `prosodyarchive` WHERE `host`=? AND `user`=? AND `store`=? AND `key`=?", host, user or "", store, key); + else + key = uuid.generate(); + end local t, value = serialize(value); engine:insert("INSERT INTO `prosodyarchive` (`host`, `user`, `store`, `when`, `with`, `key`, `type`, `value`) VALUES (?,?,?,?,?,?,?,?)", host, user or "", store, when, with, key, t, value); return key; @@ -270,7 +281,7 @@ local function archive_where(query, args, where) if query.start then where[#where] = "`when` BETWEEN ? AND ?" -- is this inclusive? else - where[#where+1] = "`when` >= ?" + where[#where+1] = "`when` <= ?" end end @@ -287,14 +298,16 @@ local function archive_where(query, args, where) end end local function archive_where_id_range(query, args, where) + local args_len = #args -- Before or after specific item, exclusive if query.after then -- keys better be unique! - where[#where+1] = "`sort_id` > (SELECT `sort_id` FROM `prosodyarchive` WHERE `key` = ? LIMIT 1)" - args[#args+1] = query.after + where[#where+1] = "`sort_id` > (SELECT `sort_id` FROM `prosodyarchive` WHERE `key` = ? AND `host` = ? AND `user` = ? AND `store` = ? LIMIT 1)" + args[args_len+1], args[args_len+2], args[args_len+3], args[args_len+4] = query.after, args[1], args[2], args[3]; + args_len = args_len + 4 end if query.before then - where[#where+1] = "`sort_id` < (SELECT `sort_id` FROM `prosodyarchive` WHERE `key` = ? LIMIT 1)" - args[#args+1] = query.before + where[#where+1] = "`sort_id` < (SELECT `sort_id` FROM `prosodyarchive` WHERE `key` = ? AND `host` = ? AND `user` = ? AND `store` = ? LIMIT 1)" + args[args_len+1], args[args_len+2], args[args_len+3], args[args_len+4] = query.before, args[1], args[2], args[3]; end end @@ -311,7 +324,7 @@ function archive_store:find(username, query) -- Total matching if query.total then - local stats = engine:select(sql_query:gsub("^(SELECT).-(FROM)", "%1 COUNT(*) %2"):format(t_concat(where, " AND "), "DESC", ""), unpack(args)); + local stats = engine:select("SELECT COUNT(*) FROM `prosodyarchive` WHERE " .. t_concat(where, " AND "), unpack(args)); if stats then local _total = stats() total = _total and _total[1]; @@ -347,6 +360,10 @@ function archive_store:delete(username, query) local sql_query = "DELETE FROM `prosodyarchive` WHERE %s;"; local args = { host, user or "", store, }; local where = { "`host` = ?", "`user` = ?", "`store` = ?", }; + if user == true then + table.remove(args, 2); + table.remove(where, 2); + end archive_where(query, args, where); archive_where_id_range(query, args, where); sql_query = sql_query:format(t_concat(where, " AND ")); @@ -357,6 +374,7 @@ end local stores = { keyval = keyval_store; + map = map_store; archive = archive_store; };