Skip to content

feat(postgres): dialecto PostgreSQL + connect(dsn) (Fase 2)#8

Merged
ShibaRoPinoo merged 1 commit into
feature/fase-1-5-ormfrom
feature/fase-2-postgres
May 18, 2026
Merged

feat(postgres): dialecto PostgreSQL + connect(dsn) (Fase 2)#8
ShibaRoPinoo merged 1 commit into
feature/fase-1-5-ormfrom
feature/fase-2-postgres

Conversation

@ShibaRoPinoo
Copy link
Copy Markdown
Owner

Resumen

Segundo dialecto sobre la estructura sentada en Fase 0. Sólo añade; el core y el ORM no se mueven.

```python
import shiba

cx = shiba.connect("postgres://user:pass@host/db")
cx.create_table("users")
.increments("id", primary_key=True)
.json("settings")
.build()

→ CREATE TABLE "users" ("id" INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,

"settings" JSONB, ...)

cx.table("users").upsert({"id": 1, "name": "X"}, on=["id"])

→ ON CONFLICT ("id") DO UPDATE SET ...

```

Puntos clave

  • Driver: psycopg v3 con `dict_row`. Declarado como dep opcional (`shiba_mysql[postgres]`) y como dev. El paquete `shiba.dialects.postgres` se importa OK sin psycopg instalado; el error sólo sale al construir el Database.
  • Quoting: doble comilla; identificadores citados por dialecto (mismo validate_identifier).
  • Tipos: `DATETIME → TIMESTAMP`, `JSON → JSONB`, `BLOB → BYTEA`, `DOUBLE → DOUBLE PRECISION`.
  • Auto-increment: `INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY` (no `SERIAL`).
  • Upsert: `ON CONFLICT (cols) DO UPDATE SET col = EXCLUDED.col`. `on=[...]` obligatorio en Postgres, opcional en MySQL.

Cambios mínimos al contrato

  • `Dialect.compile_upsert_update(update, conflict_columns=None)`.
  • `Dialect.compile_auto_increment_pk(quoted)`.
  • `QueryBuilder.upsert(..., on=[...])`.
  • `ShibaConnection` acepta `db=`/`dialect=` inyectados.
  • `shiba.connect(dsn)` factory.

Test plan

  • 14 tests nuevos sin DB cubren quoting, injection rejection, DDL con IDENTITY, mapping JSONB/BYTEA/TIMESTAMP, upsert ON CONFLICT con/sin `on`, factory resolviendo MySQL/Postgres/postgresql:// y rechazo de schemes no soportados
  • 114/114 total. `ruff` y `mypy --strict` limpios
  • Smoke contra Postgres real → siguiente PR (Testcontainers)

Apilado sobre #7. Mergear después de #5, #6, #7.

🤖 Generated with Claude Code

Segundo dialecto. La estructura preparada en Fase 0 paga su precio:
sólo se añade — nada del core ni del ORM se mueve.

Driver
- `shiba/dialects/postgres/driver.py` con psycopg v3, `dict_row`,
  autocommit, traducción de excepciones nativas a códigos Shiba.
- `psycopg[binary]` declarado como dep **opcional** en
  `[project.optional-dependencies] postgres`; también en `dev` para CI.
- Import perezoso: el paquete `shiba.dialects.postgres` se puede
  importar sin tener psycopg instalado; el error sale al construir
  `Database`.

Dialect
- `PostgresDialect`: comillas dobles para identificadores, placeholder
  `%s`, `ON CONFLICT (...) DO UPDATE SET col = EXCLUDED.col` para
  upsert, `INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY` para
  auto-increment.
- Mapeo de tipos: `DATETIME` → `TIMESTAMP`, `JSON` → `JSONB`,
  `BLOB` → `BYTEA`, `DOUBLE` → `DOUBLE PRECISION`.

Cambios menores en el contrato
- `Dialect.compile_upsert_update(update_columns, conflict_columns=None)`.
- Nuevo `Dialect.compile_auto_increment_pk(quoted_col)`.
- `TableBuilder.increments(pk=True)` delega al dialect.
- `QueryBuilder.upsert(..., on=[...])` para Postgres; MySQL lo ignora.

Factory
- `shiba.connect("mysql://...")`/`shiba.connect("postgres://...")` parsea
  el DSN y devuelve la `ShibaConnection` adecuada.
- `ShibaConnection` ahora acepta `db=`/`dialect=` ya construidos, sin
  romper la firma legacy `(host, port, user, password)`.

Tests
- 14 nuevos (114/114 total) cubren quoting con doble comilla,
  rechazo de injection, DDL con IDENTITY, mapping de tipos JSONB/BYTEA,
  upsert ON CONFLICT (con y sin `on`), factory resolviendo MySQL vs
  Postgres vs alias `postgresql://`, y rechazo de schemes no soportados.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ShibaRoPinoo ShibaRoPinoo merged commit d8e56af into feature/fase-1-5-orm May 18, 2026
0 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant