Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.15.12
hooks:
- id: ruff # Linter
args: [ --fix ]
- id: ruff-format # Formatter
7 changes: 5 additions & 2 deletions docs/dev/internals.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ metricsqlite/
├── engine/
│ ├── parser/ # Lexer, parser, AST nodes
│ ├── executor.py # AST evaluation, result types
│ ├── functions.py # Rollup, transformation, aggregation functions
│ ├── sqlite.py # SQLiteAdapter for raw data fetching
│ └── query.py # QueryEngine - public query interface
└── fastapi/ # Optional FastAPI routes
Expand Down Expand Up @@ -68,8 +69,10 @@ Samples may have an `end` field indicating they span a time range `[start, end]`

The executor handles overlap detection and boundary clamping when windowing.

## Function Categories
## Function Categories (`functions.py`)

- **Rollup** (`avg_over_time`, `sum_over_time`, etc.): Aggregate samples within each window
- **Rollup** (`avg_over_time`, `sum_over_time`, `integrate`, etc.): Aggregate samples within each window
- **Transformation** (`abs`, `clamp_min`, `clamp_max`): Transform individual values
- **Aggregation** (`sum`, `avg`, `min`, `max`, `count`): Aggregate across series

Each category has a dictionary mapping function names to implementations.
19 changes: 11 additions & 8 deletions metricsqlite/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from datetime import datetime
from pathlib import Path

from metricsqlite.engine import MatrixResult, QueryEngine, QueryResult
from metricsqlite.engine import MatrixResult, QueryEngine, QueryResult, sqlite_regexp
from metricsqlite.exceptions import CompactedRangeError
from metricsqlite.util import parse_interval, parse_timestamp

Expand All @@ -29,6 +29,7 @@ def __init__(
db_path: str | Path | None,
tables_prefix: str = "metricsqlite",
enable_wal: bool = False,
register_regexp: bool = True,
) -> None:
"""
Args:
Expand All @@ -37,10 +38,14 @@ def __init__(
enable_wal: Enable WAL journal mode for better concurrent read/write
performance. Note: This is a database-level setting that
affects all connections to this database file.
register_regexp: Register a custom REGEXP function for regex label
matching. Set to False if your database already has a
REGEXP implementation (e.g., from sqlite3-pcre extension).
"""
self._db_path = db_path
self._tables_prefix = tables_prefix
self._enable_wal = enable_wal
self._register_regexp = register_regexp
self._lock = threading.Lock()

self._connection: sqlite3.Connection | None = None
Expand Down Expand Up @@ -90,6 +95,8 @@ def connect(self) -> None:
db_path = self._db_path if self._db_path is not None else ":memory:"
self._connection = sqlite3.connect(db_path, check_same_thread=False)
self._connection.row_factory = sqlite3.Row
if self._register_regexp:
self._connection.create_function("regexp", 2, sqlite_regexp)
if self._enable_wal:
self._get_connection().execute("PRAGMA journal_mode=WAL")
self._engine = QueryEngine(
Expand Down Expand Up @@ -233,9 +240,8 @@ def __exit__(self, exc_type: object, exc_val: object, exc_tb: object) -> None:
def query(
self,
query: str,
time: float | str | None = None,
time: float | str | datetime | None = None,
step: float | str | None = None,
timeout: float | str | None = None,
) -> QueryResult:
"""Execute an instant query.

Expand All @@ -247,21 +253,19 @@ def query(
step: Lookback window. If set, only samples within
[time - step, time] are considered. Accepts seconds or
duration strings like "5m", "1h".
timeout: Query timeout (not yet implemented).

Returns:
Query result (InstantVector, RangeVectorResult, or ScalarResult).
"""
with self._lock:
return self._get_engine().query(query, eval_time=time, step=step, timeout=timeout)
return self._get_engine().query(query, eval_time=time, step=step)

def query_range(
self,
query: str,
start: float | str | datetime,
end: float | str | datetime | None = None,
step: float | str | None = None,
timeout: float | str | None = None,
) -> MatrixResult:
"""Execute a range query.

Expand All @@ -272,13 +276,12 @@ def query_range(
start: Start timestamp (Unix seconds).
end: End timestamp (Unix seconds). Defaults to current time.
step: Query resolution step in seconds. Defaults to 5m (300s).
timeout: Query timeout (not yet implemented).

Returns:
MatrixResult containing series with multiple samples over time.
"""
with self._lock:
return self._get_engine().query_range(query, start=start, end=end, step=step, timeout=timeout)
return self._get_engine().query_range(query, start=start, end=end, step=step)

@staticmethod
def _build_time_filter(
Expand Down
2 changes: 2 additions & 0 deletions metricsqlite/engine/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
tokenize,
)
from metricsqlite.engine.query import QueryEngine
from metricsqlite.engine.sqlite import sqlite_regexp

__all__ = [
"BinaryExpr",
Expand All @@ -53,5 +54,6 @@
"TokenType",
"UnaryExpr",
"parse",
"sqlite_regexp",
"tokenize",
]
Loading