macrostrat.database 3.3.0__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.0
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
@@ -17,6 +18,7 @@ from sqlalchemy.exc import (
17
18
  InternalError,
18
19
  InvalidRequestError,
19
20
  ProgrammingError,
21
+ OperationalError
20
22
  )
21
23
  from sqlalchemy.orm import sessionmaker
22
24
  from sqlalchemy.schema import Table
@@ -264,8 +266,28 @@ def _run_sql(connectable, sql, params=None, **kwargs):
264
266
  if not isinstance(params, list) or not len(params) == len(queries):
265
267
  params = [params] * len(queries)
266
268
 
267
- for query, params in zip(queries, params):
268
- 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
+
269
291
  # This only does something for postgresql, but it's harmless to run it for other engines
270
292
  set_wait_callback(wait_select)
271
293
 
@@ -274,27 +296,6 @@ def _run_sql(connectable, sql, params=None, **kwargs):
274
296
  except InvalidRequestError:
275
297
  trans = None
276
298
  try:
277
- params, pre_bind_params = _split_params(params)
278
-
279
- if pre_bind_params is not None:
280
- if not isinstance(query, SQL):
281
- query = SQL(query)
282
- # Pre-bind the parameters using PsycoPG2
283
- query = query.format(**pre_bind_params)
284
-
285
- if isinstance(query, (SQL, Composed)):
286
- query = _render_query(query, connectable)
287
-
288
- sql_text = str(query)
289
- if isinstance(query, str):
290
- sql_text = format(query, strip_comments=True).strip()
291
- if sql_text == "":
292
- continue
293
- # Check for server-bound parameters in sql native style. If there are none, use
294
- # the SQLAlchemy text() function, otherwise use the raw query string
295
- if has_server_binds is None:
296
- has_server_binds = infer_has_server_binds(sql_text)
297
-
298
299
  log.debug("Executing SQL: \n %s", query)
299
300
  if has_server_binds:
300
301
  conn = _get_connection(connectable)
@@ -309,24 +310,51 @@ def _run_sql(connectable, sql, params=None, **kwargs):
309
310
  elif hasattr(connectable, "commit"):
310
311
  connectable.commit()
311
312
  pretty_print(sql_text, dim=True)
312
- except (ProgrammingError, IntegrityError, InternalError) as err:
313
- _err = str(err.orig).strip()
314
- dim = "already exists" in _err
313
+ except Exception as err:
315
314
  if trans is not None:
316
315
  trans.rollback()
317
316
  elif hasattr(connectable, "rollback"):
318
317
  connectable.rollback()
319
- pretty_print(sql_text, fg=None if dim else "red", dim=True)
320
- if dim:
321
- _err = " " + _err
322
- secho(_err, fg="red", dim=dim)
323
- log.error(err)
324
- if raise_errors:
318
+ if raise_errors or _should_raise_query_error(err):
325
319
  raise err
320
+
321
+ _print_error(sql_text, err)
326
322
  finally:
327
323
  set_wait_callback(None)
328
324
 
329
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
+
330
358
  def run_sql_file(connectable, filename, params=None, **kwargs):
331
359
  return run_sql(connectable, filename, params, interpret_as_file=True, **kwargs)
332
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.0"
6
+ version = "3.3.2"
7
7
 
8
8
  [tool.poetry.dependencies]
9
9
  GeoAlchemy2 = "^0.14.0"