1- import { ViewColumn } from "vscode" ;
1+ import { Uri , ViewColumn } from "vscode" ;
2+ import { join } from "path" ;
23
34import type {
45 FromCompareViewMessage ,
6+ InterpretedQueryCompareResult ,
57 QueryCompareResult ,
68 RawQueryCompareResult ,
79 ToCompareViewMessage ,
810} from "../common/interface-types" ;
9- import { ALERTS_TABLE_NAME } from "../common/interface-types" ;
11+ import {
12+ ALERTS_TABLE_NAME ,
13+ SELECT_TABLE_NAME ,
14+ } from "../common/interface-types" ;
1015import type { Logger } from "../common/logging" ;
1116import { showAndLogExceptionWithTelemetry } from "../common/logging" ;
1217import { extLogger } from "../common/logging/vscode" ;
@@ -31,11 +36,10 @@ import {
3136 findResultSetNames ,
3237 getResultSetNames ,
3338} from "./result-set-names" ;
34- import { compareInterpretedResults } from "./interpreted-results" ;
3539import { isCanary } from "../config" ;
3640import { nanoid } from "nanoid" ;
3741
38- export interface ComparePair {
42+ interface ComparePair {
3943 from : CompletedLocalQueryInfo ;
4044 fromInfo : CompareQueryInfo ;
4145 to : CompletedLocalQueryInfo ;
@@ -44,14 +48,39 @@ export interface ComparePair {
4448 commonResultSetNames : readonly string [ ] ;
4549}
4650
47- export function findSchema ( info : BqrsInfo , name : string ) {
51+ function findSchema ( info : BqrsInfo , name : string ) {
4852 const schema = info [ "result-sets" ] . find ( ( schema ) => schema . name === name ) ;
4953 if ( schema === undefined ) {
5054 throw new Error ( `Schema ${ name } not found.` ) ;
5155 }
5256 return schema ;
5357}
5458
59+ /**
60+ * Check that `fromInfo` and `toInfo` have comparable schemas for the given
61+ * result set names and get them if so.
62+ */
63+ function getComparableSchemas (
64+ fromInfo : CompareQueryInfo ,
65+ toInfo : CompareQueryInfo ,
66+ fromResultSetName : string ,
67+ toResultSetName : string ,
68+ ) : { fromSchema : BqrsResultSetSchema ; toSchema : BqrsResultSetSchema } {
69+ const fromSchema = findSchema ( fromInfo . schemas , fromResultSetName ) ;
70+ const toSchema = findSchema ( toInfo . schemas , toResultSetName ) ;
71+
72+ if ( fromSchema . columns . length !== toSchema . columns . length ) {
73+ throw new Error ( "CodeQL Compare: Columns do not match." ) ;
74+ }
75+ if ( fromSchema . rows === 0 ) {
76+ throw new Error ( "CodeQL Compare: Source query has no results." ) ;
77+ }
78+ if ( toSchema . rows === 0 ) {
79+ throw new Error ( "CodeQL Compare: Target query has no results." ) ;
80+ }
81+ return { fromSchema, toSchema } ;
82+ }
83+
5584export class CompareView extends AbstractWebview <
5685 ToCompareViewMessage ,
5786 FromCompareViewMessage
@@ -178,11 +207,7 @@ export class CompareView extends AbstractWebview<
178207 let message : string | undefined ;
179208 try {
180209 if ( currentResultSetName === ALERTS_TABLE_NAME ) {
181- result = await compareInterpretedResults (
182- this . databaseManager ,
183- this . cliServer ,
184- this . comparePair ,
185- ) ;
210+ result = await this . compareInterpretedResults ( this . comparePair ) ;
186211 } else {
187212 result = await this . compareResults (
188213 this . comparePair ,
@@ -424,6 +449,75 @@ export class CompareView extends AbstractWebview<
424449 }
425450 }
426451
452+ private async compareInterpretedResults (
453+ comparePair : ComparePair ,
454+ ) : Promise < InterpretedQueryCompareResult > {
455+ const { from : fromQuery , fromInfo, to : toQuery , toInfo } = comparePair ;
456+
457+ // `ALERTS_TABLE_NAME` is inserted by `getResultSetNames` into the schema
458+ // names even if it does not occur as a result set. Hence we check for
459+ // `SELECT_TABLE_NAME` first, and use that if it exists.
460+ const tableName = fromInfo . schemaNames . includes ( SELECT_TABLE_NAME )
461+ ? SELECT_TABLE_NAME
462+ : ALERTS_TABLE_NAME ;
463+
464+ getComparableSchemas ( fromInfo , toInfo , tableName , tableName ) ;
465+
466+ const database = this . databaseManager . findDatabaseItem (
467+ Uri . parse ( toQuery . initialInfo . databaseInfo . databaseUri ) ,
468+ ) ;
469+ if ( ! database ) {
470+ throw new Error (
471+ "Could not find database the queries. Please check that the database still exists." ,
472+ ) ;
473+ }
474+
475+ const { uniquePath1, uniquePath2, path, cleanup } =
476+ await this . cliServer . bqrsDiff (
477+ fromQuery . completedQuery . query . resultsPath ,
478+ toQuery . completedQuery . query . resultsPath ,
479+ { retainResultSets : [ "nodes" , "edges" , "subpaths" ] } ,
480+ ) ;
481+ try {
482+ const sarifOutput1 = join ( path , "from.sarif" ) ;
483+ const sarifOutput2 = join ( path , "to.sarif" ) ;
484+
485+ const sourceLocationPrefix = await database . getSourceLocationPrefix (
486+ this . cliServer ,
487+ ) ;
488+ const sourceArchiveUri = database . sourceArchive ;
489+ const sourceInfo =
490+ sourceArchiveUri === undefined
491+ ? undefined
492+ : {
493+ sourceArchive : sourceArchiveUri . fsPath ,
494+ sourceLocationPrefix,
495+ } ;
496+
497+ const fromResultSet = await this . cliServer . interpretBqrsSarif (
498+ fromQuery . completedQuery . query . metadata ! ,
499+ uniquePath1 ,
500+ sarifOutput1 ,
501+ sourceInfo ,
502+ ) ;
503+ const toResultSet = await this . cliServer . interpretBqrsSarif (
504+ toQuery . completedQuery . query . metadata ! ,
505+ uniquePath2 ,
506+ sarifOutput2 ,
507+ sourceInfo ,
508+ ) ;
509+
510+ return {
511+ kind : "interpreted" ,
512+ sourceLocationPrefix,
513+ from : fromResultSet . runs [ 0 ] . results ! ,
514+ to : toResultSet . runs [ 0 ] . results ! ,
515+ } ;
516+ } finally {
517+ await cleanup ( ) ;
518+ }
519+ }
520+
427521 private async openQuery ( kind : "from" | "to" ) {
428522 const toOpen =
429523 kind === "from" ? this . comparePair ?. from : this . comparePair ?. to ;
@@ -432,28 +526,3 @@ export class CompareView extends AbstractWebview<
432526 }
433527 }
434528}
435-
436- /**
437- * Check that `fromInfo` and `toInfo` have comparable schemas for the given
438- * result set names and get them if so.
439- */
440- export function getComparableSchemas (
441- fromInfo : CompareQueryInfo ,
442- toInfo : CompareQueryInfo ,
443- fromResultSetName : string ,
444- toResultSetName : string ,
445- ) : { fromSchema : BqrsResultSetSchema ; toSchema : BqrsResultSetSchema } {
446- const fromSchema = findSchema ( fromInfo . schemas , fromResultSetName ) ;
447- const toSchema = findSchema ( toInfo . schemas , toResultSetName ) ;
448-
449- if ( fromSchema . columns . length !== toSchema . columns . length ) {
450- throw new Error ( "CodeQL Compare: Columns do not match." ) ;
451- }
452- if ( fromSchema . rows === 0 ) {
453- throw new Error ( "CodeQL Compare: Source query has no results." ) ;
454- }
455- if ( toSchema . rows === 0 ) {
456- throw new Error ( "CodeQL Compare: Target query has no results." ) ;
457- }
458- return { fromSchema, toSchema } ;
459- }
0 commit comments