UNPKG

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
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