diff options
Diffstat (limited to 'plugins/data/data_history.lua')
-rw-r--r-- | plugins/data/data_history.lua | 208 |
1 files changed, 200 insertions, 8 deletions
diff --git a/plugins/data/data_history.lua b/plugins/data/data_history.lua index 331243b57bf..b233405e984 100644 --- a/plugins/data/data_history.lua +++ b/plugins/data/data_history.lua @@ -1,18 +1,210 @@ local dat = {} + +local db = require('data/database') local ver, info -local datread = require("data/load_dat") -datread, ver = datread.open("history.dat", "## REVISION:") +local file = 'history.xml' +local tablename + +local function init() + -- check for old history table + if db.get_version('history.dat') then + db.exec([[DROP TABLE "history.dat";]]) + db.exec([[DROP TABLE "history.dat_idx";]]) + db.set_version('history.dat', nil) + end + + local fh, filepath, dbver + fh, filepath, tablename, dbver = db.open_data_file(file) + if not fh then + if dbver then + -- data in database but missing file, just use what we have + ver = dbver + end + return + end + + -- scan file for version + for line in fh:lines() do + local match = line:match('<history([^>]*)>') + if match then + match = match:match('version="([^"]*)"') + if match then + ver = match + break + end + end + end + if (not ver) or (ver == dbver) then + fh:close() + ver = dbver + return + end + + if not dbver then + db.exec( + string.format( + [[CREATE TABLE "%s_idx" ( + name VARCHAR NOT NULL, + list VARCHAR NOT NULL, + data INTEGER NOT NULL);]], + tablename)) + db.check(string.format('creating %s index table', file)) + db.exec(string.format([[CREATE TABLE "%s" (data CLOB NOT NULL);]], tablename)) + db.check(string.format('creating %s data table', file)) + db.exec( + string.format( + [[CREATE INDEX "namelist_%s" ON "%s_idx" (name, list);]], + tablename, tablename)) + db.check(string.format('creating %s name/list index', file)) + end + + local slaxml = require('xml') + + db.exec([[BEGIN TRANSACTION;]]) + if not db.check(string.format('starting %s transaction', file)) then + fh:close() + ver = dbver + return + end + + -- clean out previous data and update the version + if dbver then + db.exec(string.format([[DELETE FROM "%s";]], tablename)) + if not db.check(string.format('deleting previous %s data', file)) then + db.exec([[ROLLBACK TRANSACTION;]]) + fh:close() + ver = dbver + return + end + db.exec(string.format([[DELETE FROM "%s_idx";]], tablename)) + if not db.check(string.format('deleting previous %s data', file)) then + db.exec([[ROLLBACK TRANSACTION;]]) + fh:close() + ver = dbver + return + end + end + db.set_version(file, ver) + if not db.check(string.format('updating %s version', file)) then + db.exec([[ROLLBACK TRANSACTION;]]) + fh:close() + ver = dbver + return + end + + fh:seek('set') + local buffer = fh:read('a') + + local lasttag + local entry = {} + local rowid + + local dataquery = db.prepare( + string.format([[INSERT INTO "%s" (data) VALUES (?);]], tablename)) + local indexquery = db.prepare( + string.format([[INSERT INTO "%s_idx" (name, list, data) VALUES (?, ?, ?);]], tablename)) + + local parser = slaxml:parser{ + startElement = function(name) + lasttag = name + if name == 'entry' then + entry = {} + rowid = nil + elseif (name == 'system') or (name == 'item') then + table.insert(entry, {}) + end + end, + attribute = function(name, value) + if (name == 'name') or (name == 'list') then + entry[#entry][name] = value + end + end, + text = function(text, cdata) + if lasttag == 'text' then + text = text:gsub('\r', '') -- strip carriage returns + dataquery:bind_values(text) + while true do + local status = dataquery:step() + if status == db.DONE then + rowid = dataquery:last_insert_rowid(); + break + elseif result == db.BUSY then + emu.print_error(string.format('Database busy: inserting %s data', file)) + -- FIXME: how to abort parse and roll back? + break + elseif result ~= db.ROW then + db.check(string.format('inserting %s data', file)) + break + end + end + dataquery:reset() + end + end, + closeElement = function(name) + if (name == 'entry') and rowid then + for num, entry in pairs(entry) do + indexquery:bind_values(entry.name, entry.list or '', rowid) + while true do + local status = indexquery:step() + if status == db.DONE then + break + elseif status == db.BUSY then + emu.print_error(string.format('Database busy: inserting %s data', file)) + -- FIXME: how to abort parse and roll back? + break + elseif result ~= db.ROW then + db.check(string.format('inserting %s data', file)) + break + end + end + indexquery:reset() + end + end + end + } + + parser:parse(buffer, { stripWhitespace = true }) + dataquery:finalize() + indexquery:finalize() + fh:close() + + db.exec([[COMMIT TRANSACTION;]]) + if not db.check(string.format('committing %s transaction', file)) then + db.exec([[ROLLBACK TRANSACTION;]]) + ver = dbver + end +end + +if db then + init() +end function dat.check(set, softlist) - if not datread then + if not ver then return nil end - local status - status, info = pcall(datread, "bio", softlist or "info", set) - if not status or not info then - return nil + + info = nil + local query = db.prepare( + string.format( + [[SELECT f.data + FROM "%s_idx" AS fi LEFT JOIN "%s" AS f ON fi.data = f.rowid + WHERE fi.name = ? AND fi.list = ?;]], + tablename, tablename)) + query:bind_values(set, softlist or '') + while not info do + local status = query:step() + if status == db.ROW then + info = query:get_value(0) + elseif status == db.DONE then + break + elseif status ~= db.BUSY then + db.check(string.format('reading %s data', file)) + break + end end - return _("History") + query:finalize() + return info and _p('plugin-data', 'History') or nil end function dat.get() |