diff options
Diffstat (limited to 'tools/migration')
-rw-r--r-- | tools/migration/migrator/jabberd14.lua | 7 | ||||
-rw-r--r-- | tools/migration/migrator/mtools.lua | 10 | ||||
-rw-r--r-- | tools/migration/migrator/prosody_files.lua | 10 | ||||
-rw-r--r-- | tools/migration/migrator/prosody_sql.lua | 148 | ||||
-rw-r--r-- | tools/migration/prosody-migrator.lua | 37 |
5 files changed, 96 insertions, 116 deletions
diff --git a/tools/migration/migrator/jabberd14.lua b/tools/migration/migrator/jabberd14.lua index 2f0b0b78..a4eef3f7 100644 --- a/tools/migration/migrator/jabberd14.lua +++ b/tools/migration/migrator/jabberd14.lua @@ -9,7 +9,6 @@ local ipairs = ipairs; local coroutine = coroutine; local print = print; -module "jabberd14" local function is_dir(path) return lfs.attributes(path, "mode") == "directory"; end local function is_file(path) return lfs.attributes(path, "mode") == "file"; end @@ -128,7 +127,7 @@ local function loop_over_hosts(path, cb) end end -function reader(input) +local function reader(input) local path = clean_path(assert(input.path, "no input.path specified")); assert(is_dir(path), "input.path is not a directory"); @@ -139,4 +138,6 @@ function reader(input) end end -return _M; +return { + reader = reader; +}; diff --git a/tools/migration/migrator/mtools.lua b/tools/migration/migrator/mtools.lua index e7b774bb..cfbfcce8 100644 --- a/tools/migration/migrator/mtools.lua +++ b/tools/migration/migrator/mtools.lua @@ -4,9 +4,8 @@ local print = print; local t_insert = table.insert; local t_sort = table.sort; -module "mtools" -function sorted(params) +local function sorted(params) local reader = params.reader; -- iterator to get items from local sorter = params.sorter; -- sorting function @@ -28,7 +27,7 @@ function sorted(params) end -function merged(reader, merger) +local function merged(reader, merger) local item1 = reader(); local merged = { item1 }; @@ -53,4 +52,7 @@ function merged(reader, merger) end -return _M; +return { + sorted = sorted; + merged = merged; +} diff --git a/tools/migration/migrator/prosody_files.lua b/tools/migration/migrator/prosody_files.lua index c9367d9c..4de09273 100644 --- a/tools/migration/migrator/prosody_files.lua +++ b/tools/migration/migrator/prosody_files.lua @@ -18,7 +18,6 @@ local error = error; prosody = {}; local dm = require "util.datamanager" -module "prosody_files" local function is_dir(path) return lfs.attributes(path, "mode") == "directory"; end local function is_file(path) return lfs.attributes(path, "mode") == "file"; end @@ -88,7 +87,7 @@ local function decode_user(item) return userdata; end -function reader(input) +local function reader(input) local path = clean_path(assert(input.path, "no input.path specified")); assert(is_dir(path), "input.path is not a directory"); local iter = coroutine.wrap(function()handle_root_dir(path);end); @@ -127,7 +126,7 @@ function reader(input) end end -function writer(output) +local function writer(output) local path = clean_path(assert(output.path, "no output.path specified")); assert(is_dir(path), "output.path is not a directory"); return function(item) @@ -139,4 +138,7 @@ function writer(output) end end -return _M; +return { + reader = reader; + writer = writer; +} diff --git a/tools/migration/migrator/prosody_sql.lua b/tools/migration/migrator/prosody_sql.lua index 27b5835e..2e902fbb 100644 --- a/tools/migration/migrator/prosody_sql.lua +++ b/tools/migration/migrator/prosody_sql.lua @@ -1,6 +1,6 @@ local assert = assert; -local have_DBI, DBI = pcall(require,"DBI"); +local have_DBI = pcall(require,"DBI"); local print = print; local type = type; local next = next; @@ -15,51 +15,25 @@ if not have_DBI then error("LuaDBI (required for SQL support) was not found, please see http://prosody.im/doc/depends#luadbi", 0); end -module "prosody_sql" +local sql = require "util.sql"; + +local function create_table(engine, name) -- luacheck: ignore 431/engine + local Table, Column, Index = sql.Table, sql.Column, sql.Index; + + local ProsodyTable = Table { + name= name or "prosody"; + Column { name="host", type="TEXT", nullable=false }; + Column { name="user", type="TEXT", nullable=false }; + 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="MEDIUMTEXT", nullable=false }; + Index { name="prosody_index", "host", "user", "store", "key" }; + }; + engine:transaction(function() + ProsodyTable:create(engine); + end); -local function create_table(connection, params) - 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"); - end - - local stmt = connection:prepare(create_sql); - if stmt then - local ok = stmt:execute(); - local commit_ok = connection:commit(); - if ok and commit_ok then - 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 stmt, err = connection:prepare(index_sql); - local ok, commit_ok, commit_err; - if stmt then - ok, err = assert(stmt:execute()); - commit_ok, commit_err = assert(connection:commit()); - end - elseif params.driver == "MySQL" then -- COMPAT: Upgrade tables from 0.8.0 - -- Failed to create, but check existing MySQL table here - local stmt = connection:prepare("SHOW COLUMNS FROM prosody WHERE Field='value' and Type='text'"); - local ok = stmt:execute(); - local commit_ok = connection:commit(); - if ok and commit_ok then - if stmt:rowcount() > 0 then - local stmt = connection:prepare("ALTER TABLE prosody MODIFY COLUMN `value` MEDIUMTEXT"); - local ok = stmt:execute(); - local commit_ok = connection:commit(); - if ok and commit_ok then - print("Database table automatically upgraded"); - end - end - repeat until not stmt:fetch(); - end - end - end end local function serialize(value) @@ -110,24 +84,45 @@ local function decode_user(item) return userdata; end -function reader(input) - local dbh = assert(DBI.Connect( - assert(input.driver, "no input.driver specified"), - assert(input.database, "no input.database specified"), - input.username, input.password, - input.host, input.port - )); - assert(dbh:ping()); - local stmt = assert(dbh:prepare("SELECT * FROM prosody")); - assert(stmt:execute()); +local function needs_upgrade(engine, params) + if params.driver == "MySQL" then + local success = engine:transaction(function() + local result = engine:execute("SHOW COLUMNS FROM prosody WHERE Field='value' and Type='text'"); + assert(result:rowcount() == 0); + + -- COMPAT w/pre-0.10: Upgrade table to UTF-8 if not already + local check_encoding_query = [[ + SELECT "COLUMN_NAME","COLUMN_TYPE","TABLE_NAME" + FROM "information_schema"."columns" + WHERE "TABLE_NAME" LIKE 'prosody%%' AND ( "CHARACTER_SET_NAME"!='%s' OR "COLLATION_NAME"!='%s_bin' ); + ]]; + check_encoding_query = check_encoding_query:format(engine.charset, engine.charset); + local result = engine:execute(check_encoding_query); + assert(result:rowcount() == 0) + end); + if not success then + -- Upgrade required + return true; + end + end + return false; +end + +local function reader(input) + local engine = assert(sql:create_engine(input, function (engine) -- luacheck: ignore 431/engine + if needs_upgrade(engine, input) then + error("Old database format detected. Please run: prosodyctl mod_storage_sql upgrade"); + end + end)); local keys = {"host", "user", "store", "key", "type", "value"}; - local f,s,val = stmt:rows(true); + assert(engine:connect()); + local f,s,val = assert(engine:select("SELECT \"host\", \"user\", \"store\", \"key\", \"type\", \"value\" FROM \"prosody\";")); -- get SQL rows, sorted local iter = mtools.sorted { reader = function() val = f(s, val); return val; end; filter = function(x) for i=1,#keys do - if not x[keys[i]] then return false; end -- TODO log error, missing field + x[ keys[i] ] = x[i]; end if x.host == "" then x.host = nil; end if x.user == "" then x.user = nil; end @@ -154,27 +149,19 @@ function reader(input) end; end -function writer(output, iter) - local dbh = assert(DBI.Connect( - assert(output.driver, "no output.driver specified"), - assert(output.database, "no output.database specified"), - output.username, output.password, - output.host, output.port - )); - assert(dbh:ping()); - create_table(dbh, output); - local stmt = assert(dbh:prepare("SELECT * FROM prosody")); - assert(stmt:execute()); - local stmt = assert(dbh:prepare("DELETE FROM prosody")); - assert(stmt:execute()); - local insert_sql = "INSERT INTO `prosody` (`host`,`user`,`store`,`key`,`type`,`value`) VALUES (?,?,?,?,?,?)"; - if output.driver == "PostgreSQL" then - insert_sql = insert_sql:gsub("`", "\""); - end - local insert = assert(dbh:prepare(insert_sql)); +local function writer(output, iter) + local engine = assert(sql:create_engine(output, function (engine) -- luacheck: ignore 431/engine + if needs_upgrade(engine, output) then + error("Old database format detected. Please run: prosodyctl mod_storage_sql upgrade"); + end + create_table(engine); + end)); + assert(engine:connect()); + assert(engine:delete("DELETE FROM \"prosody\"")); + local insert_sql = "INSERT INTO \"prosody\" (\"host\",\"user\",\"store\",\"key\",\"type\",\"value\") VALUES (?,?,?,?,?,?)"; return function(item) - if not item then assert(dbh:commit()) return dbh:close(); end -- end of input + if not item then assert(engine.conn:commit()) return end -- end of input local host = item.host or ""; local user = item.user or ""; for store, data in pairs(item.stores) do @@ -183,18 +170,21 @@ function writer(output, iter) for key, value in pairs(data) do if type(key) == "string" and key ~= "" then local t, value = assert(serialize(value)); - local ok, err = assert(insert:execute(host, user, store, key, t, value)); + local ok, err = assert(engine:insert(insert_sql, host, user, store, key, t, value)); else extradata[key] = value; end end if next(extradata) ~= nil then local t, extradata = assert(serialize(extradata)); - local ok, err = assert(insert:execute(host, user, store, "", t, extradata)); + local ok, err = assert(engine:insert(insert_sql, host, user, store, "", t, extradata)); end end end; end -return _M; +return { + reader = reader; + writer = writer; +} diff --git a/tools/migration/prosody-migrator.lua b/tools/migration/prosody-migrator.lua index 7c933b88..1219d891 100644 --- a/tools/migration/prosody-migrator.lua +++ b/tools/migration/prosody-migrator.lua @@ -5,30 +5,29 @@ CFG_CONFIGDIR=os.getenv("PROSODY_CFGDIR"); -- Substitute ~ with path to home directory in paths if CFG_CONFIGDIR then - CFG_CONFIGDIR = CFG_CONFIGDIR:gsub("^~", os.getenv("HOME")); + CFG_CONFIGDIR = CFG_CONFIGDIR:gsub("^~", os.getenv("HOME")); end if CFG_SOURCEDIR then - CFG_SOURCEDIR = CFG_SOURCEDIR:gsub("^~", os.getenv("HOME")); + CFG_SOURCEDIR = CFG_SOURCEDIR:gsub("^~", os.getenv("HOME")); end local default_config = (CFG_CONFIGDIR or ".").."/migrator.cfg.lua"; -- Command-line parsing local options = {}; -local handled_opts = 0; -for i = 1, #arg do +local i = 1; +while arg[i] do if arg[i]:sub(1,2) == "--" then local opt, val = arg[i]:match("([%w-]+)=?(.*)"); if opt then options[(opt:sub(3):gsub("%-", "_"))] = #val > 0 and val or true; end - handled_opts = i; + table.remove(arg, i); else - break; + i = i + 1; end end -table.remove(arg, handled_opts); if CFG_SOURCEDIR then package.path = CFG_SOURCEDIR.."/?.lua;"..package.path; @@ -40,24 +39,15 @@ end local envloadfile = require "util.envload".envloadfile; --- Load config file -local function loadfilein(file, env) - if loadin then - return loadin(env, io.open(file):read("*a")); - else - return envloadfile(file, env); - end -end - local config_file = options.config or default_config; local from_store = arg[1] or "input"; local to_store = arg[2] or "output"; config = {}; local config_env = setmetatable({}, { __index = function(t, k) return function(tbl) config[k] = tbl; end; end }); -local config_chunk, err = loadfilein(config_file, config_env); +local config_chunk, err = envloadfile(config_file, config_env); if not config_chunk then - print("There was an error loading the config file, check the file exists"); + print("There was an error loading the config file, check that the file exists"); print("and that the syntax is correct:"); print("", err); os.exit(1); @@ -87,13 +77,8 @@ function load_store_handler(name) else local ok, err = pcall(require, "migrator."..store_type); if not ok then - if package.loaded["migrator."..store_type] then - print(("Error: Failed to initialize '%s' store:\n\t%s") - :format(name, err)); - else - print(("Error: Unrecognised store type for '%s': %s") - :format(from_store, store_type)); - end + print(("Error: Failed to initialize '%s' store:\n\t%s") + :format(name, err)); return false; end end @@ -115,7 +100,7 @@ if have_err then print(""); os.exit(1); end - + local itype = config[from_store].type; local otype = config[to_store].type; local reader = require("migrator."..itype).reader(config[from_store]); |