DB API
DB API 提供了一个符合 Python DB-API 2.0 规范的接口。该模块使用 DuckDBPyConnection 对象。他比较特殊的一点是不再提供 Cursor 对象,他们统一都是用 DuckDBPyConnection 对象:
Python
from duckdb import DuckDBPyConnection, DuckDBPyRelation
import pyarrow as pa
import pandas as pd
class DuckDBPyConnection:
# =========== Connection 规范接口 ===============
def close(self) -> None:
"""关闭 Connection"""
pass
def commit(self) -> DuckDBPyConnection:
"""提交事务中的更改"""
pass
def cursor(self) -> DuckDBPyConnection:
"""这个是最特殊的,他返回一个当前 Connection 对象的副本
也就是他将 Cursor 和 Connection 统一了
"""
pass
def rollback(self) -> DuckDBPyConnection:
"""回滚事务"""
pass
# =========== Cursor 规范接口 ==================
# 由于 DuckDBPyConnection 和 Cursor 合并了,所以他也提供了 Cursor 的规范接口
# 描述返回列
description: str
# 执行
def execute(
self, query, parameters=None, multiple_parameter_sets: bool = False
) -> DuckDBPyConnection:
pass
def executemany(self, query, parameters=None) -> DuckDBPyConnection:
pass
# 获取数据
def fetchone(self) -> tuple | None:
pass
def fetchmany(self, size: int = 1) -> list:
pass
def fetchall(self) -> list:
pass
# ============== 规范中没有定义的扩展接口 ============
# =========== Relational API 相关 =================
def sql(self, query) -> DuckDBPyRelation:
"""根据 SQL 语句来构造关系对象
注意只能是 SELECT 语句,其他语句则等价于 execute
"""
pass
def table(self, table_name: str) -> DuckDBPyRelation:
"""根据表名来构造关系对象
等价于 sql('SELECT * FROM table_name")
"""
pass
def from_arrow(self, arrow_object) -> DuckDBPyRelation:
"""基于 arrow 类型数据来构建关系对象"""
pass
def from_df(self, df) -> DuckDBPyRelation:
"""基于 DataFrame 来构建关系对象"""
pass
def read_parquet(*arg, **kwargs) -> DuckDBPyRelation:
"""基于 parquet 文件来构建关系对象"""
pass
def read_csv(*arg, **kwargs) -> DuckDBPyRelation:
"""基于 csv 文件来构建关系对象"""
pass
def read_json(*arg, **kwargs) -> DuckDBPyRelation:
"""基于 json 文件来构建关系对象"""
pass
# =========== 数据源交换相关 ========================
def fetch_arrow_table(self, rows_per_batch: int = 1000000) -> pa.lib.Table:
"""获取 Arrow Table数据,和 pyarrow 交互"""
pass
def fetch_record_batch(
self, row_per_batch: int = 1000000
) -> pa.lib.RecordBatchReader:
"""获取 Arrow RecordBatchReader 数据,和 pyarrow 交互"""
pass
def fetch_df(self, date_as_object: bool = False) -> pd.DataFrame:
"""获取 pandas DataFrame 数据,和 pandas 交互"""
pass
def fetch_df_chunk(
self, vectors_per_chunk: int = 1, date_as_object: bool = False
) -> pd.DataFrame:
pass
# ============= UDF 定义 =======================
def create_function(
self,
func_name: str, # 函数名(用于 SQL 查询中
function, # 真正的函数 Callable
parameters_type: list = None, # 列类型列表
return_type=None, # 返回值类型 DuckDBPyType
exception_handling=None, # 默认引发异常抛出,如果为 'return_null' 则设置为 NULL
) -> DuckDBPyConnection:
"""创建 UDF"""
pass
def remove_function(self, func_name):
"""移除指定 UDF"""
pass
我们通常会与标准库实现的 SQLite 驱动来与其他驱动做对比,DuckDB 的数据库驱动有几点不太相同:
- 最大的区别在于 Connection 和 Cursor 被统一到 DuckDBPyConnection 对象中
- 大多数方法都是返回了 DuckDBPyConnection 对象本身,这样能够实现链式调用
- 作为入口对象,构造Relational API的 DuckDBPyRelation 对象也是由 DuckDBPyConnection 提供的
全局属性以及 default_connection
DB API 中规定的全局属性在 DuckDBPyConnection 对象上也都存在,并且由于他类似于 SQLite 存在内存数据库。DuckDB API 甚至直接在模块级别提供了所有的 Connection 和 Cursor 接口。他们实际上就是 default_connection 属性提供的 DuckDBPyConnection 对象提供的。
模块级别的属性和方法包括:
apilevel: str
: API级别threadsafety: int
: 线程安全级别paramstyle: str
: 表示接口支持的参数标记格式的类型,默认是 qmark,实际上支持多个default_conection: DuckDBPyConnection
: 如果直接在模块级别使用默认使用的就是这个连接,他操作的就是内存数据库- 所有由 DuckDBPyConnection 提供的属性和方法
并发处理
DuckDB 支持单进程并发(默认情况)以及多进程只读:
DuckDBPyConnection.cursor
并且还有一个比较特殊的就是 DuckDB 的 API 线程安全是 1,即线程可以共享模块,但是不能共享 Connection:
因此如果我们要在 DuckDB 中实现并发就需要创建不同的 DuckDBPyConnection 对象。而 DuckDBPyConnection.cursor()
函数的意义就是从原始的 DuckDBPyConnection 对象复制一个全新的连接对象。
DuckDB 的 Python API 中的 Connection.cursor 并不是完全为了符合 DB-API2.0 标准而存在的,它最核心的意义就是在并发中生成能够用于多线程的新的 DuckDBPyConnection 对象