mtpk-postgres 0.1.1__tar.gz → 0.1.3__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.1 → mtpk_postgres-0.1.3}/PKG-INFO +20 -1
- {mtpk_postgres-0.1.1 → mtpk_postgres-0.1.3}/README.md +19 -0
- {mtpk_postgres-0.1.1 → mtpk_postgres-0.1.3}/mtpk_postgres/__init__.py +1 -1
- {mtpk_postgres-0.1.1 → mtpk_postgres-0.1.3}/mtpk_postgres/core_sync.py +101 -15
- {mtpk_postgres-0.1.1 → mtpk_postgres-0.1.3}/mtpk_postgres.egg-info/PKG-INFO +20 -1
- {mtpk_postgres-0.1.1 → mtpk_postgres-0.1.3}/pyproject.toml +1 -1
- {mtpk_postgres-0.1.1 → mtpk_postgres-0.1.3}/LICENSE +0 -0
- {mtpk_postgres-0.1.1 → mtpk_postgres-0.1.3}/mtpk_postgres/async_adapter.py +0 -0
- {mtpk_postgres-0.1.1 → mtpk_postgres-0.1.3}/mtpk_postgres/crud.py +0 -0
- {mtpk_postgres-0.1.1 → mtpk_postgres-0.1.3}/mtpk_postgres/excepciones.py +0 -0
- {mtpk_postgres-0.1.1 → mtpk_postgres-0.1.3}/mtpk_postgres/interface.py +0 -0
- {mtpk_postgres-0.1.1 → mtpk_postgres-0.1.3}/mtpk_postgres/utils.py +0 -0
- {mtpk_postgres-0.1.1 → mtpk_postgres-0.1.3}/mtpk_postgres.egg-info/SOURCES.txt +0 -0
- {mtpk_postgres-0.1.1 → mtpk_postgres-0.1.3}/mtpk_postgres.egg-info/dependency_links.txt +0 -0
- {mtpk_postgres-0.1.1 → mtpk_postgres-0.1.3}/mtpk_postgres.egg-info/requires.txt +0 -0
- {mtpk_postgres-0.1.1 → mtpk_postgres-0.1.3}/mtpk_postgres.egg-info/top_level.txt +0 -0
- {mtpk_postgres-0.1.1 → mtpk_postgres-0.1.3}/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.3
|
|
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,9 +45,28 @@ 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).
|
|
48
54
|
|
|
49
55
|
## Historial
|
|
50
56
|
|
|
57
|
+
- **0.1.3**: corrige un bug bloqueante en `comparar_generar_alter` — al crear una tabla nueva con una columna
|
|
58
|
+
`GENERATED ALWAYS AS (...) STORED` (p.ej. una columna de búsqueda `tsvector` calculada a partir de otra), la
|
|
59
|
+
siguiente pasada de comparación la trataba como una columna normal (la introspección no reconstruye la expresión
|
|
60
|
+
de generación) y proponía un `ALTER COLUMN` sobre ella justo después de crearla — algo que Postgres puede
|
|
61
|
+
rechazar cuando otras columnas dependen de la generada. Como `aplicar_cambios()` ejecuta todo en una única
|
|
62
|
+
transacción, ese fallo deshacía también la creación de las demás tablas. Ahora las columnas `generado` nunca se
|
|
63
|
+
comparan ni se alteran una vez existen (para cambiar su expresión hay que borrarlas y recrearlas a mano); de paso,
|
|
64
|
+
`Columna.to_sql()` ya respeta `not_null=True` en columnas generadas (antes se ignoraba).
|
|
65
|
+
- **0.1.2**: soporte nativo para columnas `VECTOR(n)` (pgvector) e índices con método (`USING gin/hnsw/...`,
|
|
66
|
+
`opclass`, `WITH (...)`) en `Index`. La introspección (`obtener_columnas_postgres`) ahora reconoce las columnas
|
|
67
|
+
`vector` por su `udt_name` y reconstruye su dimensión leyendo el catálogo (`pg_attribute`/`format_type`); sin esto,
|
|
68
|
+
`data_type` llegaba como `'USER-DEFINED'` y `comparar_generar_alter` proponía un `ALTER COLUMN` en cada ejecución
|
|
69
|
+
aunque la columna no hubiera cambiado.
|
|
51
70
|
- **0.1.1**: corrige un bug bloqueante — `Tabla` no enlazaba `indice.tabla`/`fk.tabla`/`columna.tabla` cuando
|
|
52
71
|
`columnas`/`indices`/`foreign_keys` se pasaban directamente por el constructor (solo lo hacían `add_index()` y
|
|
53
72
|
compañía). Como `Index.to_sql()` necesita esa referencia para generar el `CREATE INDEX`, cualquier tabla definida
|
|
@@ -25,9 +25,28 @@ pip install -e .
|
|
|
25
25
|
reportan como `ENUM` (Postgres no permite distinguirlos después del hecho).
|
|
26
26
|
- A diferencia de MariaDB, el DDL en Postgres es transaccional: los métodos de `ManagerDB` confirman (`COMMIT`)
|
|
27
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).
|
|
28
34
|
|
|
29
35
|
## Historial
|
|
30
36
|
|
|
37
|
+
- **0.1.3**: corrige un bug bloqueante en `comparar_generar_alter` — al crear una tabla nueva con una columna
|
|
38
|
+
`GENERATED ALWAYS AS (...) STORED` (p.ej. una columna de búsqueda `tsvector` calculada a partir de otra), la
|
|
39
|
+
siguiente pasada de comparación la trataba como una columna normal (la introspección no reconstruye la expresión
|
|
40
|
+
de generación) y proponía un `ALTER COLUMN` sobre ella justo después de crearla — algo que Postgres puede
|
|
41
|
+
rechazar cuando otras columnas dependen de la generada. Como `aplicar_cambios()` ejecuta todo en una única
|
|
42
|
+
transacción, ese fallo deshacía también la creación de las demás tablas. Ahora las columnas `generado` nunca se
|
|
43
|
+
comparan ni se alteran una vez existen (para cambiar su expresión hay que borrarlas y recrearlas a mano); de paso,
|
|
44
|
+
`Columna.to_sql()` ya respeta `not_null=True` en columnas generadas (antes se ignoraba).
|
|
45
|
+
- **0.1.2**: soporte nativo para columnas `VECTOR(n)` (pgvector) e índices con método (`USING gin/hnsw/...`,
|
|
46
|
+
`opclass`, `WITH (...)`) en `Index`. La introspección (`obtener_columnas_postgres`) ahora reconoce las columnas
|
|
47
|
+
`vector` por su `udt_name` y reconstruye su dimensión leyendo el catálogo (`pg_attribute`/`format_type`); sin esto,
|
|
48
|
+
`data_type` llegaba como `'USER-DEFINED'` y `comparar_generar_alter` proponía un `ALTER COLUMN` en cada ejecución
|
|
49
|
+
aunque la columna no hubiera cambiado.
|
|
31
50
|
- **0.1.1**: corrige un bug bloqueante — `Tabla` no enlazaba `indice.tabla`/`fk.tabla`/`columna.tabla` cuando
|
|
32
51
|
`columnas`/`indices`/`foreign_keys` se pasaban directamente por el constructor (solo lo hacían `add_index()` y
|
|
33
52
|
compañía). Como `Index.to_sql()` necesita esa referencia para generar el `CREATE INDEX`, cualquier tabla definida
|
|
@@ -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"}:
|
|
@@ -156,6 +166,8 @@ class Columna:
|
|
|
156
166
|
|
|
157
167
|
if self.generado:
|
|
158
168
|
partes.append(f"{tipo_sql} GENERATED ALWAYS AS ({self.generado}) STORED")
|
|
169
|
+
if self.not_null:
|
|
170
|
+
partes.append("NOT NULL")
|
|
159
171
|
else:
|
|
160
172
|
partes.append(tipo_sql)
|
|
161
173
|
|
|
@@ -230,12 +242,20 @@ class Index:
|
|
|
230
242
|
- Atributos:
|
|
231
243
|
- `columnas` (List[str]): Lista de nombres de columnas incluidas en el índice. Pueden ser una o varias (índice compuesto).
|
|
232
244
|
- `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`.
|
|
245
|
+
- `unico` (bool): Indica si el índice es único (`UNIQUE`). Por defecto es `False`. Incompatible con `metodo`.
|
|
246
|
+
- `metodo` (str, opcional): Método de acceso del índice (`USING <metodo>`), p.ej. `"gin"`, `"gist"` o `"hnsw"`/`"ivfflat"`
|
|
247
|
+
(estos dos últimos requieren la extensión `pgvector`). Si no se indica, se usa el btree normal de Postgres.
|
|
248
|
+
- `opclass` (str, opcional): Clase de operador a aplicar a cada columna del índice, p.ej. `"vector_cosine_ops"`
|
|
249
|
+
para un HNSW/IVFFlat sobre una columna `VECTOR`, o `"gin_trgm_ops"` para búsqueda por trigramas.
|
|
250
|
+
- `with_opciones` (str, opcional): Contenido crudo de la cláusula `WITH (...)`, p.ej. `"m = 16, ef_construction = 64"` (HNSW).
|
|
234
251
|
- `tabla` (Tabla, opcional): Referencia a la tabla que contiene este índice. Se asigna automáticamente al añadir el índice a la tabla.
|
|
235
252
|
"""
|
|
236
253
|
columnas: List[str]
|
|
237
254
|
nombre: Optional[str] = None
|
|
238
255
|
unico: bool = False
|
|
256
|
+
metodo: Optional[str] = None
|
|
257
|
+
opclass: Optional[str] = None
|
|
258
|
+
with_opciones: Optional[str] = None
|
|
239
259
|
tabla: Optional["Tabla"] = field(default=None, repr=False)
|
|
240
260
|
|
|
241
261
|
def to_sql(self) -> str:
|
|
@@ -243,8 +263,25 @@ class Index:
|
|
|
243
263
|
raise ValueError(
|
|
244
264
|
f"El índice sobre {self.columnas} no está asociado a ninguna tabla; no se puede generar el CREATE INDEX."
|
|
245
265
|
)
|
|
246
|
-
|
|
266
|
+
if self.metodo and self.unico:
|
|
267
|
+
raise ValueError(
|
|
268
|
+
f"El índice sobre {self.columnas} no puede ser UNIQUE y usar 'metodo' ({self.metodo}) a la vez "
|
|
269
|
+
"(GIN/HNSW/IVFFlat no admiten UNIQUE en Postgres)."
|
|
270
|
+
)
|
|
271
|
+
|
|
247
272
|
nombre = self.nombre or f"{'_'.join(self.columnas)}_{'uk' if self.unico else 'idx'}"
|
|
273
|
+
|
|
274
|
+
if self.metodo:
|
|
275
|
+
cols = ", ".join(
|
|
276
|
+
f'"{c}" {self.opclass}' if self.opclass else f'"{c}"'
|
|
277
|
+
for c in self.columnas
|
|
278
|
+
)
|
|
279
|
+
sql = f'CREATE INDEX IF NOT EXISTS "{nombre}" ON "{self.tabla.nombre}" USING {self.metodo} ({cols})'
|
|
280
|
+
if self.with_opciones:
|
|
281
|
+
sql += f" WITH ({self.with_opciones})"
|
|
282
|
+
return sql
|
|
283
|
+
|
|
284
|
+
cols = ", ".join(f'"{c}"' for c in self.columnas)
|
|
248
285
|
tipo_sql = "UNIQUE INDEX" if self.unico else "INDEX"
|
|
249
286
|
return f'CREATE {tipo_sql} IF NOT EXISTS "{nombre}" ON "{self.tabla.nombre}" ({cols})'
|
|
250
287
|
|
|
@@ -464,7 +501,7 @@ class Tabla:
|
|
|
464
501
|
cursor.execute(
|
|
465
502
|
"""
|
|
466
503
|
SELECT
|
|
467
|
-
c.column_name, c.data_type, c.character_maximum_length,
|
|
504
|
+
c.column_name, c.data_type, c.udt_name, c.character_maximum_length,
|
|
468
505
|
c.numeric_precision, c.numeric_scale, c.is_nullable,
|
|
469
506
|
c.column_default, c.is_identity,
|
|
470
507
|
COALESCE(pk.es_pk, false) AS es_pk,
|
|
@@ -496,21 +533,33 @@ class Tabla:
|
|
|
496
533
|
filas = cursor.fetchall()
|
|
497
534
|
|
|
498
535
|
checks_por_columna = self._obtener_opciones_check(conexion)
|
|
536
|
+
dimensiones_vector = self._obtener_dimensiones_vector(conexion)
|
|
499
537
|
|
|
500
538
|
for fila in filas:
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
#
|
|
506
|
-
#
|
|
507
|
-
|
|
508
|
-
|
|
539
|
+
longitud = fila["character_maximum_length"]
|
|
540
|
+
|
|
541
|
+
if fila["udt_name"] == "vector":
|
|
542
|
+
# information_schema no expone el nº de dimensiones de un `vector(n)` (tipo de
|
|
543
|
+
# la extensión pgvector): data_type viene como 'USER-DEFINED' y
|
|
544
|
+
# character_maximum_length es NULL. Sin este caso especial, tipo_base quedaría
|
|
545
|
+
# como "USER-DEFINED" y nunca igualaría al "VECTOR" del modelo, provocando un
|
|
546
|
+
# ALTER COLUMN espurio en cada ejecución.
|
|
547
|
+
tipo_base = "VECTOR"
|
|
548
|
+
longitud = dimensiones_vector.get(fila["column_name"])
|
|
549
|
+
enum_opciones = None
|
|
550
|
+
else:
|
|
551
|
+
tipo_base = self._PG_A_MODELO.get(fila["data_type"], fila["data_type"].upper())
|
|
552
|
+
enum_opciones = None
|
|
553
|
+
if tipo_base == "TEXT" and fila["column_name"] in checks_por_columna:
|
|
554
|
+
# Postgres no distingue ENUM de SET tras reconstruir desde el CHECK: ambos
|
|
555
|
+
# se guardan como TEXT + CHECK (ver Columna.to_sql), así que se asume ENUM.
|
|
556
|
+
tipo_base = "ENUM"
|
|
557
|
+
enum_opciones = checks_por_columna[fila["column_name"]]
|
|
509
558
|
|
|
510
559
|
col = Columna(
|
|
511
560
|
nombre=fila["column_name"],
|
|
512
561
|
tipo=tipo_base,
|
|
513
|
-
longitud=
|
|
562
|
+
longitud=longitud,
|
|
514
563
|
precision=fila["numeric_precision"],
|
|
515
564
|
escala=fila["numeric_scale"],
|
|
516
565
|
not_null=(fila["is_nullable"] == "NO"),
|
|
@@ -524,6 +573,33 @@ class Tabla:
|
|
|
524
573
|
|
|
525
574
|
return columnas
|
|
526
575
|
|
|
576
|
+
def _obtener_dimensiones_vector(self, conexion) -> Dict[str, int]:
|
|
577
|
+
"""
|
|
578
|
+
Devuelve {columna: dimensiones} para las columnas `vector(n)` (extensión pgvector) de
|
|
579
|
+
esta tabla, leyendo el modificador de tipo real desde los catálogos (atttypmod vía
|
|
580
|
+
format_type), ya que information_schema no lo expone para tipos definidos por extensión.
|
|
581
|
+
"""
|
|
582
|
+
resultado: Dict[str, int] = {}
|
|
583
|
+
with conexion.cursor(row_factory=dict_row) as cursor:
|
|
584
|
+
cursor.execute(
|
|
585
|
+
"""
|
|
586
|
+
SELECT a.attname AS columna,
|
|
587
|
+
pg_catalog.format_type(a.atttypid, a.atttypmod) AS tipo_completo
|
|
588
|
+
FROM pg_attribute a
|
|
589
|
+
JOIN pg_class t ON t.oid = a.attrelid
|
|
590
|
+
JOIN pg_namespace n ON n.oid = t.relnamespace
|
|
591
|
+
JOIN pg_type ty ON ty.oid = a.atttypid
|
|
592
|
+
WHERE t.relname = %s AND n.nspname = 'public'
|
|
593
|
+
AND ty.typname = 'vector' AND a.attnum > 0 AND NOT a.attisdropped
|
|
594
|
+
""",
|
|
595
|
+
(self.nombre,)
|
|
596
|
+
)
|
|
597
|
+
for fila in cursor.fetchall():
|
|
598
|
+
m = re.match(r"vector\((\d+)\)", fila["tipo_completo"] or "", re.IGNORECASE)
|
|
599
|
+
if m:
|
|
600
|
+
resultado[fila["columna"]] = int(m.group(1))
|
|
601
|
+
return resultado
|
|
602
|
+
|
|
527
603
|
def _obtener_opciones_check(self, conexion) -> Dict[str, List[str]]:
|
|
528
604
|
"""
|
|
529
605
|
Reconstruye, de forma best-effort, las opciones de las columnas ENUM/SET (TEXT + CHECK)
|
|
@@ -697,6 +773,16 @@ class Tabla:
|
|
|
697
773
|
col_real = reales.get(nombre)
|
|
698
774
|
if not col_real:
|
|
699
775
|
col_alteraciones.append(f"ADD COLUMN {col_def.to_sql()}")
|
|
776
|
+
elif col_def.generado:
|
|
777
|
+
# Las columnas GENERATED ALWAYS AS (...) STORED no se comparan ni se alteran
|
|
778
|
+
# automáticamente una vez existen: la introspección no reconstruye la expresión
|
|
779
|
+
# de generación (obtener_columnas_postgres no la lee), así que cualquier columna
|
|
780
|
+
# "normal" reconstruida a partir de una generada se compararía con datos
|
|
781
|
+
# incompletos (p.ej. sin el NOT NULL que sí lleva la generada) y dispararía un
|
|
782
|
+
# ALTER espurio — que además Postgres puede rechazar de plano si otras columnas
|
|
783
|
+
# dependen de ella. Si la expresión cambia, hay que borrar y recrear la columna
|
|
784
|
+
# a mano.
|
|
785
|
+
continue
|
|
700
786
|
elif not iguales(col_real, col_def):
|
|
701
787
|
if not tipos_iguales(col_real, col_def):
|
|
702
788
|
tipo_sql = col_def._tipo_sql()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mtpk_postgres
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
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,9 +45,28 @@ 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).
|
|
48
54
|
|
|
49
55
|
## Historial
|
|
50
56
|
|
|
57
|
+
- **0.1.3**: corrige un bug bloqueante en `comparar_generar_alter` — al crear una tabla nueva con una columna
|
|
58
|
+
`GENERATED ALWAYS AS (...) STORED` (p.ej. una columna de búsqueda `tsvector` calculada a partir de otra), la
|
|
59
|
+
siguiente pasada de comparación la trataba como una columna normal (la introspección no reconstruye la expresión
|
|
60
|
+
de generación) y proponía un `ALTER COLUMN` sobre ella justo después de crearla — algo que Postgres puede
|
|
61
|
+
rechazar cuando otras columnas dependen de la generada. Como `aplicar_cambios()` ejecuta todo en una única
|
|
62
|
+
transacción, ese fallo deshacía también la creación de las demás tablas. Ahora las columnas `generado` nunca se
|
|
63
|
+
comparan ni se alteran una vez existen (para cambiar su expresión hay que borrarlas y recrearlas a mano); de paso,
|
|
64
|
+
`Columna.to_sql()` ya respeta `not_null=True` en columnas generadas (antes se ignoraba).
|
|
65
|
+
- **0.1.2**: soporte nativo para columnas `VECTOR(n)` (pgvector) e índices con método (`USING gin/hnsw/...`,
|
|
66
|
+
`opclass`, `WITH (...)`) en `Index`. La introspección (`obtener_columnas_postgres`) ahora reconoce las columnas
|
|
67
|
+
`vector` por su `udt_name` y reconstruye su dimensión leyendo el catálogo (`pg_attribute`/`format_type`); sin esto,
|
|
68
|
+
`data_type` llegaba como `'USER-DEFINED'` y `comparar_generar_alter` proponía un `ALTER COLUMN` en cada ejecución
|
|
69
|
+
aunque la columna no hubiera cambiado.
|
|
51
70
|
- **0.1.1**: corrige un bug bloqueante — `Tabla` no enlazaba `indice.tabla`/`fk.tabla`/`columna.tabla` cuando
|
|
52
71
|
`columnas`/`indices`/`foreign_keys` se pasaban directamente por el constructor (solo lo hacían `add_index()` y
|
|
53
72
|
compañía). Como `Index.to_sql()` necesita esa referencia para generar el `CREATE INDEX`, cualquier tabla definida
|
|
@@ -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.3"
|
|
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"
|
|
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
|