@@ -23,8 +23,6 @@ import { resolveNamedNumericFormat, STANDARD_FORMAT_SPECIFIERS, DEFAULT_FORMAT_S
2323import {
2424 EffectiveGranularity ,
2525 NormalizedGranularitiesBlock ,
26- ResolvedGranularitySet ,
27- GRANULARITY_STRING_FIELDS ,
2826 normalizeGranularitiesBlock ,
2927 resolveDimensionGranularities ,
3028 serializeEffectiveGranularities ,
@@ -217,18 +215,12 @@ export class CubeToMetaTransformer implements CompilerInterface {
217215 */
218216 public queries : TransformedCube [ ] ;
219217
220- // Resolved-once global granularities config for this appId, baked into the compiled model.
221- // CompilerApi resolves all config forms (env / static / function) before compile and passes the
222- // result here — the transformer never sees a function.
223- private readonly granularitiesConfig ?: GlobalGranularitiesConfig ;
224-
225218 // Precomputed once in compile() so plain dimensions need no per-dimension resolution: `defaultSet`
226- // and `defaultGlobalCustoms` are shared by reference across every dimension without a local block.
219+ // is shared by reference across every dimension without a local block.
227220 private granularityState ! : {
228221 config : GlobalGranularitiesConfig ;
229222 catalog : Record < string , GranularityDefinition > ;
230223 defaultSet : EffectiveGranularity [ ] ;
231- defaultGlobalCustoms : Record < string , GranularityDefinition > ;
232224 } ;
233225
234226 public constructor (
@@ -237,44 +229,33 @@ export class CubeToMetaTransformer implements CompilerInterface {
237229 contextEvaluator : ContextEvaluator ,
238230 viewGroupEvaluator : ViewGroupEvaluator ,
239231 joinGraph : JoinGraph ,
240- granularitiesConfig ?: GlobalGranularitiesConfig
241232 ) {
242233 this . cubeValidator = cubeValidator ;
243234 this . cubeSymbols = cubeEvaluator ;
244235 this . cubeEvaluator = cubeEvaluator ;
245236 this . contextEvaluator = contextEvaluator ;
246237 this . viewGroupEvaluator = viewGroupEvaluator ;
247238 this . joinGraph = joinGraph ;
248- this . granularitiesConfig = granularitiesConfig ;
249239 this . cubes = [ ] ;
250240 this . queries = [ ] ;
251241 }
252242
253- // The resolved global config baked into this compiled model. Exposed for the /v1/granularities
254- // endpoint, which serves the per-appId catalog from the compiled model rather than re-resolving.
255- public get globalGranularitiesConfig ( ) : GlobalGranularitiesConfig | undefined {
256- return this . granularityState ?. config ;
257- }
258-
259243 public get viewGroups ( ) : CompiledViewGroup [ ] {
260244 return this . viewGroupEvaluator . compiledViewGroups ;
261245 }
262246
263247 public compile ( _cubes : any [ ] , errorReporter : ErrorReporter ) : void {
264- // The config is already resolved (env / static / function) by CompilerApi at compile time and
265- // baked in here — a missing config means the default catalog.
266- const config = this . granularitiesConfig ?? DEFAULT_GRANULARITIES_CONFIG ;
248+ // CubeSymbols resolved the global config at the start of its own compile phase.
249+ const config = this . cubeEvaluator . globalGranularitiesConfig ?? DEFAULT_GRANULARITIES_CONFIG ;
267250 const catalog = buildBuiltInsCatalog ( config ) ;
268- // Resolve the no-local-block ("default") set once; every plain time dimension shares both its
269- // serialized wire form and its global-custom map by reference (no per-dimension resolution).
270- const defaultResolved = resolveDimensionGranularities (
271- normalizeGranularitiesBlock ( undefined ) , config . enabledBuiltIns , config . customGranularities , catalog ,
272- ) ;
251+ // Resolve the no-local-block ("default") set once; every plain time dimension shares the
252+ // serialized wire form by reference (no per-dimension resolution).
273253 this . granularityState = {
274254 config,
275255 catalog,
276- defaultSet : serializeEffectiveGranularities ( defaultResolved ) ,
277- defaultGlobalCustoms : this . globalCustomsOf ( defaultResolved , config , { } ) ,
256+ defaultSet : serializeEffectiveGranularities ( resolveDimensionGranularities (
257+ normalizeGranularitiesBlock ( undefined ) , config . enabledBuiltIns , config . customGranularities , catalog ,
258+ ) ) ,
278259 } ;
279260
280261 this . cubes = this . cubeSymbols . cubeList
@@ -359,11 +340,11 @@ export class CubeToMetaTransformer implements CompilerInterface {
359340 const dimensionVisibility = isCubeVisible
360341 ? this . isVisible ( extendedDimDef , ! extendedDimDef . primaryKey )
361342 : false ;
362- // Snapshot the dimension's LOCAL customs before any merge below: the deprecated
363- // `granularities` meta field must keep listing only the model's own custom granularities.
364- const localCustoms = extendedDimDef . granularities ;
365- const localCustomEntries = localCustoms ? Object . entries ( localCustoms ) : [ ] ;
366343 const { granularitiesBlock } = extendedDimDef as any ;
344+ // The deprecated `granularities` meta field lists only the model's own custom
345+ // granularities — `granularitiesBlock.custom`, which the global merge leaves untouched.
346+ const localCustoms = granularitiesBlock ?. custom ?? extendedDimDef . granularities ;
347+ const localCustomEntries = localCustoms ? Object . entries ( localCustoms ) : [ ] ;
367348 const dimType = this . dimensionDataType ( extendedDimDef . type || 'string' ) ;
368349 const dimFormat = this . transformDimensionFormat ( extendedDimDef ) ;
369350 const dimCurrency = extendedDimDef . currency ?. toUpperCase ( ) ;
@@ -372,23 +353,13 @@ export class CubeToMetaTransformer implements CompilerInterface {
372353 if ( dimType === 'time' ) {
373354 const s = this . granularityState ;
374355 const inputs = this . granularityInputsForDimension ( cubeTitle , localCustoms , granularitiesBlock ) ;
375- // Dimensions with a local block resolve individually; plain ones reuse the shared default
376- // (both the serialized set and the global-custom map) computed once in compile().
377- let globalCustoms : Record < string , GranularityDefinition > ;
378- if ( inputs ) {
379- const resolved = resolveDimensionGranularities (
356+ // Dimensions with a local block resolve individually; plain ones reuse the shared
357+ // default set computed once in compile().
358+ effectiveGranularities = inputs
359+ ? serializeEffectiveGranularities ( resolveDimensionGranularities (
380360 inputs , s . config . enabledBuiltIns , s . config . customGranularities , s . catalog ,
381- ) ;
382- effectiveGranularities = serializeEffectiveGranularities ( resolved ) ;
383- globalCustoms = this . globalCustomsOf ( resolved , s . config , localCustoms ?? { } ) ;
384- } else {
385- effectiveGranularities = s . defaultSet ;
386- globalCustoms = s . defaultGlobalCustoms ;
387- }
388-
389- // Bake the effective GLOBAL customs into the dimension's `granularities` map (SQL resolves
390- // customs by name from this map, and pre-agg matching reads it). Locals win over globals.
391- this . mergeGlobalCustomsIntoDimension ( cubeName , dimensionName , extendedDimDef , localCustoms , globalCustoms ) ;
361+ ) )
362+ : s . defaultSet ;
392363 }
393364
394365 return {
@@ -486,64 +457,6 @@ export class CubeToMetaTransformer implements CompilerInterface {
486457 return { includes : block . includes , excludes : block . excludes , custom } ;
487458 }
488459
489- // From an already-resolved set, extract the GLOBAL customs a dimension exposes: entries that are
490- // custom, defined in the global config, and not shadowed by a local of the same name. Projected
491- // through GRANULARITY_STRING_FIELDS (the shared field list, so it can't drift from serialize/hash).
492- private globalCustomsOf (
493- resolved : ResolvedGranularitySet ,
494- config : GlobalGranularitiesConfig ,
495- localCustoms : Record < string , GranularityDefinition > ,
496- ) : Record < string , GranularityDefinition > {
497- const out : Record < string , GranularityDefinition > = { } ;
498- for ( const [ name , def ] of Object . entries ( resolved ) ) {
499- if ( def . type === 'custom' &&
500- Object . prototype . hasOwnProperty . call ( config . customGranularities , name ) &&
501- ! Object . prototype . hasOwnProperty . call ( localCustoms , name )
502- ) {
503- const projected : GranularityDefinition = { } as GranularityDefinition ;
504- for ( const field of GRANULARITY_STRING_FIELDS ) {
505- if ( def [ field ] !== undefined ) {
506- ( projected as any ) [ field ] = def [ field ] ;
507- }
508- }
509- out [ name ] = projected ;
510- }
511- }
512- return out ;
513- }
514-
515- // Bake global customs into a dimension's `granularities` map (locals win). Must write BOTH the
516- // `dimDef` object (pre-agg matching) and the distinct `symbols[cube][dim]` object (SQL
517- // resolveGranularity) — writing one leaves the other unable to resolve the custom. Reassign, never
518- // mutate in place, so a view dim sharing its source's map by reference isn't contaminated.
519- private mergeGlobalCustomsIntoDimension (
520- cubeName : string ,
521- dimensionName : string ,
522- dimDef : ExtendedCubeSymbolDefinition ,
523- localCustoms : Record < string , GranularityDefinition > | undefined ,
524- globalCustoms : Record < string , GranularityDefinition > ,
525- ) : void {
526- if ( Object . keys ( globalCustoms ) . length === 0 ) {
527- return ;
528- }
529- const hasLocals = ! ! localCustoms && Object . keys ( localCustoms ) . length > 0 ;
530-
531- // With no locals the baked map IS the shared globalCustoms — assign it by reference (every plain
532- // dimension then shares one object). Copy-on-write only when locals must be layered on top.
533- const write = ( existing : Record < string , GranularityDefinition > | undefined ) => (
534- existing && Object . keys ( existing ) . length > 0
535- ? { ...globalCustoms , ...existing } // globals first, locals last so locals win on collisions
536- : globalCustoms
537- ) ;
538-
539- dimDef . granularities = write ( hasLocals ? localCustoms : undefined ) ;
540-
541- const symbolDim = ( this . cubeEvaluator as any ) . symbols ?. [ cubeName ] ?. [ dimensionName ] ;
542- if ( symbolDim && symbolDim !== dimDef ) {
543- symbolDim . granularities = write ( symbolDim . granularities as Record < string , GranularityDefinition > | undefined ) ;
544- }
545- }
546-
547460 public queriesForContext ( contextId : string | null | undefined ) : TransformedCube [ ] {
548461 // return All queries if no context pass
549462 if ( contextId == null || contextId . length === 0 ) {
0 commit comments