Coverage for oc_ocdm/counter_handler/sqlite_counter_handler.py: 89%
46 statements
« prev ^ index » next coverage.py v6.5.0, created at 2025-12-05 23:58 +0000
« prev ^ index » next coverage.py v6.5.0, created at 2025-12-05 23:58 +0000
1#!/usr/bin/python
2# -*- coding: utf-8 -*-
3# Copyright (c) 2023, Arcangelo Massari <arcangelo.massari@unibo.it>
4#
5# Permission to use, copy, modify, and/or distribute this software for any purpose
6# with or without fee is hereby granted, provided that the above copyright notice
7# and this permission notice appear in all copies.
8#
9# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
10# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
11# FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
12# OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
13# DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
14# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
15# SOFTWARE.
17import sqlite3
19from oc_ocdm.counter_handler.counter_handler import CounterHandler
22class SqliteCounterHandler(CounterHandler):
23 """A concrete implementation of the ``CounterHandler`` interface that persistently stores
24 the counter values within a SQLite database."""
26 def __init__(self, database: str) -> None:
27 """
28 Constructor of the ``SqliteCounterHandler`` class.
30 :param database: The name of the database
31 :type info_dir: str
32 """
33 sqlite3.threadsafety = 3
34 self.database = database
35 self.con = sqlite3.connect(database)
36 self.cur = self.con.cursor()
37 self.cur.execute("""CREATE TABLE IF NOT EXISTS info(
38 entity TEXT PRIMARY KEY,
39 count INTEGER)""")
41 def set_counter(self, new_value: int, entity_name: str) -> None:
42 """
43 It allows to set the counter value of provenance entities.
45 :param new_value: The new counter value to be set
46 :type new_value: int
47 :param entity_name: The entity name
48 :type entity_name: str
49 :raises ValueError: if ``new_value`` is a negative integer.
50 :return: None
51 """
52 entity_name = str(entity_name)
53 if new_value < 0:
54 raise ValueError("new_value must be a non negative integer!")
55 self.cur.execute(f"INSERT OR REPLACE INTO info (entity, count) VALUES ('{entity_name}', {new_value})")
56 self.con.commit()
58 def read_counter(self, entity_name: str) -> int:
59 """
60 It allows to read the counter value of provenance entities.
62 :param entity_name: The entity name
63 :type entity_name: str
64 :return: The requested counter value.
65 """
66 entity_name = str(entity_name)
67 result = self.cur.execute(f"SELECT count FROM info WHERE entity='{entity_name}'")
68 rows = result.fetchall()
69 if len(rows) == 1:
70 return rows[0][0]
71 elif len(rows) == 0:
72 return 0
73 else:
74 raise(Exception("There is more than one counter for this entity. The databse id broken"))
76 def increment_counter(self, entity_name: str) -> int:
77 """
78 It allows to increment the counter value of graph and provenance entities by one unit.
80 :param entity_name: The entity name
81 :type entity_name: str
82 :return: The newly-updated (already incremented) counter value.
83 """
84 entity_name = str(entity_name)
85 cur_count = self.read_counter(entity_name)
86 count = cur_count + 1
87 self.set_counter(count, entity_name)
88 return count
90 def increment_metadata_counter(self):
91 pass
93 def read_metadata_counter(self):
94 pass
96 def set_metadata_counter(self):
97 pass
99 def __getstate__(self):
100 """
101 Support for pickle serialization.
103 Exclude the SQLite connection and cursor objects, which are not picklable.
104 The database path is preserved and the connection will be recreated upon unpickling.
105 """
106 state = self.__dict__.copy()
107 del state['con']
108 del state['cur']
109 return state
111 def __setstate__(self, state):
112 """
113 Support for pickle deserialization.
115 Recreates the SQLite connection and cursor after unpickling.
116 """
117 self.__dict__.update(state)
118 sqlite3.threadsafety = 3
119 self.con = sqlite3.connect(self.database)
120 self.cur = self.con.cursor()