stvnnnnnn commited on
Commit
1773769
·
verified ·
1 Parent(s): 869f1a1

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +79 -9
app.py CHANGED
@@ -83,22 +83,84 @@ class PostgresManager:
83
  raise KeyError(f"connection_id '{connection_id}' no registrado")
84
  return self.connections[connection_id]
85
 
86
- def _get_conn(self):
87
  conn = psycopg2.connect(self.dsn)
88
- conn.autocommit = True
89
  return conn
90
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
  # ---------- creación de BD desde dump ----------
92
 
93
  def create_database_from_dump(self, label: str, sql_text: str) -> str:
94
  """
95
  Crea un schema en Neon, fija search_path a ese schema
96
- y ejecuta el dump SQL dentro de él.
97
  """
98
  connection_id = self._new_connection_id()
99
  schema_name = f"sess_{uuid.uuid4().hex[:8]}"
100
 
101
- conn = self._get_conn()
 
102
  try:
103
  with conn.cursor() as cur:
104
  # Crear schema aislado
@@ -113,23 +175,31 @@ class PostgresManager:
113
  pgsql.Identifier(schema_name)
114
  )
115
  )
116
- # Ejecutar dump completo (puede tener múltiples sentencias)
117
- cur.execute(sql_text)
 
 
118
  except Exception as e:
119
- # Si falla, intentar limpiar el schema
 
 
120
  try:
121
- with conn.cursor() as cur:
 
122
  cur.execute(
123
  pgsql.SQL("DROP SCHEMA IF EXISTS {} CASCADE").format(
124
  pgsql.Identifier(schema_name)
125
  )
126
  )
 
127
  except Exception:
128
  pass
 
129
  conn.close()
130
  raise RuntimeError(f"Error ejecutando dump SQL en Postgres: {e}")
131
  finally:
132
- conn.close()
 
133
 
134
  self.connections[connection_id] = {
135
  "label": label,
 
83
  raise KeyError(f"connection_id '{connection_id}' no registrado")
84
  return self.connections[connection_id]
85
 
86
+ def _get_conn(self, autocommit: bool = True):
87
  conn = psycopg2.connect(self.dsn)
88
+ conn.autocommit = autocommit
89
  return conn
90
 
91
+ def _execute_pg_dump(self, cur, sql_text: str) -> None:
92
+ """
93
+ Ejecuta un script tipo pg_dump:
94
+
95
+ - Ejecuta DDL/INSERTs normales con execute
96
+ - Maneja bloques:
97
+
98
+ COPY tabla (...) FROM stdin;
99
+ datos...
100
+ \.
101
+
102
+ usando copy_expert.
103
+ """
104
+ lines = sql_text.splitlines()
105
+ n = len(lines)
106
+ i = 0
107
+ buffer: List[str] = []
108
+
109
+ def flush_buffer():
110
+ stmt = "\n".join(buffer).strip()
111
+ if not stmt:
112
+ return
113
+ # Partimos por ';' para ejecutar cada sentencia
114
+ for piece in stmt.split(";"):
115
+ piece = piece.strip()
116
+ if piece:
117
+ cur.execute(piece)
118
+
119
+ while i < n:
120
+ line = lines[i]
121
+
122
+ # ¿Inicio de bloque COPY ... FROM stdin; ?
123
+ if re.match(r"^\s*copy\s+.+from\s+stdin;?\s*$", line, re.IGNORECASE):
124
+ # Ejecutar lo acumulado antes del COPY
125
+ flush_buffer()
126
+ buffer = []
127
+
128
+ copy_sql = line.strip()
129
+ i += 1
130
+ data_lines: List[str] = []
131
+
132
+ # Acumular las filas hasta encontrar '\.'
133
+ while i < n and lines[i].strip() != r"\.":
134
+ data_lines.append(lines[i])
135
+ i += 1
136
+
137
+ # Saltar la línea '\.' si existe
138
+ if i < n and lines[i].strip() == r"\.":
139
+ i += 1
140
+
141
+ data_str = "\n".join(data_lines) + "\n"
142
+
143
+ # Ejecutar el COPY con los datos
144
+ cur.copy_expert(copy_sql, io.StringIO(data_str))
145
+ else:
146
+ buffer.append(line)
147
+ i += 1
148
+
149
+ # Ejecutar lo que quede al final
150
+ flush_buffer()
151
+
152
  # ---------- creación de BD desde dump ----------
153
 
154
  def create_database_from_dump(self, label: str, sql_text: str) -> str:
155
  """
156
  Crea un schema en Neon, fija search_path a ese schema
157
+ y ejecuta el dump SQL dentro de él (incluyendo COPY ... FROM stdin).
158
  """
159
  connection_id = self._new_connection_id()
160
  schema_name = f"sess_{uuid.uuid4().hex[:8]}"
161
 
162
+ # Para el dump queremos manejar la transacción a mano
163
+ conn = self._get_conn(autocommit=False)
164
  try:
165
  with conn.cursor() as cur:
166
  # Crear schema aislado
 
175
  pgsql.Identifier(schema_name)
176
  )
177
  )
178
+ # Ejecutar dump completo (DDL + COPY)
179
+ self._execute_pg_dump(cur, sql_text)
180
+
181
+ conn.commit()
182
  except Exception as e:
183
+ conn.rollback()
184
+
185
+ # Intentar limpiar el schema si llegó a crearse
186
  try:
187
+ cleanup_conn = self._get_conn(autocommit=True)
188
+ with cleanup_conn.cursor() as cur:
189
  cur.execute(
190
  pgsql.SQL("DROP SCHEMA IF EXISTS {} CASCADE").format(
191
  pgsql.Identifier(schema_name)
192
  )
193
  )
194
+ cleanup_conn.close()
195
  except Exception:
196
  pass
197
+
198
  conn.close()
199
  raise RuntimeError(f"Error ejecutando dump SQL en Postgres: {e}")
200
  finally:
201
+ if not conn.closed:
202
+ conn.close()
203
 
204
  self.connections[connection_id] = {
205
  "label": label,