mtpk-postgres 0.1.3__tar.gz → 0.1.4__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mtpk_postgres
3
- Version: 0.1.3
3
+ Version: 0.1.4
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
@@ -54,6 +54,18 @@ pip install -e .
54
54
 
55
55
  ## Historial
56
56
 
57
+ - **0.1.4**: corrige la causa de fondo del bug de la 0.1.3, que solo tapaba el síntoma en la columna generada
58
+ en sí. Había dos problemas relacionados: (1) `comparar_generar_alter` comparaba el tipo de columna por su
59
+ nombre "en crudo" (`col.tipo`), así que un modelo que usa alias como `INT`, `TINYINT` o `DATETIME` (en vez de
60
+ `INTEGER`/`SMALLINT`/`TIMESTAMP`, que es como se reconstruyen siempre desde Postgres) nunca coincidía con la
61
+ realidad y proponía un `ALTER COLUMN ... TYPE` en cada ejecución aunque el tipo no hubiera cambiado — inofensivo
62
+ la mayoría de las veces (Postgres permite "cambiar" una columna a su mismo tipo), pero rechazado de plano si otra
63
+ columna `GENERATED` depende de la columna "alterada"; ahora se compara por el tipo canónico
64
+ (`Columna._tipo_base()`). (2) `ManagerDB.aplicar_cambios()` — la ruta que se usa en la primera sincronización de
65
+ una base de datos nueva — creaba todas las tablas del modelo y a continuación las volvía a comparar todas,
66
+ incluidas las que acababa de crear en la misma llamada; una tabla recién creada coincide por definición con el
67
+ modelo que la creó, así que ahora se excluye de esa comparación (que solo tiene sentido sobre tablas
68
+ preexistentes). Esta segunda corrección evita esta clase entera de falsos positivos, no solo el caso ya visto.
57
69
  - **0.1.3**: corrige un bug bloqueante en `comparar_generar_alter` — al crear una tabla nueva con una columna
58
70
  `GENERATED ALWAYS AS (...) STORED` (p.ej. una columna de búsqueda `tsvector` calculada a partir de otra), la
59
71
  siguiente pasada de comparación la trataba como una columna normal (la introspección no reconstruye la expresión
@@ -34,6 +34,18 @@ pip install -e .
34
34
 
35
35
  ## Historial
36
36
 
37
+ - **0.1.4**: corrige la causa de fondo del bug de la 0.1.3, que solo tapaba el síntoma en la columna generada
38
+ en sí. Había dos problemas relacionados: (1) `comparar_generar_alter` comparaba el tipo de columna por su
39
+ nombre "en crudo" (`col.tipo`), así que un modelo que usa alias como `INT`, `TINYINT` o `DATETIME` (en vez de
40
+ `INTEGER`/`SMALLINT`/`TIMESTAMP`, que es como se reconstruyen siempre desde Postgres) nunca coincidía con la
41
+ realidad y proponía un `ALTER COLUMN ... TYPE` en cada ejecución aunque el tipo no hubiera cambiado — inofensivo
42
+ la mayoría de las veces (Postgres permite "cambiar" una columna a su mismo tipo), pero rechazado de plano si otra
43
+ columna `GENERATED` depende de la columna "alterada"; ahora se compara por el tipo canónico
44
+ (`Columna._tipo_base()`). (2) `ManagerDB.aplicar_cambios()` — la ruta que se usa en la primera sincronización de
45
+ una base de datos nueva — creaba todas las tablas del modelo y a continuación las volvía a comparar todas,
46
+ incluidas las que acababa de crear en la misma llamada; una tabla recién creada coincide por definición con el
47
+ modelo que la creó, así que ahora se excluye de esa comparación (que solo tiene sentido sobre tablas
48
+ preexistentes). Esta segunda corrección evita esta clase entera de falsos positivos, no solo el caso ya visto.
37
49
  - **0.1.3**: corrige un bug bloqueante en `comparar_generar_alter` — al crear una tabla nueva con una columna
38
50
  `GENERATED ALWAYS AS (...) STORED` (p.ej. una columna de búsqueda `tsvector` calculada a partir de otra), la
39
51
  siguiente pasada de comparación la trataba como una columna normal (la introspección no reconstruye la expresión
@@ -1,4 +1,4 @@
1
- __version__ = "0.1.3"
1
+ __version__ = "0.1.4"
2
2
  __author__ = "jjandres"
3
3
 
4
4
  from .interface import *
@@ -129,14 +129,25 @@ class Columna:
129
129
  }
130
130
  _SIN_SOPORTE = {"GEOMETRY", "POINT", "LINESTRING", "POLYGON"}
131
131
 
132
- def _tipo_sql(self) -> str:
133
- """Traduce self.tipo (+ longitud/precision/escala) al tipo SQL de Postgres."""
132
+ def _tipo_base(self) -> str:
133
+ """
134
+ Nombre canónico del tipo en Postgres (sin longitud/precisión), p.ej. "INT" y "INTEGER"
135
+ devuelven ambos "INTEGER". Se usa tanto para generar SQL como para comparar el tipo del
136
+ modelo contra el tipo reconstruido desde la base de datos (obtener_columnas_postgres ya
137
+ siempre devuelve el nombre canónico) — comparar por el nombre "en crudo" haría que un
138
+ modelo que usa alias tipo "INT"/"TINYINT"/"DATETIME" nunca coincidiera con la realidad y
139
+ disparara un ALTER COLUMN de tipo en cada ejecución, aunque el tipo real no haya cambiado.
140
+ """
134
141
  tipo_upper = self.tipo.upper()
135
142
  if tipo_upper in self._SIN_SOPORTE:
136
143
  raise NotImplementedError(
137
144
  f"El tipo '{tipo_upper}' requiere la extensión PostGIS y no está soportado."
138
145
  )
139
- base = self._MAPA_TIPOS.get(tipo_upper, tipo_upper)
146
+ return self._MAPA_TIPOS.get(tipo_upper, tipo_upper)
147
+
148
+ def _tipo_sql(self) -> str:
149
+ """Traduce self.tipo (+ longitud/precision/escala) al tipo SQL de Postgres."""
150
+ base = self._tipo_base()
140
151
 
141
152
  if base == "VECTOR":
142
153
  if self.longitud is None:
@@ -750,8 +761,14 @@ class Tabla:
750
761
  return (val_real, val_def) in equivalentes or (val_def, val_real) in equivalentes or val_real == val_def
751
762
 
752
763
  def tipos_iguales(col_real, col_def):
764
+ # Comparar por el tipo CANÓNICO (col._tipo_base()), no por el texto crudo de col.tipo:
765
+ # el modelo puede usar alias como "INT"/"TINYINT"/"DATETIME" que Postgres no distingue
766
+ # de "INTEGER"/"SMALLINT"/"TIMESTAMP" — comparando el texto crudo, esas columnas nunca
767
+ # coincidirían con la realidad y se propondría un ALTER COLUMN ... TYPE en cada
768
+ # ejecución aunque el tipo real no cambie (inofensivo casi siempre, pero Postgres lo
769
+ # rechaza si otra columna GENERATED depende de la columna "alterada").
753
770
  return (
754
- norm(col_real.tipo) == norm(col_def.tipo) and
771
+ col_real._tipo_base() == col_def._tipo_base() and
755
772
  col_real.longitud == col_def.longitud and
756
773
  col_real.precision == col_def.precision and
757
774
  (col_real.escala or None) == (col_def.escala or None)
@@ -1207,6 +1224,21 @@ class ManagerDB(Database):
1207
1224
  )
1208
1225
  """)
1209
1226
 
1227
+ # Tablas que ya existían ANTES de esta llamada: solo tiene sentido comparar
1228
+ # (y potencialmente ALTERar) esas. Una tabla recién creada en esta misma llamada
1229
+ # coincide por definición con el modelo que la creó — compararla igual puede
1230
+ # disparar un ALTER falso-positivo (p.ej. no detecta que "INT" y "INTEGER" son el
1231
+ # mismo tipo) que además Postgres puede rechazar de plano si hay una columna
1232
+ # GENERATED que depende de la columna "alterada".
1233
+ tablas_preexistentes = set()
1234
+ for nombre_tabla in self.tablas:
1235
+ cursor.execute(
1236
+ "SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = %s",
1237
+ (nombre_tabla,)
1238
+ )
1239
+ if cursor.fetchone():
1240
+ tablas_preexistentes.add(nombre_tabla)
1241
+
1210
1242
  # --- Crear tablas nuevas (+ índices + comentarios) ---
1211
1243
  for nombre_tabla, tabla in self.tablas.items():
1212
1244
  sentencias_creacion = [tabla.to_sql()] + tabla.to_sql_indices() + tabla.to_sql_comentarios()
@@ -1218,8 +1250,10 @@ class ManagerDB(Database):
1218
1250
  if self.logger:
1219
1251
  self.logger.info(f"Tabla \"{nombre_tabla}\": ejecutado CREATE IF NOT EXISTS.")
1220
1252
 
1221
- # --- Aplicar ALTERs ---
1253
+ # --- Aplicar ALTERs (solo en tablas que ya existían antes de esta llamada) ---
1222
1254
  for nombre_tabla, tabla in self.tablas.items():
1255
+ if nombre_tabla not in tablas_preexistentes:
1256
+ continue
1223
1257
  alter_sqls = tabla.comparar_generar_alter(conexion, permitir_drop=permitir_drop)
1224
1258
  for alter_sql in alter_sqls:
1225
1259
  cursor.execute(alter_sql)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mtpk_postgres
3
- Version: 0.1.3
3
+ Version: 0.1.4
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
@@ -54,6 +54,18 @@ pip install -e .
54
54
 
55
55
  ## Historial
56
56
 
57
+ - **0.1.4**: corrige la causa de fondo del bug de la 0.1.3, que solo tapaba el síntoma en la columna generada
58
+ en sí. Había dos problemas relacionados: (1) `comparar_generar_alter` comparaba el tipo de columna por su
59
+ nombre "en crudo" (`col.tipo`), así que un modelo que usa alias como `INT`, `TINYINT` o `DATETIME` (en vez de
60
+ `INTEGER`/`SMALLINT`/`TIMESTAMP`, que es como se reconstruyen siempre desde Postgres) nunca coincidía con la
61
+ realidad y proponía un `ALTER COLUMN ... TYPE` en cada ejecución aunque el tipo no hubiera cambiado — inofensivo
62
+ la mayoría de las veces (Postgres permite "cambiar" una columna a su mismo tipo), pero rechazado de plano si otra
63
+ columna `GENERATED` depende de la columna "alterada"; ahora se compara por el tipo canónico
64
+ (`Columna._tipo_base()`). (2) `ManagerDB.aplicar_cambios()` — la ruta que se usa en la primera sincronización de
65
+ una base de datos nueva — creaba todas las tablas del modelo y a continuación las volvía a comparar todas,
66
+ incluidas las que acababa de crear en la misma llamada; una tabla recién creada coincide por definición con el
67
+ modelo que la creó, así que ahora se excluye de esa comparación (que solo tiene sentido sobre tablas
68
+ preexistentes). Esta segunda corrección evita esta clase entera de falsos positivos, no solo el caso ya visto.
57
69
  - **0.1.3**: corrige un bug bloqueante en `comparar_generar_alter` — al crear una tabla nueva con una columna
58
70
  `GENERATED ALWAYS AS (...) STORED` (p.ej. una columna de búsqueda `tsvector` calculada a partir de otra), la
59
71
  siguiente pasada de comparación la trataba como una columna normal (la introspección no reconstruye la expresión
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "mtpk_postgres"
7
- version = "0.1.3"
7
+ version = "0.1.4"
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