execsql2 2.15.4__py3-none-any.whl → 2.15.6__py3-none-any.whl

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.
Files changed (23) hide show
  1. execsql/script/variables.py +36 -0
  2. execsql/utils/datetime.py +12 -0
  3. {execsql2-2.15.4.dist-info → execsql2-2.15.6.dist-info}/METADATA +1 -1
  4. {execsql2-2.15.4.dist-info → execsql2-2.15.6.dist-info}/RECORD +23 -23
  5. {execsql2-2.15.4.data → execsql2-2.15.6.data}/data/execsql2_extras/README.md +0 -0
  6. {execsql2-2.15.4.data → execsql2-2.15.6.data}/data/execsql2_extras/config_settings.sqlite +0 -0
  7. {execsql2-2.15.4.data → execsql2-2.15.6.data}/data/execsql2_extras/example_config_prompt.sql +0 -0
  8. {execsql2-2.15.4.data → execsql2-2.15.6.data}/data/execsql2_extras/execsql.conf +0 -0
  9. {execsql2-2.15.4.data → execsql2-2.15.6.data}/data/execsql2_extras/make_config_db.sql +0 -0
  10. {execsql2-2.15.4.data → execsql2-2.15.6.data}/data/execsql2_extras/md_compare.sql +0 -0
  11. {execsql2-2.15.4.data → execsql2-2.15.6.data}/data/execsql2_extras/md_glossary.sql +0 -0
  12. {execsql2-2.15.4.data → execsql2-2.15.6.data}/data/execsql2_extras/md_upsert.sql +0 -0
  13. {execsql2-2.15.4.data → execsql2-2.15.6.data}/data/execsql2_extras/pg_compare.sql +0 -0
  14. {execsql2-2.15.4.data → execsql2-2.15.6.data}/data/execsql2_extras/pg_glossary.sql +0 -0
  15. {execsql2-2.15.4.data → execsql2-2.15.6.data}/data/execsql2_extras/pg_upsert.sql +0 -0
  16. {execsql2-2.15.4.data → execsql2-2.15.6.data}/data/execsql2_extras/script_template.sql +0 -0
  17. {execsql2-2.15.4.data → execsql2-2.15.6.data}/data/execsql2_extras/ss_compare.sql +0 -0
  18. {execsql2-2.15.4.data → execsql2-2.15.6.data}/data/execsql2_extras/ss_glossary.sql +0 -0
  19. {execsql2-2.15.4.data → execsql2-2.15.6.data}/data/execsql2_extras/ss_upsert.sql +0 -0
  20. {execsql2-2.15.4.dist-info → execsql2-2.15.6.dist-info}/WHEEL +0 -0
  21. {execsql2-2.15.4.dist-info → execsql2-2.15.6.dist-info}/entry_points.txt +0 -0
  22. {execsql2-2.15.4.dist-info → execsql2-2.15.6.dist-info}/licenses/LICENSE.txt +0 -0
  23. {execsql2-2.15.4.dist-info → execsql2-2.15.6.dist-info}/licenses/NOTICE +0 -0
@@ -224,6 +224,11 @@ class SubVarSet:
224
224
  dict. This is O(1) per call instead of O(V) where V is the number of
225
225
  defined variables.
226
226
 
227
+ Falls back to a per-variable substring scan when ``_TOKEN_RX`` finds no
228
+ match — this handles nested variable names like
229
+ ``!!N_!!CHECK_GROUP!!_CHECKS!!`` where the inner ``!!CHECK_GROUP!!``
230
+ must be resolved first.
231
+
227
232
  Returns ``(modified_string, True)`` if a substitution was made, or
228
233
  ``(original_string, False)`` if no variable pattern matched.
229
234
  """
@@ -249,6 +254,37 @@ class SubVarSet:
249
254
  return command_str[: m.start()] + sub + command_str[m.end() :], True
250
255
  # Token found but variable not defined — skip it and keep searching.
251
256
  m = self._TOKEN_RX.search(command_str, m.end())
257
+ # Fallback: per-variable substring scan for nested tokens like
258
+ # !!N_!!CHECK_GROUP!!_CHECKS!! where _TOKEN_RX cannot find the inner
259
+ # variable. Matches original monolith behavior.
260
+ return self._substitute_nested(command_str)
261
+
262
+ def _substitute_nested(self, command_str: str) -> tuple:
263
+ """Scan for any defined variable as a substring — handles nested tokens."""
264
+ for varname, sub in self._subs_dict.items():
265
+ if sub is None:
266
+ sub = ""
267
+ sub = str(sub)
268
+ if os.name != "posix":
269
+ sub = sub.replace("\\", "\\\\")
270
+ pat = re.compile(re.escape(f"!!{varname}!!"), re.I)
271
+ m = pat.search(command_str)
272
+ if m:
273
+ return command_str[: m.start()] + sub + command_str[m.end() :], True
274
+ patq = re.compile(re.escape(f"!'!{varname}!'!"), re.I)
275
+ mq = patq.search(command_str)
276
+ if mq:
277
+ return (
278
+ command_str[: mq.start()] + sub.replace("'", "''") + command_str[mq.end() :],
279
+ True,
280
+ )
281
+ patdq = re.compile(re.escape(f'!"!{varname}!"!'), re.I)
282
+ mdq = patdq.search(command_str)
283
+ if mdq:
284
+ return (
285
+ command_str[: mdq.start()] + '"' + sub + '"' + command_str[mdq.end() :],
286
+ True,
287
+ )
252
288
  return command_str, False
253
289
 
254
290
  def substitute_all(self, any_text: str) -> tuple:
execsql/utils/datetime.py CHANGED
@@ -25,12 +25,22 @@ __all__ = ["parse_datetime", "parse_datetimetz"]
25
25
  # misidentified as timestamps.
26
26
  _NUMERIC_ONLY = re.compile(r"^[+-]?\d+\.?\d*$")
27
27
 
28
+ # Match time-only strings like "13:15:45", "9:30", "1:15:45.123", "09:30 AM".
29
+ # dateutil parses these by filling in today's date, which causes DT_Timestamp
30
+ # to claim the column before DT_Time gets a chance.
31
+ _TIME_ONLY = re.compile(r"^\d{1,2}:\d{2}(?::\d{2}(?:\.\d+)?)?\s*(?:[AaPp][Mm])?$")
32
+
28
33
 
29
34
  def _looks_numeric(s: str) -> bool:
30
35
  """Return True if *s* is a bare number that should not be parsed as a date."""
31
36
  return bool(_NUMERIC_ONLY.match(s.strip()))
32
37
 
33
38
 
39
+ def _looks_time_only(s: str) -> bool:
40
+ """Return True if *s* is a time-only string (no date component)."""
41
+ return bool(_TIME_ONLY.match(s.strip()))
42
+
43
+
34
44
  def parse_datetime(datestr: Any) -> datetime.datetime | None:
35
45
  """Parse a date/time string into a :class:`datetime.datetime`.
36
46
 
@@ -53,6 +63,8 @@ def parse_datetime(datestr: Any) -> datetime.datetime | None:
53
63
  return None
54
64
  if _looks_numeric(datestr):
55
65
  return None
66
+ if _looks_time_only(datestr):
67
+ return None
56
68
  try:
57
69
  return _dateutil_parser.parse(datestr)
58
70
  except (ValueError, OverflowError, TypeError):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: execsql2
3
- Version: 2.15.4
3
+ Version: 2.15.6
4
4
  Summary: Runs a SQL script against a PostgreSQL, SQLite, MariaDB/MySQL, DuckDB, Firebird, MS-Access, MS-SQL-Server, or Oracle database, or an ODBC DSN. Provides metacommands to import and export data, copy data between databases, conditionally execute SQL and metacommands, and dynamically alter SQL and metacommands with substitution variables.
5
5
  Project-URL: Homepage, https://execsql2.readthedocs.io
6
6
  Project-URL: Repository, https://github.com/geocoug/execsql
@@ -80,11 +80,11 @@ execsql/metacommands/upsert.py,sha256=imXKk2A9TuG4rS_mr40_g2A1IY9iOMKhFYkAm4n3dF
80
80
  execsql/script/__init__.py,sha256=HbVQmQEVn4gBtzwy5_nlbDGuRnbWd4dI4nG-q1KyBxs,3498
81
81
  execsql/script/control.py,sha256=s-1eZdGARM6H1FwZ6VDdO_f50j7bvvRtTHesfUm9tbc,6144
82
82
  execsql/script/engine.py,sha256=Y43vwjg3guBmNLqjopYDpjyf-tjewBQYcmXTGWPGJc4,43133
83
- execsql/script/variables.py,sha256=gTCCWY64LFmQUna-63CM1GAbupcaOTSS4cn6HDaHk9Q,11923
83
+ execsql/script/variables.py,sha256=GU4tnuyNuJP42z6JdZR5QFYfsrkmRRL-GVbYIREa_mY,13588
84
84
  execsql/utils/__init__.py,sha256=0uR6JwVJQRX3vceByNBduCAf5dd5assKjeqJUWvpZoA,278
85
85
  execsql/utils/auth.py,sha256=onXzNkNZQZxGC5w7eey06sjvAIAX_Lf9g7nUJtcsel0,7009
86
86
  execsql/utils/crypto.py,sha256=2OnBWwn9bCBGc1ZkyRv16TvhottoCNYtXqgbE3mG3Sg,2960
87
- execsql/utils/datetime.py,sha256=S-9H-jDgzOA6SyQZZdjNltErWQobuUa7D0xM9Z2-meA,3070
87
+ execsql/utils/datetime.py,sha256=rMCXAbvj6bxKCYzC97vrludO6PU5DYQ39buZ0smDC5A,3573
88
88
  execsql/utils/errors.py,sha256=cIYU7mKgjrRy5D2D4aDFHsH__lToz4Nv3fLQ62R1V2M,7786
89
89
  execsql/utils/fileio.py,sha256=U8ZtACgaynO5FchixhwZ3KOux337u_crwh4B4NJzra4,23864
90
90
  execsql/utils/gui.py,sha256=eZeFJm8EaWnzeHIw_O-tn9hO8sxGjZRX_aUFDtGQp4w,18396
@@ -93,24 +93,24 @@ execsql/utils/numeric.py,sha256=xh02ANSRk3nUpQ-rtm66ILoMqoi7HtzCoRMIOT9U8QI,1570
93
93
  execsql/utils/regex.py,sha256=diEzTZqU_HHwVMadPAvN1Vgzhl7I03eVaEFGCXyGGL8,3770
94
94
  execsql/utils/strings.py,sha256=5Dvzrk-9SIw2lpxXZQkiJbNyo1sy7iXXAtSULlZ0KG8,8488
95
95
  execsql/utils/timer.py,sha256=eDYf5VzCNFk7oo90InJucUm3XcBdhYMogjZMqeg9xzc,1899
96
- execsql2-2.15.4.data/data/execsql2_extras/README.md,sha256=sxwVyU0ZahCfANv56LahkyuM505kFjrMhe-1SvWE69E,4845
97
- execsql2-2.15.4.data/data/execsql2_extras/config_settings.sqlite,sha256=aY5cxR7Q7J6zJ4bC9lu5mHUrhy211Cq3MNKPQVCt02E,20480
98
- execsql2-2.15.4.data/data/execsql2_extras/example_config_prompt.sql,sha256=SY3Jxn1qcVm4kPW9xmmTfknHfvURXmeEYTbRjYrjGSw,7487
99
- execsql2-2.15.4.data/data/execsql2_extras/execsql.conf,sha256=_45iJ-KWZnB8uMW_gEg067MM5pmGJ-dVl7VbAZMunAE,9530
100
- execsql2-2.15.4.data/data/execsql2_extras/make_config_db.sql,sha256=WwyC6dK-Eh5CAVppiBCDHqiI1_wEI9U95Ytpr4lsZkg,8726
101
- execsql2-2.15.4.data/data/execsql2_extras/md_compare.sql,sha256=B8Wd7LZ0vnMY2qvA139JIEBkPObgRH2i5xj6PejTQt8,24092
102
- execsql2-2.15.4.data/data/execsql2_extras/md_glossary.sql,sha256=DJRHcU5NbFpxTTX-IwH3yRlsboj1q6BBGrUAHKn4Cuo,10796
103
- execsql2-2.15.4.data/data/execsql2_extras/md_upsert.sql,sha256=v_7GbWh_N1mBTmw3gvTrkagOVp2q0KmXvM8hE-DlFxY,112524
104
- execsql2-2.15.4.data/data/execsql2_extras/pg_compare.sql,sha256=9dWa8hnfy5dVJI-z2iGpd9JzQmI4j2ziMlEdpnr66ro,24352
105
- execsql2-2.15.4.data/data/execsql2_extras/pg_glossary.sql,sha256=pKjIIDsROAgJq2H-1qNEcRMAWManivcZ_AEVHzUUlic,9908
106
- execsql2-2.15.4.data/data/execsql2_extras/pg_upsert.sql,sha256=k7AFiGTLBy3nf-qO5QIaZrEYTAKvdxxU3JDLx9jqkzs,108315
107
- execsql2-2.15.4.data/data/execsql2_extras/script_template.sql,sha256=1Estacb_vm1FgK41k_G9nuduP1yiA-fQ1Kn4Z4mv5Ao,11153
108
- execsql2-2.15.4.data/data/execsql2_extras/ss_compare.sql,sha256=TsVxWm3cEpR5-EiMYXNhtaY0arSNeKZhsJdHdLA7xeI,24833
109
- execsql2-2.15.4.data/data/execsql2_extras/ss_glossary.sql,sha256=cLM7nN8JOIu9ZVP9oY9qdSK3hrnWJiDcX6nZmQQbQWI,13065
110
- execsql2-2.15.4.data/data/execsql2_extras/ss_upsert.sql,sha256=BCqmBykXBF-BpCgOFeG1qhf2XfScKsxPD17wd1hYfHw,120647
111
- execsql2-2.15.4.dist-info/METADATA,sha256=nOzzqqR1eTzBlML7GXf37J6ZMWGXQnXR9s4r6mz41Qc,18114
112
- execsql2-2.15.4.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
113
- execsql2-2.15.4.dist-info/entry_points.txt,sha256=sUOxkM-dN1eBGGpSpDLsAaE0yNXYQKWZAfxPOlMkQyk,90
114
- execsql2-2.15.4.dist-info/licenses/LICENSE.txt,sha256=LBdhuxejF8_bLCHZ2kWfmDXpDGUu914Gbd6_3JjCRe0,676
115
- execsql2-2.15.4.dist-info/licenses/NOTICE,sha256=sqVrM73Ys9zfvWC_P797lHfTnoPW_ETeBSrUTFaob0A,339
116
- execsql2-2.15.4.dist-info/RECORD,,
96
+ execsql2-2.15.6.data/data/execsql2_extras/README.md,sha256=sxwVyU0ZahCfANv56LahkyuM505kFjrMhe-1SvWE69E,4845
97
+ execsql2-2.15.6.data/data/execsql2_extras/config_settings.sqlite,sha256=aY5cxR7Q7J6zJ4bC9lu5mHUrhy211Cq3MNKPQVCt02E,20480
98
+ execsql2-2.15.6.data/data/execsql2_extras/example_config_prompt.sql,sha256=SY3Jxn1qcVm4kPW9xmmTfknHfvURXmeEYTbRjYrjGSw,7487
99
+ execsql2-2.15.6.data/data/execsql2_extras/execsql.conf,sha256=_45iJ-KWZnB8uMW_gEg067MM5pmGJ-dVl7VbAZMunAE,9530
100
+ execsql2-2.15.6.data/data/execsql2_extras/make_config_db.sql,sha256=WwyC6dK-Eh5CAVppiBCDHqiI1_wEI9U95Ytpr4lsZkg,8726
101
+ execsql2-2.15.6.data/data/execsql2_extras/md_compare.sql,sha256=B8Wd7LZ0vnMY2qvA139JIEBkPObgRH2i5xj6PejTQt8,24092
102
+ execsql2-2.15.6.data/data/execsql2_extras/md_glossary.sql,sha256=DJRHcU5NbFpxTTX-IwH3yRlsboj1q6BBGrUAHKn4Cuo,10796
103
+ execsql2-2.15.6.data/data/execsql2_extras/md_upsert.sql,sha256=v_7GbWh_N1mBTmw3gvTrkagOVp2q0KmXvM8hE-DlFxY,112524
104
+ execsql2-2.15.6.data/data/execsql2_extras/pg_compare.sql,sha256=9dWa8hnfy5dVJI-z2iGpd9JzQmI4j2ziMlEdpnr66ro,24352
105
+ execsql2-2.15.6.data/data/execsql2_extras/pg_glossary.sql,sha256=pKjIIDsROAgJq2H-1qNEcRMAWManivcZ_AEVHzUUlic,9908
106
+ execsql2-2.15.6.data/data/execsql2_extras/pg_upsert.sql,sha256=k7AFiGTLBy3nf-qO5QIaZrEYTAKvdxxU3JDLx9jqkzs,108315
107
+ execsql2-2.15.6.data/data/execsql2_extras/script_template.sql,sha256=1Estacb_vm1FgK41k_G9nuduP1yiA-fQ1Kn4Z4mv5Ao,11153
108
+ execsql2-2.15.6.data/data/execsql2_extras/ss_compare.sql,sha256=TsVxWm3cEpR5-EiMYXNhtaY0arSNeKZhsJdHdLA7xeI,24833
109
+ execsql2-2.15.6.data/data/execsql2_extras/ss_glossary.sql,sha256=cLM7nN8JOIu9ZVP9oY9qdSK3hrnWJiDcX6nZmQQbQWI,13065
110
+ execsql2-2.15.6.data/data/execsql2_extras/ss_upsert.sql,sha256=BCqmBykXBF-BpCgOFeG1qhf2XfScKsxPD17wd1hYfHw,120647
111
+ execsql2-2.15.6.dist-info/METADATA,sha256=UcC6w7L_1vV23MSGVd44R4lnLGN_noBbjXKloUdOTMA,18114
112
+ execsql2-2.15.6.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
113
+ execsql2-2.15.6.dist-info/entry_points.txt,sha256=sUOxkM-dN1eBGGpSpDLsAaE0yNXYQKWZAfxPOlMkQyk,90
114
+ execsql2-2.15.6.dist-info/licenses/LICENSE.txt,sha256=LBdhuxejF8_bLCHZ2kWfmDXpDGUu914Gbd6_3JjCRe0,676
115
+ execsql2-2.15.6.dist-info/licenses/NOTICE,sha256=sqVrM73Ys9zfvWC_P797lHfTnoPW_ETeBSrUTFaob0A,339
116
+ execsql2-2.15.6.dist-info/RECORD,,