Software /
code /
prosody
Comparison
plugins/mod_storage_sql.lua @ 3729:b351fa47a743
mod_storage_sql: Initial commit of new SQL data driver.
author | Waqas Hussain <waqas20@gmail.com> |
---|---|
date | Sun, 12 Dec 2010 05:18:36 +0500 |
child | 3730:5aa586d1366e |
comparison
equal
deleted
inserted
replaced
3728:b1b8fe846d68 | 3729:b351fa47a743 |
---|---|
1 | |
2 --[[ | |
3 | |
4 DB Tables: | |
5 Prosody - key-value, map | |
6 | host | user | store | key | subkey | type | value | | |
7 ProsodyArchive - list | |
8 | host | user | store | key | time | stanzatype | jsonvalue | | |
9 | |
10 Mapping: | |
11 Roster - Prosody | |
12 | host | user | "roster" | "contactjid" | item-subkey | type | value | | |
13 | host | user | "roster" | NULL | NULL | "json" | roster[false] data | | |
14 Account - Prosody | |
15 | host | user | "accounts" | "username" | NULL | type | value | | |
16 | |
17 Offline - ProsodyArchive | |
18 | host | user | "offline" | "contactjid" | time | "message" | json|XML | | |
19 | |
20 ]] | |
21 | |
22 local type = type; | |
23 local tostring = tostring; | |
24 local tonumber = tonumber; | |
25 local pairs = pairs; | |
26 local next = next; | |
27 local setmetatable = setmetatable; | |
28 local json = { stringify = function(s) return require"util.serialzation".serialize(s) end, parse = require"util.serialization".deserialze }; | |
29 | |
30 local connection = ...; | |
31 local host,user,store = module.host; | |
32 | |
33 do -- process options to get a db connection | |
34 local DBI = require "DBI"; | |
35 | |
36 local params = module:get_option("sql"); | |
37 assert(params and params.driver and params.database, "invalid params"); | |
38 | |
39 prosody.unlock_globals(); | |
40 local dbh, err = DBI.Connect( | |
41 params.driver, params.database, | |
42 params.username, params.password, | |
43 params.host, params.port | |
44 ); | |
45 prosody.lock_globals(); | |
46 assert(dbh, err); | |
47 | |
48 dbh:autocommit(false); -- don't commit automatically | |
49 connection = dbh; | |
50 end | |
51 | |
52 local function serialize(value) | |
53 local t = type(value); | |
54 if t == "string" or t == "boolean" or t == "number" then | |
55 return t, tostring(value); | |
56 elseif t == "table" then | |
57 local value,err = json.stringify(value); | |
58 if value then return "json", value; end | |
59 return nil, err; | |
60 end | |
61 return nil, "Unhandled value type: "..t; | |
62 end | |
63 local function deserialize(t, value) | |
64 if t == "string" then return t; | |
65 elseif t == "boolean" then | |
66 if value == "true" then return true; | |
67 elseif value == "false" then return false; end | |
68 elseif t == "number" then return tonumber(value); | |
69 elseif value == "json" then | |
70 return json.parse(value); | |
71 end | |
72 end | |
73 | |
74 local function getsql(sql, ...) | |
75 -- do prepared statement stuff | |
76 local stmt, err = connection:prepare(sql); | |
77 if not stmt then return nil, err; end | |
78 -- run query | |
79 local ok, err = stmt:execute(host, user, store, ...); | |
80 if not ok then return nil, err; end | |
81 | |
82 return stmt; | |
83 end | |
84 local function setsql(sql, ...) | |
85 local stmt, err = getsql(sql, ...); | |
86 if not stmt then return stmt, err; end | |
87 return stmt:affected(); | |
88 end | |
89 local function transact(...) | |
90 -- ... | |
91 end | |
92 local function rollback(...) | |
93 connection:rollback(); -- FIXME check for rollback error? | |
94 return ...; | |
95 end | |
96 local function commit(...) | |
97 if not connection:commit() then return nil, "SQL commit failed"; end | |
98 return ...; | |
99 end | |
100 | |
101 local keyval_store = {}; | |
102 keyval_store.__index = keyval_store; | |
103 function keyval_store:get(username) | |
104 user,store = username,self.store; | |
105 local stmt, err = getsql("SELECT * FROM Prosody WHERE host=? AND user=? AND store=? AND subkey=NULL"); | |
106 if not stmt then return nil, err; end | |
107 | |
108 local haveany; | |
109 local result = {}; | |
110 for row in stmt:rows(true) do | |
111 haveany = true; | |
112 local k = row.key; | |
113 local v = deserialize(row.type, row.value); | |
114 if v then | |
115 if k then result[k] = v; elseif type(v) == "table" then | |
116 for a,b in pairs(v) do | |
117 result[a] = b; | |
118 end | |
119 end | |
120 end | |
121 end | |
122 return haveany and result or nil; | |
123 end | |
124 function keyval_store:set(username, data) | |
125 user,store = username,self.store; | |
126 -- start transaction | |
127 local affected, err = setsql("DELETE FROM Prosody WHERE host=? AND user=? AND store=? AND subkey=NULL"); | |
128 | |
129 if data and next(data) ~= nil then | |
130 local extradata = {}; | |
131 for key, value in pairs(data) do | |
132 if type(key) == "string" then | |
133 local t, value = serialize(value); | |
134 if not t then return rollback(t, value); end | |
135 local ok, err = setsql("INSERT INTO Prosody (host,user,store,key,type,value) VALUES (?,?,?,?,?,?)", key, t, value); | |
136 if not ok then return rollback(ok, err); end | |
137 else | |
138 extradata[key] = value; | |
139 end | |
140 end | |
141 if next(extradata) ~= nil then | |
142 local t, extradata = serialize(extradata); | |
143 if not t then return rollback(t, extradata); end | |
144 local ok, err = setsql("INSERT INTO Prosody (host,user,store,key,type,value) VALUES (?,?,?,?,?,?)", nil, t, extradata); | |
145 if not ok then return rollback(ok, err); end | |
146 end | |
147 end | |
148 return commit(true); | |
149 end | |
150 | |
151 local map_store = {}; | |
152 map_store.__index = map_store; | |
153 function map_store:get(username, key) | |
154 user,store = username,self.store; | |
155 local stmt, err = getsql("SELECT * FROM Prosody WHERE host=? AND user=? AND store=? AND key=?", key); | |
156 if not stmt then return nil, err; end | |
157 | |
158 local haveany; | |
159 local result = {}; | |
160 for row in stmt:rows(true) do | |
161 haveany = true; | |
162 local k = row.subkey; | |
163 local v = deserialize(row.type, row.value); | |
164 if v then | |
165 if k then result[k] = v; elseif type(v) == "table" then | |
166 for a,b in pairs(v) do | |
167 result[a] = b; | |
168 end | |
169 end | |
170 end | |
171 end | |
172 return haveany and result or nil; | |
173 end | |
174 function map_store:set(username, key, data) | |
175 user,store = username,self.store; | |
176 -- start transaction | |
177 local affected, err = setsql("DELETE FROM Prosody WHERE host=? AND user=? AND store=? AND key=?", key); | |
178 | |
179 if data and next(data) ~= nil then | |
180 local extradata = {}; | |
181 for subkey, value in pairs(data) do | |
182 if type(subkey) == "string" then | |
183 local t, value = serialize(value); | |
184 if not t then return rollback(t, value); end | |
185 local ok, err = setsql("INSERT INTO Prosody (host,user,store,key,subkey,type,value) VALUES (?,?,?,?,?,?)", key, subkey, t, value); | |
186 if not ok then return rollback(ok, err); end | |
187 else | |
188 extradata[subkey] = value; | |
189 end | |
190 end | |
191 if next(extradata) ~= nil then | |
192 local t, extradata = serialize(extradata); | |
193 if not t then return rollback(t, extradata); end | |
194 local ok, err = setsql("INSERT INTO Prosody (host,user,store,key,subkey,type,value) VALUES (?,?,?,?,?,?)", key, nil, t, extradata); | |
195 if not ok then return rollback(ok, err); end | |
196 end | |
197 end | |
198 return commit(true); | |
199 end | |
200 | |
201 local list_store = {}; | |
202 list_store.__index = list_store; | |
203 function list_store:scan(username, from, to, jid, typ) | |
204 user,store = username,self.store; | |
205 | |
206 local cols = {"from", "to", "jid", "typ"}; | |
207 local vals = { from , to , jid , typ }; | |
208 local stmt, err; | |
209 local query = "SELECT * FROM ProsodyArchive WHERE host=? AND user=? AND store=?"; | |
210 | |
211 query = query.." ORDER BY time"; | |
212 --local stmt, err = getsql("SELECT * FROM Prosody WHERE host=? AND user=? AND store=? AND key=?", key); | |
213 | |
214 return nil, "not-implemented" | |
215 end | |
216 | |
217 local driver = { name = "sql" }; | |
218 | |
219 function driver:open(store, typ) | |
220 if not typ then -- default key-value store | |
221 return setmetatable({ store = store }, keyval_store); | |
222 end | |
223 return nil, "unsupported-store"; | |
224 end | |
225 | |
226 module:add_item("data-driver", driver); |