{
  "manifest_version": 1,
  "community_extension": {
    "extension": {
      "name": "duckhts",
      "description": "DuckDB extension for reading HTS file formats via htslib",
      "language": "C",
      "build": "cmake",
      "license": "MIT",
      "requires_toolchains": "python3",
      "excluded_platforms": "windows_amd64;windows_arm64",
      "maintainers": [
        "sounkou-bioinfo"
      ]
    },
    "repo": {
      "github": "RGenomicsETL/duckhts",
      "ref_source": "git_head"
    },
    "docs": {
      "hello_world_lines": [
        "-- Load the extension",
        "LOAD duckhts;",
        "",
        "-- Read a VCF/BCF file (tidy FORMAT columns)",
        "SELECT CHROM, POS, REF, ALT, SAMPLE_ID",
        "FROM read_bcf('test/data/formatcols.vcf.gz', tidy_format := true)",
        "LIMIT 5;",
        "",
        "-- Read a BAM/SAM file",
        "SELECT QNAME, RNAME, POS, READ_GROUP_ID, SAMPLE_ID",
        "FROM read_bam('test/data/rg.sam.gz')",
        "LIMIT 5;"
      ],
      "extended_intro": [
        "DuckHTS provides DuckDB table functions for common high-throughput sequencing (HTS) formats using htslib. Query VCF, BCF, BAM, CRAM, FASTA, FASTQ, GTF, GFF, and tabix-indexed files directly in SQL.",
        "The extension also includes sequence utility UDFs, SAM flag predicate helpers, and HTS metadata helpers."
      ],
      "feature_notes": [
        "Paired FASTQ is supported via `mate_path` or `interleaved := true`.",
        "CRAM reads are supported with an explicit reference file.",
        "GTF/GFF attributes can be returned as a parsed `MAP` with `attributes_map := true` via `read_gff` or `read_gtf`.",
        "Optional SAM tag columns and an auxiliary tag map are available through `standard_tags` and `auxiliary_tags`.",
        "Tabix readers support `header`, `header_names`, type inference with `auto_detect`, and explicit `column_types`.",
        "MSVC builds (`windows_amd64`/`windows_arm64`) are not supported; use MinGW/RTools on Windows."
      ]
    }
  },
  "functions": [
    {
      "name": "read_bcf",
      "kind": "table",
      "category": "Readers",
      "signature": "read_bcf(path, region := NULL, index_path := NULL, tidy_format := FALSE, additional_csq_column_types := NULL)",
      "returns": "table",
      "r_wrapper": "rduckhts_bcf",
      "description": "Read VCF and BCF variant data with typed INFO, FORMAT, typed CSQ/ANN/BCSQ subfields, optional tidy sample output, and optional bcftools-style CSQ type overrides.",
      "examples": [
        "SELECT CHROM, POS, REF, ALT FROM read_bcf('vcf_file.bcf') LIMIT 5;"
      ]
    },
    {
      "name": "read_bam",
      "kind": "table",
      "category": "Readers",
      "signature": "read_bam(path, standard_tags := FALSE, auxiliary_tags := FALSE, region := NULL, index_path := NULL, reference := NULL, sequence_encoding := 'string', quality_representation := 'string', decompression_threads := 2)",
      "returns": "table",
      "r_wrapper": "rduckhts_bam",
      "description": "Read SAM, BAM, and CRAM alignments with optional typed SAMtags and auxiliary tag maps. Use sequence_encoding := 'nt16' to return SEQ as UTINYINT[] and quality_representation := 'phred' to return QUAL as UTINYINT[] instead of VARCHAR. decompression_threads controls per-file htslib worker threads and defaults to 2; use 0 to disable worker threads.",
      "examples": [
        "SELECT QNAME, FLAG, RNAME, POS FROM read_bam('range.bam') LIMIT 5;"
      ]
    },
    {
      "name": "read_fasta",
      "kind": "table",
      "category": "Readers",
      "signature": "read_fasta(path, region := NULL, index_path := NULL, sequence_encoding := 'string')",
      "returns": "table",
      "r_wrapper": "rduckhts_fasta",
      "description": "Read FASTA records or indexed FASTA regions as sequence rows. Use sequence_encoding := 'nt16' to return SEQUENCE as UTINYINT[] (htslib nt16 4-bit codes) instead of VARCHAR.",
      "examples": [
        "SELECT NAME, length(SEQUENCE) FROM read_fasta('ce.fa');"
      ]
    },
    {
      "name": "read_bed",
      "kind": "table",
      "category": "Readers",
      "signature": "read_bed(path, region := NULL, index_path := NULL)",
      "returns": "table",
      "r_wrapper": "rduckhts_bed",
      "description": "Read BED3-BED12 interval files with canonical typed columns and optional tabix-backed region filtering.",
      "examples": [
        "SELECT chrom, start, \"end\", name FROM read_bed('targets.bed') LIMIT 5;"
      ]
    },
    {
      "name": "fasta_nuc",
      "kind": "table",
      "category": "Readers",
      "signature": "fasta_nuc(path, bed_path := NULL, bin_width := NULL, region := NULL, index_path := NULL, bed_index_path := NULL, include_seq := FALSE)",
      "returns": "table",
      "r_wrapper": "rduckhts_fasta_nuc",
      "description": "Compute bedtools nuc-style nucleotide composition for supplied BED intervals or generated fixed-width bins over a FASTA reference.",
      "examples": [
        "SELECT chrom, start, \"end\", pct_gc FROM fasta_nuc('ce.fa', bin_width := 1000) LIMIT 5;"
      ]
    },
    {
      "name": "duckhts_cgranges_create",
      "kind": "scalar",
      "category": "Intervals",
      "signature": "duckhts_cgranges_create(name)",
      "returns": "BOOLEAN",
      "r_wrapper": "",
      "description": "Create an empty session-scoped cgranges registry entry that can be populated with intervals and finalized for overlap queries.",
      "examples": [
        "SELECT duckhts_cgranges_create('targets_idx');"
      ]
    },
    {
      "name": "duckhts_cgranges_add",
      "kind": "scalar",
      "category": "Intervals",
      "signature": "duckhts_cgranges_add(name, chrom, start, end[, label])",
      "returns": "BOOLEAN",
      "r_wrapper": "",
      "description": "Append an interval to a session-scoped cgranges registry entry before finalization. Labels may be BIGINT-like, DOUBLE, VARCHAR, or BOOLEAN.",
      "examples": [
        "SELECT duckhts_cgranges_add('targets_idx', 'chr1', 10, 20, 'exon1');"
      ]
    },
    {
      "name": "duckhts_cgranges_index",
      "kind": "scalar",
      "category": "Intervals",
      "signature": "duckhts_cgranges_index(name)",
      "returns": "BOOLEAN",
      "r_wrapper": "",
      "description": "Finalize a populated cgranges registry entry and build its immutable overlap index for subsequent queries.",
      "examples": [
        "SELECT duckhts_cgranges_index('targets_idx');"
      ]
    },
    {
      "name": "duckhts_cgranges_destroy",
      "kind": "scalar",
      "category": "Intervals",
      "signature": "duckhts_cgranges_destroy(name)",
      "returns": "BOOLEAN",
      "r_wrapper": "",
      "description": "Destroy a session-scoped cgranges registry entry and release its indexed interval storage when it is not in active use.",
      "examples": [
        "SELECT duckhts_cgranges_destroy('targets_idx');"
      ]
    },
    {
      "name": "duckhts_cgranges_from_query",
      "kind": "scalar",
      "category": "Intervals",
      "signature": "duckhts_cgranges_from_query(name, query, chrom_col, start_col, end_col[, label_col])",
      "returns": "BOOLEAN",
      "r_wrapper": "",
      "description": "Execute a SQL query on an extension-owned DuckDB connection, append its interval rows into a session-scoped cgranges registry entry, and leave the populated index ready for explicit finalization with duckhts_cgranges_index(...).",
      "examples": [
        "SELECT duckhts_cgranges_from_query('targets_idx', 'SELECT chrom, start, \"end\", name FROM targets', 'chrom', 'start', 'end', 'name');"
      ]
    },
    {
      "name": "duckhts_cgranges_from_table",
      "kind": "scalar",
      "category": "Intervals",
      "signature": "duckhts_cgranges_from_table(name, table_name, chrom_col, start_col, end_col[, label_col])",
      "returns": "BOOLEAN",
      "r_wrapper": "",
      "description": "Reserved convenience constructor for bulk cgranges population from a table name. The current implementation is intentionally deferred and directs callers to duckhts_cgranges_from_query(...).",
      "examples": [
        "SELECT duckhts_cgranges_from_table('targets_idx', 'targets', 'chrom', 'start', 'end', 'name');"
      ]
    },
    {
      "name": "duckhts_cgranges_has_overlap",
      "kind": "scalar",
      "category": "Intervals",
      "signature": "duckhts_cgranges_has_overlap(name, chrom, start, end[, mode])",
      "returns": "BOOLEAN",
      "r_wrapper": "",
      "description": "Vectorized scalar predicate for streaming provider rows through a finalized session-scoped cgranges index. Returns TRUE when the query interval overlaps at least one indexed interval, or when mode = 'contain' and it fully contains at least one indexed interval; NULL inputs return NULL.",
      "examples": [
        "SELECT * FROM read_bed('queries.bed') WHERE duckhts_cgranges_has_overlap('targets_idx', chrom, start, \"end\");"
      ]
    },
    {
      "name": "duckhts_cgranges_count_overlaps",
      "kind": "scalar",
      "category": "Intervals",
      "signature": "duckhts_cgranges_count_overlaps(name, chrom, start, end[, mode])",
      "returns": "BIGINT",
      "r_wrapper": "",
      "description": "Vectorized scalar overlap counter for streaming provider rows through a finalized session-scoped cgranges index. Returns the number of indexed intervals that overlap the query interval, or with mode = 'contain' the number fully contained by it; NULL inputs return NULL.",
      "examples": [
        "SELECT chrom, start, \"end\", duckhts_cgranges_count_overlaps('targets_idx', chrom, start, \"end\") AS n_targets FROM read_bed('queries.bed');"
      ]
    },
    {
      "name": "duckhts_cgranges_overlaps_list",
      "kind": "scalar",
      "category": "Intervals",
      "signature": "duckhts_cgranges_overlaps_list(name, chrom, start, end[, mode])",
      "returns": "STRUCT(interval_ordinal BIGINT, label VARCHAR, label_type VARCHAR, interval_chrom VARCHAR, interval_start INTEGER, interval_end INTEGER)[]",
      "r_wrapper": "",
      "description": "Vectorized scalar overlap expander for streaming provider rows through a finalized session-scoped cgranges index. Returns a LIST of hit STRUCTs that can be expanded with UNNEST, preserving provider columns while emitting one row per matching indexed interval. Because scalar return types are fixed, labels are returned as text with label_type describing the original cgranges label kind; NULL inputs return NULL.",
      "examples": [
        "SELECT q.*, hit.interval_chrom, hit.interval_start, hit.interval_end, hit.label FROM read_bed('queries.bed') AS q CROSS JOIN UNNEST(duckhts_cgranges_overlaps_list('targets_idx', q.chrom, q.start, q.\"end\")) AS u(hit);"
      ]
    },
    {
      "name": "duckhts_cgranges_overlaps",
      "kind": "table",
      "category": "Intervals",
      "signature": "duckhts_cgranges_overlaps(name, chrom, start, end, mode := 'overlap', query_row_id := NULL)",
      "returns": "table",
      "r_wrapper": "",
      "description": "Query a finalized session-scoped cgranges registry entry and return one row per overlapping or containing indexed interval, preserving the original label type and interval coordinates.",
      "examples": [
        "SELECT * FROM duckhts_cgranges_overlaps('targets_idx', 'chr1', 100, 150);"
      ]
    },
    {
      "name": "duckhts_cgranges_overlaps_bulk",
      "kind": "table",
      "category": "Intervals",
      "signature": "duckhts_cgranges_overlaps_bulk(name, query, chrom_col, start_col, end_col, mode := 'overlap', query_row_id_col := NULL)",
      "returns": "table",
      "r_wrapper": "",
      "description": "Run a SQL query that yields overlap probes, stream those rows through a finalized session-scoped cgranges registry entry, and return one row per matching indexed interval. The probe query runs on the extension-owned helper connection, so it must reference regular tables/views rather than connection-local temp tables. When query_row_id_col is omitted, query_row_id defaults to the 1-based probe row ordinal.",
      "examples": [
        "SELECT * FROM duckhts_cgranges_overlaps_bulk('targets_idx', 'SELECT probe_id, chrom, start, \"end\" FROM probes', 'chrom', 'start', 'end', query_row_id_col := 'probe_id');"
      ]
    },
    {
      "name": "read_fastq",
      "kind": "table",
      "category": "Readers",
      "signature": "read_fastq(path, interleaved := FALSE, mate_path := NULL, sequence_encoding := 'string', quality_representation := 'string', input_quality_encoding := 'phred33')",
      "returns": "table",
      "r_wrapper": "rduckhts_fastq",
      "description": "Read single-end, paired-end, or interleaved FASTQ files with optional legacy quality decoding. By default, FASTQ qualities are interpreted as modern Phred+33 input. Use sequence_encoding := 'nt16' to return SEQUENCE as UTINYINT[] and quality_representation := 'phred' to return QUALITY as UTINYINT[] instead of VARCHAR. input_quality_encoding accepts 'phred33', 'auto', 'phred64', or 'solexa64'.",
      "examples": [
        "SELECT NAME, MATE FROM read_fastq('r1.fq', mate_path := 'r2.fq') LIMIT 5;"
      ]
    },
    {
      "name": "detect_quality_encoding",
      "kind": "table",
      "category": "Metadata",
      "signature": "detect_quality_encoding(path, max_records := 10000)",
      "returns": "table",
      "r_wrapper": "rduckhts_detect_quality_encoding",
      "description": "Inspect a FASTQ file's observed quality ASCII range and report compatible legacy encodings with a heuristic guessed encoding.",
      "examples": [
        "SELECT * FROM detect_quality_encoding('reads.fq.gz');"
      ]
    },
    {
      "name": "read_gff",
      "kind": "table",
      "category": "Readers",
      "signature": "read_gff(path, header_names := NULL, header := FALSE, column_types := NULL, auto_detect := FALSE, attributes_map := FALSE, attributes_list := FALSE, attributes_pairs := FALSE, strict := FALSE, region := NULL, index_path := NULL)",
      "returns": "table",
      "r_wrapper": "rduckhts_gff",
      "description": "Read GFF annotations with optional raw scalar and richer list/pair parsed attribute columns, strict GFF3 structural validation, and indexed region filtering.",
      "examples": [
        "SELECT seqname, feature, start, \"end\" FROM read_gff('gff_file.gff.gz') LIMIT 5;"
      ]
    },
    {
      "name": "read_gtf",
      "kind": "table",
      "category": "Readers",
      "signature": "read_gtf(path, header_names := NULL, header := FALSE, column_types := NULL, auto_detect := FALSE, attributes_map := FALSE, attributes_list := FALSE, attributes_pairs := FALSE, region := NULL, index_path := NULL)",
      "returns": "table",
      "r_wrapper": "rduckhts_gtf",
      "description": "Read GTF annotations with optional raw scalar and richer list/pair parsed attribute columns and indexed region filtering.",
      "examples": [
        "SELECT seqname, feature, start, \"end\" FROM read_gtf('annotations.gtf.gz') LIMIT 5;"
      ]
    },
    {
      "name": "read_tabix",
      "kind": "table",
      "category": "Readers",
      "signature": "read_tabix(path, header_names := NULL, header := FALSE, column_types := NULL, auto_detect := FALSE, region := NULL, index_path := NULL)",
      "returns": "table",
      "r_wrapper": "rduckhts_tabix",
      "description": "Read generic tabix-indexed text data with optional header handling and type inference.",
      "examples": [
        "SELECT * FROM read_tabix('meta_tabix.tsv.gz') LIMIT 5;"
      ]
    },
    {
      "name": "fasta_index",
      "kind": "table",
      "category": "Readers",
      "signature": "fasta_index(path, index_path := NULL)",
      "returns": "table",
      "r_wrapper": "rduckhts_fasta_index",
      "description": "Build a FASTA index (.fai) and return a single row with columns success (BOOLEAN) and index_path (VARCHAR).",
      "examples": [
        "SELECT * FROM fasta_index('ce.fa');"
      ]
    },
    {
      "name": "bgzip",
      "kind": "table",
      "category": "Compression",
      "signature": "bgzip(path, output_path := NULL, threads := 4, level := -1, keep := TRUE, overwrite := FALSE)",
      "returns": "table",
      "r_wrapper": "rduckhts_bgzip",
      "description": "Compress a plain file to BGZF and return the created output path and byte counts.",
      "examples": [
        "SELECT * FROM bgzip('regions.bed');"
      ]
    },
    {
      "name": "bgunzip",
      "kind": "table",
      "category": "Compression",
      "signature": "bgunzip(path, output_path := NULL, threads := 4, keep := TRUE, overwrite := FALSE)",
      "returns": "table",
      "r_wrapper": "rduckhts_bgunzip",
      "description": "Decompress a BGZF-compressed file and return the created output path and byte counts.",
      "examples": [
        "SELECT * FROM bgunzip('regions.bed.gz');"
      ]
    },
    {
      "name": "bam_index",
      "kind": "table",
      "category": "Indexing",
      "signature": "bam_index(path, index_path := NULL, min_shift := 0, threads := 4)",
      "returns": "table",
      "r_wrapper": "rduckhts_bam_index",
      "description": "Build a BAM or CRAM index and report the written index path and format.",
      "examples": [
        "SELECT * FROM bam_index('range.bam');"
      ]
    },
    {
      "name": "bcf_index",
      "kind": "table",
      "category": "Indexing",
      "signature": "bcf_index(path, index_path := NULL, min_shift := NULL, threads := 4)",
      "returns": "table",
      "r_wrapper": "rduckhts_bcf_index",
      "description": "Build a TBI or CSI index for a VCF or BCF file and report the written index path and format.",
      "examples": [
        "SELECT * FROM bcf_index('formatcols.vcf.gz');"
      ]
    },
    {
      "name": "tabix_index",
      "kind": "table",
      "category": "Indexing",
      "signature": "tabix_index(path, preset := 'vcf', index_path := NULL, min_shift := 0, threads := 4, seq_col := NULL, start_col := NULL, end_col := NULL, comment_char := NULL, skip_lines := NULL)",
      "returns": "table",
      "r_wrapper": "rduckhts_tabix_index",
      "description": "Build a tabix index for a BGZF-compressed text file using a preset or explicit coordinate columns.",
      "examples": [
        "SELECT * FROM tabix_index('gff_file.gff.gz', preset := 'gff');"
      ]
    },
    {
      "name": "bam_bin_counts",
      "kind": "table",
      "category": "Coverage",
      "signature": "bam_bin_counts(path, bin_width, chrom := NULL, reference := NULL, index_path := NULL, mapq := 0, require_flags := 0, exclude_flags := 0, rmdup := 'none', stats := NULL)",
      "returns": "table",
      "r_wrapper": "rduckhts_bam_bin_counts",
      "description": "Count BAM or CRAM read starts into fixed-width bins. Returns one row per bin across the selected contig span, including zero-count bins, with total, forward, and reverse counts; `rmdup := 'streaming'` applies the WisecondorX-style larp/larp2 consecutive-position deduplication, `rmdup := 'flag'` drops SAM duplicate-flagged reads, and `stats := 'gc'`, `'mq'`, or `'gc,mq'` adds per-bin pre/post-filter GC and MAPQ sufficient statistics, including reference GC when `reference` is provided.",
      "examples": [
        "SELECT * FROM bam_bin_counts('fixture_mixed.cram', 5000, reference := 'fixture_ref.fa', rmdup := 'streaming', stats := 'gc,mq');"
      ]
    },
    {
      "name": "duckhts_bam_bed_coverage",
      "kind": "table",
      "category": "Coverage",
      "signature": "duckhts_bam_bed_coverage(path, bed_path, reference := NULL, index_path := NULL, bed_index_path := NULL, mapq := 0, min_baseq := 0, min_read_len := 0, require_flags := 0, exclude_flags := 1796, min_depth := 1, max_depth := 1000000, decompression_threads := 0, fragment_mode := FALSE, strand_outputs := TRUE, processing_threads := 0)",
      "returns": "table",
      "r_wrapper": "rduckhts_bam_bed_coverage",
      "description": "Compute samtools coverage-like regional summaries for BAM or CRAM input over a BED target set, returning one row per BED interval with DuckHTS-specific pre/post-filter read counts, covered bases, percentage covered, mean depth, mean baseQ, mean mapQ, and strand-specific post-filter summaries in read mode. Indexed BAM/CRAM input is required in the current implementation. decompression_threads controls htslib worker threads for BAM/CRAM decoding; use 0 to disable them.",
      "examples": [
        "SELECT chrom, start, \"end\", numreads_post, covbases_post, coverage_post FROM duckhts_bam_bed_coverage('fixture_mixed.bam', 'fixture_mixed_regions.bed');"
      ]
    },
    {
      "name": "duckhts_mosdepth",
      "kind": "table",
      "category": "Coverage",
      "signature": "duckhts_mosdepth(prefix, path, chrom := NULL, by := NULL, fasta := NULL, read_groups := NULL, no_per_base := FALSE, threads := 2, processing_threads := 2, flag := 1796, include_flag := 0, fast_mode := FALSE, fragment_mode := FALSE, use_median := FALSE, mapq := 0, min_frag_len := -1, max_frag_len := -1, precision_digits := 2, quantize := NULL, thresholds := NULL, index_path := NULL, overwrite := FALSE)",
      "returns": "table",
      "r_wrapper": "rduckhts_mosdepth",
      "description": "Write native mosdepth-compatible coverage outputs for indexed BAM or CRAM input. Produces mosdepth-style summary, global distribution, per-base BED.gz + CSI, optional window/BED region outputs, optional quantized BED.gz + CSI, and optional threshold counts for `by`; `fast_mode` defaults to FALSE to match upstream mosdepth, default mode performs CIGAR-aware coverage with mate-overlap correction, `fragment_mode` switches coverage to full-fragment insert spans for proper pairs, `use_median` switches `by` outputs from mean to median, `read_groups` filters by comma-separated RG IDs, `min_frag_len` and `max_frag_len` filter on absolute template length, `fasta` is required for CRAM when htslib needs a reference, `precision_digits` controls decimal places in the text outputs, and `processing_threads` enables parallel contig processing (0 = sequential, >0 = number of worker threads).",
      "examples": [
        "SELECT * FROM duckhts_mosdepth('sample', 'range.cram', fasta := 'ce.fa', by := '1000', fragment_mode := TRUE, read_groups := '1', use_median := TRUE, min_frag_len := 50, max_frag_len := 500, quantize := ':1:4:', thresholds := '1,10,20', precision_digits := 4, overwrite := TRUE);"
      ]
    },
    {
      "name": "duckhts_samtools_idxstats",
      "kind": "table",
      "category": "Metadata",
      "signature": "duckhts_samtools_idxstats(path, output := NULL, index_path := NULL, threads := 0, overwrite := FALSE)",
      "returns": "table",
      "r_wrapper": "rduckhts_samtools_idxstats",
      "description": "Write samtools idxstats-compatible TAB-delimited output for BAM, CRAM, or SAM input. Indexed BAM uses `hts_idx_get_stat(...)` for the fast path; CRAM, SAM, and unindexed BAM fall back to a full scan while preserving samtools-style contig rows plus the final `*` row.",
      "examples": [
        "SELECT * FROM duckhts_samtools_idxstats('range.bam', output := 'range.idxstats.txt', overwrite := TRUE);"
      ]
    },
    {
      "name": "read_hts_header",
      "kind": "table",
      "category": "Metadata",
      "signature": "read_hts_header(path, format := NULL, mode := NULL)",
      "returns": "table",
      "r_wrapper": "rduckhts_hts_header",
      "description": "Inspect HTS headers in parsed, raw, or combined form across supported formats.",
      "examples": [
        "SELECT record_type, id FROM read_hts_header('formatcols.vcf.gz') LIMIT 10;"
      ]
    },
    {
      "name": "read_hts_index",
      "kind": "table",
      "category": "Metadata",
      "signature": "read_hts_index(path, format := NULL, index_path := NULL)",
      "returns": "table",
      "r_wrapper": "rduckhts_hts_index",
      "description": "Inspect high-level HTS index metadata such as sequence names and mapped counts.",
      "examples": [
        "SELECT seqname, index_type FROM read_hts_index('vcf_file.bcf');"
      ]
    },
    {
      "name": "read_hts_index_spans",
      "kind": "table",
      "category": "Metadata",
      "signature": "read_hts_index_spans(path, format := NULL, index_path := NULL)",
      "returns": "table",
      "r_wrapper": "rduckhts_hts_index_spans",
      "description": "Expand index metadata into span and chunk rows suitable for low-level index inspection.",
      "examples": [
        "SELECT seqname, chunk_beg_vo, chunk_end_vo FROM read_hts_index_spans('vcf_file.bcf') LIMIT 5;"
      ]
    },
    {
      "name": "read_hts_index_raw",
      "kind": "table_macro",
      "category": "Metadata",
      "signature": "read_hts_index_raw(path, format := NULL, index_path := NULL)",
      "returns": "table",
      "r_wrapper": "rduckhts_hts_index_raw",
      "description": "Return the raw on-disk HTS index blob together with basic identifying metadata.",
      "examples": [
        "SELECT length(raw) FROM read_hts_index_raw('formatcols.vcf.gz');"
      ]
    },
    {
      "name": "bcftools_liftover",
      "kind": "scalar",
      "category": "Variants",
      "signature": "bcftools_liftover(chrom, pos, ref, alt, chain_path, dst_fasta_ref, src_fasta_ref, max_snp_gap, max_indel_inc, lift_mt, end_pos, no_left_align)",
      "returns": "STRUCT",
      "r_wrapper": "rduckhts_liftover",
      "description": "Row-oriented liftover kernel intended to mirror bcftools +liftover semantics as closely as possible while returning one STRUCT per input row with fields: src_chrom, src_pos, src_ref, src_alt, dest_chrom, dest_pos, dest_end, dest_ref, dest_alt, mapped, reverse_complemented, swap, reject_reason, and note. Set no_left_align := true to skip post-liftover left-alignment of lifted indels (mirrors --no-left-align in bcftools +liftover).",
      "examples": [
        "SELECT (bcftools_liftover(chrom, pos, ref, alt, 'hg19ToHg38.over.chain.gz', 'hg38.fa', 'hg19.fa', 1, 250, false, NULL::BIGINT, false)).dest_pos FROM variants;"
      ]
    },
    {
      "name": "duckdb_liftover",
      "kind": "table_macro",
      "category": "Variants",
      "signature": "duckdb_liftover(table_name, chrom_col, pos_col, ref_col := NULL, alt_col := NULL, chain_path := NULL, dst_fasta_ref := NULL, src_fasta_ref := NULL, max_snp_gap := 1, max_indel_inc := 250, lift_mt := false, end_pos_col := NULL, no_left_align := false)",
      "returns": "table",
      "r_wrapper": "rduckhts_liftover",
      "description": "DuckDB-specific wrapper over bcftools_liftover that takes either a table name or a derived-table expression plus column-name strings for chrom/pos/ref/alt and returns the lifted table. The no_left_align parameter mirrors --no-left-align in bcftools +liftover.",
      "examples": [
        "SELECT * FROM duckdb_liftover('variants', 'chrom', 'pos', ref_col := 'ref', alt_col := 'alt', chain_path := 'hg19ToHg38.over.chain.gz', dst_fasta_ref := 'hg38.fa');",
        "SELECT * FROM duckdb_liftover('(SELECT chrom, pos, ref, alt FROM variants) AS v', 'chrom', 'pos', ref_col := 'ref', alt_col := 'alt', chain_path := 'hg19ToHg38.over.chain.gz', dst_fasta_ref := 'hg38.fa');"
      ]
    },
    {
      "name": "bcftools_score",
      "kind": "table",
      "category": "Variants",
      "signature": "bcftools_score(bcf_path, summary_path_or_list, use := NULL, columns := 'PLINK', columns_file := NULL, q_score_thr := NULL, summaries_list_file := NULL, log_path := NULL, use_variant_id := FALSE, counts := FALSE, samples := NULL, force_samples := FALSE, regions := NULL, regions_file := NULL, regions_overlap := 1, targets := NULL, targets_file := NULL, targets_overlap := 0, apply_filters := NULL, include := NULL, exclude := NULL)",
      "returns": "table",
      "r_wrapper": "rduckhts_score",
      "description": "Compute polygenic scores from one genotype BCF/VCF and one or more summary-statistics files with bcftools +score-compatible GT/DS/HDS/AP/GP/AS dosage semantics, sample subsetting, and region/target/FILTER-string controls. The second argument accepts a scalar path or a DuckDB LIST/array of paths; TSV/SSF summaries produce one PRS column per file in a single genotype scan, while GWAS-VCF summaries still produce one PRS column per FORMAT sample. Use summaries_list_file with a NULL second argument to read paths from a file or directory; list-file entries are interpreted as written, matching upstream `bcftools +score --summaries` behavior, while directory inputs scan supported regular summary files in lexicographic order and ignore index sidecars. Use log_path to write per-PRS loaded/matched/allele-mismatch/duplicate-marker audit counts.",
      "examples": [
        "SELECT * FROM bcftools_score('cohort.bcf', 'gwas.tsv.gz', columns := 'PLINK') LIMIT 5;",
        "SELECT * FROM bcftools_score('cohort.bcf', ['score1.tsv.gz', 'score2.tsv.gz'], columns := 'GWAS-SSF') LIMIT 5;",
        "SELECT * FROM bcftools_score('cohort.bcf', NULL, columns := 'GWAS-SSF', summaries_list_file := 'scores.list', log_path := 'score.log') LIMIT 5;"
      ]
    },
    {
      "name": "bcftools_munge_row",
      "kind": "scalar",
      "category": "Variants",
      "signature": "bcftools_munge_row(chrom, pos, a1, a2, id, p, z, or, beta, n, n_cas, n_con, info, frq, se, lp, ac, neff, neffdiv2, het_i2, het_p, het_lp, dire, fasta_ref, iffy_tag := 'IFFY', mismatch_tag := 'REF_MISMATCH', ns := NULL, nc := NULL, ne := NULL)",
      "returns": "STRUCT",
      "r_wrapper": "",
      "description": "Normalize one summary-statistics row into GWAS-VCF-style fields (chrom/pos/ref/alt/effect metrics), resolving REF/ALT orientation against a FASTA reference and applying swap-aware sign/frequency/count transforms. The output flag `alleles_swapped` means REF/ALT orientation was swapped to match the FASTA reference.",
      "examples": [
        "SELECT (bcftools_munge_row('chr1', 12345, 'A', 'G', 'rs1', 0.01, NULL, NULL, 0.12, 1000, NULL, NULL, NULL, 0.3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'ref.fa')).ref;"
      ]
    },
    {
      "name": "duckdb_munge",
      "kind": "table_macro",
      "category": "Variants",
      "signature": "duckdb_munge(table_name, preset := '', column_map := map([''], ['']), column_map_file := '', fasta_ref := NULL, iffy_tag := 'IFFY', mismatch_tag := 'REF_MISMATCH', ns := NULL, nc := NULL, ne := NULL)",
      "returns": "table",
      "r_wrapper": "rduckhts_munge",
      "description": "DuckDB macro wrapper over bcftools_munge_row that maps source columns (via preset or explicit map) and returns normalized GWAS-VCF-style rows with lean outputs and explicit `alleles_swapped` semantics. Output columns: chrom, pos, id, ref, alt, alleles_swapped, filter, ns, ez, nc, es, se, lp, af, ac, ne (16 columns). For METAL meta-analysis output with SI/I2/CQ/ED columns, use duckdb_munge_metal.",
      "examples": [
        "SELECT * FROM duckdb_munge('gwas_table', preset := 'PLINK', fasta_ref := 'ref.fa');",
        "SELECT * FROM duckdb_munge('(SELECT * FROM gwas_table) AS s', column_map := map(['CHR','BP','A1','A2','SNP'], ['chrom','pos','ea','nea','id']), fasta_ref := 'ref.fa');"
      ]
    },
    {
      "name": "duckdb_munge_metal",
      "kind": "table_macro",
      "category": "Variants",
      "signature": "duckdb_munge_metal(table_name, preset := '', column_map := map([''], ['']), column_map_file := '', fasta_ref := NULL, iffy_tag := 'IFFY', mismatch_tag := 'REF_MISMATCH', ns := NULL, nc := NULL, ne := NULL)",
      "returns": "table",
      "r_wrapper": "rduckhts_munge",
      "description": "Extended munge macro with METAL meta-analysis output columns. Same as duckdb_munge but additionally emits: si (imputation info, from INFO input), i2 (Cochran's I² heterogeneity, from HET_I2), cq (Cochran's Q -log10 p, from HET_LP or -log10(HET_P)), and ed (effect direction string, from DIRE; +/- flipped on allele swap). The R wrapper rduckhts_munge() auto-dispatches to this macro when metal keys (INFO, HET_I2, HET_P, HET_LP, DIRE) are present in the resolved column map.",
      "examples": [
        "SELECT * FROM duckdb_munge_metal('metal_results', preset := 'METAL', fasta_ref := 'ref.fa');",
        "SELECT * FROM duckdb_munge_metal('meta', column_map := map(['CHR','BP','A1','A2','SNP','HET_I2','DIRE'], ['chr','pos','ea','nea','snp','HetISq','Direction']), fasta_ref := 'ref.fa');"
      ]
    },
    {
      "name": "hts_union_query",
      "kind": "scalar_macro",
      "category": "Readers",
      "signature": "hts_union_query(reader, pattern, params := '')",
      "returns": "VARCHAR",
      "r_wrapper": "rduckhts_bam_multi, rduckhts_bcf_multi, rduckhts_fastq_multi, rduckhts_fasta_multi, rduckhts_bed_multi, rduckhts_tabix_multi, rduckhts_gff_multi, rduckhts_gtf_multi",
      "description": "Generate a UNION ALL BY NAME query string that reads every file matching a glob pattern through the named reader function. The result includes a 'filename' column identifying the source file for each row. Assign to a variable with SET VARIABLE and execute via query(getvariable(...)). Optional params string is appended to each reader call. In R, use the typed rduckhts_*_multi() helpers instead, which accept file vectors with optional per-file parameters and create DuckDB tables directly.",
      "examples": [
        "SET VARIABLE q = hts_union_query('read_bam', 'samples/*.bam'); SELECT * FROM query(getvariable('q'));",
        "SET VARIABLE q = hts_union_query('read_bcf', 'cohort/*.vcf.gz', 'tidy_format := true'); SELECT * FROM query(getvariable('q'));"
      ]
    },
    {
      "name": "seq_revcomp",
      "kind": "scalar",
      "category": "Sequence UDFs",
      "signature": "seq_revcomp(sequence)",
      "returns": "VARCHAR",
      "r_wrapper": "",
      "description": "Compute the reverse complement of a DNA sequence using A, C, G, T, and N bases.",
      "examples": [
        "SELECT seq_revcomp('ACGTN');"
      ]
    },
    {
      "name": "seq_canonical",
      "kind": "scalar",
      "category": "Sequence UDFs",
      "signature": "seq_canonical(sequence)",
      "returns": "VARCHAR",
      "r_wrapper": "",
      "description": "Return the lexicographically smaller of a sequence and its reverse complement.",
      "examples": [
        "SELECT seq_canonical('ACGTN');"
      ]
    },
    {
      "name": "seq_hash_2bit",
      "kind": "scalar",
      "category": "Sequence UDFs",
      "signature": "seq_hash_2bit(sequence)",
      "returns": "UBIGINT",
      "r_wrapper": "",
      "description": "Encode a short DNA sequence as a 2-bit unsigned integer hash.",
      "examples": [
        "SELECT seq_hash_2bit('ACGT');"
      ]
    },
    {
      "name": "seq_encode_4bit",
      "kind": "scalar",
      "category": "Sequence UDFs",
      "signature": "seq_encode_4bit(sequence)",
      "returns": "UTINYINT[]",
      "r_wrapper": "",
      "description": "Encode an IUPAC DNA sequence as a list of 4-bit base codes, preserving ambiguity symbols including N.",
      "examples": [
        "SELECT seq_encode_4bit('ACGTRYSWKMBDHVN');"
      ]
    },
    {
      "name": "seq_decode_4bit",
      "kind": "scalar",
      "category": "Sequence UDFs",
      "signature": "seq_decode_4bit(codes)",
      "returns": "VARCHAR",
      "r_wrapper": "",
      "description": "Decode a list of 4-bit IUPAC DNA base codes back into a sequence string.",
      "examples": [
        "SELECT seq_decode_4bit(seq_encode_4bit('ACGTRYSWKMBDHVN'));"
      ]
    },
    {
      "name": "seq_gc_content",
      "kind": "scalar",
      "category": "Sequence UDFs",
      "signature": "seq_gc_content(sequence)",
      "returns": "DOUBLE",
      "r_wrapper": "",
      "description": "Compute GC fraction for a DNA sequence as a value between 0 and 1.",
      "examples": [
        "SELECT seq_gc_content('ACGT');"
      ]
    },
    {
      "name": "seq_kmers",
      "kind": "table",
      "category": "Sequence UDFs",
      "signature": "seq_kmers(sequence, k, canonical := FALSE)",
      "returns": "table",
      "r_wrapper": "",
      "description": "Expand a sequence into positional k-mers with optional canonicalization.",
      "examples": [
        "SELECT * FROM seq_kmers('ACGT', 2);"
      ]
    },
    {
      "name": "sam_flag_bits",
      "kind": "scalar",
      "category": "SAM Flag UDFs",
      "signature": "sam_flag_bits(flag)",
      "returns": "STRUCT",
      "r_wrapper": "",
      "description": "Decode a SAM flag into a struct of boolean bit fields using explicit SAM-oriented names such as `is_paired`, `is_proper_pair`, `is_next_segment_unmapped`, and `is_supplementary`.",
      "examples": [
        "SELECT (sam_flag_bits(99)).is_proper_pair;"
      ]
    },
    {
      "name": "sam_flag_has",
      "kind": "scalar",
      "category": "SAM Flag UDFs",
      "signature": "sam_flag_has(flag, mask)",
      "returns": "BOOLEAN",
      "r_wrapper": "",
      "description": "Test whether any bits from the provided SAM flag mask are set in a flag value.",
      "examples": [
        "SELECT sam_flag_has(99, 2);"
      ]
    },
    {
      "name": "is_forward_aligned",
      "kind": "scalar",
      "category": "SAM Flag UDFs",
      "signature": "is_forward_aligned(flag)",
      "returns": "BOOLEAN",
      "r_wrapper": "",
      "description": "Test whether a mapped segment is aligned to the forward strand. Returns `NULL` for unmapped segments because SAM flag `0x10` does not define genomic strand when `0x4` is set.",
      "examples": [
        "SELECT is_forward_aligned(0);"
      ]
    },
    {
      "name": "cigar_has_soft_clip",
      "kind": "scalar",
      "category": "CIGAR Utils",
      "signature": "cigar_has_soft_clip(cigar)",
      "returns": "BOOLEAN",
      "r_wrapper": "",
      "description": "Test whether a CIGAR string contains any soft-clipped segment (`S`).",
      "examples": [
        "SELECT cigar_has_soft_clip('5S90M5S');"
      ]
    },
    {
      "name": "cigar_has_hard_clip",
      "kind": "scalar",
      "category": "CIGAR Utils",
      "signature": "cigar_has_hard_clip(cigar)",
      "returns": "BOOLEAN",
      "r_wrapper": "",
      "description": "Test whether a CIGAR string contains any hard-clipped segment (`H`).",
      "examples": [
        "SELECT cigar_has_hard_clip('5H95M');"
      ]
    },
    {
      "name": "cigar_left_soft_clip",
      "kind": "scalar",
      "category": "CIGAR Utils",
      "signature": "cigar_left_soft_clip(cigar)",
      "returns": "BIGINT",
      "r_wrapper": "",
      "description": "Return the left-end soft-clipped length from a CIGAR string, or zero if the alignment does not start with `S`.",
      "examples": [
        "SELECT cigar_left_soft_clip('5S90M5S');"
      ]
    },
    {
      "name": "cigar_right_soft_clip",
      "kind": "scalar",
      "category": "CIGAR Utils",
      "signature": "cigar_right_soft_clip(cigar)",
      "returns": "BIGINT",
      "r_wrapper": "",
      "description": "Return the right-end soft-clipped length from a CIGAR string, or zero if the alignment does not end with `S`.",
      "examples": [
        "SELECT cigar_right_soft_clip('5S90M5S');"
      ]
    },
    {
      "name": "cigar_query_length",
      "kind": "scalar",
      "category": "CIGAR Utils",
      "signature": "cigar_query_length(cigar)",
      "returns": "BIGINT",
      "r_wrapper": "",
      "description": "Return the query-consuming length from a CIGAR string, counting `M`, `I`, `S`, `=`, and `X`.",
      "examples": [
        "SELECT cigar_query_length('5S90M5I');"
      ]
    },
    {
      "name": "cigar_aligned_query_length",
      "kind": "scalar",
      "category": "CIGAR Utils",
      "signature": "cigar_aligned_query_length(cigar)",
      "returns": "BIGINT",
      "r_wrapper": "",
      "description": "Return the aligned query length from a CIGAR string, counting `M`, `=`, and `X` but excluding clips and insertions.",
      "examples": [
        "SELECT cigar_aligned_query_length('5S90M5I');"
      ]
    },
    {
      "name": "cigar_reference_length",
      "kind": "scalar",
      "category": "CIGAR Utils",
      "signature": "cigar_reference_length(cigar)",
      "returns": "BIGINT",
      "r_wrapper": "",
      "description": "Return the reference-consuming length from a CIGAR string, counting `M`, `D`, `N`, `=`, and `X`.",
      "examples": [
        "SELECT cigar_reference_length('90M5D');"
      ]
    },
    {
      "name": "cigar_has_op",
      "kind": "scalar",
      "category": "CIGAR Utils",
      "signature": "cigar_has_op(cigar, op)",
      "returns": "BOOLEAN",
      "r_wrapper": "",
      "description": "Test whether a CIGAR string contains at least one instance of the requested operator.",
      "examples": [
        "SELECT cigar_has_op('5S90M5S', 'S');"
      ]
    },
    {
      "name": "is_paired",
      "kind": "scalar",
      "category": "SAM Flag UDFs",
      "signature": "is_paired(flag)",
      "returns": "BOOLEAN",
      "r_wrapper": "",
      "description": "Test whether the SAM flag indicates that the template has multiple segments in sequencing (`0x1`).",
      "examples": [
        "SELECT is_paired(99);"
      ]
    },
    {
      "name": "is_proper_pair",
      "kind": "scalar",
      "category": "SAM Flag UDFs",
      "signature": "is_proper_pair(flag)",
      "returns": "BOOLEAN",
      "r_wrapper": "",
      "description": "Test whether the SAM flag indicates that each segment is properly aligned according to the aligner (`0x2`).",
      "examples": [
        "SELECT is_proper_pair(99);"
      ]
    },
    {
      "name": "is_unmapped",
      "kind": "scalar",
      "category": "SAM Flag UDFs",
      "signature": "is_unmapped(flag)",
      "returns": "BOOLEAN",
      "r_wrapper": "",
      "description": "Test whether the read itself is unmapped according to the SAM flag.",
      "examples": [
        "SELECT is_unmapped(4);"
      ]
    },
    {
      "name": "is_next_segment_unmapped",
      "kind": "scalar",
      "category": "SAM Flag UDFs",
      "signature": "is_next_segment_unmapped(flag)",
      "returns": "BOOLEAN",
      "r_wrapper": "",
      "description": "Test whether the next segment in the template is flagged as unmapped (`0x8`).",
      "examples": [
        "SELECT is_next_segment_unmapped(9);"
      ]
    },
    {
      "name": "is_reverse_complemented",
      "kind": "scalar",
      "category": "SAM Flag UDFs",
      "signature": "is_reverse_complemented(flag)",
      "returns": "BOOLEAN",
      "r_wrapper": "",
      "description": "Test whether `SEQ` is stored reverse complemented (`0x10`); for mapped reads this corresponds to reverse-strand alignment.",
      "examples": [
        "SELECT is_reverse_complemented(16);"
      ]
    },
    {
      "name": "is_next_segment_reverse_complemented",
      "kind": "scalar",
      "category": "SAM Flag UDFs",
      "signature": "is_next_segment_reverse_complemented(flag)",
      "returns": "BOOLEAN",
      "r_wrapper": "",
      "description": "Test whether `SEQ` of the next segment in the template is stored reverse complemented (`0x20`).",
      "examples": [
        "SELECT is_next_segment_reverse_complemented(32);"
      ]
    },
    {
      "name": "is_first_segment",
      "kind": "scalar",
      "category": "SAM Flag UDFs",
      "signature": "is_first_segment(flag)",
      "returns": "BOOLEAN",
      "r_wrapper": "",
      "description": "Test whether the read is marked as the first segment in the template.",
      "examples": [
        "SELECT is_first_segment(64);"
      ]
    },
    {
      "name": "is_last_segment",
      "kind": "scalar",
      "category": "SAM Flag UDFs",
      "signature": "is_last_segment(flag)",
      "returns": "BOOLEAN",
      "r_wrapper": "",
      "description": "Test whether the read is marked as the last segment in the template.",
      "examples": [
        "SELECT is_last_segment(128);"
      ]
    },
    {
      "name": "is_secondary",
      "kind": "scalar",
      "category": "SAM Flag UDFs",
      "signature": "is_secondary(flag)",
      "returns": "BOOLEAN",
      "r_wrapper": "",
      "description": "Test whether the alignment is marked as secondary.",
      "examples": [
        "SELECT is_secondary(256);"
      ]
    },
    {
      "name": "is_qc_fail",
      "kind": "scalar",
      "category": "SAM Flag UDFs",
      "signature": "is_qc_fail(flag)",
      "returns": "BOOLEAN",
      "r_wrapper": "",
      "description": "Test whether the read failed vendor or pipeline quality checks.",
      "examples": [
        "SELECT is_qc_fail(512);"
      ]
    },
    {
      "name": "is_duplicate",
      "kind": "scalar",
      "category": "SAM Flag UDFs",
      "signature": "is_duplicate(flag)",
      "returns": "BOOLEAN",
      "r_wrapper": "",
      "description": "Test whether the alignment is flagged as a duplicate.",
      "examples": [
        "SELECT is_duplicate(1024);"
      ]
    },
    {
      "name": "is_supplementary",
      "kind": "scalar",
      "category": "SAM Flag UDFs",
      "signature": "is_supplementary(flag)",
      "returns": "BOOLEAN",
      "r_wrapper": "",
      "description": "Test whether the alignment is marked as supplementary.",
      "examples": [
        "SELECT is_supplementary(2048);"
      ]
    }
  ]
}
