From f81a43d08eb81c7a573d8fa43ec723891e4d0186 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Fri, 15 Aug 2025 22:53:03 +0300 Subject: [PATCH 1/6] create PerfTracker --- .../src/compiler/PerfTracker.ts | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 packages/cubejs-schema-compiler/src/compiler/PerfTracker.ts diff --git a/packages/cubejs-schema-compiler/src/compiler/PerfTracker.ts b/packages/cubejs-schema-compiler/src/compiler/PerfTracker.ts new file mode 100644 index 0000000000000..acbdb2488a478 --- /dev/null +++ b/packages/cubejs-schema-compiler/src/compiler/PerfTracker.ts @@ -0,0 +1,88 @@ +import { performance, PerformanceObserver } from 'perf_hooks'; + +interface PerfMetric { + count: number; + totalTime: number; + avgTime: number; +} + +interface PerfStats { + [key: string]: PerfMetric; +} + +class PerfTracker { + private metrics: PerfStats = {}; + + private globalMetric: string | null = null; + + public constructor() { + const obs = new PerformanceObserver((items) => { + for (const entry of items.getEntries()) { + const { name } = entry; + if (!this.metrics[name]) { + this.metrics[name] = { count: 0, totalTime: 0, avgTime: 0 }; + } + const m = this.metrics[name]; + m.count++; + m.totalTime += entry.duration; + m.avgTime = m.totalTime / m.count; + } + }); + obs.observe({ entryTypes: ['measure'] }); + } + + public start(name: string, global: boolean = false): { end: () => void } { + const uid = `${name}-${performance.now()}`; + const startMark = `${uid}-start`; + const endMark = `${uid}-end`; + performance.mark(startMark); + + if (global && !this.globalMetric) { + this.globalMetric = name; + } + + let ended = false; + + return { + end: () => { + if (ended) return; + performance.mark(endMark); + performance.measure(name, startMark, endMark); + ended = true; + } + }; + } + + public printReport() { + console.log('\nšŸš€ PERFORMANCE REPORT šŸš€\n'); + console.log('═'.repeat(90)); + + const sorted = Object.entries(this.metrics) + .sort(([, a], [, b]) => b.totalTime - a.totalTime); + + if (!sorted.length) { + console.log('No performance data collected.'); + return; + } + + let totalTime: number = 0; + + if (this.globalMetric) { + totalTime = this.metrics[this.globalMetric]?.totalTime; + } else { + totalTime = sorted.reduce((sum, [, m]) => sum + m.totalTime, 0); + } + + console.log(`ā±ļø TOTAL TIME: ${totalTime.toFixed(2)}ms\n`); + + sorted.forEach(([name, m]) => { + const pct = totalTime > 0 ? (m.totalTime / totalTime * 100) : 0; + console.log(` ${name.padEnd(40)} │ ${m.totalTime.toFixed(2).padStart(8)}ms │ ${m.avgTime.toFixed(2).padStart(7)}ms avg │ ${pct.toFixed(1).padStart(5)}% │ ${m.count.toString().padStart(4)} calls`); + }); + + console.log('═'.repeat(90)); + console.log('šŸŽÆ End of Performance Report\n'); + } +} + +export const perfTracker = new PerfTracker(); From 8586948dd6fa5809ec26e19e45b3f74866bcc7e2 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Thu, 21 Aug 2025 15:24:28 +0300 Subject: [PATCH 2/6] inject PerfTracker for tracking in dataschemacompiler --- .../src/compiler/DataSchemaCompiler.ts | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/packages/cubejs-schema-compiler/src/compiler/DataSchemaCompiler.ts b/packages/cubejs-schema-compiler/src/compiler/DataSchemaCompiler.ts index ea230df9fb0b2..d5c30decfc634 100644 --- a/packages/cubejs-schema-compiler/src/compiler/DataSchemaCompiler.ts +++ b/packages/cubejs-schema-compiler/src/compiler/DataSchemaCompiler.ts @@ -23,6 +23,7 @@ import { CompilerInterface } from './PrepareCompiler'; import { YamlCompiler } from './YamlCompiler'; import { CubeDictionary } from './CubeDictionary'; import { CompilerCache } from './CompilerCache'; +import { perfTracker } from './PerfTracker'; const ctxFileStorage = new AsyncLocalStorage(); @@ -211,8 +212,12 @@ export class DataSchemaCompiler { } protected async doCompile() { + const compileTimer = perfTracker.start('doCompile', true); + const files = await this.repository.dataSchemaFiles(); + console.log(`Compiling ${files.length} files...`); + this.pythonContext = await this.loadPythonContext(files, 'globals.py'); this.yamlCompiler.initFromPythonContext(this.pythonContext); @@ -235,6 +240,8 @@ export class DataSchemaCompiler { } const transpile = async (stage: CompileStage): Promise => { + const transpileTimer = perfTracker.start(`transpilation-stage-${stage}`); + let cubeNames: string[] = []; let cubeSymbols: Record> = {}; let transpilerNames: string[] = []; @@ -301,6 +308,8 @@ export class DataSchemaCompiler { results = await Promise.all(toCompile.map(f => this.transpileFile(f, errorsReport, {}))); } + transpileTimer.end(); + return results.filter(f => !!f) as FileContent[]; }; @@ -398,6 +407,8 @@ export class DataSchemaCompiler { }); const compilePhase = async (compilers: CompileCubeFilesCompilers, stage: 0 | 1 | 2 | 3) => { + const compilePhaseTimer = perfTracker.start(`compilation-phase-${stage}`); + // clear the objects for the next phase cubes = []; exports = {}; @@ -406,7 +417,9 @@ export class DataSchemaCompiler { asyncModules = []; transpiledFiles = await transpile(stage); - return this.compileCubeFiles(cubes, contexts, compiledFiles, asyncModules, compilers, transpiledFiles, errorsReport); + const res = this.compileCubeFiles(cubes, contexts, compiledFiles, asyncModules, compilers, transpiledFiles, errorsReport); + compilePhaseTimer.end(); + return res; }; return compilePhase({ cubeCompilers: this.cubeNameCompilers }, 0) @@ -442,6 +455,12 @@ export class DataSchemaCompiler { } else if (transpilationWorkerThreads && this.workerPool) { this.workerPool.terminate(); } + + // End overall compilation timing and print performance report + compileTimer.end(); + setImmediate(() => { + perfTracker.printReport(); + }); }); } @@ -640,7 +659,9 @@ export class DataSchemaCompiler { asyncModules ); }); + const asyncModulesTimer = perfTracker.start('asyncModules.reduce (jinja)'); await asyncModules.reduce((a: Promise, b: CallableFunction) => a.then(() => b()), Promise.resolve()); + asyncModulesTimer.end(); return this.compileObjects(compilers.cubeCompilers || [], cubes, errorsReport) .then(() => this.compileObjects(compilers.contextCompilers || [], contexts, errorsReport)); } @@ -663,7 +684,9 @@ export class DataSchemaCompiler { compiledFiles[file.fileName] = true; if (file.fileName.endsWith('.js')) { + const compileJsFileTimer = perfTracker.start('compileJsFile'); this.compileJsFile(file, errorsReport, { doSyntaxCheck }); + compileJsFileTimer.end(); } else if (file.fileName.endsWith('.yml.jinja') || file.fileName.endsWith('.yaml.jinja') || ( file.fileName.endsWith('.yml') || file.fileName.endsWith('.yaml') @@ -677,7 +700,9 @@ export class DataSchemaCompiler { this.pythonContext! )); } else if (file.fileName.endsWith('.yml') || file.fileName.endsWith('.yaml')) { + const compileYamlFileTimer = perfTracker.start('compileYamlFile'); this.yamlCompiler.compileYamlFile(file, errorsReport); + compileYamlFileTimer.end(); } } From 38212b911a2ef0c8d221f54587ad6a16949d145d Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Mon, 18 Aug 2025 19:41:59 +0300 Subject: [PATCH 3/6] perf tracking in yaml compiler --- .../src/compiler/YamlCompiler.ts | 36 ++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/packages/cubejs-schema-compiler/src/compiler/YamlCompiler.ts b/packages/cubejs-schema-compiler/src/compiler/YamlCompiler.ts index abc207be9cb05..9eb555b5df951 100644 --- a/packages/cubejs-schema-compiler/src/compiler/YamlCompiler.ts +++ b/packages/cubejs-schema-compiler/src/compiler/YamlCompiler.ts @@ -16,6 +16,7 @@ import { nonStringFields } from './CubeValidator'; import { CubeDictionary } from './CubeDictionary'; import { ErrorReporter } from './ErrorReporter'; import { camelizeCube } from './utils'; +import { perfTracker } from './PerfTracker'; type EscapeStateStack = { inFormattedStr?: boolean; @@ -24,6 +25,8 @@ type EscapeStateStack = { depth?: number; }; +const PY_TEMPLATE_SYNTAX = /\{.*}/ms; + export class YamlCompiler { public dataSchemaCompiler: DataSchemaCompiler | null = null; @@ -90,7 +93,9 @@ export class YamlCompiler { for (const key of Object.keys(yamlObj)) { if (key === 'cubes') { (yamlObj.cubes || []).forEach(({ name, ...cube }) => { + const transpileAndPrepareJsFileTimer = perfTracker.start('yaml-transpileAndPrepareJsFile'); const transpiledFile = this.transpileAndPrepareJsFile(file, 'cube', { name, ...cube }, errorsReport); + transpileAndPrepareJsFileTimer.end(); this.dataSchemaCompiler?.compileJsFile(transpiledFile, errorsReport); }); } else if (key === 'views') { @@ -132,7 +137,10 @@ export class YamlCompiler { cubeObj.hierarchies = this.yamlArrayToObj(cubeObj.hierarchies || [], 'hierarchies', errorsReport); - return this.transpileYaml(cubeObj, [], cubeObj.name, errorsReport); + const transpileYamlTimer = perfTracker.start('transpileYaml'); + const res = this.transpileYaml(cubeObj, [], cubeObj.name, errorsReport); + transpileYamlTimer.end(); + return res; } private transpileYaml(obj, propertyPath, cubeName, errorsReport: ErrorReporter) { @@ -146,11 +154,17 @@ export class YamlCompiler { return this.parsePythonIntoArrowFunction(obj, cubeName, obj, errorsReport); } else if (Array.isArray(obj)) { const resultAst = t.program([t.expressionStatement(t.arrayExpression(obj.map(code => { - let ast: t.Program | t.NullLiteral | t.BooleanLiteral | t.NumericLiteral | null = null; + let ast: t.Program | t.NullLiteral | t.BooleanLiteral | t.NumericLiteral | t.StringLiteral | null = null; // Special case for accessPolicy.rowLevel.filter.values and other values-like fields if (propertyPath[propertyPath.length - 1] === 'values') { if (typeof code === 'string') { - ast = this.parsePythonAndTranspileToJs(`f"${this.escapeDoubleQuotes(code)}"`, errorsReport); + if (code.match(PY_TEMPLATE_SYNTAX)) { + const parsePythonAndTranspileToJsTimer184 = perfTracker.start('parsePythonAndTranspileToJs call 184'); + ast = this.parsePythonAndTranspileToJs(`f"${this.escapeDoubleQuotes(code)}"`, errorsReport); + parsePythonAndTranspileToJsTimer184.end(); + } else { + ast = t.stringLiteral(code); + } } else if (typeof code === 'boolean') { ast = t.booleanLiteral(code); } else if (typeof code === 'number') { @@ -162,7 +176,9 @@ export class YamlCompiler { } } if (ast === null) { + const parsePythonAndTranspileToJsTimer201 = perfTracker.start('parsePythonAndTranspileToJs call 201'); ast = this.parsePythonAndTranspileToJs(code, errorsReport); + parsePythonAndTranspileToJsTimer201.end(); } return this.extractProgramBodyIfNeeded(ast); }).filter(ast => !!ast)))]); @@ -173,7 +189,9 @@ export class YamlCompiler { } if (propertyPath[propertyPath.length - 1] === 'extends') { + const parsePythonAndTranspileToJsTimer214 = perfTracker.start('parsePythonAndTranspileToJs call 214'); const ast = this.parsePythonAndTranspileToJs(obj, errorsReport); + parsePythonAndTranspileToJsTimer214.end(); return this.astIntoArrowFunction(ast, obj, cubeName, name => this.cubeDictionary.resolveCube(name)); } else if (typeof obj === 'string') { let code = obj; @@ -182,7 +200,9 @@ export class YamlCompiler { code = `f"${this.escapeDoubleQuotes(obj)}"`; } + const parsePythonAndTranspileToJsTimer225 = perfTracker.start('parsePythonAndTranspileToJs call 225'); const ast = this.parsePythonAndTranspileToJs(code, errorsReport); + parsePythonAndTranspileToJsTimer225.end(); return this.extractProgramBodyIfNeeded(ast); } else if (typeof obj === 'boolean') { return t.booleanLiteral(obj); @@ -260,7 +280,9 @@ export class YamlCompiler { } private parsePythonIntoArrowFunction(codeString: string, cubeName, originalObj, errorsReport: ErrorReporter) { + const parsePythonAndTranspileToJsTimer301 = perfTracker.start('parsePythonAndTranspileToJs call 301'); const ast = this.parsePythonAndTranspileToJs(codeString, errorsReport); + parsePythonAndTranspileToJsTimer301.end(); return this.astIntoArrowFunction(ast as any, codeString, cubeName); } @@ -270,8 +292,12 @@ export class YamlCompiler { } try { + const parsePythonAndTranspileToJsTimer = perfTracker.start('PythonParser->transpileToJs()'); + const pythonParser = new PythonParser(codeString); - return pythonParser.transpileToJs(); + const res = pythonParser.transpileToJs(); + parsePythonAndTranspileToJsTimer.end(); + return res; } catch (e: any) { errorsReport.error(`Can't parse python expression. Most likely this type of syntax isn't supported yet: ${e.message || e}`); } @@ -280,6 +306,7 @@ export class YamlCompiler { } private astIntoArrowFunction(input: t.Program | t.NullLiteral, codeString: string, cubeName, resolveSymbol?: (string) => any) { + const astIntoArrowFunctionTimer = perfTracker.start('astIntoArrowFunction'); const initialJs = babelGenerator(input, {}, codeString).code; // Re-parse generated JS to set all necessary parent paths @@ -304,6 +331,7 @@ export class YamlCompiler { babelTraverse(ast, traverseObj); const body: any = ast.program.body[0]; + astIntoArrowFunctionTimer.end(); return body?.expression; } From 25ef517cb03bcd2c7ce125840e556c93412042b2 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Wed, 20 Aug 2025 15:30:48 +0300 Subject: [PATCH 4/6] attempt to optimize py parser calls --- .../src/compiler/YamlCompiler.ts | 38 +++++++++++++++---- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/packages/cubejs-schema-compiler/src/compiler/YamlCompiler.ts b/packages/cubejs-schema-compiler/src/compiler/YamlCompiler.ts index 9eb555b5df951..a409f1b074ddc 100644 --- a/packages/cubejs-schema-compiler/src/compiler/YamlCompiler.ts +++ b/packages/cubejs-schema-compiler/src/compiler/YamlCompiler.ts @@ -149,9 +149,19 @@ export class YamlCompiler { const fullPath = propertyPath.join('.'); if (fullPath.match(p)) { if (typeof obj === 'string' && ['sql', 'sqlTable'].includes(propertyPath[propertyPath.length - 1])) { - return this.parsePythonIntoArrowFunction(`f"${this.escapeDoubleQuotes(obj)}"`, cubeName, obj, errorsReport); + if (obj.match(PY_TEMPLATE_SYNTAX)) { + return this.parsePythonIntoArrowFunction(`f"${this.escapeDoubleQuotes(obj)}"`, cubeName, obj, errorsReport); + } else { + // Optimization: directly create arrow function returning string instead of parsing Python + return t.arrowFunctionExpression([], t.stringLiteral(obj)); + } } else if (typeof obj === 'string') { - return this.parsePythonIntoArrowFunction(obj, cubeName, obj, errorsReport); + if (obj.match(PY_TEMPLATE_SYNTAX)) { + return this.parsePythonIntoArrowFunction(obj, cubeName, obj, errorsReport); + } else { + // Optimization: directly create arrow function returning identifier instead of parsing Python + return this.astIntoArrowFunction(t.program([t.expressionStatement(t.identifier(obj))]), obj, cubeName); + } } else if (Array.isArray(obj)) { const resultAst = t.program([t.expressionStatement(t.arrayExpression(obj.map(code => { let ast: t.Program | t.NullLiteral | t.BooleanLiteral | t.NumericLiteral | t.StringLiteral | null = null; @@ -172,7 +182,7 @@ export class YamlCompiler { } else if (code instanceof Date) { // Special case when dates are defined in YAML as strings without quotes // YAML parser treats them as Date objects, but for conversion we need them as strings - ast = this.parsePythonAndTranspileToJs(`f"${this.escapeDoubleQuotes(code.toISOString())}"`, errorsReport); + ast = t.stringLiteral(code.toISOString()); } } if (ast === null) { @@ -196,14 +206,26 @@ export class YamlCompiler { } else if (typeof obj === 'string') { let code = obj; + if (obj === '') { + return t.nullLiteral(); + } + + if (code.match(PY_TEMPLATE_SYNTAX)) { + if (!nonStringFields.has(propertyPath[propertyPath.length - 1])) { + code = `f"${this.escapeDoubleQuotes(obj)}"`; + } + + const parsePythonAndTranspileToJsTimer225 = perfTracker.start('parsePythonAndTranspileToJs call 225'); + const ast = this.parsePythonAndTranspileToJs(code, errorsReport); + parsePythonAndTranspileToJsTimer225.end(); + return this.extractProgramBodyIfNeeded(ast); + } + if (!nonStringFields.has(propertyPath[propertyPath.length - 1])) { - code = `f"${this.escapeDoubleQuotes(obj)}"`; + return t.templateLiteral([t.templateElement({ raw: code, cooked: code })], []); } - const parsePythonAndTranspileToJsTimer225 = perfTracker.start('parsePythonAndTranspileToJs call 225'); - const ast = this.parsePythonAndTranspileToJs(code, errorsReport); - parsePythonAndTranspileToJsTimer225.end(); - return this.extractProgramBodyIfNeeded(ast); + return t.identifier(code); } else if (typeof obj === 'boolean') { return t.booleanLiteral(obj); } else if (typeof obj === 'number') { From cf10a91d7064d76409df57b453bd61945b626702 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Thu, 21 Aug 2025 18:40:09 +0300 Subject: [PATCH 5/6] remove perfTracker tracking... --- .../src/compiler/DataSchemaCompiler.ts | 27 +------------------ .../src/compiler/YamlCompiler.ts | 26 ++---------------- 2 files changed, 3 insertions(+), 50 deletions(-) diff --git a/packages/cubejs-schema-compiler/src/compiler/DataSchemaCompiler.ts b/packages/cubejs-schema-compiler/src/compiler/DataSchemaCompiler.ts index d5c30decfc634..ea230df9fb0b2 100644 --- a/packages/cubejs-schema-compiler/src/compiler/DataSchemaCompiler.ts +++ b/packages/cubejs-schema-compiler/src/compiler/DataSchemaCompiler.ts @@ -23,7 +23,6 @@ import { CompilerInterface } from './PrepareCompiler'; import { YamlCompiler } from './YamlCompiler'; import { CubeDictionary } from './CubeDictionary'; import { CompilerCache } from './CompilerCache'; -import { perfTracker } from './PerfTracker'; const ctxFileStorage = new AsyncLocalStorage(); @@ -212,12 +211,8 @@ export class DataSchemaCompiler { } protected async doCompile() { - const compileTimer = perfTracker.start('doCompile', true); - const files = await this.repository.dataSchemaFiles(); - console.log(`Compiling ${files.length} files...`); - this.pythonContext = await this.loadPythonContext(files, 'globals.py'); this.yamlCompiler.initFromPythonContext(this.pythonContext); @@ -240,8 +235,6 @@ export class DataSchemaCompiler { } const transpile = async (stage: CompileStage): Promise => { - const transpileTimer = perfTracker.start(`transpilation-stage-${stage}`); - let cubeNames: string[] = []; let cubeSymbols: Record> = {}; let transpilerNames: string[] = []; @@ -308,8 +301,6 @@ export class DataSchemaCompiler { results = await Promise.all(toCompile.map(f => this.transpileFile(f, errorsReport, {}))); } - transpileTimer.end(); - return results.filter(f => !!f) as FileContent[]; }; @@ -407,8 +398,6 @@ export class DataSchemaCompiler { }); const compilePhase = async (compilers: CompileCubeFilesCompilers, stage: 0 | 1 | 2 | 3) => { - const compilePhaseTimer = perfTracker.start(`compilation-phase-${stage}`); - // clear the objects for the next phase cubes = []; exports = {}; @@ -417,9 +406,7 @@ export class DataSchemaCompiler { asyncModules = []; transpiledFiles = await transpile(stage); - const res = this.compileCubeFiles(cubes, contexts, compiledFiles, asyncModules, compilers, transpiledFiles, errorsReport); - compilePhaseTimer.end(); - return res; + return this.compileCubeFiles(cubes, contexts, compiledFiles, asyncModules, compilers, transpiledFiles, errorsReport); }; return compilePhase({ cubeCompilers: this.cubeNameCompilers }, 0) @@ -455,12 +442,6 @@ export class DataSchemaCompiler { } else if (transpilationWorkerThreads && this.workerPool) { this.workerPool.terminate(); } - - // End overall compilation timing and print performance report - compileTimer.end(); - setImmediate(() => { - perfTracker.printReport(); - }); }); } @@ -659,9 +640,7 @@ export class DataSchemaCompiler { asyncModules ); }); - const asyncModulesTimer = perfTracker.start('asyncModules.reduce (jinja)'); await asyncModules.reduce((a: Promise, b: CallableFunction) => a.then(() => b()), Promise.resolve()); - asyncModulesTimer.end(); return this.compileObjects(compilers.cubeCompilers || [], cubes, errorsReport) .then(() => this.compileObjects(compilers.contextCompilers || [], contexts, errorsReport)); } @@ -684,9 +663,7 @@ export class DataSchemaCompiler { compiledFiles[file.fileName] = true; if (file.fileName.endsWith('.js')) { - const compileJsFileTimer = perfTracker.start('compileJsFile'); this.compileJsFile(file, errorsReport, { doSyntaxCheck }); - compileJsFileTimer.end(); } else if (file.fileName.endsWith('.yml.jinja') || file.fileName.endsWith('.yaml.jinja') || ( file.fileName.endsWith('.yml') || file.fileName.endsWith('.yaml') @@ -700,9 +677,7 @@ export class DataSchemaCompiler { this.pythonContext! )); } else if (file.fileName.endsWith('.yml') || file.fileName.endsWith('.yaml')) { - const compileYamlFileTimer = perfTracker.start('compileYamlFile'); this.yamlCompiler.compileYamlFile(file, errorsReport); - compileYamlFileTimer.end(); } } diff --git a/packages/cubejs-schema-compiler/src/compiler/YamlCompiler.ts b/packages/cubejs-schema-compiler/src/compiler/YamlCompiler.ts index a409f1b074ddc..9b71ed456e28d 100644 --- a/packages/cubejs-schema-compiler/src/compiler/YamlCompiler.ts +++ b/packages/cubejs-schema-compiler/src/compiler/YamlCompiler.ts @@ -16,7 +16,6 @@ import { nonStringFields } from './CubeValidator'; import { CubeDictionary } from './CubeDictionary'; import { ErrorReporter } from './ErrorReporter'; import { camelizeCube } from './utils'; -import { perfTracker } from './PerfTracker'; type EscapeStateStack = { inFormattedStr?: boolean; @@ -93,9 +92,7 @@ export class YamlCompiler { for (const key of Object.keys(yamlObj)) { if (key === 'cubes') { (yamlObj.cubes || []).forEach(({ name, ...cube }) => { - const transpileAndPrepareJsFileTimer = perfTracker.start('yaml-transpileAndPrepareJsFile'); const transpiledFile = this.transpileAndPrepareJsFile(file, 'cube', { name, ...cube }, errorsReport); - transpileAndPrepareJsFileTimer.end(); this.dataSchemaCompiler?.compileJsFile(transpiledFile, errorsReport); }); } else if (key === 'views') { @@ -137,10 +134,7 @@ export class YamlCompiler { cubeObj.hierarchies = this.yamlArrayToObj(cubeObj.hierarchies || [], 'hierarchies', errorsReport); - const transpileYamlTimer = perfTracker.start('transpileYaml'); - const res = this.transpileYaml(cubeObj, [], cubeObj.name, errorsReport); - transpileYamlTimer.end(); - return res; + return this.transpileYaml(cubeObj, [], cubeObj.name, errorsReport); } private transpileYaml(obj, propertyPath, cubeName, errorsReport: ErrorReporter) { @@ -169,9 +163,7 @@ export class YamlCompiler { if (propertyPath[propertyPath.length - 1] === 'values') { if (typeof code === 'string') { if (code.match(PY_TEMPLATE_SYNTAX)) { - const parsePythonAndTranspileToJsTimer184 = perfTracker.start('parsePythonAndTranspileToJs call 184'); ast = this.parsePythonAndTranspileToJs(`f"${this.escapeDoubleQuotes(code)}"`, errorsReport); - parsePythonAndTranspileToJsTimer184.end(); } else { ast = t.stringLiteral(code); } @@ -186,9 +178,7 @@ export class YamlCompiler { } } if (ast === null) { - const parsePythonAndTranspileToJsTimer201 = perfTracker.start('parsePythonAndTranspileToJs call 201'); ast = this.parsePythonAndTranspileToJs(code, errorsReport); - parsePythonAndTranspileToJsTimer201.end(); } return this.extractProgramBodyIfNeeded(ast); }).filter(ast => !!ast)))]); @@ -199,9 +189,7 @@ export class YamlCompiler { } if (propertyPath[propertyPath.length - 1] === 'extends') { - const parsePythonAndTranspileToJsTimer214 = perfTracker.start('parsePythonAndTranspileToJs call 214'); const ast = this.parsePythonAndTranspileToJs(obj, errorsReport); - parsePythonAndTranspileToJsTimer214.end(); return this.astIntoArrowFunction(ast, obj, cubeName, name => this.cubeDictionary.resolveCube(name)); } else if (typeof obj === 'string') { let code = obj; @@ -215,9 +203,7 @@ export class YamlCompiler { code = `f"${this.escapeDoubleQuotes(obj)}"`; } - const parsePythonAndTranspileToJsTimer225 = perfTracker.start('parsePythonAndTranspileToJs call 225'); const ast = this.parsePythonAndTranspileToJs(code, errorsReport); - parsePythonAndTranspileToJsTimer225.end(); return this.extractProgramBodyIfNeeded(ast); } @@ -302,9 +288,7 @@ export class YamlCompiler { } private parsePythonIntoArrowFunction(codeString: string, cubeName, originalObj, errorsReport: ErrorReporter) { - const parsePythonAndTranspileToJsTimer301 = perfTracker.start('parsePythonAndTranspileToJs call 301'); const ast = this.parsePythonAndTranspileToJs(codeString, errorsReport); - parsePythonAndTranspileToJsTimer301.end(); return this.astIntoArrowFunction(ast as any, codeString, cubeName); } @@ -314,12 +298,8 @@ export class YamlCompiler { } try { - const parsePythonAndTranspileToJsTimer = perfTracker.start('PythonParser->transpileToJs()'); - const pythonParser = new PythonParser(codeString); - const res = pythonParser.transpileToJs(); - parsePythonAndTranspileToJsTimer.end(); - return res; + return pythonParser.transpileToJs(); } catch (e: any) { errorsReport.error(`Can't parse python expression. Most likely this type of syntax isn't supported yet: ${e.message || e}`); } @@ -328,7 +308,6 @@ export class YamlCompiler { } private astIntoArrowFunction(input: t.Program | t.NullLiteral, codeString: string, cubeName, resolveSymbol?: (string) => any) { - const astIntoArrowFunctionTimer = perfTracker.start('astIntoArrowFunction'); const initialJs = babelGenerator(input, {}, codeString).code; // Re-parse generated JS to set all necessary parent paths @@ -353,7 +332,6 @@ export class YamlCompiler { babelTraverse(ast, traverseObj); const body: any = ast.program.body[0]; - astIntoArrowFunctionTimer.end(); return body?.expression; } From cf9d35d84bd34c096f69a09c4dcbc8080ab6c09b Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Tue, 26 Aug 2025 22:07:42 +0300 Subject: [PATCH 6/6] revert some [dangerous] changes --- .../src/compiler/YamlCompiler.ts | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/packages/cubejs-schema-compiler/src/compiler/YamlCompiler.ts b/packages/cubejs-schema-compiler/src/compiler/YamlCompiler.ts index 9b71ed456e28d..0feb91db81019 100644 --- a/packages/cubejs-schema-compiler/src/compiler/YamlCompiler.ts +++ b/packages/cubejs-schema-compiler/src/compiler/YamlCompiler.ts @@ -194,24 +194,12 @@ export class YamlCompiler { } else if (typeof obj === 'string') { let code = obj; - if (obj === '') { - return t.nullLiteral(); - } - - if (code.match(PY_TEMPLATE_SYNTAX)) { - if (!nonStringFields.has(propertyPath[propertyPath.length - 1])) { - code = `f"${this.escapeDoubleQuotes(obj)}"`; - } - - const ast = this.parsePythonAndTranspileToJs(code, errorsReport); - return this.extractProgramBodyIfNeeded(ast); - } - if (!nonStringFields.has(propertyPath[propertyPath.length - 1])) { - return t.templateLiteral([t.templateElement({ raw: code, cooked: code })], []); + code = `f"${this.escapeDoubleQuotes(obj)}"`; } - return t.identifier(code); + const ast = this.parsePythonAndTranspileToJs(code, errorsReport); + return this.extractProgramBodyIfNeeded(ast); } else if (typeof obj === 'boolean') { return t.booleanLiteral(obj); } else if (typeof obj === 'number') {