Metadata-Version: 2.4
Name: pure-hsqldb-client
Version: 0.3.0
Summary: Pure Python HSQLDB hsql:// client with no JVM, JDBC, or runtime jar dependency.
Author: pure-hsqldb maintainers
License-Expression: MIT
Keywords: hsqldb,hsql,database,pure-python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Topic :: Database
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# pure-hsqldb-client

`pure-hsqldb-client` is a pure Python HSQLDB wire-protocol client for HSQLDB 2.x `hsql://` server mode.

It opens a TCP socket and speaks the HSQLDB result/row protocol directly. Runtime use does not start a JVM, import JDBC bindings, shell out to Java, or require `hsqldb.jar`.

## What This Is Not

- Not a JDBC wrapper.
- Not a JVM bridge.
- Not an embedded HSQLDB engine.
- Not a claimed full clone of every HSQLDB client feature.

Compatibility is documented from the test matrix below. Features not covered by tests are listed as partial or unsupported.

## Install

From Git:

```bash
python -m pip install "git+https://github.com/<owner>/<repo>.git"
```

From PyPI after publication:

```bash
python -m pip install pure-hsqldb-client
```

With `uv` from Git:

```bash
uv add "git+https://github.com/<owner>/<repo>.git"
```

With `uv` after publication:

```bash
uv add pure-hsqldb-client
```

## Basic Usage

```python
import os

from pure_hsqldb import HSQLDB

url = "hsql://<host>:<port>/<database>"
user = os.environ["HSQLDB_USER"]
password = os.environ.get("HSQLDB_PASSWORD", "")

with HSQLDB.from_url(url, user=user, password=password) as db:
    rows = db.fetchall("VALUES 1")
    print(rows)
```

JDBC-form HSQLDB URLs are also accepted for compatibility:

```python
url = "jdbc:hsqldb:hsql://<host>:<port>/<database>"
```

Credentials in URLs are rejected. Pass them separately.

Database aliases parsed from URLs are normalized to lowercase before CONNECT. This matches common HSQLDB server alias configuration and avoids case-only URL mismatches.

## DB-API Style Usage

```python
import os

from pure_hsqldb.dbapi import connect

conn = connect(
    url="hsql://<host>:<port>/<database>",
    user=os.environ["HSQLDB_USER"],
    password=os.environ.get("HSQLDB_PASSWORD", ""),
)
try:
    cur = conn.cursor()
    cur.execute("VALUES CAST(? AS INTEGER)", [123])
    print(cur.fetchone())
finally:
    conn.close()
```

`paramstyle` is `qmark`. Parameterized execution uses server-side prepare/execute.

## CLI

Do not pass credentials directly as command-line arguments. Use an environment variable, stdin, or an interactive prompt.

```bash
export HSQLDB_URL='hsql://<host>:<port>/<database>'
export HSQLDB_USER='<username>'
export HSQLDB_PASSWORD='<password>'

pure-hsqldb \
  --url "$HSQLDB_URL" \
  --user "$HSQLDB_USER" \
  --password-env HSQLDB_PASSWORD \
  --sql 'VALUES 1' \
  --json
```

Interactive prompt:

```bash
pure-hsqldb \
  --url "$HSQLDB_URL" \
  --user "$HSQLDB_USER" \
  --prompt-password \
  --sql 'VALUES 1'
```

## Security Notes

- No JAR is bundled in the package.
- Runtime code does not import JPype, JayDeBeApi, JDBC, or Java subprocesses.
- CLI has no raw password argument.
- `repr(HSQLDB(...))` redacts the password field.
- URL credentials are rejected to avoid leaking sensitive values through logs and process lists.
- Tests use environment-provided integration credentials.

## Compatibility Matrix

| Area | Status | Evidence |
| --- | --- | --- |
| TCP connect and HSQLDB handshake | Tested | Integration server started from local `hsqldb.jar` |
| CONNECT / CONNECTACKNOWLEDGE | Tested | Login success and failure tests |
| Direct SQL execution | Tested | Scalar, DDL, INSERT, UPDATE, DELETE |
| Server-side prepare / execute | Tested | `?` parameter tests and prepared insert/query |
| SQL functions and procedures | Tested | `CREATE FUNCTION`, `CREATE PROCEDURE`, `CALL`, routine error path |
| DB-API cursor | Tested | `execute`, `executemany`, `fetchone`, `fetchmany`, `fetchall` |
| Result modes | Partial | Tested DATA, DATAROWS, UPDATECOUNT, ERROR; other modes parsed when known |
| Multi-block fetch | Tested | Fetch sizes 0, 1, small, larger-than-result |
| Transactions | Tested | Autocommit control, commit, rollback |
| Clean close | Tested indirectly | Context managers and integration teardown |
| Malformed payload handling | Unit tested | Short buffer and invalid modified UTF |
| Thread safety | Documented only | DB-API `threadsafety = 1`; share connections carefully |

## Supported Types

| HSQLDB type | Python representation | Status |
| --- | --- | --- |
| NULL | `None` | Unit tested |
| BOOLEAN | `bool` | Unit/integration tested |
| TINYINT, SMALLINT, INTEGER, BIGINT | `int` | Unit/integration tested |
| REAL, FLOAT, DOUBLE | `float` | Unit/integration tested |
| NUMERIC, DECIMAL | `decimal.Decimal` | Unit/integration tested |
| CHAR, VARCHAR, VARCHAR_IGNORECASE, national text types | `str` | Unit/integration tested for VARCHAR |
| BINARY, VARBINARY, LONGVARBINARY | `bytes` | Unit/integration tested for VARBINARY |
| BIT, BIT VARYING | `pure_hsqldb.types.BitString` | Unit/integration tested |
| DATE | `datetime.date` | Unit/integration tested |
| TIME | `datetime.time` | Unit/integration tested |
| TIME WITH TIME ZONE | timezone-aware `datetime.time` | Integration tested |
| TIMESTAMP | `datetime.datetime` | Unit/integration tested |
| TIMESTAMP WITH TIME ZONE | timezone-aware `datetime.datetime` | Integration tested |
| INTERVAL YEAR/MONTH/YEAR TO MONTH | `IntervalMonth` | Unit/integration tested |
| INTERVAL DAY through MINUTE TO SECOND | `IntervalSecond` | Unit/integration tested for DAY TO SECOND |
| ARRAY | `tuple` | Unit/integration tested for scalar arrays |
| BLOB/CLOB locators | `LobRef` | Parser support; not integration-proven |
| OTHER/JAVA_OBJECT | `bytes` or explicit unsupported path | Partial |
| UUID | `bytes` | Parser support; not integration-proven |

## Unsupported Or Partial

- HTTP transport and TLS transport are not implemented.
- Full LOB streaming operations are not implemented.
- Batch protocol modes are parsed, but DB-API `executemany` currently loops prepared executions.
- Nested arrays are not integration-proven.
- Generated keys are not exposed as a high-level API.
- Warnings are parsed as chained results but not promoted to a DB-API warning collection.
- Full metadata nullability/display-size semantics are not yet mapped.

## Development

```bash
python -m pip install -e .
python -m pytest
```

Integration tests need a local HSQLDB JAR and credentials supplied through the environment:

```bash
export HSQLDB_JAR='/path/to/hsqldb.jar'
export HSQLDB_TEST_USER='<username>'
export HSQLDB_TEST_PASSWORD='<password>'
python -m pytest -m integration
```

The JAR is only used to start a throwaway test server. It is not packaged.

External write smoke tests can be run against a server you control. They create temporary objects with random names and clean them up afterwards:

```bash
export HSQLDB_EXTERNAL_URL='hsql://<host>:<port>/<database>'
export HSQLDB_EXTERNAL_USER='<username>'
export HSQLDB_EXTERNAL_PASSWORD='<password>'
export HSQLDB_EXTERNAL_WRITE_TESTS=1
python -m pytest -m external
```

## Verification

```bash
./verify.sh
```

The script runs compile checks, unit tests, security grep gates, build, wheel content inspection, clean-venv import smoke, and CLI smoke.

## Publishing With uv

```bash
uv build
uv publish
```

Before publishing, run `./verify.sh` and inspect `dist/`.

## License

MIT
