Skip to content

Commit d45ec34

Browse files
committed
Release v6.2.0
1 parent dc04ea4 commit d45ec34

File tree

9 files changed

+344
-223
lines changed

9 files changed

+344
-223
lines changed

CHANGES.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,15 @@
22
Change History
33
==============
44

5+
6.2.0 (2024-01-25)
6+
------------------
7+
8+
New features:
9+
10+
- `Pull #13`_: ADD SQL Server style named parameter.
11+
12+
.. _`Pull #13`: https://github.com/cpburnz/python-sqlparams/pull/13
13+
514

615
6.1.0 (2024-08-17)
716
------------------

Makefile

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -67,18 +67,15 @@ dev-venv-create: dev-venv-base dev-venv-install
6767

6868
dev-venv-install:
6969
${VENV} pip3 install --upgrade pip setuptools wheel
70-
${VENV} pip3 install --upgrade build pypi_attestations sphinx tox twine typing-extensions
70+
${VENV} pip3 install --upgrade build sphinx tox twine typing-extensions
7171
${VENV} pip3 install -e "${SRC_DIR}" -C editable_mode=compat
7272

7373

7474
################################################################################
7575
# Distribution
7676
################################################################################
7777

78-
.PHONY: dist-attest dist-build dist-prebuild dist-publish
79-
80-
dist-attest: dist-build
81-
${VENV} python -m pypi_attestations sign ./dist/*
78+
.PHONY: dist-build dist-prebuild dist-publish
8279

8380
dist-build: dist-prebuild
8481
find ./dist -type f -delete
@@ -87,6 +84,6 @@ dist-build: dist-prebuild
8784
dist-prebuild:
8885
${VENV} python ./prebuild.py
8986

90-
dist-publish: dist-attest
87+
dist-publish:
9188
${VENV} twine check ./dist/*
92-
${VENV} twine upload -r sqlparams --attestations --skip-existing ./dist/*
89+
${VENV} twine upload -r sqlparams --skip-existing ./dist/*

README-dist.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,15 @@ Documentation for *sqlparams* is available on `Read the Docs`_.
105105
Change History
106106
==============
107107

108+
6.2.0 (2024-01-25)
109+
------------------
110+
111+
New features:
112+
113+
- `Pull #13`_: ADD SQL Server style named parameter.
114+
115+
.. _`Pull #13`: https://github.com/cpburnz/python-sqlparams/pull/13
116+
108117

109118
6.1.0 (2024-08-17)
110119
------------------

sqlparams/__init__.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,13 @@ def __init__(
135135
136136
.. NOTE:: This is not defined by `PEP 249`_.
137137
138+
- "named_sqlserver" indicates parameters will use the named at-sign style
139+
supported by Microsoft SQL Server::
140+
141+
... WHERE name = @name
142+
143+
.. NOTE:: This is not defined by `PEP 249`_.
144+
138145
- "pyformat" indicates parameters will use the named Python extended
139146
format style::
140147

sqlparams/_meta.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"""
44

55
__author__ = "Caleb P. Burns"
6-
__copyright__ = "Copyright © 2012-2024 by Caleb P. Burns"
6+
__copyright__ = "Copyright © 2012-2025 by Caleb P. Burns"
77
__credits__ = [
88
"khomyakov42 <https://github.com/khomyakov42>",
99
"pedermoller <https://github.com/pedermoller>",
@@ -15,4 +15,4 @@
1515
"dedabob <https://github.com/dedabob>",
1616
]
1717
__license__ = "MIT License"
18-
__version__ = "6.1.1"
18+
__version__ = "6.2.0"

sqlparams/_styles.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ class OrdinalStyle(Style):
128128
name="named_oracle",
129129
escape_char=":",
130130
escape_regex="(?P<escape>{char}:)",
131-
param_regex=r'(?<!:):(?P<quote>"?)(?P<param>[A-Za-z_]\w*)(?P=quote)',
131+
param_regex='(?<!:):(?P<quote>"?)(?P<param>[A-Za-z_]\\w*)(?P=quote)',
132132
out_format=":{param}",
133133
param_quotes=True
134134
)
@@ -138,7 +138,7 @@ class OrdinalStyle(Style):
138138
name="named_sqlserver",
139139
escape_char="@",
140140
escape_regex="(?P<escape>{char}@)",
141-
param_regex=r'(?<!@)@(?P<param>[A-Za-z_]\w*)',
141+
param_regex="(?<!@)@(?P<param>[A-Za-z_]\\w*)",
142142
out_format="@{param}"
143143
)
144144

tests/test_2_named_to_ordinal.py

Lines changed: 118 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,82 @@ def test_1_named_oracle_2_to_qmark_3_many_mixed(self):
408408
self.assertEqual(sql, dest_sql)
409409
self.assertEqual(many_params, dest_params)
410410

411+
def test_1_named_sqlserver_to_qmark(self):
412+
"""
413+
Test converting from::
414+
415+
... WHERE name = @name
416+
417+
to::
418+
419+
... WHERE name = ?
420+
"""
421+
# Create instance.
422+
query = sqlparams.SQLParams('named_sqlserver', 'qmark')
423+
424+
# Source SQL and params.
425+
src_sql = """
426+
SELECT *
427+
FROM users
428+
WHERE name = @name OR id = @id;
429+
"""
430+
src_params = {'id': 5, 'name': "Dori"}
431+
432+
# Desired SQL and params.
433+
dest_sql = """
434+
SELECT *
435+
FROM users
436+
WHERE name = ? OR id = ?;
437+
"""
438+
dest_params = [src_params['name'], src_params['id']]
439+
440+
# Format SQL with params.
441+
sql, params = query.format(src_sql, src_params)
442+
443+
# Make sure desired SQL and parameters are created.
444+
self.assertEqual(sql, dest_sql)
445+
self.assertEqual(params, dest_params)
446+
447+
def test_1_named_sqlserver_to_qmark_many(self):
448+
"""
449+
Test converting from::
450+
451+
... WHERE name = @name
452+
453+
to::
454+
455+
... WHERE name = ?
456+
"""
457+
# Create instance.
458+
query = sqlparams.SQLParams('named_sqlserver', 'qmark')
459+
460+
# Source SQL and params.
461+
src_sql = """
462+
SELECT *
463+
FROM users
464+
WHERE name = @name OR id = @id;
465+
"""
466+
src_params = [
467+
{'id': 7, 'name': "Ori"},
468+
{'id': 5, 'name': "Dori"},
469+
{'id': 10, 'name': "Bifur"},
470+
]
471+
472+
# Desired SQL and params.
473+
dest_sql = """
474+
SELECT *
475+
FROM users
476+
WHERE name = ? OR id = ?;
477+
"""
478+
dest_params = [[__row['name'], __row['id']] for __row in src_params]
479+
480+
# Format SQL with params.
481+
sql, many_params = query.formatmany(src_sql, src_params)
482+
483+
# Make sure desired SQL and parameters are created.
484+
self.assertEqual(sql, dest_sql)
485+
self.assertEqual(many_params, dest_params)
486+
411487
def test_1_pyformat_to_format(self):
412488
"""
413489
Test converting from::
@@ -880,18 +956,18 @@ def test_4_named_escape_char_disabled(self):
880956
self.assertEqual(sql, dest_sql)
881957
self.assertEqual(params, dest_params)
882958

883-
def test_4_pyformat_escape_char(self):
959+
def test_4_named_sqlserver_escape_char(self):
884960
"""
885-
Test escaping a pyformat parameter.
961+
Test escaping a named sqlserver parameter.
886962
"""
887963
# Create instance.
888-
query = sqlparams.SQLParams('pyformat', 'qmark', escape_char=True)
964+
query = sqlparams.SQLParams('named_sqlserver', 'qmark', escape_char=True)
889965

890966
# Source SQL and params.
891967
src_sql = """
892968
SELECT *
893969
FROM users
894-
WHERE name = %(name)s AND tag IN ('%%(Y2941)s', '%%(2941)s');
970+
WHERE name = @name AND tag IN ('@@Y2941', '@@2941');
895971
"""
896972
name = "Bilbo"
897973
src_params = {'name': name}
@@ -900,7 +976,7 @@ def test_4_pyformat_escape_char(self):
900976
dest_sql = """
901977
SELECT *
902978
FROM users
903-
WHERE name = ? AND tag IN ('%(Y2941)s', '%(2941)s');
979+
WHERE name = ? AND tag IN ('@Y2941', '@2941');
904980
"""
905981
dest_params = [name]
906982

@@ -911,18 +987,18 @@ def test_4_pyformat_escape_char(self):
911987
self.assertEqual(sql, dest_sql)
912988
self.assertEqual(params, dest_params)
913989

914-
def test_4_pyformat_escape_char_disabled(self):
990+
def test_4_named_sqlserver_escape_char_disabled(self):
915991
"""
916-
Test disabling escaping of a pyformat parameter.
992+
Test disabling escaping of a named sqlserver parameter.
917993
"""
918994
# Create instance.
919-
query = sqlparams.SQLParams('pyformat', 'qmark', escape_char=False)
995+
query = sqlparams.SQLParams('named_sqlserver', 'qmark', escape_char=False)
920996

921997
# Source SQL and params.
922998
src_sql = """
923999
SELECT *
9241000
FROM users
925-
WHERE name = %(name)s AND tag IN ('%%(Y2941)s', '%(2941)s');
1001+
WHERE name = @name AND tag IN ('@@Y2941', '@2941');
9261002
"""
9271003
name = "Bilbo"
9281004
src_params = {'name': name}
@@ -931,7 +1007,7 @@ def test_4_pyformat_escape_char_disabled(self):
9311007
dest_sql = """
9321008
SELECT *
9331009
FROM users
934-
WHERE name = ? AND tag IN ('%%(Y2941)s', '%(2941)s');
1010+
WHERE name = ? AND tag IN ('@@Y2941', '@2941');
9351011
"""
9361012
dest_params = [name]
9371013

@@ -942,31 +1018,29 @@ def test_4_pyformat_escape_char_disabled(self):
9421018
self.assertEqual(sql, dest_sql)
9431019
self.assertEqual(params, dest_params)
9441020

945-
def test_5_named_to_format_escaped_percent(self):
1021+
def test_4_pyformat_escape_char(self):
9461022
"""
947-
Test converting from::
948-
949-
SELECT 5 % :value
950-
951-
to::
952-
953-
SELECT 5 %% %s
1023+
Test escaping a pyformat parameter.
9541024
"""
9551025
# Create instance.
956-
query = sqlparams.SQLParams('named', 'format')
1026+
query = sqlparams.SQLParams('pyformat', 'qmark', escape_char=True)
9571027

9581028
# Source SQL and params.
9591029
src_sql = """
960-
SELECT 5 % :value;
1030+
SELECT *
1031+
FROM users
1032+
WHERE name = %(name)s AND tag IN ('%%(Y2941)s', '%%(2941)s');
9611033
"""
962-
value = 2
963-
src_params = {'value': value}
1034+
name = "Bilbo"
1035+
src_params = {'name': name}
9641036

9651037
# Desired SQL and params.
9661038
dest_sql = """
967-
SELECT 5 %% %s;
1039+
SELECT *
1040+
FROM users
1041+
WHERE name = ? AND tag IN ('%(Y2941)s', '%(2941)s');
9681042
"""
969-
dest_params = [value]
1043+
dest_params = [name]
9701044

9711045
# Format SQL with params.
9721046
sql, params = query.format(src_sql, src_params)
@@ -975,31 +1049,29 @@ def test_5_named_to_format_escaped_percent(self):
9751049
self.assertEqual(sql, dest_sql)
9761050
self.assertEqual(params, dest_params)
9771051

978-
def test_5_named_to_qmark_unescaped_percent(self):
1052+
def test_4_pyformat_escape_char_disabled(self):
9791053
"""
980-
Test converting from::
981-
982-
SELECT 5 % :value
983-
984-
to::
985-
986-
SELECT 5 % ?
1054+
Test disabling escaping of a pyformat parameter.
9871055
"""
9881056
# Create instance.
989-
query = sqlparams.SQLParams('named', 'qmark')
1057+
query = sqlparams.SQLParams('pyformat', 'qmark', escape_char=False)
9901058

9911059
# Source SQL and params.
9921060
src_sql = """
993-
SELECT 5 % :value;
1061+
SELECT *
1062+
FROM users
1063+
WHERE name = %(name)s AND tag IN ('%%(Y2941)s', '%(2941)s');
9941064
"""
995-
value = 2
996-
src_params = {'value': value}
1065+
name = "Bilbo"
1066+
src_params = {'name': name}
9971067

9981068
# Desired SQL and params.
9991069
dest_sql = """
1000-
SELECT 5 % ?;
1070+
SELECT *
1071+
FROM users
1072+
WHERE name = ? AND tag IN ('%%(Y2941)s', '%(2941)s');
10011073
"""
1002-
dest_params = [value]
1074+
dest_params = [name]
10031075

10041076
# Format SQL with params.
10051077
sql, params = query.format(src_sql, src_params)
@@ -1008,22 +1080,22 @@ def test_5_named_to_qmark_unescaped_percent(self):
10081080
self.assertEqual(sql, dest_sql)
10091081
self.assertEqual(params, dest_params)
10101082

1011-
def test_5_named_dollar_to_format_escaped_percent(self):
1083+
def test_5_named_to_format_escaped_percent(self):
10121084
"""
10131085
Test converting from::
10141086
1015-
SELECT 5 % $value
1087+
SELECT 5 % :value
10161088
10171089
to::
10181090
10191091
SELECT 5 %% %s
10201092
"""
10211093
# Create instance.
1022-
query = sqlparams.SQLParams('named_dollar', 'format')
1094+
query = sqlparams.SQLParams('named', 'format')
10231095

10241096
# Source SQL and params.
10251097
src_sql = """
1026-
SELECT 5 % $value;
1098+
SELECT 5 % :value;
10271099
"""
10281100
value = 2
10291101
src_params = {'value': value}
@@ -1041,22 +1113,22 @@ def test_5_named_dollar_to_format_escaped_percent(self):
10411113
self.assertEqual(sql, dest_sql)
10421114
self.assertEqual(params, dest_params)
10431115

1044-
def test_5_named_dollar_to_qmark_unescaped_percent(self):
1116+
def test_5_named_to_qmark_unescaped_percent(self):
10451117
"""
10461118
Test converting from::
10471119
1048-
SELECT 5 % $value
1120+
SELECT 5 % :value
10491121
10501122
to::
10511123
10521124
SELECT 5 % ?
10531125
"""
10541126
# Create instance.
1055-
query = sqlparams.SQLParams('named_dollar', 'qmark')
1127+
query = sqlparams.SQLParams('named', 'qmark')
10561128

10571129
# Source SQL and params.
10581130
src_sql = """
1059-
SELECT 5 % $value;
1131+
SELECT 5 % :value;
10601132
"""
10611133
value = 2
10621134
src_params = {'value': value}

0 commit comments

Comments
 (0)