Now running pgsql, added rollbacks for failed insert/updates/deletes

This commit is contained in:
mrhanky 2017-06-30 15:03:01 +02:00
parent 45c4b55cf2
commit 8821161f71
No known key found for this signature in database
GPG Key ID: 67D772C481CB41B8
7 changed files with 257 additions and 148 deletions

View File

@ -4,46 +4,69 @@ import irc3
from docopt import Dict as DocOptDict from docopt import Dict as DocOptDict
from irc3.plugins.command import command from irc3.plugins.command import command
from irc3.utils import IrcString from irc3.utils import IrcString
from psycopg2 import Error
from . import DatabasePlugin from . import DatabasePlugin
from ..utils import parse_int from ..utils import parse_int
# TODO: fix sql shit
@irc3.plugin
class McManiac(DatabasePlugin): class McManiac(DatabasePlugin):
requires = ['irc3.plugins.command', requires = ['irc3.plugins.command',
'nxy.plugins.database'] 'nxy.plugins.storage']
table = 'mcmaniacs'
@command(options_first=True) @command(options_first=True)
def mcmaniac(self, mask: IrcString, channel: IrcString, args: DocOptDict): def mcmaniac(self, mask: IrcString, target: IrcString, args: DocOptDict):
"""Get a random McManiaC or by index. """Get a random McManiaC or by index.
%%mcmaniac [<index>] %%mcmaniac [<index>]
""" """
index = args.get('<index>') index = args.get('<index>')
if index: if index:
index = parse_int(index) index = parse_int(index)
if not index: if not index:
return return
index, order, _ = index index, order, _ = index
order = 'id {order}'.format(order=order) order = 'id {order}'.format(order=order)
extra = 'offset ?' offset = 'offset %s'
binds = [index]
else: else:
order = 'random()' order = 'random()'
extra = '' offset = ''
binds = []
self.cur.execute('''select item,
(select count(id) from mcmaniacs b where a.id >= b.id) as idx,
(select count(id) from mcmaniacs) as len
from mcmaniacs a order by {order} limit 1 {extra}
'''.format(order=order, extra=extra), binds)
result = self.cur.fetchone()
if result:
return '[{idx}/{len}] {item}'.format(**result)
@irc3.event(r'(?i)^:(?P<mask>\S+) PRIVMSG \S+ :.*(?P<item>Mc\S+iaC).*') # Fetch result from database
self.cur.execute('''
select
item,
rank() over (order by id),
count(*) over (rows between unbounded preceding
and unbounded following) as total
from
mcmaniacs
order by
{order}
limit
1
{offset}
'''.format(order=order, offset=offset), [index])
result = self.cur.fetchone()
if result:
return '[{rank}/{total}] {item}'.format(**result)
@irc3.event(r'^:(?P<mask>\S+) PRIVMSG \S+ :.*(?P<item>Mc\S+iaC).*')
def save(self, mask: str, item: str): def save(self, mask: str, item: str):
if IrcString(mask).nick != self.bot.nick: if IrcString(mask).nick != self.bot.nick:
self.cur.execute('insert into mcmaniacs (item) values (?)', [item]) try:
# Insert into database
self.cur.execute('''
insert into
mcmaniacs (item)
values
(%s)
''', [item])
self.con.commit() self.con.commit()
except Error:
# Rollback transaction on error
self.con.rollback()

View File

@ -4,18 +4,85 @@ import irc3
from docopt import Dict as DocOptDict from docopt import Dict as DocOptDict
from irc3.plugins.command import command from irc3.plugins.command import command
from irc3.utils import IrcString from irc3.utils import IrcString
from psycopg2 import Error
from . import DatabasePlugin from . import DatabasePlugin
from ..utils import NICK_REGEX, parse_int from ..utils import NICK_REGEX, parse_int
"""
delete from
quotes
where
id = (
select
id
from
quotes a
where
nick like %s and %s = (
select
count(id)
from
quotes b where a.id {op} b.id
)
order by
id {order}
)
"""
@irc3.plugin @irc3.plugin
class Quotes(DatabasePlugin): class Quotes(DatabasePlugin):
requires = ['irc3.plugins.command', requires = ['irc3.plugins.command',
'nxy.plugins.database'] 'nxy.plugins.storage']
def add_quote(self, mask, nick, quote):
# Parse nick from "<@foobar>" like strings
nick = NICK_REGEX.match(nick).group(1)
if not nick:
self.bot.notice(mask.nick, '[Quotes] Error parsing nick')
else:
# Insert quote into database
self.cur.execute('''
insert into
quotes (nick, item)
values
(%s, %s)
''', [nick, quote])
def delete_quote(self, nick, quote):
index, order, _ = parse_int(quote, select=False)
if index:
# Delete from database
self.cur.execute('''
with ranked_quotes as (
select
id,
rank() over (partition by nick order by id {order})
from
quotes
where
nick = %s
)
delete from
quotes
where
id = (
select
id
from
ranked_quotes
where
rank = %s
)
'''.format(order=order), [nick, index])
@command(options_first=True) @command(options_first=True)
def q(self, mask: IrcString, channel: IrcString, args: DocOptDict): def q(self, mask: IrcString, target: IrcString, args: DocOptDict):
"""Get, add or delete quots for a user """Get, add or delete quots for a user
%%q <cmd> <nick> <quote>... %%q <cmd> <nick> <quote>...
@ -23,48 +90,62 @@ class Quotes(DatabasePlugin):
""" """
cmd = args.get('<cmd>') cmd = args.get('<cmd>')
nick = args['<nick>'] nick = args['<nick>']
item = args.get('<quote>') quote = ''.join(args.get('<quote>'))
if cmd and item:
if self.guard.has_permission(mask, 'admin'): if cmd and quote:
try:
# Anybody can add
if cmd == 'add': if cmd == 'add':
nick = NICK_REGEX.match(nick).group(1) self.add_quote(mask, nick, quote)
if not nick: # But only admins can delete
return '[Quotes] Error parsing nick' elif cmd == 'del' and self.guard.has_permission(mask, 'admin'):
self.cur.execute('insert into quotes (nick, item) values ' self.delete_quote(nick, quote)
'(?, ?', [nick, ' '.join(item)])
self.con.commit() self.con.commit()
if cmd == 'del': except Error:
index, order, op = parse_int(''.join(item), select=False) # Rollback transaction on error
if not index: self.con.rollback()
return
self.cur.execute('''delete from quotes where id =
(select id from quotes a where nick like ? and ? =
(select count(id) from quotes b where a.id {op} b.id)
order by id {order})
'''.format(op=op, order=order), [nick, index])
self.con.commit()
else:
self.bot.notice(mask.nick, 'Permission denied')
else: else:
index = args.get('<index>') index = args.get('<index>')
binds = [nick, nick, nick] values = [nick]
if index: if index:
index = parse_int(index) index = parse_int(index)
if not index: if not index:
return return
index, order, _ = index index, order, _ = index
order = 'id {order}'.format(order=order) order = 'rank {order}'.format(order=order)
extra = 'offset ?' offset = 'offset %s'
binds.append(index) values.append(index)
else: else:
order = 'random()' order = 'random()'
extra = '' offset = ''
self.cur.execute('''select nick, item,
(select count(id) from quotes b where b.nick like ? and # Fetch quote from database
a.id >= b.id) as idx, self.cur.execute("""
(select count(id) from quotes where nick like ?) as len with ranked_quotes as (
from quotes a where nick like ? order by {order} limit 1 {extra} select
'''.format(order=order, extra=extra), binds) nick,
item,
rank() over (partition by nick order by id),
count(*) over (partition by nick) as total
from
quotes
)
select
*
from
ranked_quotes
where
nick like %s
order by
{order}
limit
1
{offset}
""".format(order=order, offset=offset), values)
result = self.cur.fetchone() result = self.cur.fetchone()
if result: if result:
return '[{idx}/{len}] <{nick}> {item}'.format(**result) return '[{rank}/{total}] <{nick}> {item}'.format(**result)

View File

@ -5,6 +5,7 @@ import irc3
from docopt import Dict as DocOptDict from docopt import Dict as DocOptDict
from irc3.plugins.command import command from irc3.plugins.command import command
from irc3.utils import IrcString from irc3.utils import IrcString
from psycopg2 import Error
from . import DatabasePlugin from . import DatabasePlugin
@ -56,6 +57,7 @@ class Rape(DatabasePlugin):
else: else:
fine = random.randint(1, 500) fine = random.randint(1, 500)
try:
# Insert or add fine to database and return total owe # Insert or add fine to database and return total owe
self.cur.execute(''' self.cur.execute('''
insert into insert into
@ -80,3 +82,6 @@ class Rape(DatabasePlugin):
fine=fine, fine=fine,
reason=reason, reason=reason,
total=self.cur.fetchone()['amount'])) total=self.cur.fetchone()['amount']))
except Error:
# Rollback transaction on error
self.con.rollback()

View File

@ -6,6 +6,7 @@ import irc3
from docopt import Dict as DocOptDict from docopt import Dict as DocOptDict
from irc3.plugins.command import command from irc3.plugins.command import command
from irc3.utils import IrcString from irc3.utils import IrcString
from psycopg2 import Error
from . import DatabasePlugin from . import DatabasePlugin
@ -52,6 +53,7 @@ class Seen(DatabasePlugin):
@irc3.event(r'(?i)^:(?P<mask>\S+) PRIVMSG (?P<target>\S+) :(?P<msg>.*)') @irc3.event(r'(?i)^:(?P<mask>\S+) PRIVMSG (?P<target>\S+) :(?P<msg>.*)')
def save(self, mask: str, target: str, msg: str): def save(self, mask: str, target: str, msg: str):
try:
# Insert or update if user writes a message # Insert or update if user writes a message
self.cur.execute(''' self.cur.execute('''
insert into insert into
@ -64,3 +66,6 @@ class Seen(DatabasePlugin):
message = excluded.message message = excluded.message
''', [IrcString(mask).nick, target, msg, datetime.now()]) ''', [IrcString(mask).nick, target, msg, datetime.now()])
self.con.commit() self.con.commit()
except Error:
# Rollback transaction on error
self.con.rollback()

View File

@ -3,6 +3,7 @@ import irc3
from docopt import Dict as DocOptDict from docopt import Dict as DocOptDict
from irc3.plugins.command import command from irc3.plugins.command import command
from irc3.utils import IrcString from irc3.utils import IrcString
from psycopg2 import Error
from . import DatabasePlugin from . import DatabasePlugin
@ -49,6 +50,7 @@ class Tell(DatabasePlugin):
self.tell_queue[nick] = [] self.tell_queue[nick] = []
self.tell_queue[nick].append(tell) self.tell_queue[nick].append(tell)
try:
# Insert tell into database # Insert tell into database
self.cur.execute(''' self.cur.execute('''
insert into insert into
@ -57,6 +59,9 @@ class Tell(DatabasePlugin):
(%s, %s, %s) (%s, %s, %s)
''', tell) ''', tell)
self.con.commit() self.con.commit()
except Error:
# Rollback transaction on error
self.con.rollback()
@irc3.event(r'(?i)^:(?P<mask>.*) PRIVMSG .* :.*') @irc3.event(r'(?i)^:(?P<mask>.*) PRIVMSG .* :.*')
def check(self, mask: str): def check(self, mask: str):
@ -72,6 +77,7 @@ class Tell(DatabasePlugin):
# Remove nick from queue # Remove nick from queue
del self.tell_queue[nick] del self.tell_queue[nick]
try:
# Remove tells from database # Remove tells from database
self.cur.execute(''' self.cur.execute('''
delete from delete from
@ -80,3 +86,6 @@ class Tell(DatabasePlugin):
to_nick = %s to_nick = %s
''', [nick]) ''', [nick])
self.con.commit() self.con.commit()
except Error:
# Rollback transaction on error
self.con.rollback()

View File

@ -6,6 +6,7 @@ import irc3
from docopt import Dict as DocOptDict from docopt import Dict as DocOptDict
from irc3.plugins.command import command from irc3.plugins.command import command
from irc3.utils import IrcString from irc3.utils import IrcString
from psycopg2 import Error
from . import DatabasePlugin from . import DatabasePlugin
from ..utils import time_delta from ..utils import time_delta
@ -51,6 +52,7 @@ class Timer(DatabasePlugin):
message = ' '.join(args['<message>']) message = ' '.join(args['<message>'])
values = [mask, target, message, delay] values = [mask, target, message, delay]
try:
# Insert into database (add now + delta) # Insert into database (add now + delta)
self.cur.execute(''' self.cur.execute('''
insert into insert into
@ -68,6 +70,9 @@ class Timer(DatabasePlugin):
# Send notice to user that timer has been set # Send notice to user that timer has been set
self.bot.notice(mask.nick, 'Timer in {delay} set: {message}' self.bot.notice(mask.nick, 'Timer in {delay} set: {message}'
.format(delay=delay, message=message)) .format(delay=delay, message=message))
except Error:
# Rollback transaction on error
self.con.rollback()
def start_timer(self, mask: IrcString, target: IrcString, message: str, def start_timer(self, mask: IrcString, target: IrcString, message: str,
delay: str, delta: timedelta, row_id: int): delay: str, delta: timedelta, row_id: int):
@ -85,6 +90,7 @@ class Timer(DatabasePlugin):
nick=mask.nick, nick=mask.nick,
delay=delay)) delay=delay))
try:
# Delete timer from database # Delete timer from database
self.cur.execute(''' self.cur.execute('''
delete from delete from
@ -93,5 +99,8 @@ class Timer(DatabasePlugin):
id = %s id = %s
''', [row_id]) ''', [row_id])
self.con.commit() self.con.commit()
except Error:
# Rollback transaction on error
self.con.rollback()
asyncio.ensure_future(callback()) asyncio.ensure_future(callback())

View File

@ -45,26 +45,3 @@ create table if not exists owes (
amount integer not null, amount integer not null,
unique (nick) unique (nick)
); );
with ranked_quotes as (
select
id,
rank() over (partition by nick order by id desc)
from
quotes
where
nick = 'mrhanky'
)
delete from
quotes
where
id = (
select
id
from
ranked_quotes
where
rank = 1
)