agentsclimarketplace

Duckdb

Skill petercool/pcore/skills/duckdb

Write DuckDB code for analytical queries, Parquet scanning, data exploration, and in-memory analytics. Covers the full DuckDB API including connections, SQL queries, Parquet integration, concurrency model, and DuckLake. Triggers when: duckdb, read_parquet, parquet scan, analytical query, OLAP, "query parquet", hive_partitioning, union_by_name, duckdb.connect, DuckLake, in-memory database, columnar query.From its SKILL.md

Install
npx -y skills add petercool/pcore --skill duckdb

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

5.3 KB, as published. Nobody here has run it

DuckDB

This skill guides writing correct DuckDB code for analytical queries, Parquet file scanning, and in-memory data exploration. In this project, DuckDB is used as a Parquet query engine (not for data storage — ClickHouse handles that).

Official Documentation

Docs URL: https://duckdb.org/docs

Installation

uv add "duckdb>=0.10.0"

Core Patterns

In-Memory Connection (preferred for analytics)

import duckdb

conn = duckdb.connect(":memory:")  # no file, no locks, pure query engine
df = conn.execute("SELECT 42 AS answer").fetchdf()
conn.close()

Query Parquet Files

conn = duckdb.connect(":memory:")

# Single file
df = conn.execute("SELECT * FROM read_parquet('results.parquet')").fetchdf()

# Glob pattern — scan entire directory tree
df = conn.execute("""
    SELECT * FROM read_parquet('lake/backtests/**/*.parquet')
    ORDER BY sharpe DESC
""").fetchdf()

# Hive-style partition inference (infers partition columns from directory names)
df = conn.execute("""
    SELECT symbol, strategy, sharpe
    FROM read_parquet('lake/backtests/**/*.parquet', hive_partitioning=true)
    WHERE symbol = 'BTCUSDT'
""").fetchdf()
# 'symbol' and 'strategy' columns come from directory names like symbol=BTCUSDT/strategy=trend/

# Union by name (for files with different schemas)
df = conn.execute("""
    SELECT * FROM read_parquet('lake/validation/**/*.parquet',
                               hive_partitioning=true,
                               union_by_name=true)
""").fetchdf()

Write Query Results to Parquet

conn.execute("""
    COPY (SELECT * FROM read_parquet('input/**/*.parquet') WHERE sharpe > 1.0)
    TO 'filtered_results.parquet' (FORMAT parquet, COMPRESSION snappy)
""")

Query Pandas DataFrames Directly

import pandas as pd

df = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6]})
conn = duckdb.connect(":memory:")
result = conn.execute("SELECT * FROM df WHERE a > 1").fetchdf()
# DuckDB can query pandas DataFrames directly by name

Concurrency Model

The Fundamental Rule

DuckDB is single-writer, multiple-reader for .duckdb files:

  • Multiple processes can read the same file via read_only=True
  • Only ONE process can write at a time
  • A second writer will get a lock error
# SAFE — multiple readers
conn = duckdb.connect("data.duckdb", read_only=True)

# SAFE — in-memory (no file, no locks)
conn = duckdb.connect(":memory:")

# UNSAFE from multiple processes
conn = duckdb.connect("data.duckdb")  # read-write, exclusive lock

Multi-Worker Pattern (used in this project)

For Prefect/Ray pipelines with multiple workers:

  1. Workers write to Parquet files (immutable, no contention)
  2. Coordinator queries via duckdb.connect(":memory:") + read_parquet()
  3. No .duckdb file involved — pure in-memory query engine
# Worker (runs on any Ray node):
result_df.to_parquet(f"lake/backtests/symbol={symbol}/result.parquet")

# Coordinator (runs once, consolidates):
conn = duckdb.connect(":memory:")
leaderboard = conn.execute("""
    SELECT * FROM read_parquet('lake/backtests/**/*.parquet', hive_partitioning=true)
    ORDER BY sharpe DESC LIMIT 20
""").fetchdf()

DuckLake (v1.0, April 2026)

DuckLake provides ACID multi-table transactions over Parquet files with a SQL catalog. Useful when multiple writers need to modify the same logical table transactionally.

ATTACH 'ducklake:sqlite:catalog.db' AS lake;
INSERT INTO lake.backtests VALUES (...);
COMMIT;

When to use: ACID requirements across concurrent writers. When NOT to use: append-only workloads where each worker writes independent files (use plain Parquet instead — simpler).

Quick Reference

TaskCode
In-memory connectionduckdb.connect(":memory:")
Read ParquetSELECT * FROM read_parquet('path')
Glob scanread_parquet('dir/**/*.parquet')
Hive partitionsread_parquet('...', hive_partitioning=true)
Mixed schemasread_parquet('...', union_by_name=true)
Write ParquetCOPY (...) TO 'out.parquet' (FORMAT parquet)
Query DataFrameSELECT * FROM df (pandas DF by variable name)
Read-only fileduckdb.connect('file.duckdb', read_only=True)
Get as DataFrame.fetchdf()
Get as list.fetchall()

Anti-Patterns

  • Multiple processes writing to same .duckdb file — lock error; use Parquet files instead
  • Opening .duckdb without read_only=True from workers — blocks other connections
  • Forgetting conn.close() — holds the file lock
  • Not using hive_partitioning=true — loses partition column info from directory names
  • Not using union_by_name=true for files with different schemas — causes schema mismatch errors
  • Creating a .duckdb file when :memory: suffices — unnecessary file I/O and lock complexity

Keep looking

Skills are one crate of 627,801. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.