@sqlrooms/pivot 0.29.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/dist/PivotCellContent.d.ts +10 -0
- package/dist/PivotCellContent.d.ts.map +1 -0
- package/dist/PivotCellContent.js +37 -0
- package/dist/PivotCellContent.js.map +1 -0
- package/dist/PivotCoreSlice.d.ts +73 -0
- package/dist/PivotCoreSlice.d.ts.map +1 -0
- package/dist/PivotCoreSlice.js +175 -0
- package/dist/PivotCoreSlice.js.map +1 -0
- package/dist/PivotEditor.d.ts +66 -0
- package/dist/PivotEditor.d.ts.map +1 -0
- package/dist/PivotEditor.js +341 -0
- package/dist/PivotEditor.js.map +1 -0
- package/dist/PivotResults.d.ts +15 -0
- package/dist/PivotResults.d.ts.map +1 -0
- package/dist/PivotResults.js +102 -0
- package/dist/PivotResults.js.map +1 -0
- package/dist/PivotSlice.d.ts +7 -0
- package/dist/PivotSlice.d.ts.map +1 -0
- package/dist/PivotSlice.js +353 -0
- package/dist/PivotSlice.js.map +1 -0
- package/dist/PivotView.d.ts +3 -0
- package/dist/PivotView.d.ts.map +1 -0
- package/dist/PivotView.js +39 -0
- package/dist/PivotView.js.map +1 -0
- package/dist/TableRenderer.d.ts +14 -0
- package/dist/TableRenderer.d.ts.map +1 -0
- package/dist/TableRenderer.js +100 -0
- package/dist/TableRenderer.js.map +1 -0
- package/dist/TsvRenderer.d.ts +7 -0
- package/dist/TsvRenderer.d.ts.map +1 -0
- package/dist/TsvRenderer.js +26 -0
- package/dist/TsvRenderer.js.map +1 -0
- package/dist/aggregators.d.ts +24 -0
- package/dist/aggregators.d.ts.map +1 -0
- package/dist/aggregators.js +232 -0
- package/dist/aggregators.js.map +1 -0
- package/dist/helpers.d.ts +85 -0
- package/dist/helpers.d.ts.map +1 -0
- package/dist/helpers.js +348 -0
- package/dist/helpers.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/pivotCellRegistryEntry.d.ts +4 -0
- package/dist/pivotCellRegistryEntry.d.ts.map +1 -0
- package/dist/pivotCellRegistryEntry.js +137 -0
- package/dist/pivotCellRegistryEntry.js.map +1 -0
- package/dist/pivotCellTypes.d.ts +23 -0
- package/dist/pivotCellTypes.d.ts.map +1 -0
- package/dist/pivotCellTypes.js +14 -0
- package/dist/pivotCellTypes.js.map +1 -0
- package/dist/pivotExecution.d.ts +16 -0
- package/dist/pivotExecution.d.ts.map +1 -0
- package/dist/pivotExecution.js +49 -0
- package/dist/pivotExecution.js.map +1 -0
- package/dist/sql.d.ts +17 -0
- package/dist/sql.d.ts.map +1 -0
- package/dist/sql.js +278 -0
- package/dist/sql.js.map +1 -0
- package/dist/types.d.ts +513 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +107 -0
- package/dist/types.js.map +1 -0
- package/package.json +58 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pivotCellTypes.d.ts","sourceRoot":"","sources":["../src/pivotCellTypes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AACtB,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,iBAAiB,CAAC;AAE1C,eAAO,MAAM,aAAa;;iBAExB,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAE1D,eAAO,MAAM,SAAS;;;;;;iBAIpB,CAAC;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,CAAC;AAClD,eAAO,MAAM,eAAe;;;;;;iBAAY,CAAC;AAEzC,wBAAgB,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,IAAI,SAAS,CAEzD"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const PivotCellData = z.object({
|
|
3
|
+
pivotId: z.string(),
|
|
4
|
+
});
|
|
5
|
+
export const PivotCell = z.object({
|
|
6
|
+
id: z.string(),
|
|
7
|
+
type: z.literal('pivot'),
|
|
8
|
+
data: PivotCellData,
|
|
9
|
+
});
|
|
10
|
+
export const PivotCellSchema = PivotCell;
|
|
11
|
+
export function isPivotCell(cell) {
|
|
12
|
+
return cell.type === 'pivot';
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=pivotCellTypes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pivotCellTypes.js","sourceRoot":"","sources":["../src/pivotCellTypes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAGtB,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC;IACpC,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;CACpB,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,CAAC,MAAM,CAAC;IAChC,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE;IACd,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC;IACxB,IAAI,EAAE,aAAa;CACpB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,eAAe,GAAG,SAAS,CAAC;AAEzC,MAAM,UAAU,WAAW,CAAC,IAAU;IACpC,OAAO,IAAI,CAAC,IAAI,KAAK,OAAO,CAAC;AAC/B,CAAC","sourcesContent":["import {z} from 'zod';\nimport type {Cell} from '@sqlrooms/cells';\n\nexport const PivotCellData = z.object({\n pivotId: z.string(),\n});\nexport type PivotCellData = z.infer<typeof PivotCellData>;\n\nexport const PivotCell = z.object({\n id: z.string(),\n type: z.literal('pivot'),\n data: PivotCellData,\n});\nexport type PivotCell = z.infer<typeof PivotCell>;\nexport const PivotCellSchema = PivotCell;\n\nexport function isPivotCell(cell: Cell): cell is PivotCell {\n return cell.type === 'pivot';\n}\n"]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type DuckDbConnector } from '@sqlrooms/duckdb';
|
|
2
|
+
import type { PivotConfig, PivotQuerySource, PivotRelationViews } from './types';
|
|
3
|
+
export declare function createPivotRelationViews(relationBaseName: string, schemaName?: string): PivotRelationViews;
|
|
4
|
+
export declare function createOrReplacePivotRelations(args: {
|
|
5
|
+
connector: DuckDbConnector;
|
|
6
|
+
source: PivotQuerySource;
|
|
7
|
+
config: PivotConfig;
|
|
8
|
+
relationBaseName: string;
|
|
9
|
+
schemaName?: string;
|
|
10
|
+
signal?: AbortSignal;
|
|
11
|
+
}): Promise<PivotRelationViews>;
|
|
12
|
+
export declare function dropPivotRelations(args: {
|
|
13
|
+
connector: DuckDbConnector;
|
|
14
|
+
relations?: PivotRelationViews;
|
|
15
|
+
}): Promise<void>;
|
|
16
|
+
//# sourceMappingURL=pivotExecution.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pivotExecution.d.ts","sourceRoot":"","sources":["../src/pivotExecution.ts"],"names":[],"mappings":"AAAA,OAAO,EAAW,KAAK,eAAe,EAAC,MAAM,kBAAkB,CAAC;AAQhE,OAAO,KAAK,EAAC,WAAW,EAAE,gBAAgB,EAAE,kBAAkB,EAAC,MAAM,SAAS,CAAC;AAE/E,wBAAgB,wBAAwB,CACtC,gBAAgB,EAAE,MAAM,EACxB,UAAU,SAAS,GAClB,kBAAkB,CAWpB;AAED,wBAAsB,6BAA6B,CAAC,IAAI,EAAE;IACxD,SAAS,EAAE,eAAe,CAAC;IAC3B,MAAM,EAAE,gBAAgB,CAAC;IACzB,MAAM,EAAE,WAAW,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAmD9B;AAED,wBAAsB,kBAAkB,CAAC,IAAI,EAAE;IAC7C,SAAS,EAAE,eAAe,CAAC;IAC3B,SAAS,CAAC,EAAE,kBAAkB,CAAC;CAChC,iBAcA"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { escapeId } from '@sqlrooms/duckdb';
|
|
2
|
+
import { buildCellsQuery, buildColTotalsQuery, buildGrandTotalQuery, buildPivotExportQuery, buildRowTotalsQuery, } from './sql';
|
|
3
|
+
export function createPivotRelationViews(relationBaseName, schemaName = 'main') {
|
|
4
|
+
const qualify = (suffix) => `${escapeId(schemaName)}.${escapeId(`${relationBaseName}_${suffix}`)}`;
|
|
5
|
+
return {
|
|
6
|
+
cells: qualify('cells'),
|
|
7
|
+
rowTotals: qualify('row_totals'),
|
|
8
|
+
colTotals: qualify('col_totals'),
|
|
9
|
+
grandTotal: qualify('grand_total'),
|
|
10
|
+
export: qualify('export'),
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export async function createOrReplacePivotRelations(args) {
|
|
14
|
+
const { connector, source, config, relationBaseName, schemaName = 'main', signal, } = args;
|
|
15
|
+
const relations = createPivotRelationViews(relationBaseName, schemaName);
|
|
16
|
+
await connector.query(`CREATE SCHEMA IF NOT EXISTS ${escapeId(schemaName)}`, {
|
|
17
|
+
signal,
|
|
18
|
+
});
|
|
19
|
+
const cellsQuery = buildCellsQuery(config, source);
|
|
20
|
+
const rowTotalsQuery = buildRowTotalsQuery(config, source);
|
|
21
|
+
const colTotalsQuery = buildColTotalsQuery(config, source);
|
|
22
|
+
const grandTotalQuery = buildGrandTotalQuery(config, source);
|
|
23
|
+
await connector.query(`CREATE OR REPLACE VIEW ${relations.cells} AS ${cellsQuery}`, { signal });
|
|
24
|
+
await connector.query(`CREATE OR REPLACE VIEW ${relations.rowTotals} AS ${rowTotalsQuery}`, { signal });
|
|
25
|
+
await connector.query(`CREATE OR REPLACE VIEW ${relations.colTotals} AS ${colTotalsQuery}`, { signal });
|
|
26
|
+
await connector.query(`CREATE OR REPLACE VIEW ${relations.grandTotal} AS ${grandTotalQuery}`, { signal });
|
|
27
|
+
const colLabels = await connector.query(`SELECT DISTINCT col_label FROM ${relations.cells} ORDER BY col_label`, { signal });
|
|
28
|
+
const colKeys = Array.from({ length: colLabels.numRows }, (_, index) => String(colLabels.getChild('col_label')?.get(index) ?? ''));
|
|
29
|
+
const exportQuery = buildPivotExportQuery(config, source, colKeys);
|
|
30
|
+
await connector.query(`CREATE OR REPLACE VIEW ${relations.export} AS ${exportQuery}`, { signal });
|
|
31
|
+
return relations;
|
|
32
|
+
}
|
|
33
|
+
export async function dropPivotRelations(args) {
|
|
34
|
+
const { connector, relations } = args;
|
|
35
|
+
if (!relations) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
for (const relation of Object.values(relations)) {
|
|
39
|
+
if (!relation)
|
|
40
|
+
continue;
|
|
41
|
+
try {
|
|
42
|
+
await connector.query(`DROP VIEW IF EXISTS ${relation}`);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// Best-effort cleanup.
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=pivotExecution.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pivotExecution.js","sourceRoot":"","sources":["../src/pivotExecution.ts"],"names":[],"mappings":"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","sourcesContent":["import {escapeId, type DuckDbConnector} from '@sqlrooms/duckdb';\nimport {\n buildCellsQuery,\n buildColTotalsQuery,\n buildGrandTotalQuery,\n buildPivotExportQuery,\n buildRowTotalsQuery,\n} from './sql';\nimport type {PivotConfig, PivotQuerySource, PivotRelationViews} from './types';\n\nexport function createPivotRelationViews(\n relationBaseName: string,\n schemaName = 'main',\n): PivotRelationViews {\n const qualify = (suffix: string) =>\n `${escapeId(schemaName)}.${escapeId(`${relationBaseName}_${suffix}`)}`;\n\n return {\n cells: qualify('cells'),\n rowTotals: qualify('row_totals'),\n colTotals: qualify('col_totals'),\n grandTotal: qualify('grand_total'),\n export: qualify('export'),\n };\n}\n\nexport async function createOrReplacePivotRelations(args: {\n connector: DuckDbConnector;\n source: PivotQuerySource;\n config: PivotConfig;\n relationBaseName: string;\n schemaName?: string;\n signal?: AbortSignal;\n}): Promise<PivotRelationViews> {\n const {\n connector,\n source,\n config,\n relationBaseName,\n schemaName = 'main',\n signal,\n } = args;\n const relations = createPivotRelationViews(relationBaseName, schemaName);\n\n await connector.query(`CREATE SCHEMA IF NOT EXISTS ${escapeId(schemaName)}`, {\n signal,\n });\n\n const cellsQuery = buildCellsQuery(config, source);\n const rowTotalsQuery = buildRowTotalsQuery(config, source);\n const colTotalsQuery = buildColTotalsQuery(config, source);\n const grandTotalQuery = buildGrandTotalQuery(config, source);\n\n await connector.query(\n `CREATE OR REPLACE VIEW ${relations.cells} AS ${cellsQuery}`,\n {signal},\n );\n await connector.query(\n `CREATE OR REPLACE VIEW ${relations.rowTotals} AS ${rowTotalsQuery}`,\n {signal},\n );\n await connector.query(\n `CREATE OR REPLACE VIEW ${relations.colTotals} AS ${colTotalsQuery}`,\n {signal},\n );\n await connector.query(\n `CREATE OR REPLACE VIEW ${relations.grandTotal} AS ${grandTotalQuery}`,\n {signal},\n );\n\n const colLabels = await connector.query(\n `SELECT DISTINCT col_label FROM ${relations.cells} ORDER BY col_label`,\n {signal},\n );\n const colKeys = Array.from({length: colLabels.numRows}, (_, index) =>\n String(colLabels.getChild('col_label')?.get(index) ?? ''),\n );\n const exportQuery = buildPivotExportQuery(config, source, colKeys);\n await connector.query(\n `CREATE OR REPLACE VIEW ${relations.export} AS ${exportQuery}`,\n {signal},\n );\n\n return relations;\n}\n\nexport async function dropPivotRelations(args: {\n connector: DuckDbConnector;\n relations?: PivotRelationViews;\n}) {\n const {connector, relations} = args;\n if (!relations) {\n return;\n }\n\n for (const relation of Object.values(relations)) {\n if (!relation) continue;\n try {\n await connector.query(`DROP VIEW IF EXISTS ${relation}`);\n } catch {\n // Best-effort cleanup.\n }\n }\n}\n"]}
|
package/dist/sql.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { DataTable } from '@sqlrooms/duckdb';
|
|
2
|
+
import { PivotConfig, PivotField, PivotQuerySource } from './types';
|
|
3
|
+
type PivotQueryInput = DataTable | PivotQuerySource;
|
|
4
|
+
export declare function createPivotQuerySource(tableRef: string, columns: PivotField[]): PivotQuerySource;
|
|
5
|
+
export declare function createPivotQuerySourceFromTable(table: DataTable): PivotQuerySource;
|
|
6
|
+
export declare function buildCellsQuery(config: PivotConfig, source: PivotQueryInput): string;
|
|
7
|
+
export declare function buildRowTotalsQuery(config: PivotConfig, source: PivotQueryInput): string;
|
|
8
|
+
export declare function buildColTotalsQuery(config: PivotConfig, source: PivotQueryInput): string;
|
|
9
|
+
export declare function buildGrandTotalQuery(config: PivotConfig, source: PivotQueryInput): string;
|
|
10
|
+
export declare function buildDistinctValuesQuery(source: PivotQueryInput, attribute: string, menuLimit: number): string;
|
|
11
|
+
export declare function buildPivotExportQuery(config: PivotConfig, source: PivotQueryInput, colKeys: string[]): string;
|
|
12
|
+
export declare function buildRendererTitle(config: PivotConfig, transpose?: boolean): string;
|
|
13
|
+
export declare function getRowAlias(index: number): string;
|
|
14
|
+
export declare function getColAlias(index: number): string;
|
|
15
|
+
export declare function getNullLabel(): string;
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=sql.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sql.d.ts","sourceRoot":"","sources":["../src/sql.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,EAAsB,MAAM,kBAAkB,CAAC;AAEhE,OAAO,EACL,WAAW,EACX,UAAU,EACV,gBAAgB,EAEjB,MAAM,SAAS,CAAC;AAIjB,KAAK,eAAe,GAAG,SAAS,GAAG,gBAAgB,CAAC;AAMpD,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,UAAU,EAAE,GACpB,gBAAgB,CAElB;AAED,wBAAgB,+BAA+B,CAC7C,KAAK,EAAE,SAAS,GACf,gBAAgB,CAQlB;AAsED,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,eAAe,UAoE3E;AAmFD,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,WAAW,EACnB,MAAM,EAAE,eAAe,UAGxB;AAED,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,WAAW,EACnB,MAAM,EAAE,eAAe,UAGxB;AAED,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,WAAW,EACnB,MAAM,EAAE,eAAe,UAGxB;AAED,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,eAAe,EACvB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,UAWlB;AAED,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,WAAW,EACnB,MAAM,EAAE,eAAe,EACvB,OAAO,EAAE,MAAM,EAAE,UAoClB;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,WAAW,EAAE,SAAS,UAAQ,UAcxE;AASD,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,UAExC;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,UAExC;AAED,wBAAgB,YAAY,WAE3B"}
|
package/dist/sql.js
ADDED
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import { escapeId, escapeVal } from '@sqlrooms/duckdb';
|
|
2
|
+
import { getAggregatorLabel, getPivotAggregator } from './aggregators';
|
|
3
|
+
const NULL_LABEL = 'null';
|
|
4
|
+
function getTableReference(source) {
|
|
5
|
+
return 'tableRef' in source ? source.tableRef : source.table.toString();
|
|
6
|
+
}
|
|
7
|
+
export function createPivotQuerySource(tableRef, columns) {
|
|
8
|
+
return { tableRef, columns };
|
|
9
|
+
}
|
|
10
|
+
export function createPivotQuerySourceFromTable(table) {
|
|
11
|
+
return {
|
|
12
|
+
tableRef: table.table.toString(),
|
|
13
|
+
columns: table.columns.map((column) => ({
|
|
14
|
+
name: column.name,
|
|
15
|
+
type: column.type,
|
|
16
|
+
})),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function getDimensionValueSql(field) {
|
|
20
|
+
return `COALESCE(CAST(${escapeId(field)} AS VARCHAR), ${escapeVal(NULL_LABEL)})`;
|
|
21
|
+
}
|
|
22
|
+
function buildFilterClause(config) {
|
|
23
|
+
const predicates = Object.entries(config.valueFilter)
|
|
24
|
+
.filter(([, values]) => Object.keys(values).length > 0)
|
|
25
|
+
.map(([field, values]) => {
|
|
26
|
+
const excludedValues = Object.keys(values);
|
|
27
|
+
const valueExpression = getDimensionValueSql(field);
|
|
28
|
+
return `${valueExpression} NOT IN (${excludedValues
|
|
29
|
+
.map((value) => escapeVal(value))
|
|
30
|
+
.join(', ')})`;
|
|
31
|
+
});
|
|
32
|
+
if (predicates.length === 0) {
|
|
33
|
+
return '';
|
|
34
|
+
}
|
|
35
|
+
return `WHERE ${predicates.join(' AND ')}`;
|
|
36
|
+
}
|
|
37
|
+
function buildDimensionMetadata(fields, prefix) {
|
|
38
|
+
return fields.map((field, index) => ({
|
|
39
|
+
field,
|
|
40
|
+
alias: `${prefix}_${index}`,
|
|
41
|
+
expression: getDimensionValueSql(field),
|
|
42
|
+
}));
|
|
43
|
+
}
|
|
44
|
+
function buildDimensionSelects(metadata) {
|
|
45
|
+
return metadata.map((item) => `${item.expression} AS ${escapeId(item.alias)}`);
|
|
46
|
+
}
|
|
47
|
+
function buildDimensionGroupBy(metadata) {
|
|
48
|
+
return metadata.map((item) => item.expression);
|
|
49
|
+
}
|
|
50
|
+
function buildConcatLabelSql(metadata) {
|
|
51
|
+
if (metadata.length === 0) {
|
|
52
|
+
return `${escapeVal('')}`;
|
|
53
|
+
}
|
|
54
|
+
return `CONCAT_WS(' / ', ${metadata.map((item) => item.expression).join(', ')})`;
|
|
55
|
+
}
|
|
56
|
+
function buildBaseAggregateSql(config) {
|
|
57
|
+
const aggregator = getPivotAggregator(config.aggregatorName);
|
|
58
|
+
return aggregator.buildSql(config.vals.slice(0, aggregator.numInputs));
|
|
59
|
+
}
|
|
60
|
+
function buildOrderByAliases(metadata, sortOrder) {
|
|
61
|
+
if (sortOrder !== 'key_a_to_z') {
|
|
62
|
+
return [];
|
|
63
|
+
}
|
|
64
|
+
return metadata.map((item) => escapeId(item.alias));
|
|
65
|
+
}
|
|
66
|
+
export function buildCellsQuery(config, source) {
|
|
67
|
+
const aggregator = getPivotAggregator(config.aggregatorName);
|
|
68
|
+
const tableRef = getTableReference(source);
|
|
69
|
+
const rowMetadata = buildDimensionMetadata(config.rows, 'row');
|
|
70
|
+
const colMetadata = buildDimensionMetadata(config.cols, 'col');
|
|
71
|
+
const allMetadata = [...rowMetadata, ...colMetadata];
|
|
72
|
+
const filterClause = buildFilterClause(config);
|
|
73
|
+
const baseAggregateSql = buildBaseAggregateSql(config);
|
|
74
|
+
const groupBy = buildDimensionGroupBy(allMetadata);
|
|
75
|
+
const orderBy = [
|
|
76
|
+
...buildOrderByAliases(rowMetadata, config.rowOrder),
|
|
77
|
+
...buildOrderByAliases(colMetadata, config.colOrder),
|
|
78
|
+
];
|
|
79
|
+
const selectList = [
|
|
80
|
+
...buildDimensionSelects(rowMetadata),
|
|
81
|
+
...buildDimensionSelects(colMetadata),
|
|
82
|
+
`${buildConcatLabelSql(rowMetadata)} AS ${escapeId('row_label')}`,
|
|
83
|
+
`${buildConcatLabelSql(colMetadata)} AS ${escapeId('col_label')}`,
|
|
84
|
+
];
|
|
85
|
+
if (aggregator.kind === 'default') {
|
|
86
|
+
return `WITH filtered AS (
|
|
87
|
+
SELECT *
|
|
88
|
+
FROM ${tableRef}
|
|
89
|
+
${filterClause}
|
|
90
|
+
)
|
|
91
|
+
SELECT
|
|
92
|
+
${[...selectList, `${baseAggregateSql} AS ${escapeId('value')}`].join(',\n ')}
|
|
93
|
+
FROM filtered
|
|
94
|
+
${groupBy.length > 0 ? `GROUP BY ${groupBy.join(', ')}` : ''}
|
|
95
|
+
${orderBy.length > 0 ? `ORDER BY ${orderBy.join(', ')}` : ''}`;
|
|
96
|
+
}
|
|
97
|
+
const partitionMetadata = aggregator.kind === 'fraction_row'
|
|
98
|
+
? rowMetadata
|
|
99
|
+
: aggregator.kind === 'fraction_col'
|
|
100
|
+
? colMetadata
|
|
101
|
+
: [];
|
|
102
|
+
const partitionBy = partitionMetadata.length > 0
|
|
103
|
+
? `PARTITION BY ${partitionMetadata
|
|
104
|
+
.map((item) => escapeId(item.alias))
|
|
105
|
+
.join(', ')}`
|
|
106
|
+
: '';
|
|
107
|
+
return `WITH filtered AS (
|
|
108
|
+
SELECT *
|
|
109
|
+
FROM ${tableRef}
|
|
110
|
+
${filterClause}
|
|
111
|
+
),
|
|
112
|
+
grouped AS (
|
|
113
|
+
SELECT
|
|
114
|
+
${[...selectList, `${baseAggregateSql} AS ${escapeId('base_value')}`].join(',\n ')}
|
|
115
|
+
FROM filtered
|
|
116
|
+
${groupBy.length > 0 ? `GROUP BY ${groupBy.join(', ')}` : ''}
|
|
117
|
+
)
|
|
118
|
+
SELECT
|
|
119
|
+
${[
|
|
120
|
+
...rowMetadata.map((item) => escapeId(item.alias)),
|
|
121
|
+
...colMetadata.map((item) => escapeId(item.alias)),
|
|
122
|
+
escapeId('row_label'),
|
|
123
|
+
escapeId('col_label'),
|
|
124
|
+
`${escapeId('base_value')} / NULLIF(SUM(${escapeId('base_value')}) OVER (${partitionBy}), 0) AS ${escapeId('value')}`,
|
|
125
|
+
].join(',\n ')}
|
|
126
|
+
FROM grouped
|
|
127
|
+
${orderBy.length > 0 ? `ORDER BY ${orderBy.join(', ')}` : ''}`;
|
|
128
|
+
}
|
|
129
|
+
function buildAxisTotalsQuery(config, source, axis) {
|
|
130
|
+
const aggregator = getPivotAggregator(config.aggregatorName);
|
|
131
|
+
const tableRef = getTableReference(source);
|
|
132
|
+
const rowMetadata = buildDimensionMetadata(config.rows, 'row');
|
|
133
|
+
const colMetadata = buildDimensionMetadata(config.cols, 'col');
|
|
134
|
+
const metadata = axis === 'row'
|
|
135
|
+
? rowMetadata
|
|
136
|
+
: axis === 'col'
|
|
137
|
+
? colMetadata
|
|
138
|
+
: [];
|
|
139
|
+
const filterClause = buildFilterClause(config);
|
|
140
|
+
const baseAggregateSql = buildBaseAggregateSql(config);
|
|
141
|
+
if (axis === 'grand') {
|
|
142
|
+
if (aggregator.kind === 'default') {
|
|
143
|
+
return `SELECT ${baseAggregateSql} AS ${escapeId('value')}
|
|
144
|
+
FROM ${tableRef}
|
|
145
|
+
${filterClause}`;
|
|
146
|
+
}
|
|
147
|
+
return `SELECT 1.0 AS ${escapeId('value')}`;
|
|
148
|
+
}
|
|
149
|
+
const axisSelects = [
|
|
150
|
+
...buildDimensionSelects(metadata),
|
|
151
|
+
`${buildConcatLabelSql(metadata)} AS ${escapeId(`${axis}_label`)}`,
|
|
152
|
+
];
|
|
153
|
+
const groupBy = buildDimensionGroupBy(metadata);
|
|
154
|
+
const orderBy = buildOrderByAliases(metadata, axis === 'row' ? config.rowOrder : config.colOrder);
|
|
155
|
+
if (aggregator.kind === 'default') {
|
|
156
|
+
return `SELECT
|
|
157
|
+
${[...axisSelects, `${baseAggregateSql} AS ${escapeId('value')}`].join(',\n ')}
|
|
158
|
+
FROM ${tableRef}
|
|
159
|
+
${filterClause}
|
|
160
|
+
${groupBy.length > 0 ? `GROUP BY ${groupBy.join(', ')}` : ''}
|
|
161
|
+
${orderBy.length > 0 ? `ORDER BY ${orderBy.join(', ')}` : ''}`;
|
|
162
|
+
}
|
|
163
|
+
if ((aggregator.kind === 'fraction_row' && axis === 'row') ||
|
|
164
|
+
(aggregator.kind === 'fraction_col' && axis === 'col')) {
|
|
165
|
+
return `SELECT
|
|
166
|
+
${[...axisSelects, `1.0 AS ${escapeId('value')}`].join(',\n ')}
|
|
167
|
+
FROM ${tableRef}
|
|
168
|
+
${filterClause}
|
|
169
|
+
${groupBy.length > 0 ? `GROUP BY ${groupBy.join(', ')}` : ''}
|
|
170
|
+
${orderBy.length > 0 ? `ORDER BY ${orderBy.join(', ')}` : ''}`;
|
|
171
|
+
}
|
|
172
|
+
return `WITH totals AS (
|
|
173
|
+
SELECT
|
|
174
|
+
${[...axisSelects, `${baseAggregateSql} AS ${escapeId('base_value')}`].join(',\n ')}
|
|
175
|
+
FROM ${tableRef}
|
|
176
|
+
${filterClause}
|
|
177
|
+
${groupBy.length > 0 ? `GROUP BY ${groupBy.join(', ')}` : ''}
|
|
178
|
+
),
|
|
179
|
+
grand_total AS (
|
|
180
|
+
SELECT ${baseAggregateSql} AS ${escapeId('grand_value')}
|
|
181
|
+
FROM ${tableRef}
|
|
182
|
+
${filterClause}
|
|
183
|
+
)
|
|
184
|
+
SELECT
|
|
185
|
+
${[
|
|
186
|
+
...metadata.map((item) => escapeId(item.alias)),
|
|
187
|
+
escapeId(`${axis}_label`),
|
|
188
|
+
`${escapeId('base_value')} / NULLIF(${escapeId('grand_value')}, 0) AS ${escapeId('value')}`,
|
|
189
|
+
].join(',\n ')}
|
|
190
|
+
FROM totals
|
|
191
|
+
CROSS JOIN grand_total
|
|
192
|
+
${orderBy.length > 0 ? `ORDER BY ${orderBy.join(', ')}` : ''}`;
|
|
193
|
+
}
|
|
194
|
+
export function buildRowTotalsQuery(config, source) {
|
|
195
|
+
return buildAxisTotalsQuery(config, source, 'row');
|
|
196
|
+
}
|
|
197
|
+
export function buildColTotalsQuery(config, source) {
|
|
198
|
+
return buildAxisTotalsQuery(config, source, 'col');
|
|
199
|
+
}
|
|
200
|
+
export function buildGrandTotalQuery(config, source) {
|
|
201
|
+
return buildAxisTotalsQuery(config, source, 'grand');
|
|
202
|
+
}
|
|
203
|
+
export function buildDistinctValuesQuery(source, attribute, menuLimit) {
|
|
204
|
+
const tableRef = getTableReference(source);
|
|
205
|
+
const valueExpression = getDimensionValueSql(attribute);
|
|
206
|
+
return `SELECT
|
|
207
|
+
${valueExpression} AS ${escapeId('value')},
|
|
208
|
+
COUNT(*) AS ${escapeId('count')}
|
|
209
|
+
FROM ${tableRef}
|
|
210
|
+
GROUP BY 1
|
|
211
|
+
ORDER BY 1
|
|
212
|
+
LIMIT ${menuLimit + 1}`;
|
|
213
|
+
}
|
|
214
|
+
export function buildPivotExportQuery(config, source, colKeys) {
|
|
215
|
+
const rowMetadata = buildDimensionMetadata(config.rows, 'row');
|
|
216
|
+
const cellsQuery = buildCellsQuery(config, source);
|
|
217
|
+
const orderBy = buildOrderByAliases(rowMetadata, config.rowOrder);
|
|
218
|
+
if (colKeys.length === 0) {
|
|
219
|
+
return `WITH cells AS (
|
|
220
|
+
${indentSql(cellsQuery)}
|
|
221
|
+
)
|
|
222
|
+
SELECT
|
|
223
|
+
${[
|
|
224
|
+
...rowMetadata.map((item) => escapeId(item.alias)),
|
|
225
|
+
`${escapeId('value')} AS ${escapeId(getAggregatorLabel(config.aggregatorName, config.vals))}`,
|
|
226
|
+
].join(',\n ')}
|
|
227
|
+
FROM cells`;
|
|
228
|
+
}
|
|
229
|
+
return `WITH cells AS (
|
|
230
|
+
${indentSql(cellsQuery)}
|
|
231
|
+
)
|
|
232
|
+
SELECT *
|
|
233
|
+
FROM (
|
|
234
|
+
SELECT
|
|
235
|
+
${[
|
|
236
|
+
...rowMetadata.map((item) => escapeId(item.alias)),
|
|
237
|
+
escapeId('col_label'),
|
|
238
|
+
escapeId('value'),
|
|
239
|
+
].join(',\n ')}
|
|
240
|
+
FROM cells
|
|
241
|
+
)
|
|
242
|
+
PIVOT(
|
|
243
|
+
MAX(${escapeId('value')})
|
|
244
|
+
FOR ${escapeId('col_label')} IN (${colKeys.map((key) => escapeVal(key)).join(', ')})
|
|
245
|
+
)
|
|
246
|
+
${orderBy.length > 0 ? `ORDER BY ${orderBy.join(', ')}` : ''}`;
|
|
247
|
+
}
|
|
248
|
+
export function buildRendererTitle(config, transpose = false) {
|
|
249
|
+
const fullAggName = getAggregatorLabel(config.aggregatorName, config.vals);
|
|
250
|
+
const axisTitle = transpose ? config.rows.join(', ') : config.cols.join(', ');
|
|
251
|
+
const groupByTitle = transpose
|
|
252
|
+
? config.cols.join(', ')
|
|
253
|
+
: config.rows.join(', ');
|
|
254
|
+
let title = fullAggName;
|
|
255
|
+
if (axisTitle) {
|
|
256
|
+
title += ` vs ${axisTitle}`;
|
|
257
|
+
}
|
|
258
|
+
if (groupByTitle) {
|
|
259
|
+
title += ` by ${groupByTitle}`;
|
|
260
|
+
}
|
|
261
|
+
return title;
|
|
262
|
+
}
|
|
263
|
+
function indentSql(sql) {
|
|
264
|
+
return sql
|
|
265
|
+
.split('\n')
|
|
266
|
+
.map((line) => ` ${line}`)
|
|
267
|
+
.join('\n');
|
|
268
|
+
}
|
|
269
|
+
export function getRowAlias(index) {
|
|
270
|
+
return `row_${index}`;
|
|
271
|
+
}
|
|
272
|
+
export function getColAlias(index) {
|
|
273
|
+
return `col_${index}`;
|
|
274
|
+
}
|
|
275
|
+
export function getNullLabel() {
|
|
276
|
+
return NULL_LABEL;
|
|
277
|
+
}
|
|
278
|
+
//# sourceMappingURL=sql.js.map
|
package/dist/sql.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sql.js","sourceRoot":"","sources":["../src/sql.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,QAAQ,EAAE,SAAS,EAAC,MAAM,kBAAkB,CAAC;AAChE,OAAO,EAAC,kBAAkB,EAAE,kBAAkB,EAAC,MAAM,eAAe,CAAC;AAQrE,MAAM,UAAU,GAAG,MAAM,CAAC;AAI1B,SAAS,iBAAiB,CAAC,MAAuB;IAChD,OAAO,UAAU,IAAI,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;AAC1E,CAAC;AAED,MAAM,UAAU,sBAAsB,CACpC,QAAgB,EAChB,OAAqB;IAErB,OAAO,EAAC,QAAQ,EAAE,OAAO,EAAC,CAAC;AAC7B,CAAC;AAED,MAAM,UAAU,+BAA+B,CAC7C,KAAgB;IAEhB,OAAO;QACL,QAAQ,EAAE,KAAK,CAAC,KAAK,CAAC,QAAQ,EAAE;QAChC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;YACtC,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,IAAI,EAAE,MAAM,CAAC,IAAI;SAClB,CAAC,CAAC;KACJ,CAAC;AACJ,CAAC;AAED,SAAS,oBAAoB,CAAC,KAAa;IACzC,OAAO,iBAAiB,QAAQ,CAAC,KAAK,CAAC,iBAAiB,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC;AACnF,CAAC;AAED,SAAS,iBAAiB,CAAC,MAAmB;IAC5C,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,WAA+B,CAAC;SACtE,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;SACtD,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,EAAE;QACvB,MAAM,cAAc,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC3C,MAAM,eAAe,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;QACpD,OAAO,GAAG,eAAe,YAAY,cAAc;aAChD,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;aAChC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;IACnB,CAAC,CAAC,CAAC;IAEL,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,OAAO,SAAS,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;AAC7C,CAAC;AAED,SAAS,sBAAsB,CAAC,MAAgB,EAAE,MAAqB;IACrE,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QACnC,KAAK;QACL,KAAK,EAAE,GAAG,MAAM,IAAI,KAAK,EAAE;QAC3B,UAAU,EAAE,oBAAoB,CAAC,KAAK,CAAC;KACxC,CAAC,CAAC,CAAC;AACN,CAAC;AAED,SAAS,qBAAqB,CAC5B,QAAmD;IAEnD,OAAO,QAAQ,CAAC,GAAG,CACjB,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,UAAU,OAAO,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAC1D,CAAC;AACJ,CAAC;AAED,SAAS,qBAAqB,CAC5B,QAAmD;IAEnD,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;AACjD,CAAC;AAED,SAAS,mBAAmB,CAC1B,QAAmD;IAEnD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,GAAG,SAAS,CAAC,EAAE,CAAC,EAAE,CAAC;IAC5B,CAAC;IACD,OAAO,oBAAoB,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;AACnF,CAAC;AAED,SAAS,qBAAqB,CAAC,MAAmB;IAChD,MAAM,UAAU,GAAG,kBAAkB,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;IAC7D,OAAO,UAAU,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC;AACzE,CAAC;AAED,SAAS,mBAAmB,CAC1B,QAAmD,EACnD,SAAkC;IAElC,IAAI,SAAS,KAAK,YAAY,EAAE,CAAC;QAC/B,OAAO,EAAc,CAAC;IACxB,CAAC;IACD,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;AACtD,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,MAAmB,EAAE,MAAuB;IAC1E,MAAM,UAAU,GAAG,kBAAkB,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;IAC7D,MAAM,QAAQ,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAC3C,MAAM,WAAW,GAAG,sBAAsB,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC/D,MAAM,WAAW,GAAG,sBAAsB,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC/D,MAAM,WAAW,GAAG,CAAC,GAAG,WAAW,EAAE,GAAG,WAAW,CAAC,CAAC;IACrD,MAAM,YAAY,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAC/C,MAAM,gBAAgB,GAAG,qBAAqB,CAAC,MAAM,CAAC,CAAC;IACvD,MAAM,OAAO,GAAG,qBAAqB,CAAC,WAAW,CAAC,CAAC;IACnD,MAAM,OAAO,GAAG;QACd,GAAG,mBAAmB,CAAC,WAAW,EAAE,MAAM,CAAC,QAAQ,CAAC;QACpD,GAAG,mBAAmB,CAAC,WAAW,EAAE,MAAM,CAAC,QAAQ,CAAC;KACrD,CAAC;IACF,MAAM,UAAU,GAAG;QACjB,GAAG,qBAAqB,CAAC,WAAW,CAAC;QACrC,GAAG,qBAAqB,CAAC,WAAW,CAAC;QACrC,GAAG,mBAAmB,CAAC,WAAW,CAAC,OAAO,QAAQ,CAAC,WAAW,CAAC,EAAE;QACjE,GAAG,mBAAmB,CAAC,WAAW,CAAC,OAAO,QAAQ,CAAC,WAAW,CAAC,EAAE;KAClE,CAAC;IAEF,IAAI,UAAU,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAClC,OAAO;;SAEF,QAAQ;IACb,YAAY;;;IAGZ,CAAC,GAAG,UAAU,EAAE,GAAG,gBAAgB,OAAO,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC;;EAE9E,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE;EAC1D,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;IAC7D,CAAC;IAED,MAAM,iBAAiB,GACrB,UAAU,CAAC,IAAI,KAAK,cAAc;QAChC,CAAC,CAAC,WAAW;QACb,CAAC,CAAC,UAAU,CAAC,IAAI,KAAK,cAAc;YAClC,CAAC,CAAC,WAAW;YACb,CAAC,CAAC,EAAE,CAAC;IAEX,MAAM,WAAW,GACf,iBAAiB,CAAC,MAAM,GAAG,CAAC;QAC1B,CAAC,CAAC,gBAAgB,iBAAiB;aAC9B,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;aACnC,IAAI,CAAC,IAAI,CAAC,EAAE;QACjB,CAAC,CAAC,EAAE,CAAC;IAET,OAAO;;SAEA,QAAQ;IACb,YAAY;;;;MAIV,CAAC,GAAG,UAAU,EAAE,GAAG,gBAAgB,OAAO,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC;;IAErF,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE;;;IAG1D;QACA,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAClD,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAClD,QAAQ,CAAC,WAAW,CAAC;QACrB,QAAQ,CAAC,WAAW,CAAC;QACrB,GAAG,QAAQ,CAAC,YAAY,CAAC,iBAAiB,QAAQ,CAAC,YAAY,CAAC,WAAW,WAAW,YAAY,QAAQ,CAAC,OAAO,CAAC,EAAE;KACtH,CAAC,IAAI,CAAC,OAAO,CAAC;;EAEf,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;AAC/D,CAAC;AAED,SAAS,oBAAoB,CAC3B,MAAmB,EACnB,MAAuB,EACvB,IAA6B;IAE7B,MAAM,UAAU,GAAG,kBAAkB,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;IAC7D,MAAM,QAAQ,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAC3C,MAAM,WAAW,GAAG,sBAAsB,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC/D,MAAM,WAAW,GAAG,sBAAsB,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC/D,MAAM,QAAQ,GACZ,IAAI,KAAK,KAAK;QACZ,CAAC,CAAC,WAAW;QACb,CAAC,CAAC,IAAI,KAAK,KAAK;YACd,CAAC,CAAC,WAAW;YACb,CAAC,CAAE,EAAyB,CAAC;IACnC,MAAM,YAAY,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAC/C,MAAM,gBAAgB,GAAG,qBAAqB,CAAC,MAAM,CAAC,CAAC;IAEvD,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;QACrB,IAAI,UAAU,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAClC,OAAO,UAAU,gBAAgB,OAAO,QAAQ,CAAC,OAAO,CAAC;OACxD,QAAQ;EACb,YAAY,EAAE,CAAC;QACb,CAAC;QACD,OAAO,iBAAiB,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;IAC9C,CAAC;IAED,MAAM,WAAW,GAAG;QAClB,GAAG,qBAAqB,CAAC,QAAQ,CAAC;QAClC,GAAG,mBAAmB,CAAC,QAAQ,CAAC,OAAO,QAAQ,CAAC,GAAG,IAAI,QAAQ,CAAC,EAAE;KACnE,CAAC;IACF,MAAM,OAAO,GAAG,qBAAqB,CAAC,QAAQ,CAAC,CAAC;IAChD,MAAM,OAAO,GAAG,mBAAmB,CACjC,QAAQ,EACR,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CACnD,CAAC;IAEF,IAAI,UAAU,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAClC,OAAO;IACP,CAAC,GAAG,WAAW,EAAE,GAAG,gBAAgB,OAAO,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC;OAC1E,QAAQ;EACb,YAAY;EACZ,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE;EAC1D,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;IAC7D,CAAC;IAED,IACE,CAAC,UAAU,CAAC,IAAI,KAAK,cAAc,IAAI,IAAI,KAAK,KAAK,CAAC;QACtD,CAAC,UAAU,CAAC,IAAI,KAAK,cAAc,IAAI,IAAI,KAAK,KAAK,CAAC,EACtD,CAAC;QACD,OAAO;IACP,CAAC,GAAG,WAAW,EAAE,UAAU,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC;OAC1D,QAAQ;EACb,YAAY;EACZ,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE;EAC1D,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;IAC7D,CAAC;IAED,OAAO;;MAEH,CAAC,GAAG,WAAW,EAAE,GAAG,gBAAgB,OAAO,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC;SACjF,QAAQ;IACb,YAAY;IACZ,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE;;;WAGnD,gBAAgB,OAAO,QAAQ,CAAC,aAAa,CAAC;SAChD,QAAQ;IACb,YAAY;;;IAGZ;QACA,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC/C,QAAQ,CAAC,GAAG,IAAI,QAAQ,CAAC;QACzB,GAAG,QAAQ,CAAC,YAAY,CAAC,aAAa,QAAQ,CAAC,aAAa,CAAC,WAAW,QAAQ,CAAC,OAAO,CAAC,EAAE;KAC5F,CAAC,IAAI,CAAC,OAAO,CAAC;;;EAGf,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;AAC/D,CAAC;AAED,MAAM,UAAU,mBAAmB,CACjC,MAAmB,EACnB,MAAuB;IAEvB,OAAO,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;AACrD,CAAC;AAED,MAAM,UAAU,mBAAmB,CACjC,MAAmB,EACnB,MAAuB;IAEvB,OAAO,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;AACrD,CAAC;AAED,MAAM,UAAU,oBAAoB,CAClC,MAAmB,EACnB,MAAuB;IAEvB,OAAO,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;AACvD,CAAC;AAED,MAAM,UAAU,wBAAwB,CACtC,MAAuB,EACvB,SAAiB,EACjB,SAAiB;IAEjB,MAAM,QAAQ,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAC3C,MAAM,eAAe,GAAG,oBAAoB,CAAC,SAAS,CAAC,CAAC;IACxD,OAAO;IACL,eAAe,OAAO,QAAQ,CAAC,OAAO,CAAC;gBAC3B,QAAQ,CAAC,OAAO,CAAC;OAC1B,QAAQ;;;QAGP,SAAS,GAAG,CAAC,EAAE,CAAC;AACxB,CAAC;AAED,MAAM,UAAU,qBAAqB,CACnC,MAAmB,EACnB,MAAuB,EACvB,OAAiB;IAEjB,MAAM,WAAW,GAAG,sBAAsB,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC/D,MAAM,UAAU,GAAG,eAAe,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnD,MAAM,OAAO,GAAG,mBAAmB,CAAC,WAAW,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;IAElE,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO;EACT,SAAS,CAAC,UAAU,CAAC;;;IAGnB;YACA,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAClD,GAAG,QAAQ,CAAC,OAAO,CAAC,OAAO,QAAQ,CAAC,kBAAkB,CAAC,MAAM,CAAC,cAAc,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE;SAC9F,CAAC,IAAI,CAAC,OAAO,CAAC;WACN,CAAC;IACV,CAAC;IAED,OAAO;EACP,SAAS,CAAC,UAAU,CAAC;;;;;MAKjB;QACA,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAClD,QAAQ,CAAC,WAAW,CAAC;QACrB,QAAQ,CAAC,OAAO,CAAC;KAClB,CAAC,IAAI,CAAC,SAAS,CAAC;;;;QAIb,QAAQ,CAAC,OAAO,CAAC;QACjB,QAAQ,CAAC,WAAW,CAAC,QAAQ,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;;EAElF,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;AAC/D,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,MAAmB,EAAE,SAAS,GAAG,KAAK;IACvE,MAAM,WAAW,GAAG,kBAAkB,CAAC,MAAM,CAAC,cAAc,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;IAC3E,MAAM,SAAS,GAAG,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC9E,MAAM,YAAY,GAAG,SAAS;QAC5B,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;QACxB,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3B,IAAI,KAAK,GAAG,WAAW,CAAC;IACxB,IAAI,SAAS,EAAE,CAAC;QACd,KAAK,IAAI,OAAO,SAAS,EAAE,CAAC;IAC9B,CAAC;IACD,IAAI,YAAY,EAAE,CAAC;QACjB,KAAK,IAAI,OAAO,YAAY,EAAE,CAAC;IACjC,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,SAAS,CAAC,GAAW;IAC5B,OAAO,GAAG;SACP,KAAK,CAAC,IAAI,CAAC;SACX,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC;SAC1B,IAAI,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,OAAO,OAAO,KAAK,EAAE,CAAC;AACxB,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,OAAO,OAAO,KAAK,EAAE,CAAC;AACxB,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,OAAO,UAAU,CAAC;AACpB,CAAC","sourcesContent":["import {DataTable, escapeId, escapeVal} from '@sqlrooms/duckdb';\nimport {getAggregatorLabel, getPivotAggregator} from './aggregators';\nimport {\n PivotConfig,\n PivotField,\n PivotQuerySource,\n PivotValueFilter,\n} from './types';\n\nconst NULL_LABEL = 'null';\n\ntype PivotQueryInput = DataTable | PivotQuerySource;\n\nfunction getTableReference(source: PivotQueryInput) {\n return 'tableRef' in source ? source.tableRef : source.table.toString();\n}\n\nexport function createPivotQuerySource(\n tableRef: string,\n columns: PivotField[],\n): PivotQuerySource {\n return {tableRef, columns};\n}\n\nexport function createPivotQuerySourceFromTable(\n table: DataTable,\n): PivotQuerySource {\n return {\n tableRef: table.table.toString(),\n columns: table.columns.map((column) => ({\n name: column.name,\n type: column.type,\n })),\n };\n}\n\nfunction getDimensionValueSql(field: string) {\n return `COALESCE(CAST(${escapeId(field)} AS VARCHAR), ${escapeVal(NULL_LABEL)})`;\n}\n\nfunction buildFilterClause(config: PivotConfig) {\n const predicates = Object.entries(config.valueFilter as PivotValueFilter)\n .filter(([, values]) => Object.keys(values).length > 0)\n .map(([field, values]) => {\n const excludedValues = Object.keys(values);\n const valueExpression = getDimensionValueSql(field);\n return `${valueExpression} NOT IN (${excludedValues\n .map((value) => escapeVal(value))\n .join(', ')})`;\n });\n\n if (predicates.length === 0) {\n return '';\n }\n\n return `WHERE ${predicates.join(' AND ')}`;\n}\n\nfunction buildDimensionMetadata(fields: string[], prefix: 'row' | 'col') {\n return fields.map((field, index) => ({\n field,\n alias: `${prefix}_${index}`,\n expression: getDimensionValueSql(field),\n }));\n}\n\nfunction buildDimensionSelects(\n metadata: ReturnType<typeof buildDimensionMetadata>,\n) {\n return metadata.map(\n (item) => `${item.expression} AS ${escapeId(item.alias)}`,\n );\n}\n\nfunction buildDimensionGroupBy(\n metadata: ReturnType<typeof buildDimensionMetadata>,\n) {\n return metadata.map((item) => item.expression);\n}\n\nfunction buildConcatLabelSql(\n metadata: ReturnType<typeof buildDimensionMetadata>,\n) {\n if (metadata.length === 0) {\n return `${escapeVal('')}`;\n }\n return `CONCAT_WS(' / ', ${metadata.map((item) => item.expression).join(', ')})`;\n}\n\nfunction buildBaseAggregateSql(config: PivotConfig) {\n const aggregator = getPivotAggregator(config.aggregatorName);\n return aggregator.buildSql(config.vals.slice(0, aggregator.numInputs));\n}\n\nfunction buildOrderByAliases(\n metadata: ReturnType<typeof buildDimensionMetadata>,\n sortOrder: PivotConfig['rowOrder'],\n) {\n if (sortOrder !== 'key_a_to_z') {\n return [] as string[];\n }\n return metadata.map((item) => escapeId(item.alias));\n}\n\nexport function buildCellsQuery(config: PivotConfig, source: PivotQueryInput) {\n const aggregator = getPivotAggregator(config.aggregatorName);\n const tableRef = getTableReference(source);\n const rowMetadata = buildDimensionMetadata(config.rows, 'row');\n const colMetadata = buildDimensionMetadata(config.cols, 'col');\n const allMetadata = [...rowMetadata, ...colMetadata];\n const filterClause = buildFilterClause(config);\n const baseAggregateSql = buildBaseAggregateSql(config);\n const groupBy = buildDimensionGroupBy(allMetadata);\n const orderBy = [\n ...buildOrderByAliases(rowMetadata, config.rowOrder),\n ...buildOrderByAliases(colMetadata, config.colOrder),\n ];\n const selectList = [\n ...buildDimensionSelects(rowMetadata),\n ...buildDimensionSelects(colMetadata),\n `${buildConcatLabelSql(rowMetadata)} AS ${escapeId('row_label')}`,\n `${buildConcatLabelSql(colMetadata)} AS ${escapeId('col_label')}`,\n ];\n\n if (aggregator.kind === 'default') {\n return `WITH filtered AS (\n SELECT *\n FROM ${tableRef}\n ${filterClause}\n)\nSELECT\n ${[...selectList, `${baseAggregateSql} AS ${escapeId('value')}`].join(',\\n ')}\nFROM filtered\n${groupBy.length > 0 ? `GROUP BY ${groupBy.join(', ')}` : ''}\n${orderBy.length > 0 ? `ORDER BY ${orderBy.join(', ')}` : ''}`;\n }\n\n const partitionMetadata =\n aggregator.kind === 'fraction_row'\n ? rowMetadata\n : aggregator.kind === 'fraction_col'\n ? colMetadata\n : [];\n\n const partitionBy =\n partitionMetadata.length > 0\n ? `PARTITION BY ${partitionMetadata\n .map((item) => escapeId(item.alias))\n .join(', ')}`\n : '';\n\n return `WITH filtered AS (\n SELECT *\n FROM ${tableRef}\n ${filterClause}\n),\ngrouped AS (\n SELECT\n ${[...selectList, `${baseAggregateSql} AS ${escapeId('base_value')}`].join(',\\n ')}\n FROM filtered\n ${groupBy.length > 0 ? `GROUP BY ${groupBy.join(', ')}` : ''}\n)\nSELECT\n ${[\n ...rowMetadata.map((item) => escapeId(item.alias)),\n ...colMetadata.map((item) => escapeId(item.alias)),\n escapeId('row_label'),\n escapeId('col_label'),\n `${escapeId('base_value')} / NULLIF(SUM(${escapeId('base_value')}) OVER (${partitionBy}), 0) AS ${escapeId('value')}`,\n ].join(',\\n ')}\nFROM grouped\n${orderBy.length > 0 ? `ORDER BY ${orderBy.join(', ')}` : ''}`;\n}\n\nfunction buildAxisTotalsQuery(\n config: PivotConfig,\n source: PivotQueryInput,\n axis: 'row' | 'col' | 'grand',\n) {\n const aggregator = getPivotAggregator(config.aggregatorName);\n const tableRef = getTableReference(source);\n const rowMetadata = buildDimensionMetadata(config.rows, 'row');\n const colMetadata = buildDimensionMetadata(config.cols, 'col');\n const metadata =\n axis === 'row'\n ? rowMetadata\n : axis === 'col'\n ? colMetadata\n : ([] as typeof rowMetadata);\n const filterClause = buildFilterClause(config);\n const baseAggregateSql = buildBaseAggregateSql(config);\n\n if (axis === 'grand') {\n if (aggregator.kind === 'default') {\n return `SELECT ${baseAggregateSql} AS ${escapeId('value')}\nFROM ${tableRef}\n${filterClause}`;\n }\n return `SELECT 1.0 AS ${escapeId('value')}`;\n }\n\n const axisSelects = [\n ...buildDimensionSelects(metadata),\n `${buildConcatLabelSql(metadata)} AS ${escapeId(`${axis}_label`)}`,\n ];\n const groupBy = buildDimensionGroupBy(metadata);\n const orderBy = buildOrderByAliases(\n metadata,\n axis === 'row' ? config.rowOrder : config.colOrder,\n );\n\n if (aggregator.kind === 'default') {\n return `SELECT\n ${[...axisSelects, `${baseAggregateSql} AS ${escapeId('value')}`].join(',\\n ')}\nFROM ${tableRef}\n${filterClause}\n${groupBy.length > 0 ? `GROUP BY ${groupBy.join(', ')}` : ''}\n${orderBy.length > 0 ? `ORDER BY ${orderBy.join(', ')}` : ''}`;\n }\n\n if (\n (aggregator.kind === 'fraction_row' && axis === 'row') ||\n (aggregator.kind === 'fraction_col' && axis === 'col')\n ) {\n return `SELECT\n ${[...axisSelects, `1.0 AS ${escapeId('value')}`].join(',\\n ')}\nFROM ${tableRef}\n${filterClause}\n${groupBy.length > 0 ? `GROUP BY ${groupBy.join(', ')}` : ''}\n${orderBy.length > 0 ? `ORDER BY ${orderBy.join(', ')}` : ''}`;\n }\n\n return `WITH totals AS (\n SELECT\n ${[...axisSelects, `${baseAggregateSql} AS ${escapeId('base_value')}`].join(',\\n ')}\n FROM ${tableRef}\n ${filterClause}\n ${groupBy.length > 0 ? `GROUP BY ${groupBy.join(', ')}` : ''}\n),\ngrand_total AS (\n SELECT ${baseAggregateSql} AS ${escapeId('grand_value')}\n FROM ${tableRef}\n ${filterClause}\n)\nSELECT\n ${[\n ...metadata.map((item) => escapeId(item.alias)),\n escapeId(`${axis}_label`),\n `${escapeId('base_value')} / NULLIF(${escapeId('grand_value')}, 0) AS ${escapeId('value')}`,\n ].join(',\\n ')}\nFROM totals\nCROSS JOIN grand_total\n${orderBy.length > 0 ? `ORDER BY ${orderBy.join(', ')}` : ''}`;\n}\n\nexport function buildRowTotalsQuery(\n config: PivotConfig,\n source: PivotQueryInput,\n) {\n return buildAxisTotalsQuery(config, source, 'row');\n}\n\nexport function buildColTotalsQuery(\n config: PivotConfig,\n source: PivotQueryInput,\n) {\n return buildAxisTotalsQuery(config, source, 'col');\n}\n\nexport function buildGrandTotalQuery(\n config: PivotConfig,\n source: PivotQueryInput,\n) {\n return buildAxisTotalsQuery(config, source, 'grand');\n}\n\nexport function buildDistinctValuesQuery(\n source: PivotQueryInput,\n attribute: string,\n menuLimit: number,\n) {\n const tableRef = getTableReference(source);\n const valueExpression = getDimensionValueSql(attribute);\n return `SELECT\n ${valueExpression} AS ${escapeId('value')},\n COUNT(*) AS ${escapeId('count')}\nFROM ${tableRef}\nGROUP BY 1\nORDER BY 1\nLIMIT ${menuLimit + 1}`;\n}\n\nexport function buildPivotExportQuery(\n config: PivotConfig,\n source: PivotQueryInput,\n colKeys: string[],\n) {\n const rowMetadata = buildDimensionMetadata(config.rows, 'row');\n const cellsQuery = buildCellsQuery(config, source);\n const orderBy = buildOrderByAliases(rowMetadata, config.rowOrder);\n\n if (colKeys.length === 0) {\n return `WITH cells AS (\n${indentSql(cellsQuery)}\n)\nSELECT\n ${[\n ...rowMetadata.map((item) => escapeId(item.alias)),\n `${escapeId('value')} AS ${escapeId(getAggregatorLabel(config.aggregatorName, config.vals))}`,\n ].join(',\\n ')}\nFROM cells`;\n }\n\n return `WITH cells AS (\n${indentSql(cellsQuery)}\n)\nSELECT *\nFROM (\n SELECT\n ${[\n ...rowMetadata.map((item) => escapeId(item.alias)),\n escapeId('col_label'),\n escapeId('value'),\n ].join(',\\n ')}\n FROM cells\n)\nPIVOT(\n MAX(${escapeId('value')})\n FOR ${escapeId('col_label')} IN (${colKeys.map((key) => escapeVal(key)).join(', ')})\n)\n${orderBy.length > 0 ? `ORDER BY ${orderBy.join(', ')}` : ''}`;\n}\n\nexport function buildRendererTitle(config: PivotConfig, transpose = false) {\n const fullAggName = getAggregatorLabel(config.aggregatorName, config.vals);\n const axisTitle = transpose ? config.rows.join(', ') : config.cols.join(', ');\n const groupByTitle = transpose\n ? config.cols.join(', ')\n : config.rows.join(', ');\n let title = fullAggName;\n if (axisTitle) {\n title += ` vs ${axisTitle}`;\n }\n if (groupByTitle) {\n title += ` by ${groupByTitle}`;\n }\n return title;\n}\n\nfunction indentSql(sql: string) {\n return sql\n .split('\\n')\n .map((line) => ` ${line}`)\n .join('\\n');\n}\n\nexport function getRowAlias(index: number) {\n return `row_${index}`;\n}\n\nexport function getColAlias(index: number) {\n return `col_${index}`;\n}\n\nexport function getNullLabel() {\n return NULL_LABEL;\n}\n"]}
|