Line data Source code
1 : import type * as arrow from 'apache-arrow';
2 :
3 : import type { QueryExecutor } from '../../connections/query_executor.js';
4 : import { SessionScripts, ScriptData, isAnalysisOutdated } from './session_scripts.js';
5 : import { ensureSessionScriptAnalyzed, ModifySessionScripts } from './session_scripts_registry.js';
6 : import { stringifyError, type LoggerLike } from '../../platform/logger/logger.js';
7 : import { isSessionExecutionRunning, releaseSessionExecution, waitForSessionExecution } from './session_execution.js';
8 : import { runPlannedScript, scriptRunServices, type ScriptRunOptions } from './script_run.js';
9 :
10 : /// Run a script on its connection, resolves when the run ended. A failed statement and a plan that fails, e.g. with an
11 : /// unknown script, show their errors in the script. A refresh reads no cached results, neither of the script nor of
12 : /// the scripts it references.
13 1 : export async function runSessionScript(
14 1 : sessionScripts: SessionScripts,
15 1 : scriptData: ScriptData,
16 1 : executeQuery: QueryExecutor,
17 1 : modifySessionScripts: ModifySessionScripts,
18 1 : logger: LoggerLike,
19 1 : options: ScriptRunOptions = {},
20 1 : ): Promise<void> {
21 1 : if (isSessionExecutionRunning(sessionScripts)) return;
22 1 : logger?.info('Session script execution requested', {
23 1 : connectionId: scriptData.connection.connectionId,
24 1 : sessionId: sessionScripts.sessionId,
25 1 : scriptKey: scriptData.scriptKey.toString(),
26 1 : analysisOutdated: scriptData.analysisOutdated.toString(),
27 1 : analysisAvailable: scriptData.editorUpdate?.analysisAvailable.toString(),
28 1 : textVersion: scriptData.editorUpdate?.textVersion.toString(),
29 1 : nativeTextVersion: scriptData.scriptSession.getTextVersion?.().toString(),
30 1 : textLength: scriptData.scriptSession.getText?.().length.toString(),
31 1 : }, 'session_execution');
32 1 : if (isAnalysisOutdated(scriptData)) {
33 1 : const analyzed = await ensureSessionScriptAnalyzed(sessionScripts, scriptData.scriptKey, modifySessionScripts);
34 1 : if (analyzed == null) {
35 0 : logger?.warn('Session execution stopped because analysis returned no script', {
36 0 : sessionId: sessionScripts.sessionId,
37 0 : scriptKey: scriptData.scriptKey.toString(),
38 0 : }, 'session_execution');
39 0 : return;
40 0 : }
41 1 : logger?.info('Outdated session script analysis completed', {
42 1 : sessionId: sessionScripts.sessionId,
43 1 : scriptKey: scriptData.scriptKey.toString(),
44 1 : analysisAvailable: analyzed.editorUpdate?.analysisAvailable.toString(),
45 1 : }, 'session_execution');
46 1 : scriptData = analyzed;
47 1 : }
48 1 : await executeSessionScript(scriptData, sessionScripts, executeQuery, modifySessionScripts, logger, false, options);
49 1 : }
50 :
51 : /// Run a script on its connection and wait for its result, e.g. a run of the agent. The run waits for the run of
52 : /// another script of the session to end. Fails when the whole session runs, the script is gone, can't be planned,
53 : /// holds no statement, or a statement fails or is cancelled.
54 1 : export async function runSessionScriptForResult(
55 1 : getSession: () => SessionScripts | null,
56 1 : scriptKey: number,
57 1 : executeQuery: QueryExecutor,
58 1 : modifySessionScripts: ModifySessionScripts,
59 1 : logger: LoggerLike,
60 1 : ): Promise<arrow.Table | null> {
61 1 : const sessionId = getSession()?.sessionId;
62 1 : if (sessionId == null) throw new Error('The session is gone');
63 1 : await waitForSessionExecution(sessionId);
64 1 : let handedOver = false;
65 1 : try {
66 : // The session as the runs before left it, e.g. with analyzed scripts
67 1 : const waited = getSession();
68 1 : const scriptData = waited?.scripts[scriptKey];
69 1 : if (waited == null || scriptData == null) throw new Error('The script is gone');
70 1 : const analyzed = isAnalysisOutdated(scriptData)
71 1 : ? await ensureSessionScriptAnalyzed(waited, scriptKey, modifySessionScripts)
72 0 : : scriptData;
73 1 : if (analyzed == null) {
74 0 : throw new Error('The script could not be analyzed');
75 : }
76 : // The run releases the session. The caller reports the failure, e.g. to the agent, the script shows it as well.
77 1 : handedOver = true;
78 1 : return await executeSessionScript(analyzed, getSession() ?? waited, executeQuery, modifySessionScripts, logger, true, { held: true });
79 1 : } finally {
80 1 : if (!handedOver) releaseSessionExecution(sessionId);
81 1 : }
82 1 : }
83 :
84 : /// Run a script through a plan of the script and the scripts it references (docs/design/script_references.md)
85 1 : function executeSessionScript(
86 1 : scriptData: ScriptData,
87 1 : sessionScripts: SessionScripts,
88 1 : executeQuery: QueryExecutor,
89 1 : modifySessionScripts: ModifySessionScripts,
90 1 : logger: LoggerLike,
91 1 : throwOnError: boolean = false,
92 1 : options: ScriptRunOptions = {},
93 1 : ): Promise<arrow.Table | null> {
94 1 : const services = scriptRunServices(sessionScripts.instance);
95 1 : return runPlannedScript(services, sessionScripts, scriptData, executeQuery, modifySessionScripts, { logger, ...options }).catch(error => {
96 1 : if (throwOnError) throw error;
97 1 : logger?.warn('Session script run failed', {
98 1 : sessionId: sessionScripts.sessionId,
99 1 : scriptKey: scriptData.scriptKey.toString(),
100 1 : error: stringifyError(error),
101 1 : }, 'session_execution');
102 1 : return null;
103 1 : });
104 1 : }
|