Comparison

plugins/mod_storage_sql.lua @ 8073:7361412a9664

SQL: Use standard quotes for columns and other identifiers, rewrite to grave accents for MySQL only (fixes #885)
author Kim Alvefur <zash@zash.se>
date Mon, 10 Apr 2017 23:13:39 +0200
parent 8050:c0f81dea4662
child 8074:4b403f881176
child 8075:a4dd08fd64cf
comparison
equal deleted inserted replaced
8072:194409dcba22 8073:7361412a9664
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 local select_sql = [[ 60 local select_sql = [[
61 SELECT `key`,`type`,`value` 61 SELECT "key","type","value"
62 FROM `prosody` 62 FROM "prosody"
63 WHERE `host`=? AND `user`=? AND `store`=?; 63 WHERE "host"=? AND "user"=? AND "store"=?;
64 ]] 64 ]]
65 for row in engine:select(select_sql, host, user or "", store) do 65 for row in engine:select(select_sql, host, user or "", store) do
66 haveany = true; 66 haveany = true;
67 local k = row[1]; 67 local k = row[1];
68 local v = deserialize(row[2], row[3]); 68 local v = deserialize(row[2], row[3]);
78 return result; 78 return result;
79 end 79 end
80 end 80 end
81 local function keyval_store_set(data) 81 local function keyval_store_set(data)
82 local delete_sql = [[ 82 local delete_sql = [[
83 DELETE FROM `prosody` 83 DELETE FROM "prosody"
84 WHERE `host`=? AND `user`=? AND `store`=? 84 WHERE "host"=? AND "user"=? AND "store"=?
85 ]]; 85 ]];
86 engine:delete(delete_sql, host, user or "", store); 86 engine:delete(delete_sql, host, user or "", store);
87 87
88 local insert_sql = [[ 88 local insert_sql = [[
89 INSERT INTO `prosody` 89 INSERT INTO "prosody"
90 (`host`,`user`,`store`,`key`,`type`,`value`) 90 ("host","user","store","key","type","value")
91 VALUES (?,?,?,?,?,?); 91 VALUES (?,?,?,?,?,?);
92 ]] 92 ]]
93 if data and next(data) ~= nil then 93 if data and next(data) ~= nil then
94 local extradata = {}; 94 local extradata = {};
95 for key, value in pairs(data) do 95 for key, value in pairs(data) do
128 end); 128 end);
129 end 129 end
130 function keyval_store:users() 130 function keyval_store:users()
131 local ok, result = engine:transaction(function() 131 local ok, result = engine:transaction(function()
132 local select_sql = [[ 132 local select_sql = [[
133 SELECT DISTINCT `user` 133 SELECT DISTINCT "user"
134 FROM `prosody` 134 FROM "prosody"
135 WHERE `host`=? AND `store`=?; 135 WHERE "host"=? AND "store"=?;
136 ]]; 136 ]];
137 return engine:select(select_sql, host, self.store); 137 return engine:select(select_sql, host, self.store);
138 end); 138 end);
139 if not ok then return ok, result end 139 if not ok then return ok, result end
140 return iterator(result); 140 return iterator(result);
147 map_store.__index = map_store; 147 map_store.__index = map_store;
148 map_store.remove = {}; 148 map_store.remove = {};
149 function map_store:get(username, key) 149 function map_store:get(username, key)
150 local ok, result = engine:transaction(function() 150 local ok, result = engine:transaction(function()
151 local query = [[ 151 local query = [[
152 SELECT `type`, `value` 152 SELECT "type", "value"
153 FROM `prosody` 153 FROM "prosody"
154 WHERE `host`=? AND `user`=? AND `store`=? AND `key`=? 154 WHERE "host"=? AND "user"=? AND "store"=? AND "key"=?
155 LIMIT 1 155 LIMIT 1
156 ]]; 156 ]];
157 local data; 157 local data;
158 if type(key) == "string" and key ~= "" then 158 if type(key) == "string" and key ~= "" then
159 for row in engine:select(query, host, username or "", self.store, key) do 159 for row in engine:select(query, host, username or "", self.store, key) do
175 return self:set_keys(username, { [key] = data }); 175 return self:set_keys(username, { [key] = data });
176 end 176 end
177 function map_store:set_keys(username, keydatas) 177 function map_store:set_keys(username, keydatas)
178 local ok, result = engine:transaction(function() 178 local ok, result = engine:transaction(function()
179 local delete_sql = [[ 179 local delete_sql = [[
180 DELETE FROM `prosody` 180 DELETE FROM "prosody"
181 WHERE `host`=? AND `user`=? AND `store`=? AND `key`=?; 181 WHERE "host"=? AND "user"=? AND "store"=? AND "key"=?;
182 ]]; 182 ]];
183 local insert_sql = [[ 183 local insert_sql = [[
184 INSERT INTO `prosody` 184 INSERT INTO "prosody"
185 (`host`,`user`,`store`,`key`,`type`,`value`) 185 ("host","user","store","key","type","value")
186 VALUES (?,?,?,?,?,?); 186 VALUES (?,?,?,?,?,?);
187 ]]; 187 ]];
188 local select_extradata_sql = [[ 188 local select_extradata_sql = [[
189 SELECT `type`, `value` 189 SELECT "type", "value"
190 FROM `prosody` 190 FROM "prosody"
191 WHERE `host`=? AND `user`=? AND `store`=? AND `key`=? 191 WHERE "host"=? AND "user"=? AND "store"=? AND "key"=?
192 LIMIT 1; 192 LIMIT 1;
193 ]]; 193 ]];
194 for key, data in pairs(keydatas) do 194 for key, data in pairs(keydatas) do
195 if type(key) == "string" and key ~= "" then 195 if type(key) == "string" and key ~= "" then
196 engine:delete(delete_sql, 196 engine:delete(delete_sql,
225 local user,store = username,self.store; 225 local user,store = username,self.store;
226 when = when or os.time(); 226 when = when or os.time();
227 with = with or ""; 227 with = with or "";
228 local ok, ret = engine:transaction(function() 228 local ok, ret = engine:transaction(function()
229 local delete_sql = [[ 229 local delete_sql = [[
230 DELETE FROM `prosodyarchive` 230 DELETE FROM "prosodyarchive"
231 WHERE `host`=? AND `user`=? AND `store`=? AND `key`=?; 231 WHERE "host"=? AND "user"=? AND "store"=? AND "key"=?;
232 ]]; 232 ]];
233 local insert_sql = [[ 233 local insert_sql = [[
234 INSERT INTO `prosodyarchive` 234 INSERT INTO "prosodyarchive"
235 (`host`, `user`, `store`, `when`, `with`, `key`, `type`, `value`) 235 ("host", "user", "store", "when", "with", "key", "type", "value")
236 VALUES (?,?,?,?,?,?,?,?); 236 VALUES (?,?,?,?,?,?,?,?);
237 ]]; 237 ]];
238 if key then 238 if key then
239 engine:delete(delete_sql, host, user or "", store, key); 239 engine:delete(delete_sql, host, user or "", store, key);
240 else 240 else
251 -- Helpers for building the WHERE clause 251 -- Helpers for building the WHERE clause
252 local function archive_where(query, args, where) 252 local function archive_where(query, args, where)
253 -- Time range, inclusive 253 -- Time range, inclusive
254 if query.start then 254 if query.start then
255 args[#args+1] = query.start 255 args[#args+1] = query.start
256 where[#where+1] = "`when` >= ?" 256 where[#where+1] = "\"when\" >= ?"
257 end 257 end
258 258
259 if query["end"] then 259 if query["end"] then
260 args[#args+1] = query["end"]; 260 args[#args+1] = query["end"];
261 if query.start then 261 if query.start then
262 where[#where] = "`when` BETWEEN ? AND ?" -- is this inclusive? 262 where[#where] = "\"when\" BETWEEN ? AND ?" -- is this inclusive?
263 else 263 else
264 where[#where+1] = "`when` <= ?" 264 where[#where+1] = "\"when\" <= ?"
265 end 265 end
266 end 266 end
267 267
268 -- Related name 268 -- Related name
269 if query.with then 269 if query.with then
270 where[#where+1] = "`with` = ?"; 270 where[#where+1] = "\"with\" = ?";
271 args[#args+1] = query.with 271 args[#args+1] = query.with
272 end 272 end
273 273
274 -- Unique id 274 -- Unique id
275 if query.key then 275 if query.key then
276 where[#where+1] = "`key` = ?"; 276 where[#where+1] = "\"key\" = ?";
277 args[#args+1] = query.key 277 args[#args+1] = query.key
278 end 278 end
279 end 279 end
280 local function archive_where_id_range(query, args, where) 280 local function archive_where_id_range(query, args, where)
281 local args_len = #args 281 local args_len = #args
282 -- Before or after specific item, exclusive 282 -- Before or after specific item, exclusive
283 if query.after then -- keys better be unique! 283 if query.after then -- keys better be unique!
284 where[#where+1] = [[ 284 where[#where+1] = [[
285 `sort_id` > COALESCE( 285 "sort_id" > COALESCE(
286 ( 286 (
287 SELECT `sort_id` 287 SELECT "sort_id"
288 FROM `prosodyarchive` 288 FROM "prosodyarchive"
289 WHERE `key` = ? AND `host` = ? AND `user` = ? AND `store` = ? 289 WHERE "key" = ? AND "host" = ? AND "user" = ? AND "store" = ?
290 LIMIT 1 290 LIMIT 1
291 ), 0) 291 ), 0)
292 ]]; 292 ]];
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]; 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];
294 args_len = args_len + 4 294 args_len = args_len + 4
295 end 295 end
296 if query.before then 296 if query.before then
297 where[#where+1] = [[ 297 where[#where+1] = [[
298 `sort_id` < COALESCE( 298 "sort_id" < COALESCE(
299 ( 299 (
300 SELECT `sort_id` 300 SELECT "sort_id"
301 FROM `prosodyarchive` 301 FROM "prosodyarchive"
302 WHERE `key` = ? AND `host` = ? AND `user` = ? AND `store` = ? 302 WHERE "key" = ? AND "host" = ? AND "user" = ? AND "store" = ?
303 LIMIT 1 303 LIMIT 1
304 ), 304 ),
305 ( 305 (
306 SELECT MAX(`sort_id`)+1 306 SELECT MAX("sort_id")+1
307 FROM `prosodyarchive` 307 FROM "prosodyarchive"
308 ) 308 )
309 ) 309 )
310 ]] 310 ]]
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]; 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];
312 end 312 end
316 query = query or {}; 316 query = query or {};
317 local user,store = username,self.store; 317 local user,store = username,self.store;
318 local total; 318 local total;
319 local ok, result = engine:transaction(function() 319 local ok, result = engine:transaction(function()
320 local sql_query = [[ 320 local sql_query = [[
321 SELECT `key`, `type`, `value`, `when`, `with` 321 SELECT "key", "type", "value", "when", "with"
322 FROM `prosodyarchive` 322 FROM "prosodyarchive"
323 WHERE %s 323 WHERE %s
324 ORDER BY `sort_id` %s%s; 324 ORDER BY "sort_id" %s%s;
325 ]]; 325 ]];
326 local args = { host, user or "", store, }; 326 local args = { host, user or "", store, };
327 local where = { "`host` = ?", "`user` = ?", "`store` = ?", }; 327 local where = { "\"host\" = ?", "\"user\" = ?", "\"store\" = ?", };
328 328
329 archive_where(query, args, where); 329 archive_where(query, args, where);
330 330
331 -- Total matching 331 -- Total matching
332 if query.total then 332 if query.total then
333 local stats = engine:select("SELECT COUNT(*) FROM `prosodyarchive` WHERE " 333 local stats = engine:select("SELECT COUNT(*) FROM \"prosodyarchive\" WHERE "
334 .. t_concat(where, " AND "), unpack(args)); 334 .. t_concat(where, " AND "), unpack(args));
335 if stats then 335 if stats then
336 for row in stats do 336 for row in stats do
337 total = row[1]; 337 total = row[1];
338 end 338 end
363 363
364 function archive_store:delete(username, query) 364 function archive_store:delete(username, query)
365 query = query or {}; 365 query = query or {};
366 local user,store = username,self.store; 366 local user,store = username,self.store;
367 local ok, stmt = engine:transaction(function() 367 local ok, stmt = engine:transaction(function()
368 local sql_query = "DELETE FROM `prosodyarchive` WHERE %s;"; 368 local sql_query = "DELETE FROM \"prosodyarchive\" WHERE %s;";
369 local args = { host, user or "", store, }; 369 local args = { host, user or "", store, };
370 local where = { "`host` = ?", "`user` = ?", "`store` = ?", }; 370 local where = { "\"host\" = ?", "\"user\" = ?", "\"store\" = ?", };
371 if user == true then 371 if user == true then
372 table.remove(args, 2); 372 table.remove(args, 2);
373 table.remove(where, 2); 373 table.remove(where, 2);
374 end 374 end
375 archive_where(query, args, where); 375 archive_where(query, args, where);
399 end 399 end
400 return nil, "unsupported-store"; 400 return nil, "unsupported-store";
401 end 401 end
402 402
403 function driver:stores(username) 403 function driver:stores(username)
404 local query = "SELECT DISTINCT `store` FROM `prosody` WHERE `host`=? AND `user`" .. 404 local query = "SELECT DISTINCT \"store\" FROM \"prosody\" WHERE \"host\"=? AND \"user\"" ..
405 (username == true and "!=?" or "=?"); 405 (username == true and "!=?" or "=?");
406 if username == true or not username then 406 if username == true or not username then
407 username = ""; 407 username = "";
408 end 408 end
409 local ok, result = engine:transaction(function() 409 local ok, result = engine:transaction(function()
413 return iterator(result); 413 return iterator(result);
414 end 414 end
415 415
416 function driver:purge(username) 416 function driver:purge(username)
417 return engine:transaction(function() 417 return engine:transaction(function()
418 local stmt,err = engine:delete("DELETE FROM `prosody` WHERE `host`=? AND `user`=?", host, username); 418 local stmt,err = engine:delete("DELETE FROM \"prosody\" WHERE \"host\"=? AND \"user\"=?", host, username);
419 return true, err; 419 return true, err;
420 end); 420 end);
421 end 421 end
422 422
423 --- Initialization 423 --- Initialization
465 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'");
466 if result:rowcount() > 0 then 466 if result:rowcount() > 0 then
467 changes = true; 467 changes = true;
468 if apply_changes then 468 if apply_changes then
469 module:log("info", "Upgrading database schema..."); 469 module:log("info", "Upgrading database schema...");
470 engine:execute("ALTER TABLE prosody MODIFY COLUMN `value` MEDIUMTEXT"); 470 engine:execute("ALTER TABLE prosody MODIFY COLUMN \"value\" MEDIUMTEXT");
471 module:log("info", "Database table automatically upgraded"); 471 module:log("info", "Database table automatically upgraded");
472 end 472 end
473 end 473 end
474 return true; 474 return true;
475 end); 475 end);
480 return false; 480 return false;
481 end 481 end
482 482
483 -- 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
484 local check_encoding_query = [[ 484 local check_encoding_query = [[
485 SELECT `COLUMN_NAME`,`COLUMN_TYPE`,`TABLE_NAME` 485 SELECT "COLUMN_NAME","COLUMN_TYPE","TABLE_NAME"
486 FROM `information_schema`.`columns` 486 FROM "information_schema"."columns"
487 WHERE `TABLE_NAME` LIKE 'prosody%%' AND ( `CHARACTER_SET_NAME`!='%s' OR `COLLATION_NAME`!='%s_bin' ); 487 WHERE "TABLE_NAME" LIKE 'prosody%%' AND ( "CHARACTER_SET_NAME"!='%s' OR "COLLATION_NAME"!='%s_bin' );
488 ]]; 488 ]];
489 check_encoding_query = check_encoding_query:format(engine.charset, engine.charset); 489 check_encoding_query = check_encoding_query:format(engine.charset, engine.charset);
490 -- FIXME Is it ok to ignore the return values from this? 490 -- FIXME Is it ok to ignore the return values from this?
491 engine:transaction(function() 491 engine:transaction(function()
492 local result = engine:execute(check_encoding_query); 492 local result = engine:execute(check_encoding_query);
493 local n_bad_columns = result:rowcount(); 493 local n_bad_columns = result:rowcount();
494 if n_bad_columns > 0 then 494 if n_bad_columns > 0 then
495 changes = true; 495 changes = true;
496 if apply_changes then 496 if apply_changes then
497 module:log("warn", "Found %d columns in prosody table requiring encoding change, updating now...", n_bad_columns); 497 module:log("warn", "Found %d columns in prosody table requiring encoding change, updating now...", n_bad_columns);
498 local fix_column_query1 = "ALTER TABLE `%s` CHANGE `%s` `%s` BLOB;"; 498 local fix_column_query1 = "ALTER TABLE \"%s\" CHANGE \"%s\" \"%s\" BLOB;";
499 local fix_column_query2 = "ALTER TABLE `%s` CHANGE `%s` `%s` %s CHARACTER SET '%s' COLLATE '%s_bin';"; 499 local fix_column_query2 = "ALTER TABLE \"%s\" CHANGE \"%s\" \"%s\" %s CHARACTER SET '%s' COLLATE '%s_bin';";
500 for row in result:rows() do 500 for row in result:rows() do
501 local column_name, column_type, table_name = unpack(row); 501 local column_name, column_type, table_name = unpack(row);
502 module:log("debug", "Fixing column %s in table %s", column_name, table_name); 502 module:log("debug", "Fixing column %s in table %s", column_name, table_name);
503 engine:execute(fix_column_query1:format(table_name, column_name, column_name)); 503 engine:execute(fix_column_query1:format(table_name, column_name, column_name));
504 engine:execute(fix_column_query2:format(table_name, column_name, column_name, column_type, engine.charset, engine.charset)); 504 engine:execute(fix_column_query2:format(table_name, column_name, column_name, column_type, engine.charset, engine.charset));