Comparison

plugins/mod_storage_sql.lua @ 8039:1f4bd8009961

Merge 0.10->trunk
author Kim Alvefur <zash@zash.se>
date Sat, 01 Apr 2017 21:48:54 +0200
parent 7909:428d4abee723
parent 8038:cd6cef579e82
child 8051:b2681397bafa
comparison
equal deleted inserted replaced
8028:adfc7f3b29ce 8039:1f4bd8009961
30 if t == "string" or t == "boolean" or t == "number" then 30 if t == "string" or t == "boolean" or t == "number" then
31 return t, tostring(value); 31 return t, tostring(value);
32 elseif is_stanza(value) then 32 elseif is_stanza(value) then
33 return "xml", tostring(value); 33 return "xml", tostring(value);
34 elseif t == "table" then 34 elseif t == "table" then
35 local value,err = json.encode(value); 35 local encoded,err = json.encode(value);
36 if value then return "json", value; end 36 if value then return "json", encoded; end
37 return nil, err; 37 return nil, err;
38 end 38 end
39 return nil, "Unhandled value type: "..t; 39 return nil, "Unhandled value type: "..t;
40 end 40 end
41 local function deserialize(t, value) 41 local function deserialize(t, value)
55 local user, store; 55 local user, store;
56 56
57 local function keyval_store_get() 57 local function keyval_store_get()
58 local haveany; 58 local haveany;
59 local result = {}; 59 local result = {};
60 for row in engine:select("SELECT `key`,`type`,`value` FROM `prosody` WHERE `host`=? AND `user`=? AND `store`=?", host, user or "", store) do 60 local select_sql = [[
61 SELECT `key`,`type`,`value`
62 FROM `prosody`
63 WHERE `host`=? AND `user`=? AND `store`=?;
64 ]]
65 for row in engine:select(select_sql, host, user or "", store) do
61 haveany = true; 66 haveany = true;
62 local k = row[1]; 67 local k = row[1];
63 local v = deserialize(row[2], row[3]); 68 local v = deserialize(row[2], row[3]);
64 if k and v then 69 if k and v then
65 if k ~= "" then result[k] = v; elseif type(v) == "table" then 70 if k ~= "" then result[k] = v; elseif type(v) == "table" then
72 if haveany then 77 if haveany then
73 return result; 78 return result;
74 end 79 end
75 end 80 end
76 local function keyval_store_set(data) 81 local function keyval_store_set(data)
77 engine:delete("DELETE FROM `prosody` WHERE `host`=? AND `user`=? AND `store`=?", host, user or "", store); 82 local delete_sql = [[
78 83 DELETE FROM `prosody`
84 WHERE `host`=? AND `user`=? AND `store`=?
85 ]];
86 engine:delete(delete_sql, host, user or "", store);
87
88 local insert_sql = [[
89 INSERT INTO `prosody`
90 (`host`,`user`,`store`,`key`,`type`,`value`)
91 VALUES (?,?,?,?,?,?);
92 ]]
79 if data and next(data) ~= nil then 93 if data and next(data) ~= nil then
80 local extradata = {}; 94 local extradata = {};
81 for key, value in pairs(data) do 95 for key, value in pairs(data) do
82 if type(key) == "string" and key ~= "" then 96 if type(key) == "string" and key ~= "" then
83 local t, value = assert(serialize(value)); 97 local t, encoded_value = assert(serialize(value));
84 engine:insert("INSERT INTO `prosody` (`host`,`user`,`store`,`key`,`type`,`value`) VALUES (?,?,?,?,?,?)", host, user or "", store, key, t, value); 98 engine:insert(delete_sql, host, user or "", store, key, t, encoded_value);
85 else 99 else
86 extradata[key] = value; 100 extradata[key] = value;
87 end 101 end
88 end 102 end
89 if next(extradata) ~= nil then 103 if next(extradata) ~= nil then
90 local t, extradata = assert(serialize(extradata)); 104 local t, encoded_extradata = assert(serialize(extradata));
91 engine:insert("INSERT INTO `prosody` (`host`,`user`,`store`,`key`,`type`,`value`) VALUES (?,?,?,?,?,?)", host, user or "", store, "", t, extradata); 105 engine:insert(insert_sql, host, user or "", store, "", t, encoded_extradata);
92 end 106 end
93 end 107 end
94 return true; 108 return true;
95 end 109 end
96 110
113 return keyval_store_set(data); 127 return keyval_store_set(data);
114 end); 128 end);
115 end 129 end
116 function keyval_store:users() 130 function keyval_store:users()
117 local ok, result = engine:transaction(function() 131 local ok, result = engine:transaction(function()
118 return engine:select("SELECT DISTINCT `user` FROM `prosody` WHERE `host`=? AND `store`=?", host, self.store); 132 local select_sql = [[
133 SELECT DISTINCT `user`
134 FROM `prosody`
135 WHERE `host`=? AND `store`=?;
136 ]];
137 return engine:select(select_sql, host, self.store);
119 end); 138 end);
120 if not ok then return ok, result end 139 if not ok then return ok, result end
121 return iterator(result); 140 return iterator(result);
122 end 141 end
123 142
127 local map_store = {}; 146 local map_store = {};
128 map_store.__index = map_store; 147 map_store.__index = map_store;
129 map_store.remove = {}; 148 map_store.remove = {};
130 function map_store:get(username, key) 149 function map_store:get(username, key)
131 local ok, result = engine:transaction(function() 150 local ok, result = engine:transaction(function()
151 local query = [[
152 SELECT `type`, `value`
153 FROM `prosody`
154 WHERE `host`=? AND `user`=? AND `store`=? AND `key`=?
155 LIMIT 1
156 ]];
132 local data; 157 local data;
133 if type(key) == "string" and key ~= "" then 158 if type(key) == "string" and key ~= "" then
134 for row in engine:select("SELECT `type`, `value` FROM `prosody` WHERE `host`=? AND `user`=? AND `store`=? AND `key`=? LIMIT 1", host, username or "", self.store, key) do 159 for row in engine:select(query, host, username or "", self.store, key) do
135 data = deserialize(row[1], row[2]); 160 data = deserialize(row[1], row[2]);
136 end 161 end
137 return data; 162 return data;
138 else 163 else
139 for row in engine:select("SELECT `type`, `value` FROM `prosody` WHERE `host`=? AND `user`=? AND `store`=? AND `key`=? LIMIT 1", host, username or "", self.store, "") do 164 for row in engine:select(query, host, username or "", self.store, "") do
140 data = deserialize(row[1], row[2]); 165 data = deserialize(row[1], row[2]);
141 end 166 end
142 return data and data[key] or nil; 167 return data and data[key] or nil;
143 end 168 end
144 end); 169 end);
149 if data == nil then data = self.remove; end 174 if data == nil then data = self.remove; end
150 return self:set_keys(username, { [key] = data }); 175 return self:set_keys(username, { [key] = data });
151 end 176 end
152 function map_store:set_keys(username, keydatas) 177 function map_store:set_keys(username, keydatas)
153 local ok, result = engine:transaction(function() 178 local ok, result = engine:transaction(function()
179 local delete_sql = [[
180 DELETE FROM `prosody`
181 WHERE `host`=? AND `user`=? AND `store`=? AND `key`=?;
182 ]];
183 local insert_sql = [[
184 INSERT INTO `prosody`
185 (`host`,`user`,`store`,`key`,`type`,`value`)
186 VALUES (?,?,?,?,?,?);
187 ]];
188 local select_extradata_sql = [[
189 SELECT `type`, `value`
190 FROM `prosody`
191 WHERE `host`=? AND `user`=? AND `store`=? AND `key`=?
192 LIMIT 1;
193 ]];
154 for key, data in pairs(keydatas) do 194 for key, data in pairs(keydatas) do
155 if type(key) == "string" and key ~= "" then 195 if type(key) == "string" and key ~= "" then
156 engine:delete("DELETE FROM `prosody` WHERE `host`=? AND `user`=? AND `store`=? AND `key`=?", 196 engine:delete(delete_sql,
157 host, username or "", self.store, key); 197 host, username or "", self.store, key);
158 if data ~= self.remove then 198 if data ~= self.remove then
159 local t, value = assert(serialize(data)); 199 local t, value = assert(serialize(data));
160 engine:insert("INSERT INTO `prosody` (`host`,`user`,`store`,`key`,`type`,`value`) VALUES (?,?,?,?,?,?)", host, username or "", self.store, key, t, value); 200 engine:insert(insert_sql, host, username or "", self.store, key, t, value);
161 end 201 end
162 else 202 else
163 local extradata = {}; 203 local extradata = {};
164 for row in engine:select("SELECT `type`, `value` FROM `prosody` WHERE `host`=? AND `user`=? AND `store`=? AND `key`=? LIMIT 1", host, username or "", self.store, "") do 204 for row in engine:select(select_extradata_sql, host, username or "", self.store, "") do
165 extradata = deserialize(row[1], row[2]); 205 extradata = deserialize(row[1], row[2]);
166 end 206 end
167 engine:delete("DELETE FROM `prosody` WHERE `host`=? AND `user`=? AND `store`=? AND `key`=?", 207 engine:delete(delete_sql, host, username or "", self.store, "");
168 host, username or "", self.store, "");
169 extradata[key] = data; 208 extradata[key] = data;
170 local t, value = assert(serialize(extradata)); 209 local t, value = assert(serialize(extradata));
171 engine:insert("INSERT INTO `prosody` (`host`,`user`,`store`,`key`,`type`,`value`) VALUES (?,?,?,?,?,?)", host, username or "", self.store, "", t, value); 210 engine:insert(insert_sql, host, username or "", self.store, "", t, value);
172 end 211 end
173 end 212 end
174 return true; 213 return true;
175 end); 214 end);
176 if not ok then return nil, result; end 215 if not ok then return nil, result; end
181 archive_store.caps = { 220 archive_store.caps = {
182 total = true; 221 total = true;
183 }; 222 };
184 archive_store.__index = archive_store 223 archive_store.__index = archive_store
185 function archive_store:append(username, key, value, when, with) 224 function archive_store:append(username, key, value, when, with)
186 if type(when) ~= "number" then
187 when, with, value = value, when, with;
188 end
189 local user,store = username,self.store; 225 local user,store = username,self.store;
190 local ok, key = engine:transaction(function() 226 when = when or os.time();
227 with = with or "";
228 local ok, ret = engine:transaction(function()
229 local delete_sql = [[
230 DELETE FROM `prosodyarchive`
231 WHERE `host`=? AND `user`=? AND `store`=? AND `key`=?;
232 ]];
233 local insert_sql = [[
234 INSERT INTO `prosodyarchive`
235 (`host`, `user`, `store`, `when`, `with`, `key`, `type`, `value`)
236 VALUES (?,?,?,?,?,?,?,?);
237 ]];
191 if key then 238 if key then
192 engine:delete("DELETE FROM `prosodyarchive` WHERE `host`=? AND `user`=? AND `store`=? AND `key`=?", host, user or "", store, key); 239 engine:delete(delete_sql, host, user or "", store, key);
193 else 240 else
194 key = uuid.generate(); 241 key = uuid.generate();
195 end 242 end
196 local t, value = assert(serialize(value)); 243 local t, encoded_value = assert(serialize(value));
197 engine:insert("INSERT INTO `prosodyarchive` (`host`, `user`, `store`, `when`, `with`, `key`, `type`, `value`) VALUES (?,?,?,?,?,?,?,?)", host, user or "", store, when, with, key, t, value); 244 engine:insert(insert_sql, host, user or "", store, when, with, key, t, encoded_value);
198 return key; 245 return key;
199 end); 246 end);
200 if not ok then return ok, key; end 247 if not ok then return ok, ret; end
201 return key; 248 return ret; -- the key
202 end 249 end
203 250
204 -- Helpers for building the WHERE clause 251 -- Helpers for building the WHERE clause
205 local function archive_where(query, args, where) 252 local function archive_where(query, args, where)
206 -- Time range, inclusive 253 -- Time range, inclusive
232 end 279 end
233 local function archive_where_id_range(query, args, where) 280 local function archive_where_id_range(query, args, where)
234 local args_len = #args 281 local args_len = #args
235 -- Before or after specific item, exclusive 282 -- Before or after specific item, exclusive
236 if query.after then -- keys better be unique! 283 if query.after then -- keys better be unique!
237 where[#where+1] = "`sort_id` > COALESCE((SELECT `sort_id` FROM `prosodyarchive` WHERE `key` = ? AND `host` = ? AND `user` = ? AND `store` = ? LIMIT 1), 0)" 284 where[#where+1] = [[
285 `sort_id` > COALESCE(
286 (
287 SELECT `sort_id`
288 FROM `prosodyarchive`
289 WHERE `key` = ? AND `host` = ? AND `user` = ? AND `store` = ?
290 LIMIT 1
291 ), 0)
292 ]];
238 args[args_len+1], args[args_len+2], args[args_len+3], args[args_len+4] = query.after, args[1], args[2], args[3]; 293 args[args_len+1], args[args_len+2], args[args_len+3], args[args_len+4] = query.after, args[1], args[2], args[3];
239 args_len = args_len + 4 294 args_len = args_len + 4
240 end 295 end
241 if query.before then 296 if query.before then
242 where[#where+1] = "`sort_id` < COALESCE((SELECT `sort_id` FROM `prosodyarchive` WHERE `key` = ? AND `host` = ? AND `user` = ? AND `store` = ? LIMIT 1), (SELECT MAX(`sort_id`)+1 FROM `prosodyarchive`))" 297 where[#where+1] = [[
298 `sort_id` < COALESCE(
299 (
300 SELECT `sort_id`
301 FROM `prosodyarchive`
302 WHERE `key` = ? AND `host` = ? AND `user` = ? AND `store` = ?
303 LIMIT 1
304 ),
305 (
306 SELECT MAX(`sort_id`)+1
307 FROM `prosodyarchive`
308 )
309 )
310 ]]
243 args[args_len+1], args[args_len+2], args[args_len+3], args[args_len+4] = query.before, args[1], args[2], args[3]; 311 args[args_len+1], args[args_len+2], args[args_len+3], args[args_len+4] = query.before, args[1], args[2], args[3];
244 end 312 end
245 end 313 end
246 314
247 function archive_store:find(username, query) 315 function archive_store:find(username, query)
248 query = query or {}; 316 query = query or {};
249 local user,store = username,self.store; 317 local user,store = username,self.store;
250 local total; 318 local total;
251 local ok, result = engine:transaction(function() 319 local ok, result = engine:transaction(function()
252 local sql_query = "SELECT `key`, `type`, `value`, `when`, `with` FROM `prosodyarchive` WHERE %s ORDER BY `sort_id` %s%s;"; 320 local sql_query = [[
321 SELECT `key`, `type`, `value`, `when`, `with`
322 FROM `prosodyarchive`
323 WHERE %s
324 ORDER BY `sort_id` %s%s;
325 ]];
253 local args = { host, user or "", store, }; 326 local args = { host, user or "", store, };
254 local where = { "`host` = ?", "`user` = ?", "`store` = ?", }; 327 local where = { "`host` = ?", "`user` = ?", "`store` = ?", };
255 328
256 archive_where(query, args, where); 329 archive_where(query, args, where);
257 330
258 -- Total matching 331 -- Total matching
259 if query.total then 332 if query.total then
260 local stats = engine:select("SELECT COUNT(*) FROM `prosodyarchive` WHERE " .. t_concat(where, " AND "), unpack(args)); 333 local stats = engine:select("SELECT COUNT(*) FROM `prosodyarchive` WHERE "
334 .. t_concat(where, " AND "), unpack(args));
261 if stats then 335 if stats then
262 for row in stats do 336 for row in stats do
263 total = row[1]; 337 total = row[1];
264 end 338 end
265 end 339 end
272 346
273 if query.limit then 347 if query.limit then
274 args[#args+1] = query.limit; 348 args[#args+1] = query.limit;
275 end 349 end
276 350
277 sql_query = sql_query:format(t_concat(where, " AND "), query.reverse and "DESC" or "ASC", query.limit and " LIMIT ?" or ""); 351 sql_query = sql_query:format(t_concat(where, " AND "), query.reverse
352 and "DESC" or "ASC", query.limit and " LIMIT ?" or "");
278 return engine:select(sql_query, unpack(args)); 353 return engine:select(sql_query, unpack(args));
279 end); 354 end);
280 if not ok then return ok, result end 355 if not ok then return ok, result end
281 return function() 356 return function()
282 local row = result(); 357 local row = result();
346 end 421 end
347 422
348 --- Initialization 423 --- Initialization
349 424
350 425
351 local function create_table(name) 426 local function create_table(engine, name) -- luacheck: ignore 431/engine
352 local Table, Column, Index = sql.Table, sql.Column, sql.Index; 427 local Table, Column, Index = sql.Table, sql.Column, sql.Index;
353 428
354 local ProsodyTable = Table { 429 local ProsodyTable = Table {
355 name= name or "prosody"; 430 name= name or "prosody";
356 Column { name="host", type="TEXT", nullable=false }; 431 Column { name="host", type="TEXT", nullable=false };
381 engine:transaction(function() 456 engine:transaction(function()
382 ProsodyArchiveTable:create(engine); 457 ProsodyArchiveTable:create(engine);
383 end); 458 end);
384 end 459 end
385 460
386 local function upgrade_table(params, apply_changes) 461 local function upgrade_table(engine, params, apply_changes) -- luacheck: ignore 431/engine
387 local changes = false; 462 local changes = false;
388 if params.driver == "MySQL" then 463 if params.driver == "MySQL" then
389 local success,err = engine:transaction(function() 464 local success,err = engine:transaction(function()
390 local result = engine:execute("SHOW COLUMNS FROM prosody WHERE Field='value' and Type='text'"); 465 local result = engine:execute("SHOW COLUMNS FROM prosody WHERE Field='value' and Type='text'");
391 if result:rowcount() > 0 then 466 if result:rowcount() > 0 then
404 err or "unknown error"); 479 err or "unknown error");
405 return false; 480 return false;
406 end 481 end
407 482
408 -- COMPAT w/pre-0.10: Upgrade table to UTF-8 if not already 483 -- COMPAT w/pre-0.10: Upgrade table to UTF-8 if not already
409 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' );"; 484 local check_encoding_query = [[
485 SELECT `COLUMN_NAME`,`COLUMN_TYPE`,`TABLE_NAME`
486 FROM `information_schema`.`columns`
487 WHERE `TABLE_NAME` LIKE 'prosody%%' AND ( `CHARACTER_SET_NAME`!='%s' OR `COLLATION_NAME`!='%s_bin' );
488 ]];
410 check_encoding_query = check_encoding_query:format(engine.charset, engine.charset); 489 check_encoding_query = check_encoding_query:format(engine.charset, engine.charset);
411 success,err = engine:transaction(function() 490 -- FIXME Is it ok to ignore the return values from this?
491 engine:transaction(function()
412 local result = engine:execute(check_encoding_query); 492 local result = engine:execute(check_encoding_query);
413 local n_bad_columns = result:rowcount(); 493 local n_bad_columns = result:rowcount();
414 if n_bad_columns > 0 then 494 if n_bad_columns > 0 then
415 changes = true; 495 changes = true;
416 if apply_changes then 496 if apply_changes then
434 end 514 end
435 end 515 end
436 return changes; 516 return changes;
437 end 517 end
438 518
439 local function normalize_database(driver, database) 519 local function normalize_database(driver, database) -- luacheck: ignore 431/driver
440 if driver == "SQLite3" and database ~= ":memory:" then 520 if driver == "SQLite3" and database ~= ":memory:" then
441 return resolve_relative_path(prosody.paths.data or ".", database or "prosody.sqlite"); 521 return resolve_relative_path(prosody.paths.data or ".", database or "prosody.sqlite");
442 end 522 end
443 return database; 523 return database;
444 end 524 end
445 525
446 local function normalize_params(params) 526 local function normalize_params(params)
447 return { 527 return {
448 driver = assert(params.driver, "Configuration error: Both the SQL driver and the database need to be specified"); 528 driver = assert(params.driver,
449 database = assert(normalize_database(params.driver, params.database), "Configuration error: Both the SQL driver and the database need to be specified"); 529 "Configuration error: Both the SQL driver and the database need to be specified");
530 database = assert(normalize_database(params.driver, params.database),
531 "Configuration error: Both the SQL driver and the database need to be specified");
450 username = params.username; 532 username = params.username;
451 password = params.password; 533 password = params.password;
452 host = params.host; 534 host = params.host;
453 port = params.port; 535 port = params.port;
454 }; 536 };
459 local engines = module:shared("/*/sql/connections"); 541 local engines = module:shared("/*/sql/connections");
460 local params = normalize_params(module:get_option("sql", default_params)); 542 local params = normalize_params(module:get_option("sql", default_params));
461 engine = engines[sql.db2uri(params)]; 543 engine = engines[sql.db2uri(params)];
462 if not engine then 544 if not engine then
463 module:log("debug", "Creating new engine"); 545 module:log("debug", "Creating new engine");
464 engine = sql:create_engine(params, function (engine) 546 engine = sql:create_engine(params, function (engine) -- luacheck: ignore 431/engine
465 if module:get_option("sql_manage_tables", true) then 547 if module:get_option("sql_manage_tables", true) then
466 -- Automatically create table, ignore failure (table probably already exists) 548 -- Automatically create table, ignore failure (table probably already exists)
467 -- FIXME: we should check in information_schema, etc. 549 -- FIXME: we should check in information_schema, etc.
468 create_table(); 550 create_table(engine);
469 -- Check whether the table needs upgrading 551 -- Check whether the table needs upgrading
470 if upgrade_table(params, false) then 552 if upgrade_table(engine, params, false) then
471 module:log("error", "Old database format detected. Please run: prosodyctl mod_%s upgrade", module.name); 553 module:log("error", "Old database format detected. Please run: prosodyctl mod_%s upgrade", module.name);
472 return false, "database upgrade needed"; 554 return false, "database upgrade needed";
473 end 555 end
474 end 556 end
475 end); 557 end);
484 local prosodyctl = require "util.prosodyctl"; 566 local prosodyctl = require "util.prosodyctl";
485 local command = table.remove(arg, 1); 567 local command = table.remove(arg, 1);
486 if command == "upgrade" then 568 if command == "upgrade" then
487 -- We need to find every unique dburi in the config 569 -- We need to find every unique dburi in the config
488 local uris = {}; 570 local uris = {};
489 for host in pairs(prosody.hosts) do 571 for host in pairs(prosody.hosts) do -- luacheck: ignore 431/host
490 local params = normalize_params(config.get(host, "sql") or default_params); 572 local params = normalize_params(config.get(host, "sql") or default_params);
491 uris[sql.db2uri(params)] = params; 573 uris[sql.db2uri(params)] = params;
492 end 574 end
493 print("We will check and upgrade the following databases:\n"); 575 print("We will check and upgrade the following databases:\n");
494 for _, params in pairs(uris) do 576 for _, params in pairs(uris) do
502 end 584 end
503 -- Upgrade each one 585 -- Upgrade each one
504 for _, params in pairs(uris) do 586 for _, params in pairs(uris) do
505 print("Checking "..params.database.."..."); 587 print("Checking "..params.database.."...");
506 engine = sql:create_engine(params); 588 engine = sql:create_engine(params);
507 upgrade_table(params, true); 589 upgrade_table(engine, params, true);
508 end 590 end
509 print("All done!"); 591 print("All done!");
510 elseif command then 592 elseif command then
511 print("Unknown command: "..command); 593 print("Unknown command: "..command);
512 else 594 else