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

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. 

16 

17import sqlite3 

18 

19from oc_ocdm.counter_handler.counter_handler import CounterHandler 

20 

21 

22class SqliteCounterHandler(CounterHandler): 

23 """A concrete implementation of the ``CounterHandler`` interface that persistently stores 

24 the counter values within a SQLite database.""" 

25 

26 def __init__(self, database: str) -> None: 

27 """ 

28 Constructor of the ``SqliteCounterHandler`` class. 

29 

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)""") 

40 

41 def set_counter(self, new_value: int, entity_name: str) -> None: 

42 """ 

43 It allows to set the counter value of provenance entities. 

44 

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() 

57 

58 def read_counter(self, entity_name: str) -> int: 

59 """ 

60 It allows to read the counter value of provenance entities. 

61 

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")) 

75 

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. 

79 

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 

89 

90 def increment_metadata_counter(self): 

91 pass 

92 

93 def read_metadata_counter(self): 

94 pass 

95 

96 def set_metadata_counter(self): 

97 pass 

98 

99 def __getstate__(self): 

100 """ 

101 Support for pickle serialization. 

102 

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 

110 

111 def __setstate__(self, state): 

112 """ 

113 Support for pickle deserialization. 

114 

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()