Software /
code /
prosody
Changeset
4082:eeab15f8aa35
Merge 0.8->trunk
author | Matthew Wild <mwild1@gmail.com> |
---|---|
date | Fri, 07 Jan 2011 03:18:40 +0000 |
parents | 4078:05a58497a903 (current diff) 4081:ce02a56b268f (diff) |
children | 4088:0e5585910583 |
files | plugins/mod_storage_sql_ejabberd.lua plugins/storage/ejabberd_init.lib.lua plugins/storage/ejabberdstore.lib.lua |
diffstat | 3 files changed, 0 insertions(+), 674 deletions(-) [+] |
line wrap: on
line diff
--- a/plugins/mod_storage_sql_ejabberd.lua Thu Jan 06 14:46:59 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,232 +0,0 @@ - -local setmetatable = setmetatable; -local error = error; -local unpack = unpack; -local module = module; -local tostring = tostring; -local pairs, next = pairs, next; -local prosody = prosody; -local assert = assert; -local require = require; -local st = require "util.stanza"; -local DBI = require "DBI"; - --- connect to db -local params = module:get_option("sql_ejabberd") or error("No sql_ejabberd config option"); -local database; -do - module:log("debug", "Opening database: %s", "dbi:"..params.driver..":"..params.database); - prosody.unlock_globals(); - local dbh, err = DBI.Connect( - params.driver, params.database, - params.username, params.password, - params.host, params.port - ); - prosody.lock_globals(); - assert(dbh, err); - dbh:autocommit(true); - database = dbh; -end - --- initialize db -local ejabberd_init = module:require("ejabberd_init"); -ejabberd_init.init(database); - -local sqlcache = {}; -local function prepare(sql) - module:log("debug", "query: %s", sql); - local err; - local r = sqlcache[sql]; - if not r then - r, err = database:prepare(sql); - if not r then error("Unable to prepare SQL statement: "..err); end - sqlcache[sql] = r; - end - return r; -end - -local _parse_xml = module:require("xmlparse"); -local function parse_xml(str) - local s = _parse_xml(str); - if s and not s.gsub then - return st.preserialize(s); - end -end -local function unparse_xml(s) - return tostring(st.deserialize(s)); -end - - -local handlers = {}; - -handlers.accounts = { - get = function(self, user) - local select = self:query("select password from users where username=? and host=?", user, self.host); - local row = select and select:fetch(); - if row then return { password = row[1] }; end - end; - set = function(self, user, data) - if data and data.password then - return self:modify("update users set password=? where username=? and host=?", data.password, user, self.host) - or self:modify("insert into users (username, host, password) values (?, ?, ?)", user, self.host, data.password); - else - return self:modify("delete from users where username=? and host=?", user, self.host); - end - end; -}; -handlers.vcard = { - get = function(self, user) - local select = self:query("select vcard from vcard where username=? and host=?", user, self.host); - local row = select and select:fetch(); - if row then return parse_xml(row[1]); end - end; - set = function(self, user, data) - if data then - data = unparse_xml(data); - return self:modify("update vcard set vcard=? where username=? and host=?", data, user, self.host) - or self:modify("insert into vcard (username, host, vcard) values (?, ?, ?)", user, self.host, data); - else - return self:modify("delete from vcard where username=? and host=?", user, self.host); - end - end; -}; -handlers.private = { - get = function(self, user) - local select = self:query("select namespace,data from private_storage where username=? and host=?", user, self.host); - if select then - local data = {}; - for row in select:rows() do - data[row[1]] = parse_xml(row[2]); - end - return data; - end - end; - set = function(self, user, data) - if data then - self:modify("delete from private_storage where username=? and host=?", user, self.host); - for namespace,text in pairs(data) do - self:modify("insert into private_storage (username, host, namespace, data) values (?, ?, ?, ?)", user, self.host, namespace, unparse_xml(text)); - end - return true; - else - return self:modify("delete from private_storage where username=? and host=?", user, self.host); - end - end; - -- TODO map_set, map_get -}; -local subscription_map = { N = "none", B = "both", F = "from", T = "to" }; -local subscription_map_reverse = { none = "N", both = "B", from = "F", to = "T" }; -handlers.roster = { - get = function(self, user) - local select = self:query("select jid,nick,subscription,ask,server,subscribe,type from rosterusers where username=?", user); - if select then - local roster = { pending = {} }; - for row in select:rows() do - local jid,nick,subscription,ask,server,subscribe,typ = unpack(row); - local item = { groups = {} }; - if nick == "" then nick = nil; end - item.nick = nick; - item.subscription = subscription_map[subscription]; - if ask == "N" then ask = nil; - elseif ask == "O" then ask = "subscribe" - elseif ask == "I" then roster.pending[jid] = true; ask = nil; - elseif ask == "B" then roster.pending[jid] = true; ask = "subscribe"; - else module:log("debug", "bad roster_item.ask: %s", ask); ask = nil; end - item.ask = ask; - roster[jid] = item; - end - - select = self:query("select jid,grp from rostergroups where username=?", user); - if select then - for row in select:rows() do - local jid,grp = unpack(row); - if roster[jid] then roster[jid].groups[grp] = true; end - end - end - select = self:query("select version from roster_version where username=?", user); - local row = select and select:fetch(); - if row then - roster[false] = { version = row[1]; }; - end - return roster; - end - end; - set = function(self, user, data) - if data and next(data) ~= nil then - self:modify("delete from rosterusers where username=?", user); - self:modify("delete from rostergroups where username=?", user); - self:modify("delete from roster_version where username=?", user); - local done = {}; - local pending = data.pending or {}; - for jid,item in pairs(data) do - if jid and jid ~= "pending" then - local subscription = subscription_map_reverse[item.subscription]; - local ask; - if pending[jid] then - if item.ask then ask = "B"; else ask = "I"; end - else - if item.ask then ask = "O"; else ask = "N"; end - end - local r = self:modify("insert into rosterusers (username,jid,nick,subscription,ask,askmessage,server,subscribe) values (?, ?, ?, ?, ?, '', '', '')", user, jid, item.nick or "", subscription, ask); - if not r then module:log("debug", "--- :( %s", tostring(r)); end - done[jid] = true; - for group in pairs(item.groups) do - self:modify("insert into rostergroups (username,jid,grp) values (?, ?, ?)", user, jid, group); - end - end - end - for jid in pairs(pending) do - if not done[jid] then - self:modify("insert into rosterusers (username,jid,nick,subscription,ask,askmessage,server,subscribe) values (?, ?, ?, ?, ?. ''. ''. '')", user, jid, "", "N", "I"); - end - end - local version = data[false] and data[false].version; - if version then - self:modify("insert into roster_version (username,version) values (?, ?)", user, version); - end - return true; - else - self:modify("delete from rosterusers where username=?", user); - self:modify("delete from rostergroups where username=?", user); - self:modify("delete from roster_version where username=?", user); - end - end; -}; - ------------------------------ -local driver = {}; -driver.__index = driver; - -function driver:query(sql, ...) - local stmt,err = prepare(sql); - if not stmt then - module:log("error", "Failed to prepare SQL [[%s]], error: %s", sql, err); - return nil, err; - end - local ok, err = stmt:execute(...); - if not ok then - module:log("error", "Failed to execute SQL [[%s]], error: %s", sql, err); - return nil, err; - end - return stmt; -end -function driver:modify(sql, ...) - local stmt, err = self:query(sql, ...); - if stmt and stmt:affected() > 0 then return stmt; end - return nil, err; -end - -function driver:open(datastore, typ) - local instance = setmetatable({ host = module.host, datastore = datastore }, self); - local handler = handlers[datastore]; - if not handler then return nil; end - for key,val in pairs(handler) do - instance[key] = val; - end - if instance.init then instance:init(); end - return instance; -end - ------------------------------ - -module:add_item("data-driver", driver);
--- a/plugins/storage/ejabberd_init.lib.lua Thu Jan 06 14:46:59 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,252 +0,0 @@ - -local t_concat = table.concat; -local t_insert = table.insert; -local pairs = pairs; -local DBI = require "DBI"; - -local sqlite = true; -local q = {}; - -local function set(key, val) --- t_insert(q, "SET "..key.."="..val..";\n") -end -local function create_table(name, fields) - t_insert(q, "CREATE TABLE ".."IF NOT EXISTS "..name.." (\n"); - for _, field in pairs(fields) do - t_insert(q, "\t"); - field = t_concat(field, " "); - if sqlite then - if field:lower():match("^primary key *%(") then field = field:gsub("%(%d+%)", ""); end - end - t_insert(q, field); - if _ ~= #fields then t_insert(q, ",\n"); end - t_insert(q, "\n"); - end - if sqlite then - t_insert(q, ");\n"); - else - t_insert(q, ") CHARACTER SET utf8;\n"); - end -end -local function create_index(name, index) - --t_insert(q, "CREATE INDEX "..name.." ON "..index..";\n"); -end -local function create_unique_index(name, index) - --t_insert(q, "CREATE UNIQUE INDEX "..name.." ON "..index..";\n"); -end -local function insert(target, value) - t_insert(q, "INSERT INTO "..target.."\nVALUES "..value..";\n"); -end -local function foreign_key(name, fkey, fname, fcol) - t_insert(q, "ALTER TABLE `"..name.."` ADD FOREIGN KEY (`"..fkey.."`) REFERENCES `"..fname.."` (`"..fcol.."`) ON DELETE CASCADE;\n"); -end - -function build_query() - q = {}; - set('table_type', 'InnoDB'); - create_table('hosts', { - {'clusterid','integer','NOT','NULL'}; - {'host','varchar(250)','NOT','NULL','PRIMARY','KEY'}; - {'config','text','NOT','NULL'}; - }); - insert("hosts (clusterid, host, config)", "(1, 'localhost', '')"); - create_table('users', { - {'host','varchar(250)','NOT','NULL'}; - {'username','varchar(250)','NOT','NULL'}; - {'password','text','NOT','NULL'}; - {'created_at','timestamp','NOT','NULL','DEFAULT','CURRENT_TIMESTAMP'}; - {'PRIMARY','KEY','(host, username)'}; - }); - create_table('last', { - {'host','varchar(250)','NOT','NULL'}; - {'username','varchar(250)','NOT','NULL'}; - {'seconds','text','NOT','NULL'}; - {'state','text','NOT','NULL'}; - {'PRIMARY','KEY','(host, username)'}; - }); - create_table('rosterusers', { - {'host','varchar(250)','NOT','NULL'}; - {'username','varchar(250)','NOT','NULL'}; - {'jid','varchar(250)','NOT','NULL'}; - {'nick','text','NOT','NULL'}; - {'subscription','character(1)','NOT','NULL'}; - {'ask','character(1)','NOT','NULL'}; - {'askmessage','text','NOT','NULL'}; - {'server','character(1)','NOT','NULL'}; - {'subscribe','text','NOT','NULL'}; - {'type','text'}; - {'created_at','timestamp','NOT','NULL','DEFAULT','CURRENT_TIMESTAMP'}; - {'PRIMARY','KEY','(host(75), username(75), jid(75))'}; - }); - create_index('i_rosteru_username', 'rosterusers(username)'); - create_index('i_rosteru_jid', 'rosterusers(jid)'); - create_table('rostergroups', { - {'host','varchar(250)','NOT','NULL'}; - {'username','varchar(250)','NOT','NULL'}; - {'jid','varchar(250)','NOT','NULL'}; - {'grp','text','NOT','NULL'}; - {'PRIMARY','KEY','(host(75), username(75), jid(75))'}; - }); - --[[create_table('spool', { - {'host','varchar(250)','NOT','NULL'}; - {'username','varchar(250)','NOT','NULL'}; - {'xml','text','NOT','NULL'}; - {'seq','BIGINT','UNSIGNED','NOT','NULL','AUTO_INCREMENT','UNIQUE'}; - {'created_at','timestamp','NOT','NULL','DEFAULT','CURRENT_TIMESTAMP'}; - {'PRIMARY','KEY','(host, username, seq)'}; - });]] - create_table('vcard', { - {'host','varchar(250)','NOT','NULL'}; - {'username','varchar(250)','NOT','NULL'}; - {'vcard','text','NOT','NULL'}; - {'created_at','timestamp','NOT','NULL','DEFAULT','CURRENT_TIMESTAMP'}; - {'PRIMARY','KEY','(host, username)'}; - }); - create_table('vcard_search', { - {'host','varchar(250)','NOT','NULL'}; - {'username','varchar(250)','NOT','NULL'}; - {'lusername','varchar(250)','NOT','NULL'}; - {'fn','text','NOT','NULL'}; - {'lfn','varchar(250)','NOT','NULL'}; - {'family','text','NOT','NULL'}; - {'lfamily','varchar(250)','NOT','NULL'}; - {'given','text','NOT','NULL'}; - {'lgiven','varchar(250)','NOT','NULL'}; - {'middle','text','NOT','NULL'}; - {'lmiddle','varchar(250)','NOT','NULL'}; - {'nickname','text','NOT','NULL'}; - {'lnickname','varchar(250)','NOT','NULL'}; - {'bday','text','NOT','NULL'}; - {'lbday','varchar(250)','NOT','NULL'}; - {'ctry','text','NOT','NULL'}; - {'lctry','varchar(250)','NOT','NULL'}; - {'locality','text','NOT','NULL'}; - {'llocality','varchar(250)','NOT','NULL'}; - {'email','text','NOT','NULL'}; - {'lemail','varchar(250)','NOT','NULL'}; - {'orgname','text','NOT','NULL'}; - {'lorgname','varchar(250)','NOT','NULL'}; - {'orgunit','text','NOT','NULL'}; - {'lorgunit','varchar(250)','NOT','NULL'}; - {'PRIMARY','KEY','(host, lusername)'}; - }); - create_index('i_vcard_search_lfn ', 'vcard_search(lfn)'); - create_index('i_vcard_search_lfamily ', 'vcard_search(lfamily)'); - create_index('i_vcard_search_lgiven ', 'vcard_search(lgiven)'); - create_index('i_vcard_search_lmiddle ', 'vcard_search(lmiddle)'); - create_index('i_vcard_search_lnickname', 'vcard_search(lnickname)'); - create_index('i_vcard_search_lbday ', 'vcard_search(lbday)'); - create_index('i_vcard_search_lctry ', 'vcard_search(lctry)'); - create_index('i_vcard_search_llocality', 'vcard_search(llocality)'); - create_index('i_vcard_search_lemail ', 'vcard_search(lemail)'); - create_index('i_vcard_search_lorgname ', 'vcard_search(lorgname)'); - create_index('i_vcard_search_lorgunit ', 'vcard_search(lorgunit)'); - create_table('privacy_default_list', { - {'host','varchar(250)','NOT','NULL'}; - {'username','varchar(250)'}; - {'name','varchar(250)','NOT','NULL'}; - {'PRIMARY','KEY','(host, username)'}; - }); - --[[create_table('privacy_list', { - {'host','varchar(250)','NOT','NULL'}; - {'username','varchar(250)','NOT','NULL'}; - {'name','varchar(250)','NOT','NULL'}; - {'id','BIGINT','UNSIGNED','NOT','NULL','AUTO_INCREMENT','UNIQUE'}; - {'created_at','timestamp','NOT','NULL','DEFAULT','CURRENT_TIMESTAMP'}; - {'PRIMARY','KEY','(host, username, name)'}; - });]] - create_table('privacy_list_data', { - {'id','bigint'}; - {'t','character(1)','NOT','NULL'}; - {'value','text','NOT','NULL'}; - {'action','character(1)','NOT','NULL'}; - {'ord','NUMERIC','NOT','NULL'}; - {'match_all','boolean','NOT','NULL'}; - {'match_iq','boolean','NOT','NULL'}; - {'match_message','boolean','NOT','NULL'}; - {'match_presence_in','boolean','NOT','NULL'}; - {'match_presence_out','boolean','NOT','NULL'}; - }); - create_table('private_storage', { - {'host','varchar(250)','NOT','NULL'}; - {'username','varchar(250)','NOT','NULL'}; - {'namespace','varchar(250)','NOT','NULL'}; - {'data','text','NOT','NULL'}; - {'created_at','timestamp','NOT','NULL','DEFAULT','CURRENT_TIMESTAMP'}; - {'PRIMARY','KEY','(host(75), username(75), namespace(75))'}; - }); - create_index('i_private_storage_username USING BTREE', 'private_storage(username)'); - create_table('roster_version', { - {'username','varchar(250)','PRIMARY','KEY'}; - {'version','text','NOT','NULL'}; - }); - --[[create_table('pubsub_node', { - {'host','text'}; - {'node','text'}; - {'parent','text'}; - {'type','text'}; - {'nodeid','bigint','auto_increment','primary','key'}; - }); - create_index('i_pubsub_node_parent', 'pubsub_node(parent(120))'); - create_unique_index('i_pubsub_node_tuple', 'pubsub_node(host(20), node(120))'); - create_table('pubsub_node_option', { - {'nodeid','bigint'}; - {'name','text'}; - {'val','text'}; - }); - create_index('i_pubsub_node_option_nodeid', 'pubsub_node_option(nodeid)'); - foreign_key('pubsub_node_option', 'nodeid', 'pubsub_node', 'nodeid'); - create_table('pubsub_node_owner', { - {'nodeid','bigint'}; - {'owner','text'}; - }); - create_index('i_pubsub_node_owner_nodeid', 'pubsub_node_owner(nodeid)'); - foreign_key('pubsub_node_owner', 'nodeid', 'pubsub_node', 'nodeid'); - create_table('pubsub_state', { - {'nodeid','bigint'}; - {'jid','text'}; - {'affiliation','character(1)'}; - {'subscriptions','text'}; - {'stateid','bigint','auto_increment','primary','key'}; - }); - create_index('i_pubsub_state_jid', 'pubsub_state(jid(60))'); - create_unique_index('i_pubsub_state_tuple', 'pubsub_state(nodeid, jid(60))'); - foreign_key('pubsub_state', 'nodeid', 'pubsub_node', 'nodeid'); - create_table('pubsub_item', { - {'nodeid','bigint'}; - {'itemid','text'}; - {'publisher','text'}; - {'creation','text'}; - {'modification','text'}; - {'payload','text'}; - }); - create_index('i_pubsub_item_itemid', 'pubsub_item(itemid(36))'); - create_unique_index('i_pubsub_item_tuple', 'pubsub_item(nodeid, itemid(36))'); - foreign_key('pubsub_item', 'nodeid', 'pubsub_node', 'nodeid'); - create_table('pubsub_subscription_opt', { - {'subid','text'}; - {'opt_name','varchar(32)'}; - {'opt_value','text'}; - }); - create_unique_index('i_pubsub_subscription_opt', 'pubsub_subscription_opt(subid(32), opt_name(32))');]] - return t_concat(q); -end - -local function init(dbh) - local q = build_query(); - for statement in q:gmatch("[^;]*;") do - statement = statement:gsub("\n", ""):gsub("\t", " "); - if sqlite then - statement = statement:gsub("AUTO_INCREMENT", "AUTOINCREMENT"); - statement = statement:gsub("auto_increment", "autoincrement"); - end - local result, err = DBI.Do(dbh, statement); - if not result then - print("X", result, err); - print("Y", statement); - end - end -end - -local _M = { init = init }; -return _M;
--- a/plugins/storage/ejabberdstore.lib.lua Thu Jan 06 14:46:59 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,190 +0,0 @@ - -local handlers = {}; - -handlers.accounts = { - get = function(self, user) - local select = self:query("select password from users where username=?", user); - local row = select and select:fetch(); - if row then return { password = row[1] }; end - end; - set = function(self, user, data) - if data and data.password then - return self:modify("update users set password=? where username=?", data.password, user) - or self:modify("insert into users (username, password) values (?, ?)", user, data.password); - else - return self:modify("delete from users where username=?", user); - end - end; -}; -handlers.vcard = { - get = function(self, user) - local select = self:query("select vcard from vcard where username=?", user); - local row = select and select:fetch(); - if row then return parse_xml(row[1]); end - end; - set = function(self, user, data) - if data then - data = unparse_xml(data); - return self:modify("update vcard set vcard=? where username=?", data, user) - or self:modify("insert into vcard (username, vcard) values (?, ?)", user, data); - else - return self:modify("delete from vcard where username=?", user); - end - end; -}; -handlers.private = { - get = function(self, user) - local select = self:query("select namespace,data from private_storage where username=?", user); - if select then - local data = {}; - for row in select:rows() do - data[row[1]] = parse_xml(row[2]); - end - return data; - end - end; - set = function(self, user, data) - if data then - self:modify("delete from private_storage where username=?", user); - for namespace,text in pairs(data) do - self:modify("insert into private_storage (username, namespace, data) values (?, ?, ?)", user, namespace, unparse_xml(text)); - end - return true; - else - return self:modify("delete from private_storage where username=?", user); - end - end; - -- TODO map_set, map_get -}; -local subscription_map = { N = "none", B = "both", F = "from", T = "to" }; -local subscription_map_reverse = { none = "N", both = "B", from = "F", to = "T" }; -handlers.roster = { - get = function(self, user) - local select = self:query("select jid,nick,subscription,ask,server,subscribe,type from rosterusers where username=?", user); - if select then - local roster = { pending = {} }; - for row in select:rows() do - local jid,nick,subscription,ask,server,subscribe,typ = unpack(row); - local item = { groups = {} }; - if nick == "" then nick = nil; end - item.nick = nick; - item.subscription = subscription_map[subscription]; - if ask == "N" then ask = nil; - elseif ask == "O" then ask = "subscribe" - elseif ask == "I" then roster.pending[jid] = true; ask = nil; - elseif ask == "B" then roster.pending[jid] = true; ask = "subscribe"; - else module:log("debug", "bad roster_item.ask: %s", ask); ask = nil; end - item.ask = ask; - roster[jid] = item; - end - - select = self:query("select jid,grp from rostergroups where username=?", user); - if select then - for row in select:rows() do - local jid,grp = unpack(rows); - if roster[jid] then roster[jid].groups[grp] = true; end - end - end - select = self:query("select version from roster_version where username=?", user); - local row = select and select:fetch(); - if row then - roster[false] = { version = row[1]; }; - end - return roster; - end - end; - set = function(self, user, data) - if data and next(data) ~= nil then - self:modify("delete from rosterusers where username=?", user); - self:modify("delete from rostergroups where username=?", user); - self:modify("delete from roster_version where username=?", user); - local done = {}; - local pending = data.pending or {}; - for jid,item in pairs(data) do - if jid and jid ~= "pending" then - local subscription = subscription_map_reverse[item.subscription]; - local ask; - if pending[jid] then - if item.ask then ask = "B"; else ask = "I"; end - else - if item.ask then ask = "O"; else ask = "N"; end - end - local r = self:modify("insert into rosterusers (username,jid,nick,subscription,ask,askmessage,server,subscribe) values (?, ?, ?, ?, ?, '', '', '')", user, jid, item.nick or "", subscription, ask); - if not r then module:log("debug", "--- :( %s", tostring(r)); end - done[jid] = true; - for group in pairs(item.groups) do - self:modify("insert into rostergroups (username,jid,grp) values (?, ?, ?)", user, jid, group); - end - end - end - for jid in pairs(pending) do - if not done[jid] then - self:modify("insert into rosterusers (username,jid,nick,subscription,ask,askmessage,server,subscribe) values (?, ?, ?, ?, ?. ''. ''. '')", user, jid, "", "N", "I"); - end - end - local version = data[false] and data[false].version; - if version then - self:modify("insert into roster_version (username,version) values (?, ?)", user, version); - end - return true; - else - self:modify("delete from rosterusers where username=?", user); - self:modify("delete from rostergroups where username=?", user); - self:modify("delete from roster_version where username=?", user); - end - end; -}; - ------------------------------ -local driver = {}; -driver.__index = driver; - -function driver:prepare(sql) - module:log("debug", "query: %s", sql); - local err; - if not self.sqlcache then self.sqlcache = {}; end - local r = self.sqlcache[sql]; - if r then return r; end - r, err = self.database:prepare(sql); - if not r then error("Unable to prepare SQL statement: "..err); end - self.sqlcache[sql] = r; - return r; -end - -function driver:query(sql, ...) - local stmt = self:prepare(sql); - if stmt:execute(...) then return stmt; end -end -function driver:modify(sql, ...) - local stmt = self:query(sql, ...); - if stmt and stmt:affected() > 0 then return stmt; end -end - -function driver:open(host, datastore, typ) - local cache_key = host.." "..datastore; - if self.ds_cache[cache_key] then return self.ds_cache[cache_key]; end - local instance = setmetatable({}, self); - instance.host = host; - instance.datastore = datastore; - local handler = handlers[datastore]; - if not handler then return nil; end - for key,val in pairs(handler) do - instance[key] = val; - end - if instance.init then instance:init(); end - self.ds_cache[cache_key] = instance; - return instance; -end - ------------------------------ -local _M = {}; - -function _M.new(dbtype, dbname, ...) - local instance = setmetatable({}, driver); - instance.__index = instance; - instance.database = get_database(dbtype, dbname, ...); - instance.ds_cache = {}; - return instance; -end - -return _M;