Metadata-Version: 2.5
Name: liteql
Version: 0.2.0
Summary: Lightweight SQL wrapper for SQLite and MySQL
Author-email: James <git@jamesisboredcoding.com>
License-Expression: MIT
Requires-Python: >=3.14
Provides-Extra: mysql
Requires-Dist: pymysql; extra == 'mysql'
Description-Content-Type: text/markdown

# LiteQL

A lightweight SQL wrapper for SQLite (MySQL planned). No dependencies.

```python
from liteql import LiteQL
from liteql.attributes import *
from liteql.operations import *

db = LiteQL("app.db")          # or "sqlite:///app.db", ":memory:"
db.connect()

users = db.create_table("users", {
    "id": [INT(), PRIMARY_KEY(), AUTO_INCREMENT()],
    "name": [VARCHAR(), NOTNULL()],
    "email": [VARCHAR(), NOTNULL(), UNIQUE()],
    "income": [INT()],
    "createdAt": [DATE(), DEFAULT(CURRENT_DATE())],
})

orders = db.create_table("orders", {
    "order_id": [INT(), PRIMARY_KEY()],
    "user_id": [INT(), FOREIGN_KEY(users, "id", on_delete="CASCADE")],
    "total": [FLOAT()],
})
```

Calling `create_table` on an existing table adds any new columns.

## Schema

| Datatypes | Attributes | Functions |
|---|---|---|
| `INT`, `VARCHAR`, `TEXT`, `FLOAT`, `BOOLEAN`, `BLOB`, `DATE`, `DATETIME` | `PRIMARY_KEY`, `FOREIGN_KEY`, `AUTO_INCREMENT`, `NOTNULL`, `UNIQUE`, `DEFAULT` | `CURRENT_DATE`, `CURRENT_TIME`, `CURRENT_TIMESTAMP` |

`DATE`, `DATETIME` and `BOOLEAN` columns come back as `date`, `datetime` and `bool`.

## Writing

```python
users.insert(name="John", email="john@mail.com", income=2000)
users.insert_many([{"name": "Jane", "email": "jane@mail.com"}, ...])
users.upsert(id=1, name="Johnny", email="john@mail.com")       # conflict on primary key
users.upsert(conflict="email", email="john@mail.com", name="J") # or on a UNIQUE column
users.update(conditions=[RAW(name="John")], income=2500)
users.delete(conditions=[RAW(name="John")])
users.drop()
```

`update` and `delete` refuse to touch every row unless `allow_all=True`.

## Reading

```python
users.find_one(name="John")
users.find_many(income=GT(2000), order=("income", "DESC"), limit=(10, 20))
users.find_many(select=["name"], email=ENDS_WITH("@gmail.com"))
users.find_many(conditions=[OR(name="John", income=GT(2000))])
users.find_one(name="John", includes=["orders"]).orders   # related rows

users.count(income=IS_NULL())
users.exists(name="John")
users.sum("income"); users.avg("income"); users.min("income"); users.max("income")
```

Operations: `EQ`, `NE`, `GT`, `GTE`, `LT`, `LTE`, `LIKE`, `CONTAINS`, `STARTS_WITH`,
`ENDS_WITH`, `BETWEEN`, `IN`, `NOT_IN`, `IS_NULL`, `IS_NOT_NULL`, `NOT`, `AND`, `OR`, `RAW`.

Results expose columns as attributes or keys: `user.name`, `user["name"]`.

## Transactions

```python
with db.transaction():
    users.insert(name="Bob", email="bob@mail.com")
    orders.insert(user_id=1, total=50)   # if anything raises, both are rolled back
```

## Errors

All errors derive from `LiteQLError`: `LiteQLConnectionError`, `LiteQLQueryError`
(and its subclass `LiteQLIntegrityError` for constraint violations),
`LiteQLOperationError`, `LiteQLSchemaError`, `LiteQLTransactionError`.

## Tests

```sh
PYTHONPATH=src python -m unittest discover tests -p "test_*.py"
```
