pyLodStorage 0.19.2__tar.gz → 0.19.4__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.
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/PKG-INFO +3 -1
- pylodstorage-0.19.4/lodstorage/__init__.py +1 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/lodstorage/duckdb_query.py +9 -1
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/lodstorage/mysql.py +17 -5
- pylodstorage-0.19.4/lodstorage/postgresql.py +152 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/lodstorage/query_cmd.py +5 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/lodstorage/querymain.py +7 -1
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/lodstorage/sql.py +10 -1
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/lodstorage/sql_backend.py +20 -3
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/lodstorage/version.py +1 -1
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/pyproject.toml +4 -0
- pylodstorage-0.19.4/tests/test_postgresql.py +120 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/tests/test_queries.py +31 -0
- pylodstorage-0.19.2/lodstorage/__init__.py +0 -1
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/.github/workflows/build.yml +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/.github/workflows/upload-to-pypi.yml +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/.gitignore +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/.project +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/.pydevproject +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/.readthedocs.yml +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/.travis.yml +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/AGENTS.md +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/CITATION.cff +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/LICENSE +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/README.md +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/index.md +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/Makefile +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/doctrees/environment.pickle +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/doctrees/index.doctree +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/doctrees/lodstorage.doctree +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/doctrees/sampledata.doctree +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/doctrees/setup.doctree +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/doctrees/tests.doctree +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/.buildinfo +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/_modules/index.html +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/_modules/lodstorage/csv.html +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/_modules/lodstorage/docstring_parser.html +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/_modules/lodstorage/entity.html +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/_modules/lodstorage/jsonable.html +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/_modules/lodstorage/jsonpicklemixin.html +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/_modules/lodstorage/linkml.html +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/_modules/lodstorage/linkml_gen.html +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/_modules/lodstorage/lod.html +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/_modules/lodstorage/mwTable.html +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/_modules/lodstorage/plot.html +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/_modules/lodstorage/query.html +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/_modules/lodstorage/querymain.html +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/_modules/lodstorage/rdf.html +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/_modules/lodstorage/sample.html +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/_modules/lodstorage/sample2.html +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/_modules/lodstorage/schema.html +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/_modules/lodstorage/sparql.html +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/_modules/lodstorage/sql.html +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/_modules/lodstorage/storageconfig.html +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/_modules/lodstorage/sync.html +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/_modules/lodstorage/tabulateCounter.html +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/_modules/lodstorage/trulytabular.html +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/_modules/lodstorage/uml.html +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/_modules/lodstorage/version.html +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/_modules/lodstorage/xml.html +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/_modules/lodstorage/yamlable.html +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/_modules/lodstorage/yamlablemixin.html +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/_sources/index.rst.txt +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/_sources/lodstorage.rst.txt +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/_sources/sampledata.rst.txt +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/_sources/setup.rst.txt +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/_sources/tests.rst.txt +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/_static/alabaster.css +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/_static/basic.css +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/_static/custom.css +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/_static/doctools.js +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/_static/documentation_options.js +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/_static/file.png +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/_static/language_data.js +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/_static/minus.png +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/_static/plus.png +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/_static/pygments.css +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/_static/searchtools.js +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/_static/sphinx_highlight.js +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/genindex.html +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/index.html +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/lodstorage.html +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/objects.inv +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/py-modindex.html +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/sampledata.html +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/search.html +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/searchindex.js +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/setup.html +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/tests.html +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/conf.py +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/index.rst +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/lodstorage.rst +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/make.bat +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/sampledata.rst +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/setup.rst +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/tests.rst +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/lodstorage/docstring_parser.py +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/lodstorage/exception_handler.py +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/lodstorage/linkml.py +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/lodstorage/linkml_gen.py +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/lodstorage/lod.py +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/lodstorage/lod2xml.py +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/lodstorage/lod_csv.py +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/lodstorage/multilang_querymanager.py +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/lodstorage/mwTable.py +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/lodstorage/params.py +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/lodstorage/plot.py +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/lodstorage/prefix_config.py +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/lodstorage/prefixes.py +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/lodstorage/profiler.py +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/lodstorage/query.py +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/lodstorage/rate_limiter.py +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/lodstorage/rdf.py +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/lodstorage/rdf_format.py +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/lodstorage/sample2.py +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/lodstorage/schema.py +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/lodstorage/sparql.py +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/lodstorage/sqlite_api.py +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/lodstorage/storageconfig.py +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/lodstorage/sync.py +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/lodstorage/tabulateCounter.py +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/lodstorage/uml.py +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/lodstorage/yaml_path.py +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/mkdocs.yml +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/sampledata/__init__.py +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/sampledata/dblp.yaml +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/sampledata/endpoints.yaml +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/sampledata/endpoints_qlever.yaml +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/sampledata/example.ttl +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/sampledata/formats.yaml +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/sampledata/got.yaml +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/sampledata/prefixes.yaml +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/sampledata/queries/queries_olympics.yaml +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/sampledata/queries/queries_tiny_wikidatasubsets_claude4_5.yaml +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/sampledata/queries/queries_tiny_wikidatasubsets_gemini3pro.yaml +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/sampledata/queries/queries_tiny_wikidatasubsets_gpt5.yaml +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/sampledata/queries/queries_tiny_wikidatasubsets_grok4.yaml +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/sampledata/queries.yaml +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/sampledata/royals.yaml +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/sampledata/royals_linkml_schema.yaml +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/sampledata/scholia.yaml +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/sampledata/trulytabular.yaml +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/sampledata/wf.yaml +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/sampledata/wikidata.yaml +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/scripts/blackisort +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/scripts/doc +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/scripts/install +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/scripts/installAndTest +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/scripts/jena +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/scripts/release +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/scripts/sparqlquery +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/scripts/test +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/scripts/vsenv +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/tests/__init__.py +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/tests/action_stats.py +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/tests/basetest.py +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/tests/endpoint_test.py +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/tests/test_Plot.py +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/tests/test_csv.py +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/tests/test_docstring_parser.py +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/tests/test_linkml.py +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/tests/test_linkml_schema.py +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/tests/test_lod.py +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/tests/test_multilang_querymanager.py +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/tests/test_params.py +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/tests/test_prefix_config.py +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/tests/test_prefixes.py +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/tests/test_query_yaml.py +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/tests/test_rdf.py +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/tests/test_samples.py +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/tests/test_sql_backend.py +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/tests/test_sqlite3.py +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/tests/test_sync.py +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/tests/test_tabulate.py +0 -0
- {pylodstorage-0.19.2 → pylodstorage-0.19.4}/tests/test_yaml_path.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyLodStorage
|
|
3
|
-
Version: 0.19.
|
|
3
|
+
Version: 0.19.4
|
|
4
4
|
Summary: python List of Dict (Table) Storage library
|
|
5
5
|
Project-URL: Home, https://github.com/WolfgangFahl/pyLodStorage
|
|
6
6
|
Project-URL: Documentation, http://wiki.bitplan.com/index.php/PyLoDStorage
|
|
@@ -35,6 +35,8 @@ Provides-Extra: duckdb
|
|
|
35
35
|
Requires-Dist: duckdb>=1.1.0; extra == 'duckdb'
|
|
36
36
|
Provides-Extra: mysql
|
|
37
37
|
Requires-Dist: pymysql>=1.1.1; extra == 'mysql'
|
|
38
|
+
Provides-Extra: postgresql
|
|
39
|
+
Requires-Dist: psycopg2-binary>=2.9.9; extra == 'postgresql'
|
|
38
40
|
Provides-Extra: test
|
|
39
41
|
Requires-Dist: green; extra == 'test'
|
|
40
42
|
Description-Content-Type: text/markdown
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.19.4"
|
|
@@ -40,13 +40,15 @@ class DuckDBQuery:
|
|
|
40
40
|
self.debug = debug
|
|
41
41
|
self.con = duckdb.connect(self.path)
|
|
42
42
|
|
|
43
|
-
def query(self, sql: str, params: Any = None) -> list[dict]:
|
|
43
|
+
def query(self, sql: str, params: Any = None, commit: bool = False) -> list[dict]:
|
|
44
44
|
"""
|
|
45
45
|
Execute an SQL query and return all results eagerly.
|
|
46
46
|
|
|
47
47
|
Args:
|
|
48
48
|
sql: the SQL query string to execute
|
|
49
49
|
params: optional positional parameters (list or tuple)
|
|
50
|
+
commit: if True, commit the connection after execution
|
|
51
|
+
(required for DDL/DML statements such as CREATE/INSERT/UPDATE)
|
|
50
52
|
|
|
51
53
|
Returns:
|
|
52
54
|
list of dicts, one per row
|
|
@@ -54,9 +56,15 @@ class DuckDBQuery:
|
|
|
54
56
|
if self.debug:
|
|
55
57
|
logging.debug(f"DuckDBQuery.query: {sql!r} params={params}")
|
|
56
58
|
rel = self.con.execute(sql, params or [])
|
|
59
|
+
if rel.description is None:
|
|
60
|
+
if commit:
|
|
61
|
+
self.con.commit()
|
|
62
|
+
return []
|
|
57
63
|
cols = [d[0] for d in rel.description]
|
|
58
64
|
rows = rel.fetchall()
|
|
59
65
|
result = [dict(zip(cols, row)) for row in rows]
|
|
66
|
+
if commit:
|
|
67
|
+
self.con.commit()
|
|
60
68
|
return result
|
|
61
69
|
|
|
62
70
|
def query_gen(self, sql: str, params: Any = None) -> Generator[dict, None, None]:
|
|
@@ -69,20 +69,28 @@ class MySqlQuery:
|
|
|
69
69
|
decoded_record[key] = value
|
|
70
70
|
return decoded_record
|
|
71
71
|
|
|
72
|
-
def execute_sql_query(
|
|
72
|
+
def execute_sql_query(
|
|
73
|
+
self, query: str, commit: bool = False
|
|
74
|
+
) -> List[Dict[str, Any]]:
|
|
73
75
|
"""
|
|
74
76
|
Executes an SQL query using the provided connection parameters.
|
|
75
77
|
|
|
76
78
|
Args:
|
|
77
79
|
query (str): The SQL query to execute.
|
|
78
|
-
|
|
80
|
+
commit (bool): if True, commit the connection before closing
|
|
81
|
+
(required for DDL/DML statements such as CREATE/INSERT/UPDATE)
|
|
79
82
|
|
|
80
83
|
Returns:
|
|
81
84
|
list: A list of dictionaries representing the query results.
|
|
82
85
|
"""
|
|
83
86
|
connection, cursor = self.get_cursor(query)
|
|
84
87
|
cursor.execute(query)
|
|
85
|
-
|
|
88
|
+
if cursor.description is None:
|
|
89
|
+
raw_lod = []
|
|
90
|
+
else:
|
|
91
|
+
raw_lod = cursor.fetchall()
|
|
92
|
+
if commit:
|
|
93
|
+
connection.commit()
|
|
86
94
|
connection.close()
|
|
87
95
|
lod = []
|
|
88
96
|
for raw_row in raw_lod:
|
|
@@ -108,18 +116,22 @@ class MySqlQuery:
|
|
|
108
116
|
cursor.close()
|
|
109
117
|
connection.close()
|
|
110
118
|
|
|
111
|
-
def query(
|
|
119
|
+
def query(
|
|
120
|
+
self, sql: str, params: Any = None, commit: bool = False
|
|
121
|
+
) -> List[Dict[str, Any]]:
|
|
112
122
|
"""
|
|
113
123
|
SQLBackend protocol alias for execute_sql_query.
|
|
114
124
|
|
|
115
125
|
Args:
|
|
116
126
|
sql: the SQL query to execute
|
|
117
127
|
params: ignored (not yet supported at the pymysql layer)
|
|
128
|
+
commit: if True, commit the connection after execution
|
|
129
|
+
(required for DDL/DML statements such as CREATE/INSERT/UPDATE)
|
|
118
130
|
|
|
119
131
|
Returns:
|
|
120
132
|
list of dicts, one per row
|
|
121
133
|
"""
|
|
122
|
-
return self.execute_sql_query(sql)
|
|
134
|
+
return self.execute_sql_query(sql, commit=commit)
|
|
123
135
|
|
|
124
136
|
def query_gen(
|
|
125
137
|
self, sql: str, params: Any = None
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"""
|
|
2
|
+
postgresql.py:
|
|
3
|
+
|
|
4
|
+
PostgreSQL support
|
|
5
|
+
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
from typing import Any, Dict, Generator, List
|
|
10
|
+
|
|
11
|
+
import psycopg2
|
|
12
|
+
import psycopg2.extras
|
|
13
|
+
|
|
14
|
+
from lodstorage.query import Endpoint
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class PostgreSqlQuery:
|
|
18
|
+
"""
|
|
19
|
+
A class to manage and execute PostgreSQL queries with optional debugging.
|
|
20
|
+
|
|
21
|
+
Attributes:
|
|
22
|
+
endpoint_info (Endpoint): endpoint configuration.
|
|
23
|
+
debug (bool): Flag to enable debugging.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def __init__(self, endpoint: Endpoint, debug: bool = False):
|
|
27
|
+
"""
|
|
28
|
+
Initializes the PostgreSqlQuery class.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
endpoint (Endpoint): endpoint configuration.
|
|
32
|
+
debug (bool): Flag to enable debugging.
|
|
33
|
+
"""
|
|
34
|
+
self.db_params = {
|
|
35
|
+
"host": endpoint.host or "localhost",
|
|
36
|
+
"port": endpoint.port or 5432,
|
|
37
|
+
"user": endpoint.user or "postgres",
|
|
38
|
+
"password": endpoint.password,
|
|
39
|
+
"dbname": endpoint.database,
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
self.debug = debug
|
|
43
|
+
|
|
44
|
+
def get_cursor(self, query: str):
|
|
45
|
+
if self.debug:
|
|
46
|
+
logging.debug(f"Executing query: {query}")
|
|
47
|
+
logging.debug(f"With connection parameters: {self.db_params}")
|
|
48
|
+
|
|
49
|
+
connection = psycopg2.connect(**self.db_params)
|
|
50
|
+
cursor = connection.cursor(cursor_factory=psycopg2.extras.RealDictCursor)
|
|
51
|
+
return connection, cursor
|
|
52
|
+
|
|
53
|
+
def decode_record(self, record: Dict[str, Any]) -> Dict[str, Any]:
|
|
54
|
+
"""
|
|
55
|
+
Converts binary values to UTF-8 strings.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
record (Dict[str, Any]): Raw database row data
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
Dict[str, Any]: Data with binary values decoded to strings
|
|
62
|
+
"""
|
|
63
|
+
decoded_record = {}
|
|
64
|
+
for key, value in record.items():
|
|
65
|
+
if isinstance(value, (bytes, memoryview)):
|
|
66
|
+
decoded_record[key] = (
|
|
67
|
+
bytes(value).decode("utf-8", errors="replace")
|
|
68
|
+
if isinstance(value, memoryview)
|
|
69
|
+
else value.decode("utf-8", errors="replace")
|
|
70
|
+
)
|
|
71
|
+
else:
|
|
72
|
+
decoded_record[key] = value
|
|
73
|
+
return decoded_record
|
|
74
|
+
|
|
75
|
+
def execute_sql_query(
|
|
76
|
+
self, query: str, commit: bool = False
|
|
77
|
+
) -> List[Dict[str, Any]]:
|
|
78
|
+
"""
|
|
79
|
+
Executes an SQL query using the provided connection parameters.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
query (str): The SQL query to execute.
|
|
83
|
+
commit (bool): if True, commit the connection before closing
|
|
84
|
+
(required for DDL/DML statements such as CREATE/INSERT/UPDATE)
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
list: A list of dictionaries representing the query results.
|
|
88
|
+
"""
|
|
89
|
+
connection, cursor = self.get_cursor(query)
|
|
90
|
+
cursor.execute(query)
|
|
91
|
+
if cursor.description is None:
|
|
92
|
+
raw_lod = []
|
|
93
|
+
else:
|
|
94
|
+
raw_lod = cursor.fetchall()
|
|
95
|
+
if commit:
|
|
96
|
+
connection.commit()
|
|
97
|
+
connection.close()
|
|
98
|
+
lod = []
|
|
99
|
+
for raw_row in raw_lod:
|
|
100
|
+
row = self.decode_record(dict(raw_row))
|
|
101
|
+
lod.append(row)
|
|
102
|
+
return lod
|
|
103
|
+
|
|
104
|
+
def query_generator(self, query: str) -> Generator[Dict[str, Any], None, None]:
|
|
105
|
+
"""
|
|
106
|
+
Generator for fetching records one by one from a SQL query.
|
|
107
|
+
"""
|
|
108
|
+
connection, cursor = self.get_cursor(query)
|
|
109
|
+
try:
|
|
110
|
+
cursor.execute(query)
|
|
111
|
+
while True:
|
|
112
|
+
raw_record = cursor.fetchone()
|
|
113
|
+
if not raw_record:
|
|
114
|
+
break
|
|
115
|
+
record = self.decode_record(dict(raw_record))
|
|
116
|
+
yield record
|
|
117
|
+
|
|
118
|
+
finally:
|
|
119
|
+
cursor.close()
|
|
120
|
+
connection.close()
|
|
121
|
+
|
|
122
|
+
def query(
|
|
123
|
+
self, sql: str, params: Any = None, commit: bool = False
|
|
124
|
+
) -> List[Dict[str, Any]]:
|
|
125
|
+
"""
|
|
126
|
+
SQLBackend protocol alias for execute_sql_query.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
sql: the SQL query to execute
|
|
130
|
+
params: ignored (not yet supported at the psycopg2 layer)
|
|
131
|
+
commit: if True, commit the connection after execution
|
|
132
|
+
(required for DDL/DML statements such as CREATE/INSERT/UPDATE)
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
list of dicts, one per row
|
|
136
|
+
"""
|
|
137
|
+
return self.execute_sql_query(sql, commit=commit)
|
|
138
|
+
|
|
139
|
+
def query_gen(
|
|
140
|
+
self, sql: str, params: Any = None
|
|
141
|
+
) -> Generator[Dict[str, Any], None, None]:
|
|
142
|
+
"""
|
|
143
|
+
SQLBackend protocol alias for query_generator.
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
sql: the SQL query to execute
|
|
147
|
+
params: ignored (not yet supported at the psycopg2 layer)
|
|
148
|
+
|
|
149
|
+
Yields:
|
|
150
|
+
one dict per row
|
|
151
|
+
"""
|
|
152
|
+
return self.query_generator(sql)
|
|
@@ -136,6 +136,11 @@ class QueryCmd:
|
|
|
136
136
|
xml = lod2xml.asXml()
|
|
137
137
|
print(xml)
|
|
138
138
|
|
|
139
|
+
elif args.format is Format.raw:
|
|
140
|
+
for row in qlod:
|
|
141
|
+
values = list(row.values())
|
|
142
|
+
line = "\t".join(str(v) for v in values)
|
|
143
|
+
print(line)
|
|
139
144
|
else:
|
|
140
145
|
raise Exception(f"format {args.format} not supported yet")
|
|
141
146
|
|
|
@@ -96,7 +96,7 @@ class QueryMain(QueryCmd):
|
|
|
96
96
|
qlod = sparql.queryAsListOfDicts(self.queryCode)
|
|
97
97
|
elif args.language == "sql":
|
|
98
98
|
backend = get_sql_backend(endpointConf, debug=args.debug)
|
|
99
|
-
qlod = backend.query(self.queryCode)
|
|
99
|
+
qlod = backend.query(self.queryCode, commit=args.commit)
|
|
100
100
|
else:
|
|
101
101
|
raise Exception(f"language {args.language} not known/supported")
|
|
102
102
|
self.format_output(qlod)
|
|
@@ -267,6 +267,12 @@ USAGE
|
|
|
267
267
|
action="store_true",
|
|
268
268
|
help="return the raw query result from the endpoint. (MIME type defined over -f or -m)",
|
|
269
269
|
)
|
|
270
|
+
parser.add_argument(
|
|
271
|
+
"--commit",
|
|
272
|
+
action="store_true",
|
|
273
|
+
help="commit the SQL transaction after execution "
|
|
274
|
+
"(required for DDL/DML statements such as CREATE/INSERT/UPDATE)",
|
|
275
|
+
)
|
|
270
276
|
parser.add_argument(
|
|
271
277
|
"-V", "--version", action="version", version=program_version_message
|
|
272
278
|
)
|
|
@@ -256,6 +256,11 @@ class SQLDB(object):
|
|
|
256
256
|
query = cur.execute(sqlQuery, params)
|
|
257
257
|
else:
|
|
258
258
|
query = cur.execute(sqlQuery)
|
|
259
|
+
# description is None for statements that do not return rows
|
|
260
|
+
# (e.g. CREATE TABLE, INSERT, UPDATE, DELETE)
|
|
261
|
+
if query.description is None:
|
|
262
|
+
cur.close()
|
|
263
|
+
return
|
|
259
264
|
colname = [d[0] for d in query.description]
|
|
260
265
|
try:
|
|
261
266
|
# loop over all rows
|
|
@@ -281,7 +286,7 @@ class SQLDB(object):
|
|
|
281
286
|
"""
|
|
282
287
|
return self.queryGen(sql, params)
|
|
283
288
|
|
|
284
|
-
def query(self, sql, params=None):
|
|
289
|
+
def query(self, sql, params=None, commit: bool = False):
|
|
285
290
|
"""
|
|
286
291
|
run the given sql query and return a list of Dicts
|
|
287
292
|
|
|
@@ -289,6 +294,8 @@ class SQLDB(object):
|
|
|
289
294
|
|
|
290
295
|
sql(string): the SQL query to be executed
|
|
291
296
|
params(tuple): the query params, if any
|
|
297
|
+
commit(bool): if True, commit the connection after execution
|
|
298
|
+
(required for DDL/DML statements such as CREATE/INSERT/UPDATE)
|
|
292
299
|
|
|
293
300
|
Returns:
|
|
294
301
|
list: a list of Dicts
|
|
@@ -296,6 +303,8 @@ class SQLDB(object):
|
|
|
296
303
|
resultList = []
|
|
297
304
|
for record in self.queryGen(sql, params):
|
|
298
305
|
resultList.append(record)
|
|
306
|
+
if commit:
|
|
307
|
+
self.c.commit()
|
|
299
308
|
return resultList
|
|
300
309
|
|
|
301
310
|
def queryAll(self, entityInfo, fixDates=True):
|
|
@@ -5,7 +5,7 @@ Unified SQL backend abstraction for pyLoDStorage.
|
|
|
5
5
|
|
|
6
6
|
Defines the SQLBackend Protocol (duck-typed interface) and the
|
|
7
7
|
get_sql_backend() factory that resolves the correct backend from
|
|
8
|
-
an endpoint descriptor, hiding the jdbc:mysql and jdbc:
|
|
8
|
+
an endpoint descriptor, hiding the jdbc:mysql, jdbc:duckdb, and jdbc:postgresql
|
|
9
9
|
routing details from all call sites.
|
|
10
10
|
|
|
11
11
|
Created on 2026-02-21
|
|
@@ -27,6 +27,11 @@ try:
|
|
|
27
27
|
except ImportError:
|
|
28
28
|
DuckDBQuery = None # type: ignore
|
|
29
29
|
|
|
30
|
+
try:
|
|
31
|
+
from lodstorage.postgresql import PostgreSqlQuery
|
|
32
|
+
except ImportError:
|
|
33
|
+
PostgreSqlQuery = None # type: ignore
|
|
34
|
+
|
|
30
35
|
|
|
31
36
|
@runtime_checkable
|
|
32
37
|
class SQLBackend(Protocol):
|
|
@@ -40,15 +45,18 @@ class SQLBackend(Protocol):
|
|
|
40
45
|
- lodstorage.sql.SQLDB (SQLite, always available)
|
|
41
46
|
- lodstorage.mysql.MySqlQuery (MySQL/MariaDB, optional: pip install pyLodStorage[mysql])
|
|
42
47
|
- lodstorage.duckdb_query.DuckDBQuery (DuckDB, optional: pip install pyLodStorage[duckdb])
|
|
48
|
+
- lodstorage.postgresql.PostgreSqlQuery (PostgreSQL, optional: pip install pyLodStorage[postgresql])
|
|
43
49
|
"""
|
|
44
50
|
|
|
45
|
-
def query(self, sql: str, params: Any = None) -> list[dict]:
|
|
51
|
+
def query(self, sql: str, params: Any = None, commit: bool = False) -> list[dict]:
|
|
46
52
|
"""
|
|
47
53
|
Execute an SQL query and return all results eagerly.
|
|
48
54
|
|
|
49
55
|
Args:
|
|
50
56
|
sql: the SQL query string to execute
|
|
51
57
|
params: optional query parameters (support depends on backend)
|
|
58
|
+
commit: if True, commit the underlying connection after execution
|
|
59
|
+
(required for DDL/DML statements such as CREATE/INSERT/UPDATE)
|
|
52
60
|
|
|
53
61
|
Returns:
|
|
54
62
|
list of dicts, one per row
|
|
@@ -82,7 +90,7 @@ def get_sql_backend(endpoint, debug: bool = False) -> SQLBackend:
|
|
|
82
90
|
debug: enable debug output on the returned backend
|
|
83
91
|
|
|
84
92
|
Returns:
|
|
85
|
-
SQLBackend — SQLDB, MySqlQuery, or
|
|
93
|
+
SQLBackend — SQLDB, MySqlQuery, DuckDBQuery, or PostgreSqlQuery depending on endpoint
|
|
86
94
|
|
|
87
95
|
Raises:
|
|
88
96
|
Exception: if the requested backend's optional dependency is not installed
|
|
@@ -112,5 +120,14 @@ def get_sql_backend(endpoint, debug: bool = False) -> SQLBackend:
|
|
|
112
120
|
backend = DuckDBQuery(endpoint=endpoint, debug=debug)
|
|
113
121
|
return backend
|
|
114
122
|
|
|
123
|
+
if url.startswith("jdbc:postgresql"):
|
|
124
|
+
if PostgreSqlQuery is None:
|
|
125
|
+
raise Exception(
|
|
126
|
+
"PostgreSQL backend requested but psycopg2 is not installed. "
|
|
127
|
+
"Install it with: pip install pyLodStorage[postgresql]"
|
|
128
|
+
)
|
|
129
|
+
backend = PostgreSqlQuery(endpoint=endpoint, debug=debug)
|
|
130
|
+
return backend
|
|
131
|
+
|
|
115
132
|
backend = SQLDB(dbname=url, debug=debug)
|
|
116
133
|
return backend
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"""
|
|
2
|
+
test_postgresql.py
|
|
3
|
+
|
|
4
|
+
Tests for lodstorage.postgresql — PostgreSqlQuery with mocked psycopg2.
|
|
5
|
+
|
|
6
|
+
Created on 2026-03-22
|
|
7
|
+
|
|
8
|
+
@author: wf
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import unittest
|
|
12
|
+
from unittest.mock import MagicMock, patch
|
|
13
|
+
|
|
14
|
+
from lodstorage.query import Endpoint
|
|
15
|
+
from lodstorage.sql_backend import PostgreSqlQuery, SQLBackend, get_sql_backend
|
|
16
|
+
from tests.basetest import Basetest
|
|
17
|
+
|
|
18
|
+
postgresql_available = PostgreSqlQuery is not None
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class TestPostgreSqlQuery(Basetest):
|
|
22
|
+
"""
|
|
23
|
+
Verify PostgreSqlQuery with mocked psycopg2.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def setUp(self, debug=False, profile=True):
|
|
27
|
+
Basetest.setUp(self, debug=debug, profile=profile)
|
|
28
|
+
|
|
29
|
+
def postgresql_endpoint(self) -> Endpoint:
|
|
30
|
+
"""Return an Endpoint configured for PostgreSQL."""
|
|
31
|
+
ep = Endpoint()
|
|
32
|
+
ep.endpoint = "jdbc:postgresql"
|
|
33
|
+
ep.host = "127.0.0.1"
|
|
34
|
+
ep.port = 5432
|
|
35
|
+
ep.user = "postgres"
|
|
36
|
+
ep.password = "testpass"
|
|
37
|
+
ep.database = "template1"
|
|
38
|
+
return ep
|
|
39
|
+
|
|
40
|
+
@unittest.skipUnless(postgresql_available, "psycopg2 not installed")
|
|
41
|
+
def testProtocolConformance(self):
|
|
42
|
+
"""PostgreSqlQuery must satisfy SQLBackend at runtime."""
|
|
43
|
+
with patch("lodstorage.postgresql.psycopg2"):
|
|
44
|
+
pq = PostgreSqlQuery(endpoint=self.postgresql_endpoint())
|
|
45
|
+
self.assertIsInstance(pq, SQLBackend)
|
|
46
|
+
|
|
47
|
+
@unittest.skipUnless(postgresql_available, "psycopg2 not installed")
|
|
48
|
+
def testFactoryRouting(self):
|
|
49
|
+
"""Factory must route jdbc:postgresql Endpoint -> PostgreSqlQuery."""
|
|
50
|
+
backend = get_sql_backend(self.postgresql_endpoint())
|
|
51
|
+
self.assertIsInstance(backend, PostgreSqlQuery)
|
|
52
|
+
|
|
53
|
+
@unittest.skipUnless(postgresql_available, "psycopg2 not installed")
|
|
54
|
+
@patch("lodstorage.postgresql.psycopg2")
|
|
55
|
+
def testQuery(self, mock_psycopg2):
|
|
56
|
+
"""query() returns a list of dicts from mocked cursor."""
|
|
57
|
+
mock_cursor = MagicMock()
|
|
58
|
+
mock_cursor.fetchall.return_value = [
|
|
59
|
+
{"datname": "template1"},
|
|
60
|
+
{"datname": "n8n"},
|
|
61
|
+
]
|
|
62
|
+
mock_connection = MagicMock()
|
|
63
|
+
mock_psycopg2.connect.return_value = mock_connection
|
|
64
|
+
mock_connection.cursor.return_value = mock_cursor
|
|
65
|
+
|
|
66
|
+
pq = PostgreSqlQuery(endpoint=self.postgresql_endpoint())
|
|
67
|
+
rows = pq.query("SELECT datname FROM pg_database")
|
|
68
|
+
|
|
69
|
+
self.assertEqual(2, len(rows))
|
|
70
|
+
self.assertEqual("template1", rows[0]["datname"])
|
|
71
|
+
self.assertEqual("n8n", rows[1]["datname"])
|
|
72
|
+
mock_cursor.execute.assert_called_once_with("SELECT datname FROM pg_database")
|
|
73
|
+
mock_connection.close.assert_called_once()
|
|
74
|
+
|
|
75
|
+
@unittest.skipUnless(postgresql_available, "psycopg2 not installed")
|
|
76
|
+
@patch("lodstorage.postgresql.psycopg2")
|
|
77
|
+
def testQueryGen(self, mock_psycopg2):
|
|
78
|
+
"""query_gen() yields rows one by one from mocked cursor."""
|
|
79
|
+
mock_cursor = MagicMock()
|
|
80
|
+
mock_cursor.fetchone.side_effect = [
|
|
81
|
+
{"datname": "template1"},
|
|
82
|
+
{"datname": "n8n"},
|
|
83
|
+
None,
|
|
84
|
+
]
|
|
85
|
+
mock_connection = MagicMock()
|
|
86
|
+
mock_psycopg2.connect.return_value = mock_connection
|
|
87
|
+
mock_connection.cursor.return_value = mock_cursor
|
|
88
|
+
|
|
89
|
+
pq = PostgreSqlQuery(endpoint=self.postgresql_endpoint())
|
|
90
|
+
names = [r["datname"] for r in pq.query_gen("SELECT datname FROM pg_database")]
|
|
91
|
+
|
|
92
|
+
self.assertEqual(["template1", "n8n"], names)
|
|
93
|
+
mock_cursor.close.assert_called_once()
|
|
94
|
+
mock_connection.close.assert_called_once()
|
|
95
|
+
|
|
96
|
+
@unittest.skipUnless(postgresql_available, "psycopg2 not installed")
|
|
97
|
+
@patch("lodstorage.postgresql.psycopg2")
|
|
98
|
+
def testDecodeBytes(self, mock_psycopg2):
|
|
99
|
+
"""Binary values should be decoded to UTF-8 strings."""
|
|
100
|
+
mock_cursor = MagicMock()
|
|
101
|
+
mock_cursor.fetchall.return_value = [
|
|
102
|
+
{"name": b"caf\xc3\xa9"},
|
|
103
|
+
]
|
|
104
|
+
mock_connection = MagicMock()
|
|
105
|
+
mock_psycopg2.connect.return_value = mock_connection
|
|
106
|
+
mock_connection.cursor.return_value = mock_cursor
|
|
107
|
+
|
|
108
|
+
pq = PostgreSqlQuery(endpoint=self.postgresql_endpoint())
|
|
109
|
+
rows = pq.query("SELECT name FROM test")
|
|
110
|
+
|
|
111
|
+
self.assertEqual(1, len(rows))
|
|
112
|
+
self.assertEqual("café", rows[0]["name"])
|
|
113
|
+
|
|
114
|
+
def testFactoryRaisesWithoutPsycopg2(self):
|
|
115
|
+
"""Factory must raise if psycopg2 is not installed."""
|
|
116
|
+
ep = self.postgresql_endpoint()
|
|
117
|
+
with patch("lodstorage.sql_backend.PostgreSqlQuery", None):
|
|
118
|
+
with self.assertRaises(Exception) as ctx:
|
|
119
|
+
get_sql_backend(ep)
|
|
120
|
+
self.assertIn("psycopg2", str(ctx.exception))
|
|
@@ -24,6 +24,7 @@ from lodstorage.query import (
|
|
|
24
24
|
ValueFormatter,
|
|
25
25
|
ValueFormatters,
|
|
26
26
|
)
|
|
27
|
+
from lodstorage.query_cmd import QueryCmd
|
|
27
28
|
from lodstorage.querymain import QueryMain
|
|
28
29
|
from lodstorage.querymain import main as queryMain
|
|
29
30
|
from lodstorage.sparql import SPARQL
|
|
@@ -705,6 +706,36 @@ determines the number of instances available in the OpenStreetMap for the placeT
|
|
|
705
706
|
except Exception as ex:
|
|
706
707
|
print(f"{query.title} at {endpointUrl} failed: {ex}")
|
|
707
708
|
|
|
709
|
+
def test_issue162_raw_format(self):
|
|
710
|
+
"""
|
|
711
|
+
test raw format output
|
|
712
|
+
|
|
713
|
+
https://github.com/WolfgangFahl/pyLoDStorage/issues/162
|
|
714
|
+
"""
|
|
715
|
+
debug = self.debug
|
|
716
|
+
qlod = [
|
|
717
|
+
{"grant": "GRANT USAGE ON *.* TO `test`@`%`"},
|
|
718
|
+
{"grant": "GRANT ALL PRIVILEGES ON `testdb`.* TO `test`@`%`"},
|
|
719
|
+
]
|
|
720
|
+
args = Namespace(
|
|
721
|
+
debug=debug,
|
|
722
|
+
format=Format.raw,
|
|
723
|
+
)
|
|
724
|
+
query_cmd = QueryCmd.__new__(QueryCmd)
|
|
725
|
+
query_cmd.args = args
|
|
726
|
+
query_cmd.query = Query(name="test", query="SHOW GRANTS", lang="sql")
|
|
727
|
+
stdout = io.StringIO()
|
|
728
|
+
with redirect_stdout(stdout):
|
|
729
|
+
query_cmd.format_output(qlod)
|
|
730
|
+
result = stdout.getvalue()
|
|
731
|
+
if debug:
|
|
732
|
+
print(result)
|
|
733
|
+
self.assertIn("GRANT USAGE ON *.* TO `test`@`%`", result)
|
|
734
|
+
self.assertIn("GRANT ALL PRIVILEGES ON `testdb`.* TO `test`@`%`", result)
|
|
735
|
+
# raw format should not contain any table markup or quoting
|
|
736
|
+
self.assertNotIn("{|", result)
|
|
737
|
+
self.assertNotIn('"GRANT', result)
|
|
738
|
+
|
|
708
739
|
|
|
709
740
|
class TestEndpoints(EndpointTest):
|
|
710
741
|
"""
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.19.2"
|
|
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
|
|
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
|
{pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/_modules/lodstorage/csv.html
RENAMED
|
File without changes
|
|
File without changes
|
{pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/_modules/lodstorage/entity.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/_modules/lodstorage/linkml.html
RENAMED
|
File without changes
|
|
File without changes
|
{pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/_modules/lodstorage/lod.html
RENAMED
|
File without changes
|
{pylodstorage-0.19.2 → pylodstorage-0.19.4}/docs/source/_build/html/_modules/lodstorage/mwTable.html
RENAMED
|
File without changes
|