/* * CloudBeaver - Cloud Database Manager * Copyright (C) 2020-2025 DBeaver Corp and others * * Licensed under the Apache License, Version 2.0. * you may not use this file except in compliance with the License. */ import { isArraysEqual } from '@cloudbeaver/core-utils'; import { action, computed, makeObservable, observable } from 'mobx'; export interface IQueryInfo { start: number; end: number; } export interface ISQLScriptSegment { query: string; /** query begin index in script */ begin: number; /** query end index in script */ end: number; } export interface ISQLScriptLine { index: number; begin: number; end: number; } export class SQLParser { get scripts(): ISQLScriptSegment[] { return this._scripts; } get actualScript(): string { return this.script; } private _scripts: ISQLScriptSegment[]; private script: string; private lastParsingArgs: any[]; constructor() { this._scripts = []; this.script = ''; this.lastParsingArgs = []; makeObservable(this, { actualScript: computed, _scripts: observable.ref, script: observable.ref, getScriptSegment: action, getSegment: action, getQueryAtPos: action, setScript: action, setQueries: action, }); } parse | IQueryInfo[]>( parser: (script: string, ...args: Args) => TResult, ...args: Args ): TResult extends Promise ? Promise : void { const parsingScript = this.actualScript; const parsingArgs = [parsingScript, parser, ...args]; if (isArraysEqual(this.lastParsingArgs, parsingArgs)) { return undefined as any; } const result = parser(parsingScript, ...args); const applyResult = (queries: IQueryInfo[]) => { if (this.actualScript === parsingScript) { this.setQueries(queries); this.lastParsingArgs = parsingArgs; } }; if (result instanceof Promise) { return result.then(applyResult) as any; } applyResult(result); return undefined as any; } getScriptSegment(): ISQLScriptSegment { const script = this.actualScript || ''; return { query: script, begin: 0, end: script.length, }; } getSegment(begin: number, end: number): ISQLScriptSegment | undefined { if (begin === end) { return this.getQueryAtPos(begin); } return { query: this.actualScript.substring(begin, end), begin, end, }; } setScript(script: string): void { this.script = script; } setQueries(queries: IQueryInfo[]): this { this._scripts = queries.map(query => ({ query: this.actualScript.substring(query.start, query.end), begin: query.start, end: query.end, })); return this; } private getQueryAtPos(position: number): ISQLScriptSegment | undefined { const script = this._scripts.find(script => script.begin <= position && script.end > position); if (script) { return script; } return { query: this.actualScript.substring(position, position), begin: position, end: position, }; } }