@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.
Files changed (66) hide show
  1. package/README.md +3 -0
  2. package/dist/PivotCellContent.d.ts +10 -0
  3. package/dist/PivotCellContent.d.ts.map +1 -0
  4. package/dist/PivotCellContent.js +37 -0
  5. package/dist/PivotCellContent.js.map +1 -0
  6. package/dist/PivotCoreSlice.d.ts +73 -0
  7. package/dist/PivotCoreSlice.d.ts.map +1 -0
  8. package/dist/PivotCoreSlice.js +175 -0
  9. package/dist/PivotCoreSlice.js.map +1 -0
  10. package/dist/PivotEditor.d.ts +66 -0
  11. package/dist/PivotEditor.d.ts.map +1 -0
  12. package/dist/PivotEditor.js +341 -0
  13. package/dist/PivotEditor.js.map +1 -0
  14. package/dist/PivotResults.d.ts +15 -0
  15. package/dist/PivotResults.d.ts.map +1 -0
  16. package/dist/PivotResults.js +102 -0
  17. package/dist/PivotResults.js.map +1 -0
  18. package/dist/PivotSlice.d.ts +7 -0
  19. package/dist/PivotSlice.d.ts.map +1 -0
  20. package/dist/PivotSlice.js +353 -0
  21. package/dist/PivotSlice.js.map +1 -0
  22. package/dist/PivotView.d.ts +3 -0
  23. package/dist/PivotView.d.ts.map +1 -0
  24. package/dist/PivotView.js +39 -0
  25. package/dist/PivotView.js.map +1 -0
  26. package/dist/TableRenderer.d.ts +14 -0
  27. package/dist/TableRenderer.d.ts.map +1 -0
  28. package/dist/TableRenderer.js +100 -0
  29. package/dist/TableRenderer.js.map +1 -0
  30. package/dist/TsvRenderer.d.ts +7 -0
  31. package/dist/TsvRenderer.d.ts.map +1 -0
  32. package/dist/TsvRenderer.js +26 -0
  33. package/dist/TsvRenderer.js.map +1 -0
  34. package/dist/aggregators.d.ts +24 -0
  35. package/dist/aggregators.d.ts.map +1 -0
  36. package/dist/aggregators.js +232 -0
  37. package/dist/aggregators.js.map +1 -0
  38. package/dist/helpers.d.ts +85 -0
  39. package/dist/helpers.d.ts.map +1 -0
  40. package/dist/helpers.js +348 -0
  41. package/dist/helpers.js.map +1 -0
  42. package/dist/index.d.ts +20 -0
  43. package/dist/index.d.ts.map +1 -0
  44. package/dist/index.js +17 -0
  45. package/dist/index.js.map +1 -0
  46. package/dist/pivotCellRegistryEntry.d.ts +4 -0
  47. package/dist/pivotCellRegistryEntry.d.ts.map +1 -0
  48. package/dist/pivotCellRegistryEntry.js +137 -0
  49. package/dist/pivotCellRegistryEntry.js.map +1 -0
  50. package/dist/pivotCellTypes.d.ts +23 -0
  51. package/dist/pivotCellTypes.d.ts.map +1 -0
  52. package/dist/pivotCellTypes.js +14 -0
  53. package/dist/pivotCellTypes.js.map +1 -0
  54. package/dist/pivotExecution.d.ts +16 -0
  55. package/dist/pivotExecution.d.ts.map +1 -0
  56. package/dist/pivotExecution.js +49 -0
  57. package/dist/pivotExecution.js.map +1 -0
  58. package/dist/sql.d.ts +17 -0
  59. package/dist/sql.d.ts.map +1 -0
  60. package/dist/sql.js +278 -0
  61. package/dist/sql.js.map +1 -0
  62. package/dist/types.d.ts +513 -0
  63. package/dist/types.d.ts.map +1 -0
  64. package/dist/types.js +107 -0
  65. package/dist/types.js.map +1 -0
  66. 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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sql.js","sourceRoot":"","sources":["../src/sql.ts"],"names":[],"mappings":"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","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"]}