From 0fe1134225b15d1fb6a84b056629a05ae12202ce Mon Sep 17 00:00:00 2001 From: Whisperity Date: Wed, 17 Jan 2024 14:03:12 +0100 Subject: [PATCH] feat(server): Store checker list as analysis-info This patch allows users to gather whether a checker was enabled or disabled during the analysis, irrespective of whether a checker produced any reports (which might have been deleted from the server since!). This improves auditing capabilities as the definite knowledge on which checkers were _available_ is kept in the database, and not lost after the analysis temporaries are cleaned up from the analysing client. Features: - Create a new table, `checkers`, to store unique ID (per product database) for a checker's name. - Add information about checkers and enabledness to the database, based on the `metadata.json`, if available. - Extend the `AnalysisInfo` API object to report the collected information to the client. Refactoring: - Normalise the use of the `checkers` table by lifting additional checker-unique information (`severity`) from `reports`, leaving only a `FOREIGN KEY` in the `reports` table. - Add facilities for explicitly annotating `zlib`-compressed strings in the database. - Ensure that all versions of `metadata.json` is represented the same way in memory once the `MetadataInfoParser` succeeded. --- .github/workflows/test.yml | 6 +- alembic.ini | 35 -- analyzer/tests/unit/test_checker_labels.py | 7 + codechecker_common/checker_labels.py | 13 +- codechecker_common/util.py | 36 +- .../report/__init__.py | 10 +- .../dist/codechecker-api-6.54.0.tgz | Bin 62288 -> 0 bytes .../dist/codechecker-api-6.55.0.tgz | Bin 0 -> 62764 bytes web/api/js/codechecker-api-node/package.json | 2 +- .../dist/codechecker_api.tar.gz | Bin 54261 -> 58918 bytes web/api/py/codechecker_api/setup.py | 2 +- .../dist/codechecker_api_shared.tar.gz | Bin 3634 -> 7943 bytes web/api/py/codechecker_api_shared/setup.py | 2 +- web/api/report_server.thrift | 17 +- web/codechecker_web/shared/version.py | 2 +- .../codechecker_server/api/mass_store_run.py | 505 ++++++++++++------ .../codechecker_server/api/report_server.py | 246 +++++---- .../codechecker_server/database/common.py | 155 ++++++ .../database/config_db_model.py | 4 +- .../codechecker_server/database/database.py | 6 + .../codechecker_server/database/db_cleanup.py | 202 +++---- .../database/run_db_model.py | 113 +++- web/server/codechecker_server/metadata.py | 52 +- .../codechecker_server/migrations/common.py | 100 ++++ .../migrations/config/README | 1 - ...cf38af_add_extra_product_detail_columns.py | 5 +- .../migrations/report/README | 1 - .../migrations/report/env.py | 58 +- ...enabled_and_disabled_checkers_for_a_run.py | 448 ++++++++++++++++ .../dabc6998b8f0_analysis_info_table.py | 8 +- web/server/codechecker_server/server.py | 6 +- .../tests/unit/metadata_test_files/v1.5.json | 50 ++ .../tests/unit/metadata_test_files/v1.json | 14 +- web/server/tests/unit/test_metadata_merge.py | 1 + web/server/tests/unit/test_metadata_parser.py | 60 ++- web/server/vue-cli/package-lock.json | 12 +- web/server/vue-cli/package.json | 2 +- .../src/components/AnalysisInfoDialog.vue | 6 + web/tests/functional/blame/test_blame_info.py | 10 +- .../functional/cppcheck/test_cppcheck.py | 8 +- .../report_viewer_api/test_run_data.py | 71 ++- 41 files changed, 1783 insertions(+), 493 deletions(-) delete mode 100644 web/api/js/codechecker-api-node/dist/codechecker-api-6.54.0.tgz create mode 100644 web/api/js/codechecker-api-node/dist/codechecker-api-6.55.0.tgz create mode 100644 web/server/codechecker_server/database/common.py create mode 100644 web/server/codechecker_server/migrations/common.py delete mode 100644 web/server/codechecker_server/migrations/config/README delete mode 100644 web/server/codechecker_server/migrations/report/README create mode 100644 web/server/codechecker_server/migrations/report/versions/c3dad71f8e6b_store_information_about_enabled_and_disabled_checkers_for_a_run.py create mode 100644 web/server/tests/unit/metadata_test_files/v1.5.json diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e4e2983193..00e8ffca07 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -155,6 +155,7 @@ jobs: run: sh .github/workflows/install-deps.sh - name: Init .pgpass + if: matrix.database != 'sqlite' run: | echo '*:*:*:*:postgres' > $HOME/.pgpass chmod 0600 $HOME/.pgpass @@ -163,7 +164,10 @@ jobs: env: PGPASSWORD: postgres run: | - export PGPASSFILE=$HOME/.pgpass + if [[ "${{ matrix.database != 'sqlite' }}" == "true" ]] + then + export PGPASSFILE=$HOME/.pgpass + fi make pip_dev_deps pip3 install -r web/requirements_py/auth/requirements.txt diff --git a/alembic.ini b/alembic.ini index fa1dd4ce4d..55fbd96dfd 100644 --- a/alembic.ini +++ b/alembic.ini @@ -61,38 +61,3 @@ script_location = web/server/codechecker_server/migrations/report sqlalchemy.url = postgres://postgres@localhost:5432/default #sqlalchemy.url = sqlite:////home/username/.codechecker/Default.sqlite - -# Logging configuration -[loggers] -keys = root,sqlalchemy,alembic - -[handlers] -keys = console - -[formatters] -keys = generic - -[logger_root] -level = WARN -handlers = console -qualname = - -[logger_sqlalchemy] -level = WARN -handlers = -qualname = sqlalchemy.engine - -[logger_alembic] -level = INFO -handlers = -qualname = alembic - -[handler_console] -class = StreamHandler -args = (sys.stderr,) -level = NOTSET -formatter = generic - -[formatter_generic] -format = %(levelname)-5.5s [%(name)s] %(message)s -datefmt = %H:%M:%S diff --git a/analyzer/tests/unit/test_checker_labels.py b/analyzer/tests/unit/test_checker_labels.py index 262c7c6882..22593e9254 100644 --- a/analyzer/tests/unit/test_checker_labels.py +++ b/analyzer/tests/unit/test_checker_labels.py @@ -115,6 +115,13 @@ def initialize_labels_dir(self): def test_checker_labels(self): cl = CheckerLabels(self.labels_dir.name) + self.assertEqual( + sorted(cl.get_analyzers()), + sorted([ + "clang-tidy", + "clangsa" + ])) + self.assertEqual( sorted(cl.checkers_by_labels([ 'profile:extreme'])), diff --git a/codechecker_common/checker_labels.py b/codechecker_common/checker_labels.py index dea883ebc6..6b7039e8ff 100644 --- a/codechecker_common/checker_labels.py +++ b/codechecker_common/checker_labels.py @@ -1,6 +1,12 @@ -import os +# ------------------------------------------------------------------------- +# +# Part of the CodeChecker project, under the Apache License v2.0 with +# LLVM Exceptions. See LICENSE for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +# ------------------------------------------------------------------------- from collections import defaultdict - +import os from typing import Any, cast, DefaultDict, Dict, Iterable, List, Optional, \ Set, Tuple, Union @@ -148,6 +154,9 @@ def __get_analyzer_data( if analyzer is None or a == analyzer: yield a, c + def get_analyzers(self) -> Iterable[str]: + return self.__data.keys() + def checkers_by_labels( self, filter_labels: Iterable[str], diff --git a/codechecker_common/util.py b/codechecker_common/util.py index 197755968f..a4bc9b532f 100644 --- a/codechecker_common/util.py +++ b/codechecker_common/util.py @@ -8,12 +8,12 @@ """ Util module. """ - - import itertools import json -from typing import TextIO +from math import ceil +from typing import Callable, TextIO import os + import portalocker from codechecker_common.logger import get_logger @@ -45,6 +45,36 @@ def chunks(iterator, n): yield itertools.chain([first], rest_of_chunk) +def progress(g, count: int, n: int, + callback: Callable[[int, float], None]): + """ + Wraps a generator of a known total length and fires 'callback' after having + yielded every (T/N)th element. The 'callback' is given the index of the + element handled just before firing it, and the percentage of progress. + """ + # E.g., if count == 100 and n == 5, then becomes [100, 95, ..., 10, 5, 0]. + try: + checkpoints = [count] + list(reversed( + [list(chk)[0] + for chk in chunks( + range(0, count + 1), + int(ceil(count / n)) + )])) + if checkpoints[-1] == 0: + checkpoints.pop() + except ValueError: + # The range is too small to have (count / n) many slices. + checkpoints = [count] + + i = 0 + for e in g: + i = i + 1 + yield e + if i == checkpoints[-1]: + callback(i, float(i) / count * 100) + checkpoints.pop() + + def load_json(path: str, default=None, lock=False, display_warning=True): """ Load the contents of the given file as a JSON and return it's value, diff --git a/tools/report-converter/codechecker_report_converter/report/__init__.py b/tools/report-converter/codechecker_report_converter/report/__init__.py index cadea68fbc..c500015a4c 100644 --- a/tools/report-converter/codechecker_report_converter/report/__init__.py +++ b/tools/report-converter/codechecker_report_converter/report/__init__.py @@ -338,8 +338,8 @@ def __init__( self.severity = severity self.report_hash = report_hash self.analyzer_name = analyzer_name - self.category = category - self.type = type + self.category = category # TODO: Remove this. DEPRECATED. + self.type = type # TODO: Remove this. DEPRECATED. self.annotations = annotations self.static_message = \ @@ -488,7 +488,13 @@ def to_json(self) -> Dict: "severity": self.severity, "report_hash": self.report_hash, "analyzer_name": self.analyzer_name, + # DEPRECATED: 'category' is deprecated in 6.24.0, as it is not + # parsed, understood, or handled by the report server. + # It should be removed! "category": self.category, + # DEPRECATED: 'type' is deprecated in 6.24.0, as it is not + # parsed, understood, or handled by the report server. + # It should be removed! "type": self.type, "review_status": self.review_status.status if self.review_status else '', diff --git a/web/api/js/codechecker-api-node/dist/codechecker-api-6.54.0.tgz b/web/api/js/codechecker-api-node/dist/codechecker-api-6.54.0.tgz deleted file mode 100644 index ad14888696a3a95238ea7862f03ba4609c602845..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 62288 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 diff --git a/web/api/js/codechecker-api-node/dist/codechecker-api-6.55.0.tgz b/web/api/js/codechecker-api-node/dist/codechecker-api-6.55.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..a8a714c56f2a86f0e1e527b4469633279a78a8cf GIT binary patch literal 62764 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 literal 0 HcmV?d00001 diff --git a/web/api/js/codechecker-api-node/package.json b/web/api/js/codechecker-api-node/package.json index 6bdf935418..873f658109 100644 --- a/web/api/js/codechecker-api-node/package.json +++ b/web/api/js/codechecker-api-node/package.json @@ -1,6 +1,6 @@ { "name": "codechecker-api", - "version": "6.54.0", + "version": "6.55.0", "description": "Generated node.js compatible API stubs for CodeChecker server.", "main": "lib", "homepage": "https://github.com/Ericsson/codechecker", diff --git a/web/api/py/codechecker_api/dist/codechecker_api.tar.gz b/web/api/py/codechecker_api/dist/codechecker_api.tar.gz index e2fb1622dfddb96b91ea1bb2c5082e0237f585f8..000692b19282a1f3db9ec7570370021b37f8d447 100644 GIT binary patch literal 58918 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
e{5ZGyg_<|5=qtV#2#b|GO`N<`AJKp0( zzTrr_9qspgYmTRgb(TWDJx_iTV*k)m9-_ryzZEyBOzj#Amd%tTW|mHV8lGOUYy2A|K12jTXg5klRf=BIx7CTD6W z+dQKWgy`}iW11PQj_TuUAcC<$;I*A=DN zUK-lfx1Q)E_I@y{!~D9O_3>+s81uI5;|V82p2-lO=O#S^GdX}Ny@}GnKI2xw{~x+Qgw6= zitX4^nD(WmU&^Ov`PKIY+j1^ryQJ18Fb22Oz_Q%N+LT#5T$t4B2M2XrgO$GNX6>nY z$x+;5#6vD0wx|7BG;a5dvIl{xQ0I(Uvl*rZekadpD9jY&({1jVj=oWp5+*N5ho{qZ z20VB;nMl(478EftM7LcwYWyoM`|l24c$y#CEswDUO>wNM^IGfNUA5df^@U|k;~U8+ zMO$2DT}e84V+#m)2S*VnTO|mz_SzX7U6RM4bSXaiYdEFO+Eh(om`cPo?%#MkfEHwc0roQ_rDjs79x zr+F8PTC;bYCeDHgA_W3p()&yvz|Ug}YZ%X?hSp1fPYr8`no$5zH=@joxusA%Sv``_ z?$%oUJ9c6}R5ea7vS*e>SAmmt*IxSKvj6Yx1mXBs$5sX+>=pYWWa8U_%m@eITH+7An zcCuiheH7gyx?@Lgl-e8&hMLEpDE=Oa+KQYjchenQT^GtA_a)XtMJ%8p=jXY)oGR(B zvQ?)*k<@Ee2&xSiZF%0D>b)52&8%eFTB)is{`}@qZVB#kqpxk(c)n!qO#I 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 F5CEeBj?4f6 literal 54261 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 diff --git a/web/api/py/codechecker_api/setup.py b/web/api/py/codechecker_api/setup.py index 806b546b36..c5f7fe14fb 100644 --- a/web/api/py/codechecker_api/setup.py +++ b/web/api/py/codechecker_api/setup.py @@ -8,7 +8,7 @@ with open('README.md', encoding='utf-8', errors="ignore") as f: long_description = f.read() -api_version = '6.54.0' +api_version = '6.55.0' setup( name='codechecker_api', diff --git a/web/api/py/codechecker_api_shared/dist/codechecker_api_shared.tar.gz b/web/api/py/codechecker_api_shared/dist/codechecker_api_shared.tar.gz index ee89d70e0599b3f87d39ca5881f029cdd201a233..b54c641a171171b51a2a79e56bb67cb578d64390 100644 GIT binary patch literal 7943 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 E0J?HQL;wH) diff --git a/web/api/py/codechecker_api_shared/setup.py b/web/api/py/codechecker_api_shared/setup.py index 747e74be03..13dda9d3f1 100644 --- a/web/api/py/codechecker_api_shared/setup.py +++ b/web/api/py/codechecker_api_shared/setup.py @@ -8,7 +8,7 @@ with open('README.md', encoding='utf-8', errors="ignore") as f: long_description = f.read() -api_version = '6.54.0' +api_version = '6.55.0' setup( name='codechecker_api_shared', diff --git a/web/api/report_server.thrift b/web/api/report_server.thrift index e37dcf67d6..428f1bc18d 100644 --- a/web/api/report_server.thrift +++ b/web/api/report_server.thrift @@ -198,7 +198,8 @@ struct RunHistoryData { 4: string user, // User name who analysed the run. 5: string time, // Date time when the run was analysed. 6: i64 id, // Id of the run history tag. - 7: string checkCommand, // Check command. !!!DEPRECATED!!! This field will be empty so use the getCheckCommand API function to get the check command for a run. + // !!!DEPRECATED!!! This field will be empty so use the getCheckCommand() API function to get the check command for a run. + 7: string checkCommand, 8: string codeCheckerVersion, // CodeChecker client version of the latest analysis. 9: AnalyzerStatisticsData analyzerStatistics, // Statistics for analyzers. Only number of failed and successfully analyzed // files field will be set. To get full analyzer statistics please use the @@ -467,8 +468,16 @@ union AnalysisInfoFilter { 3: i64 reportId, } +struct AnalysisInfoChecker { + 1: optional bool enabled, // If the checker was enabled during the analysis. +} + struct AnalysisInfo { - 1: string analyzerCommand, + 1: string analyzerCommand, + // For each analyzer, the checkers and their status as was available during + // the analysis. + 2: optional map> checkers, } typedef string CommitHash @@ -534,12 +543,12 @@ service codeCheckerDBAccess { // Get check command for a run. // PERMISSION: PRODUCT_VIEW - // !DEPRECATED Use getAnalysisInfo API to get the check commands. + // !DEPRECATED Use getAnalysisInfo() API to get the check commands. string getCheckCommand(1: i64 runHistoryId, 2: i64 runId) throws (1: codechecker_api_shared.RequestFailed requestError), - // Get analyzer commands based on the given filters. + // Get analyzer execution information based on the given filters. // PERMISSION: PRODUCT_VIEW list getAnalysisInfo(1: AnalysisInfoFilter analysisInfoFilter, 2: i64 limit, diff --git a/web/codechecker_web/shared/version.py b/web/codechecker_web/shared/version.py index c045815196..d5f83d22b0 100644 --- a/web/codechecker_web/shared/version.py +++ b/web/codechecker_web/shared/version.py @@ -18,7 +18,7 @@ # The newest supported minor version (value) for each supported major version # (key) in this particular build. SUPPORTED_VERSIONS = { - 6: 54 + 6: 55 } # Used by the client to automatically identify the latest major and minor diff --git a/web/server/codechecker_server/api/mass_store_run.py b/web/server/codechecker_server/api/mass_store_run.py index 6ea0a6bb49..7b5710c1f4 100644 --- a/web/server/codechecker_server/api/mass_store_run.py +++ b/web/server/codechecker_server/api/mass_store_run.py @@ -16,10 +16,10 @@ import zlib from collections import defaultdict -from datetime import datetime +from datetime import datetime, timedelta from hashlib import sha256 from tempfile import TemporaryDirectory -from typing import Any, Dict, List, Optional, Set, Tuple +from typing import Any, Dict, List, Optional, Set, Tuple, cast import codechecker_api_shared from codechecker_api.codeCheckerDBAccess_v6 import ttypes @@ -37,10 +37,14 @@ from ..database import db_cleanup from ..database.config_db_model import Product from ..database.database import DBSession -from ..database.run_db_model import AnalysisInfo, AnalyzerStatistic, \ - BugPathEvent, BugReportPoint, ReportAnnotations, ExtendedReportData, \ - File, FileContent, Report as DBReport, ReviewStatus as ReviewStatusRule, \ - Run, RunHistory, RunLock +from ..database.run_db_model import \ + AnalysisInfo, AnalysisInfoChecker, AnalyzerStatistic, \ + BugPathEvent, BugReportPoint, \ + Checker, \ + ExtendedReportData, \ + File, FileContent, \ + Report as DBReport, ReportAnnotations, ReviewStatus as ReviewStatusRule, \ + Run, RunLock, RunHistory from ..metadata import checker_is_unavailable, MetadataInfoParser from .report_server import ThriftRequestHandler @@ -61,10 +65,44 @@ def __enter__(self, *args): LOG.info("[%s] %s...", self.__run_name, self.__msg) def __exit__(self, *args): - LOG.info("[%s] %s done... (duration: %s sec)", self.__run_name, + LOG.info("[%s] %s. Done. (Duration: %s sec)", self.__run_name, self.__msg, round(time.time() - self.__start_time, 2)) +class _RunLock: + def __init__(self, session: DBSession, run_name: str): + self.__session = session + self.__run_name = run_name + + def __enter__(self, *args): + # Load the lock record for "FOR UPDATE" so that the transaction that + # handles the run's store operations has a lock on the database row + # itself. + self.__run_lock = self.__session.query(RunLock) \ + .filter(RunLock.name == self.__run_name) \ + .with_for_update(nowait=True) \ + .one() + + # Do *NOT* remove this seemingly dummy print, we need to make sure + # that the execution of the SQL statement is not optimised away and + # the fetched row is not garbage collected. + LOG.debug("Acquired exclusive lock for run '%s' that was originally " + "locked at '%s'.", + self.__run_name, self.__run_lock.locked_at) + return self + + def __exit__(self, *args): + self.__run_lock = None + self.__session = None + + def noop(self): + """ + Does nothing, but prevents Pylint from complaining, and prevents the + context from being optimised out. + """ + return None + + def unzip(b64zip: str, output_dir: str) -> int: """ This function unzips the base64 encoded zip file. This zip is extracted @@ -191,6 +229,14 @@ def get_blame_file_data( return blame_info, remote_url, tracking_branch +def checker_name_for_report(report: Report) -> Tuple[str, str]: + analyzer_name, checker_name = \ + getattr(report, "analyzer_name"), getattr(report, "checker_name") + analyzer_name = analyzer_name if analyzer_name else "UNKNOWN" + checker_name = checker_name if checker_name else "NOT FOUND" + return (analyzer_name, checker_name) + + class MassStoreRun: def __init__( self, @@ -214,17 +260,17 @@ def __init__( self.__trim_path_prefixes = trim_path_prefixes self.__description = description - self.__mips: Dict[str, MetadataInfoParser] = {} - self.__analysis_info: Dict[str, AnalysisInfo] = {} + self.__mips: Dict[str, MetadataInfoParser] = dict() + self.__analysis_info: Dict[str, AnalysisInfo] = dict() self.__duration: int = 0 self.__report_count: int = 0 self.__report_limit: int = 0 - self.__wrong_src_code_comments: List[str] = [] + self.__wrong_src_code_comments: List[str] = list() self.__already_added_report_hashes: Set[str] = set() - self.__severity_map: Dict[str, int] = {} - self.__new_report_hashes: Dict[str, Tuple] = {} + self.__new_report_hashes: Dict[str, Tuple] = dict() self.__all_report_checkers: Set[str] = set() self.__added_reports: List[Tuple[DBReport, Report]] = list() + self.__reports_with_fake_checkers: List[Tuple[int, Report]] = list() self.__get_report_limit_for_product() @@ -529,6 +575,66 @@ def __add_file_content( # the meantime. session.rollback() + def __store_checker_identifiers(self, checkers: Set[Tuple[str, str]]): + """ + Stores the identifiers "(analyzer, checker_name)" in the database into + a look-up table where each unique checker is given a unique numeric + identifier. + + Due to the use of an M-to-N connection table + (see `AnalysisInfoChecker`) one side of the joins must have their IDs + eagerly populated, otherwise the Python bindings will fail. + However, eager population will result in exceptions that a flush was + created before the transaction was complete. + + Moreover, this is performed separately from the storing of the details + of a run to reduce contention if two parallel stores, especially across + server instances (in a distributed/load-balanced environment) want to + store the same identifier(s). + """ + max_tries, tries, wait_time = 3, 0, timedelta(seconds=30) + # The "fake" checker is a temporary row that is needed intermittently + # during report storage because there might be reports that point to + # checkers which are not found in a preemptively parsed + # 'metadata.json', or, in the worst case, there might simply not be + # a 'metadata.json' at all in the to-be-stored structure. + all_checkers = {("__FAKE__", "__FAKE__"), + ("UNKNOWN", "NOT FOUND")} | checkers + while tries < max_tries: + tries += 1 + try: + LOG.debug("[%s] Begin attempt %d...", self.__name, tries) + with DBSession(self.__Session) as session: + known_checkers = {(r.analyzer_name, r.checker_name) + for r in session.query(Checker).all()} + unknown_checkers = all_checkers - known_checkers + for r in unknown_checkers: + analyzer, checker = r[0], r[1] + s = self.__context.checker_labels.severity(r[1]) + s = ttypes.Severity._NAMES_TO_VALUES[s] + session.add(Checker(analyzer, checker, s)) + + session.commit() + return + except (sqlalchemy.exc.OperationalError, + sqlalchemy.exc.ProgrammingError) as ex: + LOG.error("Storing checkers of run '%s' failed: %s.\n" + "Waiting %d before trying again...", + self.__name, ex, wait_time) + time.sleep(wait_time.total_seconds()) + wait_time *= 2 + except Exception as ex: + LOG.error("Failed to store checkers due to some other error: " + "%s", ex) + import traceback + traceback.print_exc() + raise + + raise codechecker_api_shared.ttypes.RequestFailed( + codechecker_api_shared.ttypes.ErrorCode.DATABASE, + "Storing the names of the checkers in the run failed due to " + "excessive contention!") + def __store_analysis_statistics( self, session: DBSession, @@ -602,14 +708,10 @@ def __store_analysis_info( """ Store analysis info for the given run history. """ for src_dir_path, mip in self.__mips.items(): for analyzer_command in mip.check_commands: - cmd = zlib.compress( - analyzer_command.encode("utf-8"), - zlib.Z_BEST_COMPRESSION) - analysis_info_rows = session \ .query(AnalysisInfo) \ - .filter(AnalysisInfo.analyzer_command == cmd) \ - .all() + .filter(AnalysisInfo.analyzer_command == + analyzer_command).all() if analysis_info_rows: # It is possible when multiple runs are stored @@ -618,8 +720,26 @@ def __store_analysis_info( # database. In this case we will select the first one. analysis_info = analysis_info_rows[0] else: - analysis_info = AnalysisInfo(analyzer_command=cmd) + analysis_info = AnalysisInfo( + analyzer_command=analyzer_command) + + # Obtain the ID eagerly to be able to use the M-to-N table. session.add(analysis_info) + session.flush() + session.refresh(analysis_info, ["id"]) + + for analyzer in mip.analyzers: + q = session \ + .query(Checker) \ + .filter(Checker.analyzer_name == analyzer) + db_checkers = {r.checker_name: r for r in q.all()} + + connection_rows = [AnalysisInfoChecker( + analysis_info, db_checkers[chk], is_enabled) + for chk, is_enabled + in mip.checkers.get(analyzer, dict()).items()] + for r in connection_rows: + session.add(r) run_history.analysis_info.append(analysis_info) self.__analysis_info[src_dir_path] = analysis_info @@ -718,6 +838,24 @@ def __add_or_update_run( codechecker_api_shared.ttypes.ErrorCode.GENERAL, str(ex)) + def __get_checker(self, + session: DBSession, + analyzer_name: Optional[str], + checker_name: Optional[str]) -> Optional[Checker]: + analyzer_name = analyzer_name if analyzer_name else "UNKNOWN" + checker_name = checker_name if checker_name else "NOT FOUND" + return session.query(Checker) \ + .filter(sqlalchemy.and_( + Checker.analyzer_name == analyzer_name, + Checker.checker_name == checker_name)) \ + .one_or_none() + + def __checker_for_report(self, + session: DBSession, + report: Report) -> Optional[Checker]: + analyzer_name, checker_name = checker_name_for_report(report) + return self.__get_checker(session, analyzer_name, checker_name) + def __add_report( self, session: DBSession, @@ -729,43 +867,47 @@ def __add_report( detection_time: datetime, run_history_time: datetime, analysis_info: AnalysisInfo, - analyzer_name: Optional[str] = None, fixed_at: Optional[datetime] = None ) -> int: """ Add report to the database. """ try: - checker_name = report.checker_name - - # Cache the severity of the checkers - try: - severity = self.__severity_map[checker_name] - except KeyError: - severity_name = \ - self.__context.checker_labels.severity(checker_name) - severity = ttypes.Severity._NAMES_TO_VALUES[severity_name] - self.__severity_map[checker_name] = severity + checker = self.__checker_for_report(session, report) + if not checker: + # It would be too easy to create a 'Checker' instance with the + # observed data right here, but __add_report() is called in + # the context of the *BIG* TRANSACTION which has all the + # reports of the entire store pending. Losing all that + # information on a potential UNIQUE CONSTRAINT violation due + # to multiple concurrent massStoreRun()s trying to store the + # same checker ID which was never seen in a 'metadata.json' is + # not worth it. + checker = self.__get_checker(session, "__FAKE__", "__FAKE__") + if not checker: + LOG.fatal("Psuedo-checker '__FAKE__/__FAKE__' has no " + "identity in the database, even though " + "__store_checker_identifiers() should have " + "always preemptively created it!") + raise KeyError("__FAKE__") db_report = DBReport( - run_id, report.report_hash, file_path_to_id[report.file.path], - report.message, checker_name or 'NOT FOUND', - report.category, report.type, report.line, report.column, - severity, review_status.status, review_status.author, + file_path_to_id[report.file.path], run_id, report.report_hash, + checker, report.line, report.column, + len(report.bug_path_events), report.message, detection_status, + review_status.status, review_status.author, review_status.message, run_history_time, - review_status.in_source, - detection_status, detection_time, - len(report.bug_path_events), analyzer_name) - - db_report.fixed_at = fixed_at - + review_status.in_source, detection_time, fixed_at) if analysis_info: db_report.analysis_info.append(analysis_info) session.add(db_report) + session.flush() + session.refresh(db_report, ["id"]) + self.__added_reports.append((db_report, report)) + if db_report.checker.checker_name == "__FAKE__": + self.__reports_with_fake_checkers \ + .append((db_report.id, report)) - # THE id is none at this point of time - # wondering if not returning anything is good? - # The report is already handled at the above lines return db_report.id except Exception as ex: @@ -773,6 +915,53 @@ def __add_report( codechecker_api_shared.ttypes.ErrorCode.GENERAL, str(ex)) + def __get_faked_checkers(self) \ + -> Set[Tuple[str, str]]: + """ + Extracts the "real" checker identifiers from the + __reports_with_fake_checkers list that might contain some yet not + fully handled reports by __add_report(). This function does NOT touch + the database! + """ + return set(checker_name_for_report(r) + for _, r in self.__reports_with_fake_checkers) + + def __realise_fake_checkers(self, session): + """ + __add_report() might leave some reports that have checker names in + their data that were not found in the usually full checker list + available in the 'metadata.json'. This usually happens if the report + directory that is being stored is not from an 'analyze' or 'check' + invocation (that would create an appropriate 'metadata.json') but from + other tools (e.g., report-converter, or Cppcheck's native PLISTs). + + This function assumes that for each report in the + __reports_with_fake_checkers list, their actual checkers' IDs are now + added to the database (which was not the case when __add_report() ran), + so all it does is upgrade the 'checker_id' FOREIGN KEY field to point + at the real checker. + """ + try: + grouped_by_checker: Dict[Tuple[str, str], List[int]] = \ + defaultdict(list) + for report_id, report in self.__reports_with_fake_checkers: + checker: Tuple[str, str] = checker_name_for_report(report) + grouped_by_checker[checker].append(report_id) + + for chk, report_ids in grouped_by_checker.items(): + analyzer_name, checker_name = chk + chk_obj = cast(Checker, self.__get_checker(session, + analyzer_name, + checker_name)) + session.query(DBReport) \ + .filter(DBReport.id.in_(report_ids)) \ + .update({"checker_id": chk_obj.id}, + synchronize_session=False) + except Exception as ex: + raise codechecker_api_shared.ttypes.RequestFailed( + codechecker_api_shared.ttypes.ErrorCode.DATABASE, + str(ex)) + def __add_report_context(self, session, file_path_to_id): try: for db_report, report in self.__added_reports: @@ -892,7 +1081,7 @@ def get_missing_file_ids(report: Report) -> List[str]: if old_status == 'resolved' else 'unresolved' detected_at = old_report.detected_at - analyzer_name = mip.checker_to_analyzer.get( + report.analyzer_name = mip.checker_to_analyzer.get( report.checker_name, report.analyzer_name) review_status = SourceReviewStatus() @@ -919,9 +1108,9 @@ def get_missing_file_ids(report: Report) -> List[str]: self.__check_report_count() report_id = self.__add_report( - session, run_id, report, file_path_to_id, - review_status, detection_status, detected_at, - run_history_time, analysis_info, analyzer_name, fixed_at) + session, run_id, report, file_path_to_id, review_status, + detection_status, detected_at, run_history_time, + analysis_info, fixed_at) self.__new_report_hashes[report.report_hash] = \ review_status.status @@ -1046,7 +1235,6 @@ def get_skip_handler( self.__already_added_report_hashes = set() self.__new_report_hashes = dict() self.__all_report_checkers = set() - self.__severity_map = dict() all_reports = session.query(DBReport) \ .filter(DBReport.run_id == run_id) \ @@ -1147,10 +1335,11 @@ def get_skip_handler( reports_to_delete.update([x.id for x in reports]) else: for report in reports: - checker = report.checker_id - if checker in disabled_checkers: + checker_name: str = report.checker.checker_name + if checker_name in disabled_checkers: report.detection_status = 'off' - elif checker_is_unavailable(checker, enabled_checkers): + elif checker_is_unavailable(checker_name, + enabled_checkers): report.detection_status = 'unavailable' else: report.detection_status = 'resolved' @@ -1228,121 +1417,109 @@ def store(self) -> int: run_history_time = datetime.now() # Parse all metadata information from the report directory. - for root_dir_path, _, _ in os.walk(report_dir): - metadata_file_path = os.path.join( - root_dir_path, 'metadata.json') - - self.__mips[root_dir_path] = \ - MetadataInfoParser(metadata_file_path) - - # When we use multiple server instances and we try to run - # multiple storage to each server which contain at least two - # reports which have the same report hash and have source code - # comments it is possible that the following exception will be - # thrown: (psycopg2.extensions.TransactionRollbackError) - # deadlock detected. - # The problem is that the report hash is the key for the - # review_statuses table and both of the store actions try to - # update the same review_statuses data row. - # Neither of the two processes can continue, and they will wait - # for each other indefinitely. PostgreSQL in this case will - # terminate one transaction with the above exception. - # For this reason in case of failure we will wait some seconds - # and try to run the storage again. - # For more information see #2655 and #2653 issues on github. - # TODO: Since review status is stored in "reports" table and - # "review_statuses" table is not written during storage, this - # multiple trials should be unnecessary. - max_num_of_tries = 3 - num_of_tries = 0 - sec_to_wait_after_failure = 60 - while True: - try: - # This session's transaction buffer stores the actual - # run data into the database. - with DBSession(self.__Session) as session: - # Load the lock record for "FOR UPDATE" so that the - # transaction that handles the run's store - # operations has a lock on the database row itself. - run_lock = session.query(RunLock) \ - .filter(RunLock.name == self.__name) \ - .with_for_update(nowait=True).one() - - # Do not remove this seemingly dummy print, we need - # to make sure that the execution of the SQL - # statement is not optimised away and the fetched - # row is not garbage collected. - LOG.debug("Storing into run '%s' locked at '%s'.", - self.__name, run_lock.locked_at) - - # Actual store operation begins here. - run_id, update_run = self.__add_or_update_run( - session, run_history_time) + with LogTask(run_name=self.__name, + message="Parse 'metadata.json's"): + for root_dir_path, _, _ in os.walk(report_dir): + metadata_file_path = os.path.join( + root_dir_path, 'metadata.json') + self.__mips[root_dir_path] = \ + MetadataInfoParser(metadata_file_path) + + with LogTask(run_name=self.__name, + message="Get look-up ID for all known checkers"): + checkers_in_metadata = { + (analyzer, checker) + for metadata in self.__mips.values() + for analyzer in metadata.analyzers + for checker + in metadata.checkers.get(analyzer, dict()).keys()} + self.__store_checker_identifiers(checkers_in_metadata) + + try: + # This session's transaction buffer stores the actual + # run data into the database. + with DBSession(self.__Session) as session, \ + _RunLock(session, self.__name) as _lock: + # Actual store operation begins here. + run_id, update_run = self.__add_or_update_run( + session, run_history_time) + + with LogTask(run_name=self.__name, + message="Store reports"): + self.__store_reports( + session, report_dir, source_root, run_id, + file_path_to_id, run_history_time) + + session.commit() + _lock.noop() + + if self.__reports_with_fake_checkers: + with LogTask(run_name=self.__name, + message="Get look-up ID for checkers " + "not present in 'metadata.json'"): + additional_checkers = self.__get_faked_checkers() + # __store_checker_identifiers() has its own + # TRANSACTION! + self.__store_checker_identifiers( + additional_checkers) + + with DBSession(self.__Session) as session, \ + _RunLock(session, self.__name) as _lock: + # The data of the run has been successfully committed + # into the database. Deal with post-processing issues + # that could only be done after-the-fact. + if self.__reports_with_fake_checkers: with LogTask(run_name=self.__name, - message="Store reports"): - self.__store_reports( - session, report_dir, source_root, run_id, - file_path_to_id, run_history_time) - - self.finish_checker_run(session, run_id) - - session.commit() - - inc_num_of_runs = 1 - - # If it's a run update, do not increment the number - # of runs of the current product. - if update_run: - inc_num_of_runs = None - - self.__report_server._set_run_data_for_curr_product( - inc_num_of_runs, run_history_time) - - runtime = round(time.time() - start_time, 2) - zip_size_kb = round(zip_size / 1024) - - tag_desc = "" - if self.__tag: - tag_desc = f", under tag '{self.__tag}'" - - LOG.info("'%s' stored results (%s KB " - "/decompressed/) to run '%s' (id: %d) %s in " - "%s seconds.", self.user_name, - zip_size_kb, self.__name, run_id, tag_desc, - runtime) - - iso_start_time = datetime.fromtimestamp( - start_time).isoformat() - - log_msg = f"{iso_start_time}, " +\ - f"{runtime}s, " +\ - f'"{self.__product.name}", ' +\ - f'"{self.__name}", ' +\ - f"{zip_size_kb}KB, " +\ - f"{self.__report_count}, " +\ - f"{run_id}" - - STORE_TIME_LOG.info(log_msg) - - return run_id - except (sqlalchemy.exc.OperationalError, - sqlalchemy.exc.ProgrammingError) as ex: - num_of_tries += 1 - - if num_of_tries == max_num_of_tries: - raise codechecker_api_shared.ttypes.RequestFailed( - codechecker_api_shared.ttypes. - ErrorCode.DATABASE, - "Storing reports to the database failed: " - "{0}".format(ex)) - - LOG.error("Storing reports of '%s' run failed: " - "%s.\nWaiting %d sec before trying to store " - "it again!", self.__name, ex, - sec_to_wait_after_failure) - time.sleep(sec_to_wait_after_failure) - sec_to_wait_after_failure *= 2 + message="Fix-up report-to-checker " + "associations"): + self.__realise_fake_checkers(session) + + self.finish_checker_run(session, run_id) + + session.commit() + _lock.noop() + + # If it's a run update, do not increment the number + # of runs of the current product. + inc_num_of_runs = 1 if not update_run else None + + self.__report_server._set_run_data_for_curr_product( + inc_num_of_runs, run_history_time) + + runtime = round(time.time() - start_time, 2) + zip_size_kb = round(zip_size / 1024) + + tag_desc = "" + if self.__tag: + tag_desc = f", under tag '{self.__tag}'" + + LOG.info("'%s' stored results (%s KB " + "/decompressed/) to run '%s' (id: %d) %s in " + "%s seconds.", self.user_name, + zip_size_kb, self.__name, run_id, tag_desc, + runtime) + + iso_start_time = datetime.fromtimestamp( + start_time).isoformat() + + log_msg = f"{iso_start_time}, " +\ + f"{runtime}s, " +\ + f'"{self.__product.name}", ' +\ + f'"{self.__name}", ' +\ + f"{zip_size_kb}KB, " +\ + f"{self.__report_count}, " +\ + f"{run_id}" + + STORE_TIME_LOG.info(log_msg) + + return run_id + except (sqlalchemy.exc.OperationalError, + sqlalchemy.exc.ProgrammingError) as ex: + raise codechecker_api_shared.ttypes.RequestFailed( + codechecker_api_shared.ttypes.ErrorCode.DATABASE, + "Storing reports to the database failed: {0}" + .format(ex)) except Exception as ex: LOG.error("Failed to store results: %s", ex) import traceback diff --git a/web/server/codechecker_server/api/report_server.py b/web/server/codechecker_server/api/report_server.py index be082d2577..3e2628b63a 100644 --- a/web/server/codechecker_server/api/report_server.py +++ b/web/server/codechecker_server/api/report_server.py @@ -22,21 +22,27 @@ from copy import deepcopy from collections import OrderedDict, defaultdict from datetime import datetime, timedelta -from typing import Dict, List, Optional, Set, Tuple +from typing import Any, Dict, List, Optional, Set, Tuple import sqlalchemy from sqlalchemy.sql.expression import or_, and_, not_, func, \ asc, desc, union_all, select, bindparam, literal_column +from sqlalchemy.orm import contains_eager import codechecker_api_shared from codechecker_api.codeCheckerDBAccess_v6 import constants, ttypes -from codechecker_api.codeCheckerDBAccess_v6.ttypes import AnalysisInfoFilter, \ - BlameData, BlameInfo, BugPathPos, CheckerCount, Commit, CommitAuthor, \ - CommentData, DiffType, Encoding, RunHistoryData, Order, ReportData, \ - ReportDetails, ReviewData, ReviewStatusRule, ReviewStatusRuleFilter, \ - ReviewStatusRuleSortMode, ReviewStatusRuleSortType, RunData, RunFilter, \ - RunReportCount, RunSortType, RunTagCount, SourceComponentData, \ - SourceFileData, SortMode, SortType, ExportData +from codechecker_api.codeCheckerDBAccess_v6.ttypes import \ + AnalysisInfoFilter, AnalysisInfoChecker as API_AnalysisInfoChecker, \ + BlameData, BlameInfo, BugPathPos, \ + CheckerCount, Commit, CommitAuthor, CommentData, \ + DiffType, \ + Encoding, ExportData, \ + Order, \ + ReportData, ReportDetails, ReviewData, ReviewStatusRule, \ + ReviewStatusRuleFilter, ReviewStatusRuleSortMode, \ + ReviewStatusRuleSortType, RunData, RunFilter, RunHistoryData, \ + RunReportCount, RunSortType, RunTagCount, \ + SourceComponentData, SourceFileData, SortMode, SortType from codechecker_common import util from codechecker_common.logger import get_logger @@ -51,10 +57,14 @@ from ..database.config_db_model import Product from ..database.database import conv, DBSession, escape_like from ..database.run_db_model import \ - AnalysisInfo, AnalyzerStatistic, BugPathEvent, BugReportPoint, \ - CleanupPlan, CleanupPlanReportHash, Comment, ReportAnnotations, \ - ExtendedReportData, File, FileContent, Report, ReportAnalysisInfo, \ - ReviewStatus, Run, RunHistory, RunHistoryAnalysisInfo, RunLock, \ + AnalysisInfo, AnalysisInfoChecker as DB_AnalysisInfoChecker, \ + AnalyzerStatistic, \ + BugPathEvent, BugReportPoint, \ + CleanupPlan, CleanupPlanReportHash, Checker, Comment, \ + ExtendedReportData, \ + File, FileContent, \ + Report, ReportAnnotations, ReportAnalysisInfo, ReviewStatus, \ + Run, RunHistory, RunHistoryAnalysisInfo, RunLock, \ SourceComponent from .thrift_enum_helper import detection_status_enum, \ @@ -138,6 +148,8 @@ def wrapper(*args, **kwargs): except sqlalchemy.exc.SQLAlchemyError as alchemy_ex: # Convert SQLAlchemy exceptions. msg = str(alchemy_ex) + import traceback + traceback.print_exc() raise codechecker_api_shared.ttypes.RequestFailed( codechecker_api_shared.ttypes.ErrorCode.DATABASE, msg) except codechecker_api_shared.ttypes.RequestFailed as rf: @@ -233,15 +245,22 @@ def process_report_filter( for cm in report_filter.checkerMsg] AND.append(or_(*OR)) - if report_filter.checkerName: - OR = [Report.checker_id.ilike(conv(cn)) - for cn in report_filter.checkerName] - AND.append(or_(*OR)) + if report_filter.analyzerNames or report_filter.checkerName \ + or report_filter.severity: + if report_filter.analyzerNames: + OR = [Checker.analyzer_name.ilike(conv(an)) + for an in report_filter.analyzerNames] + AND.append(or_(*OR)) - if report_filter.analyzerNames: - OR = [Report.analyzer_name.ilike(conv(an)) - for an in report_filter.analyzerNames] - AND.append(or_(*OR)) + if report_filter.checkerName: + OR = [Checker.checker_name.ilike(conv(cn)) + for cn in report_filter.checkerName] + AND.append(or_(*OR)) + + if report_filter.severity: + AND.append(Checker.severity.in_(report_filter.severity)) + + join_tables.append(Checker) if report_filter.runName: OR = [Run.name.ilike(conv(rn)) @@ -280,9 +299,6 @@ def process_report_filter( AND.append(or_(*OR)) - if report_filter.severity: - AND.append(Report.severity.in_(report_filter.severity)) - if report_filter.detectionStatus: dst = list(map(detection_status_str, report_filter.detectionStatus)) @@ -948,16 +964,26 @@ def create_review_data( isInSource=is_in_source) -def apply_report_filter(q, filter_expression, join_tables): +def apply_report_filter(q, filter_expression, + join_tables: List[Any], + already_joined_tables: Optional[List[Any]] = None): """ - Applies the given filter expression and joins the File, Run and RunHistory - tables if necessary based on join_tables parameter. + Applies the given filter expression and joins the Checker, File, Run, and + RunHistory tables if necessary based on join_tables parameter. If a table + is already joined by the main query and this is indicated, that will not + be joined by this function to prevent a "duplicate alias" error. """ - if File in join_tables: + def needs_join(tbl): + return tbl in join_tables and (already_joined_tables is None or + tbl not in already_joined_tables) + + if needs_join(Checker): + q = q.join(Checker, Report.checker_id == Checker.id) + if needs_join(File): q = q.outerjoin(File, Report.file_id == File.id) - if Run in join_tables: + if needs_join(Run): q = q.outerjoin(Run, Run.id == Report.run_id) - if RunHistory in join_tables: + if needs_join(RunHistory): q = q.outerjoin(RunHistory, RunHistory.run_id == Report.run_id) return q.filter(filter_expression) @@ -969,8 +995,8 @@ def get_sort_map(sort_types, is_unique=False): SortType.FILENAME: [(File.filepath, 'filepath'), (Report.line, 'line')], SortType.BUG_PATH_LENGTH: [(Report.path_length, 'bug_path_length')], - SortType.CHECKER_NAME: [(Report.checker_id, 'checker_id')], - SortType.SEVERITY: [(Report.severity, 'severity')], + SortType.CHECKER_NAME: [(Checker.checker_name, 'checker_name')], + SortType.SEVERITY: [(Checker.severity, 'severity')], SortType.REVIEW_STATUS: [(Report.review_status, 'rw_status')], SortType.DETECTION_STATUS: [(Report.detection_status, 'dt_status')], SortType.TIMESTAMP: [('annotation_timestamp', 'annotation_timestamp')]} @@ -1538,11 +1564,25 @@ def getAnalysisInfo(self, analysis_info_filter, limit, offset): .limit(limit).offset(offset) for cmd in analysis_info_query: - command = \ - zlib.decompress(cmd.analyzer_command).decode('utf-8') + checkers_q = session \ + .query(Checker.analyzer_name, + Checker.checker_name, + DB_AnalysisInfoChecker.enabled) \ + .join(Checker, DB_AnalysisInfoChecker.checker_id == + Checker.id) \ + .filter(DB_AnalysisInfoChecker. + analysis_info_id == cmd.id) + + checkers: Dict[str, Dict[str, API_AnalysisInfoChecker]] = \ + defaultdict(dict) + for chk in checkers_q.all(): + analyzer, checker, enabled = chk + checkers[analyzer][checker] = API_AnalysisInfoChecker( + enabled=enabled) res.append(ttypes.AnalysisInfo( - analyzerCommand=html.escape(command))) + analyzerCommand=html.escape(cmd.analyzer_command), + checkers=checkers)) return res @@ -1609,6 +1649,8 @@ def getReport(self, reportId): result = session \ .query(Report, File) \ .filter(Report.id == reportId) \ + .join(Checker, Report.checker_id == Checker.id) \ + .options(contains_eager(Report.checker)) \ .outerjoin(File, Report.file_id == File.id) \ .limit(1).one_or_none() @@ -1627,8 +1669,9 @@ def getReport(self, reportId): fileId=source_file.id, line=report.line, column=report.column, - checkerId=report.checker_id, - severity=report.severity, + analyzerName=report.checker.analyzer_name, + checkerId=report.checker.checker_name, + severity=report.checker.severity, reviewData=create_review_data( report.review_status, report.review_status_message, @@ -1768,10 +1811,10 @@ def getRunResults(self, run_ids, limit, offset, sort_types, # # reports # ================= - # id, severity, ... - # ----------------- - # 1, HIGH, ... - # 2, MEDIUM, ... + # id, checker_id, ... + # ------------------- + # 1, 123456, ... + # 2, 999999, ... # # report_annotations # ======================= @@ -1785,10 +1828,10 @@ def getRunResults(self, run_ids, limit, offset, sort_types, # # reports extended # =================================================== - # id, severity, ..., annotation_key1, annotation_key2 + # id, checker_id, ..., annotation_key1, annotation_key2 # --------------------------------------------------- - # 1, HIGH, ..., value1, value2 - # 2, MEDIUM, ..., value3, NULL + # 1, 123456, ..., value1, value2 + # 2, 999999, ..., value3, NULL # # The SQL query which results this table is similar to this: # @@ -1855,22 +1898,22 @@ def getRunResults(self, run_ids, limit, offset, sort_types, .group_by(Report.bug_id) \ .subquery() - # Sort the results - sorted_reports = \ - session.query(unique_reports.c.id) - + # Sort the results. + sorted_reports = session.query(unique_reports.c.id) sorted_reports = sort_results_query(sorted_reports, sort_types, sort_type_map, order_type_map, True) - sorted_reports = sorted_reports \ .limit(limit).offset(offset).subquery() q = session.query(Report, File.filename, *annotation_cols.values()) \ + .join(Checker, + Report.checker_id == Checker.id) \ + .options(contains_eager(Report.checker)) \ .outerjoin( File, Report.file_id == File.id) \ @@ -1897,7 +1940,7 @@ def getRunResults(self, run_ids, limit, offset, sort_types, sort_types, sort_type_map, order_type_map) - q = q.group_by(Report.id, File.id) + q = q.group_by(Report.id, File.id, Checker.id) query_result = q.all() @@ -1908,8 +1951,7 @@ def getRunResults(self, run_ids, limit, offset, sort_types, report_details = get_report_details(session, report_ids) for row in query_result: - report = row[0] - filename = row[1] + report, filename = row[0], row[1] annotations = { k: v for k, v in zip(annotation_keys, row[2:]) if v is not None} @@ -1930,8 +1972,9 @@ def getRunResults(self, run_ids, limit, offset, sort_types, fileId=report.file_id, line=report.line, column=report.column, - checkerId=report.checker_id, - severity=report.severity, + analyzerName=report.checker.analyzer_name, + checkerId=report.checker.checker_name, + severity=report.checker.severity, reviewData=review_data, detectionStatus=detection_status_enum( report.detection_status), @@ -1939,7 +1982,6 @@ def getRunResults(self, run_ids, limit, offset, sort_types, fixedAt=str(report.fixed_at), bugPathLength=report.path_length, details=report_details.get(report.id), - analyzerName=report.analyzer_name, annotations=annotations)) else: # not is_unique filter_expression, join_tables = process_report_filter( @@ -1952,24 +1994,34 @@ def getRunResults(self, run_ids, limit, offset, sort_types, q = session.query(Report, File.filepath, *annotation_cols.values()) \ + .join(Checker, + Report.checker_id == Checker.id) \ + .options(contains_eager(Report.checker)) \ + .outerjoin(File, + Report.file_id == File.id) \ .outerjoin( ReportAnnotations, Report.id == ReportAnnotations.report_id) - if File not in join_tables: - q = q.outerjoin(File, Report.file_id == File.id) - # Grouping by "reports.id" is described at the beginning of # this function. Grouping by "files.id" is necessary, because # "files" table is joined for gathering file names belonging to # the given report. According to SQL syntax if there is a group # by report IDs then files should also be either grouped or an - # aggregate function must be applied on them. - q = q.group_by(Report.id, File.id) - - q = apply_report_filter(q, filter_expression, join_tables) - - q = sort_results_query(q, sort_types, sort_type_map, + # aggregate function must be applied on them. The same applies + # to the "checkers" table. + q = q.group_by(Report.id, File.id, Checker.id) + + # The "Checker" entity is eagerly loaded for each "Report" as + # there is a guaranteed FOREIGN KEY ... NOT NULL relationship + # to a valid entity. Because of this, letting "join_tables" + # add "Checker" here is actually ill-formed, as it would + # result in queries that ambiguously refer to the same table. + q = apply_report_filter(q, filter_expression, join_tables, + [File, Checker]) + q = sort_results_query(q, + sort_types, + sort_type_map, order_type_map) if report_filter.annotations is not None: @@ -1993,8 +2045,7 @@ def getRunResults(self, run_ids, limit, offset, sort_types, report_details = get_report_details(session, report_ids) for row in query_result: - report = row[0] - filepath = row[1] + report, filepath = row[0], row[1] annotations = { k: v for k, v in zip(annotation_keys, row[2:]) if v is not None} @@ -2015,8 +2066,9 @@ def getRunResults(self, run_ids, limit, offset, sort_types, fileId=report.file_id, line=report.line, column=report.column, - checkerId=report.checker_id, - severity=report.severity, + analyzerName=report.checker.analyzer_name, + checkerId=report.checker.checker_name, + severity=report.checker.severity, reviewData=review_data, detectionStatus=detection_status_enum( report.detection_status), @@ -2025,7 +2077,6 @@ def getRunResults(self, run_ids, limit, offset, sort_types, report.fixed_at else None, bugPathLength=report.path_length, details=report_details.get(report.id), - analyzerName=report.analyzer_name, annotations=annotations)) return results @@ -2591,11 +2642,9 @@ def getCheckerLabels( """ Return the list of labels to each checker. """ labels = [] for checker in checkers: - # Analyzer default value in the database is 'unknown' which is not - # a valid analyzer name. So this code handles this use case. - analyzer_name = None - if checker.analyzerName != "unknown": - analyzer_name = checker.analyzerName + analyzer_name = None \ + if str(checker.analyzerName).lower() == "unknown" \ + else str(checker.analyzerName) labels.append(list(map( lambda x: f'{x[0]}:{x[1]}', @@ -2753,10 +2802,12 @@ def getCheckerCounts(self, run_ids, report_filter, cmp_data, limit, filter_expression, join_tables = process_report_filter( session, run_ids, report_filter, cmp_data) - extended_table = session.query( - Report.checker_id, - Report.severity, - Report.bug_id) + extended_table = session \ + .query(Report.bug_id, + Checker.checker_name, + Checker.severity) \ + .join(Checker, + Report.checker_id == Checker.id) if report_filter.annotations is not None: extended_table = extended_table.outerjoin( @@ -2765,18 +2816,19 @@ def getCheckerCounts(self, run_ids, report_filter, cmp_data, limit, extended_table = extended_table.group_by(Report.id) extended_table = apply_report_filter( - extended_table, filter_expression, join_tables) + extended_table, filter_expression, join_tables, [Checker]) extended_table = extended_table.subquery() if report_filter.isUnique: q = session.query( - func.max(extended_table.c.checker_id).label('checker_id'), - func.max(extended_table.c.severity).label('severity'), + func.max(extended_table.c.checker_name) + .label("checker_name"), + func.max(extended_table.c.severity).label("severity"), extended_table.c.bug_id) else: q = session.query( - extended_table.c.checker_id, + extended_table.c.checker_name, extended_table.c.severity, func.count(literal_column('*'))) @@ -2784,15 +2836,15 @@ def getCheckerCounts(self, run_ids, report_filter, cmp_data, limit, if report_filter.isUnique: q = q.group_by(extended_table.c.bug_id).subquery() - unique_checker_q = session.query(q.c.checker_id, + unique_checker_q = session.query(q.c.checker_name, func.max(q.c.severity), func.count(q.c.bug_id)) \ - .group_by(q.c.checker_id) \ - .order_by(q.c.checker_id) + .group_by(q.c.checker_name) \ + .order_by(q.c.checker_name) else: - unique_checker_q = q.group_by(extended_table.c.checker_id, + unique_checker_q = q.group_by(extended_table.c.checker_name, extended_table.c.severity) \ - .order_by(extended_table.c.checker_id) + .order_by(extended_table.c.checker_name) if limit: unique_checker_q = unique_checker_q.limit(limit).offset(offset) @@ -2822,9 +2874,11 @@ def getAnalyzerNameCounts(self, run_ids, report_filter, cmp_data, limit, filter_expression, join_tables = process_report_filter( session, run_ids, report_filter, cmp_data) - extended_table = session.query( - Report.analyzer_name, - Report.bug_id) + extended_table = session \ + .query(Checker.analyzer_name, + Report.bug_id) \ + .join(Checker, + Report.checker_id == Checker.id) if report_filter.annotations is not None: extended_table = extended_table.outerjoin( @@ -2833,7 +2887,7 @@ def getAnalyzerNameCounts(self, run_ids, report_filter, cmp_data, limit, extended_table = extended_table.group_by(Report.id) extended_table = apply_report_filter( - extended_table, filter_expression, join_tables) + extended_table, filter_expression, join_tables, [Checker]) extended_table = extended_table.subquery() @@ -2879,9 +2933,11 @@ def getSeverityCounts(self, run_ids, report_filter, cmp_data): filter_expression, join_tables = process_report_filter( session, run_ids, report_filter, cmp_data) - extended_table = session.query( - Report.severity, - Report.bug_id) + extended_table = session \ + .query(Report.bug_id, + Checker.severity) \ + .join(Checker, + Report.checker_id == Checker.id) if report_filter.annotations is not None: extended_table = extended_table.outerjoin( @@ -2890,13 +2946,13 @@ def getSeverityCounts(self, run_ids, report_filter, cmp_data): extended_table = extended_table.group_by(Report.id) extended_table = apply_report_filter( - extended_table, filter_expression, join_tables) + extended_table, filter_expression, join_tables, [Checker]) extended_table = extended_table.subquery() if report_filter.isUnique: q = session.query( - func.max(extended_table.c.severity).label('severity'), + func.max(extended_table.c.severity).label("severity"), extended_table.c.bug_id) else: q = session.query(extended_table.c.severity, diff --git a/web/server/codechecker_server/database/common.py b/web/server/codechecker_server/database/common.py new file mode 100644 index 0000000000..84272becc6 --- /dev/null +++ b/web/server/codechecker_server/database/common.py @@ -0,0 +1,155 @@ +# ------------------------------------------------------------------------- +# +# Part of the CodeChecker project, under the Apache License v2.0 with +# LLVM Exceptions. See LICENSE for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +# ------------------------------------------------------------------------- +""" +This module implements customised common types and associated operations +to be used in the ORM layer. +""" +import json +from typing import Any, AnyStr, Optional, Tuple, Union, cast +import zlib + +from sqlalchemy.types import LargeBinary, TypeDecorator + + +def to_zlib_tag_prefix(kind: str, compression_level: int) -> bytes: + if '@' in kind: + raise ValueError("'kind' must NOT contain a '@' as it breaks the " + "encoding format!") + return ("zlib[%d]:%s@" % (compression_level, kind)).encode() + + +def from_zlib_tag_prefix(buffer: bytes) -> Tuple[bytes, str, int]: + """ + Splits and parses the tag in a ZLib-compressed binary buffer. + Returns: + - The actual compressed payload, without the prefix. + - The kind of the object compressed within (user-defined tag). + - The compression ratio marker. + """ + split_marker = buffer.index('@'.encode()) + prefix, payload = buffer[:split_marker].decode(), buffer[split_marker + 1:] + + algorithm = prefix.split('[')[0] + if algorithm != "zlib": + raise ValueError("Compression tag '%s' does not match expected 'zlib'" + % algorithm) + + compression = int(prefix.split('[')[1].split(']')[0]) + kind = prefix.split(':')[1] + + return payload, kind, compression + + +def encode_zlib(value: AnyStr, + kind: Optional[str] = None, + compression_level=zlib.Z_BEST_COMPRESSION) -> bytes: + """ + Encodes the given 'value' string or buffer to a tagged ZLib-compressed + buffer. The buffer is then prefix-tagged with type and compression + information useful during raw data inspection or recovery. + """ + if kind is None: + if isinstance(value, str): + kind = "str" + elif isinstance(value, bytes): + kind = "blob" + kind = cast(str, kind) + + buffer: bytes = value.encode() if isinstance(value, str) else value + buffer = zlib.compress(buffer, compression_level) + + return to_zlib_tag_prefix(kind, compression_level) + buffer + + +def decode_zlib(buffer: bytes) -> Tuple[str, Union[str, bytes]]: + """ + Decodes the given 'buffer', which is a ZLib-compressed and prefixed data + as created by 'encode_zlib()'. Returns the 'kind' attribute that was saved + in the tagging information, and the decoded payload. + + If 'kind' is "str", the decoded payload will be decoded into a string. + Otherwise, it is returned as a raw 'bytes' buffer. + """ + buffer, kind, _ = from_zlib_tag_prefix(buffer) + buffer = zlib.decompress(buffer) + return kind, buffer.decode() if kind == "str" else buffer + + +class ZLibCompressedString(TypeDecorator): + """ + Stores arbitrary user-defined strings ('str') as a ZLib-compressed + binary datum. + """ + impl = LargeBinary + client_type = str + + compression_level = zlib.Z_BEST_COMPRESSION + + def process_bind_param(self, value: Optional[client_type], + dialect: str) -> Optional[impl]: + """ + Transform a bound parameter of a client-side query value to the value + of the underlying implementation type, i.e., Python -> Database. + """ + if value is None: + return None + + return cast(LargeBinary, encode_zlib(value)) + + def process_result_value(self, value: Optional[impl], + dialect: str) -> Optional[client_type]: + """ + Transforms a value obtained from the underlying implementation type + to the value (and type) expected by client code, i.e., + Database -> Python. + """ + if value is None: + return None + + _, decoded = decode_zlib(cast(bytes, value)) + return cast(str, decoded) + + +class ZLibCompressedJSON(TypeDecorator): + """ + Stores an arbitrary JSON object as a serialised ZLib-compressed binary + datum. + """ + + # Note that since SQLAlchemy 0.6, types.Binary is deprecated and + # automatically redirects to types.LargeBinary instead. + impl = LargeBinary + client_type = Any + + compression_level = zlib.Z_BEST_COMPRESSION + + def process_bind_param(self, value: Optional[client_type], + dialect: str) -> Optional[impl]: + """ + Transform a bound parameter of a client-side query value to the value + of the underlying implementation type, i.e., Python -> Database. + """ + if value is None: + return None + + serialised = json.dumps(value, sort_keys=True) + return cast(LargeBinary, encode_zlib(serialised)) + + def process_result_value(self, value: Optional[impl], + dialect: str) -> Optional[client_type]: + """ + Transforms a value obtained from the underlying implementation type + to the value (and type) expected by client code, i.e., + Database -> Python. + """ + if value is None: + return None + + _, decoded = decode_zlib(cast(bytes, value)) + serialised = cast(str, decoded) + return json.loads(serialised) diff --git a/web/server/codechecker_server/database/config_db_model.py b/web/server/codechecker_server/database/config_db_model.py index 376162d90c..bfccb91eb6 100644 --- a/web/server/codechecker_server/database/config_db_model.py +++ b/web/server/codechecker_server/database/config_db_model.py @@ -11,8 +11,8 @@ from datetime import datetime import sys -from sqlalchemy import MetaData, Column, Integer, Enum, String, Boolean, \ - ForeignKey, CHAR, DateTime, Text +from sqlalchemy import Boolean, CHAR, Column, DateTime, Enum, ForeignKey, \ + Integer, MetaData, String, Text from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.sql.expression import false, true diff --git a/web/server/codechecker_server/database/database.py b/web/server/codechecker_server/database/database.py index 4043947be4..2faa4891fa 100644 --- a/web/server/codechecker_server/database/database.py +++ b/web/server/codechecker_server/database/database.py @@ -203,6 +203,8 @@ def _create_schema(self): except Exception as ex: LOG.error("Failed to create initial database schema") LOG.error(ex) + import traceback + traceback.print_exc() return False def get_schema_version(self): @@ -328,10 +330,14 @@ def upgrade(self): return DBStatus.OK except sqlalchemy.exc.SQLAlchemyError as alch_err: + import traceback + traceback.print_exc() LOG.error(str(alch_err)) return DBStatus.SCHEMA_UPGRADE_FAILED except CommandError as cerr: + import traceback + traceback.print_exc() LOG.debug(str(cerr)) return DBStatus.SCHEMA_UPGRADE_FAILED diff --git a/web/server/codechecker_server/database/db_cleanup.py b/web/server/codechecker_server/database/db_cleanup.py index c0c54afd25..342bfbafb4 100644 --- a/web/server/codechecker_server/database/db_cleanup.py +++ b/web/server/codechecker_server/database/db_cleanup.py @@ -12,7 +12,6 @@ from datetime import datetime, timedelta import sqlalchemy -from sqlalchemy.sql.expression import bindparam, union_all, select, cast from codechecker_api.codeCheckerDBAccess_v6.ttypes import Severity @@ -20,26 +19,50 @@ from codechecker_common.logger import get_logger from .database import DBSession -from .run_db_model import AnalysisInfo, BugPathEvent, BugReportPoint, \ - Comment, File, FileContent, Report, ReportAnalysisInfo, \ - RunHistoryAnalysisInfo, RunLock +from .run_db_model import \ + AnalysisInfo, \ + BugPathEvent, BugReportPoint, \ + Comment, Checker, \ + File, FileContent, \ + Report, ReportAnalysisInfo, RunHistoryAnalysisInfo, RunLock LOG = get_logger('server') RUN_LOCK_TIMEOUT_IN_DATABASE = 30 * 60 # 30 minutes. SQLITE_LIMIT_COMPOUND_SELECT = 500 -def remove_expired_run_locks(session_maker): - LOG.debug("Garbage collection of expired run locks started...") +def remove_expired_data(session_maker): + """ Remove information that has timed out from the database. """ + remove_expired_run_locks(session_maker) + + +def remove_unused_data(session_maker): + """ Remove dangling data (files, comments, etc.) from the database. """ + remove_unused_files(session_maker) + remove_unused_comments(session_maker) + remove_unused_analysis_info(session_maker) + + +def update_contextual_data(session_maker, context): + """ + Updates information in the database that comes from potentially changing + contextual configuration of the server package. + """ + upgrade_severity_levels(session_maker, context.checker_labels) + +def remove_expired_run_locks(session_maker): with DBSession(session_maker) as session: + LOG.debug("Garbage collection of expired run locks started...") try: locks_expired_at = datetime.now() - timedelta( seconds=RUN_LOCK_TIMEOUT_IN_DATABASE) - session.query(RunLock) \ + count = session.query(RunLock) \ .filter(RunLock.locked_at < locks_expired_at) \ .delete(synchronize_session=False) + if count: + LOG.debug("%d expired run locks deleted.", count) session.commit() @@ -50,8 +73,6 @@ def remove_expired_run_locks(session_maker): def remove_unused_files(session_maker): - LOG.debug("Garbage collection of dangling files started...") - # File deletion is a relatively slow operation due to database cascades. # Removing files in big chunks prevents reaching a potential database # statement timeout. This hard-coded value is a safe choice according to @@ -61,6 +82,7 @@ def remove_unused_files(session_maker): CHUNK_SIZE = 500000 with DBSession(session_maker) as session: + LOG.debug("Garbage collection of dangling files started...") try: bpe_files = session.query(BugPathEvent.file_id) \ .group_by(BugPathEvent.file_id) \ @@ -73,10 +95,16 @@ def remove_unused_files(session_maker): .filter(File.id.notin_(bpe_files), File.id.notin_(brp_files)) files_to_delete = map(lambda x: x[0], files_to_delete) + total_count = 0 for chunk in util.chunks(iter(files_to_delete), CHUNK_SIZE): - session.query(File) \ - .filter(File.id.in_(chunk)) \ - .delete(synchronize_session=False) + q = session.query(File) \ + .filter(File.id.in_(chunk)) + count = q.delete(synchronize_session=False) + if count: + total_count += count + + if total_count: + LOG.debug("%d dangling files deleted.", total_count) files = session.query(File.content_hash) \ .group_by(File.content_hash) \ @@ -94,26 +122,20 @@ def remove_unused_files(session_maker): LOG.error("Failed to remove unused files: %s", str(ex)) -def remove_unused_data(session_maker): - """ Remove dangling data (files, comments, etc.) from the database. """ - remove_unused_files(session_maker) - remove_unused_comments(session_maker) - remove_unused_analysis_info(session_maker) - - def remove_unused_comments(session_maker): """ Remove dangling comments from the database. """ - LOG.debug("Garbage collection of dangling comments started...") - with DBSession(session_maker) as session: + LOG.debug("Garbage collection of dangling comments started...") try: report_hashes = session.query(Report.bug_id) \ .group_by(Report.bug_id) \ .subquery() - session.query(Comment) \ + count = session.query(Comment) \ .filter(Comment.bug_hash.notin_(report_hashes)) \ .delete(synchronize_session=False) + if count: + LOG.debug("%d dangling comments deleted.", count) session.commit() @@ -123,78 +145,6 @@ def remove_unused_comments(session_maker): LOG.error("Failed to remove dangling comments: %s", str(ex)) -def upgrade_severity_levels(session_maker, checker_labels): - """ - Updates the potentially changed severities at the reports. - """ - LOG.debug("Upgrading severity levels started...") - - severity_map = {} - for checker in checker_labels.checkers(): - severity_map[checker] = checker_labels.severity(checker) - - for severity_map_small in util.chunks( - iter(severity_map.items()), SQLITE_LIMIT_COMPOUND_SELECT): - severity_map_small = dict(severity_map_small) - - with DBSession(session_maker) as session: - try: - # Create a sql query from the severity map. - severity_map_q = union_all(*[ - select([cast(bindparam('checker_id' + str(i), - str(checker_id)) - .label('checker_id'), sqlalchemy.String), - cast(bindparam('severity' + str(i), - Severity._NAMES_TO_VALUES[ - severity_map_small[checker_id]]) - .label('severity'), sqlalchemy.Integer)]) - for i, checker_id in enumerate(severity_map_small)]) \ - .alias('new_severities') - - checker_ids = list(severity_map_small.keys()) - - # Get checkers which has been changed. - changed_checker_q = select( - [Report.checker_id, Report.severity]) \ - .group_by(Report.checker_id, Report.severity) \ - .where(Report.checker_id.in_(checker_ids)) \ - .except_(session.query(severity_map_q)) \ - .alias('changed_severites') - - changed_checkers = session.query( - changed_checker_q.c.checker_id, - changed_checker_q.c.severity) - - # Update severity levels of checkers. - if changed_checkers: - updated_checker_ids = set() - for checker_id, severity_old in changed_checkers: - severity_new = severity_map_small[checker_id] - severity_id = Severity._NAMES_TO_VALUES[severity_new] - - LOG.info("Upgrading severity level of '%s' checker " - "from %s to %s", - checker_id, - Severity._VALUES_TO_NAMES[severity_old], - severity_new) - - if checker_id in updated_checker_ids: - continue - - session.query(Report) \ - .filter(Report.checker_id == checker_id) \ - .update({Report.severity: severity_id}) - - updated_checker_ids.add(checker_id) - - session.commit() - - LOG.debug("Upgrading of severity levels finished...") - except (sqlalchemy.exc.OperationalError, - sqlalchemy.exc.ProgrammingError) as ex: - LOG.error("Failed to upgrade severity levels: %s", str(ex)) - - def remove_unused_analysis_info(session_maker): """ Remove unused analysis information from the database. """ # Analysis info deletion is a relatively slow operation due to database @@ -203,9 +153,8 @@ def remove_unused_analysis_info(session_maker): # according to some measurements. CHUNK_SIZE = 500 - LOG.debug("Garbage collection of dangling analysis info started...") - with DBSession(session_maker) as session: + LOG.debug("Garbage collection of dangling analysis info started...") try: to_delete = session.query(AnalysisInfo.id) \ .join( @@ -223,13 +172,70 @@ def remove_unused_analysis_info(session_maker): to_delete = map(lambda x: x[0], to_delete) + total_count = 0 for chunk in util.chunks(to_delete, CHUNK_SIZE): - session.query(AnalysisInfo) \ + count = session.query(AnalysisInfo) \ .filter(AnalysisInfo.id.in_(chunk)) \ .delete(synchronize_session=False) - session.commit() + if count: + total_count += count + + if total_count: + LOG.debug("%d dangling analysis info deleted.", total_count) + + session.commit() LOG.debug("Garbage collection of dangling analysis info finished.") except (sqlalchemy.exc.OperationalError, sqlalchemy.exc.ProgrammingError) as ex: LOG.error("Failed to remove dangling analysis info: %s", str(ex)) + + +def upgrade_severity_levels(session_maker, checker_labels): + """ + Updates the potentially changed severities at the reports. + """ + LOG.debug("Upgrading severity levels started...") + + with DBSession(session_maker) as session: + try: + count = 0 + for analyzer in sorted(checker_labels.get_analyzers()): + checkers_for_analyzer_in_database = session \ + .query(Checker.id, + Checker.checker_name, + Checker.severity) \ + .filter(Checker.analyzer_name == analyzer) \ + .all() + for checker_row in checkers_for_analyzer_in_database: + checker: str = checker_row.checker_name + old_severity_db: int = checker_row.severity + old_severity: str = \ + Severity._VALUES_TO_NAMES[old_severity_db] + new_severity: str = \ + checker_labels.severity(checker, analyzer) + new_severity_db: int = \ + Severity._NAMES_TO_VALUES[new_severity] + + if old_severity_db == new_severity_db: + continue + + LOG.info("Upgrading the severity level of checker " + "'%s/%s' from '%s' (%d) to '%s' (%d).", + analyzer, checker, + old_severity, old_severity_db, + new_severity, new_severity_db) + session.query(Checker) \ + .filter(Checker.id == checker_row.id) \ + .update({Checker.severity: new_severity_db}) + count += 1 + + if count: + LOG.debug("%d checker severities upgraded.", count) + + session.commit() + except (sqlalchemy.exc.OperationalError, + sqlalchemy.exc.ProgrammingError) as ex: + LOG.error("Failed to upgrade severity levels: %s", str(ex)) + + LOG.debug("Upgrading severity levels finished.") diff --git a/web/server/codechecker_server/database/run_db_model.py b/web/server/codechecker_server/database/run_db_model.py index 8862d1eed4..f41373052f 100644 --- a/web/server/codechecker_server/database/run_db_model.py +++ b/web/server/codechecker_server/database/run_db_model.py @@ -11,6 +11,7 @@ from datetime import datetime, timedelta from math import ceil import os +from typing import Optional from sqlalchemy import Boolean, Column, DateTime, Enum, ForeignKey, Integer, \ LargeBinary, MetaData, String, UniqueConstraint, Table, Text @@ -18,6 +19,8 @@ from sqlalchemy.orm import relationship from sqlalchemy.sql.expression import true, false +from .common import ZLibCompressedString + CC_META = MetaData(naming_convention={ "ix": 'ix_%(column_0_label)s', "uq": "uq_%(table_name)s_%(column_0_name)s", @@ -30,13 +33,62 @@ Base = declarative_base(metadata=CC_META) +class Checker(Base): + """ + Records of a look-up table that associates a product-global ID for each + analyzer name and checker name encountered. + """ + __tablename__ = "checkers" + + id = Column(Integer, autoincrement=True, primary_key=True) + analyzer_name = Column(String) + checker_name = Column(String) + severity = Column(Integer, index=True) + + __table_args__ = ( + UniqueConstraint("analyzer_name", "checker_name"), + ) + + def __init__(self, analyzer_name: str, checker_name: str, severity: int): + self.analyzer_name = analyzer_name + self.checker_name = checker_name + self.severity = severity + + +class AnalysisInfoChecker(Base): + __tablename__ = "analysis_info_checkers" + + analysis_info_id = Column(Integer, + ForeignKey("analysis_info.id", + deferrable=True, + initially="DEFERRED", + ondelete="CASCADE"), + primary_key=True) + checker_id = Column(Integer, + ForeignKey("checkers.id", + deferrable=True, + initially="DEFERRED", + ondelete="RESTRICT"), + primary_key=True) + enabled = Column(Boolean) + + def __init__(self, + analysis_info: "AnalysisInfo", + checker: Checker, + is_enabled: bool): + self.analysis_info_id = analysis_info.id + self.checker_id = checker.id + self.enabled = is_enabled + + class AnalysisInfo(Base): - __tablename__ = 'analysis_info' + __tablename__ = "analysis_info" id = Column(Integer, autoincrement=True, primary_key=True) - analyzer_command = Column(LargeBinary) + analyzer_command = Column(ZLibCompressedString) + available_checkers = relationship(AnalysisInfoChecker, uselist=True) - def __init__(self, analyzer_command): + def __init__(self, analyzer_command: str): self.analyzer_command = analyzer_command @@ -352,16 +404,17 @@ class Report(Base): ondelete='CASCADE'), index=True) bug_id = Column(String, index=True) - checker_id = Column(String) - checker_cat = Column(String) - bug_type = Column(String) - severity = Column(Integer) + checker_id = Column(Integer, ForeignKey("checkers.id", + deferrable=False, + ondelete="RESTRICT"), + nullable=False, + index=True) + checker = relationship(Checker, innerjoin=True, lazy="joined", + foreign_keys=[checker_id]) + line = Column(Integer) column = Column(Integer) path_length = Column(Integer) - analyzer_name = Column(String, - nullable=False, - server_default="unknown") # TODO: multiple messages to multiple source locations? checker_message = Column(String) @@ -403,32 +456,38 @@ class Report(Base): annotations = relationship("ReportAnnotations") - # Priority/severity etc... - def __init__(self, run_id, bug_id, file_id, checker_message, checker_id, - checker_cat, bug_type, line, column, severity, review_status, - review_status_author, review_status_message, - review_status_date, review_status_is_in_source, - detection_status, detection_date, path_length, - analyzer_name=None): - self.run_id = run_id + def __init__(self, + file_id: int, + run_id: int, + bug_id: Optional[str], + checker: Checker, + line: int, + column: int, + path_length: int, + checker_message: str, + detection_status, + review_status, + review_status_author: Optional[str], + review_status_message: Optional[bytes], + review_status_date: Optional[datetime], + review_status_is_in_source: bool, detection_date: datetime, + fixed_date: Optional[datetime]): self.file_id = file_id + self.run_id = run_id self.bug_id = bug_id + self.checker = checker + self.line = line + self.column = column + self.path_length = path_length self.checker_message = checker_message - self.severity = severity - self.checker_id = checker_id - self.checker_cat = checker_cat - self.bug_type = bug_type + self.detection_status = detection_status self.review_status = review_status self.review_status_author = review_status_author self.review_status_message = review_status_message self.review_status_date = review_status_date self.review_status_is_in_source = review_status_is_in_source - self.detection_status = detection_status - self.line = line - self.column = column self.detected_at = detection_date - self.path_length = path_length - self.analyzer_name = analyzer_name + self.fixed_at = fixed_date class ReportAnnotations(Base): diff --git a/web/server/codechecker_server/metadata.py b/web/server/codechecker_server/metadata.py index 6c9e69977d..13aef8b2fc 100644 --- a/web/server/codechecker_server/metadata.py +++ b/web/server/codechecker_server/metadata.py @@ -9,7 +9,7 @@ Helpers to parse metadata.json file. """ -from typing import Any, Dict, List, Optional, Set, Union +from typing import Any, Dict, Iterable, List, Optional, Set, cast import os from codechecker_common.logger import get_logger @@ -20,13 +20,18 @@ AnalyzerStatistics = Any +AnalyzerList = List[str] CheckCommands = List[str] CheckDurations = List[float] +CheckerNamesView = Iterable[str] CheckerToAnalyzer = Dict[str, str] CodeCheckerVersion = Optional[str] DisabledCheckers = Set[str] EnabledCheckers = Set[str] -MetadataCheckers = Dict[str, Union[Dict[str, bool], List[str]]] +# Checker name to enabled status. +MetadataCheckerInfo = Dict[str, bool] +# Analyzer to checker info. +MetadataCheckers = Dict[str, MetadataCheckerInfo] def checker_is_unavailable( @@ -61,32 +66,29 @@ def __init__(self, metadata_file_path): self.disabled_checkers: DisabledCheckers = set() self.checker_to_analyzer: CheckerToAnalyzer = dict() - self.__metadata_dict = {} + self.__metadata_dict: Dict[str, Any] = {} if os.path.isfile(metadata_file_path): - self.__metadata_dict = load_json(metadata_file_path, {}) + self.__metadata_dict = cast(Dict[str, Any], + load_json(metadata_file_path, {})) if 'version' in self.__metadata_dict: self.__process_metadata_info_v2() else: self.__process_metadata_info_v1() + self.analyzers: AnalyzerList = list(self.checkers.keys()) + def __process_metadata_checkers(self): """ Get enabled/disabled checkers and a checker to analyze dictionary. """ for analyzer_name, analyzer_checkers in self.checkers.items(): - if isinstance(analyzer_checkers, dict): - for checker_name, enabled in analyzer_checkers.items(): - self.checker_to_analyzer[checker_name] = analyzer_name - if enabled: - self.enabled_checkers.add(checker_name) - else: - self.disabled_checkers.add(checker_name) - else: - self.enabled_checkers.update(analyzer_checkers) - - for checker_name in analyzer_checkers: - self.checker_to_analyzer[checker_name] = analyzer_name + for checker_name, enabled in analyzer_checkers.items(): + self.checker_to_analyzer[checker_name] = analyzer_name + if enabled: + self.enabled_checkers.add(checker_name) + else: + self.disabled_checkers.add(checker_name) def __process_metadata_info_v1(self): """ Set metadata information from the old version json file. """ @@ -109,6 +111,24 @@ def __process_metadata_info_v1(self): # Get analyzer checkers. self.checkers = self.__metadata_dict.get('checkers', {}) + for analyzer, checkers in self.checkers.items(): + if isinstance(checkers, list): + # Version 1 metadata files originally only stored the list of + # enabled checkers grouped by analyzer. This was true from at + # least September 2017 + # (commit 7254d05a8b7262e4979ac613f32d6c3e0aa0d3cc) all the + # way to March 2020 + # (commit bd775d60950d48884b8f1dc83d8b82653b83cfa3). Before + # the official introduction of "v2" files, in December 2019, + # (commit 0cd28acac7d31e4a0260c147b8e803b7a36908f0) the + # ability to store the enabled status (bool) of a checker + # was added. However, the mismatch between the representation + # types in old formats are causing all sorts of troubles when + # getting the 'checkers' data structure, so instead, represent + # the structure with the new format even for a "v1" file. + # (See web/tests/functional/report_viewer_api) + self.checkers[analyzer] = {checker: True + for checker in checkers} self.__process_metadata_checkers() def __insert_analyzer_statistics( diff --git a/web/server/codechecker_server/migrations/common.py b/web/server/codechecker_server/migrations/common.py new file mode 100644 index 0000000000..6160718801 --- /dev/null +++ b/web/server/codechecker_server/migrations/common.py @@ -0,0 +1,100 @@ +# ------------------------------------------------------------------------- +# +# Part of the CodeChecker project, under the Apache License v2.0 with +# LLVM Exceptions. See LICENSE for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +# ------------------------------------------------------------------------- +from typing import Tuple, cast +import zlib + +from codechecker_server.database.common import decode_zlib, encode_zlib + + +def raw_zlib_encode_buf(value: bytes) -> bytes: + """ + Encodes the given 'value' binary buffer with ZLib's Z_BEST_COMPRESSION. + """ + return zlib.compress(value, zlib.Z_BEST_COMPRESSION) + + +def raw_zlib_decode_buf(value: bytes) -> bytes: + """ + Decodes the given ZLib-compressed 'value' into a binary buffer. + """ + return zlib.decompress(value) + + +def raw_zlib_decode_str(value: bytes) -> str: + """ + Decodes the given ZLib-compressed 'value' into a string. + """ + return raw_zlib_decode_buf(value).decode("utf-8", errors="ignore") + + +def recompress_zlib_as_tagged(value: bytes, kind: str = "str") -> bytes: + """ + Recompresses the given raw ZLib-compressed 'value' by tagging it with the + 'kind' to be usable with the 'ZLibCompressed*' BLOB column adaptors. + + This method always encodes using Z_BEST_COMPRESSION as the compression + strategy. + """ + try: + raw = raw_zlib_decode_str(value).encode() + kind = "str" + except UnicodeDecodeError: + raw = raw_zlib_decode_buf(value) + kind = "blob" + + return encode_zlib(raw, kind) + + +def recompress_zlib_as_untagged(value: bytes) -> bytes: + """ + Recompresses the given tagged and ZLib-compressed 'value' as a raw + ZLib-compressed binary buffer without any additional tags. + + This method always encodes using Z_BEST_COMPRESSION as the compression + strategy. + """ + kind, payload = decode_zlib(value) + payload = cast(bytes, + cast(str, payload).encode() if kind == "str" else payload) + + return raw_zlib_encode_buf(payload) + + +def recompress_zlib_as_tagged_exact_ratio( + value: bytes, + kind: str = "str" +) -> Tuple[int, bytes]: + """ + Recompresses the given raw ZLib-compressed 'value' by tagging it with the + 'kind' to be usable with the 'ZLibCompressed*' BLOB column adaptors. + + This method is more costly as it searches for the exact compression ratio + that was originally used by performing up to 11 rounds of re-encoding + internally until the compression ratio is figured out. Unfortunately, there + are no good and deterministic ways to recover this information in a single + go. + + The exact compression ratio might not be found, e.g., if the zlib version + used to encode the original 'value' no longer matches what is available + on the current machine, and all possible compression ratios produce a + different result than originally present. In this case, Z_BEST_COMPRESSION + will be used for the re-compressed buffer. + """ + data = raw_zlib_decode_buf(value) + + def _attempt(level: int) -> bool: + return zlib.compress(data, level) == value + + level_to_use = zlib.Z_BEST_COMPRESSION + for compression_level in reversed(range(-1, 10)): + if _attempt(compression_level): + # Found a matching compression ratio, use this one. + level_to_use = compression_level + break + + return level_to_use, encode_zlib(data, kind, level_to_use) diff --git a/web/server/codechecker_server/migrations/config/README b/web/server/codechecker_server/migrations/config/README deleted file mode 100644 index 98e4f9c44e..0000000000 --- a/web/server/codechecker_server/migrations/config/README +++ /dev/null @@ -1 +0,0 @@ -Generic single-database configuration. \ No newline at end of file diff --git a/web/server/codechecker_server/migrations/config/versions/4db450cf38af_add_extra_product_detail_columns.py b/web/server/codechecker_server/migrations/config/versions/4db450cf38af_add_extra_product_detail_columns.py index a1b6cbef61..0226b3954a 100644 --- a/web/server/codechecker_server/migrations/config/versions/4db450cf38af_add_extra_product_detail_columns.py +++ b/web/server/codechecker_server/migrations/config/versions/4db450cf38af_add_extra_product_detail_columns.py @@ -12,18 +12,19 @@ branch_labels = None depends_on = None +from logging import getLogger + from alembic import op import sqlalchemy as sa -from codechecker_common.logger import get_logger from codechecker_server.database import database from codechecker_server.database.run_db_model import IDENTIFIER as RUN_META from codechecker_web.shared import webserver_context -LOG = get_logger('system') def upgrade(): + LOG = getLogger("migration") op.add_column( 'products', sa.Column( diff --git a/web/server/codechecker_server/migrations/report/README b/web/server/codechecker_server/migrations/report/README deleted file mode 100644 index 98e4f9c44e..0000000000 --- a/web/server/codechecker_server/migrations/report/README +++ /dev/null @@ -1 +0,0 @@ -Generic single-database configuration. \ No newline at end of file diff --git a/web/server/codechecker_server/migrations/report/env.py b/web/server/codechecker_server/migrations/report/env.py index 40bfb4466e..3fe916d2c8 100644 --- a/web/server/codechecker_server/migrations/report/env.py +++ b/web/server/codechecker_server/migrations/report/env.py @@ -5,15 +5,15 @@ # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # # ------------------------------------------------------------------------- - +import logging import os import sys from alembic import context from sqlalchemy import engine_from_config, pool -# This is the Alembic Config object, which provides -# access to the values within the .ini file in use. +# This is the Alembic Config object, which provides access to the values +# within the .ini file in use. config = context.config # Add model's MetaData object here for 'autogenerate' support. @@ -24,11 +24,53 @@ target_metadata = Base.metadata +# Other values from the config, defined by the needs of env.py, can be +# acquired: my_important_option = config.get_main_option("my_important_option") + + +class MigrationFormatter(logging.Formatter): + """ + Truncates the filename to show only the revision that is being migrated + in the log output. + """ + def __init__(self): + super().__init__(fmt="[%(levelname)s][%(asctime)s] " + "{migration/report} " + "[%(schemaVersion)s]:%(lineno)d " + "- %(message)s", + datefmt="%Y-%m-%d %H:%M:%S") + + def format(self, record): + record.schemaVersion = record.filename[:record.filename.find("_")] + return super().format(record) + + +def setup_logger(): + """ + Set up a logging system that should be used during schema migration. + These outputs are not affected by the environment that executes a migration, + e.g., by the running CodeChecker server! + + In migration scripts, use the built-in logging facilities instead of + CodeChecker's wrapper, and ensure that the name of the logger created + exactly matches "migration"! + """ + fmt = MigrationFormatter() + handler = logging.StreamHandler() + handler.setFormatter(fmt) + handler.setLevel(logging.INFO) + handler.setStream(sys.stdout) + + # Use the default logging class that came with Python, temporarily turning + # away from potentially existing global changes. + existing_logger_cls = logging.getLoggerClass() + logging.setLoggerClass(logging.Logger) + logger = logging.getLogger("migration") + logging.setLoggerClass(existing_logger_cls) + + logger.setLevel(logging.INFO) + logger.addHandler(handler) -# Other values from the config, defined by the needs of env.py, -# can be acquired: -# my_important_option = config.get_main_option("my_important_option") -# ... etc. def run_migrations_offline(): """Run migrations in 'offline' mode. @@ -71,7 +113,7 @@ def run_migrations_online(): with context.begin_transaction(): context.run_migrations() - +setup_logger() if context.is_offline_mode(): run_migrations_offline() else: diff --git a/web/server/codechecker_server/migrations/report/versions/c3dad71f8e6b_store_information_about_enabled_and_disabled_checkers_for_a_run.py b/web/server/codechecker_server/migrations/report/versions/c3dad71f8e6b_store_information_about_enabled_and_disabled_checkers_for_a_run.py new file mode 100644 index 0000000000..c8da19121c --- /dev/null +++ b/web/server/codechecker_server/migrations/report/versions/c3dad71f8e6b_store_information_about_enabled_and_disabled_checkers_for_a_run.py @@ -0,0 +1,448 @@ +"""Store information about enabled and disabled checkers for a run + +Revision ID: c3dad71f8e6b +Revises: 9d956a0fae8d +Create Date: 2023-10-20 14:11:48.371981 + +""" + +# revision identifiers, used by Alembic. +revision = 'c3dad71f8e6b' +down_revision = '9d956a0fae8d' +branch_labels = None +depends_on = None + + +from logging import getLogger +from typing import Dict, List, Set, Tuple + +from alembic import op +import sqlalchemy as sa +from sqlalchemy.orm import Session +from sqlalchemy.ext.automap import automap_base + +from codechecker_common.util import progress +from codechecker_server.migrations.common import \ + recompress_zlib_as_untagged, recompress_zlib_as_tagged_exact_ratio + + +def upgrade(): + # Note: The instantiation of the LOG variable *MUST* stay here so that it + # uses the facilities that are sourced from the Alembic env.py. + # Symbols created on the module-level are created *before* Alembic's env.py + # had loaded. + LOG = getLogger("migration") + dialect = op.get_context().dialect.name + conn = op.get_bind() + + def upgrade_analysis_info(): + # Upgrade the contents of the existing columns in AnalysisInfo to the + # new ZLibCompressed format. + # + # Note: The underlying type of ZLibCompressedString is still + # LargeBinary in this revision, so the Column type itself needs not be + # modified, only the content values need migration. + Base = automap_base() + Base.prepare(conn, reflect=True) + # 'analysis_info' is the table! + AnalysisInfo = Base.classes.analysis_info + + db = Session(bind=conn) + count = db.query(AnalysisInfo.id).count() + if count: + def _print_progress(index: int, percent: float): + LOG.info("[%d/%d] Upgrading 'analysis_info'... %.0f%% done.", + index, count, percent) + + LOG.info("Preparing to upgrade %d 'analysis_info'...", count) + for analysis_info in progress(db.query(AnalysisInfo).all(), count, + 100 // 5, + callback=_print_progress): + if analysis_info.analyzer_command is None: + continue + _, new_analyzer_command = recompress_zlib_as_tagged_exact_ratio( + analysis_info.analyzer_command) + db.query(AnalysisInfo) \ + .filter(AnalysisInfo.id == analysis_info.id) \ + .update({"analyzer_command": new_analyzer_command}, + synchronize_session=False) + db.commit() + LOG.info("Done upgrading 'analysis_info'.") + + def create_new_tables(): + op.create_table( + "checkers", + sa.Column("id", sa.Integer(), autoincrement=True, nullable=False), + sa.Column("analyzer_name", sa.String(), nullable=True), + sa.Column("checker_name", sa.String(), nullable=True), + sa.Column("severity", sa.Integer()), + sa.PrimaryKeyConstraint("id", name=op.f("pk_checkers")), + sa.UniqueConstraint("analyzer_name", "checker_name", + name=op.f("uq_checkers_analyzer_name")) + ) + op.create_index(op.f("ix_checkers_severity"), + "checkers", + ["severity"], + unique=False + ) + op.create_table( + "analysis_info_checkers", + sa.Column("analysis_info_id", sa.Integer(), nullable=False), + sa.Column("checker_id", sa.Integer(), nullable=False), + sa.Column("enabled", sa.Boolean(), nullable=True), + sa.ForeignKeyConstraint( + ["analysis_info_id"], ["analysis_info.id"], + name=op.f("fk_analysis_info_checkers_analysis_info_id_analysis_info"), + ondelete="CASCADE", initially="DEFERRED", + deferrable=True), + sa.ForeignKeyConstraint( + ["checker_id"], ["checkers.id"], + name=op.f("fk_analysis_info_checkers_checker_id_checkers"), + ondelete="RESTRICT", initially="DEFERRED", deferrable=True), + sa.PrimaryKeyConstraint("analysis_info_id", "checker_id", + name=op.f("pk_analysis_info_checkers")) + ) + + def add_new_checker_id_column(): + # Upgrade the 'reports' table to use the 'checkers' foreign look-up + # instead of containing the strings allocated locally with the record. + col_reports_checker_id = sa.Column("checker_id", sa.Integer(), + nullable=False, server_default="0") + + if dialect == "sqlite": + with op.batch_alter_table("reports", recreate="never") as ba: + ba.alter_column("checker_id", new_column_name="checker_name") + # Due to the rename reusing the previous name, these can't merge. + with op.batch_alter_table("reports", recreate="never") as ba: + ba.add_column(col_reports_checker_id) + else: + op.alter_column("reports", "checker_id", + new_column_name="checker_name") + op.add_column("reports", col_reports_checker_id) + + def add_checkers(): + # Pre-allocate IDs in the look-up table for all checkers that were + # encountered according to the currently present reports in the DB. + Base = automap_base() + Base.prepare(conn, reflect=True) + Report = Base.classes.reports # 'reports' is the table name! + Checker = Base.classes.checkers + + db = Session(bind=conn) + db.add(Checker(analyzer_name="__FAKE__", + checker_name="__FAKE__", + severity=0)) + db.add(Checker(analyzer_name="UNKNOWN", + checker_name="NOT FOUND", + severity=0)) + + count = db.query(Report.id).count() + checkers_to_severity: Dict[Tuple[str, str], int] = dict() + if count: + def _print_progress(index: int, percent: float): + LOG.info("[%d/%d] Gathering checkers from 'reports'... " + "%.0f%% done. %d checkers found.", + index, count, percent, len(checkers_to_severity)) + + LOG.info("Preparing to fill 'checkers' from %d 'reports'...", + count) + for report in progress(db.query(Report.analyzer_name, + Report.checker_name, + Report.severity), count, + 100 // 5, + callback=_print_progress): + chk = (report.analyzer_name, report.checker_name) + checkers_to_severity[chk] = report.severity + + for chk in sorted(checkers_to_severity.keys()): + obj = Checker(analyzer_name=chk[0], checker_name=chk[1], + severity=checkers_to_severity[chk]) + db.add(obj) + + db.commit() + LOG.info("Done filling 'checkers'.") + + def upgrade_reports(): + LOG.info("Linking 'reports' to 'checkers'...") + conn.execute(f""" + UPDATE reports + SET + checker_id = ( + SELECT checkers.id + FROM checkers + WHERE checkers.analyzer_name = reports.analyzer_name + AND checkers.checker_name = reports.checker_name + ) + ; + """) + + def drop_reports_table_columns(): + LOG.info("Dropping unneeded 'reports' columns...") + + if dialect == "sqlite": + op.execute("PRAGMA foreign_keys=OFF") + + with op.batch_alter_table("reports", recreate="never") as ba: + # FIXME: Allowing recreate="auto" (the default) here would + # result in SQLAlchemy creating a new 'reports' table, which + # will break the database as the incoming constraints + # FOREIGN KEYing against 'reports' will essentially TRUNCATE + # due to the ON DELETE CASCADE markers, e.g., in + # 'bug_path_events'. This manifests as the Python-based database + # cleanup routine essentially wiping all reports off the + # database, following this migration. + # + # Until SQLite 3.35.0 is widely available (Ubuntu 22.04, see + # also dabc6998b8f0_analysis_info_table.py), we side-step the + # current problem with a simple rename. However, in general, + # this is always bound to get bad, and a real solution is only + # expectable from a **significant**, ground-up redesign of both + # the database and the migration logic, and our processes. + # (E.g., if we always assumed migrations can trash the database + # because there are always back-ups, the whole problem with + # "SQLite + transaction + pragma = no-op" would not be here.) + # + # It generally appears that the big issue here is the fact that + # migration scripts execute as part of a TRANSACTION! As such, + # the "PRAGMA foreign_keys=OFF" is useless. See the docs at + # http://sqlite.org/foreignkeys.html (quoted Jan 16, 2024) + # + # > It is not possible to enable or disable foreign key + # > constraints in the middle of a multi-statement transaction + # > (when SQLite is not in autocommit mode). Attempting to do + # > so does not return an error; it simply has no effect. + # + # This means that the foreign keys are still enabled, and when + # the migration commits, 'bug_path_events' and + # 'bug_report_points' **WILL** TRUNCATE themselves as the batch + # operation executed a "DROP TABLE reports;". These claims can + # be re-verified later by executing the following steps: + # 1. Change the database manually and set the incoming + # foreign keys to "ON DELETE RESTRICT" instead of "CASCADE". + # 2. Run the migration. An exception will come from this + # context but will refer to an internal "DROP TABLE" stmt. + # 3. Add an op.execute("COMMIT") before the PRAGMA call above. + # 4. Re-run the migration and observe everything is good and + # there was no data loss whatsoever! + + # ba.drop_column("checker_name") + ba.alter_column("checker_name", + new_column_name="checker_name_MOVED_TO_checkers") + + # These columns are deleted as this data is now available + # through the 'checkers' lookup-table. + # ba.drop_column("analyzer_name") + # ba.drop_column("severity") + ba.alter_column("analyzer_name", + new_column_name="analyzer_name_MOVED_TO_checkers") + ba.alter_column("severity", + new_column_name="severity_MOVED_TO_checkers") + + # These columns are dropped because they rarely contained any + # meaningful data with new informational value, and their + # contents were never actually exposed on the API. + # ba.drop_column("checker_cat") + # ba.drop_column("bug_type") + ba.alter_column("checker_cat", + new_column_name="checker_cat_UNUSED") + ba.alter_column("bug_type", + new_column_name="bug_type_UNUSED") + + op.execute("PRAGMA foreign_keys=ON") + else: + op.drop_column("reports", "checker_name") + op.drop_column("reports", "analyzer_name") + op.drop_column("reports", "severity") + op.drop_column("reports", "checker_cat") + op.drop_column("reports", "bug_type") + + def upgrade_reports_table_constraints(): + ix_reports_checker_id = { + "index_name": op.f("ix_reports_checker_id"), + "columns": ["checker_id"], + "unique": False + } + fk_reports_checker_id = { + "constraint_name": op.f("fk_reports_checker_id_checkers"), + "referent_table": "checkers", + "local_cols": ["checker_id"], + "remote_cols": ["id"], + "deferrable": False, + "ondelete": "RESTRICT" + } + if dialect == "sqlite": + op.execute("PRAGMA foreign_keys=OFF") + + with op.batch_alter_table("reports", recreate="never") as ba: + # Now that the values are filled, ensure that the constriants + # are appropriately enforced. + ba.create_index(**ix_reports_checker_id) + # This should really be a FOREIGN KEY, but it is not possible + # without recreating the entire 'reports' table, which breaks + # other FOREIGN KEYs. + # ba.create_foreign_key(**fk_reports_checker_id) + + op.execute("PRAGMA foreign_keys=ON") + else: + op.create_index(table_name="reports", **ix_reports_checker_id) + op.create_foreign_key(source_table="reports", + **fk_reports_checker_id) + op.alter_column("reports", "checker_id", nullable=False, + server_default=None) + + upgrade_analysis_info() + create_new_tables() + add_new_checker_id_column() + add_checkers() + upgrade_reports() + drop_reports_table_columns() + upgrade_reports_table_constraints() + + +def downgrade(): + LOG = getLogger("migration") + dialect = op.get_context().dialect.name + conn = op.get_bind() + + def downgrade_analysis_info(): + # Downgrade AnalysisInfo to use raw BLOBs instead of the typed and + # tagged ZLibCompressedString feature. The actual type of the Column + # needs no modification, only the values. + Base = automap_base() + Base.prepare(conn, reflect=True) + # 'analysis_info' is the table! + AnalysisInfo = Base.classes.analysis_info + + db = Session(bind=conn) + count = db.query(AnalysisInfo.id).count() + if count: + def _print_progress(index: int, percent: float): + LOG.info("[%d/%d] Downgrading 'analysis_info'... %.0f%% done.", + index, count, percent) + + LOG.info("Preparing to downgrade %d 'analysis_info'...", count) + for analysis_info in progress(db.query(AnalysisInfo).all(), count, + 100 // 5, + callback=_print_progress): + if analysis_info.analyzer_command is None: + continue + old_analyzer_command = recompress_zlib_as_untagged( + analysis_info.analyzer_command) + db.query(AnalysisInfo) \ + .filter(AnalysisInfo.id == analysis_info.id) \ + .update({"analyzer_command": old_analyzer_command}, + synchronize_session=False) + LOG.info("Done downgrading 'analysis_info'.") + db.commit() + + def restore_report_columns(): + col_reports_analyzer_name = sa.Column("analyzer_name", + sa.String(), nullable=False, + server_default="unknown") + col_reports_checker_id = sa.Column("checker_id", sa.String()) + col_reports_checker_cat = sa.Column("checker_cat", sa.String()) + col_reports_bug_type = sa.Column("bug_type", sa.String()) + col_reports_severity = sa.Column("severity", sa.Integer()) + + if dialect == "sqlite": + op.execute("PRAGMA foreign_keys=OFF") + + with op.batch_alter_table("reports", recreate="never") as ba: + # Drop the column that was introduced in this revision. + ba.drop_constraint( + op.f("fk_reports_checker_id_checkers")) + ba.drop_index(op.f("ix_reports_checker_id")) + ba.alter_column("checker_id", + new_column_name="checker_id_lookup") + + with op.batch_alter_table("reports", recreate="never") as ba: + # Restore the columns that were deleted in this revision. + # ba.add_column(col_reports_analyzer_name) + # ba.add_column(col_reports_checker_id) + # ba.add_column(col_reports_checker_cat) + # ba.add_column(col_reports_bug_type) + # ba.add_column(col_reports_severity) + + # FIXME: Until SQLite 3.35 is available and we can actually + # delete columns without a table recreation, we can instead just + # restore the existing partial data! + ba.alter_column("analyzer_name_MOVED_TO_checkers", + new_column_name="analyzer_name") + ba.alter_column("checker_name_MOVED_TO_checkers", + new_column_name="checker_id") + ba.alter_column("severity_moved_to_checkers", + new_column_name="severity") + ba.alter_column("checker_cat_UNUSED", + new_column_name="checker_cat") + ba.alter_column("bug_type_UNUSED", + new_column_name="bug_type") + + op.execute("PRAGMA foreign_keys=ON") + else: + op.drop_constraint(of.f("fk_reports_checker_id_checkers"), + "reports") + op.drop_index(op.f("ix_reports_checker_id"), "reports") + op.alter_column("reports", "checker_id", + new_column_name="checker_id_lookup") + + ba.add_column("reports", col_reports_analyzer_name) + ba.add_column("reports", col_reports_checker_id) + ba.add_column("reports", col_reports_checker_cat) + ba.add_column("reports", col_reports_bug_type) + ba.add_column("reports", col_reports_severity) + + LOG.info("Restored type of columns 'reports.bug_type', " + "'reports.checker_cat'. However, their contents can NOT " + "be restored to the original values, as those were " + "irrevocably lost during a previous schema upgrade. " + "Note, that these columns NEVER contained any actual " + "value that was accessible by users of the API, so " + "this is a technical note.") + + def downgrade_reports(): + LOG.info("Unlinking 'reports' from 'checkers'...") + + if dialect == "sqlite": + conn.execute(f""" + UPDATE reports + SET + (analyzer_name, checker_name, severity) = + (SELECT analyzer_name, checker_name, severity + FROM checkers + WHERE checkers.id = reports.checker_id) + ; + """) + else: + conn.execute(f""" + UPDATE reports + SET + analyzer_name = chk.analyzer_name, + checker_name = chk.checker_name, + severity = chk.severity + FROM checkers AS chk + WHERE chk.id = reports.checker_id + ; + """) + + def drop_checker_id_numeric_column(): + if dialect == "sqlite": + with op.batch_alter_table("reports", recreate="never") as ba: + # FIXME: SQLite >= 3.35 will allow DROP COLUMN... + # ba.drop_column("checker_id_lookup") + ba.alter_column("checker_id_lookup", + new_column_name="checker_id_fk_UNUSED") + else: + op.drop_column("reports", "checker_id_lookup") + + def drop_new_tables(): + # Drop all tables and columns that were created in this revision. + # This data is not needed anymore. + op.drop_index(op.f("ix_checkers_severity"), "checkers") + op.drop_table("analysis_info_checkers") + op.drop_table("checkers") + + downgrade_analysis_info() + restore_report_columns() + downgrade_reports() + drop_checker_id_numeric_column() + drop_new_tables() diff --git a/web/server/codechecker_server/migrations/report/versions/dabc6998b8f0_analysis_info_table.py b/web/server/codechecker_server/migrations/report/versions/dabc6998b8f0_analysis_info_table.py index a275d73c59..80ad54ef22 100644 --- a/web/server/codechecker_server/migrations/report/versions/dabc6998b8f0_analysis_info_table.py +++ b/web/server/codechecker_server/migrations/report/versions/dabc6998b8f0_analysis_info_table.py @@ -12,11 +12,14 @@ branch_labels = None depends_on = None +from logging import getLogger + from alembic import op import sqlalchemy as sa def upgrade(): + LOG = getLogger("migration") conn = op.get_bind() ctx = op.get_context() dialect = ctx.dialect.name @@ -88,12 +91,13 @@ def upgrade(): op.bulk_insert( run_history_analysis_info_tbl, run_history_analysis_info) except: - print("Analyzer command data migration failed!") + LOG.error("Analyzer command data migration failed!") else: # If data migration was successfully finished we can remove the # columns. if dialect == 'sqlite': - # Unfortunately removing columns in SQLite is not supported. + # Unfortunately removing columns in SQLite is not supported until + # version 3.35.0 (only Ubuntu 22.04's default version). Using the # 'batch_alter_table' function can be used to remove a column here (it # will create a new database) but it will clear the table which have # foreign keys with cascade delete property. Unfortunately disabling diff --git a/web/server/codechecker_server/server.py b/web/server/codechecker_server/server.py index 7dd218c683..618c0cf8a7 100644 --- a/web/server/codechecker_server/server.py +++ b/web/server/codechecker_server/server.py @@ -669,10 +669,10 @@ def cleanup_run_db(self): LOG.info("Garbage collection for product '%s' started...", self.endpoint) - db_cleanup.remove_expired_run_locks(self.session_factory) + db_cleanup.remove_expired_data(self.session_factory) db_cleanup.remove_unused_data(self.session_factory) - db_cleanup.upgrade_severity_levels(self.session_factory, - self.__context.checker_labels) + db_cleanup.update_contextual_data(self.session_factory, + self.__context) LOG.info("Garbage collection finished.") return True diff --git a/web/server/tests/unit/metadata_test_files/v1.5.json b/web/server/tests/unit/metadata_test_files/v1.5.json new file mode 100644 index 0000000000..c54930953d --- /dev/null +++ b/web/server/tests/unit/metadata_test_files/v1.5.json @@ -0,0 +1,50 @@ +{ + "action_num": 1, + "analyzer_statistics": { + "clang-tidy": { + "failed": 0, + "failed_sources": [], + "successful": 10, + "version": "LLVM version 7.0.0" + }, + "clangsa": { + "failed": 0, + "failed_sources": [], + "successful": 1, + "version": "clang version 7.0.0" + } + }, + "checkers": { + "clang-tidy": { + "abseil-string-find-startswith": false, + "bugprone-use-after-move": true + }, + "clangsa": { + "alpha.clone.CloneChecker": false, + "deadcode.DeadStores": true + } + }, + "command": [ + "CodeChecker.py", + "analyze", + "-o", + "/path/to/reports", + "/path/to/build.json" + ], + "output_path": "/path/to/reports", + "result_source_files": { + "/path/to/reports/main.cpp_cd2085addd2b226005b7f9cf1827c082.plist": "/path/to/main.cpp", + "/path/to/reports/reports/main.cpp_ed1ce6c18431138a19465e60aa69a4ba.plist": "/path/to/main.cpp" + }, + "skipped": 0, + "timestamps": { + "begin": 1571297867, + "end": 1571297868 + }, + "versions": { + "clang": "clang version 7.0.0", + "clang-tidy": "LLVM version 7.0.0", + "codechecker": "6.11 (930440d6a6cae80f615146547f4b169c7629d558)" + }, + "working_directory": "/path/to/workspace" +} diff --git a/web/server/tests/unit/metadata_test_files/v1.json b/web/server/tests/unit/metadata_test_files/v1.json index c54930953d..44cc941c6f 100644 --- a/web/server/tests/unit/metadata_test_files/v1.json +++ b/web/server/tests/unit/metadata_test_files/v1.json @@ -15,14 +15,12 @@ } }, "checkers": { - "clang-tidy": { - "abseil-string-find-startswith": false, - "bugprone-use-after-move": true - }, - "clangsa": { - "alpha.clone.CloneChecker": false, - "deadcode.DeadStores": true - } + "clang-tidy": [ + "bugprone-use-after-move" + ], + "clangsa": [ + "deadcode.DeadStores" + ] }, "command": [ "CodeChecker.py", diff --git a/web/server/tests/unit/test_metadata_merge.py b/web/server/tests/unit/test_metadata_merge.py index 579b78dcd3..62e723a8f2 100644 --- a/web/server/tests/unit/test_metadata_merge.py +++ b/web/server/tests/unit/test_metadata_merge.py @@ -136,6 +136,7 @@ def test_merge_metadata(self): }] } + # FIXME: What even is this?! The name is 'v3' but it says "version: 2". metadata_v3 = { "version": 2, 'num_of_report_dir': 1, diff --git a/web/server/tests/unit/test_metadata_parser.py b/web/server/tests/unit/test_metadata_parser.py index 4533ec2ad2..70d400611c 100644 --- a/web/server/tests/unit/test_metadata_parser.py +++ b/web/server/tests/unit/test_metadata_parser.py @@ -43,6 +43,28 @@ 'bugprone-use-after-move': True, 'abseil-string-find-startswith': False } + }, + "checkers_enabled_only": { + "clangsa": { + "deadcode.DeadStores": True + }, + "clang-tidy": { + "bugprone-use-after-move": True, + } + }, + "analyzers": [ + "clang-tidy", + "clangsa" + ], + "analyzers_to_checkers": { + "clangsa": [ + "alpha.clone.CloneChecker", + "deadcode.DeadStores" + ], + "clang-tidy": [ + "abseil-string-find-startswith", + "bugprone-use-after-move" + ] } } @@ -73,6 +95,10 @@ "version": "Cppcheck 1.87" } }, + "analyzers": [ + "clang-tidy", + "clangsa" + ], 'checkers': { 'clangsa': { 'alpha.clone.CloneChecker': False, @@ -135,9 +161,30 @@ def test_metadata_info_v1(self): self.assertDictEqual(metadata_cc_info['analyzer_statistics'], mip.analyzer_statistics) - self.assertDictEqual(metadata_cc_info['checkers'], + self.assertListEqual(metadata_cc_info["analyzers"], + mip.analyzers) + + self.assertDictEqual(metadata_cc_info["checkers_enabled_only"], mip.checkers) + def test_metadata_info_v1_point_5(self): + """ + Get metadata info for old version format json file, but the kind where + checker "enabled status" was already represented, but before it + officially became the version 2 format. + """ + metadata_v1_5 = os.path.join(self.__metadata_test_files, "v1.5.json") + mip = MetadataInfoParser(metadata_v1_5) + + self.assertDictEqual(metadata_cc_info["checkers"], + mip.checkers) + self.assertListEqual(metadata_cc_info + ["analyzers_to_checkers"]["clangsa"], + list(mip.checkers["clangsa"].keys())) + self.assertListEqual(metadata_cc_info + ["analyzers_to_checkers"]["clang-tidy"], + list(mip.checkers["clang-tidy"].keys())) + def test_metadata_info_v2(self): """ Get metadata info for new version format json. """ metadata_v2 = os.path.join(self.__metadata_test_files, 'v2.json') @@ -155,8 +202,17 @@ def test_metadata_info_v2(self): self.assertDictEqual(metadata_cc_info['analyzer_statistics'], mip.analyzer_statistics) - self.assertDictEqual(metadata_cc_info['checkers'], + self.assertListEqual(metadata_cc_info["analyzers"], + mip.analyzers) + + self.assertDictEqual(metadata_cc_info["checkers"], mip.checkers) + self.assertListEqual(metadata_cc_info + ["analyzers_to_checkers"]["clangsa"], + list(mip.checkers["clangsa"].keys())) + self.assertListEqual(metadata_cc_info + ["analyzers_to_checkers"]["clang-tidy"], + list(mip.checkers["clang-tidy"].keys())) def test_multiple_metadata_info(self): """ Get metadata info from multiple analyzers. """ diff --git a/web/server/vue-cli/package-lock.json b/web/server/vue-cli/package-lock.json index 9f5345a413..4451021fd3 100644 --- a/web/server/vue-cli/package-lock.json +++ b/web/server/vue-cli/package-lock.json @@ -11,7 +11,7 @@ "@mdi/font": "^6.5.95", "chart.js": "^2.9.4", "chartjs-plugin-datalabels": "^0.7.0", - "codechecker-api": "file:../../api/js/codechecker-api-node/dist/codechecker-api-6.54.0.tgz", + "codechecker-api": "file:../../api/js/codechecker-api-node/dist/codechecker-api-6.55.0.tgz", "codemirror": "^5.65.0", "date-fns": "^2.28.0", "js-cookie": "^3.0.1", @@ -4875,9 +4875,9 @@ } }, "node_modules/codechecker-api": { - "version": "6.54.0", - "resolved": "file:../../api/js/codechecker-api-node/dist/codechecker-api-6.54.0.tgz", - "integrity": "sha512-1+5Q0B4ehDO7s9bHe0iIqprajdQqVl9iW4TPjyMq9ITlsN5/XQLhzyCwmvhlADpNLKVQVY7oAuvQ67JVMw62GQ==", + "version": "6.55.0", + "resolved": "file:../../api/js/codechecker-api-node/dist/codechecker-api-6.55.0.tgz", + "integrity": "sha512-gCh2H/q68D3zGgpYAgNX/2h+7puzPtPVMgQq+af/F/Yq7eQGBjIQqSWwL+/hhsawwdjIHE9bbEgUuCXzDi4l/Q==", "license": "SEE LICENSE IN LICENSE", "dependencies": { "thrift": "0.13.0-hotfix.1" @@ -19832,8 +19832,8 @@ "dev": true }, "codechecker-api": { - "version": "file:../../api/js/codechecker-api-node/dist/codechecker-api-6.54.0.tgz", - "integrity": "sha512-1+5Q0B4ehDO7s9bHe0iIqprajdQqVl9iW4TPjyMq9ITlsN5/XQLhzyCwmvhlADpNLKVQVY7oAuvQ67JVMw62GQ==", + "version": "file:../../api/js/codechecker-api-node/dist/codechecker-api-6.55.0.tgz", + "integrity": "sha512-gCh2H/q68D3zGgpYAgNX/2h+7puzPtPVMgQq+af/F/Yq7eQGBjIQqSWwL+/hhsawwdjIHE9bbEgUuCXzDi4l/Q==", "requires": { "thrift": "0.13.0-hotfix.1" } diff --git a/web/server/vue-cli/package.json b/web/server/vue-cli/package.json index ca09e78b09..b0043acf96 100644 --- a/web/server/vue-cli/package.json +++ b/web/server/vue-cli/package.json @@ -27,7 +27,7 @@ }, "dependencies": { "@mdi/font": "^6.5.95", - "codechecker-api": "file:../../api/js/codechecker-api-node/dist/codechecker-api-6.54.0.tgz", + "codechecker-api": "file:../../api/js/codechecker-api-node/dist/codechecker-api-6.55.0.tgz", "chart.js": "^2.9.4", "chartjs-plugin-datalabels": "^0.7.0", "codemirror": "^5.65.0", diff --git a/web/server/vue-cli/src/components/AnalysisInfoDialog.vue b/web/server/vue-cli/src/components/AnalysisInfoDialog.vue index cfeed9ff34..cfba1c26be 100644 --- a/web/server/vue-cli/src/components/AnalysisInfoDialog.vue +++ b/web/server/vue-cli/src/components/AnalysisInfoDialog.vue @@ -126,6 +126,12 @@ export default { offset, handleThriftError(analysisInfo => { this.analysisInfo = analysisInfo.map(cmd => this.highlightOptions(cmd)); + + // FIXME: Do this more ergonomically. This is only good enough for + // testing the backend's stability for now... + console.warn("The available checkers might have been received, " + + "but showing them is not supported yet!"); + console.warn(analysisInfo.map(ai => ai.checkers)); })); } } diff --git a/web/tests/functional/blame/test_blame_info.py b/web/tests/functional/blame/test_blame_info.py index a8482d185a..df9b8c48dd 100644 --- a/web/tests/functional/blame/test_blame_info.py +++ b/web/tests/functional/blame/test_blame_info.py @@ -187,7 +187,12 @@ def test_update_blame_info(self): self.assertFalse(blame_info.commits) self.assertFalse(blame_info.blame) - subprocess.Popen(['git', 'init', proj_dir]).communicate() + # Create a .git structure that is as bare as possible, without + # getting interference from the user's configuration. + subprocess.Popen(['git', 'init', proj_dir, + "--template", "/usr/share/git-core/templates" + ]).communicate() + subprocess.Popen([ 'git', 'remote', @@ -200,7 +205,8 @@ def test_update_blame_info(self): '-c', 'user.name=hello', '-c', 'user.email=world', 'commit', - '-m', 'message']).communicate() + '--no-verify', + '--message', 'message']).communicate() codechecker.store(self._codechecker_cfg, run_name) diff --git a/web/tests/functional/cppcheck/test_cppcheck.py b/web/tests/functional/cppcheck/test_cppcheck.py index 20c4040c6a..8bfafc36d2 100644 --- a/web/tests/functional/cppcheck/test_cppcheck.py +++ b/web/tests/functional/cppcheck/test_cppcheck.py @@ -122,6 +122,10 @@ def test_cppcheck_report_storage(self): print(out) reports = json.loads(out) self.assertEqual(len(reports), 5) - # The stored hash should not be "0". for report in reports: - self.assertNotEqual(report['bugHash'], "0") + # The stored hash should not be "0". + self.assertNotEqual(report["bugHash"], "0") + # The stored checker name should not be the fake(d) default that + # was created because no 'metadata.json' (and thus no checker + # list) exists for this "project". + self.assertNotEqual(report["checkerId"], "__FAKE__") diff --git a/web/tests/functional/report_viewer_api/test_run_data.py b/web/tests/functional/report_viewer_api/test_run_data.py index b258e021f8..81de5ec8eb 100644 --- a/web/tests/functional/report_viewer_api/test_run_data.py +++ b/web/tests/functional/report_viewer_api/test_run_data.py @@ -17,8 +17,11 @@ from libtest import env -from codechecker_api.codeCheckerDBAccess_v6.ttypes import DetectionStatus, \ - Order, ReportFilter, RunFilter, RunSortMode, RunSortType +from codechecker_api.codeCheckerDBAccess_v6.ttypes import \ + AnalysisInfoFilter, \ + DetectionStatus, \ + Order, \ + ReportFilter, RunFilter, RunSortMode, RunSortType from . import setup_class_common, teardown_class_common @@ -171,3 +174,67 @@ def test_sort_run_data(self): # contains special characters. sort_mode = RunSortMode(RunSortType.NAME, Order.ASC) self._cc_client.getRunData(None, None, 0, sort_mode) + + def test_analysis_info(self): + """ + Test that storing runs to the server records the executed analyzer + command and the list of checkers present and executed. + """ + workspace = os.environ["TEST_WORKSPACE"] + runs = self.__get_runs("test_files*%") + self.assertEqual(len(runs), 1, + "There should be one run for this test.") + run = runs[0] + run_id = run.runId + + analysis_infos = self._cc_client.getAnalysisInfo( + AnalysisInfoFilter(run_id, None, None), 1, 0) + self.assertEqual(len(analysis_infos), 1, + "An analysis_info must be recorded for the run!") + + info = analysis_infos[0] + cmd = info.analyzerCommand + print(run_id, analysis_infos) + + self.assertTrue(workspace in cmd, + "The name of the test workspace should be part of " + "the report directory, found in the cmdline.") + # Ensure that the tests here are up-to-date with what's in __init__.py. + self.assertTrue("-d core.StackAddressEscape" in cmd, + "A disabled checker is needed for this test to work!") + self.assertTrue("-d unix.Malloc" in cmd, + "A disabled checker is needed for this test to work!") + self.assertTrue("-d clang-diagnostic" in cmd, + "A disabled checker is needed for this test to work!") + self.assertTrue("-e clang-diagnostic-division-by-zero" in cmd, + "An enabled checker is needed for this test to work!") + + checkers = info.checkers + + def assertChecker(analyzer, checker): + self.assertTrue(checkers[analyzer][checker].enabled) + + def assertNotChecker(analyzer, checker): + self.assertFalse(checkers[analyzer][checker].enabled) + + assertNotChecker("clangsa", "alpha.cplusplus.MismatchedIterator") + assertNotChecker("clangsa", "alpha.webkit.UncountedCallArgsChecker") + assertNotChecker("clangsa", "core.StackAddressEscape") + assertChecker("clangsa", "core.CallAndMessage") + assertChecker("clangsa", "deadcode.DeadStores") + assertChecker("clangsa", "cplusplus.NewDelete") + assertNotChecker("clangsa", "osx.cocoa.Loops") + assertNotChecker("clangsa", "unix.Malloc") + + assertNotChecker("clang-tidy", "bugprone-easily-swappable-parameters") + assertChecker("clang-tidy", "clang-diagnostic-division-by-zero") + assertNotChecker("clang-tidy", "clang-diagnostic-return-type") + assertNotChecker("clang-tidy", "clang-diagnostic-vla") + assertNotChecker("clang-tidy", "llvmlibc-restrict-system-libc-headers") + assertChecker("clang-tidy", "misc-definitions-in-headers") + assertNotChecker("clang-tidy", "objc-super-self") + + self.assertTrue("cppcheck" not in checkers.keys(), + "This analysis was run without CppCheck!") + self.assertTrue("gcc" not in checkers.keys(), + "This analysis was run without GCC!")