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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +83 -18
app.py CHANGED
@@ -153,53 +153,118 @@ class PostgresManager:
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
167
  cur.execute(
168
  pgsql.SQL("CREATE SCHEMA {}").format(
169
  pgsql.Identifier(schema_name)
170
  )
171
  )
172
- # Usar ese schema por defecto
173
  cur.execute(
174
  pgsql.SQL("SET search_path TO {}").format(
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,
 
153
 
154
  def create_database_from_dump(self, label: str, sql_text: str) -> str:
155
  """
156
+ Crea un schema aislado en Neon y restaura dentro de él
157
+ un dump de Postgres tipo psql (con COPY FROM stdin, DROP DATABASE, etc.).
158
  """
159
  connection_id = self._new_connection_id()
160
  schema_name = f"sess_{uuid.uuid4().hex[:8]}"
161
 
162
+ conn = self._get_conn()
 
163
  try:
164
  with conn.cursor() as cur:
165
+ # 1) Crear schema aislado
166
  cur.execute(
167
  pgsql.SQL("CREATE SCHEMA {}").format(
168
  pgsql.Identifier(schema_name)
169
  )
170
  )
171
+ # 2) Usar ese schema como search_path
172
  cur.execute(
173
  pgsql.SQL("SET search_path TO {}").format(
174
  pgsql.Identifier(schema_name)
175
  )
176
  )
 
 
177
 
178
+ # 3) Parsear el dump línea por línea
179
+ in_copy = False
180
+ copy_sql = ""
181
+ copy_rows: list[str] = []
182
+ stmt_lines: list[str] = []
183
+
184
+ def flush_statement():
185
+ """Ejecuta el statement acumulado si es útil."""
186
+ stmt = "\n".join(stmt_lines).strip()
187
+ stmt_lines.clear()
188
+ if not stmt or stmt == ";":
189
+ return
190
+
191
+ # saltar cosas peligrosas o innecesarias
192
+ upper = stmt.upper()
193
+ skip_prefixes = (
194
+ "SET ",
195
+ "SELECT PG_CATALOG.SET_CONFIG",
196
+ "COMMENT ON EXTENSION",
197
+ "DROP DATABASE",
198
+ "CREATE DATABASE",
199
+ "ALTER DATABASE",
200
+ "REVOKE ",
201
+ "GRANT ",
202
+ "BEGIN",
203
+ "COMMIT",
204
+ "ROLLBACK",
205
+ )
206
+ if upper.startswith(skip_prefixes):
207
+ return
208
+
209
+ # quitar ';' final si viene solo
210
+ if stmt.endswith(";"):
211
+ stmt = stmt[:-1]
212
+
213
+ cur.execute(stmt)
214
+
215
+ for raw_line in sql_text.splitlines():
216
+ line = raw_line.rstrip("\n")
217
+
218
+ # Comentarios y líneas vacías
219
+ if not in_copy and (not line.strip() or line.strip().startswith("--")):
220
+ continue
221
+
222
+ if in_copy:
223
+ # Estamos dentro de un bloque COPY ... FROM stdin
224
+ if line.strip() == r"\.":
225
+ # Fin del COPY → mandar todo a copy_expert
226
+ data_str = "\n".join(copy_rows) + "\n"
227
+ copy_rows.clear()
228
+ in_copy = False
229
+ cur.copy_expert(copy_sql, io.StringIO(data_str))
230
+ else:
231
+ copy_rows.append(line)
232
+ continue
233
+
234
+ # Detectar COPY ... FROM stdin;
235
+ stripped = line.strip()
236
+ if stripped.upper().startswith("COPY ") and "FROM stdin" in stripped:
237
+ # Antes de entrar al COPY, ejecutar lo que hubiera acumulado
238
+ flush_statement()
239
+ in_copy = True
240
+ copy_sql = stripped # tal cual viene en el dump
241
+ copy_rows = []
242
+ continue
243
+
244
+ # Acumular statement “normal”
245
+ stmt_lines.append(line)
246
+ if stripped.endswith(";"):
247
+ flush_statement()
248
+
249
+ # Por si quedó algo sin ';' al final
250
+ if stmt_lines:
251
+ flush_statement()
252
 
253
+ except Exception as e:
254
+ # Si falla, eliminar el schema recién creado
255
  try:
256
+ with conn.cursor() as cur:
 
257
  cur.execute(
258
  pgsql.SQL("DROP SCHEMA IF EXISTS {} CASCADE").format(
259
  pgsql.Identifier(schema_name)
260
  )
261
  )
 
262
  except Exception:
263
  pass
 
264
  conn.close()
265
  raise RuntimeError(f"Error ejecutando dump SQL en Postgres: {e}")
266
  finally:
267
+ conn.close()
 
268
 
269
  self.connections[connection_id] = {
270
  "label": label,