49df40cb11
TRQL (pronounced Treacle like the delicious British dark sweet syrup) is the TRiggerQueryLanguage. It allows users to safely write queries on their data. The queries are safely turned into ClickHouse queries which are tenant-safe and not SQL injectable. https://github.com/user-attachments/assets/bbfca473-b3fc-4150-8fe6-79e8840a2d29 This started out as a translation of HogQL by PostHog from Python to TypeScript. Features - Tenant safe queries. - Many underlying ClickHouse features including functions and aggregations. - Virtual columns, which are exposed to users as real columns but are actually expressions. - Transformations of data types and where clauses. - Simple JSON path querying. - Limits on execution time. - Reporting of query statistics. ## Query page There’s a new Query page (currently behind a feature flag) where you can write TRQL queries and execute them against your environment, project or organization. Features - Executing TRQL queries - Syntax highlighting and errors - Autocomplete - AI generation/editing of queries - Help and examples - Table with auto-inferred data types from the table schema - Table cell renderers for our special types like Run ids, environments, machines, tasks, queues, etc. - Copy/export as CSV/JSON - Line and bar graphs with grouping and stacking - History of queries
80 lines
2.2 KiB
TypeScript
80 lines
2.2 KiB
TypeScript
import type { OutputColumnMetadata } from "@internal/clickhouse";
|
|
|
|
/**
|
|
* Escape a value for CSV format.
|
|
* - Wraps in quotes if the value contains commas, quotes, or newlines
|
|
* - Escapes quotes by doubling them
|
|
*/
|
|
function escapeCSVValue(value: unknown): string {
|
|
if (value === null || value === undefined) {
|
|
return "";
|
|
}
|
|
|
|
const stringValue = typeof value === "object" ? JSON.stringify(value) : String(value);
|
|
|
|
// Check if we need to quote the value
|
|
if (
|
|
stringValue.includes(",") ||
|
|
stringValue.includes('"') ||
|
|
stringValue.includes("\n") ||
|
|
stringValue.includes("\r")
|
|
) {
|
|
// Escape quotes by doubling them and wrap in quotes
|
|
return `"${stringValue.replace(/"/g, '""')}"`;
|
|
}
|
|
|
|
return stringValue;
|
|
}
|
|
|
|
/**
|
|
* Convert query result rows to CSV format.
|
|
*
|
|
* @param rows - Array of row objects from query results
|
|
* @param columns - Column metadata describing the result columns
|
|
* @returns CSV string with header row and data rows
|
|
*/
|
|
export function rowsToCSV(rows: Record<string, unknown>[], columns: OutputColumnMetadata[]): string {
|
|
if (columns.length === 0) {
|
|
return "";
|
|
}
|
|
|
|
const columnNames = columns.map((col) => col.name);
|
|
|
|
// Header row
|
|
const headerRow = columnNames.map(escapeCSVValue).join(",");
|
|
|
|
// Data rows
|
|
const dataRows = rows.map((row) => columnNames.map((name) => escapeCSVValue(row[name])).join(","));
|
|
|
|
return [headerRow, ...dataRows].join("\n");
|
|
}
|
|
|
|
/**
|
|
* Convert query result rows to JSON format.
|
|
*
|
|
* @param rows - Array of row objects from query results
|
|
* @returns Formatted JSON string
|
|
*/
|
|
export function rowsToJSON(rows: Record<string, unknown>[]): string {
|
|
return JSON.stringify(rows, null, 2);
|
|
}
|
|
|
|
/**
|
|
* Trigger a file download in the browser.
|
|
*
|
|
* @param content - The file content as a string
|
|
* @param filename - The name for the downloaded file
|
|
* @param mimeType - The MIME type of the file
|
|
*/
|
|
export function downloadFile(content: string, filename: string, mimeType: string): void {
|
|
const blob = new Blob([content], { type: mimeType });
|
|
const url = URL.createObjectURL(blob);
|
|
const a = document.createElement("a");
|
|
a.href = url;
|
|
a.download = filename;
|
|
a.click();
|
|
URL.revokeObjectURL(url);
|
|
}
|
|
|
|
|