-
Notifications
You must be signed in to change notification settings - Fork 0
/
litecounter.py
104 lines (84 loc) · 3.1 KB
/
litecounter.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
import sqlite3
import pathlib
from contextlib import contextmanager
import pprint
__version__ = "0.2"
# SQLite works better in autocommit mode when using short DML (INSERT / UPDATE / DELETE) statements
# source: https://charlesleifer.com/blog/going-fast-with-sqlite-and-python/
@contextmanager
def transaction(conn: sqlite3.Connection):
# We must issue a "BEGIN" explicitly when running in auto-commit mode.
conn.execute("BEGIN")
try:
# Yield control back to the caller.
yield conn
except BaseException as e:
conn.rollback() # Roll back all changes if an exception occurs.
raise e
else:
conn.commit()
class SQLCounter:
def __init__(
self,
filename_or_conn=None,
memory=False,
**kwargs,
):
assert (filename_or_conn is not None and not memory) or (
filename_or_conn is None and memory
), "Either specify a filename_or_conn or pass memory=True"
if memory or filename_or_conn == ":memory:":
self.conn = sqlite3.connect(":memory:", isolation_level=None, **kwargs)
elif isinstance(filename_or_conn, (str, pathlib.Path)):
self.conn = sqlite3.connect(
str(filename_or_conn), isolation_level=None, **kwargs
)
else:
assert filename_or_conn is not None
self.conn = filename_or_conn
self.conn.isolation_level = None
with transaction(self.conn) as c:
c.execute(
"CREATE TABLE IF NOT EXISTS Counter (key text NOT NULL PRIMARY KEY, value integer)"
)
# if fast:
self.conn.execute("PRAGMA journal_mode = 'WAL';")
self.conn.execute("PRAGMA temp_store = 2;")
self.conn.execute("PRAGMA synchronous = 1;")
self.conn.execute(f"PRAGMA cache_size = {-1 * 64_000};")
def incr(self, key):
self.conn.execute(
"INSERT INTO Counter VALUES (?, 1) ON CONFLICT(key) DO UPDATE SET value = value + 1",
(key,),
)
return
def decr(self, key):
self.conn.execute(
"INSERT INTO Counter VALUES (?, -1) ON CONFLICT(key) DO UPDATE SET value = value - 1",
(key,),
)
return
def count(self, key):
c = self.conn.execute("SELECT value FROM Counter WHERE Key=?", (key,))
row = c.fetchone()
if row is None:
return None
return row[0]
def zero(self, key):
self.conn.execute(
"INSERT INTO Counter VALUES (?, 0) ON CONFLICT(key) DO UPDATE SET value = 0",
(key,),
)
return
def delete(self, key):
"""
Delete counter key, if the key does NOT exist
it will just ignore it (no exceptions raised)
"""
self.conn.execute("DELETE FROM Counter WHERE key=?", (key,))
def __repr__(self):
return f"{type(self).__name__}(Connection={self.conn!r}, items={pprint.pformat(self.conn.execute('SELECT * FROM Counter').fetchall())})"
def vacuum(self):
self.conn.execute("VACUUM;")
def close(self):
self.conn.close()