Build the final SELECT clause
(self, group_cols: List[str],
latency_columns: Optional[List[str]],
required_sources: Set[str],
need_sc_histogram: bool,
need_io_histogram: bool,
low_time: Optional[datetime],
high_time: Optional[datetime])
| 747 | return ctes |
| 748 | |
| 749 | def _build_final_select(self, group_cols: List[str], |
| 750 | latency_columns: Optional[List[str]], |
| 751 | required_sources: Set[str], |
| 752 | need_sc_histogram: bool, |
| 753 | need_io_histogram: bool, |
| 754 | low_time: Optional[datetime], |
| 755 | high_time: Optional[datetime]) -> str: |
| 756 | """Build the final SELECT clause""" |
| 757 | select_parts = [] |
| 758 | |
| 759 | # When we have histograms, use pre-calculated counts from sample_counts CTE |
| 760 | has_histogram = need_sc_histogram or need_io_histogram |
| 761 | |
| 762 | # Add group columns (case-insensitive checks) |
| 763 | for col in group_cols: |
| 764 | col_lower = col.lower() |
| 765 | if col_lower in ['samples', 'avg_threads', 'sclat_histogram', 'iolat_histogram']: |
| 766 | continue |
| 767 | if col_lower.startswith('sc.') or col_lower.startswith('io.'): |
| 768 | continue |
| 769 | |
| 770 | if has_histogram: |
| 771 | # When using sample_counts CTE, reference columns from sc table |
| 772 | select_parts.append(f"sc.{col}") |
| 773 | else: |
| 774 | # Handle stack current function columns (case-insensitive) |
| 775 | if col_lower == 'kstack_current_func': |
| 776 | select_parts.append("""CASE |
| 777 | WHEN bs.KSTACK_SYMS IS NOT NULL AND bs.KSTACK_SYMS != '' |
| 778 | THEN SPLIT_PART(SPLIT_PART(bs.KSTACK_SYMS, ';', 1), '+', 1) |
| 779 | ELSE '-' |
| 780 | END AS KSTACK_CURRENT_FUNC""") |
| 781 | elif col_lower == 'ustack_current_func': |
| 782 | select_parts.append("""CASE |
| 783 | WHEN bs.USTACK_SYMS IS NOT NULL AND bs.USTACK_SYMS != '' |
| 784 | THEN SPLIT_PART(SPLIT_PART(bs.USTACK_SYMS, ';', 1), '+', 1) |
| 785 | ELSE '-' |
| 786 | END AS USTACK_CURRENT_FUNC""") |
| 787 | else: |
| 788 | select_parts.append(f"bs.{col}") |
| 789 | |
| 790 | # Add aggregate columns |
| 791 | if has_histogram: |
| 792 | # Use pre-calculated counts from sample_counts CTE |
| 793 | select_parts.append("MAX(sc.samples) AS samples") |
| 794 | select_parts.append("MAX(sc.avg_threads) AS avg_threads") |
| 795 | else: |
| 796 | select_parts.append("COUNT(*) AS samples") |
| 797 | |
| 798 | # avg_threads is computed as samples per second (rate over time period) |
| 799 | if low_time and high_time: |
| 800 | # Calculate the time difference in seconds using DuckDB's EXTRACT function |
| 801 | select_parts.append( |
| 802 | f"ROUND(COUNT(*) / EXTRACT(EPOCH FROM (TIMESTAMP '{high_time}' - TIMESTAMP '{low_time}')), 2) AS avg_threads" |
| 803 | ) |
| 804 | else: |
| 805 | # Fallback when time range is not provided |
| 806 | select_parts.append("COUNT(*) AS avg_threads") |
no test coverage detected