2424logger = logging .getLogger (__name__ )
2525
2626connect_lock = asyncio .Lock ()
27+ apply_revisions_lock = asyncio .Lock ()
2728
2829
2930# ----------------------------------------------------------------------------------------
@@ -60,7 +61,10 @@ def __init__(self, specification):
6061
6162 self .__tables = {}
6263
63- self .LATEST_REVISION = 1
64+ # Deriving class has not established its latest revision?
65+ if not hasattr (self , "LATEST_REVISION" ) or self .LATEST_REVISION is None :
66+ # Presume it is 1.
67+ self .LATEST_REVISION = 1
6468
6569 self .__backup_restore_lock = asyncio .Lock ()
6670
@@ -100,8 +104,8 @@ async def connect(self):
100104 # rows = await self.query("SELECT * from mainTable", why="main table check")
101105
102106 await self .__connection .create_function ("regexp" , 2 , sqlite_regexp_callback )
103- logger .debug ("created regexp function" )
104107
108+ # Let the base class contribute its table definitions to the in-memory list.
105109 await self .add_table_definitions ()
106110
107111 if should_create_schemas :
@@ -111,42 +115,59 @@ async def connect(self):
111115 )
112116 # TODO: Set permission on sqlite file from configuration.
113117 os .chmod (self .__filename , 0o666 )
114- else :
115- try :
116- records = await self .query (
117- f"SELECT number FROM { Tablenames .REVISION } " ,
118- why = "get database revision" ,
119- )
120- if len (records ) == 0 :
121- old_revision = 0
122- else :
123- old_revision = records [0 ]["number" ]
124- except Exception as exception :
125- logger .warning (
126- f"could not get revision, presuming legacy database with no table: { exception } "
127- )
128- old_revision = 0
129-
130- if old_revision < self .LATEST_REVISION :
131- logger .debug (
132- f"need to update old revision { old_revision } "
133- f" to latest revision { self .LATEST_REVISION } "
134- )
135- for revision in range (old_revision , self .LATEST_REVISION ):
136- logger .debug (f"updating to revision { revision + 1 } " )
137- await self .apply_revision (revision + 1 )
138- await self .update (
139- Tablenames .REVISION ,
140- {"number" : self .LATEST_REVISION },
141- "1 = 1" ,
142- why = "update database revision" ,
143- )
144118
145119 # Emit the name of the database file for positive confirmation on console.
146120 logger .info (
147121 f"{ callsign (self )} database file is { self .__filename } revision { self .LATEST_REVISION } "
148122 )
149123
124+ # ----------------------------------------------------------------------------------------
125+ async def apply_revisions (self ):
126+ """
127+ Apply revision updates to databse if needed.
128+ """
129+
130+ # TODO: Consider how to lock database while running applying_revisions.
131+ # TODO: Establish transaction arouund apply_revisions with rollback if error.
132+ async with apply_revisions_lock :
133+ try :
134+ records = await self .query (
135+ f"SELECT number FROM { Tablenames .REVISION } " ,
136+ why = "get database revision" ,
137+ )
138+ if len (records ) == 0 :
139+ old_revision = 0
140+ else :
141+ old_revision = records [0 ]["number" ]
142+ except Exception as exception :
143+ logger .warning (
144+ f"could not get revision, presuming legacy database with no table: { exception } "
145+ )
146+ old_revision = 0
147+
148+ if old_revision < self .LATEST_REVISION :
149+ # Backup before applying revisions.
150+ logger .debug (
151+ f"[BKREVL] backing up before updating to revision { self .LATEST_REVISION } "
152+ )
153+
154+ await self .backup ()
155+
156+ for revision in range (old_revision , self .LATEST_REVISION ):
157+ logger .debug (f"updating to revision { revision + 1 } " )
158+ await self .apply_revision (revision + 1 )
159+ await self .update (
160+ Tablenames .REVISION ,
161+ {"number" : self .LATEST_REVISION },
162+ "1 = 1" ,
163+ why = "update database revision" ,
164+ )
165+ else :
166+ logger .debug (
167+ f"[BKREVL] no need to update old revision { old_revision } "
168+ f" which matches latest revision { self .LATEST_REVISION } "
169+ )
170+
150171 # ----------------------------------------------------------------------------------------
151172 async def apply_revision (self , revision ):
152173 logger .debug (f"updating to revision { revision } " )
@@ -469,25 +490,26 @@ async def backup(self):
469490 Back up database to timestamped location.
470491 """
471492
472- # Prune all the restores which were orphaned.
473- directory = self .__backup_directory
493+ async with self .__backup_restore_lock :
494+ # Prune all the restores which were orphaned.
495+ directory = self .__backup_directory
496+ if directory is None :
497+ raise RuntimeError ("no backup directory supplied in confirmation" )
474498
475- basename , suffix = os .path .splitext (os .path .basename (self .__filename ))
499+ basename , suffix = os .path .splitext (os .path .basename (self .__filename ))
476500
477- filenames = glob .glob (f"{ directory } /{ basename } .*{ suffix } " )
501+ filenames = glob .glob (f"{ directory } /{ basename } .*{ suffix } " )
478502
479- filenames .sort (reverse = True )
503+ filenames .sort (reverse = True )
480504
481- logger .debug (f"[BACKPRU] { self .__last_restore } is last restore" )
482- for restore in range (self .__last_restore ):
483- logger .debug (
484- f"[BACKPRU] removing { restore } -th restore { filenames [restore ]} "
485- )
486- os .remove (filenames [restore ])
505+ for restore in range (self .__last_restore ):
506+ logger .debug (
507+ f"[BACKPRU] removing { restore } -th restore { filenames [restore ]} "
508+ )
509+ os .remove (filenames [restore ])
487510
488- self .__last_restore = 0
511+ self .__last_restore = 0
489512
490- async with self .__backup_restore_lock :
491513 timestamp = isodatetime_filename ()
492514 to_filename = f"{ directory } /{ basename } .{ timestamp } { suffix } "
493515
@@ -509,6 +531,8 @@ async def restore(self, nth):
509531
510532 async with self .__backup_restore_lock :
511533 directory = self .__backup_directory
534+ if directory is None :
535+ raise RuntimeError ("no backup directory supplied in confirmation" )
512536
513537 basename , suffix = os .path .splitext (os .path .basename (self .__filename ))
514538
0 commit comments