drizzle-cube
Version:
Drizzle ORM-first semantic layer with Cube.js compatibility. Type-safe analytics and dashboards with SQL injection protection.
1,643 lines • 432 kB
JavaScript
var FR = Object.defineProperty;
var YR = (T, E, R) => E in T ? FR(T, E, { enumerable: !0, configurable: !0, writable: !0, value: R }) : T[E] = R;
var l = (T, E, R) => YR(T, typeof E != "symbol" ? E + "" : E, R);
const X = Symbol.for("drizzle:entityKind");
function F(T, E) {
if (!T || typeof T != "object")
return !1;
if (T instanceof E)
return !0;
if (!Object.prototype.hasOwnProperty.call(E, X))
throw new Error(
`Class "${E.name ?? "<unknown>"}" doesn't look like a Drizzle entity. If this is incorrect and the class is provided by Drizzle, please report this as a bug.`
);
let R = Object.getPrototypeOf(T).constructor;
if (R)
for (; R; ) {
if (X in R && R[X] === E[X])
return !0;
R = Object.getPrototypeOf(R);
}
return !1;
}
var gT;
gT = X;
class EE {
constructor(E, R) {
l(this, "name");
l(this, "keyAsName");
l(this, "primary");
l(this, "notNull");
l(this, "default");
l(this, "defaultFn");
l(this, "onUpdateFn");
l(this, "hasDefault");
l(this, "isUnique");
l(this, "uniqueName");
l(this, "uniqueType");
l(this, "dataType");
l(this, "columnType");
l(this, "enumValues");
l(this, "generated");
l(this, "generatedIdentity");
l(this, "config");
this.table = E, this.config = R, this.name = R.name, this.keyAsName = R.keyAsName, this.notNull = R.notNull, this.default = R.default, this.defaultFn = R.defaultFn, this.onUpdateFn = R.onUpdateFn, this.hasDefault = R.hasDefault, this.primary = R.primaryKey, this.isUnique = R.isUnique, this.uniqueName = R.uniqueName, this.uniqueType = R.uniqueType, this.dataType = R.dataType, this.columnType = R.columnType, this.generated = R.generated, this.generatedIdentity = R.generatedIdentity;
}
mapFromDriverValue(E) {
return E;
}
mapToDriverValue(E) {
return E;
}
// ** @internal */
shouldDisableInsert() {
return this.config.generated !== void 0 && this.config.generated.type !== "byDefault";
}
}
l(EE, gT, "Column");
const DE = Symbol.for("drizzle:Name"), ET = Symbol.for("drizzle:isPgEnum");
function pR(T) {
return !!T && typeof T == "function" && ET in T && T[ET] === !0;
}
var JT;
JT = X;
class QE {
constructor(E, R, A, S = !1, e = []) {
this._ = {
brand: "Subquery",
sql: E,
selectedFields: R,
alias: A,
isWith: S,
usedTables: e
};
}
// getSQL(): SQL<unknown> {
// return new SQL([this]);
// }
}
l(QE, JT, "Subquery");
const dR = {
startActiveSpan(T, E) {
return E();
}
}, SE = Symbol.for("drizzle:ViewBaseConfig"), aE = Symbol.for("drizzle:Schema"), TT = Symbol.for("drizzle:Columns"), RT = Symbol.for("drizzle:ExtraConfigColumns"), oE = Symbol.for("drizzle:OriginalName"), PE = Symbol.for("drizzle:BaseName"), CE = Symbol.for("drizzle:IsAlias"), AT = Symbol.for("drizzle:ExtraConfigBuilder"), fR = Symbol.for("drizzle:IsDrizzleTable");
var wT, xT, vT, QT, ZT, qT, jT, kT, zT, ER;
ER = X, zT = DE, kT = oE, jT = aE, qT = TT, ZT = RT, QT = PE, vT = CE, xT = fR, wT = AT;
class W {
constructor(E, R, A) {
/**
* @internal
* Can be changed if the table is aliased.
*/
l(this, zT);
/**
* @internal
* Used to store the original name of the table, before any aliasing.
*/
l(this, kT);
/** @internal */
l(this, jT);
/** @internal */
l(this, qT);
/** @internal */
l(this, ZT);
/**
* @internal
* Used to store the table name before the transformation via the `tableCreator` functions.
*/
l(this, QT);
/** @internal */
l(this, vT, !1);
/** @internal */
l(this, xT, !0);
/** @internal */
l(this, wT);
this[DE] = this[oE] = E, this[aE] = R, this[PE] = A;
}
}
l(W, ER, "Table"), /** @internal */
l(W, "Symbol", {
Name: DE,
Schema: aE,
OriginalName: oE,
Columns: TT,
ExtraConfigColumns: RT,
BaseName: PE,
IsAlias: CE,
ExtraConfigBuilder: AT
});
function sR(T) {
return T != null && typeof T.getSQL == "function";
}
function hR(T) {
var R;
const E = { sql: "", params: [] };
for (const A of T)
E.sql += A.sql, E.params.push(...A.params), (R = A.typings) != null && R.length && (E.typings || (E.typings = []), E.typings.push(...A.typings));
return E;
}
var TR;
TR = X;
class h {
constructor(E) {
l(this, "value");
this.value = Array.isArray(E) ? E : [E];
}
getSQL() {
return new m([this]);
}
}
l(h, TR, "StringChunk");
var RR;
RR = X;
const v = class v {
constructor(E) {
/** @internal */
l(this, "decoder", CR);
l(this, "shouldInlineParams", !1);
/** @internal */
l(this, "usedTables", []);
this.queryChunks = E;
for (const R of E)
if (F(R, W)) {
const A = R[W.Symbol.Schema];
this.usedTables.push(
A === void 0 ? R[W.Symbol.Name] : A + "." + R[W.Symbol.Name]
);
}
}
append(E) {
return this.queryChunks.push(...E.queryChunks), this;
}
toQuery(E) {
return dR.startActiveSpan("drizzle.buildSQL", (R) => {
const A = this.buildQueryFromSourceParams(this.queryChunks, E);
return R == null || R.setAttributes({
"drizzle.query.text": A.sql,
"drizzle.query.params": JSON.stringify(A.params)
}), A;
});
}
buildQueryFromSourceParams(E, R) {
const A = Object.assign({}, R, {
inlineParams: R.inlineParams || this.shouldInlineParams,
paramStartIndex: R.paramStartIndex || { value: 0 }
}), {
casing: S,
escapeName: e,
escapeParam: O,
prepareTyping: C,
inlineParams: _,
paramStartIndex: I
} = A;
return hR(E.map((N) => {
var s;
if (F(N, h))
return { sql: N.value.join(""), params: [] };
if (F(N, LE))
return { sql: e(N.value), params: [] };
if (N === void 0)
return { sql: "", params: [] };
if (Array.isArray(N)) {
const t = [new h("(")];
for (const [n, P] of N.entries())
t.push(P), n < N.length - 1 && t.push(new h(", "));
return t.push(new h(")")), this.buildQueryFromSourceParams(t, A);
}
if (F(N, v))
return this.buildQueryFromSourceParams(N.queryChunks, {
...A,
inlineParams: _ || N.shouldInlineParams
});
if (F(N, W)) {
const t = N[W.Symbol.Schema], n = N[W.Symbol.Name];
return {
sql: t === void 0 || N[CE] ? e(n) : e(t) + "." + e(n),
params: []
};
}
if (F(N, EE)) {
const t = S.getColumnCasing(N);
if (R.invokeSource === "indexes")
return { sql: e(t), params: [] };
const n = N.table[W.Symbol.Schema];
return {
sql: N.table[CE] || n === void 0 ? e(N.table[W.Symbol.Name]) + "." + e(t) : e(n) + "." + e(N.table[W.Symbol.Name]) + "." + e(t),
params: []
};
}
if (F(N, ZE)) {
const t = N[SE].schema, n = N[SE].name;
return {
sql: t === void 0 || N[SE].isAlias ? e(n) : e(t) + "." + e(n),
params: []
};
}
if (F(N, IE)) {
if (F(N.value, OE))
return { sql: O(I.value++, N), params: [N], typings: ["none"] };
const t = N.value === null ? null : N.encoder.mapToDriverValue(N.value);
if (F(t, v))
return this.buildQueryFromSourceParams([t], A);
if (_)
return { sql: this.mapInlineParam(t, A), params: [] };
let n = ["none"];
return C && (n = [C(N.encoder)]), { sql: O(I.value++, t), params: [t], typings: n };
}
return F(N, OE) ? { sql: O(I.value++, N), params: [N], typings: ["none"] } : F(N, v.Aliased) && N.fieldAlias !== void 0 ? { sql: e(N.fieldAlias), params: [] } : F(N, QE) ? N._.isWith ? { sql: e(N._.alias), params: [] } : this.buildQueryFromSourceParams([
new h("("),
N._.sql,
new h(") "),
new LE(N._.alias)
], A) : pR(N) ? N.schema ? { sql: e(N.schema) + "." + e(N.enumName), params: [] } : { sql: e(N.enumName), params: [] } : sR(N) ? (s = N.shouldOmitSQLParens) != null && s.call(N) ? this.buildQueryFromSourceParams([N.getSQL()], A) : this.buildQueryFromSourceParams([
new h("("),
N.getSQL(),
new h(")")
], A) : _ ? { sql: this.mapInlineParam(N, A), params: [] } : { sql: O(I.value++, N), params: [N], typings: ["none"] };
}));
}
mapInlineParam(E, { escapeString: R }) {
if (E === null)
return "null";
if (typeof E == "number" || typeof E == "boolean")
return E.toString();
if (typeof E == "string")
return R(E);
if (typeof E == "object") {
const A = E.toString();
return R(A === "[object Object]" ? JSON.stringify(E) : A);
}
throw new Error("Unexpected param value: " + E);
}
getSQL() {
return this;
}
as(E) {
return E === void 0 ? this : new v.Aliased(this, E);
}
mapWith(E) {
return this.decoder = typeof E == "function" ? { mapFromDriverValue: E } : E, this;
}
inlineParams() {
return this.shouldInlineParams = !0, this;
}
/**
* This method is used to conditionally include a part of the query.
*
* @param condition - Condition to check
* @returns itself if the condition is `true`, otherwise `undefined`
*/
if(E) {
return E ? this : void 0;
}
};
l(v, RR, "SQL");
let m = v;
var AR;
AR = X;
class LE {
constructor(E) {
l(this, "brand");
this.value = E;
}
getSQL() {
return new m([this]);
}
}
l(LE, AR, "Name");
function VR(T) {
return typeof T == "object" && T !== null && "mapToDriverValue" in T && typeof T.mapToDriverValue == "function";
}
const CR = {
mapFromDriverValue: (T) => T
}, LR = {
mapToDriverValue: (T) => T
};
({
...CR,
...LR
});
var SR;
SR = X;
class IE {
/**
* @param value - Parameter value
* @param encoder - Encoder to convert the value to a driver parameter
*/
constructor(E, R = LR) {
l(this, "brand");
this.value = E, this.encoder = R;
}
getSQL() {
return new m([this]);
}
}
l(IE, SR, "Param");
function L(T, ...E) {
const R = [];
(E.length > 0 || T.length > 0 && T[0] !== "") && R.push(new h(T[0]));
for (const [A, S] of E.entries())
R.push(S, new h(T[A + 1]));
return new m(R);
}
((T) => {
function E() {
return new m([]);
}
T.empty = E;
function R(_) {
return new m(_);
}
T.fromList = R;
function A(_) {
return new m([new h(_)]);
}
T.raw = A;
function S(_, I) {
const N = [];
for (const [s, t] of _.entries())
s > 0 && I !== void 0 && N.push(I), N.push(t);
return new m(N);
}
T.join = S;
function e(_) {
return new LE(_);
}
T.identifier = e;
function O(_) {
return new OE(_);
}
T.placeholder = O;
function C(_, I) {
return new IE(_, I);
}
T.param = C;
})(L || (L = {}));
((T) => {
var R;
R = X;
const A = class A {
constructor(e, O) {
/** @internal */
l(this, "isSelectionField", !1);
this.sql = e, this.fieldAlias = O;
}
getSQL() {
return this.sql;
}
/** @internal */
clone() {
return new A(this.sql, this.fieldAlias);
}
};
l(A, R, "SQL.Aliased");
let E = A;
T.Aliased = E;
})(m || (m = {}));
var eR;
eR = X;
class OE {
constructor(E) {
this.name = E;
}
getSQL() {
return new m([this]);
}
}
l(OE, eR, "Placeholder");
const WR = Symbol.for("drizzle:IsDrizzleView");
var IR, NR, tR;
tR = X, NR = SE, IR = WR;
class ZE {
constructor({ name: E, schema: R, selectedFields: A, query: S }) {
/** @internal */
l(this, NR);
/** @internal */
l(this, IR, !0);
this[SE] = {
name: E,
originalName: E,
schema: R,
selectedFields: A,
query: S,
isExisting: !S,
isAlias: !1
};
}
getSQL() {
return new m([this]);
}
}
l(ZE, tR, "View");
EE.prototype.getSQL = function() {
return new m([this]);
};
W.prototype.getSQL = function() {
return new m([this]);
};
QE.prototype.getSQL = function() {
return new m([this]);
};
function $(T, E) {
return VR(E) && !sR(T) && !F(T, IE) && !F(T, OE) && !F(T, EE) && !F(T, W) && !F(T, ZE) ? new IE(T, E) : T;
}
const qE = (T, E) => L`${T} = ${$(E, T)}`, XR = (T, E) => L`${T} <> ${$(E, T)}`;
function b(...T) {
const E = T.filter(
(R) => R !== void 0
);
if (E.length !== 0)
return E.length === 1 ? new m(E) : new m([
new h("("),
L.join(E, new h(" and ")),
new h(")")
]);
}
function bR(...T) {
const E = T.filter(
(R) => R !== void 0
);
if (E.length !== 0)
return E.length === 1 ? new m(E) : new m([
new h("("),
L.join(E, new h(" or ")),
new h(")")
]);
}
const ST = (T, E) => L`${T} > ${$(E, T)}`, RE = (T, E) => L`${T} >= ${$(E, T)}`, eT = (T, E) => L`${T} < ${$(E, T)}`, AE = (T, E) => L`${T} <= ${$(E, T)}`;
function IT(T, E) {
return Array.isArray(E) ? E.length === 0 ? L`false` : L`${T} in ${E.map((R) => $(R, T))}` : L`${T} in ${$(E, T)}`;
}
function yR(T, E) {
return Array.isArray(E) ? E.length === 0 ? L`true` : L`${T} not in ${E.map((R) => $(R, T))}` : L`${T} not in ${$(E, T)}`;
}
function KR(T) {
return L`${T} is null`;
}
function $R(T) {
return L`${T} is not null`;
}
function OT(T) {
return L`${T} asc`;
}
function gR(T) {
return L`${T} desc`;
}
function ME(T) {
return L`count(${T || L.raw("*")})`.mapWith(Number);
}
function JR(T) {
return L`count(distinct ${T})`.mapWith(Number);
}
function Q(T) {
return L`sum(${T})`.mapWith(String);
}
function wE(T) {
return L`max(${T})`.mapWith(F(T, EE) ? T : String);
}
function xE(T) {
return L`min(${T})`.mapWith(F(T, EE) ? T : String);
}
class jE {
/**
* Helper method to build pattern for string matching
* Can be overridden by specific adapters if needed
*/
buildPattern(E, R) {
switch (E) {
case "contains":
case "notContains":
return `%${R}%`;
case "startsWith":
return `${R}%`;
case "endsWith":
return `%${R}`;
default:
return R;
}
}
}
class wR extends jE {
getEngineType() {
return "postgres";
}
/**
* Build PostgreSQL time dimension using DATE_TRUNC function
* Extracted from executor.ts:649-670 and multi-cube-builder.ts:306-320
*/
buildTimeDimension(E, R) {
switch (E) {
case "year":
return L`DATE_TRUNC('year', ${R}::timestamp)`;
case "quarter":
return L`DATE_TRUNC('quarter', ${R}::timestamp)`;
case "month":
return L`DATE_TRUNC('month', ${R}::timestamp)`;
case "week":
return L`DATE_TRUNC('week', ${R}::timestamp)`;
case "day":
return L`DATE_TRUNC('day', ${R}::timestamp)::timestamp`;
case "hour":
return L`DATE_TRUNC('hour', ${R}::timestamp)`;
case "minute":
return L`DATE_TRUNC('minute', ${R}::timestamp)`;
case "second":
return L`DATE_TRUNC('second', ${R}::timestamp)`;
default:
return R;
}
}
/**
* Build PostgreSQL string matching conditions using ILIKE (case-insensitive)
* Extracted from executor.ts:807-813 and multi-cube-builder.ts:468-474
*/
buildStringCondition(E, R, A) {
const S = this.buildPattern(R, A);
switch (R) {
case "contains":
return L`${E} ILIKE ${S}`;
case "notContains":
return L`${E} NOT ILIKE ${S}`;
case "startsWith":
return L`${E} ILIKE ${S}`;
case "endsWith":
return L`${E} ILIKE ${S}`;
default:
throw new Error(`Unsupported string operator: ${R}`);
}
}
/**
* Build PostgreSQL type casting using :: syntax
* Extracted from various locations where ::timestamp was used
*/
castToType(E, R) {
switch (R) {
case "timestamp":
return L`${E}::timestamp`;
case "decimal":
return L`${E}::decimal`;
case "integer":
return L`${E}::integer`;
default:
throw new Error(`Unsupported cast type: ${R}`);
}
}
/**
* Build PostgreSQL AVG aggregation with COALESCE for NULL handling
* PostgreSQL AVG returns NULL for empty sets, so we use COALESCE for consistent behavior
* Extracted from multi-cube-builder.ts:284
*/
buildAvg(E) {
return L`COALESCE(AVG(${E}), 0)`;
}
/**
* Build PostgreSQL CASE WHEN conditional expression
*/
buildCaseWhen(E, R) {
const A = E.map((S) => L`WHEN ${S.when} THEN ${S.then}`).reduce((S, e) => L`${S} ${e}`);
return R !== void 0 ? L`CASE ${A} ELSE ${R} END` : L`CASE ${A} END`;
}
/**
* Build PostgreSQL boolean literal
* PostgreSQL uses TRUE/FALSE keywords
*/
buildBooleanLiteral(E) {
return E ? L`TRUE` : L`FALSE`;
}
/**
* Convert filter values - PostgreSQL uses native types
* No conversion needed for PostgreSQL
*/
convertFilterValue(E) {
return E;
}
/**
* Prepare date value for PostgreSQL
* PostgreSQL accepts Date objects directly
*/
prepareDateValue(E) {
return E;
}
/**
* PostgreSQL stores timestamps as native timestamp types
*/
isTimestampInteger() {
return !1;
}
/**
* PostgreSQL time dimensions already return proper values
* No conversion needed
*/
convertTimeDimensionResult(E) {
return E;
}
}
class xR extends jE {
getEngineType() {
return "mysql";
}
/**
* Build MySQL time dimension using DATE_FORMAT function
* MySQL equivalent to PostgreSQL's DATE_TRUNC
*/
buildTimeDimension(E, R) {
const A = {
year: "%Y-01-01 00:00:00",
quarter: "%Y-%q-01 00:00:00",
// %q gives quarter (1,2,3,4), but we need to map this properly
month: "%Y-%m-01 00:00:00",
week: "%Y-%u-01 00:00:00",
// %u gives week of year
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 L`DATE_ADD(MAKEDATE(YEAR(${R}), 1), INTERVAL (QUARTER(${R}) - 1) * 3 MONTH)`;
case "week":
return L`DATE_SUB(${R}, INTERVAL WEEKDAY(${R}) DAY)`;
default:
const S = A[E];
return S ? L`STR_TO_DATE(DATE_FORMAT(${R}, ${S}), '%Y-%m-%d %H:%i:%s')` : R;
}
}
/**
* Build MySQL string matching conditions using LIKE
* MySQL LIKE is case-insensitive by default (depending on collation)
* For guaranteed case-insensitive matching, we use LOWER() functions
*/
buildStringCondition(E, R, A) {
const S = this.buildPattern(R, A.toLowerCase());
switch (R) {
case "contains":
return L`LOWER(${E}) LIKE ${S}`;
case "notContains":
return L`LOWER(${E}) NOT LIKE ${S}`;
case "startsWith":
return L`LOWER(${E}) LIKE ${S}`;
case "endsWith":
return L`LOWER(${E}) LIKE ${S}`;
default:
throw new Error(`Unsupported string operator: ${R}`);
}
}
/**
* Build MySQL type casting using CAST() function
* MySQL equivalent to PostgreSQL's :: casting syntax
*/
castToType(E, R) {
switch (R) {
case "timestamp":
return L`CAST(${E} AS DATETIME)`;
case "decimal":
return L`CAST(${E} AS DECIMAL(10,2))`;
case "integer":
return L`CAST(${E} AS SIGNED INTEGER)`;
default:
throw new Error(`Unsupported cast type: ${R}`);
}
}
/**
* Build MySQL AVG aggregation with IFNULL for NULL handling
* MySQL AVG returns NULL for empty sets, using IFNULL for consistency
*/
buildAvg(E) {
return L`IFNULL(AVG(${E}), 0)`;
}
/**
* Build MySQL CASE WHEN conditional expression
*/
buildCaseWhen(E, R) {
const A = E.map((S) => L`WHEN ${S.when} THEN ${S.then}`).reduce((S, e) => L`${S} ${e}`);
return R !== void 0 ? L`CASE ${A} ELSE ${R} END` : L`CASE ${A} END`;
}
/**
* Build MySQL boolean literal
* MySQL uses TRUE/FALSE keywords (equivalent to 1/0)
*/
buildBooleanLiteral(E) {
return E ? L`TRUE` : L`FALSE`;
}
/**
* Convert filter values - MySQL uses native types
* No conversion needed for MySQL
*/
convertFilterValue(E) {
return E;
}
/**
* Prepare date value for MySQL
* MySQL accepts Date objects directly
*/
prepareDateValue(E) {
return E;
}
/**
* MySQL stores timestamps as native timestamp types
*/
isTimestampInteger() {
return !1;
}
/**
* MySQL time dimensions already return proper values
* No conversion needed
*/
convertTimeDimensionResult(E) {
return E;
}
}
class vR extends jE {
getEngineType() {
return "sqlite";
}
/**
* Build SQLite time dimension using date/datetime functions with modifiers
* For integer timestamp columns (milliseconds), first convert to datetime
* SQLite doesn't have DATE_TRUNC like PostgreSQL, so we use strftime and date modifiers
* Returns datetime strings for consistency with other databases
*/
buildTimeDimension(E, R) {
switch (E) {
case "year":
return L`datetime(${R}, 'unixepoch', 'start of year')`;
case "quarter":
const A = L`datetime(${R}, 'unixepoch')`;
return L`datetime(${A}, 'start of year',
'+' || (((CAST(strftime('%m', ${A}) AS INTEGER) - 1) / 3) * 3) || ' months')`;
case "month":
return L`datetime(${R}, 'unixepoch', 'start of month')`;
case "week":
return L`date(datetime(${R}, 'unixepoch'), 'weekday 1', '-6 days')`;
case "day":
return L`datetime(${R}, 'unixepoch', 'start of day')`;
case "hour":
const S = L`datetime(${R}, 'unixepoch')`;
return L`datetime(strftime('%Y-%m-%d %H:00:00', ${S}))`;
case "minute":
const e = L`datetime(${R}, 'unixepoch')`;
return L`datetime(strftime('%Y-%m-%d %H:%M:00', ${e}))`;
case "second":
const O = L`datetime(${R}, 'unixepoch')`;
return L`datetime(strftime('%Y-%m-%d %H:%M:%S', ${O}))`;
default:
return L`datetime(${R}, 'unixepoch')`;
}
}
/**
* Build SQLite string matching conditions using LOWER() + LIKE for case-insensitive matching
* SQLite LIKE is case-insensitive by default, but LOWER() ensures consistency
*/
buildStringCondition(E, R, A) {
const S = this.buildPattern(R, A.toLowerCase());
switch (R) {
case "contains":
return L`LOWER(${E}) LIKE ${S}`;
case "notContains":
return L`LOWER(${E}) NOT LIKE ${S}`;
case "startsWith":
return L`LOWER(${E}) LIKE ${S}`;
case "endsWith":
return L`LOWER(${E}) LIKE ${S}`;
default:
throw new Error(`Unsupported string operator: ${R}`);
}
}
/**
* Build SQLite type casting using CAST() function
* SQLite has dynamic typing but supports CAST for consistency
*/
castToType(E, R) {
switch (R) {
case "timestamp":
return L`datetime(${E} / 1000, 'unixepoch')`;
case "decimal":
return L`CAST(${E} AS REAL)`;
case "integer":
return L`CAST(${E} AS INTEGER)`;
default:
throw new Error(`Unsupported cast type: ${R}`);
}
}
/**
* Build SQLite AVG aggregation with IFNULL for NULL handling
* SQLite AVG returns NULL for empty sets, using IFNULL for consistency
*/
buildAvg(E) {
return L`IFNULL(AVG(${E}), 0)`;
}
/**
* Build SQLite CASE WHEN conditional expression
*/
buildCaseWhen(E, R) {
const A = E.map((S) => S.then && typeof S.then == "object" && (S.then.queryChunks || S.then._ || S.then.sql) ? L`WHEN ${S.when} THEN ${L.raw("(")}${S.then}${L.raw(")")}` : L`WHEN ${S.when} THEN ${S.then}`).reduce((S, e) => L`${S} ${e}`);
return R !== void 0 ? R && typeof R == "object" && (R.queryChunks || R._ || R.sql) ? L`CASE ${A} ELSE ${L.raw("(")}${R}${L.raw(")")} END` : L`CASE ${A} ELSE ${R} END` : L`CASE ${A} END`;
}
/**
* Build SQLite boolean literal
* SQLite uses 1/0 for true/false
*/
buildBooleanLiteral(E) {
return E ? L`1` : L`0`;
}
/**
* Convert filter values to SQLite-compatible types
* SQLite doesn't support boolean types - convert boolean to integer (1/0)
* Convert Date objects to milliseconds for integer timestamp columns
*/
convertFilterValue(E) {
return typeof E == "boolean" ? E ? 1 : 0 : E instanceof Date ? E.getTime() : Array.isArray(E) ? E.map((R) => this.convertFilterValue(R)) : E;
}
/**
* Prepare date value for SQLite integer timestamp storage
* Convert Date objects to milliseconds (Unix timestamp * 1000)
*/
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();
}
/**
* SQLite stores timestamps as integers (milliseconds)
*/
isTimestampInteger() {
return !0;
}
/**
* Convert SQLite time dimension results back to Date objects
* SQLite time dimensions return datetime strings, but clients expect Date objects
*/
convertTimeDimensionResult(E) {
return E;
}
}
function QR(T) {
switch (T) {
case "postgres":
return new wR();
case "mysql":
return new xR();
case "sqlite":
return new vR();
default:
throw new Error(`Unsupported database engine: ${T}`);
}
}
class kE {
constructor(E, R, A) {
l(this, "databaseAdapter");
this.db = E, this.schema = R;
const S = A || this.getEngineType();
this.databaseAdapter = QR(S);
}
}
class ZR extends kE {
async execute(E, R) {
if (E && typeof E == "object") {
if (typeof E.execute == "function") {
const S = await E.execute();
return Array.isArray(S) ? S.map((e) => this.convertNumericFields(e, R)) : S;
}
if (this.db && typeof this.db.execute == "function")
try {
const S = await this.db.execute(E);
return Array.isArray(S) ? S.map((e) => this.convertNumericFields(e, R)) : S;
} catch (S) {
if (typeof E.getSQL == "function") {
const e = E.getSQL(), O = await this.db.execute(e);
return Array.isArray(O) ? O.map((C) => this.convertNumericFields(C, R)) : O;
}
throw S;
}
}
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((S) => this.convertNumericFields(S, R)) : A;
}
/**
* Convert numeric string fields to numbers (only for measure fields)
*/
convertNumericFields(E, R) {
if (!E || typeof E != "object") return E;
const A = {};
for (const [S, e] of Object.entries(E))
R && R.includes(S) ? A[S] = this.coerceToNumber(e) : A[S] = e;
return A;
}
/**
* Coerce a value to a number if it represents a numeric type
*/
coerceToNumber(E) {
var R, A;
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 S = E.toString();
if (/^-?\d+(\.\d+)?$/.test(S))
return S.includes(".") ? parseFloat(S) : parseInt(S, 10);
}
if (((R = E.constructor) == null ? void 0 : R.name) === "Numeric" || ((A = E.constructor) == null ? void 0 : A.name) === "Decimal" || "digits" in E || "sign" in E) {
const S = E.toString();
return parseFloat(S);
}
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";
}
}
class qR extends kE {
async execute(E, R) {
if (E && typeof E == "object" && typeof E.execute == "function") {
const A = await E.execute();
return Array.isArray(A) ? A.map((S) => this.convertNumericFields(S, R)) : A;
}
try {
if (this.db.all) {
const A = this.db.all(E);
return Array.isArray(A) ? A.map((S) => this.convertNumericFields(S, R)) : 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"}`);
}
}
/**
* Convert numeric string fields to numbers (only for measure fields)
*/
convertNumericFields(E, R) {
if (!E || typeof E != "object") return E;
const A = {};
for (const [S, e] of Object.entries(E))
R && R.includes(S) ? A[S] = this.coerceToNumber(e) : A[S] = e;
return A;
}
/**
* Coerce a value to a number if it represents a numeric type
*/
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";
}
}
class jR extends kE {
async execute(E, R) {
if (E && typeof E == "object" && typeof E.execute == "function") {
const S = await E.execute();
return Array.isArray(S) ? S.map((e) => this.convertNumericFields(e, R)) : S;
}
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((S) => this.convertNumericFields(S, R)) : A;
}
/**
* Convert numeric string fields to numbers (measure fields + numeric dimensions)
*/
convertNumericFields(E, R) {
if (!E || typeof E != "object") return E;
const A = {};
for (const [S, e] of Object.entries(E))
R && R.includes(S) ? A[S] = this.coerceToNumber(e) : A[S] = e;
return A;
}
/**
* Coerce a value to a number if it represents a numeric type
*/
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 NT(T, E) {
return new ZR(T, E, "postgres");
}
function tT(T, E) {
return new qR(T, E, "sqlite");
}
function kR(T, E) {
return new jR(T, E, "mysql");
}
function sT(T, E, R) {
if (R)
switch (R) {
case "postgres":
return NT(T, E);
case "mysql":
return kR(T, E);
case "sqlite":
return tT(T, E);
}
if (T.all && T.run)
return tT(T, E);
if (T.execute)
return NT(T, E);
throw new Error("Unable to determine database engine type. Please specify engineType parameter.");
}
function CT(T) {
return typeof T == "function" ? T() : T;
}
function zR(T, E) {
if (E) return E;
switch (T) {
case "belongsTo":
return "inner";
case "hasOne":
return "left";
case "hasMany":
return "left";
default:
return "left";
}
}
function x(T, E) {
return typeof T == "function" ? T(E) : T;
}
class EA {
constructor(E) {
this.databaseAdapter = E;
}
/**
* Build dynamic selections for measures, dimensions, and time dimensions
* Works for both single and multi-cube queries
*/
buildSelections(E, R, A) {
const S = {}, e = E instanceof Map ? E : /* @__PURE__ */ new Map([[E.name, E]]);
if (R.dimensions)
for (const O of R.dimensions) {
const [C, _] = O.split("."), I = e.get(C);
if (I && I.dimensions && I.dimensions[_]) {
const N = I.dimensions[_], s = x(N.sql, A);
S[O] = L`${s}`.as(O);
}
}
if (R.measures)
for (const O of R.measures) {
const [C, _] = O.split("."), I = e.get(C);
if (I && I.measures && I.measures[_]) {
const N = I.measures[_], s = this.buildMeasureExpression(N, A);
S[O] = L`${s}`.as(O);
}
}
if (R.timeDimensions)
for (const O of R.timeDimensions) {
const [C, _] = O.dimension.split("."), I = e.get(C);
if (I && I.dimensions && I.dimensions[_]) {
const N = I.dimensions[_], s = this.buildTimeDimensionExpression(
N.sql,
O.granularity,
A
);
S[O.dimension] = L`${s}`.as(O.dimension);
}
}
return Object.keys(S).length === 0 && (S.count = ME()), S;
}
/**
* Build measure expression for HAVING clause, handling CTE references correctly
*/
buildHavingMeasureExpression(E, R, A, S, e) {
if (e && e.preAggregationCTEs) {
const O = e.preAggregationCTEs.find((C) => C.cube.name === E);
if (O && O.measures.includes(`${E}.${R}`)) {
const C = L`${L.identifier(O.cteAlias)}.${L.identifier(R)}`;
switch (A.type) {
case "count":
case "countDistinct":
case "sum":
return Q(C);
case "avg":
return this.databaseAdapter.buildAvg(C);
case "min":
return xE(C);
case "max":
return wE(C);
case "number":
return Q(C);
default:
return Q(C);
}
}
}
return this.buildMeasureExpression(A, S);
}
/**
* Build measure expression with aggregation and filters
*/
buildMeasureExpression(E, R) {
let A = x(E.sql, R);
if (E.filters && E.filters.length > 0) {
const S = E.filters.map((e) => e(R)).filter(Boolean);
if (S.length > 0) {
const e = S.length === 1 ? S[0] : b(...S);
A = this.databaseAdapter.buildCaseWhen([
{ when: e, then: A }
]);
}
}
switch (E.type) {
case "count":
return ME(A);
case "countDistinct":
return JR(A);
case "sum":
return Q(A);
case "avg":
return this.databaseAdapter.buildAvg(A);
case "min":
return xE(A);
case "max":
return wE(A);
case "number":
return A;
default:
return ME(A);
}
}
/**
* Build time dimension expression with granularity using database adapter
*/
buildTimeDimensionExpression(E, R, A) {
const S = x(E, A);
return R ? this.databaseAdapter.buildTimeDimension(R, S) : S instanceof m ? S : L`${S}`;
}
/**
* Build WHERE conditions from semantic query filters (dimensions only)
* Works for both single and multi-cube queries
*/
buildWhereConditions(E, R, A, S) {
const e = [], O = E instanceof Map ? E : /* @__PURE__ */ new Map([[E.name, E]]);
if (R.filters && R.filters.length > 0)
for (const C of R.filters) {
const _ = this.processFilter(C, O, A, "where", S);
_ && e.push(_);
}
if (R.timeDimensions)
for (const C of R.timeDimensions) {
const [_, I] = C.dimension.split("."), N = O.get(_);
if (N && N.dimensions[I] && C.dateRange) {
if (S != null && S.preAggregationCTEs && S.preAggregationCTEs.some((M) => M.cube.name === _))
continue;
const s = N.dimensions[I], t = x(s.sql, A), n = this.buildDateRangeCondition(t, C.dateRange);
n && e.push(n);
}
}
return e;
}
/**
* Build HAVING conditions from semantic query filters (measures only)
* Works for both single and multi-cube queries
*/
buildHavingConditions(E, R, A, S) {
const e = [], O = E instanceof Map ? E : /* @__PURE__ */ new Map([[E.name, E]]);
if (R.filters && R.filters.length > 0)
for (const C of R.filters) {
const _ = this.processFilter(C, O, A, "having", S);
_ && e.push(_);
}
return e;
}
/**
* Process a single filter (basic or logical)
* @param filterType - 'where' for dimension filters, 'having' for measure filters
*/
processFilter(E, R, A, S, e) {
if ("and" in E || "or" in E) {
const n = E;
if (n.and) {
const P = n.and.map((M) => this.processFilter(M, R, A, S, e)).filter((M) => M !== null);
return P.length > 0 ? b(...P) : null;
}
if (n.or) {
const P = n.or.map((M) => this.processFilter(M, R, A, S, e)).filter((M) => M !== null);
return P.length > 0 ? bR(...P) : null;
}
}
const O = E, [C, _] = O.member.split("."), I = R.get(C);
if (!I) return null;
const N = I.dimensions[_], s = I.measures[_], t = N || s;
if (!t) return null;
if (S === "where" && N) {
if (e != null && e.preAggregationCTEs && e.preAggregationCTEs.some((M) => M.cube.name === C))
return null;
const n = x(N.sql, A);
return this.buildFilterCondition(n, O.operator, O.values, t);
} else {
if (S === "where" && s)
return null;
if (S === "having" && s) {
const n = this.buildHavingMeasureExpression(C, _, s, A, e);
return this.buildFilterCondition(n, O.operator, O.values, t);
}
}
return null;
}
/**
* Build filter condition using Drizzle operators
*/
buildFilterCondition(E, R, A, S) {
if (!A || A.length === 0)
return R === "equals" ? this.databaseAdapter.buildBooleanLiteral(!1) : null;
const e = A.filter((C) => !(C == null || C === "" || typeof C == "string" && C.includes("\0"))).map(this.databaseAdapter.convertFilterValue);
if (e.length === 0 && !["set", "notSet"].includes(R))
return R === "equals" ? this.databaseAdapter.buildBooleanLiteral(!1) : null;
const O = e[0];
switch (R) {
case "equals":
if (e.length > 1) {
if ((S == null ? void 0 : S.type) === "time") {
const C = e.map((_) => this.normalizeDate(_) || _);
return IT(E, C);
}
return IT(E, e);
} else if (e.length === 1) {
const C = (S == null ? void 0 : S.type) === "time" && this.normalizeDate(O) || O;
return qE(E, C);
}
return this.databaseAdapter.buildBooleanLiteral(!1);
case "notEquals":
return e.length > 1 ? yR(E, e) : e.length === 1 ? XR(E, O) : null;
case "contains":
return this.databaseAdapter.buildStringCondition(E, "contains", O);
case "notContains":
return this.databaseAdapter.buildStringCondition(E, "notContains", O);
case "startsWith":
return this.databaseAdapter.buildStringCondition(E, "startsWith", O);
case "endsWith":
return this.databaseAdapter.buildStringCondition(E, "endsWith", O);
case "gt":
return ST(E, O);
case "gte":
return RE(E, O);
case "lt":
return eT(E, O);
case "lte":
return AE(E, O);
case "set":
return $R(E);
case "notSet":
return KR(E);
case "inDateRange":
if (e.length >= 2) {
const C = this.normalizeDate(e[0]), _ = this.normalizeDate(e[1]);
if (C && _)
return b(
RE(E, C),
AE(E, _)
);
}
return null;
case "beforeDate": {
const C = this.normalizeDate(O);
return C ? eT(E, C) : null;
}
case "afterDate": {
const C = this.normalizeDate(O);
return C ? ST(E, C) : null;
}
default:
return null;
}
}
/**
* Build date range condition for time dimensions
*/
buildDateRangeCondition(E, R) {
if (!R) return null;
if (Array.isArray(R) && R.length >= 2) {
const A = this.normalizeDate(R[0]), S = this.normalizeDate(R[1]);
return !A || !S ? null : b(
RE(E, A),
AE(E, S)
);
}
if (typeof R == "string") {
const A = this.parseRelativeDateRange(R);
if (A)
return b(
RE(E, A.start),
AE(E, A.end)
);
const S = this.normalizeDate(R);
if (!S) return null;
const e = new Date(S);
e.setUTCHours(0, 0, 0, 0);
const O = new Date(S);
return O.setUTCHours(23, 59, 59, 999), b(
RE(E, e),
AE(E, O)
);
}
return null;
}
/**
* Parse relative date range expressions like "today", "yesterday", "last 7 days", "this month", etc.
* Handles all 14 DATE_RANGE_OPTIONS from the client
*/
parseRelativeDateRange(E) {
const R = /* @__PURE__ */ new Date(), A = E.toLowerCase().trim(), S = R.getUTCFullYear(), e = R.getUTCMonth(), O = R.getUTCDate(), C = R.getUTCDay();
if (A === "today") {
const s = new Date(R);
s.setUTCHours(0, 0, 0, 0);
const t = new Date(R);
return t.setUTCHours(23, 59, 59, 999), { start: s, end: t };
}
if (A === "yesterday") {
const s = new Date(R);
s.setUTCDate(O - 1), s.setUTCHours(0, 0, 0, 0);
const t = new Date(R);
return t.setUTCDate(O - 1), t.setUTCHours(23, 59, 59, 999), { start: s, end: t };
}
if (A === "this week") {
const s = C === 0 ? -6 : 1 - C, t = new Date(R);
t.setUTCDate(O + s), t.setUTCHours(0, 0, 0, 0);
const n = new Date(t);
return n.setUTCDate(t.getUTCDate() + 6), n.setUTCHours(23, 59, 59, 999), { start: t, end: n };
}
if (A === "this month") {
const s = new Date(Date.UTC(S, e, 1, 0, 0, 0, 0)), t = new Date(Date.UTC(S, e + 1, 0, 23, 59, 59, 999));
return { start: s, end: t };
}
if (A === "this quarter") {
const s = Math.floor(e / 3), t = new Date(Date.UTC(S, s * 3, 1, 0, 0, 0, 0)), n = new Date(Date.UTC(S, s * 3 + 3, 0, 23, 59, 59, 999));
return { start: t, end: n };
}
if (A === "this year") {
const s = new Date(Date.UTC(S, 0, 1, 0, 0, 0, 0)), t = new Date(Date.UTC(S, 11, 31, 23, 59, 59, 999));
return { start: s, end: t };
}
const _ = A.match(/^last\s+(\d+)\s+days?$/);
if (_) {
const s = parseInt(_[1], 10), t = new Date(R);
t.setUTCDate(O - s + 1), t.setUTCHours(0, 0, 0, 0);
const n = new Date(R);
return n.setUTCHours(23, 59, 59, 999), { start: t, end: n };
}
if (A === "last week") {
const s = C === 0 ? -13 : -6 - C, t = new Date(R);
t.setUTCDate(O + s), t.setUTCHours(0, 0, 0, 0);
const n = new Date(t);
return n.setUTCDate(t.getUTCDate() + 6), n.setUTCHours(23, 59, 59, 999), { start: t, end: n };
}
if (A === "last month") {
const s = new Date(Date.UTC(S, e - 1, 1, 0, 0, 0, 0)), t = new Date(Date.UTC(S, e, 0, 23, 59, 59, 999));
return { start: s, end: t };
}
if (A === "last quarter") {
const s = Math.floor(e / 3), t = s === 0 ? 3 : s - 1, n = s === 0 ? S - 1 : S, P = new Date(Date.UTC(n, t * 3, 1, 0, 0, 0, 0)), M = new Date(Date.UTC(n, t * 3 + 3, 0, 23, 59, 59, 999));
return { start: P, end: M };
}
if (A === "last year") {
const s = new Date(Date.UTC(S - 1, 0, 1, 0, 0, 0, 0)), t = new Date(Date.UTC(S - 1, 11, 31, 23, 59, 59, 999));
return { start: s, end: t };
}
if (A === "last 12 months") {
const s = new Date(Date.UTC(S, e - 11, 1, 0, 0, 0, 0)), t = new Date(R);
return t.setUTCHours(23, 59, 59, 999), { start: s, end: t };
}
const I = A.match(/^last\s+(\d+)\s+months?$/);
if (I) {
const s = parseInt(I[1], 10), t = new Date(Date.UTC(S, e - s + 1, 1, 0, 0, 0, 0)), n = new Date(R);
return n.setUTCHours(23, 59, 59, 999), { start: t, end: n };
}
const N = A.match(/^last\s+(\d+)\s+years?$/);
if (N) {
const s = parseInt(N[1], 10), t = new Date(Date.UTC(S - s, 0, 1, 0, 0, 0, 0)), n = new Date(R);
return n.setUTCHours(23, 59, 59, 999), { start: t, end: n };
}
return null;
}
/**
* Normalize date values to handle strings, numbers, and Date objects
* Always returns a JavaScript Date object or null
* Database-agnostic - just ensures we have a valid Date
*/
normalizeDate(E) {
if (!E) return null;
if (E instanceof Date)
return isNaN(E.getTime()) ? null : E;
if (typeof E == "number") {
const A = E < 1e10 ? E * 1e3 : E, S = new Date(A);
return isNaN(S.getTime()) ? null : S;
}
if (typeof E == "string") {
const A = new Date(E);
return isNaN(A.getTime()) ? null : A;
}
const R = new Date(E);
return isNaN(R.getTime()) ? null : R;
}
/**
* Build GROUP BY fields from dimensions and time dimensions
* Works for both single and multi-cube queries
*/
buildGroupByFields(E, R, A, S) {
var _, I;
const e = [];
if (!(R.measures && R.measures.length > 0))
return [];
const C = E instanceof Map ? E : /* @__PURE__ */ new Map([[E.name, E]]);
if (R.dimensions)
for (const N of R.dimensions) {
const [s, t] = N.split("."), n = C.get(s);
if (n && n.dimensions && n.dimensions[t])
if ((_ = S == null ? void 0 : S.preAggregationCTEs) == null ? void 0 : _.some((M) => M.cube.name === s)) {
const M = S.preAggregationCTEs.find((G) => G.cube.name === s), o = M.joinKeys.find((G) => G.targetColumn === t);
if (o && o.sourceColumnObj)
e.push(o.sourceColumnObj);
else {
const G = L`${L.identifier(M.cteAlias)}.${L.identifier(t)}`;
e.push(G);
}
} else {
const M = n.dimensions[t], o = x(M.sql, A);
e.push(o);
}
}
if (R.timeDimensions)
for (const N of R.timeDimensions) {
const [s, t] = N.dimension.split("."), n = C.get(s);
if (n && n.dimensions && n.dimensions[t])
if ((I = S == null ? void 0 : S.preAggregationCTEs) == null ? void 0 : I.some((M) => M.cube.name === s)) {
const M = S.preAggregationCTEs.find((G) => G.cube.name === s), o = M.joinKeys.find((G) => G.targetColumn === t);
if (o && o.sourceColumnObj) {
const G = this.buildTimeDimensionExpression(
o.sourceColumnObj,
N.granularity,
A
);
e.push(G);
} else {
const G = L`${L.identifier(M.cteAlias)}.${L.identifier(t)}`;
e.push(G);
}
} else {
const M = n.dimensions[t], o = this.buildTimeDimensionExpression(
M.sql,
N.granularity,
A
);
e.push(o);
}
}
return e;
}
/**
* Build ORDER BY clause with automatic time dimension sorting
*/
buildOrderBy(E, R) {
var e;
const A = [], S = R || [
...E.measures || [],
...E.dimensions || [],
...((e = E.timeDimensions) == null ? void 0 : e.map((O) => O.dimension)) || []
];
if (E.order && Object.keys(E.order).length > 0)
for (const [O, C] of Object.entries(E.order)) {
if (!S.includes(O))
throw new Error(`Cannot order by '${O}': field is not selected in the query`);
const _ = C === "desc" ? gR(L.identifier(O)) : OT(L.identifier(O));
A.push(_);
}
if (E.timeDimensions && E.timeDimensions.length > 0) {
const O = new Set(Object.keys(E.order || {})), C = [...E.timeDimensions].sort(
(_, I) => _.dimension.localeCompare(I.dimension)
);
for (const _ of C)
O.has(_.dimension) || A.push(OT(L.identifier(_.dimension)));
}
return A;
}
/**
* Collect numeric field names (measures + numeric dimensions) for type conversion
* Works for both single and multi-cube queries
*/
collectNumericFields(E, R) {
const A = [], S = E instanceof Map ? E : /* @__PURE__ */ new Map([[E.name, E]]);
if (R.measures && A.push(...R.measures), R.dimensions)
for (const e of R.dimensions) {
const [O, C] = e.split("."), _ = S.get(O);
if (_) {
const I = _.dimensions[C];
I && I.type === "number" && A.push(e);
}
}
return A;
}
/**
* Apply LIMIT and OFFSET to a query with validation
* If offset is provided without limit, add a reasonable default limit
*/
applyLimitAndOffset(E, R) {
let A = R.limit;
R.offset !== void 0 && R.offset > 0 && A === void 0 && (A = 50);
let S = E;
if (A !== void 0) {
if (A < 0)
throw new Error("Limit must be non-negative");
S = S.limit(A);
}
if (R.offset !== void 0) {
if (R.offset < 0)
throw new Error("Offset must be non-negative");
S = S.offset(R.offset);
}
return S;
}
}
class TA {
/**
* Analyze a semantic query to determine which cubes are involved
*/
analyzeCubeUsage(E) {
const R = /* @__PURE__ */ new Set();
if (E.measures)
for (const A of E.measures) {
const [S] = A.split(".");
R.add(S);
}
if (E.dimensions)
for (const A of E.dimensions) {
const [S] = A.split(".");
R.add(S);
}
if (E.timeDimensions)
for (const A of E.timeDimensions) {
const [S] = A.dimension.split(".");
R.add(S);
}
if (E.filters)
for (const A of E.filters)
this.extractCubeNamesFromFilter(A, R);
return R;
}
/**
* Recursively extract cube names from filters (handles logical filters)
*/
extractCubeNamesFromFilter(E, R) {
if ("and" in E || "or" in E) {
const A = E.and || E.or || [];
for (const S of A)
this.extractCubeNamesFromFilter(S, R);
return;
}
if ("member" in E) {
const [A] = E.member.split(".");
A && R.add(A);
}
}
/**
* Extract measures referenced in filters (for CTE inclusion)
*/
extractMeasuresFromFilters(E, R) {
const A = [];
if (!E.filters)
return A;
for (const S of E.filters)
this.extractMeasuresFromFilter(S, R, A);
return A;
}
/**
* Recursively extract measures from filters for a specific cube
*/
extractMeasuresFromFilter(E, R, A) {
if ("and" in E || "or" in E) {
const S = E.and || E.or || [];
for (const e of S)
this.extractMeasuresFromFi