apexbase 1.35.0

High-performance HTAP embedded database with Rust core
# Core Concepts

This page gives you the mental model for ApexBase before you dive into the API reference.

## Database Root

An `ApexClient` opens a root directory:

```python
from apexbase import ApexClient

client = ApexClient("./data")
```

The root directory contains the default database and any named databases. ApexBase stores each table as a `.apex` file.

For an ephemeral database, pass the special path `":memory:"`:

```python
client = ApexClient(":memory:")
```

This is a true process-local storage backend, not a temporary directory.
Tables still use the normal SQL, DDL, DML, index, FTS, schema, and result
paths, but no database, catalog, WAL, delta, blob-sidecar, or index files are
created. Its contents disappear when the client is closed and are not shared
with another independently created in-memory client.

FTS behaves the same as on a filesystem database: `CREATE FTS INDEX` and
`init_fts()` both back-fill rows that already exist, whichever order you create
the index and write the rows, and the Python search API and SQL `MATCH()`
agree on which tables are enabled. `ANALYZE` and `REINDEX` also work, keeping
their statistics and postings in process memory.

## Databases

ApexBase supports multiple isolated databases under one root directory. The default database maps to the root directory. Named databases live in subdirectories.

```python
client.use_database("analytics")
client.use(database="analytics", table="events")

print(client.list_databases())
print(client.current_database)
```

SQL can refer to another database with `database.table` syntax:

```python
client.execute("""
    SELECT u.name, e.event
    FROM default.users u
    JOIN analytics.events e ON u.id = e.user_id
""")
```

## Tables

Tables are explicit. Create or select a table before using table-scoped methods such as `store()`, `retrieve_all()`, and `list_fields()`.

```python
client.create_table("users")
client.use_table("users")
```

You can also create tables with SQL:

```python
client.execute("CREATE TABLE IF NOT EXISTS users")
```

## Schemas

You may let ApexBase infer column types from the first write, or provide a schema up front for clearer contracts and faster bulk loading.

```python
client.create_table("orders", schema={
    "order_id": "int64",
    "customer": "string",
    "total": "float64",
    "paid": "bool",
})
```

## Query Execution And Visibility

SQL semantics do not depend on whether rows live in the persisted base file or
in delta/overlay state. Supported conjunctive filters can be translated into a
shared physical scan request; the backend either uses selective mmap access or
an authoritative merged read, then applies grouping, `HAVING`, ordering, and
limits in SQL order. Unsupported or inexact predicate forms fall back to the
general evaluator rather than returning approximate results.

For implementation details, see
[Scan & Physical Execution](SCAN_EXECUTION_ARCHITECTURE.md).

## Records And Columns

ApexBase accepts row-oriented dictionaries, lists of dictionaries, and columnar dictionaries. For bulk ingest, columnar data is usually the fastest path.

```python
client.store({"name": "Alice", "age": 30})

client.store([
    {"name": "Bob", "age": 25},
    {"name": "Charlie", "age": 35},
])

client.store({
    "name": ["Diana", "Eve"],
    "age": [28, 41],
})
```

Every stored row has an internal `_id`. SQL hides `_id` unless you request it explicitly.

## ResultView

Queries return a `ResultView`, which can convert to Python-native rows or columnar DataFrame formats.

```python
result = client.execute("SELECT * FROM users")

rows = result.to_dict()
pandas_df = result.to_pandas()
polars_df = result.to_polars()
arrow_table = result.to_arrow()
```

Use `ResultView` when you want to move smoothly between SQL, Python lists, Pandas, Polars, and PyArrow.

## Process-Local Default Connection

The module-level `apexbase.execute()` helper lazily creates one shared
in-memory client inside the current Python process:

```python
import apexbase

apexbase.execute("CREATE TABLE counters (name TEXT, value INT)")
apexbase.execute("INSERT INTO counters VALUES ('jobs', 3)")
value = apexbase.execute(
    "SELECT value FROM counters WHERE name = ?", ["jobs"]
).scalar()
```

Later module-level calls see the same tables. Use an explicit `ApexClient`
when you need lifecycle control, isolation, persistence, or more than one
database connection.

## Durability

Durability is configured when opening the client:

| Mode | Best for | Behavior |
| --- | --- | --- |
| `fast` | Local analytics, scratch data, benchmarks | Leaves writes in the OS page cache; no fsync |
| `safe` | Application data with balanced speed and safety | Fsyncs on explicit `flush()` / `close()` |
| `max` | Highest crash safety | Fsyncs every write |

```python
client = ApexClient("./data", durability="safe")
```

## Bounded Process Caches

ApexBase keeps several per-process caches bounded so long-running applications
have a predictable memory ceiling: parsed SQL statements, planner feedback
(256 query shapes per table across at most 256 tables), table statistics, CTE
batches, dictionary/null caches, and the client-side analytical result cache
(at most 64 results, each at most 4096 rows / 8 MiB). Caches are invalidated by
epoch and file mtime, so external writers never serve stale rows. See
[Resource Ownership](RESOURCE_OWNERSHIP.md) for the authoritative inventory.

## Interfaces

The same storage engine can be reached through several interfaces:

- Python API for embedded applications and notebooks.
- Rust embedded API for native Rust applications.
- PostgreSQL Wire server for SQL clients and database tools.
- Arrow Flight server for high-throughput columnar result streaming.

Start with the Python API unless you already know you need a wire protocol or Rust integration.