t-sql 1.0.0__tar.gz → 1.0.1__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.
- t_sql-1.0.1/.dockerignore +128 -0
- t_sql-1.0.1/.gitignore +177 -0
- t_sql-1.0.1/.idea/.gitignore +8 -0
- t_sql-1.0.1/.idea/inspectionProfiles/Project_Default.xml +24 -0
- t_sql-1.0.1/.idea/inspectionProfiles/profiles_settings.xml +6 -0
- t_sql-1.0.1/.idea/misc.xml +11 -0
- t_sql-1.0.1/.idea/tsql.iml +10 -0
- t_sql-1.0.1/.idea/vcs.xml +6 -0
- t_sql-1.0.1/.idea/workspace.xml +92 -0
- t_sql-1.0.1/Dockerfile +8 -0
- {t_sql-1.0.0/t_sql.egg-info → t_sql-1.0.1}/PKG-INFO +2 -3
- t_sql-1.0.1/compose.yaml +14 -0
- {t_sql-1.0.0 → t_sql-1.0.1}/pyproject.toml +8 -1
- t_sql-1.0.1/pytest.ini +2 -0
- {t_sql-1.0.0 → t_sql-1.0.1}/tests/test_helper_functions.py +40 -4
- {t_sql-1.0.0 → t_sql-1.0.1}/tests/test_injections_for_escaped.py +14 -20
- {t_sql-1.0.0 → t_sql-1.0.1}/tsql/__init__.py +3 -3
- t_sql-1.0.0/PKG-INFO +0 -183
- t_sql-1.0.0/setup.cfg +0 -4
- t_sql-1.0.0/t_sql.egg-info/SOURCES.txt +0 -19
- t_sql-1.0.0/t_sql.egg-info/dependency_links.txt +0 -1
- t_sql-1.0.0/t_sql.egg-info/top_level.txt +0 -1
- {t_sql-1.0.0 → t_sql-1.0.1}/LICENSE +0 -0
- {t_sql-1.0.0 → t_sql-1.0.1}/README.md +0 -0
- {t_sql-1.0.0 → t_sql-1.0.1}/tests/test_asyncpg_integration.py +0 -0
- {t_sql-1.0.0 → t_sql-1.0.1}/tests/test_different_object_types.py +0 -0
- {t_sql-1.0.0 → t_sql-1.0.1}/tests/test_escaped.py +0 -0
- {t_sql-1.0.0 → t_sql-1.0.1}/tests/test_escaped_binary_hex.py +0 -0
- {t_sql-1.0.0 → t_sql-1.0.1}/tests/test_injection_edge_cases.py +0 -0
- {t_sql-1.0.0 → t_sql-1.0.1}/tests/test_injection_protection_validation.py +0 -0
- {t_sql-1.0.0 → t_sql-1.0.1}/tests/test_styles.py +0 -0
- {t_sql-1.0.0 → t_sql-1.0.1}/tests/test_tsql.py +0 -0
- {t_sql-1.0.0 → t_sql-1.0.1}/tsql/styles.py +0 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
.git
|
|
2
|
+
.venv
|
|
3
|
+
|
|
4
|
+
# Byte-compiled / optimized / DLL files
|
|
5
|
+
__pycache__/
|
|
6
|
+
*.py[cod]
|
|
7
|
+
*$py.class
|
|
8
|
+
|
|
9
|
+
# C extensions
|
|
10
|
+
*.so
|
|
11
|
+
|
|
12
|
+
# Distribution / packaging
|
|
13
|
+
.Python
|
|
14
|
+
build/
|
|
15
|
+
develop-eggs/
|
|
16
|
+
dist/
|
|
17
|
+
downloads/
|
|
18
|
+
eggs/
|
|
19
|
+
.eggs/
|
|
20
|
+
lib/
|
|
21
|
+
lib64/
|
|
22
|
+
parts/
|
|
23
|
+
sdist/
|
|
24
|
+
var/
|
|
25
|
+
wheels/
|
|
26
|
+
share/python-wheels/
|
|
27
|
+
*.egg-info/
|
|
28
|
+
.installed.cfg
|
|
29
|
+
*.egg
|
|
30
|
+
MANIFEST
|
|
31
|
+
|
|
32
|
+
# PyInstaller
|
|
33
|
+
# Usually these files are written by a python script from a template
|
|
34
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
35
|
+
*.manifest
|
|
36
|
+
*.spec
|
|
37
|
+
|
|
38
|
+
# Installer logs
|
|
39
|
+
pip-log.txt
|
|
40
|
+
pip-delete-this-directory.txt
|
|
41
|
+
|
|
42
|
+
# Unit test / coverage reports
|
|
43
|
+
htmlcov/
|
|
44
|
+
.tox/
|
|
45
|
+
.nox/
|
|
46
|
+
.coverage
|
|
47
|
+
.coverage.*
|
|
48
|
+
.cache
|
|
49
|
+
nosetests.xml
|
|
50
|
+
coverage.xml
|
|
51
|
+
*.cover
|
|
52
|
+
*.py,cover
|
|
53
|
+
.hypothesis/
|
|
54
|
+
.pytest_cache/
|
|
55
|
+
cover/
|
|
56
|
+
|
|
57
|
+
# Translations
|
|
58
|
+
*.mo
|
|
59
|
+
*.pot
|
|
60
|
+
|
|
61
|
+
# Django stuff:
|
|
62
|
+
*.log
|
|
63
|
+
local_settings.py
|
|
64
|
+
db.sqlite3
|
|
65
|
+
db.sqlite3-journal
|
|
66
|
+
|
|
67
|
+
# Flask stuff:
|
|
68
|
+
instance/
|
|
69
|
+
.webassets-cache
|
|
70
|
+
|
|
71
|
+
# Scrapy stuff:
|
|
72
|
+
.scrapy
|
|
73
|
+
|
|
74
|
+
# Sphinx documentation
|
|
75
|
+
docs/_build/
|
|
76
|
+
|
|
77
|
+
# PyBuilder
|
|
78
|
+
.pybuilder/
|
|
79
|
+
target/
|
|
80
|
+
|
|
81
|
+
# Jupyter Notebook
|
|
82
|
+
.ipynb_checkpoints
|
|
83
|
+
|
|
84
|
+
# IPython
|
|
85
|
+
profile_default/
|
|
86
|
+
ipython_config.py
|
|
87
|
+
|
|
88
|
+
# pyenv
|
|
89
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
90
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
91
|
+
# .python-version
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
# Environments
|
|
95
|
+
.env
|
|
96
|
+
.venv
|
|
97
|
+
env/
|
|
98
|
+
venv/
|
|
99
|
+
ENV/
|
|
100
|
+
env.bak/
|
|
101
|
+
venv.bak/
|
|
102
|
+
|
|
103
|
+
# mypy
|
|
104
|
+
.mypy_cache/
|
|
105
|
+
.dmypy.json
|
|
106
|
+
dmypy.json
|
|
107
|
+
|
|
108
|
+
# Pyre type checker
|
|
109
|
+
.pyre/
|
|
110
|
+
|
|
111
|
+
# pytype static type analyzer
|
|
112
|
+
.pytype/
|
|
113
|
+
|
|
114
|
+
# Cython debug symbols
|
|
115
|
+
cython_debug/
|
|
116
|
+
|
|
117
|
+
# PyCharm
|
|
118
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
119
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
120
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
121
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
122
|
+
#.idea/
|
|
123
|
+
|
|
124
|
+
# Ruff stuff:
|
|
125
|
+
.ruff_cache/
|
|
126
|
+
|
|
127
|
+
# PyPI configuration file
|
|
128
|
+
.pypirc
|
t_sql-1.0.1/.gitignore
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# ai
|
|
2
|
+
.claude/
|
|
3
|
+
|
|
4
|
+
# Byte-compiled / optimized / DLL files
|
|
5
|
+
__pycache__/
|
|
6
|
+
*.py[cod]
|
|
7
|
+
*$py.class
|
|
8
|
+
|
|
9
|
+
# C extensions
|
|
10
|
+
*.so
|
|
11
|
+
|
|
12
|
+
# Distribution / packaging
|
|
13
|
+
.Python
|
|
14
|
+
build/
|
|
15
|
+
develop-eggs/
|
|
16
|
+
dist/
|
|
17
|
+
downloads/
|
|
18
|
+
eggs/
|
|
19
|
+
.eggs/
|
|
20
|
+
lib/
|
|
21
|
+
lib64/
|
|
22
|
+
parts/
|
|
23
|
+
sdist/
|
|
24
|
+
var/
|
|
25
|
+
wheels/
|
|
26
|
+
share/python-wheels/
|
|
27
|
+
*.egg-info/
|
|
28
|
+
.installed.cfg
|
|
29
|
+
*.egg
|
|
30
|
+
MANIFEST
|
|
31
|
+
|
|
32
|
+
# PyInstaller
|
|
33
|
+
# Usually these files are written by a python script from a template
|
|
34
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
35
|
+
*.manifest
|
|
36
|
+
*.spec
|
|
37
|
+
|
|
38
|
+
# Installer logs
|
|
39
|
+
pip-log.txt
|
|
40
|
+
pip-delete-this-directory.txt
|
|
41
|
+
|
|
42
|
+
# Unit test / coverage reports
|
|
43
|
+
htmlcov/
|
|
44
|
+
.tox/
|
|
45
|
+
.nox/
|
|
46
|
+
.coverage
|
|
47
|
+
.coverage.*
|
|
48
|
+
.cache
|
|
49
|
+
nosetests.xml
|
|
50
|
+
coverage.xml
|
|
51
|
+
*.cover
|
|
52
|
+
*.py,cover
|
|
53
|
+
.hypothesis/
|
|
54
|
+
.pytest_cache/
|
|
55
|
+
cover/
|
|
56
|
+
|
|
57
|
+
# Translations
|
|
58
|
+
*.mo
|
|
59
|
+
*.pot
|
|
60
|
+
|
|
61
|
+
# Django stuff:
|
|
62
|
+
*.log
|
|
63
|
+
local_settings.py
|
|
64
|
+
db.sqlite3
|
|
65
|
+
db.sqlite3-journal
|
|
66
|
+
|
|
67
|
+
# Flask stuff:
|
|
68
|
+
instance/
|
|
69
|
+
.webassets-cache
|
|
70
|
+
|
|
71
|
+
# Scrapy stuff:
|
|
72
|
+
.scrapy
|
|
73
|
+
|
|
74
|
+
# Sphinx documentation
|
|
75
|
+
docs/_build/
|
|
76
|
+
|
|
77
|
+
# PyBuilder
|
|
78
|
+
.pybuilder/
|
|
79
|
+
target/
|
|
80
|
+
|
|
81
|
+
# Jupyter Notebook
|
|
82
|
+
.ipynb_checkpoints
|
|
83
|
+
|
|
84
|
+
# IPython
|
|
85
|
+
profile_default/
|
|
86
|
+
ipython_config.py
|
|
87
|
+
|
|
88
|
+
# pyenv
|
|
89
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
90
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
91
|
+
.python-version
|
|
92
|
+
|
|
93
|
+
# pipenv
|
|
94
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
95
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
96
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
97
|
+
# install all needed dependencies.
|
|
98
|
+
#Pipfile.lock
|
|
99
|
+
|
|
100
|
+
# UV
|
|
101
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
102
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
103
|
+
# commonly ignored for libraries.
|
|
104
|
+
uv.lock
|
|
105
|
+
|
|
106
|
+
# poetry
|
|
107
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
108
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
109
|
+
# commonly ignored for libraries.
|
|
110
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
111
|
+
#poetry.lock
|
|
112
|
+
|
|
113
|
+
# pdm
|
|
114
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
115
|
+
#pdm.lock
|
|
116
|
+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
|
117
|
+
# in version control.
|
|
118
|
+
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
|
|
119
|
+
.pdm.toml
|
|
120
|
+
.pdm-python
|
|
121
|
+
.pdm-build/
|
|
122
|
+
|
|
123
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
124
|
+
__pypackages__/
|
|
125
|
+
|
|
126
|
+
# Celery stuff
|
|
127
|
+
celerybeat-schedule
|
|
128
|
+
celerybeat.pid
|
|
129
|
+
|
|
130
|
+
# SageMath parsed files
|
|
131
|
+
*.sage.py
|
|
132
|
+
|
|
133
|
+
# Environments
|
|
134
|
+
.env
|
|
135
|
+
.venv
|
|
136
|
+
env/
|
|
137
|
+
venv/
|
|
138
|
+
ENV/
|
|
139
|
+
env.bak/
|
|
140
|
+
venv.bak/
|
|
141
|
+
|
|
142
|
+
# Spyder project settings
|
|
143
|
+
.spyderproject
|
|
144
|
+
.spyproject
|
|
145
|
+
|
|
146
|
+
# Rope project settings
|
|
147
|
+
.ropeproject
|
|
148
|
+
|
|
149
|
+
# mkdocs documentation
|
|
150
|
+
/site
|
|
151
|
+
|
|
152
|
+
# mypy
|
|
153
|
+
.mypy_cache/
|
|
154
|
+
.dmypy.json
|
|
155
|
+
dmypy.json
|
|
156
|
+
|
|
157
|
+
# Pyre type checker
|
|
158
|
+
.pyre/
|
|
159
|
+
|
|
160
|
+
# pytype static type analyzer
|
|
161
|
+
.pytype/
|
|
162
|
+
|
|
163
|
+
# Cython debug symbols
|
|
164
|
+
cython_debug/
|
|
165
|
+
|
|
166
|
+
# PyCharm
|
|
167
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
168
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
169
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
170
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
171
|
+
#.idea/
|
|
172
|
+
|
|
173
|
+
# Ruff stuff:
|
|
174
|
+
.ruff_cache/
|
|
175
|
+
|
|
176
|
+
# PyPI configuration file
|
|
177
|
+
.pypirc
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<component name="InspectionProjectProfileManager">
|
|
2
|
+
<profile version="1.0">
|
|
3
|
+
<option name="myName" value="Project Default" />
|
|
4
|
+
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
|
|
5
|
+
<inspection_tool class="PyCompatibilityInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
|
6
|
+
<option name="ourVersions">
|
|
7
|
+
<value>
|
|
8
|
+
<list size="3">
|
|
9
|
+
<item index="0" class="java.lang.String" itemvalue="3.11" />
|
|
10
|
+
<item index="1" class="java.lang.String" itemvalue="3.12" />
|
|
11
|
+
<item index="2" class="java.lang.String" itemvalue="3.13" />
|
|
12
|
+
</list>
|
|
13
|
+
</value>
|
|
14
|
+
</option>
|
|
15
|
+
</inspection_tool>
|
|
16
|
+
<inspection_tool class="PyUnresolvedReferencesInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
|
17
|
+
<option name="ignoredIdentifiers">
|
|
18
|
+
<list>
|
|
19
|
+
<option value="base64.binascii" />
|
|
20
|
+
</list>
|
|
21
|
+
</option>
|
|
22
|
+
</inspection_tool>
|
|
23
|
+
</profile>
|
|
24
|
+
</component>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<project version="4">
|
|
3
|
+
<component name="Black">
|
|
4
|
+
<option name="sdkName" value="uv (tsql)" />
|
|
5
|
+
</component>
|
|
6
|
+
<component name="KubernetesApiPersistence">{}</component>
|
|
7
|
+
<component name="KubernetesApiProvider"><![CDATA[{
|
|
8
|
+
"isMigrated": true
|
|
9
|
+
}]]></component>
|
|
10
|
+
<component name="ProjectRootManager" version="2" project-jdk-name="uv (tsql)" project-jdk-type="Python SDK" />
|
|
11
|
+
</project>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<module version="4">
|
|
3
|
+
<component name="PyDocumentationSettings">
|
|
4
|
+
<option name="format" value="PLAIN" />
|
|
5
|
+
<option name="myDocStringFormat" value="Plain" />
|
|
6
|
+
</component>
|
|
7
|
+
<component name="TestRunnerService">
|
|
8
|
+
<option name="PROJECT_TEST_RUNNER" value="py.test" />
|
|
9
|
+
</component>
|
|
10
|
+
</module>
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<project version="4">
|
|
3
|
+
<component name="AutoImportSettings">
|
|
4
|
+
<option name="autoReloadType" value="SELECTIVE" />
|
|
5
|
+
</component>
|
|
6
|
+
<component name="ChangeListManager">
|
|
7
|
+
<list default="true" id="059146b3-62bd-4ad4-890d-4356cf237b89" name="Changes" comment="">
|
|
8
|
+
<change beforePath="$PROJECT_DIR$/pyproject.toml" beforeDir="false" afterPath="$PROJECT_DIR$/pyproject.toml" afterDir="false" />
|
|
9
|
+
<change beforePath="$PROJECT_DIR$/tests/test_helper_functions.py" beforeDir="false" afterPath="$PROJECT_DIR$/tests/test_helper_functions.py" afterDir="false" />
|
|
10
|
+
<change beforePath="$PROJECT_DIR$/tests/test_injections_for_escaped.py" beforeDir="false" afterPath="$PROJECT_DIR$/tests/test_injections_for_escaped.py" afterDir="false" />
|
|
11
|
+
<change beforePath="$PROJECT_DIR$/tsql/__init__.py" beforeDir="false" afterPath="$PROJECT_DIR$/tsql/__init__.py" afterDir="false" />
|
|
12
|
+
</list>
|
|
13
|
+
<option name="SHOW_DIALOG" value="false" />
|
|
14
|
+
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
|
15
|
+
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
|
16
|
+
<option name="LAST_RESOLUTION" value="IGNORE" />
|
|
17
|
+
</component>
|
|
18
|
+
<component name="Git.Settings">
|
|
19
|
+
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
|
20
|
+
</component>
|
|
21
|
+
<component name="GitHubPullRequestSearchHistory">{
|
|
22
|
+
"lastFilter": {
|
|
23
|
+
"state": "OPEN",
|
|
24
|
+
"assignee": "nhumrich"
|
|
25
|
+
}
|
|
26
|
+
}</component>
|
|
27
|
+
<component name="GithubPullRequestsUISettings">{
|
|
28
|
+
"selectedUrlAndAccountId": {
|
|
29
|
+
"url": "git@github.com:nhumrich/tsql.git",
|
|
30
|
+
"accountId": "f308fc0d-c429-47fb-8e52-74bdc95408d8"
|
|
31
|
+
}
|
|
32
|
+
}</component>
|
|
33
|
+
<component name="ProjectColorInfo">{
|
|
34
|
+
"associatedIndex": 2
|
|
35
|
+
}</component>
|
|
36
|
+
<component name="ProjectId" id="33DagtaPqCq8RWQhigWGQcgOtLh" />
|
|
37
|
+
<component name="ProjectViewState">
|
|
38
|
+
<option name="compactDirectories" value="true" />
|
|
39
|
+
<option name="hideEmptyMiddlePackages" value="true" />
|
|
40
|
+
<option name="showLibraryContents" value="true" />
|
|
41
|
+
</component>
|
|
42
|
+
<component name="PropertiesComponent"><![CDATA[{
|
|
43
|
+
"keyToString": {
|
|
44
|
+
"ModuleVcsDetector.initialDetectionPerformed": "true",
|
|
45
|
+
"RunOnceActivity.ShowReadmeOnStart": "true",
|
|
46
|
+
"RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true",
|
|
47
|
+
"RunOnceActivity.git.unshallow": "true",
|
|
48
|
+
"git-widget-placeholder": "main",
|
|
49
|
+
"junie.onboarding.icon.badge.shown": "true",
|
|
50
|
+
"last_opened_file_path": "/home/nhumrich/personal/tsql",
|
|
51
|
+
"node.js.detected.package.eslint": "true",
|
|
52
|
+
"node.js.detected.package.tslint": "true",
|
|
53
|
+
"node.js.selected.package.eslint": "(autodetect)",
|
|
54
|
+
"node.js.selected.package.tslint": "(autodetect)",
|
|
55
|
+
"nodejs_package_manager_path": "npm",
|
|
56
|
+
"to.speed.mode.migration.done": "true",
|
|
57
|
+
"vue.rearranger.settings.migration": "true"
|
|
58
|
+
}
|
|
59
|
+
}]]></component>
|
|
60
|
+
<component name="SharedIndexes">
|
|
61
|
+
<attachedChunks>
|
|
62
|
+
<set>
|
|
63
|
+
<option value="bundled-js-predefined-d6986cc7102b-3aa1da707db6-JavaScript-PY-252.26830.99" />
|
|
64
|
+
<option value="bundled-python-sdk-164cda30dcd9-0af03a5fa574-com.jetbrains.pycharm.pro.sharedIndexes.bundled-PY-252.26830.99" />
|
|
65
|
+
</set>
|
|
66
|
+
</attachedChunks>
|
|
67
|
+
</component>
|
|
68
|
+
<component name="TaskManager">
|
|
69
|
+
<task active="true" id="Default" summary="Default task">
|
|
70
|
+
<changelist id="059146b3-62bd-4ad4-890d-4356cf237b89" name="Changes" comment="" />
|
|
71
|
+
<created>1758854212149</created>
|
|
72
|
+
<option name="number" value="Default" />
|
|
73
|
+
<option name="presentableId" value="Default" />
|
|
74
|
+
<updated>1758854212149</updated>
|
|
75
|
+
</task>
|
|
76
|
+
<servers />
|
|
77
|
+
</component>
|
|
78
|
+
<component name="TypeScriptGeneratedFilesManager">
|
|
79
|
+
<option name="version" value="3" />
|
|
80
|
+
</component>
|
|
81
|
+
<component name="Vcs.Log.Tabs.Properties">
|
|
82
|
+
<option name="TAB_STATES">
|
|
83
|
+
<map>
|
|
84
|
+
<entry key="MAIN">
|
|
85
|
+
<value>
|
|
86
|
+
<State />
|
|
87
|
+
</value>
|
|
88
|
+
</entry>
|
|
89
|
+
</map>
|
|
90
|
+
</option>
|
|
91
|
+
</component>
|
|
92
|
+
</project>
|
t_sql-1.0.1/Dockerfile
ADDED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: t-sql
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.1
|
|
4
4
|
Summary: Safe SQL. SQL queries for python t-strings (PEP 750)
|
|
5
5
|
Project-URL: Homepage, https://github.com/nhumrich/tsql
|
|
6
|
+
License-File: LICENSE
|
|
6
7
|
Requires-Python: >=3.14
|
|
7
8
|
Description-Content-Type: text/markdown
|
|
8
|
-
License-File: LICENSE
|
|
9
|
-
Dynamic: license-file
|
|
10
9
|
|
|
11
10
|
# tsql
|
|
12
11
|
|
t_sql-1.0.1/compose.yaml
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
services:
|
|
2
|
+
postgres:
|
|
3
|
+
image: postgres:16
|
|
4
|
+
ports:
|
|
5
|
+
- "5454:5432"
|
|
6
|
+
environment:
|
|
7
|
+
POSTGRES_PASSWORD: password
|
|
8
|
+
POSTGRES_USER: postgres
|
|
9
|
+
POSTGRES_DB: postgres
|
|
10
|
+
healthcheck:
|
|
11
|
+
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
|
12
|
+
interval: 5s
|
|
13
|
+
timeout: 5s
|
|
14
|
+
retries: 5
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
1
5
|
[project]
|
|
2
6
|
name = "t-sql"
|
|
3
|
-
version = "1.0.
|
|
7
|
+
version = "1.0.1"
|
|
4
8
|
description = "Safe SQL. SQL queries for python t-strings (PEP 750)"
|
|
5
9
|
readme = "README.md"
|
|
6
10
|
requires-python = ">=3.14"
|
|
@@ -17,3 +21,6 @@ dev = [
|
|
|
17
21
|
"pytest>=8.3.5",
|
|
18
22
|
"pytest-asyncio>=0.24.0",
|
|
19
23
|
]
|
|
24
|
+
|
|
25
|
+
[tool.hatch.build.targets.wheel]
|
|
26
|
+
packages = ["tsql"]
|
t_sql-1.0.1/pytest.ini
ADDED
|
@@ -104,15 +104,51 @@ def test_select_complex():
|
|
|
104
104
|
"""Test select with multiple clauses"""
|
|
105
105
|
min_age = 18
|
|
106
106
|
status = 'active'
|
|
107
|
-
|
|
107
|
+
|
|
108
108
|
query = tsql.select(
|
|
109
109
|
'users', columns=[
|
|
110
110
|
'name', 'email'],
|
|
111
111
|
ids=['a', 'b']
|
|
112
112
|
)
|
|
113
|
-
|
|
113
|
+
|
|
114
114
|
result = tsql.render(query)
|
|
115
|
-
|
|
115
|
+
|
|
116
116
|
assert "SELECT name, email FROM users" in result[0]
|
|
117
117
|
assert "WHERE id in (?,?)" in result[0]
|
|
118
|
-
assert result[1] == ['a', 'b']
|
|
118
|
+
assert result[1] == ['a', 'b']
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def test_select_string_id_injection():
|
|
122
|
+
"""Test that select() with malicious string id is properly parameterized"""
|
|
123
|
+
malicious_id = "1 OR 1=1"
|
|
124
|
+
|
|
125
|
+
result = tsql.select('users', malicious_id)
|
|
126
|
+
query, params = result.render()
|
|
127
|
+
|
|
128
|
+
# Should be parameterized, not directly embedded
|
|
129
|
+
assert "1 OR 1=1" not in query
|
|
130
|
+
assert "?" in query
|
|
131
|
+
assert params == [malicious_id]
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def test_select_tuple_id_injection():
|
|
135
|
+
"""Test that select() with malicious tuple is properly parameterized"""
|
|
136
|
+
malicious_tuple = ("1", "2'; DROP TABLE users; --")
|
|
137
|
+
|
|
138
|
+
result = tsql.select('users', malicious_tuple)
|
|
139
|
+
query, params = result.render()
|
|
140
|
+
|
|
141
|
+
# Should be parameterized, not directly embedded
|
|
142
|
+
assert "DROP TABLE users" not in query
|
|
143
|
+
assert "?" in query
|
|
144
|
+
assert malicious_tuple[1] in params
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def test_select_int_id_safe():
|
|
148
|
+
"""Test that select() with int id works correctly"""
|
|
149
|
+
result = tsql.select('users', 42)
|
|
150
|
+
query, params = result.render()
|
|
151
|
+
|
|
152
|
+
# Int should be parameterized
|
|
153
|
+
assert "?" in query
|
|
154
|
+
assert params == [42]
|
|
@@ -141,23 +141,17 @@ async def test_malicious_data_stored_safely(conn):
|
|
|
141
141
|
|
|
142
142
|
|
|
143
143
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
# Test ESCAPED style
|
|
160
|
-
result_escaped = tsql.render(t"SELECT * FROM users WHERE id = {attack}", style=tsql.styles.ESCAPED)
|
|
161
|
-
expected_escaped = attack.replace("'", "''")
|
|
162
|
-
assert result_escaped[0] == f"SELECT * FROM users WHERE id = '{expected_escaped}'"
|
|
163
|
-
assert result_escaped[1] == []
|
|
144
|
+
@pytest.mark.parametrize("attack", [
|
|
145
|
+
"' AND (SELECT TOP 1 name FROM sysobjects WHERE xtype='U') > 0 --",
|
|
146
|
+
"' AND EXTRACTVALUE(1, CONCAT(0x7e, (SELECT version()), 0x7e)) --",
|
|
147
|
+
"' AND (SELECT * FROM (SELECT COUNT(*),CONCAT(version(),FLOOR(RAND(0)*2))x FROM information_schema.tables GROUP BY x)a) --"
|
|
148
|
+
])
|
|
149
|
+
async def test_error_based_injection_patterns(attack, conn):
|
|
150
|
+
query, params = tsql.render(t"SELECT * FROM users WHERE id = {attack}")
|
|
151
|
+
assert query == "SELECT * FROM users WHERE id = ?"
|
|
152
|
+
assert params == [attack]
|
|
153
|
+
|
|
154
|
+
query_escaped, params_escaped = tsql.render(t"SELECT * FROM users WHERE id = {attack}", style=tsql.styles.ESCAPED)
|
|
155
|
+
expected_escaped = attack.replace("'", "''")
|
|
156
|
+
assert query_escaped == f"SELECT * FROM users WHERE id = '{expected_escaped}'"
|
|
157
|
+
assert params_escaped == []
|
|
@@ -212,11 +212,11 @@ def select(table, ids:str|int|list[str|int]=None, *, columns=None):
|
|
|
212
212
|
case list():
|
|
213
213
|
where_clause = t" WHERE id in {tuple(ids)}"
|
|
214
214
|
case tuple():
|
|
215
|
-
where_clause =
|
|
215
|
+
where_clause = t" WHERE id in {ids}"
|
|
216
216
|
case int():
|
|
217
|
-
where_clause =
|
|
217
|
+
where_clause = t" WHERE id = {ids}"
|
|
218
218
|
case str():
|
|
219
|
-
where_clause =
|
|
219
|
+
where_clause = t" WHERE id = {ids}"
|
|
220
220
|
|
|
221
221
|
return TSQL(t'SELECT {t_columns} FROM {table:literal}{where_clause}')
|
|
222
222
|
|
t_sql-1.0.0/PKG-INFO
DELETED
|
@@ -1,183 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: t-sql
|
|
3
|
-
Version: 1.0.0
|
|
4
|
-
Summary: Safe SQL. SQL queries for python t-strings (PEP 750)
|
|
5
|
-
Project-URL: Homepage, https://github.com/nhumrich/tsql
|
|
6
|
-
Requires-Python: >=3.14
|
|
7
|
-
Description-Content-Type: text/markdown
|
|
8
|
-
License-File: LICENSE
|
|
9
|
-
Dynamic: license-file
|
|
10
|
-
|
|
11
|
-
# tsql
|
|
12
|
-
|
|
13
|
-
A lightweight SQL templating library that leverages Python 3.14's t-strings (PEP 750).
|
|
14
|
-
|
|
15
|
-
TSQL provides a safe way to write SQL queries using Python's template strings (t-strings) while preventing SQL injection attacks through multiple parameter styling options.
|
|
16
|
-
|
|
17
|
-
## ⚠️ Python Version Requirement
|
|
18
|
-
This library requires Python 3.14+
|
|
19
|
-
|
|
20
|
-
TSQL is built specifically to take advantage of the new t-string feature introduced in PEP 750, which is only available in Python 3.14+.
|
|
21
|
-
|
|
22
|
-
## Installing
|
|
23
|
-
|
|
24
|
-
```
|
|
25
|
-
# with pip
|
|
26
|
-
pip install t-sql
|
|
27
|
-
|
|
28
|
-
# with uv
|
|
29
|
-
uv add t-sql
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
## using
|
|
33
|
-
|
|
34
|
-
```
|
|
35
|
-
import tsql
|
|
36
|
-
|
|
37
|
-
tsql.render(t"select * from users where name={name)")
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
## Parameter Styles
|
|
41
|
-
|
|
42
|
-
- **QMARK** (default): Uses `?` placeholders
|
|
43
|
-
- **NUMERIC**: Uses `:1`, `:2`, etc. placeholders
|
|
44
|
-
- **NAMED**: Uses `:name` placeholders
|
|
45
|
-
- **FORMAT**: Uses `%s` placeholders
|
|
46
|
-
- **PYFORMAT**: Uses `%(name)s` placeholders
|
|
47
|
-
- **NUMERIC_DOLLAR**: Uses `$1`, `$2`, etc. (PostgreSQL native)
|
|
48
|
-
- **ESCAPED**: Escapes values directly into SQL (no parameters)
|
|
49
|
-
|
|
50
|
-
## Examples:
|
|
51
|
-
|
|
52
|
-
```python
|
|
53
|
-
|
|
54
|
-
# Basic usage with different parameter styles
|
|
55
|
-
import tsql
|
|
56
|
-
import tsql.styles
|
|
57
|
-
|
|
58
|
-
name = 'billy'
|
|
59
|
-
query = t'select * from users where name={name}'
|
|
60
|
-
|
|
61
|
-
# Default QMARK style
|
|
62
|
-
print(tsql.render(query))
|
|
63
|
-
# ('select * from users where name = ?', ['billy'])
|
|
64
|
-
|
|
65
|
-
# PostgreSQL native style
|
|
66
|
-
print(tsql.render(query, style=tsql.styles.NUMERIC_DOLLAR))
|
|
67
|
-
# ('select * from users where name = $1', ['billy'])
|
|
68
|
-
|
|
69
|
-
# ESCAPED style (no parameters)
|
|
70
|
-
print(tsql.render(query, style=tsql.styles.ESCAPED))
|
|
71
|
-
# ("select * from users where name = 'billy'", [])
|
|
72
|
-
|
|
73
|
-
# SQL injection prevention
|
|
74
|
-
name = "billy ' and 1=1 --"
|
|
75
|
-
print(tsql.render(query, style=tsql.styles.ESCAPED))
|
|
76
|
-
# ("select * from users where name = 'billy '' and 1=1 --'", [])
|
|
77
|
-
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
## Format-spec helpers
|
|
81
|
-
|
|
82
|
-
There are some built-in format spec helpers that can change the way some
|
|
83
|
-
parts of the library work.
|
|
84
|
-
|
|
85
|
-
### Literal
|
|
86
|
-
One common example is you may want to set the name
|
|
87
|
-
of a column dynamically. By using the `literal` format spec, the value will
|
|
88
|
-
be sanitized against a valid literal and put straight into the sql query since
|
|
89
|
-
you cannot parameterize that part of a query, example:
|
|
90
|
-
|
|
91
|
-
```python
|
|
92
|
-
query = t'select * from {table:literal} where {col:literal}={val}'
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
or, a full example:
|
|
96
|
-
```python
|
|
97
|
-
|
|
98
|
-
# with a like clause
|
|
99
|
-
min_age = 30
|
|
100
|
-
search_column = "name"
|
|
101
|
-
pattern = "O'Brien"
|
|
102
|
-
is_active = True
|
|
103
|
-
tsql.render(t"SELECT * FROM test_users WHERE age >= {min_age} AND {search_column:literal} LIKE '%' || {pattern} || '%' AND active = {is_active}")
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
### unsafe
|
|
107
|
-
You may want to do advanced things that may otherwise be considered unsfe.
|
|
108
|
-
This is okay if you can be sure that a user is not providing input. Like maybe
|
|
109
|
-
you care storing a query for some reason.
|
|
110
|
-
As per the name, this can open you up to sql injection and should be used with
|
|
111
|
-
extreme caution.
|
|
112
|
-
You can use the "unsafe" format spec for these
|
|
113
|
-
cases:
|
|
114
|
-
```python
|
|
115
|
-
dynamic_where = input('type where clause')
|
|
116
|
-
tsql.render(t"SELECT * FROM users WHERE {dynamic_where:unsafe}")
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
### as_values
|
|
120
|
-
|
|
121
|
-
The spec `:as_values` formats a dictionary into the format:
|
|
122
|
-
`(key1, key2, ...) VALUES (value1, value2, ...)` for uses in insert statements.
|
|
123
|
-
|
|
124
|
-
### as_set
|
|
125
|
-
|
|
126
|
-
The spec `:as_set` formats a dictionary into the format:
|
|
127
|
-
`key1='?', key2='?'` for uses in update statements.
|
|
128
|
-
|
|
129
|
-
### traditional format_spec
|
|
130
|
-
|
|
131
|
-
All other format specs should be handled as they would in a normal f-string.
|
|
132
|
-
|
|
133
|
-
## Included helper methods
|
|
134
|
-
|
|
135
|
-
```python
|
|
136
|
-
# select
|
|
137
|
-
tsql.select('table', 'abc123')
|
|
138
|
-
# SELECT * FROM table WHERE id='abc123'
|
|
139
|
-
|
|
140
|
-
# select with multiple ids and specific columns
|
|
141
|
-
tsql.select('users', ['abc123', 'def456'], columns=['name', 'age'])
|
|
142
|
-
# SELECT name, age FROM users WHERE id in ('abc123', 'def456')
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
# t_join (joins multiple t-strings together like .join on a str)
|
|
146
|
-
tsql.t_join(t" ", [t"hello", t"there"])
|
|
147
|
-
# t"hello there"
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
# insert
|
|
151
|
-
table = 'users'
|
|
152
|
-
values = {'id': 'abc123', 'name': 'bob', 'email': 'bob@example.com'}
|
|
153
|
-
tsql.insert(table, values)
|
|
154
|
-
# INSERT INTO users (id, name, email) VALUES ('abc123', 'bob', 'bob@example.com')
|
|
155
|
-
|
|
156
|
-
# update values on a single row
|
|
157
|
-
table = 'users'
|
|
158
|
-
values = {'name': 'joe', 'email': 'joe@example.com'}
|
|
159
|
-
tsql.update(table, values, id='abc123')
|
|
160
|
-
# UPDATE users SET name='joe', email='joe@example.com' WHERE id='abc123'
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
# Note on usage
|
|
164
|
-
|
|
165
|
-
This library should ideally be used inside middleware or library code
|
|
166
|
-
right before making an actual query. It can be used to enforce
|
|
167
|
-
using t-strings and prevent using raw strings.
|
|
168
|
-
|
|
169
|
-
For example:
|
|
170
|
-
|
|
171
|
-
```
|
|
172
|
-
from string.templatelib import Template
|
|
173
|
-
|
|
174
|
-
import tsql
|
|
175
|
-
|
|
176
|
-
def execute_sql_query(query):
|
|
177
|
-
if not isinstance(query, Template):
|
|
178
|
-
raise TypeError('Cannot make a query without using t-strings')
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
return sql_engine.execute(*tsql.render(query))
|
|
182
|
-
|
|
183
|
-
```
|
t_sql-1.0.0/setup.cfg
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
LICENSE
|
|
2
|
-
README.md
|
|
3
|
-
pyproject.toml
|
|
4
|
-
t_sql.egg-info/PKG-INFO
|
|
5
|
-
t_sql.egg-info/SOURCES.txt
|
|
6
|
-
t_sql.egg-info/dependency_links.txt
|
|
7
|
-
t_sql.egg-info/top_level.txt
|
|
8
|
-
tests/test_asyncpg_integration.py
|
|
9
|
-
tests/test_different_object_types.py
|
|
10
|
-
tests/test_escaped.py
|
|
11
|
-
tests/test_escaped_binary_hex.py
|
|
12
|
-
tests/test_helper_functions.py
|
|
13
|
-
tests/test_injection_edge_cases.py
|
|
14
|
-
tests/test_injection_protection_validation.py
|
|
15
|
-
tests/test_injections_for_escaped.py
|
|
16
|
-
tests/test_styles.py
|
|
17
|
-
tests/test_tsql.py
|
|
18
|
-
tsql/__init__.py
|
|
19
|
-
tsql/styles.py
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
tsql
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|