3
0
Fork 0
mirror of https://github.com/YosysHQ/sby.git synced 2025-08-11 07:40:54 +00:00

Test status db schema

`--status` reports if the schema doesn't match.
`--statusreset` is able to perform a hard reset, dropping all the existing tables and calling `_setup()`.
This commit is contained in:
Krystine Sherwin 2025-07-08 15:44:01 +12:00
parent ad9382d46c
commit 10040ce859
No known key found for this signature in database
2 changed files with 66 additions and 50 deletions

View file

@ -85,12 +85,14 @@ if status_show or status_reset:
status_db = SbyStatusDb(status_path, task=None) status_db = SbyStatusDb(status_path, task=None)
if status_show:
status_db.print_status_summary()
sys.exit(0)
if status_reset: if status_reset:
status_db.reset() status_db.reset()
elif status_db.test_schema():
print(f"ERROR: Status database does not match expected formatted. Use --statusreset to reset.")
sys.exit(1)
if status_show:
status_db.print_status_summary()
status_db.db.close() status_db.db.close()
sys.exit(0) sys.exit(0)

View file

@ -4,6 +4,7 @@ import sqlite3
import os import os
import time import time
import json import json
import re
from collections import defaultdict from collections import defaultdict
from functools import wraps from functools import wraps
from pathlib import Path from pathlib import Path
@ -13,6 +14,45 @@ from sby_design import SbyProperty, pretty_path
Fn = TypeVar("Fn", bound=Callable[..., Any]) Fn = TypeVar("Fn", bound=Callable[..., Any])
SQLSCRIPT = """\
CREATE TABLE task (
id INTEGER PRIMARY KEY,
workdir TEXT,
mode TEXT,
created REAL
);
CREATE TABLE task_status (
id INTEGER PRIMARY KEY,
task INTEGER,
status TEXT,
data TEXT,
created REAL,
FOREIGN KEY(task) REFERENCES task(id)
);
CREATE TABLE task_property (
id INTEGER PRIMARY KEY,
task INTEGER,
src TEXT,
name TEXT,
created REAL,
FOREIGN KEY(task) REFERENCES task(id)
);
CREATE TABLE task_property_status (
id INTEGER PRIMARY KEY,
task_property INTEGER,
status TEXT,
data TEXT,
created REAL,
FOREIGN KEY(task_property) REFERENCES task_property(id)
);
CREATE TABLE task_property_data (
id INTEGER PRIMARY KEY,
task_property INTEGER,
kind TEXT,
data TEXT,
created REAL,
FOREIGN KEY(task_property) REFERENCES task_property(id)
);"""
def transaction(method: Fn) -> Fn: def transaction(method: Fn) -> Fn:
@wraps(method) @wraps(method)
@ -79,50 +119,17 @@ class SbyStatusDb:
@transaction @transaction
def _setup(self): def _setup(self):
script = """ for statement in SQLSCRIPT.split(";\n"):
CREATE TABLE task (
id INTEGER PRIMARY KEY,
workdir TEXT,
mode TEXT,
created REAL
);
CREATE TABLE task_status (
id INTEGER PRIMARY KEY,
task INTEGER,
status TEXT,
data TEXT,
created REAL,
FOREIGN KEY(task) REFERENCES task(id)
);
CREATE TABLE task_property (
id INTEGER PRIMARY KEY,
task INTEGER,
src TEXT,
name TEXT,
created REAL,
FOREIGN KEY(task) REFERENCES task(id)
);
CREATE TABLE task_property_status (
id INTEGER PRIMARY KEY,
task_property INTEGER,
status TEXT,
data TEXT,
created REAL,
FOREIGN KEY(task_property) REFERENCES task_property(id)
);
CREATE TABLE task_property_data (
id INTEGER PRIMARY KEY,
task_property INTEGER,
kind TEXT,
data TEXT,
created REAL,
FOREIGN KEY(task_property) REFERENCES task_property(id)
);
"""
for statement in script.split(";\n"):
statement = statement.strip() statement = statement.strip()
if statement: if statement:
self.db.execute(statement) self.db.execute(statement)
self.db.execute("""PRAGMA foreign_keys = ON;""")
def test_schema(self) -> bool:
schema = self.db.execute("SELECT sql FROM sqlite_master;").fetchall()
schema_script = '\n'.join(str(sql[0] + ';') for sql in schema)
self._tables = re.findall(r"CREATE TABLE (\w+) \(", schema_script)
return schema_script != SQLSCRIPT
@transaction @transaction
def create_task(self, workdir: str, mode: str) -> int: def create_task(self, workdir: str, mode: str) -> int:
@ -284,11 +291,18 @@ class SbyStatusDb:
@transaction @transaction
def reset(self): def reset(self):
self.db.execute("""DELETE FROM task_property_status""") hard_reset = self.test_schema()
self.db.execute("""DELETE FROM task_property_data""") # table names can't be parameters, so we need to use f-strings
self.db.execute("""DELETE FROM task_property""") # but it is safe to use here because it comes from the regex "\w+"
self.db.execute("""DELETE FROM task_status""") for table in self._tables:
self.db.execute("""DELETE FROM task""") if hard_reset:
self.log_debug(f"dropping {table}")
self.db.execute(f"DROP TABLE {table}")
else:
self.log_debug(f"clearing {table}")
self.db.execute(f"DELETE FROM {table}")
if hard_reset:
self._setup()
def print_status_summary(self): def print_status_summary(self):
tasks, task_properties, task_property_statuses = self.all_status_data() tasks, task_properties, task_property_statuses = self.all_status_data()