UNPKG

drizzle-cube

Version:

Drizzle ORM-first semantic layer with Cube.js compatibility. Type-safe analytics and dashboards with SQL injection protection.

2 lines 307 kB
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const s=require("drizzle-orm");class dE{preprocessCalculatedTemplate(E){return E}buildPattern(E,T){switch(E){case"contains":case"notContains":return`%${T}%`;case"startsWith":return`${T}%`;case"endsWith":return`%${T}`;default:return T}}}class fT extends dE{getEngineType(){return"postgres"}buildTimeDimension(E,T){switch(E){case"year":return s.sql`DATE_TRUNC('year', ${T}::timestamp)`;case"quarter":return s.sql`DATE_TRUNC('quarter', ${T}::timestamp)`;case"month":return s.sql`DATE_TRUNC('month', ${T}::timestamp)`;case"week":return s.sql`DATE_TRUNC('week', ${T}::timestamp)`;case"day":return s.sql`DATE_TRUNC('day', ${T}::timestamp)::timestamp`;case"hour":return s.sql`DATE_TRUNC('hour', ${T}::timestamp)`;case"minute":return s.sql`DATE_TRUNC('minute', ${T}::timestamp)`;case"second":return s.sql`DATE_TRUNC('second', ${T}::timestamp)`;default:return T}}buildStringCondition(E,T,A){switch(T){case"contains":return s.sql`${E} ILIKE ${`%${A}%`}`;case"notContains":return s.sql`${E} NOT ILIKE ${`%${A}%`}`;case"startsWith":return s.sql`${E} ILIKE ${`${A}%`}`;case"endsWith":return s.sql`${E} ILIKE ${`%${A}`}`;case"like":return s.sql`${E} LIKE ${A}`;case"notLike":return s.sql`${E} NOT LIKE ${A}`;case"ilike":return s.sql`${E} ILIKE ${A}`;case"regex":return s.sql`${E} ~* ${A}`;case"notRegex":return s.sql`${E} !~* ${A}`;default:throw new Error(`Unsupported string operator: ${T}`)}}castToType(E,T){switch(T){case"timestamp":return s.sql`${E}::timestamp`;case"decimal":return s.sql`${E}::decimal`;case"integer":return s.sql`${E}::integer`;default:throw new Error(`Unsupported cast type: ${T}`)}}buildAvg(E){return s.sql`COALESCE(AVG(${E}), 0)`}buildCaseWhen(E,T){const A=E.map(e=>s.sql`WHEN ${e.when} THEN ${e.then}`).reduce((e,S)=>s.sql`${e} ${S}`);return T!==void 0?s.sql`CASE ${A} ELSE ${T} END`:s.sql`CASE ${A} END`}buildBooleanLiteral(E){return E?s.sql`TRUE`:s.sql`FALSE`}convertFilterValue(E){return E}prepareDateValue(E){return E}isTimestampInteger(){return!1}convertTimeDimensionResult(E){return E}}class LT extends dE{getEngineType(){return"mysql"}buildTimeDimension(E,T){const A={year:"%Y-01-01 00:00:00",quarter:"%Y-%q-01 00:00:00",month:"%Y-%m-01 00:00:00",week:"%Y-%u-01 00:00:00",day:"%Y-%m-%d 00:00:00",hour:"%Y-%m-%d %H:00:00",minute:"%Y-%m-%d %H:%i:00",second:"%Y-%m-%d %H:%i:%s"};switch(E){case"quarter":return s.sql`DATE_ADD(MAKEDATE(YEAR(${T}), 1), INTERVAL (QUARTER(${T}) - 1) * 3 MONTH)`;case"week":return s.sql`DATE_SUB(${T}, INTERVAL WEEKDAY(${T}) DAY)`;default:const e=A[E];return e?s.sql`STR_TO_DATE(DATE_FORMAT(${T}, ${e}), '%Y-%m-%d %H:%i:%s')`:T}}buildStringCondition(E,T,A){switch(T){case"contains":return s.sql`LOWER(${E}) LIKE ${`%${A.toLowerCase()}%`}`;case"notContains":return s.sql`LOWER(${E}) NOT LIKE ${`%${A.toLowerCase()}%`}`;case"startsWith":return s.sql`LOWER(${E}) LIKE ${`${A.toLowerCase()}%`}`;case"endsWith":return s.sql`LOWER(${E}) LIKE ${`%${A.toLowerCase()}`}`;case"like":return s.sql`${E} LIKE ${A}`;case"notLike":return s.sql`${E} NOT LIKE ${A}`;case"ilike":return s.sql`LOWER(${E}) LIKE ${A.toLowerCase()}`;case"regex":return s.sql`${E} REGEXP ${A}`;case"notRegex":return s.sql`${E} NOT REGEXP ${A}`;default:throw new Error(`Unsupported string operator: ${T}`)}}castToType(E,T){switch(T){case"timestamp":return s.sql`CAST(${E} AS DATETIME)`;case"decimal":return s.sql`CAST(${E} AS DECIMAL(10,2))`;case"integer":return s.sql`CAST(${E} AS SIGNED INTEGER)`;default:throw new Error(`Unsupported cast type: ${T}`)}}buildAvg(E){return s.sql`IFNULL(AVG(${E}), 0)`}buildCaseWhen(E,T){const A=E.map(e=>s.sql`WHEN ${e.when} THEN ${e.then}`).reduce((e,S)=>s.sql`${e} ${S}`);return T!==void 0?s.sql`CASE ${A} ELSE ${T} END`:s.sql`CASE ${A} END`}buildBooleanLiteral(E){return E?s.sql`TRUE`:s.sql`FALSE`}convertFilterValue(E){return E}prepareDateValue(E){return E}isTimestampInteger(){return!1}convertTimeDimensionResult(E){return E}}class VT extends dE{getEngineType(){return"sqlite"}buildTimeDimension(E,T){switch(E){case"year":return s.sql`datetime(${T}, 'unixepoch', 'start of year')`;case"quarter":const A=s.sql`datetime(${T}, 'unixepoch')`;return s.sql`datetime(${A}, 'start of year', '+' || (((CAST(strftime('%m', ${A}) AS INTEGER) - 1) / 3) * 3) || ' months')`;case"month":return s.sql`datetime(${T}, 'unixepoch', 'start of month')`;case"week":return s.sql`date(datetime(${T}, 'unixepoch'), 'weekday 1', '-6 days')`;case"day":return s.sql`datetime(${T}, 'unixepoch', 'start of day')`;case"hour":const e=s.sql`datetime(${T}, 'unixepoch')`;return s.sql`datetime(strftime('%Y-%m-%d %H:00:00', ${e}))`;case"minute":const S=s.sql`datetime(${T}, 'unixepoch')`;return s.sql`datetime(strftime('%Y-%m-%d %H:%M:00', ${S}))`;case"second":const I=s.sql`datetime(${T}, 'unixepoch')`;return s.sql`datetime(strftime('%Y-%m-%d %H:%M:%S', ${I}))`;default:return s.sql`datetime(${T}, 'unixepoch')`}}buildStringCondition(E,T,A){switch(T){case"contains":return s.sql`LOWER(${E}) LIKE ${`%${A.toLowerCase()}%`}`;case"notContains":return s.sql`LOWER(${E}) NOT LIKE ${`%${A.toLowerCase()}%`}`;case"startsWith":return s.sql`LOWER(${E}) LIKE ${`${A.toLowerCase()}%`}`;case"endsWith":return s.sql`LOWER(${E}) LIKE ${`%${A.toLowerCase()}`}`;case"like":return s.sql`${E} LIKE ${A}`;case"notLike":return s.sql`${E} NOT LIKE ${A}`;case"ilike":return s.sql`LOWER(${E}) LIKE ${A.toLowerCase()}`;case"regex":return s.sql`${E} GLOB ${A}`;case"notRegex":return s.sql`${E} NOT GLOB ${A}`;default:throw new Error(`Unsupported string operator: ${T}`)}}castToType(E,T){switch(T){case"timestamp":return s.sql`datetime(${E} / 1000, 'unixepoch')`;case"decimal":return s.sql`CAST(${E} AS REAL)`;case"integer":return s.sql`CAST(${E} AS INTEGER)`;default:throw new Error(`Unsupported cast type: ${T}`)}}buildAvg(E){return s.sql`IFNULL(AVG(${E}), 0)`}buildCaseWhen(E,T){const A=E.map(e=>e.then&&typeof e.then=="object"&&(e.then.queryChunks||e.then._||e.then.sql)?s.sql`WHEN ${e.when} THEN ${s.sql.raw("(")}${e.then}${s.sql.raw(")")}`:s.sql`WHEN ${e.when} THEN ${e.then}`).reduce((e,S)=>s.sql`${e} ${S}`);return T!==void 0?T&&typeof T=="object"&&(T.queryChunks||T._||T.sql)?s.sql`CASE ${A} ELSE ${s.sql.raw("(")}${T}${s.sql.raw(")")} END`:s.sql`CASE ${A} ELSE ${T} END`:s.sql`CASE ${A} END`}buildBooleanLiteral(E){return E?s.sql`1`:s.sql`0`}preprocessCalculatedTemplate(E){const T=/(\{[^}]+\})\s*\/\s*/g;return E.replace(T,(A,e)=>`${e.replace(/\{([^}]+)\}/,"CAST({$1} AS REAL)")} / `)}convertFilterValue(E){return typeof E=="boolean"?E?1:0:E instanceof Date?E.getTime():Array.isArray(E)?E.map(T=>this.convertFilterValue(T)):E}prepareDateValue(E){if(!(E instanceof Date)){if(typeof E=="number")return E;if(typeof E=="string")return new Date(E).getTime();throw new Error(`prepareDateValue expects a Date object, got ${typeof E}`)}return E.getTime()}isTimestampInteger(){return!0}convertTimeDimensionResult(E){return E}}class WT extends LT{getEngineType(){return"singlestore"}}function XT(R){switch(R){case"postgres":return new fT;case"mysql":return new LT;case"sqlite":return new VT;case"singlestore":return new WT;default:throw new Error(`Unsupported database engine: ${R}`)}}class j{constructor(E,T,A){this.db=E,this.schema=T;const e=A||this.getEngineType();this.databaseAdapter=XT(e)}databaseAdapter}class _T extends j{async execute(E,T){if(E&&typeof E=="object"&&typeof E.execute=="function"){const e=await E.execute();return Array.isArray(e)?e.map(S=>this.convertNumericFields(S,T)):e}if(!this.db.execute)throw new Error("PostgreSQL database instance must have an execute method");const A=await this.db.execute(E);return Array.isArray(A)?A.map(e=>this.convertNumericFields(e,T)):A}convertNumericFields(E,T){if(!E||typeof E!="object")return E;const A={};for(const[e,S]of Object.entries(E))T&&T.includes(e)?A[e]=this.coerceToNumber(S):A[e]=S;return A}coerceToNumber(E){if(E==null||typeof E=="number")return E;if(typeof E=="bigint")return Number(E);if(E&&typeof E=="object"){if(typeof E.toString=="function"){const T=E.toString();if(/^-?\d+(\.\d+)?$/.test(T))return T.includes(".")?parseFloat(T):parseInt(T,10)}if(E.constructor?.name==="Numeric"||E.constructor?.name==="Decimal"||"digits"in E||"sign"in E){const T=E.toString();return parseFloat(T)}return E}if(typeof E=="string"){if(/^-?\d+(\.\d+)?$/.test(E))return E.includes(".")?parseFloat(E):parseInt(E,10);if(!isNaN(parseFloat(E))&&isFinite(parseFloat(E)))return parseFloat(E)}return E}getEngineType(){return"postgres"}}function lE(R,E){return new _T(R,E,"postgres")}class mE extends j{async execute(E,T){if(E&&typeof E=="object"&&typeof E.execute=="function"){const e=await E.execute();return Array.isArray(e)?e.map(S=>this.convertNumericFields(S,T)):e}if(!this.db.execute)throw new Error("MySQL database instance must have an execute method");const A=await this.db.execute(E);return Array.isArray(A)?A.map(e=>this.convertNumericFields(e,T)):A}convertNumericFields(E,T){if(!E||typeof E!="object")return E;const A={};for(const[e,S]of Object.entries(E))T&&T.includes(e)?A[e]=this.coerceToNumber(S):A[e]=S;return A}coerceToNumber(E){if(E==null||typeof E=="number")return E;if(typeof E=="string"){if(/^-?\d+(\.\d+)?$/.test(E))return E.includes(".")?parseFloat(E):parseInt(E,10);if(!isNaN(parseFloat(E))&&isFinite(parseFloat(E)))return parseFloat(E)}return E}getEngineType(){return"mysql"}}function rT(R,E){return new mE(R,E,"mysql")}class nT extends j{async execute(E,T){if(E&&typeof E=="object"&&typeof E.execute=="function"){const A=await E.execute();return Array.isArray(A)?A.map(e=>this.convertNumericFields(e,T)):A}try{if(this.db.all){const A=this.db.all(E);return Array.isArray(A)?A.map(e=>this.convertNumericFields(e,T)):A}else{if(this.db.run)return this.db.run(E);throw new Error("SQLite database instance must have an all() or run() method")}}catch(A){throw new Error(`SQLite execution failed: ${A instanceof Error?A.message:"Unknown error"}`)}}convertNumericFields(E,T){if(!E||typeof E!="object")return E;const A={};for(const[e,S]of Object.entries(E))T&&T.includes(e)?A[e]=this.coerceToNumber(S):A[e]=S;return A}coerceToNumber(E){if(E==null||typeof E=="number")return E;if(typeof E=="string"){if(/^-?\d+(\.\d+)?$/.test(E))return E.includes(".")?parseFloat(E):parseInt(E,10);if(!isNaN(parseFloat(E))&&isFinite(parseFloat(E)))return parseFloat(E)}return E}getEngineType(){return"sqlite"}}function GE(R,E){return new nT(R,E,"sqlite")}class bT extends mE{getEngineType(){return"singlestore"}}function yT(R,E){return new bT(R,E)}function uE(R,E,T){if(T)switch(T){case"postgres":return lE(R,E);case"mysql":return rT(R,E);case"sqlite":return GE(R,E);case"singlestore":return yT(R,E)}if(R.all&&R.run)return GE(R,E);if(R.execute)return lE(R,E);throw new Error("Unable to determine database engine type. Please specify engineType parameter.")}function HE(R){return typeof R=="function"?R():R}function FE(R,E){if(E)return E;switch(R){case"belongsTo":return"inner";case"hasOne":return"left";case"hasMany":return"left";case"belongsToMany":return"left";default:return"left"}}function KT(R){return R&&typeof R=="object"?s.sql`${s.sql`${R}`}`:R}function f(R,E){const T=typeof R=="function"?R(E):R;return KT(T)}function gT(R,E,T){return{...R,cubes:E,currentCube:T}}function $T(R,E){return{name:R,...E}}function wT(R,E){if(R.relationship!=="belongsToMany"||!R.through)throw new Error("expandBelongsToManyJoin can only be called on belongsToMany relationships with through configuration");const{table:T,sourceKey:A,targetKey:e,securitySql:S}=R.through,I=[];for(const N of A){const C=N.as||s.eq;I.push(C(N.source,N.target))}const t=[];for(const N of e){const C=N.as||s.eq;t.push(C(N.source,N.target))}let L;if(S){const N=S(E);L=Array.isArray(N)?N:[N]}const O=FE("belongsToMany",R.sqlJoinType);return{junctionJoins:[{joinType:O,table:T,condition:s.and(...I)},{joinType:O,table:T,condition:s.and(...t)}],junctionSecurityConditions:L}}class W{dependencyGraph;cubes;constructor(E){this.cubes=E instanceof Map?E:new Map([[E.name,E]]),this.dependencyGraph=new Map}extractDependencies(E){const T=/\{([^}]+)\}/g,A=E.matchAll(T),e=[];for(const S of A){const I=S[1].trim();if(I.includes(".")){const[t,L]=I.split(".");e.push({measureName:I,cubeName:t.trim(),fieldName:L.trim()})}else e.push({measureName:I,cubeName:null,fieldName:I})}return e}buildGraph(E){for(const[T,A]of Object.entries(E.measures))if(A.type==="calculated"&&A.calculatedSql){const e=`${E.name}.${T}`,S=this.extractDependencies(A.calculatedSql),I=new Set;for(const t of S){const O=`${t.cubeName||E.name}.${t.fieldName}`;I.add(O)}this.dependencyGraph.set(e,{id:e,dependencies:I,inDegree:0})}this.calculateInDegrees()}buildGraphForMultipleCubes(E){for(const T of E.values())this.buildGraph(T)}calculateInDegrees(){for(const E of this.dependencyGraph.values())E.inDegree=0;for(const E of this.dependencyGraph.values())for(const T of E.dependencies){const A=this.dependencyGraph.get(T);A&&A.inDegree++}}topologicalSort(E){const T=new Map,A=[],e=[];for(const S of E){const I=this.dependencyGraph.get(S);I&&T.set(S,{id:I.id,dependencies:new Set(I.dependencies),inDegree:0})}for(const S of T.values()){let I=0;for(const t of S.dependencies)T.has(t)&&I++;S.inDegree=I}for(const[S,I]of T)I.inDegree===0&&A.push(S);for(;A.length>0;){const S=A.shift();e.push(S);for(const[I,t]of T)t.dependencies.has(S)&&(t.inDegree--,t.inDegree===0&&A.push(I))}if(e.length<T.size){const S=this.detectCycle();throw new Error(`Circular dependency detected in calculated measures: ${S?S.join(" -> "):"unknown cycle"}`)}return e}detectCycle(){const E=new Set,T=new Set,A=[];for(const e of this.dependencyGraph.keys())if(!E.has(e)){const S=this.dfs(e,E,T,A);if(S)return S}return null}dfs(E,T,A,e){T.add(E),A.add(E),e.push(E);const S=this.dependencyGraph.get(E);if(!S)return e.pop(),A.delete(E),null;for(const I of S.dependencies)if(T.has(I)){if(A.has(I)){const t=e.indexOf(I);return[...e.slice(t),I]}}else{const t=this.dfs(I,T,A,e);if(t)return t}return e.pop(),A.delete(E),null}getAllDependencies(E){const T=new Set,A=new Set,e=S=>{if(A.has(S))return;A.add(S);const I=this.dependencyGraph.get(S);if(I)for(const t of I.dependencies)T.add(t),e(t)};return e(E),T}validateDependencies(E){for(const[T,A]of Object.entries(E.measures))if(A.type==="calculated"&&A.calculatedSql){const e=this.extractDependencies(A.calculatedSql);for(const S of e){const I=S.cubeName||E.name,t=this.cubes.get(I);if(!t)throw new Error(`Calculated measure '${E.name}.${T}' references unknown cube '${I}'`);if(!t.measures[S.fieldName])throw new Error(`Calculated measure '${E.name}.${T}' references unknown measure '${S.measureName}'`);if(I===E.name&&S.fieldName===T)throw new Error(`Calculated measure '${E.name}.${T}' cannot reference itself`)}}}populateDependencies(E){for(const[,T]of Object.entries(E.measures))if(T.type==="calculated"&&T.calculatedSql&&!T.dependencies){const A=this.extractDependencies(T.calculatedSql);T.dependencies=A.map(e=>e.measureName)}}static isCalculatedMeasure(E){return E.type==="calculated"&&!!E.calculatedSql}}function JT(R,E){const{cube:T,allCubes:A,resolvedMeasures:e}=E,S=pE(R),I=new Map;for(const C of S){const{originalRef:n,cubeName:_,fieldName:i}=C,o=_||T.name;if(!A.get(o))throw new Error(`Cannot substitute {${n}}: cube '${o}' not found`);const P=`${o}.${i}`,c=e.get(P);if(!c)throw new Error(`Cannot substitute {${n}}: measure '${P}' not resolved yet. Ensure measures are resolved in dependency order.`);const M=c(),u=s.sql`${M}`;I.set(n,u)}const t=[],L=[];let O=0;for(const C of S){const n=`{${C.originalRef}}`,_=R.indexOf(n,O);if(_>=0){t.push(R.substring(O,_));const i=I.get(C.originalRef);i&&L.push(i),O=_+n.length}}if(t.push(R.substring(O)),L.length===0)return s.sql.raw(R);const N=[];for(let C=0;C<t.length;C++)t[C]&&N.push(new s.StringChunk(t[C])),C<L.length&&N.push(L[C]);return s.sql.join(N,s.sql.raw(""))}function pE(R){const E=/\{([^}]+)\}/g,T=R.matchAll(E),A=[];for(const e of T){const S=e[1].trim();if(S.includes(".")){const[I,t]=S.split(".").map(L=>L.trim());A.push({originalRef:S,cubeName:I,fieldName:t})}else A.push({originalRef:S,cubeName:null,fieldName:S})}return A}function xT(R){const E=[];let T=0;for(let I=0;I<R.length;I++)if(R[I]==="{")T++;else if(R[I]==="}"&&(T--,T<0)){E.push(`Unmatched closing brace at position ${I}`);break}T>0&&E.push("Unmatched opening brace in template"),/\{\s*\}/.test(R)&&E.push("Empty member reference {} found in template"),/\{[^}]*\{/.test(R)&&E.push("Nested braces are not allowed in member references");const S=pE(R);for(const I of S){const t=I.cubeName?`${I.cubeName}.${I.fieldName}`:I.fieldName;/^[a-zA-Z_][a-zA-Z0-9_.]*$/.test(t)||E.push(`Invalid member reference {${I.originalRef}}: must start with letter or underscore, and contain only letters, numbers, underscores, and dots`),t.split(".").length>2&&E.push(`Invalid member reference {${I.originalRef}}: only one dot allowed (Cube.measure format)`)}return{isValid:E.length===0,errors:E}}function TE(R,E){const T=pE(R),A=new Set;for(const e of T){const I=`${e.cubeName||E}.${e.fieldName}`;A.add(I)}return Array.from(A)}class iT{constructor(E){this.databaseAdapter=E}buildResolvedMeasures(E,T,A,e){const S=new Map,I=[],t=[],L=new Set(E),O=new W(T);for(const N of T.values())O.buildGraph(N);for(const N of E){const[C,n]=N.split("."),_=T.get(C);if(_&&_.measures&&_.measures[n]){const i=_.measures[n];W.isCalculatedMeasure(i)?(t.push(N),TE(i.calculatedSql,C).forEach(P=>L.add(P)),O.getAllDependencies(N).forEach(P=>{const[c,M]=P.split("."),u=T.get(c);if(u&&u.measures[M]){const H=u.measures[M];W.isCalculatedMeasure(H)&&TE(H.calculatedSql,c).forEach(p=>L.add(p))}})):I.push(N)}}for(const N of L){const[C,n]=N.split("."),_=T.get(C);if(_&&_.measures&&_.measures[n]){const i=_.measures[n];W.isCalculatedMeasure(i)?t.includes(N)||t.push(N):I.includes(N)||I.push(N)}}for(const N of I){const[C,n]=N.split("."),_=T.get(C),i=_.measures[n];if(e){const o=e(N,i,_);S.set(N,()=>o)}else S.set(N,()=>this.buildMeasureExpression(i,A))}if(t.length>0){const N=O.topologicalSort(t);for(const C of N){const[n,_]=C.split("."),i=T.get(n),o=i.measures[_];S.set(C,()=>this.buildCalculatedMeasure(o,i,T,S,A))}}return S}buildSelections(E,T,A){const e={},S=E instanceof Map?E:new Map([[E.name,E]]);if(T.dimensions)for(const I of T.dimensions){const[t,L]=I.split("."),O=S.get(t);if(O&&O.dimensions&&O.dimensions[L]){const N=O.dimensions[L],C=f(N.sql,A);e[I]=s.sql`${C}`.as(I)}}if(T.measures){const I=this.buildResolvedMeasures(T.measures,S,A);for(const t of T.measures){const L=I.get(t);if(L){const O=L();e[t]=s.sql`${O}`.as(t)}}}if(T.timeDimensions)for(const I of T.timeDimensions){const[t,L]=I.dimension.split("."),O=S.get(t);if(O&&O.dimensions&&O.dimensions[L]){const N=O.dimensions[L],C=this.buildTimeDimensionExpression(N.sql,I.granularity,A);e[I.dimension]=s.sql`${C}`.as(I.dimension)}}return Object.keys(e).length===0&&(e.count=s.count()),e}buildCalculatedMeasure(E,T,A,e,S){if(!E.calculatedSql)throw new Error(`Calculated measure '${T.name}.${E.name}' missing calculatedSql property`);const I=this.databaseAdapter.preprocessCalculatedTemplate(E.calculatedSql);return JT(I,{cube:T,allCubes:A,resolvedMeasures:e})}buildCTECalculatedMeasure(E,T,A,e,S){if(!E.calculatedSql)throw new Error(`Calculated measure '${T.name}.${E.name||"unknown"}' missing calculatedSql property`);const I=new Map,t=TE(E.calculatedSql,T.name);for(const L of t){const[O,N]=L.split("."),C=e.get(O);if(C&&C.measures[N]){const n=C.measures[N];if(A.measures.includes(L)){const _=s.sql`${s.sql.identifier(A.cteAlias)}.${s.sql.identifier(N)}`;let i;switch(n.type){case"count":case"countDistinct":case"sum":i=s.sum(_);break;case"avg":i=this.databaseAdapter.buildAvg(_);break;case"min":i=s.min(_);break;case"max":i=s.max(_);break;case"number":i=s.sum(_);break;default:i=s.sum(_)}I.set(L,()=>i)}}}return this.buildCalculatedMeasure(E,T,e,I,S)}buildHavingMeasureExpression(E,T,A,e,S){if(S&&S.preAggregationCTEs){const I=S.preAggregationCTEs.find(t=>t.cube.name===E);if(I&&I.measures.includes(`${E}.${T}`))if(A.type==="calculated"&&A.calculatedSql){const t=S.primaryCube.name===E?S.primaryCube:S.joinCubes?.find(O=>O.cube.name===E)?.cube;if(!t)throw new Error(`Cube ${E} not found in query plan`);const L=new Map([[S.primaryCube.name,S.primaryCube]]);if(S.joinCubes)for(const O of S.joinCubes)L.set(O.cube.name,O.cube);return this.buildCTECalculatedMeasure(A,t,I,L,e)}else{const t=s.sql`${s.sql.identifier(I.cteAlias)}.${s.sql.identifier(T)}`;switch(A.type){case"count":case"countDistinct":case"sum":return s.sum(t);case"avg":return this.databaseAdapter.buildAvg(t);case"min":return s.min(t);case"max":return s.max(t);case"number":return s.sum(t);default:return s.sum(t)}}}return this.buildMeasureExpression(A,e)}buildMeasureExpression(E,T){if(E.type==="calculated")throw new Error(`Cannot build calculated measure '${E.name}' directly. Use buildCalculatedMeasure instead.`);if(!E.sql)throw new Error(`Measure '${E.name}' of type '${E.type}' is missing required 'sql' property. Only calculated measures can omit 'sql'.`);let A=f(E.sql,T);if(E.filters&&E.filters.length>0){const e=E.filters.map(S=>{const I=S(T);return I?s.sql`(${I})`:void 0}).filter(Boolean);if(e.length>0){const S=e.length===1?e[0]:s.and(...e);A=this.databaseAdapter.buildCaseWhen([{when:S,then:A}])}}switch(E.type){case"count":return s.count(A);case"countDistinct":return s.countDistinct(A);case"sum":return s.sum(A);case"avg":return this.databaseAdapter.buildAvg(A);case"min":return s.min(A);case"max":return s.max(A);case"number":return A;default:return s.count(A)}}buildTimeDimensionExpression(E,T,A){const e=f(E,A);return T?this.databaseAdapter.buildTimeDimension(T,e):e instanceof s.SQL?e:s.sql`${e}`}buildWhereConditions(E,T,A,e){const S=[],I=E instanceof Map?E:new Map([[E.name,E]]);if(T.filters&&T.filters.length>0)for(const t of T.filters){const L=this.processFilter(t,I,A,"where",e);L&&S.push(L)}if(T.timeDimensions)for(const t of T.timeDimensions){const[L,O]=t.dimension.split("."),N=I.get(L);if(N&&N.dimensions[O]&&t.dateRange){if(e?.preAggregationCTEs&&e.preAggregationCTEs.some(o=>o.cube.name===L))continue;const C=N.dimensions[O],n=f(C.sql,A),_=this.buildDateRangeCondition(n,t.dateRange);_&&S.push(_)}}return S}buildHavingConditions(E,T,A,e){const S=[],I=E instanceof Map?E:new Map([[E.name,E]]);if(T.filters&&T.filters.length>0)for(const t of T.filters){const L=this.processFilter(t,I,A,"having",e);L&&S.push(L)}return S}processFilter(E,T,A,e,S){if("and"in E||"or"in E){const _=E;if(_.and){const i=_.and.map(o=>this.processFilter(o,T,A,e,S)).filter(o=>o!==null);return i.length>0?s.and(...i):null}if(_.or){const i=_.or.map(o=>this.processFilter(o,T,A,e,S)).filter(o=>o!==null);return i.length>0?s.or(...i):null}}const I=E,[t,L]=I.member.split("."),O=T.get(t);if(!O)return null;const N=O.dimensions[L],C=O.measures[L],n=N||C;if(!n)return null;if(e==="where"&&N){if(S?.preAggregationCTEs&&S.preAggregationCTEs.some(o=>o.cube.name===t))return null;const _=f(N.sql,A);return this.buildFilterCondition(_,I.operator,I.values,n,I.dateRange)}else{if(e==="where"&&C)return null;if(e==="having"&&C){const _=this.buildHavingMeasureExpression(t,L,C,A,S);return this.buildFilterCondition(_,I.operator,I.values,n,I.dateRange)}}return null}buildFilterCondition(E,T,A,e,S){if(S!==void 0){if(T!=="inDateRange")throw new Error(`dateRange can only be used with 'inDateRange' operator, but got '${T}'. Use explicit date values in the 'values' array for other date operators.`);if(e&&e.type!=="time")throw new Error(`dateRange can only be used on time dimensions, but field '${e.name||"unknown"}' has type '${e.type}'`);return this.buildDateRangeCondition(E,S)}if(!A||A.length===0)return T==="equals"?this.databaseAdapter.buildBooleanLiteral(!1):null;const I=A.filter(L=>!(L==null||L===""||typeof L=="string"&&L.includes("\0"))).map(this.databaseAdapter.convertFilterValue);if(I.length===0&&!["set","notSet"].includes(T))return T==="equals"?this.databaseAdapter.buildBooleanLiteral(!1):null;const t=I[0];switch(T){case"equals":if(I.length>1){if(e?.type==="time"){const L=I.map(O=>this.normalizeDate(O)||O);return s.inArray(E,L)}return s.inArray(E,I)}else if(I.length===1){const L=e?.type==="time"&&this.normalizeDate(t)||t;return s.eq(E,L)}return this.databaseAdapter.buildBooleanLiteral(!1);case"notEquals":return I.length>1?s.notInArray(E,I):I.length===1?s.ne(E,t):null;case"contains":return this.databaseAdapter.buildStringCondition(E,"contains",t);case"notContains":return this.databaseAdapter.buildStringCondition(E,"notContains",t);case"startsWith":return this.databaseAdapter.buildStringCondition(E,"startsWith",t);case"endsWith":return this.databaseAdapter.buildStringCondition(E,"endsWith",t);case"gt":return s.gt(E,t);case"gte":return s.gte(E,t);case"lt":return s.lt(E,t);case"lte":return s.lte(E,t);case"set":return s.isNotNull(E);case"notSet":return s.isNull(E);case"inDateRange":if(I.length>=2){const L=this.normalizeDate(I[0]);let O=this.normalizeDate(I[1]);if(L&&O){const N=A[1];if(typeof N=="string"&&/^\d{4}-\d{2}-\d{2}$/.test(N.trim())){const C=typeof O=="number"?new Date(O*(this.databaseAdapter.getEngineType()==="sqlite"?1e3:1)):new Date(O),n=new Date(C);n.setUTCHours(23,59,59,999),this.databaseAdapter.isTimestampInteger()?O=this.databaseAdapter.getEngineType()==="sqlite"?Math.floor(n.getTime()/1e3):n.getTime():O=n.toISOString()}return s.and(s.gte(E,L),s.lte(E,O))}}return null;case"beforeDate":{const L=this.normalizeDate(t);return L?s.lt(E,L):null}case"afterDate":{const L=this.normalizeDate(t);return L?s.gt(E,L):null}case"between":return I.length>=2?s.and(s.gte(E,I[0]),s.lte(E,I[1])):null;case"notBetween":return I.length>=2?s.or(s.lt(E,I[0]),s.gt(E,I[1])):null;case"in":return I.length>0?s.inArray(E,I):null;case"notIn":return I.length>0?s.notInArray(E,I):null;case"like":return this.databaseAdapter.buildStringCondition(E,"like",t);case"notLike":return this.databaseAdapter.buildStringCondition(E,"notLike",t);case"ilike":return this.databaseAdapter.buildStringCondition(E,"ilike",t);case"regex":return this.databaseAdapter.buildStringCondition(E,"regex",t);case"notRegex":return this.databaseAdapter.buildStringCondition(E,"notRegex",t);case"isEmpty":return s.or(s.isNull(E),s.eq(E,""));case"isNotEmpty":return s.and(s.isNotNull(E),s.ne(E,""));default:return null}}buildDateRangeCondition(E,T){if(!T)return null;if(Array.isArray(T)&&T.length>=2){const A=this.normalizeDate(T[0]);let e=this.normalizeDate(T[1]);if(!A||!e)return null;if(typeof T[1]=="string"&&/^\d{4}-\d{2}-\d{2}$/.test(T[1].trim())){const S=typeof e=="number"?new Date(e*(this.databaseAdapter.getEngineType()==="sqlite"?1e3:1)):new Date(e),I=new Date(S);I.setUTCHours(23,59,59,999),this.databaseAdapter.isTimestampInteger()?e=this.databaseAdapter.getEngineType()==="sqlite"?Math.floor(I.getTime()/1e3):I.getTime():e=I.toISOString()}return s.and(s.gte(E,A),s.lte(E,e))}if(typeof T=="string"){const A=this.parseRelativeDateRange(T);if(A){let N,C;return this.databaseAdapter.isTimestampInteger()?this.databaseAdapter.getEngineType()==="sqlite"?(N=Math.floor(A.start.getTime()/1e3),C=Math.floor(A.end.getTime()/1e3)):(N=A.start.getTime(),C=A.end.getTime()):(N=A.start.toISOString(),C=A.end.toISOString()),s.and(s.gte(E,N),s.lte(E,C))}const e=this.normalizeDate(T);if(!e)return null;const S=typeof e=="number"?new Date(e*(this.databaseAdapter.getEngineType()==="sqlite"?1e3:1)):new Date(e),I=new Date(S);I.setUTCHours(0,0,0,0);const t=new Date(S);t.setUTCHours(23,59,59,999);let L,O;return this.databaseAdapter.isTimestampInteger()?this.databaseAdapter.getEngineType()==="sqlite"?(L=Math.floor(I.getTime()/1e3),O=Math.floor(t.getTime()/1e3)):(L=I.getTime(),O=t.getTime()):(L=I.toISOString(),O=t.toISOString()),s.and(s.gte(E,L),s.lte(E,O))}return null}parseRelativeDateRange(E){const T=new Date,A=E.toLowerCase().trim(),e=T.getUTCFullYear(),S=T.getUTCMonth(),I=T.getUTCDate(),t=T.getUTCDay();if(A==="today"){const n=new Date(T);n.setUTCHours(0,0,0,0);const _=new Date(T);return _.setUTCHours(23,59,59,999),{start:n,end:_}}if(A==="yesterday"){const n=new Date(T);n.setUTCDate(I-1),n.setUTCHours(0,0,0,0);const _=new Date(T);return _.setUTCDate(I-1),_.setUTCHours(23,59,59,999),{start:n,end:_}}if(A==="this week"){const n=t===0?-6:1-t,_=new Date(T);_.setUTCDate(I+n),_.setUTCHours(0,0,0,0);const i=new Date(_);return i.setUTCDate(_.getUTCDate()+6),i.setUTCHours(23,59,59,999),{start:_,end:i}}if(A==="this month"){const n=new Date(Date.UTC(e,S,1,0,0,0,0)),_=new Date(Date.UTC(e,S+1,0,23,59,59,999));return{start:n,end:_}}if(A==="this quarter"){const n=Math.floor(S/3),_=new Date(Date.UTC(e,n*3,1,0,0,0,0)),i=new Date(Date.UTC(e,n*3+3,0,23,59,59,999));return{start:_,end:i}}if(A==="this year"){const n=new Date(Date.UTC(e,0,1,0,0,0,0)),_=new Date(Date.UTC(e,11,31,23,59,59,999));return{start:n,end:_}}const L=A.match(/^last\s+(\d+)\s+days?$/);if(L){const n=parseInt(L[1],10),_=new Date(T);_.setUTCDate(I-n+1),_.setUTCHours(0,0,0,0);const i=new Date(T);return i.setUTCHours(23,59,59,999),{start:_,end:i}}const O=A.match(/^last\s+(\d+)\s+weeks?$/);if(O){const _=parseInt(O[1],10)*7,i=new Date(T);i.setUTCDate(I-_+1),i.setUTCHours(0,0,0,0);const o=new Date(T);return o.setUTCHours(23,59,59,999),{start:i,end:o}}if(A==="last week"){const n=t===0?-13:-6-t,_=new Date(T);_.setUTCDate(I+n),_.setUTCHours(0,0,0,0);const i=new Date(_);return i.setUTCDate(_.getUTCDate()+6),i.setUTCHours(23,59,59,999),{start:_,end:i}}if(A==="last month"){const n=new Date(Date.UTC(e,S-1,1,0,0,0,0)),_=new Date(Date.UTC(e,S,0,23,59,59,999));return{start:n,end:_}}if(A==="last quarter"){const n=Math.floor(S/3),_=n===0?3:n-1,i=n===0?e-1:e,o=new Date(Date.UTC(i,_*3,1,0,0,0,0)),G=new Date(Date.UTC(i,_*3+3,0,23,59,59,999));return{start:o,end:G}}if(A==="last year"){const n=new Date(Date.UTC(e-1,0,1,0,0,0,0)),_=new Date(Date.UTC(e-1,11,31,23,59,59,999));return{start:n,end:_}}if(A==="last 12 months"){const n=new Date(Date.UTC(e,S-11,1,0,0,0,0)),_=new Date(T);return _.setUTCHours(23,59,59,999),{start:n,end:_}}const N=A.match(/^last\s+(\d+)\s+months?$/);if(N){const n=parseInt(N[1],10),_=new Date(Date.UTC(e,S-n+1,1,0,0,0,0)),i=new Date(T);return i.setUTCHours(23,59,59,999),{start:_,end:i}}const C=A.match(/^last\s+(\d+)\s+years?$/);if(C){const n=parseInt(C[1],10),_=new Date(Date.UTC(e-n,0,1,0,0,0,0)),i=new Date(T);return i.setUTCHours(23,59,59,999),{start:_,end:i}}return null}normalizeDate(E){if(!E)return null;if(E instanceof Date)return isNaN(E.getTime())?null:this.databaseAdapter.isTimestampInteger()?this.databaseAdapter.getEngineType()==="sqlite"?Math.floor(E.getTime()/1e3):E.getTime():E.toISOString();if(typeof E=="number"){const A=E<1e10?E*1e3:E,e=new Date(A);return isNaN(e.getTime())?null:this.databaseAdapter.isTimestampInteger()?this.databaseAdapter.getEngineType()==="sqlite"?Math.floor(A/1e3):A:e.toISOString()}if(typeof E=="string"){if(/^\d{4}-\d{2}-\d{2}$/.test(E.trim())){const e=new Date(E+"T00:00:00Z");return isNaN(e.getTime())?null:this.databaseAdapter.isTimestampInteger()?this.databaseAdapter.getEngineType()==="sqlite"?Math.floor(e.getTime()/1e3):e.getTime():e.toISOString()}const A=new Date(E);return isNaN(A.getTime())?null:this.databaseAdapter.isTimestampInteger()?this.databaseAdapter.getEngineType()==="sqlite"?Math.floor(A.getTime()/1e3):A.getTime():A.toISOString()}const T=new Date(E);return isNaN(T.getTime())?null:this.databaseAdapter.isTimestampInteger()?this.databaseAdapter.getEngineType()==="sqlite"?Math.floor(T.getTime()/1e3):T.getTime():T.toISOString()}buildGroupByFields(E,T,A,e){const S=[];if(!(T.measures&&T.measures.length>0))return[];const t=E instanceof Map?E:new Map([[E.name,E]]);if(T.dimensions)for(const L of T.dimensions){const[O,N]=L.split("."),C=t.get(O);if(C&&C.dimensions&&C.dimensions[N])if(e?.preAggregationCTEs?.some(_=>_.cube.name===O)){const _=e.preAggregationCTEs.find(o=>o.cube.name===O),i=_.joinKeys.find(o=>o.targetColumn===N);if(i&&i.sourceColumnObj)S.push(i.sourceColumnObj);else{const o=s.sql`${s.sql.identifier(_.cteAlias)}.${s.sql.identifier(N)}`;S.push(o)}}else{const _=C.dimensions[N],i=f(_.sql,A);S.push(i)}}if(T.timeDimensions)for(const L of T.timeDimensions){const[O,N]=L.dimension.split("."),C=t.get(O);if(C&&C.dimensions&&C.dimensions[N])if(e?.preAggregationCTEs?.some(_=>_.cube.name===O)){const _=e.preAggregationCTEs.find(o=>o.cube.name===O),i=_.joinKeys.find(o=>o.targetColumn===N);if(i&&i.sourceColumnObj){const o=this.buildTimeDimensionExpression(i.sourceColumnObj,L.granularity,A);S.push(o)}else{const o=s.sql`${s.sql.identifier(_.cteAlias)}.${s.sql.identifier(N)}`;S.push(o)}}else{const _=C.dimensions[N],i=this.buildTimeDimensionExpression(_.sql,L.granularity,A);S.push(i)}}return S}buildOrderBy(E,T){const A=[],e=T||[...E.measures||[],...E.dimensions||[],...E.timeDimensions?.map(S=>S.dimension)||[]];if(E.order&&Object.keys(E.order).length>0)for(const[S,I]of Object.entries(E.order)){if(!e.includes(S))throw new Error(`Cannot order by '${S}': field is not selected in the query`);const t=I==="desc"?s.desc(s.sql.identifier(S)):s.asc(s.sql.identifier(S));A.push(t)}if(E.timeDimensions&&E.timeDimensions.length>0){const S=new Set(Object.keys(E.order||{})),I=[...E.timeDimensions].sort((t,L)=>t.dimension.localeCompare(L.dimension));for(const t of I)S.has(t.dimension)||A.push(s.asc(s.sql.identifier(t.dimension)))}return A}collectNumericFields(E,T){const A=[],e=E instanceof Map?E:new Map([[E.name,E]]);if(T.measures&&A.push(...T.measures),T.dimensions)for(const S of T.dimensions){const[I,t]=S.split("."),L=e.get(I);if(L){const O=L.dimensions[t];O&&O.type==="number"&&A.push(S)}}return A}applyLimitAndOffset(E,T){let A=T.limit;T.offset!==void 0&&T.offset>0&&A===void 0&&(A=50);let e=E;if(A!==void 0){if(A<0)throw new Error("Limit must be non-negative");e=e.limit(A)}if(T.offset!==void 0){if(T.offset<0)throw new Error("Offset must be non-negative");e=e.offset(T.offset)}return e}}class aT{analyzeCubeUsage(E){const T=new Set;if(E.measures)for(const A of E.measures){const[e]=A.split(".");T.add(e)}if(E.dimensions)for(const A of E.dimensions){const[e]=A.split(".");T.add(e)}if(E.timeDimensions)for(const A of E.timeDimensions){const[e]=A.dimension.split(".");T.add(e)}if(E.filters)for(const A of E.filters)this.extractCubeNamesFromFilter(A,T);return T}extractCubeNamesFromFilter(E,T){if("and"in E||"or"in E){const A=E.and||E.or||[];for(const e of A)this.extractCubeNamesFromFilter(e,T);return}if("member"in E){const[A]=E.member.split(".");A&&T.add(A)}}extractMeasuresFromFilters(E,T){const A=[];if(!E.filters)return A;for(const e of E.filters)this.extractMeasuresFromFilter(e,T,A);return A}extractMeasuresFromFilter(E,T,A){if("and"in E||"or"in E){const e=E.and||E.or||[];for(const S of e)this.extractMeasuresFromFilter(S,T,A);return}if("member"in E){const e=E.member,[S]=e.split(".");S===T&&A.push(e)}}createQueryPlan(E,T,A){const e=this.analyzeCubeUsage(T),S=Array.from(e);if(S.length===0)throw new Error("No cubes found in query");const I=this.choosePrimaryCube(S,T,E),t=E.get(I);if(!t)throw new Error(`Primary cube '${I}' not found`);if(S.length===1)return{primaryCube:t,joinCubes:[],selections:{},whereConditions:[],groupByFields:[]};const L=this.buildJoinPlan(E,t,S,A),O=this.planPreAggregationCTEs(E,t,L,T);return{primaryCube:t,joinCubes:L,selections:{},whereConditions:[],groupByFields:[],preAggregationCTEs:O}}choosePrimaryCube(E,T,A){if(T.dimensions&&T.dimensions.length>0&&A){const e=T.dimensions.map(I=>I.split(".")[0]),S=new Map;for(const I of e)S.set(I,(S.get(I)||0)+1);if(S.size>0){const I=Math.max(...S.values()),t=[...S.entries()].filter(([,L])=>L===I).map(([L])=>L).sort();for(const L of t)if(this.canReachAllCubes(L,E,A))return L}}if(A){const e=new Map;for(const S of E)if(this.canReachAllCubes(S,E,A)){const I=A.get(S),t=I?.joins?Object.keys(I.joins).length:0;e.set(S,t)}if(e.size>0){const S=Math.max(...e.values());return[...e.entries()].filter(([,t])=>t===S).map(([t])=>t).sort()[0]}}return[...E].sort()[0]}canReachAllCubes(E,T,A){const e=T.filter(S=>S!==E);for(const S of e){const I=this.findJoinPath(A,E,S,new Set);if(!I||I.length===0)return!1}return!0}buildJoinPlan(E,T,A,e){const S=[],I=new Set([T.name]),t=A.filter(L=>L!==T.name);for(const L of t){if(I.has(L))continue;const O=this.findJoinPath(E,T.name,L,I);if(!O||O.length===0)throw new Error(`No join path found from '${T.name}' to '${L}'`);for(const{toCube:N,joinDef:C}of O){if(I.has(N))continue;const n=E.get(N);if(!n)throw new Error(`Cube '${N}' not found`);if(C.relationship==="belongsToMany"&&C.through){const _=wT(C,e.securityContext);S.push({cube:n,alias:`${N.toLowerCase()}_cube`,joinType:_.junctionJoins[1].joinType,joinCondition:_.junctionJoins[1].condition,junctionTable:{table:C.through.table,alias:`junction_${N.toLowerCase()}`,joinType:_.junctionJoins[0].joinType,joinCondition:_.junctionJoins[0].condition,securitySql:C.through.securitySql}})}else{const _=this.buildJoinCondition(C,null,null),i=FE(C.relationship,C.sqlJoinType);S.push({cube:n,alias:`${N.toLowerCase()}_cube`,joinType:i,joinCondition:_})}I.add(N)}}return S}buildJoinCondition(E,T,A){const e=[];for(const S of E.on){const I=T?s.sql`${s.sql.identifier(T)}.${s.sql.identifier(S.source.name)}`:S.source,t=A?s.sql`${s.sql.identifier(A)}.${s.sql.identifier(S.target.name)}`:S.target,L=S.as||s.eq;e.push(L(I,t))}return s.and(...e)}findJoinPath(E,T,A,e){if(T===A)return[];const S=[{cube:T,path:[]}],I=new Set([T,...e]);for(;S.length>0;){const{cube:t,path:L}=S.shift(),O=E.get(t);if(O?.joins)for(const[,N]of Object.entries(O.joins)){const n=HE(N.targetCube).name;if(I.has(n))continue;const _=[...L,{fromCube:t,toCube:n,joinDef:N}];if(n===A)return _;I.add(n),S.push({cube:n,path:_})}}return null}planPreAggregationCTEs(E,T,A,e){const S=[];if(!e.measures||e.measures.length===0)return S;for(const I of A){const t=this.findHasManyJoinDef(T,I.cube.name);if(!t)continue;const L=e.measures?e.measures.filter(_=>_.startsWith(I.cube.name+".")):[],O=this.extractMeasuresFromFilters(e,I.cube.name),N=[...new Set([...L,...O])];if(N.length===0)continue;const C=this.expandCalculatedMeasureDependencies(I.cube,N),n=t.on.map(_=>({sourceColumn:_.source.name,targetColumn:_.target.name,sourceColumnObj:_.source,targetColumnObj:_.target}));S.push({cube:I.cube,alias:I.alias,cteAlias:`${I.cube.name.toLowerCase()}_agg`,joinKeys:n,measures:C})}return S}expandCalculatedMeasureDependencies(E,T){const A=new Set,e=[...T];for(;e.length>0;){const S=e.pop();if(A.has(S))continue;A.add(S);const[,I]=S.split(".");if(!E.measures||!E.measures[I])continue;const t=E.measures[I];if(t.type==="calculated"&&t.calculatedSql){const L=this.extractDependenciesFromTemplate(t.calculatedSql,E.name);for(const O of L)A.has(O)||e.push(O)}}return Array.from(A)}extractDependenciesFromTemplate(E,T){const A=/\{([^}]+)\}/g,e=E.matchAll(A),S=[];for(const I of e){const t=I[1].trim();t.includes(".")?S.push(t):S.push(`${T}.${t}`)}return S}findHasManyJoinDef(E,T){if(!E.joins)return null;for(const[,A]of Object.entries(E.joins))if(HE(A.targetCube).name===T&&A.relationship==="hasMany")return A;return null}}class J{constructor(E){if(this.dbExecutor=E,this.databaseAdapter=E.databaseAdapter,!this.databaseAdapter)throw new Error("DatabaseExecutor must have a databaseAdapter property");this.queryBuilder=new iT(this.databaseAdapter),this.queryPlanner=new aT}queryBuilder;queryPlanner;databaseAdapter;async execute(E,T,A){try{const e=FT(E,T);if(!e.isValid)throw new Error(`Query validation failed: ${e.errors.join(", ")}`);const S={db:this.dbExecutor.db,schema:this.dbExecutor.schema,securityContext:A},I=this.queryPlanner.createQueryPlan(E,T,S),t=this.buildUnifiedQuery(I,T,S),L=this.queryBuilder.collectNumericFields(E,T),O=await this.dbExecutor.execute(t,L),N=Array.isArray(O)?O.map(n=>{const _={...n};if(T.timeDimensions){for(const i of T.timeDimensions)if(i.dimension in _){let o=_[i.dimension];if(typeof o=="string"&&o.match(/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/)){const G=o.replace(" ","T"),P=!G.endsWith("Z")&&!G.includes("+")?G+"Z":G;o=new Date(P)}o=this.databaseAdapter.convertTimeDimensionResult(o),_[i.dimension]=o}}return _}):[O],C=this.generateAnnotations(I,T);return{data:N,annotation:C}}catch(e){throw new Error(`Query execution failed: ${e instanceof Error?e.message:"Unknown error"}`)}}async executeQuery(E,T,A){const e=new Map;return e.set(E.name,E),this.execute(e,T,A)}buildPreAggregationCTE(E,T,A,e){const S=E.cube,I=S.sql(A),t={};for(const P of E.joinKeys)if(P.targetColumnObj){t[P.targetColumn]=P.targetColumnObj;for(const[c,M]of Object.entries(S.dimensions||{}))M.sql===P.targetColumnObj&&c!==P.targetColumn&&(t[c]=s.sql`${P.targetColumnObj}`.as(c))}const L=S.name,O=new Map([[L,S]]),N=this.queryBuilder.buildResolvedMeasures(E.measures,O,A);for(const P of E.measures){const[,c]=P.split("."),M=N.get(P);if(M){const u=M();t[c]=s.sql`${u}`.as(c)}}if(T.dimensions)for(const P of T.dimensions){const[c,M]=P.split(".");if(c===L&&S.dimensions&&S.dimensions[M]){const u=S.dimensions[M],H=this.queryBuilder.buildMeasureExpression({sql:u.sql,type:"number"},A);t[M]=s.sql`${H}`.as(M)}}if(T.timeDimensions)for(const P of T.timeDimensions){const[c,M]=P.dimension.split(".");if(c===L&&S.dimensions&&S.dimensions[M]){const u=S.dimensions[M],H=this.queryBuilder.buildTimeDimensionExpression(u.sql,P.granularity,A);t[M]=s.sql`${H}`.as(M)}}if(Object.keys(t).length===0)return null;let C=A.db.select(t).from(I.from);const n=e?{...e,preAggregationCTEs:e.preAggregationCTEs?.filter(P=>P.cube.name!==S.name)}:void 0,_=this.queryBuilder.buildWhereConditions(S,T,A,n),i=[];if(T.timeDimensions)for(const P of T.timeDimensions){const[c,M]=P.dimension.split(".");if(c===L&&S.dimensions&&S.dimensions[M]&&P.dateRange){const u=S.dimensions[M],H=this.queryBuilder.buildMeasureExpression({sql:u.sql,type:"number"},A),B=this.queryBuilder.buildDateRangeCondition(H,P.dateRange);B&&i.push(B)}}if(T.filters){for(const P of T.filters)if(!("and"in P)&&!("or"in P)&&"member"in P&&"operator"in P){const c=P,[M,u]=c.member.split(".");if(M===L&&S.dimensions&&S.dimensions[u]){const H=S.dimensions[u];if(c.operator==="inDateRange"){const B=this.queryBuilder.buildMeasureExpression({sql:H.sql,type:"number"},A),p=this.queryBuilder.buildDateRangeCondition(B,c.values);p&&i.push(p)}}}}const o=[];if(I.where&&o.push(I.where),o.push(..._,...i),o.length>0){const P=o.length===1?o[0]:s.and(...o);C=C.where(P)}const G=[];for(const P of E.joinKeys)P.targetColumnObj&&G.push(P.targetColumnObj);if(T.dimensions)for(const P of T.dimensions){const[c,M]=P.split(".");if(c===L&&S.dimensions&&S.dimensions[M]){const u=S.dimensions[M],H=f(u.sql,A);G.push(H)}}if(T.timeDimensions)for(const P of T.timeDimensions){const[c,M]=P.dimension.split(".");if(c===L&&S.dimensions&&S.dimensions[M]){const u=S.dimensions[M],H=this.queryBuilder.buildTimeDimensionExpression(u.sql,P.granularity,A);G.push(H)}}return G.length>0&&(C=C.groupBy(...G)),A.db.$with(E.cteAlias).as(C)}buildCTEJoinCondition(E,T,A){const e=A.preAggregationCTEs?.find(I=>I.cube.name===E.cube.name);if(!e)throw new Error(`CTE info not found for cube ${E.cube.name}`);const S=[];for(const I of e.joinKeys){const t=I.sourceColumnObj||s.sql.identifier(I.sourceColumn),L=s.sql`${s.sql.identifier(T)}.${s.sql.identifier(I.targetColumn)}`;S.push(s.eq(t,L))}return S.length===1?S[0]:s.and(...S)}buildUnifiedQuery(E,T,A){const e=[],S=new Map;if(E.preAggregationCTEs&&E.preAggregationCTEs.length>0)for(const o of E.preAggregationCTEs){const G=this.buildPreAggregationCTE(o,T,A,E);G&&(e.push(G),S.set(o.cube.name,o.cteAlias))}const I=E.primaryCube.sql(A),L={...this.queryBuilder.buildSelections(E.joinCubes.length>0?this.getCubesFromPlan(E):E.primaryCube,T,A)};if(E.preAggregationCTEs)for(const o of E.preAggregationCTEs){const G=o.cube.name;for(const P of o.measures)if(L[P]){const[,c]=P.split("."),M=this.getCubesFromPlan(E).get(G);if(M&&M.measures&&M.measures[c]){const u=M.measures[c];let H;if(u.type==="calculated"&&u.calculatedSql){const B=this.getCubesFromPlan(E);H=this.queryBuilder.buildCTECalculatedMeasure(u,M,o,B,A)}else{const B=s.sql`${s.sql.identifier(o.cteAlias)}.${s.sql.identifier(c)}`;switch(u.type){case"count":case"countDistinct":case"sum":H=s.sum(B);break;case"avg":H=this.databaseAdapter.buildAvg(B);break;case"min":H=s.min(B);break;case"max":H=s.max(B);break;case"number":H=s.sum(B);break;default:H=s.sum(B)}}L[P]=s.sql`${H}`.as(P)}}for(const P in L){const[c,M]=P.split(".");if(c===G){const u=this.getCubesFromPlan(E).get(G),H=u&&u.dimensions?.[M],B=P.startsWith(G+".");if(H||B){let p=o.joinKeys.find(EE=>EE.targetColumn===M);if(!p&&u?.dimensions?.[M]){const EE=u.dimensions[M].sql;p=o.joinKeys.find(hT=>hT.targetColumnObj===EE)}p?L[P]=s.sql`${s.sql.identifier(o.cteAlias)}.${s.sql.identifier(M)}`.as(P):B&&u?.dimensions?.[M]&&(L[P]=s.sql`${s.sql.identifier(o.cteAlias)}.${s.sql.identifier(M)}`.as(P))}}}}const O=[];let N=A.db.select(L).from(I.from);if(e.length>0&&(N=A.db.with(...e).select(L).from(I.from)),I.joins)for(const o of I.joins)switch(o.type||"left"){case"left":N=N.leftJoin(o.table,o.on);break;case"inner":N=N.innerJoin(o.table,o.on);break;case"right":N=N.rightJoin(o.table,o.on);break;case"full":N=N.fullJoin(o.table,o.on);break}if(E.joinCubes&&E.joinCubes.length>0)for(const o of E.joinCubes){const G=S.get(o.cube.name);if(o.junctionTable){const M=o.junctionTable,u=[];if(M.securitySql){const H=M.securitySql(A.securityContext);Array.isArray(H)?u.push(...H):u.push(H)}try{switch(M.joinType||"left"){case"left":N=N.leftJoin(M.table,M.joinCondition);break;case"inner":N=N.innerJoin(M.table,M.joinCondition);break;case"right":N=N.rightJoin(M.table,M.joinCondition);break;case"full":N=N.fullJoin(M.table,M.joinCondition);break}u.length>0&&O.push(...u)}catch{}}let P,c;G?(P=s.sql`${s.sql.identifier(G)}`,c=this.buildCTEJoinCondition(o,G,E)):(P=o.cube.sql(A).from,c=o.joinCondition);try{switch(o.joinType||"left"){case"left":N=N.leftJoin(P,c);break;case"inner":N=N.innerJoin(P,c);break;case"right":N=N.rightJoin(P,c);break;case"full":N=N.fullJoin(P,c);break}}catch{}}if(I.where&&O.push(I.where),E.joinCubes&&E.joinCubes.length>0)for(const o of E.joinCubes){if(S.get(o.cube.name))continue;const P=o.cube.sql(A);P.where&&O.push(P.where)}const C=this.queryBuilder.buildWhereConditions(E.joinCubes.length>0?this.getCubesFromPlan(E):E.primaryCube,T,A,E);if(C.length>0&&O.push(...C),O.length>0){const o=O.length===1?O[0]:s.and(...O);N=N.where(o)}const n=this.queryBuilder.buildGroupByFields(E.joinCubes.length>0?this.getCubesFromPlan(E):E.primaryCube,T,A,E);n.length>0&&(N=N.groupBy(...n));const _=this.queryBuilder.buildHavingConditions(E.joinCubes.length>0?this.getCubesFromPlan(E):E.primaryCube,T,A,E);if(_.length>0){const o=_.length===1?_[0]:s.and(..._);N=N.having(o)}const i=this.queryBuilder.buildOrderBy(T);return i.length>0&&(N=N.orderBy(...i)),N=this.queryBuilder.applyLimitAndOffset(N,T),N}getCubesFromPlan(E){const T=new Map;if(T.set(E.primaryCube.name,E.primaryCube),E.joinCubes)for(const A of E.joinCubes)T.set(A.cube.name,A.cube);return T}async generateSQL(E,T,A){const e=new Map;return e.set(E.name,E),this.generateUnifiedSQL(e,T,A)}async generateMultiCubeSQL(E,T,A){return this.generateUnifiedSQL(E,T,A)}async generateUnifiedSQL(E,T,A){const e={db:this.dbExecutor.db,schema:this.dbExecutor.schema,securityContext:A},S=this.queryPlanner.createQueryPlan(E,T,e),t=this.buildUnifiedQuery(S,T,e).toSQL();return{sql:t.sql,params:t.params}}generateAnnotations(E,T){const A={},e={},S={},I=[E.primaryCube];if(E.joinCubes&&E.joinCubes.length>0&&I.push(...E.joinCubes.map(t=>t.cube)),T.measures)for(const t of T.measures){const[L,O]=t.split("."),N=I.find(C=>C.name===L);if(N&&N.measures[O]){const C=N.measures[O];A[t]={title:C.title||O,shortTitle:C.title||O,type:C.type}}}if(T.dimensions)for(const t of T.dimensions){const[L,O]=t.split("."),N=I.find(C=>C.name===L);if(N&&N.dimensions[O]){const C=N.dimensions[O];e[t]={title:C.title||O,shortTitle:C.title||O,type:C.type}}}if(T.timeDimensions)for(const t of T.timeDimensions){const[L,O]=t.dimension.split("."),N=I.find(C=>C.name===L);if(N&&N.dimensions&&N.dimensions[O]){const C=N.dimensions[O];S[t.dimension]={title:C.title||O,shortTitle:C.title||O,type:C.type,granularity:t.granularity}}}return{measures:A,dimensions:e,segments:{},timeDimensions:S}}}const r=R=>R.flatMap(vT),vT=R=>x(qT(R)).map(QT),QT=R=>R.replace(/ +/g," ").trim(),qT=R=>({type:"mandatory_block",items:YE(R,0)[0]}),YE=(R,E,T)=>{const A=[];for(;R[E];){const[e,S]=ZT(R,E);if(A.push(e),E=S,R[E]==="|")E++;else if(R[E]==="}"||R[E]==="]"){if(T!==R[E])throw new Error(`Unbalanced parenthesis in: ${R}`);return E++,[A,E]}else if(E===R.length){if(T)throw new Error(`Unbalanced parenthesis in: ${R}`);return[A,E]}else throw new Error(`Unexpected "${R[E]}"`)}return[A,E]},ZT=(R,E)=>{const T=[];for(;;){const[A,e]=jT(R,E);if(A)T.push(A),E=e;else break}return T.length===1?[T[0],E]:[{type:"concatenation",items:T},E]},jT=(R,E)=>{if(R[E]==="{")return kT(R,E+1);if(R[E]==="[")return zT(R,E+1);{let T="";for(;R[E]&&/[A-Za-z0-9_ ]/.test(R[E]);)T+=R[E],E++;return[T,E]}},kT=(R,E)=>{const[T,A]=YE(R,E,"}");return[{type:"mandatory_block",items:T},A]},zT=(R,E)=>{const[T,A]=YE(R,E,"]");return[{type:"optional_block",items:T},A]},x=R=>{if(typeof R=="string")return[R];if(R.type==="concatenation")return R.items.map(x).reduce(ER,[""]);if(R.type==="mandatory_block")return R.items.flatMap(x);if(R.type==="optional_block")return["",...R.items.flatMap(x)];throw new Error(`Unknown node type: ${R}`)},ER=(R,E)=>{const T=[];for(const A of R)for(const e of E)T.push(A+e);return T};var D;(function(R){R.QUOTED_IDENTIFIER="QUOTED_IDENTIFIER",R.IDENTIFIER="IDENTIFIER",R.STRING="STRING",R.VARIABLE="VARIABLE",R.RESERVED_DATA_TYPE="RESERVED_DATA_TYPE",R.RESERVED_PARAMETERIZED_DATA_TYPE="RESERVED_PARAMETERIZED_DATA_TYPE",R.RESERVED_KEYWORD="RESERVED_KEYWORD",R.RESERVED_FUNCTION_NAME="RESERVED_FUNCTION_NAME",R.RESERVED_KEYWORD_PHRASE="RESERVED_KEYWORD_PHRASE",R.RESERVED_DATA_TYPE_PHRASE="RESERVED_DATA_TYPE_PHRASE",R.RESERVED_SET_OPERATION="RESERVED_SET_OPERATION",R.RESERVED_CLAUSE="RESERVED_CLAUSE",R.RESERVED_SELECT="RESERVED_SELECT",R.RESERVED_JOIN="RESERVED_JOIN",R.ARRAY_IDENTIFIER="ARRAY_IDENTIFIER",R.ARRAY_KEYWORD="ARRAY_KEYWORD",R.CASE="CASE",R.END="END",R.WHEN="WHEN",R.ELSE="ELSE",R.THEN="THEN",R.LIMIT="LIMIT",R.BETWEEN="BETW