Software /
code /
prosody
File
plugins/mod_storage_internal.lua @ 10843:8fcd46ee9bf5 0.11
mod_storage_internal: Fix error in time limited queries on items without 'when' field, fixes #1557
author | Kim Alvefur <zash@zash.se> |
---|---|
date | Fri, 15 May 2020 21:22:35 +0200 |
parent | 9105:e735c9865f42 |
child | 10844:05f4386c846e |
line wrap: on
line source
local datamanager = require "core.storagemanager".olddm; local array = require "util.array"; local datetime = require "util.datetime"; local st = require "util.stanza"; local now = require "util.time".now; local id = require "util.id".medium; local host = module.host; local driver = {}; function driver:open(store, typ) local mt = self[typ or "keyval"] if not mt then return nil, "unsupported-store"; end return setmetatable({ store = store, type = typ }, mt); end function driver:stores(username) -- luacheck: ignore 212/self return datamanager.stores(username, host); end function driver:purge(user) -- luacheck: ignore 212/self return datamanager.purge(user, host); end local keyval = { }; driver.keyval = { __index = keyval }; function keyval:get(user) return datamanager.load(user, host, self.store); end function keyval:set(user, data) return datamanager.store(user, host, self.store, data); end function keyval:users() return datamanager.users(host, self.store, self.type); end local archive = {}; driver.archive = { __index = archive }; function archive:append(username, key, value, when, with) when = when or now(); if not st.is_stanza(value) then return nil, "unsupported-datatype"; end value = st.preserialize(st.clone(value)); value.when = when; value.with = with; value.attr.stamp = datetime.datetime(when); value.attr.stamp_legacy = datetime.legacy(when); if key then local items, err = datamanager.list_load(username, host, self.store); if not items and err then return items, err; end if items then items = array(items); items:filter(function (item) return item.key ~= key; end); value.key = key; items:push(value); local ok, err = datamanager.list_store(username, host, self.store, items); if not ok then return ok, err; end return key; end else key = id(); end value.key = key; local ok, err = datamanager.list_append(username, host, self.store, value); if not ok then return ok, err; end return key; end function archive:find(username, query) local items, err = datamanager.list_load(username, host, self.store); if not items then if err then return items, err; else return function () end, 0; end end local count = #items; local i = 0; if query then items = array(items); if query.key then items:filter(function (item) return item.key == query.key; end); end if query.with then items:filter(function (item) return item.with == query.with; end); end if query.start then items:filter(function (item) local when = item.when or datetime.parse(item.attr.stamp); return when >= query.start; end); end if query["end"] then items:filter(function (item) local when = item.when or datetime.parse(item.attr.stamp); return when <= query["end"]; end); end count = #items; if query.reverse then items:reverse(); if query.before then for j = 1, count do if (items[j].key or tostring(j)) == query.before then i = j; break; end end end elseif query.after then for j = 1, count do if (items[j].key or tostring(j)) == query.after then i = j; break; end end end if query.limit and #items - i > query.limit then items[i+query.limit+1] = nil; end end return function () i = i + 1; local item = items[i]; if not item then return; end local key = item.key or tostring(i); local when = item.when or datetime.parse(item.attr.stamp); local with = item.with; item.key, item.when, item.with = nil, nil, nil; item.attr.stamp = nil; item.attr.stamp_legacy = nil; item = st.deserialize(item); return key, item, when, with; end, count; end function archive:dates(username) local items, err = datamanager.list_load(username, host, self.store); if not items then return items, err; end return array(items):pluck("when"):map(datetime.date):unique(); end function archive:delete(username, query) if not query or next(query) == nil then return datamanager.list_store(username, host, self.store, nil); end local items, err = datamanager.list_load(username, host, self.store); if not items then if err then return items, err; end -- Store is empty return 0; end items = array(items); local count_before = #items; if query then if query.key then items:filter(function (item) return item.key ~= query.key; end); end if query.with then items:filter(function (item) return item.with ~= query.with; end); end if query.start then items:filter(function (item) return item.when < query.start; end); end if query["end"] then items:filter(function (item) return item.when > query["end"]; end); end if query.truncate and #items > query.truncate then if query.reverse then -- Before: { 1, 2, 3, 4, 5, } -- After: { 1, 2, 3 } for i = #items, query.truncate + 1, -1 do items[i] = nil; end else -- Before: { 1, 2, 3, 4, 5, } -- After: { 3, 4, 5 } local offset = #items - query.truncate; for i = 1, #items do items[i] = items[i+offset]; end end end end local count = count_before - #items; if count == 0 then return 0; -- No changes, skip write end local ok, err = datamanager.list_store(username, host, self.store, items); if not ok then return ok, err; end return count; end module:provides("storage", driver);