Created
December 14, 2010 11:03
-
-
Save JakSprats/740275 to your computer and use it in GitHub Desktop.
redis.lua
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package.path = package.path .. ";redis-lua/src/?.lua;src/?.lua" | |
require "redis" | |
local redis = Redis.connect('127.0.0.1', 6379); | |
-- CREATE_TABLE CREATE_TABLE CREATE_TABLE CREATE_TABLE CREATE_TABLE | |
-- CREATE_TABLE CREATE_TABLE CREATE_TABLE CREATE_TABLE CREATE_TABLE | |
local t = redis:drop_table('logical_test_fk'); | |
print (t); | |
local t = redis:create_table('logical_test_fk','id INT, fk INT, count INT'); | |
print (t); | |
local t = redis:create_index('ind_ltestfk', 'logical_test_fk','fk'); | |
print (t); | |
-- DESC DESC DESC DESC DESC DESC DESC DESC DESC DESC DESC | |
-- DESC DESC DESC DESC DESC DESC DESC DESC DESC DESC DESC | |
local t = redis:desc('logical_test_fk'); | |
for i,v in ipairs(t) do print(i,v); end | |
-- INSERT INSERT INSERT INSERT INSERT INSERT INSERT INSERT | |
-- INSERT INSERT INSERT INSERT INSERT INSERT INSERT INSERT | |
print ('NINE INSERTS'); | |
local t = redis:insert('logical_test_fk', '1,1,11'); | |
print (t); | |
local t = redis:insert('logical_test_fk', '2,1,11'); | |
print (t); | |
local t = redis:insert('logical_test_fk', '3,2,22'); | |
print (t); | |
local t = redis:insert('logical_test_fk', '4,2,22'); | |
print (t); | |
local t = redis:insert('logical_test_fk', '5,3,33'); | |
print (t); | |
local t = redis:insert('logical_test_fk', '6,3,33'); | |
print (t); | |
local t = redis:insert('logical_test_fk', '7,4,44'); | |
print (t); | |
local t = redis:insert('logical_test_fk', '8,4,44'); | |
print (t); | |
local t = redis:insert_return_size('logical_test_fk', '9,5,55'); | |
print (t); | |
-- SELECT SELECT SELECT SELECT SELECT SELECT SELECT SELECT | |
-- SELECT SELECT SELECT SELECT SELECT SELECT SELECT SELECT | |
local t = redis:select(0); | |
print (t); | |
print ('SELECT * FROM logical_test_fk WHERE id = 8'); | |
local t = redis:select('*', 'logical_test_fk', 'id = 8'); | |
for i,v in ipairs(t) do print(i,v); end | |
print ('SELECT * FROM logical_test_fk WHERE fk = 4'); | |
local t = redis:select('*', 'logical_test_fk', 'fk = 4'); | |
for i,v in ipairs(t) do print(i,v); end | |
print ('SCANSELECT * FROM logical_test_fk'); | |
local t = redis:scanselect('*', 'logical_test_fk'); | |
for i,v in ipairs(t) do print(i,v); end | |
print ('SCANSELECT * FROM logical_test_fk WHERE count = 33'); | |
local t = redis:scanselect('*', 'logical_test_fk', 'count = 33'); | |
for i,v in ipairs(t) do print(i,v); end | |
-- UPDATE UPDATE UPDATE UPDATE UPDATE UPDATE UPDATE UPDATE | |
-- UPDATE UPDATE UPDATE UPDATE UPDATE UPDATE UPDATE UPDATE | |
print ('UPDATE logical_test_fk SET count = 99 WHERE fk=4') | |
local t = redis:update('logical_test_fk', 'count = 99', 'fk=4'); | |
print (t); | |
print ('SCANSELECT * FROM logical_test_fk'); | |
local t = redis:scanselect('*', 'logical_test_fk'); | |
for i,v in ipairs(t) do print(i,v); end | |
-- DELETE DELETE DELETE DELETE DELETE DELETE DELETE DELETE | |
-- DELETE DELETE DELETE DELETE DELETE DELETE DELETE DELETE | |
print ('DELETE FROM logical_test_fk WHERE fk=5') | |
local t = redis:delete('logical_test_fk', 'fk=5'); | |
print (t); | |
print ('SCANSELECT * FROM logical_test_fk'); | |
local t = redis:scanselect('*', 'logical_test_fk'); | |
for i,v in ipairs(t) do print(i,v); end | |
-- DUMP DUMP DUMP DUMP DUMP DUMP DUMP DUMP DUMP DUMP DUMP | |
-- DUMP DUMP DUMP DUMP DUMP DUMP DUMP DUMP DUMP DUMP DUMP | |
print ('DUMP logical_test_fk'); | |
local t = redis:dump('logical_test_fk'); | |
for i,v in ipairs(t) do print(i,v); end | |
print ('DUMP logical_test_fk TO MYSQL'); | |
local t = redis:dump_to_mysql('logical_test_fk'); | |
for i,v in ipairs(t) do print(i,v); end | |
print ('DUMP logical_test_fk TO FILE ./test/DUMP_logical_test_fk.txt'); | |
local t = redis:dump_to_file('logical_test_fk', './test/DUMP_logical_test_fk.txt'); | |
for i,v in ipairs(t) do print(i,v); end | |
-- DROP_INDEX DROP_INDEX DROP_INDEX DROP_INDEX DROP_INDEX | |
-- DROP_INDEX DROP_INDEX DROP_INDEX DROP_INDEX DROP_INDEX | |
local t = redis:drop_index('ind_ltestfk'); | |
print (t); | |
-- CREATE_TABLE_AS CREATE_TABLE_AS CREATE_TABLE_AS CREATE_TABLE_AS | |
-- CREATE_TABLE_AS CREATE_TABLE_AS CREATE_TABLE_AS CREATE_TABLE_AS | |
redis:zadd('test_zset', 1.11, 'ONE'); | |
redis:zadd('test_zset', 2.22, 'two'); | |
redis:zadd('test_zset', 3.33, 'thr33'); | |
redis:drop_table('copy_test_zset'); | |
redis:create_table_as('copy_test_zset', 'DUMP test_zset'); | |
local t = redis:desc('copy_test_zset'); | |
for i,v in ipairs(t) do print(i,v); end | |
local t = redis:dump('copy_test_zset'); | |
for i,v in ipairs(t) do print(i,v); end | |
redis:drop_table('copy_PART_test_zset'); | |
redis:create_table_as('copy_PART_test_zset', 'ZRANGE test_zset 0 1'); | |
local t = redis:desc('copy_PART_test_zset'); | |
for i,v in ipairs(t) do print(i,v); end | |
local t = redis:dump('copy_PART_test_zset'); | |
for i,v in ipairs(t) do print(i,v); end | |
-- LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA | |
-- LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA | |
redis:config('SET', 'luafilename', 'helper.lua'); | |
print ('LUA SCANSELECT * FROM logical_test_fk'); | |
local t = redis:lua("return scanselect('*', 'logical_test_fk');"); | |
for i,v in ipairs(t) do print(i,v); end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module('Redis', package.seeall) | |
local socket = require('socket') | |
local uri = require('socket.url') | |
local commands = {} | |
local network, request, response = {}, {}, {} | |
local defaults = { host = '127.0.0.1', port = 6379, tcp_nodelay = false } | |
local protocol = { | |
newline = '\r\n', | |
ok = 'OK', | |
err = 'ERR', | |
queued = 'QUEUED', | |
null = 'nil' | |
} | |
local function parse_boolean(v) | |
if v == '1' or v == 'true' or v == 'TRUE' then | |
return true | |
elseif v == '0' or v == 'false' or v == 'FALSE' then | |
return false | |
else | |
return nil | |
end | |
end | |
local function toboolean(value) return value == 1 end | |
local function fire_and_forget(client, command) | |
-- let's fire and forget! the connection is closed as soon | |
-- as the SHUTDOWN command is received by the server. | |
client.network.write(client, command .. protocol.newline) | |
return false | |
end | |
local function zset_range_parse(reply, command, ...) | |
local args = {...} | |
if #args == 4 and string.lower(args[4]) == 'withscores' then | |
local new_reply = { } | |
for i = 1, #reply, 2 do | |
table.insert(new_reply, { reply[i], reply[i + 1] }) | |
end | |
return new_reply | |
else | |
return reply | |
end | |
end | |
local function zset_store_request(client, command, ...) | |
local args, opts = {...}, { } | |
if #args >= 1 and type(args[#args]) == 'table' then | |
local options = table.remove(args, #args) | |
if options.weights and type(options.weights) == 'table' then | |
table.insert(opts, 'WEIGHTS') | |
for _, weight in ipairs(options.weights) do | |
table.insert(opts, weight) | |
end | |
end | |
if options.aggregate then | |
table.insert(opts, 'AGGREGATE') | |
table.insert(opts, options.aggregate) | |
end | |
end | |
for _, v in pairs(opts) do table.insert(args, v) end | |
request.multibulk(client, command, args) | |
end | |
local function hmset_filter_args(client, command, ...) | |
local args, arguments = {...}, {} | |
if (#args == 1 and type(args[1]) == 'table') then | |
for k,v in pairs(args[1]) do | |
table.insert(arguments, k) | |
table.insert(arguments, v) | |
end | |
else | |
arguments = args | |
end | |
request.multibulk(client, command, arguments) | |
end | |
local function load_methods(proto, methods) | |
local redis = setmetatable ({}, getmetatable(proto)) | |
for i, v in pairs(proto) do redis[i] = v end | |
for i, v in pairs(methods) do redis[i] = v end | |
return redis | |
end | |
local function create_client(proto, client_socket, methods) | |
local redis = load_methods(proto, methods) | |
redis.network = { | |
socket = client_socket, | |
read = network.read, | |
write = network.write, | |
} | |
redis.requests = { | |
multibulk = request.multibulk, | |
} | |
return redis | |
end | |
-- ############################################################################ | |
function network.write(client, buffer) | |
local _, err = client.network.socket:send(buffer) | |
if err then error(err) end | |
end | |
function network.read(client, len) | |
if len == nil then len = '*l' end | |
local line, err = client.network.socket:receive(len) | |
if not err then return line else error('connection error: ' .. err) end | |
end | |
-- ############################################################################ | |
function response.read(client) | |
local res = client.network.read(client) | |
local prefix = res:sub(1, -#res) | |
local response_handler = protocol.prefixes[prefix] | |
if not response_handler then | |
error('unknown response prefix: ' .. prefix) | |
else | |
return response_handler(client, res) | |
end | |
end | |
function response.status(client, data) | |
local sub = data:sub(2) | |
if sub == protocol.ok then | |
return true | |
elseif sub == protocol.queued then | |
return { queued = true } | |
else | |
return sub | |
end | |
end | |
function response.error(client, data) | |
local err_line = data:sub(2) | |
if err_line:sub(1, 3) == protocol.err then | |
print('redis error: ' .. err_line:sub(5)) | |
--error('redis error: ' .. err_line:sub(5)) | |
else | |
print('redis error: ' .. err_line) | |
--error('redis error: ' .. err_line) | |
end | |
end | |
function response.bulk(client, data) | |
local str = data:sub(2) | |
local len = tonumber(str) | |
if not len then | |
error('cannot parse ' .. str .. ' as data length.') | |
else | |
if len == -1 then return nil end | |
local next_chunk = client.network.read(client, len + 2) | |
return next_chunk:sub(1, -3); | |
end | |
end | |
function response.multibulk(client, data) | |
local str = data:sub(2) | |
local list_count = tonumber(str) | |
if list_count == -1 then | |
return nil | |
else | |
local list = {} | |
if list_count > 0 then | |
for i = 1, list_count do | |
table.insert(list, i, response.read(client)) | |
end | |
end | |
return list | |
end | |
end | |
function response.integer(client, data) | |
local res = data:sub(2) | |
local number = tonumber(res) | |
if not number then | |
if res == protocol.null then | |
return nil | |
else | |
error('cannot parse ' .. res .. ' as numeric response.') | |
end | |
end | |
return number | |
end | |
protocol.prefixes = { | |
['+'] = response.status, | |
['-'] = response.error, | |
['$'] = response.bulk, | |
['*'] = response.multibulk, | |
[':'] = response.integer, | |
} | |
-- ############################################################################ | |
function request.raw(client, buffer) | |
local bufferType = type(buffer) | |
if bufferType == 'table' then | |
client.network.write(client, table.concat(buffer)) | |
elseif bufferType == 'string' then | |
client.network.write(client, buffer) | |
else | |
error('argument error: ' .. bufferType) | |
end | |
end | |
function request.multibulk(client, command, ...) | |
local args = {...} | |
local args_len = #args | |
local buffer = { true, true } | |
local proto_nl = protocol.newline | |
if args_len == 1 and type(args[1]) == 'table' then | |
args_len, args = #args[1], args[1] | |
end | |
buffer[1] = '*' .. tostring(args_len + 1) .. proto_nl | |
buffer[2] = '$' .. #command .. proto_nl .. command .. proto_nl | |
for _, argument in pairs(args) do | |
s_argument = tostring(argument) | |
table.insert(buffer, '$' .. #s_argument .. proto_nl .. s_argument .. proto_nl) | |
end | |
request.raw(client, buffer) | |
end | |
-- ############################################################################ | |
local function custom(command, send, parse) | |
return function(client, ...) | |
local has_reply = send(client, command, ...) | |
if has_reply == false then return end | |
local reply = response.read(client) | |
if type(reply) == 'table' and reply.queued then | |
reply.parser = parse | |
return reply | |
else | |
if parse then | |
return parse(reply, command, ...) | |
else | |
return reply | |
end | |
end | |
end | |
end | |
function command(command, opts) | |
if opts == nil or type(opts) == 'function' then | |
return custom(command, request.multibulk, opts) | |
else | |
return custom(command, opts.request or request.multibulk, opts.response) | |
end | |
end | |
function define_command(name, opts) | |
local opts = opts or {} | |
commands[string.lower(name)] = custom( | |
opts.command or string.upper(name), | |
opts.request or request.multibulk, | |
opts.response or nil | |
) | |
end | |
function undefine_command(name) | |
commands[string.lower(name)] = nil | |
end | |
-- ############################################################################ | |
local client_prototype = {} | |
client_prototype.raw_cmd = function(client, buffer) | |
request.raw(client, buffer .. protocol.newline) | |
return response.read(client) | |
end | |
client_prototype.define_command = function(client, name, opts) | |
local opts = opts or {} | |
client[string.lower(name)] = custom( | |
opts.command or string.upper(name), | |
opts.request or request.multibulk, | |
opts.response or nil | |
) | |
end | |
client_prototype.undefine_command = function(client, name) | |
client[string.lower(name)] = nil | |
end | |
client_prototype.pipeline = function(client, block) | |
local simulate_queued = '+' .. protocol.queued | |
local requests, replies, parsers = {}, {}, {} | |
local __netwrite, __netread = client.network.write, client.network.read | |
client.network.write = function(_, buffer) | |
table.insert(requests, buffer) | |
end | |
-- TODO: this hack is necessary to temporarily reuse the current | |
-- request -> response handling implementation of redis-lua | |
-- without further changes in the code, but it will surely | |
-- disappear when the new command-definition infrastructure | |
-- will finally be in place. | |
client.network.read = function() | |
return simulate_queued | |
end | |
local pipeline = setmetatable({}, { | |
__index = function(env, name) | |
local cmd = client[name] | |
if cmd == nil then | |
if _G[name] then | |
return _G[name] | |
else | |
error('unknown redis command: ' .. name, 2) | |
end | |
end | |
return function(self, ...) | |
local reply = cmd(client, ...) | |
table.insert(parsers, #requests, reply.parser) | |
return reply | |
end | |
end | |
}) | |
local success, retval = pcall(block, pipeline) | |
client.network.write, client.network.read = __netwrite, __netread | |
if not success then error(retval, 0) end | |
client.network.write(client, table.concat(requests, '')) | |
for i = 1, #requests do | |
local raw_reply, parser = response.read(client), parsers[i] | |
if parser then | |
table.insert(replies, i, parser(raw_reply)) | |
else | |
table.insert(replies, i, raw_reply) | |
end | |
end | |
return replies | |
end | |
client_prototype.transaction = function(client, ...) | |
local queued, parsers, replies = 0, {}, {} | |
local block, watch_keys = nil, nil | |
local args = {...} | |
if #args == 2 and type(args[1]) == 'table' then | |
watch_keys = args[1] | |
block = args[2] | |
else | |
block = args[1] | |
end | |
local transaction = setmetatable({ | |
discard = function(...) | |
local reply = client:discard() | |
queued, parsers, replies = 0, {}, {} | |
return reply | |
end, | |
watch = function(...) | |
error('WATCH inside MULTI is not allowed') | |
end, | |
}, { | |
__index = function(env, name) | |
local cmd = client[name] | |
if cmd == nil then | |
if _G[name] then | |
return _G[name] | |
else | |
error('unknown redis command: ' .. name, 2) | |
end | |
end | |
return function(self, ...) | |
if queued == 0 then | |
if client.watch and watch_keys then | |
for _, key in pairs(watch_keys) do | |
client:watch(key) | |
end | |
end | |
client:multi() | |
end | |
local reply = cmd(client, ...) | |
if type(reply) ~= 'table' or reply.queued ~= true then | |
error('a QUEUED reply was expected') | |
end | |
queued = queued + 1 | |
table.insert(parsers, queued, reply.parser) | |
return reply | |
end | |
end, | |
}) | |
local success, retval = pcall(block, transaction) | |
if not success then error(retval, 0) end | |
if queued == 0 then return replies end | |
local raw_replies = client:exec() | |
if raw_replies == nil then | |
error("MULTI/EXEC transaction aborted by the server") | |
end | |
for i = 1, queued do | |
local raw_reply, parser = raw_replies[i], parsers[i] | |
if parser then | |
table.insert(replies, i, parser(raw_reply)) | |
else | |
table.insert(replies, i, raw_reply) | |
end | |
end | |
return replies | |
end | |
-- ############################################################################ | |
function connect(...) | |
local args = {...} | |
local host, port = defaults.host, defaults.port | |
local tcp_nodelay = defaults.tcp_nodelay | |
if #args == 1 then | |
if type(args[1]) == 'table' then | |
host = args[1].host or defaults.host | |
port = args[1].port or defaults.port | |
if args[1].tcp_nodelay ~= nil then | |
tcp_nodelay = args[1].tcp_nodelay == true | |
end | |
else | |
local server = uri.parse(select(1, ...)) | |
if server.scheme then | |
if server.scheme ~= 'redis' then | |
error('"' .. server.scheme .. '" is an invalid scheme') | |
end | |
host, port = server.host, server.port or defaults.port | |
if server.query then | |
for k,v in server.query:gmatch('([-_%w]+)=([-_%w]+)') do | |
if k == 'tcp_nodelay' or k == 'tcp-nodelay' then | |
tcp_nodelay = parse_boolean(v) | |
if tcp_nodelay == nil then | |
tcp_nodelay = defaults.tcp_nodelay | |
end | |
end | |
end | |
end | |
else | |
host, port = server.path, defaults.port | |
end | |
end | |
elseif #args > 1 then | |
host, port = unpack(args) | |
end | |
if host == nil then | |
error('please specify the address of running redis instance') | |
end | |
local client_socket = socket.connect(host, tonumber(port)) | |
if not client_socket then | |
error('could not connect to ' .. host .. ':' .. port) | |
end | |
client_socket:setoption('tcp-nodelay', tcp_nodelay) | |
return create_client(client_prototype, client_socket, commands) | |
end | |
-- ############################################################################ | |
commands = { | |
-- miscellaneous commands | |
ping = command('PING', { | |
response = function(response) return response == 'PONG' end | |
}), | |
echo = command('ECHO'), | |
auth = command('AUTH'), | |
-- connection handling | |
quit = command('QUIT', { request = fire_and_forget }), | |
-- transactions | |
multi = command('MULTI'), | |
exec = command('EXEC'), | |
discard = command('DISCARD'), | |
watch = command('WATCH'), | |
unwatch = command('UNWATCH'), | |
-- commands operating on string values | |
set = command('SET'), | |
setnx = command('SETNX', { response = toboolean }), | |
setex = command('SETEX'), -- >= 2.0 | |
mset = command('MSET', { request = hmset_filter_args }), | |
msetnx = command('MSETNX', { | |
request = hmset_filter_args, | |
response = toboolean | |
}), | |
get = command('GET'), | |
mget = command('MGET'), | |
getset = command('GETSET'), | |
incr = command('INCR'), | |
incrby = command('INCRBY'), | |
decr = command('DECR'), | |
decrby = command('DECRBY'), | |
exists = command('EXISTS', { response = toboolean }), | |
del = command('DEL'), | |
type = command('TYPE'), | |
append = command('APPEND'), -- >= 2.0 | |
substr = command('SUBSTR'), -- >= 2.0 | |
-- commands operating on the key space | |
keys = command('KEYS', { | |
response = function(response) | |
if type(response) == 'table' then | |
return response | |
else | |
local keys = {} | |
response:gsub('[^%s]+', function(key) | |
table.insert(keys, key) | |
end) | |
return keys | |
end | |
end | |
}), | |
randomkey = command('RANDOMKEY', { | |
response = function(response) | |
if response == '' then | |
return nil | |
else | |
return response | |
end | |
end | |
}), | |
rename = command('RENAME'), | |
renamenx = command('RENAMENX', { response = toboolean }), | |
expire = command('EXPIRE', { response = toboolean }), | |
expireat = command('EXPIREAT', { response = toboolean }), | |
dbsize = command('DBSIZE'), | |
ttl = command('TTL'), | |
-- commands operating on lists | |
rpush = command('RPUSH'), | |
lpush = command('LPUSH'), | |
llen = command('LLEN'), | |
lrange = command('LRANGE'), | |
ltrim = command('LTRIM'), | |
lindex = command('LINDEX'), | |
lset = command('LSET'), | |
lrem = command('LREM'), | |
lpop = command('LPOP'), | |
rpop = command('RPOP'), | |
rpoplpush = command('RPOPLPUSH'), | |
blpop = command('BLPOP'), | |
brpop = command('BRPOP'), | |
-- commands operating on sets | |
sadd = command('SADD', { response = toboolean }), | |
srem = command('SREM', { response = toboolean }), | |
spop = command('SPOP'), | |
smove = command('SMOVE', { response = toboolean }), | |
scard = command('SCARD'), | |
sismember = command('SISMEMBER', { response = toboolean }), | |
sinter = command('SINTER'), | |
sinterstore = command('SINTERSTORE'), | |
sunion = command('SUNION'), | |
sunionstore = command('SUNIONSTORE'), | |
sdiff = command('SDIFF'), | |
sdiffstore = command('SDIFFSTORE'), | |
smembers = command('SMEMBERS'), | |
srandmember = command('SRANDMEMBER'), | |
-- commands operating on sorted sets | |
zadd = command('ZADD', { response = toboolean }), | |
zincrby = command('ZINCRBY'), | |
zrem = command('ZREM', { response = toboolean }), | |
zrange = command('ZRANGE', { response = zset_range_parse }), | |
zrevrange = command('ZREVRANGE', { response = zset_range_parse }), | |
zrangebyscore = command('ZRANGEBYSCORE'), | |
zunionstore = command('ZUNIONSTORE', { request = zset_store_request }), | |
zinterstore = command('ZINTERSTORE', { request = zset_store_request }), | |
zcount = command('ZCOUNT'), | |
zcard = command('ZCARD'), | |
zscore = command('ZSCORE'), | |
zremrangebyscore = command('ZREMRANGEBYSCORE'), | |
zrank = command('ZRANK'), | |
zrevrank = command('ZREVRANK'), | |
zremrangebyrank = command('ZREMRANGEBYRANK'), | |
-- commands operating on hashes | |
hset = command('HSET', { response = toboolean }), | |
hsetnx = command('HSETNX', { response = toboolean }), | |
hmset = command('HMSET', { | |
request = function(client, command, ...) | |
local args, arguments = {...}, { } | |
if #args == 2 then | |
table.insert(arguments, args[1]) | |
for k, v in pairs(args[2]) do | |
table.insert(arguments, k) | |
table.insert(arguments, v) | |
end | |
else | |
arguments = args | |
end | |
request.multibulk(client, command, arguments) | |
end, | |
}), | |
hincrby = command('HINCRBY'), | |
hget = command('HGET'), | |
hmget = command('HMGET', { | |
request = function(client, command, ...) | |
local args, arguments = {...}, { } | |
if #args == 2 then | |
table.insert(arguments, args[1]) | |
for _, v in ipairs(args[2]) do | |
table.insert(arguments, v) | |
end | |
else | |
arguments = args | |
end | |
request.multibulk(client, command, arguments) | |
end, | |
}), | |
hdel = command('HDEL', { response = toboolean }), | |
hexists = command('HEXISTS', { response = toboolean }), | |
hlen = command('HLEN'), | |
hkeys = command('HKEYS'), | |
hvals = command('HVALS'), | |
hgetall = command('HGETALL', { | |
response = function(reply, command, ...) | |
local new_reply = { } | |
for i = 1, #reply, 2 do new_reply[reply[i]] = reply[i + 1] end | |
return new_reply | |
end | |
}), | |
-- publish - subscribe | |
subscribe = command('SUBSCRIBE'), | |
unsubscribe = command('UNSUBSCRIBE'), | |
psubscribe = command('PSUBSCRIBE'), | |
punsubscribe = command('PUNSUBSCRIBE'), | |
publish = command('PUBLISH'), | |
-- multiple databases handling commands | |
--select = command('SELECT'), | |
move = command('MOVE', { response = toboolean }), | |
flushdb = command('FLUSHDB'), | |
flushall = command('FLUSHALL'), | |
-- sorting | |
--[[ params = { | |
by = 'weight_*', | |
get = 'object_*', | |
limit = { 0, 10 }, | |
sort = 'desc', | |
alpha = true, | |
} | |
--]] | |
sort = command('SORT', { | |
request = function(client, command, key, params) | |
local query = { key } | |
if params then | |
if params.by then | |
table.insert(query, 'BY') | |
table.insert(query, params.by) | |
end | |
if type(params.limit) == 'table' then | |
-- TODO: check for lower and upper limits | |
table.insert(query, 'LIMIT') | |
table.insert(query, params.limit[1]) | |
table.insert(query, params.limit[2]) | |
end | |
if params.get then | |
table.insert(query, 'GET') | |
table.insert(query, params.get) | |
end | |
if params.sort then | |
table.insert(query, params.sort) | |
end | |
if params.alpha == true then | |
table.insert(query, 'ALPHA') | |
end | |
if params.store then | |
table.insert(query, 'STORE') | |
table.insert(query, params.store) | |
end | |
end | |
request.multibulk(client, command, query) | |
end | |
}), | |
-- persistence control commands | |
save = command('SAVE'), | |
bgsave = command('BGSAVE'), | |
lastsave = command('LASTSAVE'), | |
shutdown = command('SHUTDOWN', { request = fire_and_forget }), | |
bgrewriteaof = command('BGREWRITEAOF'), | |
-- remote server control commands | |
info = command('INFO', { | |
response = function(response) | |
local info = {} | |
response:gsub('([^\r\n]*)\r\n', function(kv) | |
local k,v = kv:match(('([^:]*):([^:]*)'):rep(1)) | |
if (k:match('db%d+')) then | |
info[k] = {} | |
v:gsub(',', function(dbkv) | |
local dbk,dbv = kv:match('([^:]*)=([^:]*)') | |
info[k][dbk] = dbv | |
end) | |
else | |
info[k] = v | |
end | |
end) | |
return info | |
end | |
}), | |
slaveof = command('SLAVEOF'), | |
config = command('CONFIG'), | |
-- Alchemy commands | |
create_table = command('CREATE', { | |
request = function(client, command, ...) | |
local args, arguments = {...}, {} | |
if #args ~= 2 then | |
print ('Usage: create_table tablename column_definitions'); | |
return false; | |
end | |
table.insert(arguments, 'TABLE'); | |
table.insert(arguments, args[1]); | |
table.insert(arguments, '(' .. args[2] .. ')'); | |
request.multibulk(client, command, arguments) | |
end | |
}), | |
drop_table = command('DROP', { | |
request = function(client, command, ...) | |
local args, arguments = {...}, {} | |
if #args ~= 1 then | |
print ('Usage: drop_table tablename'); | |
return false; | |
end | |
table.insert(arguments, 'TABLE'); | |
table.insert(arguments, args[1]); | |
request.multibulk(client, command, arguments) | |
end, | |
}), | |
create_index = command('CREATE', { | |
request = function(client, command, ...) | |
local args, arguments = {...}, {} | |
if #args ~= 3 then | |
print ('Usage: create_index indexname table column'); | |
return false; | |
end | |
table.insert(arguments, 'INDEX'); | |
table.insert(arguments, args[1]); | |
table.insert(arguments, 'ON'); | |
table.insert(arguments, args[2]); | |
table.insert(arguments, '(' .. args[3] .. ')'); | |
request.multibulk(client, command, arguments) | |
end | |
}), | |
drop_index = command('DROP', { | |
request = function(client, command, ...) | |
local args, arguments = {...}, {} | |
if #args ~= 1 then | |
print ('Usage: drop_index indexname'); | |
return false; | |
end | |
table.insert(arguments, 'INDEX'); | |
table.insert(arguments, args[1]); | |
request.multibulk(client, command, arguments) | |
end | |
}), | |
desc = command('DESC'), | |
dump = command('DUMP'), | |
dump_to_mysql = command('DUMP', { | |
request = function(client, command, ...) | |
local args, arguments = {...}, {} | |
if #args ~= 1 then | |
print ('Usage: dump_to_mysql tablename'); | |
return false; | |
end | |
table.insert(arguments, args[1]); | |
table.insert(arguments, 'TO'); | |
table.insert(arguments, 'MYSQL'); | |
request.multibulk(client, command, arguments) | |
end | |
}), | |
dump_to_file = command('DUMP', { | |
request = function(client, command, ...) | |
local args, arguments = {...}, {} | |
if #args ~= 2 then | |
print ('Usage: dump_to_file tablename filename'); | |
return false; | |
end | |
table.insert(arguments, args[1]); | |
table.insert(arguments, 'TO'); | |
table.insert(arguments, 'FILE'); | |
table.insert(arguments, args[2]); | |
request.multibulk(client, command, arguments) | |
end | |
}), | |
insert = command('INSERT', { | |
request = function(client, command, ...) | |
local args, arguments = {...}, {} | |
if #args ~= 2 then | |
print ('Usage: insert table "vals,,,,"'); | |
return false; | |
end | |
table.insert(arguments, 'INTO'); | |
table.insert(arguments, args[1]); | |
table.insert(arguments, 'VALUES'); | |
table.insert(arguments, '(' .. args[2] .. ')'); | |
request.multibulk(client, command, arguments) | |
end | |
}), | |
select = command('SELECT', { | |
request = function(client, command, ...) | |
local args, arguments = {...}, {} | |
if #args == 1 then | |
request.multibulk(client, command, args) | |
elseif #args ~= 3 then | |
print ('Usage: select "cols,,," "tbls,,,," where_clause'); | |
return false; | |
else | |
table.insert(arguments, args[1]); | |
table.insert(arguments, 'FROM'); | |
table.insert(arguments, args[2]); | |
table.insert(arguments, 'WHERE'); | |
table.insert(arguments, args[3]); | |
request.multibulk(client, command, arguments) | |
end | |
end | |
}), | |
scanselect = command('SCANSELECT', { | |
request = function(client, command, ...) | |
local args, arguments = {...}, {} | |
if #args < 2 then | |
print ('Usage: scanselect "cols,,," "tbls,,,," [where_clause]'); | |
return false; | |
else | |
table.insert(arguments, args[1]); | |
table.insert(arguments, 'FROM'); | |
table.insert(arguments, args[2]); | |
if #args > 2 then | |
table.insert(arguments, 'WHERE'); | |
table.insert(arguments, args[3]); | |
end | |
request.multibulk(client, command, arguments) | |
end | |
end | |
}), | |
update = command('UPDATE', { | |
request = function(client, command, ...) | |
local args, arguments = {...}, {} | |
if #args ~= 3 then | |
print ('Usage: update table "col=val,,," where_clause'); | |
return false; | |
else | |
table.insert(arguments, args[1]); | |
table.insert(arguments, 'SET'); | |
table.insert(arguments, args[2]); | |
table.insert(arguments, 'WHERE'); | |
table.insert(arguments, args[3]); | |
request.multibulk(client, command, arguments) | |
end | |
end | |
}), | |
delete = command('DELETE', { | |
request = function(client, command, ...) | |
local args, arguments = {...}, {} | |
if #args ~= 2 then | |
print ('Usage: delete table where_clause'); | |
return false; | |
else | |
table.insert(arguments, 'FROM'); | |
table.insert(arguments, args[1]); | |
table.insert(arguments, 'WHERE'); | |
table.insert(arguments, args[2]); | |
request.multibulk(client, command, arguments) | |
end | |
end | |
}), | |
-- CREATE_TABLE_AS | |
create_table_as = command('CREATE', { | |
request = function(client, command, ...) | |
local args, arguments = {...}, {} | |
if #args ~= 2 then | |
print ('Usage: create_table_as tablename command'); | |
return false; | |
end | |
table.insert(arguments, 'TABLE'); | |
table.insert(arguments, args[1]); | |
table.insert(arguments, 'AS ' .. args[2]); | |
request.multibulk(client, command, arguments) | |
end | |
}), | |
-- INSERT_RETURN_SIZE | |
insert_return_size = command('INSERT', { | |
request = function(client, command, ...) | |
local args, arguments = {...}, {} | |
if #args ~= 2 then | |
print ('Usage: insert table "vals,,,,"'); | |
return false; | |
end | |
table.insert(arguments, 'INTO'); | |
table.insert(arguments, args[1]); | |
table.insert(arguments, 'VALUES'); | |
table.insert(arguments, '(' .. args[2] .. ')'); | |
table.insert(arguments, 'RETURN'); | |
table.insert(arguments, 'SIZE'); | |
request.multibulk(client, command, arguments) | |
end | |
}), | |
lua = command('LUA'), | |
changedb = command('CHANGEDB'), | |
norm = command('NORM'), | |
denorm = command('DENORM'), | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment