diff --git a/bitbake/bin/bitbake-hashclient b/bitbake/bin/bitbake-hashclient index 5d65c7bc56..58aa02ee55 100755 --- a/bitbake/bin/bitbake-hashclient +++ b/bitbake/bin/bitbake-hashclient @@ -174,6 +174,10 @@ def main(): total_rows = sum(t["rows"] for t in usage.values()) print(f"Total rows: {total_rows}") + def handle_get_db_query_columns(args, client): + columns = client.get_db_query_columns() + print("\n".join(sorted(columns))) + parser = argparse.ArgumentParser(description='Hash Equivalence Client') parser.add_argument('--address', default=DEFAULT_ADDRESS, help='Server address (default "%(default)s")') parser.add_argument('--log', default='WARNING', help='Set logging level') @@ -239,6 +243,9 @@ def main(): db_usage_parser = subparsers.add_parser('get-db-usage', help="Database Usage") db_usage_parser.set_defaults(func=handle_get_db_usage) + db_query_columns_parser = subparsers.add_parser('get-db-query-columns', help="Show columns that can be used in database queries") + db_query_columns_parser.set_defaults(func=handle_get_db_query_columns) + args = parser.parse_args() logger = logging.getLogger('hashserv') diff --git a/bitbake/lib/hashserv/client.py b/bitbake/lib/hashserv/client.py index 5e0a462b1c..35a97687fb 100644 --- a/bitbake/lib/hashserv/client.py +++ b/bitbake/lib/hashserv/client.py @@ -190,6 +190,10 @@ class AsyncClient(bb.asyncrpc.AsyncClient): await self._set_mode(self.MODE_NORMAL) return (await self.invoke({"get-db-usage": {}}))["usage"] + async def get_db_query_columns(self): + await self._set_mode(self.MODE_NORMAL) + return (await self.invoke({"get-db-query-columns": {}}))["columns"] + class Client(bb.asyncrpc.Client): def __init__(self, username=None, password=None): @@ -219,6 +223,7 @@ class Client(bb.asyncrpc.Client): "delete_user", "become_user", "get_db_usage", + "get_db_query_columns", ) def _get_async_client(self): diff --git a/bitbake/lib/hashserv/server.py b/bitbake/lib/hashserv/server.py index c5b9797e4e..8c3d20b651 100644 --- a/bitbake/lib/hashserv/server.py +++ b/bitbake/lib/hashserv/server.py @@ -250,6 +250,7 @@ class ServerClient(bb.asyncrpc.AsyncServerConnection): "get-stream": self.handle_get_stream, "get-stats": self.handle_get_stats, "get-db-usage": self.handle_get_db_usage, + "get-db-query-columns": self.handle_get_db_query_columns, # Not always read-only, but internally checks if the server is # read-only "report": self.handle_report, @@ -572,6 +573,10 @@ class ServerClient(bb.asyncrpc.AsyncServerConnection): async def handle_get_db_usage(self, request): return {"usage": await self.db.get_usage()} + @permissions(DB_ADMIN_PERM) + async def handle_get_db_query_columns(self, request): + return {"columns": await self.db.get_query_columns()} + # The authentication API is always allowed async def handle_auth(self, request): username = str(request["username"]) diff --git a/bitbake/lib/hashserv/sqlalchemy.py b/bitbake/lib/hashserv/sqlalchemy.py index 818b51951b..cee04bffb0 100644 --- a/bitbake/lib/hashserv/sqlalchemy.py +++ b/bitbake/lib/hashserv/sqlalchemy.py @@ -415,3 +415,13 @@ class Database(object): } return usage + + async def get_query_columns(self): + columns = set() + for table in (UnihashesV2, OuthashesV2): + for c in table.__table__.columns: + if not isinstance(c.type, Text): + continue + columns.add(c.key) + + return list(columns) diff --git a/bitbake/lib/hashserv/sqlite.py b/bitbake/lib/hashserv/sqlite.py index dfdccbbaa0..f65036be93 100644 --- a/bitbake/lib/hashserv/sqlite.py +++ b/bitbake/lib/hashserv/sqlite.py @@ -399,3 +399,10 @@ class Database(object): "rows": cursor.fetchone()[0], } return usage + + async def get_query_columns(self): + columns = set() + for name, typ, _ in UNIHASH_TABLE_DEFINITION + OUTHASH_TABLE_DEFINITION: + if typ.startswith("TEXT"): + columns.add(name) + return list(columns) diff --git a/bitbake/lib/hashserv/tests.py b/bitbake/lib/hashserv/tests.py index 9d5bec2454..fc69acaf7c 100644 --- a/bitbake/lib/hashserv/tests.py +++ b/bitbake/lib/hashserv/tests.py @@ -776,6 +776,14 @@ class HashEquivalenceCommonTests(object): self.assertIn("rows", usage[name]) self.assertTrue(isinstance(usage[name]["rows"], int)) + def test_get_db_query_columns(self): + columns = self.client.get_db_query_columns() + + self.assertTrue(isinstance(columns, list)) + self.assertTrue(len(columns) > 0) + + for col in columns: + self.client.remove({col: ""}) class TestHashEquivalenceUnixServer(HashEquivalenceTestSetup, HashEquivalenceCommonTests, unittest.TestCase): def get_server_addr(self, server_idx):