Guide for implementing database engines in IterableData. Use when adding support for new SQL or NoSQL databases, implementing DBDriver classes, or extending database capabilities.
Database engines provide read-only access to SQL and NoSQL databases as iterable data sources. They wrap database drivers to provide a unified BaseIterable interface.
iterable/db/base.py) - Abstract base class for database driversiterable/db/iterable.py) - Wrapper that makes DBDriver work as BaseIterableiterable/db/__init__.py) - Registry for mapping engine names to driver classesiterable/db/<engine>.pyDBDriver in iterable/db/base.pyconnect(), iterate(), close() (close has default implementation)register_driver() in iterable/db/__init__.pyiterable/helpers/detect.py to recognize database URLstests/test_db_engines.py or add to existing test filepyproject.tomlfrom collections.abc import Iterator
from typing import Any
from ..types import Row
from .base import DBDriver
class NewEngineDriver(DBDriver):
"""Database driver for NewEngine.
Supports streaming queries using batch processing.
"""
def __init__(self, source: str | Any, **kwargs: Any) -> None:
"""Initialize driver.
Args:
source: Connection string/URL or existing connection object
**kwargs: Additional parameters:
- query: Query string or table name
- batch_size: Rows per batch (default: 10000)
- on_error: Error handling policy ('raise', 'skip', 'warn')
"""
super().__init__(source, **kwargs)
self._cursor: Any = None
def connect(self) -> None:
"""Establish database connection.
Raises:
ImportError: If required driver library is not installed
ConnectionError: If connection fails
"""
try:
import database_library
except ImportError:
raise ImportError(
"database-library is required. Install with: pip install database-library"
) from None
# Handle existing connection object
if hasattr(self.source, "cursor"):
self.conn = self.source
self._connected = True
return
# Parse connection string
if not isinstance(self.source, str):
raise ValueError("Source must be connection string or connection object")
try:
self.conn = database_library.connect(self.source, **self.kwargs.get("connect_args", {}))
self._connected = True
except Exception as e:
self._connected = False
raise ConnectionError(f"Failed to connect: {e}") from e
def iterate(self) -> Iterator[Row]:
"""Return iterator of dict rows.
Yields:
dict: Database row as dictionary
Raises:
RuntimeError: If not connected
"""
if not self._connected:
raise RuntimeError("Not connected. Call connect() first.")
self._start_metrics()
batch_size = self.kwargs.get("batch_size", 10000)
try:
query = self._build_query()
cursor = self.conn.cursor()
# Execute query with batching
cursor.execute(query)
while True:
rows = cursor.fetchmany(batch_size)
if not rows:
break
# Convert rows to dicts
column_names = [desc[0] for desc in cursor.description]
for row in rows:
row_dict = dict(zip(column_names, row))
self._update_metrics(rows_read=1)
yield row_dict
except Exception as e:
self._handle_error(e, "during iteration")
if self._on_error == "raise":
raise
self._connected = True on successquery parameter (SQL query or table name)batch_size parameter)self._update_metrics(rows_read=1)on_error policyself._start_metrics() before iterationThree policies (set via on_error kwarg):
'raise' - Raise exceptions (default)'skip' - Skip problematic rows'warn' - Warn and continueUse self._handle_error(error, context) for consistent handling.
Automatic metrics available via self.metrics:
rows_read - Number of rows readbytes_read - Bytes read (may be None)elapsed_seconds - Time elapsedUpdate during iteration: self._update_metrics(rows_read=count)
Register driver in iterable/db/__init__.py:
from .newengine import NewEngineDriver
register_driver("newengine", NewEngineDriver)
Update iterable/helpers/detect.py to recognize database URLs:
def detect_file_type(filename, content=None):
# Database URL detection
if filename.startswith(("postgresql://", "postgres://")):
return "database"
if filename.startswith("newengine://"):
return "database"
# ... existing detection
import pytest
from iterable.db.newengine import NewEngineDriver
class TestNewEngineDriver:
def test_connect(self):
driver = NewEngineDriver("newengine://localhost/db")
driver.connect()
assert driver.is_connected
driver.close()
def test_iterate(self):
driver = NewEngineDriver("newengine://localhost/db", query="SELECT * FROM table")
driver.connect()
rows = list(driver.iterate())
assert len(rows) > 0
assert isinstance(rows[0], dict)
driver.close()
def test_batch_processing(self):
driver = NewEngineDriver("newengine://localhost/db", query="SELECT * FROM table", batch_size=100)
driver.connect()
# Verify batching works correctly
driver.close()
def test_error_handling(self):
driver = NewEngineDriver("newengine://invalid", query="SELECT * FROM table", on_error="skip")
# Test error handling policies
with statement)Add to pyproject.toml:
[project.optional-dependencies]
newengine = ["database-library>=1.0.0"]
Handle missing dependencies:
try:
import database_library
except ImportError:
raise ImportError(
"newengine support requires 'database-library'. "
"Install with: pip install iterabledata[newengine]"
) from None
Look at existing implementations:
iterable/db/postgres.py - SQL database example (PostgreSQL)iterable/db/mongo.py - NoSQL database example (MongoDB)iterable/db/clickhouse.py - Columnar database exampleiterable/db/elasticsearch.py - Search engine examplefrom urllib.parse import urlparse
def parse_connection_string(conn_str: str) -> dict:
parsed = urlparse(conn_str)
return {
"host": parsed.hostname,
"port": parsed.port,
"database": parsed.path.lstrip("/"),
"username": parsed.username,
"password": parsed.password,
}
_reset_supported = False)npx skills add datenoio/database-engine-implementation下载完整 Skill 目录,包含 SKILL.md 及所有相关文件
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer