LCOV - code coverage report
Current view: top level - dashql-app/src/connections/hyper - hyper_log_query_sql.ts Coverage Total Hit
Test: DashQL 915828b Lines: 100.0 % 32 32
Test Date: 2026-11-06 19:30:32

            Line data    Source code
       1              : import { QueryScope } from './hyper_log_stats.js';
       2              : 
       3              : // The tables of the queries of a connection as SQL over the query ends of the extract of the Hyper log.
       4              : // The overview runs them with its client context, they are listed as queries of the app.
       5              : 
       6              : /// The rows that a table of queries lists
       7            1 : const MAX_ROWS = 100;
       8              : 
       9              : /// A number rounded to a number of decimals. A numeric without a scale has none in Hyper, it would round to integers.
      10            1 : const rounded = (value: string, decimals: number) => `round((${value})::numeric(38, 6), ${decimals})::double precision`;
      11              : 
      12              : /// Seconds of a column of the query ends as milliseconds, rounded to hundredths
      13            1 : const ms = (seconds: string) => rounded(`${seconds} * 1000`, 2);
      14              : 
      15              : /// The order of the query ends, newest first. The log times of Hyper in Wasm have millisecond resolution, the request
      16              : /// number orders the queries of a session that end within the same millisecond. It is text, padded to compare as number.
      17            1 : const NEWEST_FIRST = `epoch_ms DESC, lpad(req, 20, '0') DESC`;
      18              : 
      19              : /// The predicate of the query ends of a scope
      20            1 : function scopePredicate(scope: QueryScope): string {
      21            1 :     switch (scope) {
      22            1 :         case QueryScope.User: return 'NOT sent_by_app';
      23            1 :         case QueryScope.App: return 'sent_by_app';
      24            1 :         case QueryScope.All: return 'TRUE';
      25            1 :     }
      26            1 : }
      27              : 
      28              : /// The query ends of a scope that ended since a time, in milliseconds since the epoch.
      29              : /// The query ends are the rows of queryEndsQuery().
      30            1 : function selectQueryEnds(queryEnds: string, scope: QueryScope, since: number): string {
      31            1 :     return `SELECT * FROM (${queryEnds}) ends WHERE epoch_ms >= ${since} AND ${scopePredicate(scope)}`;
      32            1 : }
      33              : 
      34              : /// The finished queries, newest first.
      35            1 : export function recentQueriesSql(queryEnds: string, scope: QueryScope, since: number): string {
      36            1 :     return `
      37              :         SELECT
      38              :             local_ts::time AS time,
      39              :             CASE WHEN succeeded THEN 'ok' ELSE coalesce(error_code, 'failed') END AS status,
      40              :             coalesce(query, statement) AS query,
      41            1 :             ${ms('elapsed')} AS elapsed_ms,
      42            1 :             ${ms('cpu_time')} AS cpu_ms,
      43            1 :             ${ms('wait_time')} AS wait_ms,
      44              :             processed_rows,
      45              :             peak_memory_mb AS memory_mb,
      46              :             client_query_type AS source,
      47              :             error_message AS error
      48            1 :         FROM (${selectQueryEnds(queryEnds, scope, since)}) runs
      49            1 :         ORDER BY ${NEWEST_FIRST}
      50            1 :         LIMIT ${MAX_ROWS}`;
      51            1 : }
      52              : 
      53              : /// The shapes of queries with the text of their newest query, most elapsed time first.
      54              : /// Queries that only differ in their literals or comments share their shape, see hyperLogQueryEndsTable().
      55              : /// The shape column is the part of the query hash that they share, NULL for queries without a hash.
      56            1 : export function queryShapesSql(queryEnds: string, scope: QueryScope, since: number): string {
      57            1 :     return `
      58              :         WITH newest AS (
      59            1 :             SELECT *, row_number() OVER (PARTITION BY query_shape ORDER BY ${NEWEST_FIRST}) = 1 AS is_newest
      60            1 :             FROM (${selectQueryEnds(queryEnds, scope, since)}) ends
      61              :         )
      62              :         SELECT
      63              :             max(CASE WHEN is_newest THEN coalesce(query, statement) END) AS query,
      64              :             count(*)::integer AS queries,
      65              :             (count(*) FILTER (WHERE NOT succeeded))::integer AS failed,
      66            1 :             ${ms('sum(elapsed)')} AS total_ms,
      67            1 :             ${rounded('100 * sum(elapsed) / nullif(sum(sum(elapsed)) OVER (), 0)', 1)} AS share_pct,
      68            1 :             ${ms('avg(elapsed)')} AS mean_ms,
      69              :             sum(processed_rows)::bigint AS processed_rows,
      70              :             max(peak_memory_mb) AS memory_mb,
      71              :             CASE WHEN query_shape LIKE 'text:%' THEN NULL ELSE query_shape END AS shape
      72              :         FROM newest
      73              :         GROUP BY query_shape
      74              :         ORDER BY total_ms DESC
      75            1 :         LIMIT ${MAX_ROWS}`;
      76            1 : }
        

Generated by: LCOV version 2.0-1