Software / code / prosody
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)); |