mtpk-postgres 0.1.0__tar.gz → 0.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {mtpk_postgres-0.1.0 → mtpk_postgres-0.1.2}/PKG-INFO +21 -1
- mtpk_postgres-0.1.2/README.md +47 -0
- {mtpk_postgres-0.1.0 → mtpk_postgres-0.1.2}/mtpk_postgres/__init__.py +1 -1
- {mtpk_postgres-0.1.0 → mtpk_postgres-0.1.2}/mtpk_postgres/core_sync.py +101 -15
- {mtpk_postgres-0.1.0 → mtpk_postgres-0.1.2}/mtpk_postgres.egg-info/PKG-INFO +21 -1
- {mtpk_postgres-0.1.0 → mtpk_postgres-0.1.2}/pyproject.toml +1 -1
- mtpk_postgres-0.1.0/README.md +0 -27
- {mtpk_postgres-0.1.0 → mtpk_postgres-0.1.2}/LICENSE +0 -0
- {mtpk_postgres-0.1.0 → mtpk_postgres-0.1.2}/mtpk_postgres/async_adapter.py +0 -0
- {mtpk_postgres-0.1.0 → mtpk_postgres-0.1.2}/mtpk_postgres/crud.py +0 -0
- {mtpk_postgres-0.1.0 → mtpk_postgres-0.1.2}/mtpk_postgres/excepciones.py +0 -0
- {mtpk_postgres-0.1.0 → mtpk_postgres-0.1.2}/mtpk_postgres/interface.py +0 -0
- {mtpk_postgres-0.1.0 → mtpk_postgres-0.1.2}/mtpk_postgres/utils.py +0 -0
- {mtpk_postgres-0.1.0 → mtpk_postgres-0.1.2}/mtpk_postgres.egg-info/SOURCES.txt +0 -0
- {mtpk_postgres-0.1.0 → mtpk_postgres-0.1.2}/mtpk_postgres.egg-info/dependency_links.txt +0 -0
- {mtpk_postgres-0.1.0 → mtpk_postgres-0.1.2}/mtpk_postgres.egg-info/requires.txt +0 -0
- {mtpk_postgres-0.1.0 → mtpk_postgres-0.1.2}/mtpk_postgres.egg-info/top_level.txt +0 -0
- {mtpk_postgres-0.1.0 → mtpk_postgres-0.1.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mtpk_postgres
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: Librería para sincronización estructural de bases de datos Postgres con modelos Python (adaptación de mtpk_mariadb)
|
|
5
5
|
Author-email: José Jesús Andrés Zambrana <jjandres@multiplika.es>
|
|
6
6
|
License: MIT
|
|
@@ -45,3 +45,23 @@ pip install -e .
|
|
|
45
45
|
reportan como `ENUM` (Postgres no permite distinguirlos después del hecho).
|
|
46
46
|
- A diferencia de MariaDB, el DDL en Postgres es transaccional: los métodos de `ManagerDB` confirman (`COMMIT`)
|
|
47
47
|
explícitamente tras crear tablas, triggers y procedimientos.
|
|
48
|
+
- Columnas `VECTOR(n)` (extensión [pgvector](https://github.com/pgvector/pgvector), hay que crearla antes con
|
|
49
|
+
`CREATE EXTENSION IF NOT EXISTS vector;`): `Columna(tipo="VECTOR", longitud=n)`. Es una extensión propia de este
|
|
50
|
+
paquete, no existe en `mtpk-mariadb`.
|
|
51
|
+
- Índices con método (`GIN`, `GIST`, `HNSW`/`IVFFlat` de pgvector, etc.): `Index(columnas=[...], metodo="hnsw",
|
|
52
|
+
opclass="vector_cosine_ops", with_opciones="m = 16, ef_construction = 64")`. `metodo` es incompatible con `unico`
|
|
53
|
+
(Postgres no permite índices UNIQUE con esos métodos).
|
|
54
|
+
|
|
55
|
+
## Historial
|
|
56
|
+
|
|
57
|
+
- **0.1.2**: soporte nativo para columnas `VECTOR(n)` (pgvector) e índices con método (`USING gin/hnsw/...`,
|
|
58
|
+
`opclass`, `WITH (...)`) en `Index`. La introspección (`obtener_columnas_postgres`) ahora reconoce las columnas
|
|
59
|
+
`vector` por su `udt_name` y reconstruye su dimensión leyendo el catálogo (`pg_attribute`/`format_type`); sin esto,
|
|
60
|
+
`data_type` llegaba como `'USER-DEFINED'` y `comparar_generar_alter` proponía un `ALTER COLUMN` en cada ejecución
|
|
61
|
+
aunque la columna no hubiera cambiado.
|
|
62
|
+
- **0.1.1**: corrige un bug bloqueante — `Tabla` no enlazaba `indice.tabla`/`fk.tabla`/`columna.tabla` cuando
|
|
63
|
+
`columnas`/`indices`/`foreign_keys` se pasaban directamente por el constructor (solo lo hacían `add_index()` y
|
|
64
|
+
compañía). Como `Index.to_sql()` necesita esa referencia para generar el `CREATE INDEX`, cualquier tabla definida
|
|
65
|
+
de la forma normal y con al menos un índice fallaba con `ValueError` al sincronizar el esquema. Se añadió
|
|
66
|
+
`Tabla.__post_init__` para enlazarlos siempre.
|
|
67
|
+
- **0.1.0**: primera versión publicada.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# mtpk_postgres
|
|
2
|
+
|
|
3
|
+
Adaptación a Postgres (vía [psycopg 3](https://www.psycopg.org/psycopg3/)) de `mtpk_mariadb`: define tus tablas con
|
|
4
|
+
dataclasses (`Tabla`, `Columna`, `ForeignKey`, `Index`), sincroniza la estructura de la base de datos automáticamente
|
|
5
|
+
(CREATE/ALTER TABLE, índices, claves foráneas) y usa `AsyncCrudBase` para CRUD asíncrono.
|
|
6
|
+
|
|
7
|
+
No modifica ni depende de `mtpk_mariadb`; es un paquete independiente. Ver `docs/mtpk-postgres-adaptacion.md` en el
|
|
8
|
+
repo para el detalle de las diferencias frente a la versión MariaDB.
|
|
9
|
+
|
|
10
|
+
## Instalación
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
pip install -e .
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Notas de la adaptación
|
|
17
|
+
|
|
18
|
+
- Identificadores citados con `"comillas dobles"` en vez de backticks.
|
|
19
|
+
- `auto_increment=True` genera `GENERATED ALWAYS AS IDENTITY` en vez de `AUTO_INCREMENT`; como Postgres no tiene
|
|
20
|
+
`lastrowid`, las inserciones vía `AsyncCrudBase.insert()`/`insertar()` añaden `RETURNING id`.
|
|
21
|
+
- `Tabla.to_sql()` ya no incluye índices ni comentarios inline (Postgres no lo permite dentro de `CREATE TABLE`):
|
|
22
|
+
usa `Tabla.to_sql_indices()` y `Tabla.to_sql_comentarios()` para las sentencias `CREATE INDEX` y `COMMENT ON`
|
|
23
|
+
correspondientes. `ManagerDB` ya las ejecuta automáticamente al crear tablas.
|
|
24
|
+
- `ENUM`/`SET` se traducen a `TEXT` + `CHECK`; al reconstruir la estructura desde la base de datos ambos se
|
|
25
|
+
reportan como `ENUM` (Postgres no permite distinguirlos después del hecho).
|
|
26
|
+
- A diferencia de MariaDB, el DDL en Postgres es transaccional: los métodos de `ManagerDB` confirman (`COMMIT`)
|
|
27
|
+
explícitamente tras crear tablas, triggers y procedimientos.
|
|
28
|
+
- Columnas `VECTOR(n)` (extensión [pgvector](https://github.com/pgvector/pgvector), hay que crearla antes con
|
|
29
|
+
`CREATE EXTENSION IF NOT EXISTS vector;`): `Columna(tipo="VECTOR", longitud=n)`. Es una extensión propia de este
|
|
30
|
+
paquete, no existe en `mtpk-mariadb`.
|
|
31
|
+
- Índices con método (`GIN`, `GIST`, `HNSW`/`IVFFlat` de pgvector, etc.): `Index(columnas=[...], metodo="hnsw",
|
|
32
|
+
opclass="vector_cosine_ops", with_opciones="m = 16, ef_construction = 64")`. `metodo` es incompatible con `unico`
|
|
33
|
+
(Postgres no permite índices UNIQUE con esos métodos).
|
|
34
|
+
|
|
35
|
+
## Historial
|
|
36
|
+
|
|
37
|
+
- **0.1.2**: soporte nativo para columnas `VECTOR(n)` (pgvector) e índices con método (`USING gin/hnsw/...`,
|
|
38
|
+
`opclass`, `WITH (...)`) en `Index`. La introspección (`obtener_columnas_postgres`) ahora reconoce las columnas
|
|
39
|
+
`vector` por su `udt_name` y reconstruye su dimensión leyendo el catálogo (`pg_attribute`/`format_type`); sin esto,
|
|
40
|
+
`data_type` llegaba como `'USER-DEFINED'` y `comparar_generar_alter` proponía un `ALTER COLUMN` en cada ejecución
|
|
41
|
+
aunque la columna no hubiera cambiado.
|
|
42
|
+
- **0.1.1**: corrige un bug bloqueante — `Tabla` no enlazaba `indice.tabla`/`fk.tabla`/`columna.tabla` cuando
|
|
43
|
+
`columnas`/`indices`/`foreign_keys` se pasaban directamente por el constructor (solo lo hacían `add_index()` y
|
|
44
|
+
compañía). Como `Index.to_sql()` necesita esa referencia para generar el `CREATE INDEX`, cualquier tabla definida
|
|
45
|
+
de la forma normal y con al menos un índice fallaba con `ValueError` al sincronizar el esquema. Se añadió
|
|
46
|
+
`Tabla.__post_init__` para enlazarlos siempre.
|
|
47
|
+
- **0.1.0**: primera versión publicada.
|
|
@@ -62,12 +62,14 @@ class Columna:
|
|
|
62
62
|
tipo: Literal[
|
|
63
63
|
"TINYINT", "SMALLINT", "MEDIUMINT", "INT", "INTEGER", "BIGINT", "DECIMAL", "NUMERIC", "FLOAT", "DOUBLE",
|
|
64
64
|
"CHAR", "VARCHAR", "TEXT", "TINYTEXT", "MEDIUMTEXT", "LONGTEXT", "BINARY", "VARBINARY", "BLOB", "TINYBLOB", "MEDIUMBLOB", "LONGBLOB",
|
|
65
|
-
"DATE", "TIME", "YEAR", "DATETIME", "TIMESTAMP", "ENUM", "SET", "BOOLEAN", "GEOMETRY", "POINT", "LINESTRING", "POLYGON"
|
|
65
|
+
"DATE", "TIME", "YEAR", "DATETIME", "TIMESTAMP", "ENUM", "SET", "BOOLEAN", "GEOMETRY", "POINT", "LINESTRING", "POLYGON",
|
|
66
|
+
"VECTOR"
|
|
66
67
|
]
|
|
67
|
-
"""Tipo de dato del modelo (mismo vocabulario que mtpk-mariadb, traducido a Postgres en to_sql())
|
|
68
|
+
"""Tipo de dato del modelo (mismo vocabulario que mtpk-mariadb, traducido a Postgres en to_sql());
|
|
69
|
+
VECTOR es una extensión propia de este paquete (no existe en mtpk-mariadb) para columnas `pgvector`."""
|
|
68
70
|
|
|
69
71
|
longitud: Optional[int] = None
|
|
70
|
-
"""Longitud para tipos como VARCHAR(n), CHAR(n)."""
|
|
72
|
+
"""Longitud para tipos como VARCHAR(n), CHAR(n). Para VECTOR, es el número de dimensiones: VECTOR(n)."""
|
|
71
73
|
|
|
72
74
|
precision: Optional[int] = None
|
|
73
75
|
"""Precisión para DECIMAL(p, s), etc."""
|
|
@@ -123,6 +125,7 @@ class Columna:
|
|
|
123
125
|
"YEAR": "SMALLINT",
|
|
124
126
|
"BOOLEAN": "BOOLEAN",
|
|
125
127
|
"ENUM": "TEXT", "SET": "TEXT",
|
|
128
|
+
"VECTOR": "VECTOR",
|
|
126
129
|
}
|
|
127
130
|
_SIN_SOPORTE = {"GEOMETRY", "POINT", "LINESTRING", "POLYGON"}
|
|
128
131
|
|
|
@@ -135,6 +138,13 @@ class Columna:
|
|
|
135
138
|
)
|
|
136
139
|
base = self._MAPA_TIPOS.get(tipo_upper, tipo_upper)
|
|
137
140
|
|
|
141
|
+
if base == "VECTOR":
|
|
142
|
+
if self.longitud is None:
|
|
143
|
+
raise ValueError(
|
|
144
|
+
f"La columna '{self.nombre}' es VECTOR pero no tiene 'longitud' (número de dimensiones)."
|
|
145
|
+
)
|
|
146
|
+
# Requiere la extensión pgvector: CREATE EXTENSION IF NOT EXISTS vector;
|
|
147
|
+
return f"{base}({self.longitud})"
|
|
138
148
|
if base in {"VARCHAR", "CHAR"}:
|
|
139
149
|
return f"{base}({self.longitud})"
|
|
140
150
|
if base in {"DECIMAL", "NUMERIC"}:
|
|
@@ -230,12 +240,20 @@ class Index:
|
|
|
230
240
|
- Atributos:
|
|
231
241
|
- `columnas` (List[str]): Lista de nombres de columnas incluidas en el índice. Pueden ser una o varias (índice compuesto).
|
|
232
242
|
- `nombre` (str, opcional): Nombre personalizado del índice. Si no se indica, se generará automáticamente combinando los nombres de las columnas.
|
|
233
|
-
- `unico` (bool): Indica si el índice es único (`UNIQUE`). Por defecto es `False`.
|
|
243
|
+
- `unico` (bool): Indica si el índice es único (`UNIQUE`). Por defecto es `False`. Incompatible con `metodo`.
|
|
244
|
+
- `metodo` (str, opcional): Método de acceso del índice (`USING <metodo>`), p.ej. `"gin"`, `"gist"` o `"hnsw"`/`"ivfflat"`
|
|
245
|
+
(estos dos últimos requieren la extensión `pgvector`). Si no se indica, se usa el btree normal de Postgres.
|
|
246
|
+
- `opclass` (str, opcional): Clase de operador a aplicar a cada columna del índice, p.ej. `"vector_cosine_ops"`
|
|
247
|
+
para un HNSW/IVFFlat sobre una columna `VECTOR`, o `"gin_trgm_ops"` para búsqueda por trigramas.
|
|
248
|
+
- `with_opciones` (str, opcional): Contenido crudo de la cláusula `WITH (...)`, p.ej. `"m = 16, ef_construction = 64"` (HNSW).
|
|
234
249
|
- `tabla` (Tabla, opcional): Referencia a la tabla que contiene este índice. Se asigna automáticamente al añadir el índice a la tabla.
|
|
235
250
|
"""
|
|
236
251
|
columnas: List[str]
|
|
237
252
|
nombre: Optional[str] = None
|
|
238
253
|
unico: bool = False
|
|
254
|
+
metodo: Optional[str] = None
|
|
255
|
+
opclass: Optional[str] = None
|
|
256
|
+
with_opciones: Optional[str] = None
|
|
239
257
|
tabla: Optional["Tabla"] = field(default=None, repr=False)
|
|
240
258
|
|
|
241
259
|
def to_sql(self) -> str:
|
|
@@ -243,8 +261,25 @@ class Index:
|
|
|
243
261
|
raise ValueError(
|
|
244
262
|
f"El índice sobre {self.columnas} no está asociado a ninguna tabla; no se puede generar el CREATE INDEX."
|
|
245
263
|
)
|
|
246
|
-
|
|
264
|
+
if self.metodo and self.unico:
|
|
265
|
+
raise ValueError(
|
|
266
|
+
f"El índice sobre {self.columnas} no puede ser UNIQUE y usar 'metodo' ({self.metodo}) a la vez "
|
|
267
|
+
"(GIN/HNSW/IVFFlat no admiten UNIQUE en Postgres)."
|
|
268
|
+
)
|
|
269
|
+
|
|
247
270
|
nombre = self.nombre or f"{'_'.join(self.columnas)}_{'uk' if self.unico else 'idx'}"
|
|
271
|
+
|
|
272
|
+
if self.metodo:
|
|
273
|
+
cols = ", ".join(
|
|
274
|
+
f'"{c}" {self.opclass}' if self.opclass else f'"{c}"'
|
|
275
|
+
for c in self.columnas
|
|
276
|
+
)
|
|
277
|
+
sql = f'CREATE INDEX IF NOT EXISTS "{nombre}" ON "{self.tabla.nombre}" USING {self.metodo} ({cols})'
|
|
278
|
+
if self.with_opciones:
|
|
279
|
+
sql += f" WITH ({self.with_opciones})"
|
|
280
|
+
return sql
|
|
281
|
+
|
|
282
|
+
cols = ", ".join(f'"{c}"' for c in self.columnas)
|
|
248
283
|
tipo_sql = "UNIQUE INDEX" if self.unico else "INDEX"
|
|
249
284
|
return f'CREATE {tipo_sql} IF NOT EXISTS "{nombre}" ON "{self.tabla.nombre}" ({cols})'
|
|
250
285
|
|
|
@@ -283,6 +318,18 @@ class Tabla:
|
|
|
283
318
|
database: Optional["Database"] = None
|
|
284
319
|
db_name: Optional[str] = None # ← nuevo campo, sin conflicto con Database
|
|
285
320
|
|
|
321
|
+
def __post_init__(self):
|
|
322
|
+
# Index.to_sql() necesita conocer su tabla (Postgres genera el CREATE INDEX aparte,
|
|
323
|
+
# no como cláusula del CREATE TABLE) — sin esto, pasar columnas/indices/foreign_keys
|
|
324
|
+
# por el constructor (en vez de add_columna/add_index/add_foreign_key) los deja sin
|
|
325
|
+
# enlazar a self y provoca un ValueError en cuanto la tabla tiene algún índice.
|
|
326
|
+
for col in self.columnas:
|
|
327
|
+
col.tabla = self
|
|
328
|
+
for idx in self.indices:
|
|
329
|
+
idx.tabla = self
|
|
330
|
+
for fk in self.foreign_keys:
|
|
331
|
+
fk.tabla = self
|
|
332
|
+
|
|
286
333
|
def add_columna(self, columna: Columna):
|
|
287
334
|
"""
|
|
288
335
|
Añade una columna a la tabla si no existe ya.
|
|
@@ -452,7 +499,7 @@ class Tabla:
|
|
|
452
499
|
cursor.execute(
|
|
453
500
|
"""
|
|
454
501
|
SELECT
|
|
455
|
-
c.column_name, c.data_type, c.character_maximum_length,
|
|
502
|
+
c.column_name, c.data_type, c.udt_name, c.character_maximum_length,
|
|
456
503
|
c.numeric_precision, c.numeric_scale, c.is_nullable,
|
|
457
504
|
c.column_default, c.is_identity,
|
|
458
505
|
COALESCE(pk.es_pk, false) AS es_pk,
|
|
@@ -484,21 +531,33 @@ class Tabla:
|
|
|
484
531
|
filas = cursor.fetchall()
|
|
485
532
|
|
|
486
533
|
checks_por_columna = self._obtener_opciones_check(conexion)
|
|
534
|
+
dimensiones_vector = self._obtener_dimensiones_vector(conexion)
|
|
487
535
|
|
|
488
536
|
for fila in filas:
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
#
|
|
494
|
-
#
|
|
495
|
-
|
|
496
|
-
|
|
537
|
+
longitud = fila["character_maximum_length"]
|
|
538
|
+
|
|
539
|
+
if fila["udt_name"] == "vector":
|
|
540
|
+
# information_schema no expone el nº de dimensiones de un `vector(n)` (tipo de
|
|
541
|
+
# la extensión pgvector): data_type viene como 'USER-DEFINED' y
|
|
542
|
+
# character_maximum_length es NULL. Sin este caso especial, tipo_base quedaría
|
|
543
|
+
# como "USER-DEFINED" y nunca igualaría al "VECTOR" del modelo, provocando un
|
|
544
|
+
# ALTER COLUMN espurio en cada ejecución.
|
|
545
|
+
tipo_base = "VECTOR"
|
|
546
|
+
longitud = dimensiones_vector.get(fila["column_name"])
|
|
547
|
+
enum_opciones = None
|
|
548
|
+
else:
|
|
549
|
+
tipo_base = self._PG_A_MODELO.get(fila["data_type"], fila["data_type"].upper())
|
|
550
|
+
enum_opciones = None
|
|
551
|
+
if tipo_base == "TEXT" and fila["column_name"] in checks_por_columna:
|
|
552
|
+
# Postgres no distingue ENUM de SET tras reconstruir desde el CHECK: ambos
|
|
553
|
+
# se guardan como TEXT + CHECK (ver Columna.to_sql), así que se asume ENUM.
|
|
554
|
+
tipo_base = "ENUM"
|
|
555
|
+
enum_opciones = checks_por_columna[fila["column_name"]]
|
|
497
556
|
|
|
498
557
|
col = Columna(
|
|
499
558
|
nombre=fila["column_name"],
|
|
500
559
|
tipo=tipo_base,
|
|
501
|
-
longitud=
|
|
560
|
+
longitud=longitud,
|
|
502
561
|
precision=fila["numeric_precision"],
|
|
503
562
|
escala=fila["numeric_scale"],
|
|
504
563
|
not_null=(fila["is_nullable"] == "NO"),
|
|
@@ -512,6 +571,33 @@ class Tabla:
|
|
|
512
571
|
|
|
513
572
|
return columnas
|
|
514
573
|
|
|
574
|
+
def _obtener_dimensiones_vector(self, conexion) -> Dict[str, int]:
|
|
575
|
+
"""
|
|
576
|
+
Devuelve {columna: dimensiones} para las columnas `vector(n)` (extensión pgvector) de
|
|
577
|
+
esta tabla, leyendo el modificador de tipo real desde los catálogos (atttypmod vía
|
|
578
|
+
format_type), ya que information_schema no lo expone para tipos definidos por extensión.
|
|
579
|
+
"""
|
|
580
|
+
resultado: Dict[str, int] = {}
|
|
581
|
+
with conexion.cursor(row_factory=dict_row) as cursor:
|
|
582
|
+
cursor.execute(
|
|
583
|
+
"""
|
|
584
|
+
SELECT a.attname AS columna,
|
|
585
|
+
pg_catalog.format_type(a.atttypid, a.atttypmod) AS tipo_completo
|
|
586
|
+
FROM pg_attribute a
|
|
587
|
+
JOIN pg_class t ON t.oid = a.attrelid
|
|
588
|
+
JOIN pg_namespace n ON n.oid = t.relnamespace
|
|
589
|
+
JOIN pg_type ty ON ty.oid = a.atttypid
|
|
590
|
+
WHERE t.relname = %s AND n.nspname = 'public'
|
|
591
|
+
AND ty.typname = 'vector' AND a.attnum > 0 AND NOT a.attisdropped
|
|
592
|
+
""",
|
|
593
|
+
(self.nombre,)
|
|
594
|
+
)
|
|
595
|
+
for fila in cursor.fetchall():
|
|
596
|
+
m = re.match(r"vector\((\d+)\)", fila["tipo_completo"] or "", re.IGNORECASE)
|
|
597
|
+
if m:
|
|
598
|
+
resultado[fila["columna"]] = int(m.group(1))
|
|
599
|
+
return resultado
|
|
600
|
+
|
|
515
601
|
def _obtener_opciones_check(self, conexion) -> Dict[str, List[str]]:
|
|
516
602
|
"""
|
|
517
603
|
Reconstruye, de forma best-effort, las opciones de las columnas ENUM/SET (TEXT + CHECK)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mtpk_postgres
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: Librería para sincronización estructural de bases de datos Postgres con modelos Python (adaptación de mtpk_mariadb)
|
|
5
5
|
Author-email: José Jesús Andrés Zambrana <jjandres@multiplika.es>
|
|
6
6
|
License: MIT
|
|
@@ -45,3 +45,23 @@ pip install -e .
|
|
|
45
45
|
reportan como `ENUM` (Postgres no permite distinguirlos después del hecho).
|
|
46
46
|
- A diferencia de MariaDB, el DDL en Postgres es transaccional: los métodos de `ManagerDB` confirman (`COMMIT`)
|
|
47
47
|
explícitamente tras crear tablas, triggers y procedimientos.
|
|
48
|
+
- Columnas `VECTOR(n)` (extensión [pgvector](https://github.com/pgvector/pgvector), hay que crearla antes con
|
|
49
|
+
`CREATE EXTENSION IF NOT EXISTS vector;`): `Columna(tipo="VECTOR", longitud=n)`. Es una extensión propia de este
|
|
50
|
+
paquete, no existe en `mtpk-mariadb`.
|
|
51
|
+
- Índices con método (`GIN`, `GIST`, `HNSW`/`IVFFlat` de pgvector, etc.): `Index(columnas=[...], metodo="hnsw",
|
|
52
|
+
opclass="vector_cosine_ops", with_opciones="m = 16, ef_construction = 64")`. `metodo` es incompatible con `unico`
|
|
53
|
+
(Postgres no permite índices UNIQUE con esos métodos).
|
|
54
|
+
|
|
55
|
+
## Historial
|
|
56
|
+
|
|
57
|
+
- **0.1.2**: soporte nativo para columnas `VECTOR(n)` (pgvector) e índices con método (`USING gin/hnsw/...`,
|
|
58
|
+
`opclass`, `WITH (...)`) en `Index`. La introspección (`obtener_columnas_postgres`) ahora reconoce las columnas
|
|
59
|
+
`vector` por su `udt_name` y reconstruye su dimensión leyendo el catálogo (`pg_attribute`/`format_type`); sin esto,
|
|
60
|
+
`data_type` llegaba como `'USER-DEFINED'` y `comparar_generar_alter` proponía un `ALTER COLUMN` en cada ejecución
|
|
61
|
+
aunque la columna no hubiera cambiado.
|
|
62
|
+
- **0.1.1**: corrige un bug bloqueante — `Tabla` no enlazaba `indice.tabla`/`fk.tabla`/`columna.tabla` cuando
|
|
63
|
+
`columnas`/`indices`/`foreign_keys` se pasaban directamente por el constructor (solo lo hacían `add_index()` y
|
|
64
|
+
compañía). Como `Index.to_sql()` necesita esa referencia para generar el `CREATE INDEX`, cualquier tabla definida
|
|
65
|
+
de la forma normal y con al menos un índice fallaba con `ValueError` al sincronizar el esquema. Se añadió
|
|
66
|
+
`Tabla.__post_init__` para enlazarlos siempre.
|
|
67
|
+
- **0.1.0**: primera versión publicada.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "mtpk_postgres"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.2"
|
|
8
8
|
description = "Librería para sincronización estructural de bases de datos Postgres con modelos Python (adaptación de mtpk_mariadb)"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
mtpk_postgres-0.1.0/README.md
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
# mtpk_postgres
|
|
2
|
-
|
|
3
|
-
Adaptación a Postgres (vía [psycopg 3](https://www.psycopg.org/psycopg3/)) de `mtpk_mariadb`: define tus tablas con
|
|
4
|
-
dataclasses (`Tabla`, `Columna`, `ForeignKey`, `Index`), sincroniza la estructura de la base de datos automáticamente
|
|
5
|
-
(CREATE/ALTER TABLE, índices, claves foráneas) y usa `AsyncCrudBase` para CRUD asíncrono.
|
|
6
|
-
|
|
7
|
-
No modifica ni depende de `mtpk_mariadb`; es un paquete independiente. Ver `docs/mtpk-postgres-adaptacion.md` en el
|
|
8
|
-
repo para el detalle de las diferencias frente a la versión MariaDB.
|
|
9
|
-
|
|
10
|
-
## Instalación
|
|
11
|
-
|
|
12
|
-
```
|
|
13
|
-
pip install -e .
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
## Notas de la adaptación
|
|
17
|
-
|
|
18
|
-
- Identificadores citados con `"comillas dobles"` en vez de backticks.
|
|
19
|
-
- `auto_increment=True` genera `GENERATED ALWAYS AS IDENTITY` en vez de `AUTO_INCREMENT`; como Postgres no tiene
|
|
20
|
-
`lastrowid`, las inserciones vía `AsyncCrudBase.insert()`/`insertar()` añaden `RETURNING id`.
|
|
21
|
-
- `Tabla.to_sql()` ya no incluye índices ni comentarios inline (Postgres no lo permite dentro de `CREATE TABLE`):
|
|
22
|
-
usa `Tabla.to_sql_indices()` y `Tabla.to_sql_comentarios()` para las sentencias `CREATE INDEX` y `COMMENT ON`
|
|
23
|
-
correspondientes. `ManagerDB` ya las ejecuta automáticamente al crear tablas.
|
|
24
|
-
- `ENUM`/`SET` se traducen a `TEXT` + `CHECK`; al reconstruir la estructura desde la base de datos ambos se
|
|
25
|
-
reportan como `ENUM` (Postgres no permite distinguirlos después del hecho).
|
|
26
|
-
- A diferencia de MariaDB, el DDL en Postgres es transaccional: los métodos de `ManagerDB` confirman (`COMMIT`)
|
|
27
|
-
explícitamente tras crear tablas, triggers y procedimientos.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|