Metadata-Version: 2.4
Name: polnor-sqlalchemy
Version: 0.1.0
Summary: SQLAlchemy dialect for Polnor — connect ORM, Alembic, pandas, FastAPI, Superset to the Polnor lakehouse.
Author-email: Polnor <contact@polnor.net>
License: Apache-2.0
Project-URL: Homepage, https://polnor.net
Project-URL: Documentation, https://docs.polnor.net/sdk/python-sqlalchemy
Project-URL: Source, https://github.com/polnor/polnor
Keywords: polnor,sqlalchemy,lakehouse,iceberg,dialect
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Topic :: Database
Classifier: Topic :: Database :: Database Engines/Servers
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: polnor>=1.0
Requires-Dist: sqlalchemy>=2.0
Provides-Extra: test
Requires-Dist: pytest>=7; extra == "test"
Requires-Dist: responses>=0.24; extra == "test"
Requires-Dist: pandas>=2.0; extra == "test"

# polnor-sqlalchemy

SQLAlchemy dialect for [Polnor](https://polnor.net) — connect every
SQLAlchemy-aware tool (pandas, FastAPI, Alembic, Superset, Metabase,
ORM apps) to a Polnor SQL warehouse over a single URL.

## Install

```bash
pip install polnor-sqlalchemy
```

This installs `polnor` (the core SDK) and `sqlalchemy>=2.0` as
dependencies.

## Quick start

```python
from sqlalchemy import create_engine, text

engine = create_engine(
    "polnor://api.polnor.net/?token=<your-pat>&warehouse_id=<wh-id>"
)

with engine.connect() as conn:
    result = conn.execute(text("SELECT region, COUNT(*) c FROM demo.orders GROUP BY region"))
    for row in result:
        print(row)
```

## URL format

```
polnor://<host>[:<port>]/?token=<...>&warehouse_id=<...>[&workspace_slug=<...>]
```

- `host` — hostname of the Polnor API, e.g. `api.polnor.net`
- `token` — personal access token (also accepted as the URL password)
- `warehouse_id` — id of the SQL warehouse to run queries against
- `workspace_slug` (optional) — pin to a specific workspace when the
  token spans multiple

Inside a Polnor notebook or job, you can use the bare form
`polnor://` — the SDK auto-resolves credentials from env vars
injected by the runtime.

## Pandas

```python
import pandas as pd
df = pd.read_sql("SELECT * FROM demo.orders LIMIT 100", engine)
```

No more "Other DBAPI2 objects are not tested" warning — the dialect
is the supported integration point for pandas.

## Reflection

```python
from sqlalchemy import MetaData, Table

md = MetaData()
orders = Table("orders", md, autoload_with=engine, schema="demo")
print([c.name for c in orders.columns])
```

Reflection uses Polnor's catalog API (`polnor.tables`) to introspect
namespaces, table columns, and types. Iceberg has no FK/PK/index
metadata — these are returned empty.

## DDL — `CREATE TABLE ... USING iceberg`

```python
from sqlalchemy import Column, Integer, String, Table, MetaData

md = MetaData()
users = Table(
    "users", md,
    Column("id", Integer, nullable=False),
    Column("email", String),
    schema="demo",
)
md.create_all(engine)   # emits: CREATE TABLE demo.users (...) USING iceberg
```

The dialect appends `USING iceberg` so Polnor routes the DDL to the
Spark sidecar that can actually write Iceberg.

## Compatibility

- Python ≥ 3.9
- SQLAlchemy ≥ 2.0
- Polnor SDK ≥ 1.0

## Limitations (V1)

- Auto-commit only — no transactional rollback (Polnor warehouses run
  one statement per transaction at the engine level).
- No native FK/PK/index reflection (Iceberg doesn't have these).
- Async (`asyncio` + `create_async_engine`) not supported yet.

## Links

- Polnor: https://polnor.net
- Core SDK: https://pypi.org/project/polnor/
- Source / issues: https://github.com/polnor/polnor

## License

Apache-2.0.
