LCOV - code coverage report
Current view: top level - dashql-app/src/session/scripts - script_reference_check.ts Coverage Total Hit
Test: DashQL 915828b Lines: 96.7 % 61 59
Test Date: 2026-11-06 19:30:32

            Line data    Source code
       1              : import * as core from '@ankoh/dashql-core';
       2              : 
       3              : import { sessionPlanRequest } from '../inputs/session_inputs.js';
       4              : import { orderedSessionScripts, type SessionScripts } from './session_scripts.js';
       5              : 
       6            1 : const NONE = 0xFFFFFFFF;
       7              : 
       8              : /// What a script reference reads
       9            1 : enum ScriptReferenceKind {
      10              :     /// The query of the referenced script is inlined into the statement, both run on one connection
      11            1 :     Inline = 'inline',
      12              :     /// The statement scans the result of a script of another connection, materialized as Parquet file
      13            1 :     Scan = 'scan',
      14              :     /// The reference has errors
      15            1 :     Invalid = 'invalid',
      16              :     /// The statement scans an attachment of the session
      17            1 :     Attachment = 'attachment',
      18            1 : }
      19              : 
      20              : /// A checked table parameter of a script, `:foo`
      21              : interface ScriptReferenceMark {
      22              :     /// The begin of the parameter, at the prefix, in UTF-16 code units
      23              :     from: number;
      24              :     /// The end of the parameter
      25              :     to: number;
      26              :     /// The parameter name
      27              :     parameter: string;
      28              :     /// The file name of the referenced script, null if the name finds none
      29              :     target: string | null;
      30              :     /// The file name of the referenced attachment, null if the name finds none
      31              :     attachment: string | null;
      32              :     /// What the reference reads
      33              :     kind: ScriptReferenceKind;
      34              :     /// The errors of the reference
      35              :     errors: string[];
      36              : }
      37              : 
      38              : /// The checked script references of a script
      39              : export interface ScriptReferenceReport {
      40              :     /// The text version of the analysis that was checked, the marks lie in that text
      41              :     textVersion: bigint;
      42              :     /// The table parameters in text order
      43              :     marks: ScriptReferenceMark[];
      44              : }
      45              : 
      46              : /// Check the script references of every script of a session, by script key. A script whose analysis is not current
      47              : /// with its text has no report.
      48            1 : export function computeScriptReferenceReports(session: SessionScripts): Map<number, ScriptReferenceReport> {
      49            1 :     const scripts = orderedSessionScripts(session);
      50            1 :     const reports = new Map<number, ScriptReferenceReport>();
      51            1 :     if (scripts.length === 0) return reports;
      52            1 :     const request = sessionPlanRequest(scripts, scripts.map(script => script.fileName), session);
      53            1 :     const check: core.buffers.execution.ScriptReferenceCheckT = session.instance.checkScriptReferences(request).unpack();
      54            1 :     check.scripts.forEach((checked, index) => {
      55            1 :         if (!checked.checked) return;
      56            1 :         const marks = checked.references.map(reference => {
      57            1 :             const span = reference.textSpan;
      58            1 :             const from = span?.offset ?? 0;
      59            1 :             let kind: ScriptReferenceKind;
      60            1 :             if (reference.errors.length > 0 || reference.kind === core.buffers.execution.ScriptReferenceKind.INVALID) {
      61            1 :                 kind = ScriptReferenceKind.Invalid;
      62            1 :             } else if (reference.kind === core.buffers.execution.ScriptReferenceKind.SCAN) {
      63            0 :                 kind = ScriptReferenceKind.Scan;
      64            1 :             } else if (reference.kind === core.buffers.execution.ScriptReferenceKind.ATTACHMENT) {
      65            0 :                 kind = ScriptReferenceKind.Attachment;
      66              :             } else {
      67            1 :                 kind = ScriptReferenceKind.Inline;
      68              :             }
      69            1 :             return {
      70            1 :                 from,
      71            1 :                 to: from + (span?.length ?? 0),
      72            1 :                 parameter: String(reference.parameter ?? ''),
      73            1 :                 target: reference.script !== NONE ? scripts[reference.script]?.fileName ?? null : null,
      74            1 :                 attachment: reference.attachment !== NONE ? request.attachments?.[reference.attachment]?.file ?? null : null,
      75            1 :                 kind,
      76            1 :                 errors: reference.errors.map(String),
      77            1 :             };
      78            1 :         });
      79            1 :         reports.set(scripts[index].scriptKey, { textVersion: checked.textVersion, marks });
      80            1 :     });
      81            1 :     return reports;
      82            1 : }
      83              : 
      84              : /// A change of the text of a script, in UTF-16 code units of the text before the change
      85              : export interface ScriptTextChange {
      86              :     from: number;
      87              :     to: number;
      88              :     insert: string;
      89              : }
      90              : 
      91              : /// What a table parameter reads, by file name: a script or an attachment
      92              : export type TableParameterTarget = { script: string } | { attachment: string };
      93              : 
      94              : /// The changes that rename the references to a script or an attachment: every table parameter that reads it reads
      95              : /// `reference` instead, e.g. `:"foo-2"`
      96            1 : export function renameReferenceChanges(
      97            1 :     reports: ReadonlyMap<number, ScriptReferenceReport>,
      98            1 :     target: TableParameterTarget,
      99            1 :     reference: string,
     100            1 : ): Map<number, ScriptTextChange[]> {
     101            1 :     const reads = (mark: ScriptReferenceMark) => 'script' in target ? mark.target === target.script : mark.attachment === target.attachment;
     102            1 :     const out = new Map<number, ScriptTextChange[]>();
     103            1 :     for (const [scriptKey, report] of reports) {
     104            1 :         const changes = report.marks
     105            1 :             .filter(mark => reads(mark) && mark.parameter.length > 0)
     106            1 :             .map(mark => ({ from: mark.from, to: mark.to, insert: reference }));
     107            1 :         if (changes.length > 0) out.set(scriptKey, changes);
     108            1 :     }
     109            1 :     return out;
     110            1 : }
     111              : 
     112              : /// Apply changes to a text
     113            1 : export function applyScriptTextChanges(text: string, changes: readonly ScriptTextChange[]): string {
     114            1 :     let out = text;
     115            1 :     for (const change of [...changes].sort((l, r) => r.from - l.from)) {
     116            1 :         out = out.slice(0, change.from) + change.insert + out.slice(change.to);
     117              :     }
     118            1 :     return out;
     119            1 : }
        

Generated by: LCOV version 2.0-1