macrostrat.database 3.3.1__tar.gz → 3.3.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: macrostrat.database
3
- Version: 3.3.1
3
+ Version: 3.3.2
4
4
  Summary: A SQLAlchemy-based database toolkit.
5
5
  Author: Daven Quinn
6
6
  Author-email: dev@davenquinn.com
@@ -1,4 +1,3 @@
1
- from distutils.log import warn
2
1
  from warnings import warn
3
2
 
4
3
  # Drag in geographic types for database reflection
@@ -9,6 +9,7 @@ from click import echo, secho
9
9
  from psycopg2.extensions import set_wait_callback
10
10
  from psycopg2.extras import wait_select
11
11
  from psycopg2.sql import SQL, Composable, Composed
12
+ import psycopg2.errors
12
13
  from rich.console import Console
13
14
  from sqlalchemy import MetaData, create_engine, text
14
15
  from sqlalchemy.engine import Connection, Engine
@@ -265,8 +266,28 @@ def _run_sql(connectable, sql, params=None, **kwargs):
265
266
  if not isinstance(params, list) or not len(params) == len(queries):
266
267
  params = [params] * len(queries)
267
268
 
268
- for query, params in zip(queries, params):
269
- trans = None
269
+ for query, _params in zip(queries, params):
270
+ params, pre_bind_params = _split_params(_params)
271
+
272
+ if pre_bind_params is not None:
273
+ if not isinstance(query, SQL):
274
+ query = SQL(query)
275
+ # Pre-bind the parameters using PsycoPG2
276
+ query = query.format(**pre_bind_params)
277
+
278
+ if isinstance(query, (SQL, Composed)):
279
+ query = _render_query(query, connectable)
280
+
281
+ sql_text = str(query)
282
+ if isinstance(query, str):
283
+ sql_text = format(query, strip_comments=True).strip()
284
+ if sql_text == "":
285
+ continue
286
+ # Check for server-bound parameters in sql native style. If there are none, use
287
+ # the SQLAlchemy text() function, otherwise use the raw query string
288
+ if has_server_binds is None:
289
+ has_server_binds = infer_has_server_binds(sql_text)
290
+
270
291
  # This only does something for postgresql, but it's harmless to run it for other engines
271
292
  set_wait_callback(wait_select)
272
293
 
@@ -275,27 +296,6 @@ def _run_sql(connectable, sql, params=None, **kwargs):
275
296
  except InvalidRequestError:
276
297
  trans = None
277
298
  try:
278
- params, pre_bind_params = _split_params(params)
279
-
280
- if pre_bind_params is not None:
281
- if not isinstance(query, SQL):
282
- query = SQL(query)
283
- # Pre-bind the parameters using PsycoPG2
284
- query = query.format(**pre_bind_params)
285
-
286
- if isinstance(query, (SQL, Composed)):
287
- query = _render_query(query, connectable)
288
-
289
- sql_text = str(query)
290
- if isinstance(query, str):
291
- sql_text = format(query, strip_comments=True).strip()
292
- if sql_text == "":
293
- continue
294
- # Check for server-bound parameters in sql native style. If there are none, use
295
- # the SQLAlchemy text() function, otherwise use the raw query string
296
- if has_server_binds is None:
297
- has_server_binds = infer_has_server_binds(sql_text)
298
-
299
299
  log.debug("Executing SQL: \n %s", query)
300
300
  if has_server_binds:
301
301
  conn = _get_connection(connectable)
@@ -310,24 +310,51 @@ def _run_sql(connectable, sql, params=None, **kwargs):
310
310
  elif hasattr(connectable, "commit"):
311
311
  connectable.commit()
312
312
  pretty_print(sql_text, dim=True)
313
- except (ProgrammingError, IntegrityError, InternalError, OperationalError) as err:
314
- _err = str(err.orig).strip()
315
- dim = "already exists" in _err
313
+ except Exception as err:
316
314
  if trans is not None:
317
315
  trans.rollback()
318
316
  elif hasattr(connectable, "rollback"):
319
317
  connectable.rollback()
320
- pretty_print(sql_text, fg=None if dim else "red", dim=True)
321
- if dim:
322
- _err = " " + _err
323
- secho(_err, fg="red", dim=dim)
324
- log.error(err)
325
- if raise_errors:
318
+ if raise_errors or _should_raise_query_error(err):
326
319
  raise err
320
+
321
+ _print_error(sql_text, err)
327
322
  finally:
328
323
  set_wait_callback(None)
329
324
 
330
325
 
326
+ def _should_raise_query_error(err):
327
+ """Determine if an error should be raised for a query or not."""
328
+ if not isinstance(err, (ProgrammingError, IntegrityError, InternalError, OperationalError)):
329
+ return True
330
+
331
+ orig_err = getattr(err, "orig", None)
332
+ if orig_err is None:
333
+ return True
334
+
335
+ # If we cancel statements midstream, we should raise the error.
336
+ # We might want to change this behavior in the future.
337
+ # Ideally we could handle operational errors more gracefully
338
+ if isinstance(orig_err, psycopg2.errors.QueryCanceled) or orig_err.pgcode == "57014":
339
+ return True
340
+
341
+ return False
342
+
343
+
344
+ def _print_error(sql_text, err):
345
+ if orig := getattr(err, "orig", None):
346
+ _err = str(orig)
347
+ else:
348
+ _err = str(err)
349
+ _err = _err.strip()
350
+ dim = "already exists" in _err
351
+ pretty_print(sql_text, fg=None if dim else "red", dim=True)
352
+ if dim:
353
+ _err = " " + _err
354
+ secho(_err, fg="red", dim=dim)
355
+ log.error(err)
356
+
357
+
331
358
  def run_sql_file(connectable, filename, params=None, **kwargs):
332
359
  return run_sql(connectable, filename, params, interpret_as_file=True, **kwargs)
333
360
 
@@ -3,7 +3,7 @@ authors = ["Daven Quinn <dev@davenquinn.com>"]
3
3
  description = "A SQLAlchemy-based database toolkit."
4
4
  name = "macrostrat.database"
5
5
  packages = [{ include = "macrostrat" }]
6
- version = "3.3.1"
6
+ version = "3.3.2"
7
7
 
8
8
  [tool.poetry.dependencies]
9
9
  GeoAlchemy2 = "^0.14.0"