chDB

repository·main·Indexed 25 days ago

https://github.com/chdb-io/chdb

An in-process OLAP SQL engine powered by ClickHouse, designed for high-performance SQL capabilities within Python applications. It features a pandas-compatible API called DataStore for lazy evaluation of large datasets, a connection-based SQL API, and support for querying files (Parquet, CSV, JSON, ORC) and remote sources (S3, MySQL, PostgreSQL). chDB also supports User Defined Functions (UDF), streaming queries, and integration with AI agent frameworks like LlamaIndex, Pydantic AI, and smolagents.

Tokens
92.4K
Snippets
284
Records
401
Agent score
83%

What's inside chdb

  1. Summary of DataStore Pandas Compatibility

    main

    DataStore provides a high-performance alternative to pandas with the following coverage:

    • 209 pandas DataFrame methods implemented
    • 56 pandas .str accessor methods
    • 42+ pandas .dt accessor methods (plus ClickHouse datetime extras)
    • 334 ClickHouse functions mapped to Pandas-like API
    • ClickHouse-specific accessors: .arr (37 methods), .json, .url, .ip, .geo

    Key features include automatic DataFrame/Series wrapping, performance optimization through caching, and thread-safe, immutable operations.

  2. Understand the chDB Data Science API Core Concepts

    main

    The chDB API is designed for data science workflows, providing a Pythonic interface that bridges the gap between SQL-based ClickHouse engines and the Python ecosystem (Pandas). Key concepts include:

    • DataStore: An abstraction of data (similar to a database table) that encapsulates various ClickHouse table engines. It supports schema inference or explicit schema setting via setSchema or inferSchema.
    • Function Chaining: A fluent interface supporting .function().function() syntax. It uses Lazy Evaluation, meaning operations are recorded and optimized into a single query plan, only executing when .execute() is called.
    • Zero-copy Pandas Integration: chDB supports zero-copy reading of Pandas DataFrames. The resulting DataFrames are fully compatible with Pandas (not Polars), allowing for seamless conversion and use of existing Pandas-based workflows.
  3. Mixed SQL and pandas Execution Model

    main

    DataStore uses a Mixed Execution Engine that allows arbitrary mixing of SQL-style operations and pandas operations.

    Execution Stages:

    1. SQL Query Building (Lazy): Methods like .select() and .filter() build a query without executing it.
    2. Execution (Triggered by pandas): The first pandas-style operation (e.g., .add_prefix(), .fillna()) triggers the SQL engine to execute the built query and cache the resulting DataFrame.
    3. SQL on DataFrame (chDB Magic): Subsequent SQL operations (like .filter() on a new column created by pandas) are executed directly on the cached DataFrame using chDB's Python() table function.
  4. Pandas DataFrame Compatibility Checklist

    main

    chDB provides extensive compatibility with the pandas API. Most common pandas operations work unchanged when using the chDB DataStore. Supported features include:

    • Attributes & Properties: index, columns, dtypes, values, shape, size, ndim, empty, T, axes.
    • Indexing & Selection: loc, iloc, at, iat, column selection df['col'], head(), tail(), sample(), select_dtypes(), query(), isin(), etc.
    • Statistical Methods: describe(), mean(), median(), std(), var(), sum(), quantile(), corr(), rank(), etc.
    • Data Manipulation: drop(), dropna(), fillna(), ffill(), bfill(), replace(), rename(), assign(), astype(), copy().
    • Sorting & Reindexing: sort_values(), sort_index(), reset_index(), set_index(), reindex().
    • Reshaping & Combining: pivot(), melt(), stack(), unstack(), merge(), join(), concat().
    • Binary & Comparison Operators: add(), sub(), mul(), div(), eq(), ne(), lt(), gt(), etc.
    • Function Application: apply(), agg(), transform(), groupby().
    • Time Series: rolling(), resample(), shift(), tz_convert(), etc.
    • Missing Data: isna(), notna(), interpolate().
    • Export/IO: to_csv(), to_json(), to_parquet(), to_dict(), to_numpy(), etc.
    • Iteration & Plotting: iterrows(), itertuples(), plot(), hist().
  5. Understand chDB Engine Limitations

    main

    Some behaviors in the DataStore layer differ from pandas because of underlying limitations in the chDB/ClickHouse engine. These cannot be fixed at the DataStore layer. Key limitation areas include:

    • Type Support: chDB does not support certain numpy types like CATEGORY or TIMEDELTA. Arrays cannot be inside Nullable types, and numpy arrays may be converted to strings in SQL.
    • Missing Functions: Certain pandas operations like .prod() (product()), str.normalize(), or using quantile() with an array parameter are not natively supported in the engine.
    • DateTime Discrepancies: Issues may arise with timezone offsets in Python() table functions, column name conflicts during multiple extractions, and strftime format differences (e.g., %M returning month name instead of minutes).
    • String Method Limitations: str.pad() only supports left padding (no side parameter), str.center() implementation details differ, and startswith/endswith do not support tuple parameters.
    • dtype Differences: In some cases, values are correct, but the returned type differs from pandas (e.g., NaT returning Nullable Int32 instead of float64).
  6. Implement User Defined Functions (UDFs)

    main

    When using @chdb_udf():

    • Ensure the function is stateless.
    • Import modules inside the function to avoid scope issues.
    • If the return type is not a String, specify it using the return_type parameter (e.g., return_type="UInt64").
    • Note that all input arguments are passed as strings; convert them inside the function if needed.
    from chdb.udf import chdb_udf
    from chdb import query
    
    @chdb_udf()
    def clean_text(text):
        import re
        return re.sub(r'[\w\s]', '', text.lower())
    
    @chdb_udf(return_type="UInt64")
    def calculate_sum(a, b):
        return int(a) + int(b)
    
    result = query("SELECT clean_text('Hello, World!') as cleaned")
  7. Perform streaming queries for large datasets

    main

    To process large datasets while maintaining constant memory usage, use streaming queries via sess.send_query(). This returns a StreamingResult that can be iterated in chunks.

    Important: You must explicitly call stream_result.close() or use a with statement to release resources, otherwise subsequent queries may be blocked.

    Example: Iterating through chunks manually:

    from chdb import session as chs
    
    sess = chs.Session()
    
    # Example: Manual iteration using fetch()
    rows_cnt = 0
    stream_result = sess.send_query("SELECT * FROM numbers(200000)", "CSV")
    while True:
        chunk = stream_result.fetch()
        if chunk is None:
            break
        rows_cnt += chunk.rows_read()
    
    print(rows_cnt) # 200000

    Example: Exporting to PyArrow/Delta Lake:

    import pyarrow as pa
    from deltalake import write_deltalake
    from chdb import session as chs
    
    sess = chs.Session()
    stream_result = sess.send_query("SELECT * FROM numbers(100000)", "Arrow")
    
    # Create RecordBatchReader with custom batch size
    batch_reader = stream_result.record_batch(rows_per_batch=10000)
    
    write_deltalake(
        table_or_uri="./my_delta_table",
        data=batch_reader,
        mode="overwrite"
    )
    
    stream_result.close()
    sess.close()
  8. Resolve File Access and Format Issues

    main

    If you encounter permission errors or file not found errors:

    • Use absolute paths via os.path.abspath().
    • Explicitly specify the format (e.g., 'CSV', 'Parquet', 'JSONEachRow') if auto-detection fails.
    • You can also provide an explicit schema in the file() function to resolve type issues.
    import os
    import chdb
    
    # Use absolute path
    file_path = "data.csv"
    abs_path = os.path.abspath(file_path)
    result = chdb.query(f"SELECT * FROM file('{abs_path}', 'CSV')")
    
    # Explicitly specify format and schema
    result = chdb.query("""
        SELECT * FROM file('data.csv', 'CSV', 
                          'id UInt32, name String, age UInt8')
    """)
    
    # Query remote files via URL
    result = chdb.query("""
        SELECT * FROM url('https://example.com/data.csv', 'CSV')
    """)
  9. Register a new string method in DataStore

    main

    To add a new string method, you must register it in function_definitions.py and, if it requires execution that changes the structure, implement it in ColumnExprStringAccessor.

    1. Register the function using the @register_function decorator.
    2. Automatic Injection: The method will be automatically available via the .str accessor.
    3. Implementation: If the method needs to trigger execution, implement it in ColumnExprStringAccessor using self._execute_series().
    @register_function(
        name='my_method',
        clickhouse_name='myClickHouseFunc',
        func_type=FunctionType.SCALAR,
        category=FunctionCategory.STRING,
        doc='Description of what it does.',
    )
    def _build_my_method(expr, arg1, alias=None):
        from .functions import Function
        from .expressions import Literal
        return Function('myClickHouseFunc', expr, Literal(arg1), alias=alias)
  10. Use Cluster Functions for Distributed Processing

    main

    To perform distributed query processing across cluster nodes and enable parallel file reading, wrap a standard table function with a Cluster function using the following pattern:

    <function_name>Cluster('cluster_name', <original_function_parameters>)

    Note: While this enables parallel reading, writes are routed through the initiator node, which can become a bottleneck.

  11. Install chDB via pip

    main

    Install the core chDB package with essential functionality using pip.

    Requirements:

    • Python 3.9 or higher
    • 64-bit architecture
    • Supported platforms: macOS and Linux (x86_64 and ARM64)

    Note: Windows is not currently supported.

    pip install chdb
  12. Integrate Pandas DataFrames with chDB

    main

    chDB provides two primary ways to work with Pandas DataFrames:

    1. chdb.dataframe (cdf) module:

      • Use cdf.query(sql, tbl1=df1, ...) to join multiple DataFrames using the __tbl1__, __tbl2__ syntax.
      • Use cdf.Table(dataframe=df) to wrap a DataFrame and query it using the __table__ keyword.
      • cdf.Table objects provide metadata via .rows_read(), .bytes_read(), and .elapsed().
    2. Python() engine:

      • Pass a DataFrame directly into a SQL string using the Python(df_name) syntax within chdb.query().