Software /
code /
prosody
Comparison
plugins/mod_storage_sql.lua @ 4105:08560575762f
mod_storage_sql: Reconnect in all cases, and rollback on error.
author | Waqas Hussain <waqas20@gmail.com> |
---|---|
date | Mon, 10 Jan 2011 21:07:59 +0500 |
parent | 4101:06778bc27d53 |
child | 4109:d26db1f936f8 |
comparison
equal
deleted
inserted
replaced
4103:c994524ef953 | 4105:08560575762f |
---|---|
26 local next = next; | 26 local next = next; |
27 local setmetatable = setmetatable; | 27 local setmetatable = setmetatable; |
28 local xpcall = xpcall; | 28 local xpcall = xpcall; |
29 local json = require "util.json"; | 29 local json = require "util.json"; |
30 | 30 |
31 local DBI; | |
31 local connection; | 32 local connection; |
32 local host,user,store = module.host; | 33 local host,user,store = module.host; |
33 local params = module:get_option("sql"); | 34 local params = module:get_option("sql"); |
34 | 35 |
35 local resolve_relative_path = require "core.configmanager".resolve_relative_path; | 36 local resolve_relative_path = require "core.configmanager".resolve_relative_path; |
36 | 37 |
38 local function test_connection() | |
39 if not connection then return nil; end | |
40 if connection:ping() then | |
41 return true; | |
42 else | |
43 module:log("debug", "Database connection closed"); | |
44 connection = nil; | |
45 end | |
46 end | |
47 local function connect() | |
48 if not test_connection() then | |
49 prosody.unlock_globals(); | |
50 local dbh, err = DBI.Connect( | |
51 params.driver, params.database, | |
52 params.username, params.password, | |
53 params.host, params.port | |
54 ); | |
55 prosody.lock_globals(); | |
56 if not dbh then | |
57 module:log("debug", "Database connection failed: %s", tostring(err)); | |
58 return nil, err; | |
59 end | |
60 module:log("debug", "Successfully connected to database"); | |
61 dbh:autocommit(false); -- don't commit automatically | |
62 connection = dbh; | |
63 return connection; | |
64 end | |
65 end | |
66 | |
37 do -- process options to get a db connection | 67 do -- process options to get a db connection |
38 local DBI = require "DBI"; | 68 DBI = require "DBI"; |
39 | 69 |
40 params = params or { driver = "SQLite3" }; | 70 params = params or { driver = "SQLite3" }; |
41 | 71 |
42 if params.driver == "SQLite3" then | 72 if params.driver == "SQLite3" then |
43 params.database = resolve_relative_path(prosody.paths.data or ".", params.database or "prosody.sqlite"); | 73 params.database = resolve_relative_path(prosody.paths.data or ".", params.database or "prosody.sqlite"); |
44 end | 74 end |
45 | 75 |
46 assert(params.driver and params.database, "Both the SQL driver and the database need to be specified"); | 76 assert(params.driver and params.database, "Both the SQL driver and the database need to be specified"); |
47 | 77 |
48 prosody.unlock_globals(); | 78 assert(connect()); |
49 local dbh, err = DBI.Connect( | |
50 params.driver, params.database, | |
51 params.username, params.password, | |
52 params.host, params.port | |
53 ); | |
54 prosody.lock_globals(); | |
55 assert(dbh, err); | |
56 | |
57 dbh:autocommit(false); -- don't commit automatically | |
58 connection = dbh; | |
59 | 79 |
60 -- Automatically create table, ignore failure (table probably already exists) | 80 -- Automatically create table, ignore failure (table probably already exists) |
61 local create_sql = "CREATE TABLE `prosody` (`host` TEXT, `user` TEXT, `store` TEXT, `key` TEXT, `type` TEXT, `value` TEXT);"; | 81 local create_sql = "CREATE TABLE `prosody` (`host` TEXT, `user` TEXT, `store` TEXT, `key` TEXT, `type` TEXT, `value` TEXT);"; |
62 if params.driver == "PostgreSQL" then | 82 if params.driver == "PostgreSQL" then |
63 create_sql = create_sql:gsub("`", "\""); | 83 create_sql = create_sql:gsub("`", "\""); |
99 if params.driver == "PostgreSQL" then | 119 if params.driver == "PostgreSQL" then |
100 sql = sql:gsub("`", "\""); | 120 sql = sql:gsub("`", "\""); |
101 end | 121 end |
102 -- do prepared statement stuff | 122 -- do prepared statement stuff |
103 local stmt, err = connection:prepare(sql); | 123 local stmt, err = connection:prepare(sql); |
124 if not stmt and not test_connection() then error("connection failed"); end | |
104 if not stmt then module:log("error", "QUERY FAILED: %s %s", err, debug.traceback()); return nil, err; end | 125 if not stmt then module:log("error", "QUERY FAILED: %s %s", err, debug.traceback()); return nil, err; end |
105 -- run query | 126 -- run query |
106 local ok, err = stmt:execute(host or "", user or "", store or "", ...); | 127 local ok, err = stmt:execute(host or "", user or "", store or "", ...); |
128 if not ok and not test_connection() then error("connection failed"); end | |
107 if not ok then return nil, err; end | 129 if not ok then return nil, err; end |
108 | 130 |
109 return stmt; | 131 return stmt; |
110 end | 132 end |
111 local function setsql(sql, ...) | 133 local function setsql(sql, ...) |
115 end | 137 end |
116 local function transact(...) | 138 local function transact(...) |
117 -- ... | 139 -- ... |
118 end | 140 end |
119 local function rollback(...) | 141 local function rollback(...) |
120 connection:rollback(); -- FIXME check for rollback error? | 142 if connection then connection:rollback(); end -- FIXME check for rollback error? |
121 return ...; | 143 return ...; |
122 end | 144 end |
123 local function commit(...) | 145 local function commit(...) |
124 if not connection:commit() then return nil, "SQL commit failed"; end | 146 if not connection:commit() then return nil, "SQL commit failed"; end |
125 return ...; | 147 return ...; |
126 end | 148 end |
127 | 149 |
128 local function keyval_store_get() | 150 local function keyval_store_get() |
129 local stmt, err = getsql("SELECT * FROM `prosody` WHERE `host`=? AND `user`=? AND `store`=?"); | 151 local stmt, err = getsql("SELECT * FROM `prosody` WHERE `host`=? AND `user`=? AND `store`=?"); |
130 if not stmt then return nil, err; end | 152 if not stmt then return rollback(nil, err); end |
131 | 153 |
132 local haveany; | 154 local haveany; |
133 local result = {}; | 155 local result = {}; |
134 for row in stmt:rows(true) do | 156 for row in stmt:rows(true) do |
135 haveany = true; | 157 haveany = true; |
145 end | 167 end |
146 return commit(haveany and result or nil); | 168 return commit(haveany and result or nil); |
147 end | 169 end |
148 local function keyval_store_set(data) | 170 local function keyval_store_set(data) |
149 local affected, err = setsql("DELETE FROM `prosody` WHERE `host`=? AND `user`=? AND `store`=?"); | 171 local affected, err = setsql("DELETE FROM `prosody` WHERE `host`=? AND `user`=? AND `store`=?"); |
172 if not affected then return rollback(affected, err); end | |
150 | 173 |
151 if data and next(data) ~= nil then | 174 if data and next(data) ~= nil then |
152 local extradata = {}; | 175 local extradata = {}; |
153 for key, value in pairs(data) do | 176 for key, value in pairs(data) do |
154 if type(key) == "string" and key ~= "" then | 177 if type(key) == "string" and key ~= "" then |
172 | 195 |
173 local keyval_store = {}; | 196 local keyval_store = {}; |
174 keyval_store.__index = keyval_store; | 197 keyval_store.__index = keyval_store; |
175 function keyval_store:get(username) | 198 function keyval_store:get(username) |
176 user,store = username,self.store; | 199 user,store = username,self.store; |
200 if not connection and not connect() then return nil, "Unable to connect to database"; end | |
177 local success, ret, err = xpcall(keyval_store_get, debug.traceback); | 201 local success, ret, err = xpcall(keyval_store_get, debug.traceback); |
202 if not connection and connect() then | |
203 success, ret, err = xpcall(keyval_store_get, debug.traceback); | |
204 end | |
178 if success then return ret, err; else return rollback(nil, ret); end | 205 if success then return ret, err; else return rollback(nil, ret); end |
179 end | 206 end |
180 function keyval_store:set(username, data) | 207 function keyval_store:set(username, data) |
181 user,store = username,self.store; | 208 user,store = username,self.store; |
209 if not connection and not connect() then return nil, "Unable to connect to database"; end | |
182 local success, ret, err = xpcall(function() return keyval_store_set(data); end, debug.traceback); | 210 local success, ret, err = xpcall(function() return keyval_store_set(data); end, debug.traceback); |
211 if not connection and connect() then | |
212 success, ret, err = xpcall(function() return keyval_store_set(data); end, debug.traceback); | |
213 end | |
183 if success then return ret, err; else return rollback(nil, ret); end | 214 if success then return ret, err; else return rollback(nil, ret); end |
184 end | 215 end |
185 | 216 |
186 local function map_store_get(key) | 217 local function map_store_get(key) |
187 local stmt, err = getsql("SELECT * FROM `prosody` WHERE `host`=? AND `user`=? AND `store`=? AND `key`=?", key or ""); | 218 local stmt, err = getsql("SELECT * FROM `prosody` WHERE `host`=? AND `user`=? AND `store`=? AND `key`=?", key or ""); |
188 if not stmt then return nil, err; end | 219 if not stmt then return rollback(nil, err); end |
189 | 220 |
190 local haveany; | 221 local haveany; |
191 local result = {}; | 222 local result = {}; |
192 for row in stmt:rows(true) do | 223 for row in stmt:rows(true) do |
193 haveany = true; | 224 haveany = true; |
203 end | 234 end |
204 return commit(haveany and result[key] or nil); | 235 return commit(haveany and result[key] or nil); |
205 end | 236 end |
206 local function map_store_set(key, data) | 237 local function map_store_set(key, data) |
207 local affected, err = setsql("DELETE FROM `prosody` WHERE `host`=? AND `user`=? AND `store`=? AND `key`=?", key or ""); | 238 local affected, err = setsql("DELETE FROM `prosody` WHERE `host`=? AND `user`=? AND `store`=? AND `key`=?", key or ""); |
239 if not affected then return rollback(affected, err); end | |
208 | 240 |
209 if data and next(data) ~= nil then | 241 if data and next(data) ~= nil then |
210 if type(key) == "string" and key ~= "" then | 242 if type(key) == "string" and key ~= "" then |
211 local t, value = serialize(data); | 243 local t, value = serialize(data); |
212 if not t then return rollback(t, value); end | 244 if not t then return rollback(t, value); end |