sqlutilpy 0.29.0__tar.gz → 0.30.0__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.
- sqlutilpy-0.30.0/CHANGELOG.md +55 -0
- sqlutilpy-0.30.0/CITATION.cff +26 -0
- {sqlutilpy-0.29.0 → sqlutilpy-0.30.0}/LICENSE.txt +4 -4
- sqlutilpy-0.30.0/MANIFEST.in +6 -0
- {sqlutilpy-0.29.0/src/sqlutilpy.egg-info → sqlutilpy-0.30.0}/PKG-INFO +29 -12
- {sqlutilpy-0.29.0 → sqlutilpy-0.30.0}/README.md +18 -5
- sqlutilpy-0.30.0/pyproject.toml +35 -0
- {sqlutilpy-0.29.0 → sqlutilpy-0.30.0}/src/sqlutilpy/sqlutil.py +284 -141
- sqlutilpy-0.30.0/src/sqlutilpy/version.py +1 -0
- {sqlutilpy-0.29.0 → sqlutilpy-0.30.0/src/sqlutilpy.egg-info}/PKG-INFO +29 -12
- {sqlutilpy-0.29.0 → sqlutilpy-0.30.0}/src/sqlutilpy.egg-info/SOURCES.txt +2 -0
- {sqlutilpy-0.29.0 → sqlutilpy-0.30.0}/src/sqlutilpy.egg-info/requires.txt +1 -1
- {sqlutilpy-0.29.0 → sqlutilpy-0.30.0}/tests/test_pg.py +19 -0
- {sqlutilpy-0.29.0 → sqlutilpy-0.30.0}/tests/test_sqlite.py +32 -0
- sqlutilpy-0.29.0/MANIFEST.in +0 -9
- sqlutilpy-0.29.0/pyproject.toml +0 -31
- sqlutilpy-0.29.0/src/sqlutilpy/version.py +0 -1
- {sqlutilpy-0.29.0 → sqlutilpy-0.30.0}/setup.cfg +0 -0
- {sqlutilpy-0.29.0 → sqlutilpy-0.30.0}/src/sqlutilpy/__init__.py +0 -0
- {sqlutilpy-0.29.0 → sqlutilpy-0.30.0}/src/sqlutilpy.egg-info/dependency_links.txt +0 -0
- {sqlutilpy-0.29.0 → sqlutilpy-0.30.0}/src/sqlutilpy.egg-info/top_level.txt +0 -0
- {sqlutilpy-0.29.0 → sqlutilpy-0.30.0}/tests/killer.py +0 -0
- {sqlutilpy-0.29.0 → sqlutilpy-0.30.0}/tests/test_consistency.py +0 -0
- {sqlutilpy-0.29.0 → sqlutilpy-0.30.0}/tests/test_duckdb.py +0 -0
- {sqlutilpy-0.29.0 → sqlutilpy-0.30.0}/tests/test_fix_numpy.py +0 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
Changelog
|
|
2
|
+
|
|
3
|
+
# 0.30.0
|
|
4
|
+
* All the options of get(), execute(), upload(), local_join(), getConnection() and getCursor() are now keyword-only and listed in the same order in every function (db, driver, user, password, host, port, conn, preamb, timeout, then the function specific options). Only the query/parameters and the table name/arrays/column names can be given positionally. This stops the argument order changes between releases (e.g. the port argument added in 0.28.0 before conn in execute() and upload()) from silently shifting positional arguments
|
|
5
|
+
* get() and getCursor() accept again the notNamed option removed in 0.27.0, as a deprecated alias of batched=not notNamed / named=not notNamed. It emits a DeprecationWarning and will be removed in a future release
|
|
6
|
+
|
|
7
|
+
# 0.29.0
|
|
8
|
+
* get() with duckdb: fix the null replacement in narrow and unsigned integer columns (tinyint, utinyint, uinteger, ubigint) with numpy<2, where intNullVal was silently wrapped (e.g. -9999 became -15 in a tinyint column) instead of the column being widened to int64
|
|
9
|
+
* This is the last release supporting numpy<2
|
|
10
|
+
* An unknown driver name now raises SqlUtilException from get(), execute(), upload(), local_join(), getConnection() and getCursor() before any connection is attempted (it was added in 0.28.0)
|
|
11
|
+
|
|
12
|
+
# 0.28.0
|
|
13
|
+
* upload(): strings containing the delimiter, backslashes, newlines, tabs or carriage returns are now escaped and survive the upload unchanged
|
|
14
|
+
* upload(): numpy bytes arrays (dtype S) are now decoded as utf-8 and uploaded as strings, rather than being stored as the b'...' python literals
|
|
15
|
+
* get(): intNullVal=None keeps the nulls in integer columns as None (the column is returned as an object array) instead of failing
|
|
16
|
+
* get(): statements that do not return a result set no longer fail with sqlite3/duckdb
|
|
17
|
+
* get(): use a unique server-side cursor name so that several batched queries can be run over the same connection
|
|
18
|
+
* get() with duckdb now uses the much faster fetchnumpy() interface. As a result the integer/float columns keep the width of the database type (e.g. int -> int32, float -> float32, rather than always int64/float64), dates, timestamps with time zone and intervals are returned as datetime64/timedelta64, decimals and hugeints as float64, blobs as object arrays of bytearrays, and list columns are now supported
|
|
19
|
+
* add the port argument to execute() and upload()
|
|
20
|
+
* upload(): analyze is now run inside the try block, so a failure there rolls back the transaction
|
|
21
|
+
* fix the strLength docstring (strings are not truncated, the allocated length is extended as needed)
|
|
22
|
+
* packaging: remove numpydoc and a duplicate numpy from the dependencies; the readthedocs build uses docs/requirements.txt and python 3.12
|
|
23
|
+
|
|
24
|
+
# 0.27.0
|
|
25
|
+
* Introduce the non-batched mode with batched=False that allows (at the cost of more memory use) using the parallel PostgreSQL plans, which can speed up queries by a factor of few
|
|
26
|
+
* remove the notNamed option of sqlutil.get
|
|
27
|
+
* refactor the threading for converting data, to use the threadpool
|
|
28
|
+
* I added more tests when strings are read, so hopefully the truncation into 20 characters should be more rare now.
|
|
29
|
+
|
|
30
|
+
# 0.26.0
|
|
31
|
+
* Preserve uppercase columns when ingestng data
|
|
32
|
+
* Remove obsolet numpy functionality
|
|
33
|
+
|
|
34
|
+
# 0.25.0
|
|
35
|
+
* Allow querying timestamp with timezone types
|
|
36
|
+
* Allow sqlutilpy.connect() to the duckdb db
|
|
37
|
+
|
|
38
|
+
# 0.24.0
|
|
39
|
+
* In the case sqlutilpy.get() returns no rows and asDict=True, the column names will still be there
|
|
40
|
+
|
|
41
|
+
# 0.23.0
|
|
42
|
+
* Switch to psycopg (i.e. third version of psycopg)
|
|
43
|
+
* Make the code compatible with numpy 2.0
|
|
44
|
+
|
|
45
|
+
# 0.22.0
|
|
46
|
+
* This version migrated to pyproject.toml
|
|
47
|
+
|
|
48
|
+
# 0.21.0
|
|
49
|
+
* Allow uploading int8 columns
|
|
50
|
+
* Allow uploading data where some columns have arrays
|
|
51
|
+
|
|
52
|
+
# 0.20.0
|
|
53
|
+
|
|
54
|
+
* Increase default string length in local_join
|
|
55
|
+
* add intNullVal option to local_join
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
cff-version: 1.2.0
|
|
2
|
+
message: "If you use this software, please cite it using the metadata from this file."
|
|
3
|
+
type: software
|
|
4
|
+
title: "sqlutilpy"
|
|
5
|
+
abstract: "Database query code returning numpy arrays. A thin layer over psycopg, sqlite3 and duckdb that runs SQL queries and returns the results as numpy arrays, and uploads numpy arrays into PostgreSQL tables."
|
|
6
|
+
authors:
|
|
7
|
+
- family-names: "Koposov"
|
|
8
|
+
given-names: "Sergey E."
|
|
9
|
+
email: "skoposov@ed.ac.uk"
|
|
10
|
+
orcid: "https://orcid.org/0000-0003-2644-135X"
|
|
11
|
+
affiliation: "Institute for Astronomy, University of Edinburgh"
|
|
12
|
+
license: "BSD-3-Clause"
|
|
13
|
+
repository-code: "https://github.com/segasai/sqlutilpy"
|
|
14
|
+
url: "https://sqlutilpy.readthedocs.io"
|
|
15
|
+
doi: "10.5281/zenodo.5160118"
|
|
16
|
+
identifiers:
|
|
17
|
+
- type: doi
|
|
18
|
+
value: "10.5281/zenodo.5160118"
|
|
19
|
+
description: "Zenodo concept DOI resolving to the latest release"
|
|
20
|
+
keywords:
|
|
21
|
+
- numpy
|
|
22
|
+
- postgresql
|
|
23
|
+
- sqlite
|
|
24
|
+
- duckdb
|
|
25
|
+
- sql
|
|
26
|
+
- astronomy
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Copyright (c)
|
|
1
|
+
Copyright (c) 2026, Sergey Koposov
|
|
2
2
|
All rights reserved.
|
|
3
3
|
|
|
4
4
|
Redistribution and use in source and binary forms, with or without
|
|
@@ -8,17 +8,17 @@ modification, are permitted provided that the following conditions are met:
|
|
|
8
8
|
* Redistributions in binary form must reproduce the above copyright
|
|
9
9
|
notice, this list of conditions and the following disclaimer in the
|
|
10
10
|
documentation and/or other materials provided with the distribution.
|
|
11
|
-
* Neither the name of
|
|
11
|
+
* Neither the name of Sergey Koposov nor the
|
|
12
12
|
names of its contributors may be used to endorse or promote products
|
|
13
13
|
derived from this software without specific prior written permission.
|
|
14
14
|
|
|
15
15
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
16
16
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
17
17
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
18
|
-
DISCLAIMED. IN NO EVENT SHALL
|
|
18
|
+
DISCLAIMED. IN NO EVENT SHALL the copyright holders BE LIABLE FOR ANY
|
|
19
19
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
20
20
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
21
21
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
22
22
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
23
23
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
24
|
-
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
24
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -1,25 +1,29 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sqlutilpy
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.30.0
|
|
4
4
|
Summary: Database query code returning numpy arrays
|
|
5
5
|
Author-email: "Sergey E. Koposov" <skoposov@ed.ac.uk>
|
|
6
|
-
License: BSD
|
|
6
|
+
License-Expression: BSD-3-Clause
|
|
7
7
|
Project-URL: Homepage, https://github.com/segasai/sqlutilpy
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
Project-URL: Documentation, https://sqlutilpy.readthedocs.io
|
|
9
|
+
Project-URL: Changelog, https://github.com/segasai/sqlutilpy/blob/master/CHANGELOG.md
|
|
10
|
+
Keywords: numpy,postgresql,sqlite,duckdb,sql,query,array
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Topic :: Database
|
|
11
15
|
Classifier: Topic :: Utilities
|
|
12
|
-
Requires-Python: >=3.
|
|
16
|
+
Requires-Python: >=3.10
|
|
13
17
|
Description-Content-Type: text/markdown
|
|
14
18
|
License-File: LICENSE.txt
|
|
15
|
-
Requires-Dist: numpy
|
|
19
|
+
Requires-Dist: numpy>=2
|
|
16
20
|
Requires-Dist: psycopg[binary]
|
|
17
21
|
Dynamic: license-file
|
|
18
22
|
|
|
19
23
|
[](https://github.com/segasai/sqlutilpy/actions)
|
|
20
24
|
[](http://sqlutilpy.readthedocs.io/en/latest/?badge=latest)
|
|
21
25
|
[](https://coveralls.io/github/segasai/sqlutilpy?branch=master)
|
|
22
|
-
[](https://doi.org/10.5281/zenodo.5160118)
|
|
23
27
|
|
|
24
28
|
# sqlutilpy
|
|
25
29
|
Python module to query SQL databases and return numpy arrays, upload
|
|
@@ -58,7 +62,7 @@ This command will run the query and put the columns into variables ra,dec:
|
|
|
58
62
|
|
|
59
63
|
```python
|
|
60
64
|
import sqlutilpy
|
|
61
|
-
ra,dec =
|
|
65
|
+
ra,dec = sqlutilpy.get('select ra,dec from mytable',
|
|
62
66
|
host='HOST_NAME_OF_MY_PG_SERVER',
|
|
63
67
|
db='THE_NAME_OF_MY_DB')
|
|
64
68
|
```
|
|
@@ -67,7 +71,19 @@ By default `sqlutilpy.get` executes the query and returns the tuple with
|
|
|
67
71
|
arrays. One array for each column in the query result.
|
|
68
72
|
You can return the results as dictionary using `asDict` option.
|
|
69
73
|
|
|
70
|
-
##
|
|
74
|
+
## SQLite and DuckDB
|
|
75
|
+
|
|
76
|
+
The same `get()` and `execute()` functions work with SQLite and DuckDB
|
|
77
|
+
files, by selecting the driver and giving the file name as the database:
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
a, b = sqlutilpy.get('select a, b from mytable', db='mydata.db', driver='sqlite3')
|
|
81
|
+
a, b = sqlutilpy.get('select a, b from mytable', db='mydata.duckdb', driver='duckdb')
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Uploading tables and `local_join` are only available with PostgreSQL.
|
|
85
|
+
|
|
86
|
+
## Uploading your arrays as columns in a table (PostgreSQL)
|
|
71
87
|
|
|
72
88
|
You can use `sqlutilpy.upload` to upload your arrays as columns in a table.
|
|
73
89
|
|
|
@@ -78,7 +94,7 @@ sqlutilpy.upload('mytable',(x,y),('xcol','ycol'))
|
|
|
78
94
|
```
|
|
79
95
|
This will create a table called `mytable` with columns `xcol` and `ycol`
|
|
80
96
|
|
|
81
|
-
## Join query involving your local data and the database table
|
|
97
|
+
## Join query involving your local data and the database table (PostgreSQL)
|
|
82
98
|
|
|
83
99
|
Sometimes it is beneficial to run a join query involving your local data and the data in the database.
|
|
84
100
|
|
|
@@ -102,7 +118,8 @@ is that it uploads the data to the database and runs a query against it.
|
|
|
102
118
|
|
|
103
119
|
Often it is beneficial to preserve an open connection to the database. You can do that if you first
|
|
104
120
|
obtain the connection using `sqlutilpy.getConnection()` and then provide it directly
|
|
105
|
-
to `
|
|
121
|
+
to `sqlutilpy.get()` and similar commands using `conn=` keyword:
|
|
122
|
+
```python
|
|
106
123
|
conn = sqlutilpy.getConnection(db='mydb', user='meuser', password='something', host='hostname')
|
|
107
124
|
R= sqlutilpy.get('select 1', conn=conn)
|
|
108
125
|
R1= sqlutilpy.get('select 1', conn=conn)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[](https://github.com/segasai/sqlutilpy/actions)
|
|
2
2
|
[](http://sqlutilpy.readthedocs.io/en/latest/?badge=latest)
|
|
3
3
|
[](https://coveralls.io/github/segasai/sqlutilpy?branch=master)
|
|
4
|
-
[](https://doi.org/10.5281/zenodo.5160118)
|
|
5
5
|
|
|
6
6
|
# sqlutilpy
|
|
7
7
|
Python module to query SQL databases and return numpy arrays, upload
|
|
@@ -40,7 +40,7 @@ This command will run the query and put the columns into variables ra,dec:
|
|
|
40
40
|
|
|
41
41
|
```python
|
|
42
42
|
import sqlutilpy
|
|
43
|
-
ra,dec =
|
|
43
|
+
ra,dec = sqlutilpy.get('select ra,dec from mytable',
|
|
44
44
|
host='HOST_NAME_OF_MY_PG_SERVER',
|
|
45
45
|
db='THE_NAME_OF_MY_DB')
|
|
46
46
|
```
|
|
@@ -49,7 +49,19 @@ By default `sqlutilpy.get` executes the query and returns the tuple with
|
|
|
49
49
|
arrays. One array for each column in the query result.
|
|
50
50
|
You can return the results as dictionary using `asDict` option.
|
|
51
51
|
|
|
52
|
-
##
|
|
52
|
+
## SQLite and DuckDB
|
|
53
|
+
|
|
54
|
+
The same `get()` and `execute()` functions work with SQLite and DuckDB
|
|
55
|
+
files, by selecting the driver and giving the file name as the database:
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
a, b = sqlutilpy.get('select a, b from mytable', db='mydata.db', driver='sqlite3')
|
|
59
|
+
a, b = sqlutilpy.get('select a, b from mytable', db='mydata.duckdb', driver='duckdb')
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Uploading tables and `local_join` are only available with PostgreSQL.
|
|
63
|
+
|
|
64
|
+
## Uploading your arrays as columns in a table (PostgreSQL)
|
|
53
65
|
|
|
54
66
|
You can use `sqlutilpy.upload` to upload your arrays as columns in a table.
|
|
55
67
|
|
|
@@ -60,7 +72,7 @@ sqlutilpy.upload('mytable',(x,y),('xcol','ycol'))
|
|
|
60
72
|
```
|
|
61
73
|
This will create a table called `mytable` with columns `xcol` and `ycol`
|
|
62
74
|
|
|
63
|
-
## Join query involving your local data and the database table
|
|
75
|
+
## Join query involving your local data and the database table (PostgreSQL)
|
|
64
76
|
|
|
65
77
|
Sometimes it is beneficial to run a join query involving your local data and the data in the database.
|
|
66
78
|
|
|
@@ -84,7 +96,8 @@ is that it uploads the data to the database and runs a query against it.
|
|
|
84
96
|
|
|
85
97
|
Often it is beneficial to preserve an open connection to the database. You can do that if you first
|
|
86
98
|
obtain the connection using `sqlutilpy.getConnection()` and then provide it directly
|
|
87
|
-
to `
|
|
99
|
+
to `sqlutilpy.get()` and similar commands using `conn=` keyword:
|
|
100
|
+
```python
|
|
88
101
|
conn = sqlutilpy.getConnection(db='mydb', user='meuser', password='something', host='hostname')
|
|
89
102
|
R= sqlutilpy.get('select 1', conn=conn)
|
|
90
103
|
R1= sqlutilpy.get('select 1', conn=conn)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "sqlutilpy"
|
|
3
|
+
dynamic = ["version"]
|
|
4
|
+
description = "Database query code returning numpy arrays"
|
|
5
|
+
authors = [
|
|
6
|
+
{name = "Sergey E. Koposov", email = "skoposov@ed.ac.uk"},
|
|
7
|
+
]
|
|
8
|
+
dependencies = [
|
|
9
|
+
"numpy>=2",
|
|
10
|
+
"psycopg[binary]"
|
|
11
|
+
]
|
|
12
|
+
requires-python = ">=3.10"
|
|
13
|
+
readme = "README.md"
|
|
14
|
+
license = "BSD-3-Clause"
|
|
15
|
+
license-files = ["LICENSE.txt"]
|
|
16
|
+
keywords = ["numpy", "postgresql", "sqlite", "duckdb", "sql", "query", "array"]
|
|
17
|
+
classifiers = [
|
|
18
|
+
"Development Status :: 5 - Production/Stable",
|
|
19
|
+
"Intended Audience :: Science/Research",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Topic :: Database",
|
|
22
|
+
"Topic :: Utilities",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[project.urls]
|
|
26
|
+
Homepage = "https://github.com/segasai/sqlutilpy"
|
|
27
|
+
Documentation = "https://sqlutilpy.readthedocs.io"
|
|
28
|
+
Changelog = "https://github.com/segasai/sqlutilpy/blob/master/CHANGELOG.md"
|
|
29
|
+
|
|
30
|
+
[build-system]
|
|
31
|
+
requires = ["setuptools>=77", "setuptools-git-versioning"]
|
|
32
|
+
build-backend = "setuptools.build_meta"
|
|
33
|
+
|
|
34
|
+
[tool.setuptools-git-versioning]
|
|
35
|
+
enabled = true
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Query SQL databases (PostgreSQL, SQLite, DuckDB) and get numpy
|
|
2
|
+
arrays back, upload numpy arrays as tables and join local arrays with
|
|
3
|
+
database tables
|
|
2
4
|
"""
|
|
3
5
|
import numpy
|
|
4
6
|
import numpy as np
|
|
@@ -45,13 +47,13 @@ def __check_driver(driver):
|
|
|
45
47
|
'psycopg2 driver is not supported anymore using psycopg instead')
|
|
46
48
|
driver = 'psycopg'
|
|
47
49
|
if driver not in SUPPORTED_DRIVERS:
|
|
48
|
-
raise SqlUtilException(
|
|
49
|
-
|
|
50
|
-
(driver, ', '.join(SUPPORTED_DRIVERS)))
|
|
50
|
+
raise SqlUtilException('Unknown driver %r; supported drivers are %s' %
|
|
51
|
+
(driver, ', '.join(SUPPORTED_DRIVERS)))
|
|
51
52
|
return driver
|
|
52
53
|
|
|
53
54
|
|
|
54
|
-
def getConnection(
|
|
55
|
+
def getConnection(*,
|
|
56
|
+
db=None,
|
|
55
57
|
driver=None,
|
|
56
58
|
user=None,
|
|
57
59
|
password=None,
|
|
@@ -64,28 +66,37 @@ def getConnection(db=None,
|
|
|
64
66
|
|
|
65
67
|
Parameters
|
|
66
68
|
----------
|
|
67
|
-
|
|
68
69
|
db : string
|
|
69
|
-
The name of the database (
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
The name of the database (PostgreSQL) or the filename of the
|
|
71
|
+
database (SQLite, DuckDB). For DuckDB ':memory:' gives an
|
|
72
|
+
in-memory database. For PostgreSQL, if this and the other
|
|
73
|
+
connection details are not given, the libpq defaults and
|
|
74
|
+
environment variables (PGDATABASE, PGHOST, PGPORT, PGUSER) are used
|
|
75
|
+
driver : string
|
|
76
|
+
The database driver: 'psycopg' (PostgreSQL), 'sqlite3' or 'duckdb'
|
|
73
77
|
user : string, optional
|
|
74
|
-
Username
|
|
75
|
-
password: string, optional
|
|
76
|
-
Password
|
|
78
|
+
Username (PostgreSQL only)
|
|
79
|
+
password : string, optional
|
|
80
|
+
Password (PostgreSQL only)
|
|
77
81
|
host : string, optional
|
|
78
|
-
|
|
79
|
-
port : integer
|
|
80
|
-
Connection port (by default 5432
|
|
81
|
-
timeout : integer
|
|
82
|
-
|
|
82
|
+
Hostname (PostgreSQL only)
|
|
83
|
+
port : integer, optional
|
|
84
|
+
Connection port (PostgreSQL only, by default 5432)
|
|
85
|
+
timeout : integer, optional
|
|
86
|
+
How long to wait for a locked database, in seconds
|
|
87
|
+
(SQLite only, by default 5)
|
|
83
88
|
|
|
84
89
|
Returns
|
|
85
90
|
-------
|
|
86
91
|
conn : object
|
|
87
|
-
|
|
92
|
+
Database connection
|
|
88
93
|
|
|
94
|
+
Examples
|
|
95
|
+
--------
|
|
96
|
+
>>> conn = sqlutilpy.getConnection(db='mydb', host='myhost',
|
|
97
|
+
... driver='psycopg')
|
|
98
|
+
>>> conn = sqlutilpy.getConnection(db='mydata.db', driver='sqlite3')
|
|
99
|
+
>>> conn = sqlutilpy.getConnection(db=':memory:', driver='duckdb')
|
|
89
100
|
"""
|
|
90
101
|
driver = __check_driver(driver)
|
|
91
102
|
if driver == 'psycopg':
|
|
@@ -112,10 +123,39 @@ def getConnection(db=None,
|
|
|
112
123
|
return conn
|
|
113
124
|
|
|
114
125
|
|
|
115
|
-
def getCursor(conn, driver=None, preamb=None, named=True):
|
|
126
|
+
def getCursor(conn, *, driver=None, preamb=None, named=True, notNamed=None):
|
|
116
127
|
"""
|
|
117
128
|
Retrieve the database cursor
|
|
129
|
+
|
|
130
|
+
Parameters
|
|
131
|
+
----------
|
|
132
|
+
conn : object
|
|
133
|
+
Database connection
|
|
134
|
+
driver : string
|
|
135
|
+
The database driver: 'psycopg', 'sqlite3' or 'duckdb'
|
|
136
|
+
preamb : string, optional
|
|
137
|
+
SQL to be executed on the cursor before anything else
|
|
138
|
+
named : bool
|
|
139
|
+
If True (the default) and the driver is 'psycopg', a server-side
|
|
140
|
+
(named) cursor is returned, so that the results can be fetched in
|
|
141
|
+
batches; this is what get(batched=True) uses. If False a
|
|
142
|
+
client-side cursor is returned, as for get(batched=False).
|
|
143
|
+
Ignored for the other drivers
|
|
144
|
+
notNamed : bool, optional
|
|
145
|
+
Deprecated spelling from before 0.27: notNamed=True is the same as
|
|
146
|
+
named=False. It will be removed in a future release
|
|
147
|
+
|
|
148
|
+
Returns
|
|
149
|
+
-------
|
|
150
|
+
cur : object
|
|
151
|
+
Database cursor
|
|
118
152
|
"""
|
|
153
|
+
if notNamed is not None:
|
|
154
|
+
# the pre-0.27 spelling of named=not notNamed
|
|
155
|
+
warnings.warn('notNamed is deprecated; use named=not notNamed instead',
|
|
156
|
+
DeprecationWarning,
|
|
157
|
+
stacklevel=2)
|
|
158
|
+
named = not notNamed
|
|
119
159
|
driver = __check_driver(driver)
|
|
120
160
|
if driver == 'psycopg':
|
|
121
161
|
cur = conn.cursor(scrollable=False)
|
|
@@ -426,88 +466,123 @@ def __process_duckdb_column(col, intNullVal, strNullVal):
|
|
|
426
466
|
|
|
427
467
|
def get(query,
|
|
428
468
|
params=None,
|
|
469
|
+
*,
|
|
429
470
|
db="wsdb",
|
|
430
471
|
driver="psycopg",
|
|
431
472
|
user=None,
|
|
432
473
|
password=None,
|
|
433
474
|
host=None,
|
|
434
|
-
preamb=None,
|
|
435
|
-
conn=None,
|
|
436
475
|
port=None,
|
|
437
|
-
|
|
476
|
+
conn=None,
|
|
477
|
+
preamb=None,
|
|
438
478
|
timeout=None,
|
|
479
|
+
strLength=STRLEN_DEFAULT,
|
|
439
480
|
batched=True,
|
|
440
481
|
asDict=False,
|
|
441
482
|
intNullVal=-9999,
|
|
442
483
|
strNullVal='None',
|
|
443
|
-
nthreads=1
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
484
|
+
nthreads=1,
|
|
485
|
+
notNamed=None):
|
|
486
|
+
"""
|
|
487
|
+
Execute the SQL query and return the result as a tuple or dictionary
|
|
488
|
+
of numpy arrays, one array per column.
|
|
447
489
|
|
|
448
490
|
Parameters
|
|
449
491
|
----------
|
|
450
492
|
query : string
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
params : tuple
|
|
493
|
+
The query to execute. It can contain placeholders for the query
|
|
494
|
+
parameters: %s for PostgreSQL, ? for SQLite and DuckDB
|
|
495
|
+
params : tuple, optional
|
|
454
496
|
Query parameters
|
|
455
|
-
conn : object
|
|
456
|
-
The connection object to the DB (optional) to avoid reconnecting
|
|
457
|
-
asDict : boolean
|
|
458
|
-
Flag whether to retrieve the results as a dictionary with column
|
|
459
|
-
names as keys
|
|
460
|
-
strLength : integer
|
|
461
|
-
The minimum length allocated for string columns. If longer
|
|
462
|
-
strings are encountered, the allocated length is extended
|
|
463
|
-
to accommodate them
|
|
464
|
-
intNullVal : integer, optional
|
|
465
|
-
All the integer columns with nulls will have null replaced by
|
|
466
|
-
this value
|
|
467
|
-
strNullVal : string, optional
|
|
468
|
-
All the string columns with nulls will have null replaced by
|
|
469
|
-
this value
|
|
470
497
|
db : string
|
|
471
|
-
The name of the database
|
|
498
|
+
The name of the database (PostgreSQL) or the filename of the
|
|
499
|
+
database (SQLite, DuckDB)
|
|
472
500
|
driver : string, optional
|
|
473
|
-
The
|
|
501
|
+
The database driver: 'psycopg' (PostgreSQL, the default),
|
|
502
|
+
'sqlite3' or 'duckdb'
|
|
474
503
|
user : string, optional
|
|
475
|
-
User name for the DB connection
|
|
504
|
+
User name for the DB connection (PostgreSQL only)
|
|
476
505
|
password : string, optional
|
|
477
|
-
DB connection password
|
|
506
|
+
DB connection password (PostgreSQL only)
|
|
478
507
|
host : string, optional
|
|
479
|
-
Hostname of the database
|
|
508
|
+
Hostname of the database (PostgreSQL only)
|
|
480
509
|
port : integer, optional
|
|
481
|
-
Port of the database
|
|
482
|
-
|
|
483
|
-
|
|
510
|
+
Port of the database (PostgreSQL only)
|
|
511
|
+
conn : object, optional
|
|
512
|
+
An existing connection to the DB (see getConnection()), to avoid
|
|
513
|
+
reconnecting. The connection is left open afterwards. The driver
|
|
514
|
+
argument must still match the connection
|
|
515
|
+
preamb : string, optional
|
|
516
|
+
SQL to be executed before the query
|
|
517
|
+
timeout : integer, optional
|
|
518
|
+
How long to wait for a locked database, in seconds (SQLite only)
|
|
519
|
+
strLength : integer, optional
|
|
520
|
+
The minimum width allocated for string columns (PostgreSQL only).
|
|
521
|
+
If longer strings are encountered the width is extended to
|
|
522
|
+
accommodate them. With SQLite and DuckDB the width is always
|
|
523
|
+
inferred from the data
|
|
484
524
|
batched : bool, optional
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
and on the fly
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
525
|
+
PostgreSQL only. With the default of True the results are
|
|
526
|
+
retrieved through a server-side (named) cursor in batches of
|
|
527
|
+
config.arraysize rows and converted to numpy on the fly, so the
|
|
528
|
+
memory use is about the size of the final arrays. With False a
|
|
529
|
+
client-side cursor is used and the whole result is fetched at
|
|
530
|
+
once, which needs about twice the memory, but lets PostgreSQL use
|
|
531
|
+
parallel query plans, which can be several times faster for heavy
|
|
532
|
+
queries. The server-side cursor only accepts SELECT, VALUES,
|
|
533
|
+
TABLE and WITH statements, so statements such EXPLAIN, SHOW
|
|
534
|
+
must be run with batched=False
|
|
535
|
+
asDict : bool, optional
|
|
536
|
+
If True, return an ordered dictionary keyed by column name instead
|
|
537
|
+
of a tuple of arrays. Repeated column names get a numeric suffix
|
|
538
|
+
intNullVal : integer or None, optional
|
|
539
|
+
The value that replaces nulls in integer columns (-9999 by
|
|
540
|
+
default). If None, integer columns with nulls are returned as
|
|
541
|
+
object arrays with None in place of the nulls
|
|
542
|
+
strNullVal : string, optional
|
|
543
|
+
The value that replaces nulls in string columns ('None' by
|
|
544
|
+
default)
|
|
545
|
+
nthreads : integer, optional
|
|
546
|
+
The number of threads used to convert the batches to numpy
|
|
547
|
+
in the batched PostgreSQL mode
|
|
548
|
+
notNamed : bool, optional
|
|
549
|
+
Deprecated spelling from before 0.27: notNamed=True is the same as
|
|
550
|
+
batched=False (a client-side cursor). It will be removed in a
|
|
551
|
+
future release
|
|
552
|
+
|
|
494
553
|
Returns
|
|
495
554
|
-------
|
|
496
|
-
ret :
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
555
|
+
ret : tuple or dictionary
|
|
556
|
+
A tuple with one numpy array per column of the query result, or,
|
|
557
|
+
with asDict=True, an ordered dictionary mapping the column names
|
|
558
|
+
to the arrays. Nulls in floating point columns become NaN, in
|
|
559
|
+
timestamp columns NaT; see intNullVal and strNullVal for the
|
|
560
|
+
integer and string columns
|
|
501
561
|
|
|
502
562
|
Examples
|
|
503
563
|
--------
|
|
504
|
-
>>>
|
|
564
|
+
>>> ra, dec, d25 = sqlutilpy.get('select ra, dec, d25 from rc3',
|
|
565
|
+
... host='myhost', db='mydb')
|
|
566
|
+
|
|
567
|
+
With query parameters:
|
|
505
568
|
|
|
506
|
-
|
|
569
|
+
>>> ra, dec = sqlutilpy.get('select ra, dec from rc3 where name = %s',
|
|
570
|
+
... ('NGC 3166', ), host='myhost', db='mydb')
|
|
507
571
|
|
|
508
|
-
|
|
509
|
-
|
|
572
|
+
From a SQLite file:
|
|
573
|
+
|
|
574
|
+
>>> a, b = sqlutilpy.get('select a, b from tmp where a > ?', (3, ),
|
|
575
|
+
... db='mydata.db', driver='sqlite3')
|
|
576
|
+
"""
|
|
510
577
|
|
|
578
|
+
if notNamed is not None:
|
|
579
|
+
# the pre-0.27 spelling: notNamed=True asked for a client-side
|
|
580
|
+
# cursor, which is what batched=False does now
|
|
581
|
+
warnings.warn(
|
|
582
|
+
'notNamed is deprecated; use batched=not notNamed instead',
|
|
583
|
+
DeprecationWarning,
|
|
584
|
+
stacklevel=2)
|
|
585
|
+
batched = not notNamed
|
|
511
586
|
driver = __check_driver(driver)
|
|
512
587
|
connSupplied = (conn is not None)
|
|
513
588
|
if not connSupplied:
|
|
@@ -519,13 +594,11 @@ def get(query,
|
|
|
519
594
|
port=port,
|
|
520
595
|
timeout=timeout)
|
|
521
596
|
try:
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
namedCursor = False
|
|
528
|
-
cur = getCursor(conn, driver=driver, preamb=preamb, named=namedCursor)
|
|
597
|
+
# batched=True means a server-side (named) PostgreSQL cursor,
|
|
598
|
+
# read with fetchmany() in chunks of config.arraysize rows;
|
|
599
|
+
# batched=False means a client-side cursor read with one
|
|
600
|
+
# fetchall(). The cursor type is irrelevant for the other drivers
|
|
601
|
+
cur = getCursor(conn, driver=driver, preamb=preamb, named=batched)
|
|
529
602
|
|
|
530
603
|
if params is None:
|
|
531
604
|
res = cur.execute(query)
|
|
@@ -667,6 +740,7 @@ def get(query,
|
|
|
667
740
|
|
|
668
741
|
def execute(query,
|
|
669
742
|
params=None,
|
|
743
|
+
*,
|
|
670
744
|
db='wsdb',
|
|
671
745
|
driver="psycopg",
|
|
672
746
|
user=None,
|
|
@@ -678,35 +752,45 @@ def execute(query,
|
|
|
678
752
|
timeout=None,
|
|
679
753
|
noCommit=False):
|
|
680
754
|
"""
|
|
681
|
-
Execute
|
|
755
|
+
Execute an SQL statement without returning any results
|
|
682
756
|
|
|
683
757
|
Parameters
|
|
684
758
|
----------
|
|
685
|
-
query: string
|
|
686
|
-
The
|
|
687
|
-
|
|
688
|
-
|
|
759
|
+
query : string
|
|
760
|
+
The statement to execute. It can contain placeholders for the
|
|
761
|
+
parameters: %s for PostgreSQL, ? for SQLite and DuckDB
|
|
762
|
+
params : tuple, optional
|
|
763
|
+
Parameters of the statement
|
|
689
764
|
db : string
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
765
|
+
The name of the database (PostgreSQL) or the filename of the
|
|
766
|
+
database (SQLite, DuckDB)
|
|
767
|
+
driver : string, optional
|
|
768
|
+
The database driver: 'psycopg' (PostgreSQL, the default),
|
|
769
|
+
'sqlite3' or 'duckdb'
|
|
693
770
|
user : string, optional
|
|
694
|
-
|
|
771
|
+
User name for the DB connection (PostgreSQL only)
|
|
695
772
|
password : string, optional
|
|
696
|
-
DB connection password
|
|
773
|
+
DB connection password (PostgreSQL only)
|
|
697
774
|
host : string, optional
|
|
698
|
-
Hostname of the database
|
|
775
|
+
Hostname of the database (PostgreSQL only)
|
|
699
776
|
port : integer, optional
|
|
700
|
-
Port of the database
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
777
|
+
Port of the database (PostgreSQL only)
|
|
778
|
+
conn : object, optional
|
|
779
|
+
An existing connection to the DB (see getConnection()), to avoid
|
|
780
|
+
reconnecting. The connection is left open afterwards
|
|
781
|
+
preamb : string, optional
|
|
782
|
+
SQL to be executed before the statement
|
|
783
|
+
timeout : integer, optional
|
|
784
|
+
How long to wait for a locked database, in seconds (SQLite only)
|
|
785
|
+
noCommit : bool, optional
|
|
786
|
+
By default the statement is committed. With noCommit=True the
|
|
787
|
+
commit is not issued
|
|
704
788
|
|
|
705
789
|
Examples
|
|
706
790
|
--------
|
|
707
|
-
>>>
|
|
708
|
-
>>>
|
|
709
|
-
|
|
791
|
+
>>> sqlutilpy.execute('drop table mytab', conn=conn)
|
|
792
|
+
>>> sqlutilpy.execute('create table mytab (a int)', db='mydb')
|
|
793
|
+
>>> sqlutilpy.execute('insert into mytab values (%s)', (1, ), db='mydb')
|
|
710
794
|
"""
|
|
711
795
|
driver = __check_driver(driver)
|
|
712
796
|
connSupplied = (conn is not None)
|
|
@@ -850,6 +934,7 @@ def failure_cleanup(conn, connSupplied):
|
|
|
850
934
|
def upload(tableName,
|
|
851
935
|
arrays,
|
|
852
936
|
names=None,
|
|
937
|
+
*,
|
|
853
938
|
db="wsdb",
|
|
854
939
|
driver="psycopg",
|
|
855
940
|
user=None,
|
|
@@ -865,42 +950,59 @@ def upload(tableName,
|
|
|
865
950
|
createTable=True,
|
|
866
951
|
delimiter='|'):
|
|
867
952
|
"""
|
|
868
|
-
Upload the data stored in
|
|
953
|
+
Upload the data stored in a tuple of arrays (or a table) into a new
|
|
954
|
+
table in the DB. PostgreSQL only.
|
|
869
955
|
|
|
870
956
|
Parameters
|
|
871
957
|
----------
|
|
872
958
|
tableName : string
|
|
873
959
|
The name of the table where the data will be uploaded
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
960
|
+
arrays : tuple, astropy.table.Table, pandas.DataFrame or dict
|
|
961
|
+
A tuple of arrays that will become the columns of the table, or
|
|
962
|
+
an astropy Table, pandas DataFrame or dictionary of arrays, in
|
|
963
|
+
which case the column names are taken from it and the names
|
|
964
|
+
argument must be omitted.
|
|
965
|
+
Integer, floating point, boolean, string (unicode or bytes;
|
|
966
|
+
bytes are decoded as utf-8), and datetime64 arrays are supported,
|
|
967
|
+
as well as object arrays holding numpy arrays, which become
|
|
968
|
+
array columns
|
|
969
|
+
names : tuple, optional
|
|
970
|
+
The column names (one per array). Characters that are not allowed
|
|
971
|
+
in identifiers (spaces, brackets, dashes) are replaced by
|
|
972
|
+
underscores, with a warning
|
|
973
|
+
db : string
|
|
974
|
+
Database name
|
|
975
|
+
driver : string
|
|
976
|
+
The database driver; only 'psycopg' is supported
|
|
977
|
+
user : string, optional
|
|
978
|
+
User name for the DB connection
|
|
979
|
+
password : string, optional
|
|
980
|
+
DB connection password
|
|
981
|
+
host : string, optional
|
|
982
|
+
Hostname of the database
|
|
983
|
+
port : integer, optional
|
|
984
|
+
Port of the database
|
|
985
|
+
conn : object, optional
|
|
986
|
+
An existing connection to the DB (see getConnection()), to avoid
|
|
987
|
+
reconnecting. The connection is left open afterwards
|
|
988
|
+
preamb : string, optional
|
|
989
|
+
SQL to be executed before the upload
|
|
990
|
+
timeout : integer, optional
|
|
991
|
+
Unused for PostgreSQL
|
|
992
|
+
noCommit : bool, optional
|
|
993
|
+
If True, the commit is not executed and the table goes away
|
|
994
|
+
when the connection is closed
|
|
995
|
+
temp : bool, optional
|
|
996
|
+
If True, a temporary table is created
|
|
997
|
+
analyze : bool, optional
|
|
998
|
+
If True, the table is analyzed after the upload
|
|
999
|
+
createTable : bool, optional
|
|
1000
|
+
If True (the default), the table is created before uploading.
|
|
1001
|
+
With False the data is appended to an existing table
|
|
1002
|
+
delimiter : string, optional
|
|
1003
|
+
The column delimiter used when sending the data to the DB
|
|
1004
|
+
(the default is ``|``). Strings containing the delimiter are
|
|
1005
|
+
escaped, so it can be any character
|
|
904
1006
|
|
|
905
1007
|
Examples
|
|
906
1008
|
--------
|
|
@@ -908,7 +1010,7 @@ def upload(tableName,
|
|
|
908
1010
|
>>> y = x**.5
|
|
909
1011
|
>>> sqlutilpy.upload('mytable', (x, y), ('xcol', 'ycol'))
|
|
910
1012
|
|
|
911
|
-
>>> T = astropy.Table({'x':[1, 2, 3], 'y':['a', 'b', 'c'])
|
|
1013
|
+
>>> T = astropy.table.Table({'x': [1, 2, 3], 'y': ['a', 'b', 'c']})
|
|
912
1014
|
>>> sqlutilpy.upload('mytable', T)
|
|
913
1015
|
"""
|
|
914
1016
|
driver = __check_driver(driver)
|
|
@@ -1000,6 +1102,7 @@ def local_join(query,
|
|
|
1000
1102
|
tableName,
|
|
1001
1103
|
arrays,
|
|
1002
1104
|
names,
|
|
1105
|
+
*,
|
|
1003
1106
|
db=None,
|
|
1004
1107
|
driver="psycopg",
|
|
1005
1108
|
user=None,
|
|
@@ -1010,31 +1113,71 @@ def local_join(query,
|
|
|
1010
1113
|
preamb=None,
|
|
1011
1114
|
timeout=None,
|
|
1012
1115
|
strLength=STRLEN_DEFAULT,
|
|
1116
|
+
asDict=False,
|
|
1013
1117
|
intNullVal=-9999,
|
|
1014
|
-
strNullVal='None'
|
|
1015
|
-
asDict=False):
|
|
1118
|
+
strNullVal='None'):
|
|
1016
1119
|
"""
|
|
1017
|
-
Join your local data in python with the data in the database
|
|
1018
|
-
|
|
1019
|
-
|
|
1120
|
+
Join your local data in python with the data in the database.
|
|
1121
|
+
PostgreSQL only.
|
|
1122
|
+
|
|
1123
|
+
This uploads the arrays into a temporary table and then runs the
|
|
1124
|
+
query, which can refer to that table. The temporary table exists
|
|
1125
|
+
only for the duration of the call.
|
|
1020
1126
|
|
|
1021
1127
|
Parameters
|
|
1022
1128
|
----------
|
|
1023
|
-
query :
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1129
|
+
query : string
|
|
1130
|
+
The query to be executed; it can refer to the table tableName
|
|
1131
|
+
tableName : string
|
|
1132
|
+
The name of the temporary table holding your data
|
|
1133
|
+
arrays : tuple
|
|
1134
|
+
The tuple of arrays with the data to be loaded in the DB
|
|
1135
|
+
names : tuple
|
|
1136
|
+
The column names of the temporary table (one per array)
|
|
1137
|
+
db : string
|
|
1138
|
+
Database name
|
|
1139
|
+
driver : string
|
|
1140
|
+
The database driver; only 'psycopg' is supported
|
|
1141
|
+
user : string, optional
|
|
1142
|
+
User name for the DB connection
|
|
1143
|
+
password : string, optional
|
|
1144
|
+
DB connection password
|
|
1145
|
+
host : string, optional
|
|
1146
|
+
Hostname of the database
|
|
1147
|
+
port : integer, optional
|
|
1148
|
+
Port of the database
|
|
1149
|
+
conn : object, optional
|
|
1150
|
+
An existing connection to the DB (see getConnection()), to avoid
|
|
1151
|
+
reconnecting. The connection is left open afterwards
|
|
1152
|
+
preamb : string, optional
|
|
1153
|
+
SQL to be executed before the query
|
|
1154
|
+
timeout : integer, optional
|
|
1155
|
+
Unused for PostgreSQL
|
|
1156
|
+
strLength : integer, optional
|
|
1157
|
+
The minimum width allocated for string columns (see get())
|
|
1158
|
+
asDict : bool, optional
|
|
1159
|
+
If True, return an ordered dictionary keyed by column name instead
|
|
1160
|
+
of a tuple of arrays
|
|
1161
|
+
|
|
1162
|
+
intNullVal : integer or None, optional
|
|
1163
|
+
The value that replaces nulls in integer columns (see get())
|
|
1164
|
+
strNullVal : string, optional
|
|
1165
|
+
The value that replaces nulls in string columns (see get())
|
|
1166
|
+
Returns
|
|
1167
|
+
-------
|
|
1168
|
+
ret : tuple or dictionary
|
|
1169
|
+
The result of the query, as returned by get()
|
|
1027
1170
|
|
|
1028
1171
|
Examples
|
|
1029
1172
|
--------
|
|
1030
|
-
This
|
|
1031
|
-
|
|
1173
|
+
This extracts the rows of sometable matching the values of the
|
|
1174
|
+
array x:
|
|
1032
1175
|
|
|
1033
1176
|
>>> x = np.arange(10)
|
|
1034
1177
|
>>> y = x**.5
|
|
1035
1178
|
>>> sqlutilpy.local_join('''
|
|
1036
|
-
...
|
|
1037
|
-
...
|
|
1179
|
+
... select s.* from mytable as m left join sometable as s
|
|
1180
|
+
... on s.x = m.x order by m.x''',
|
|
1038
1181
|
... 'mytable', (x, y), ('x', 'y'))
|
|
1039
1182
|
"""
|
|
1040
1183
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '0.30.0'
|
|
@@ -1,25 +1,29 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sqlutilpy
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.30.0
|
|
4
4
|
Summary: Database query code returning numpy arrays
|
|
5
5
|
Author-email: "Sergey E. Koposov" <skoposov@ed.ac.uk>
|
|
6
|
-
License: BSD
|
|
6
|
+
License-Expression: BSD-3-Clause
|
|
7
7
|
Project-URL: Homepage, https://github.com/segasai/sqlutilpy
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
Project-URL: Documentation, https://sqlutilpy.readthedocs.io
|
|
9
|
+
Project-URL: Changelog, https://github.com/segasai/sqlutilpy/blob/master/CHANGELOG.md
|
|
10
|
+
Keywords: numpy,postgresql,sqlite,duckdb,sql,query,array
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Topic :: Database
|
|
11
15
|
Classifier: Topic :: Utilities
|
|
12
|
-
Requires-Python: >=3.
|
|
16
|
+
Requires-Python: >=3.10
|
|
13
17
|
Description-Content-Type: text/markdown
|
|
14
18
|
License-File: LICENSE.txt
|
|
15
|
-
Requires-Dist: numpy
|
|
19
|
+
Requires-Dist: numpy>=2
|
|
16
20
|
Requires-Dist: psycopg[binary]
|
|
17
21
|
Dynamic: license-file
|
|
18
22
|
|
|
19
23
|
[](https://github.com/segasai/sqlutilpy/actions)
|
|
20
24
|
[](http://sqlutilpy.readthedocs.io/en/latest/?badge=latest)
|
|
21
25
|
[](https://coveralls.io/github/segasai/sqlutilpy?branch=master)
|
|
22
|
-
[](https://doi.org/10.5281/zenodo.5160118)
|
|
23
27
|
|
|
24
28
|
# sqlutilpy
|
|
25
29
|
Python module to query SQL databases and return numpy arrays, upload
|
|
@@ -58,7 +62,7 @@ This command will run the query and put the columns into variables ra,dec:
|
|
|
58
62
|
|
|
59
63
|
```python
|
|
60
64
|
import sqlutilpy
|
|
61
|
-
ra,dec =
|
|
65
|
+
ra,dec = sqlutilpy.get('select ra,dec from mytable',
|
|
62
66
|
host='HOST_NAME_OF_MY_PG_SERVER',
|
|
63
67
|
db='THE_NAME_OF_MY_DB')
|
|
64
68
|
```
|
|
@@ -67,7 +71,19 @@ By default `sqlutilpy.get` executes the query and returns the tuple with
|
|
|
67
71
|
arrays. One array for each column in the query result.
|
|
68
72
|
You can return the results as dictionary using `asDict` option.
|
|
69
73
|
|
|
70
|
-
##
|
|
74
|
+
## SQLite and DuckDB
|
|
75
|
+
|
|
76
|
+
The same `get()` and `execute()` functions work with SQLite and DuckDB
|
|
77
|
+
files, by selecting the driver and giving the file name as the database:
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
a, b = sqlutilpy.get('select a, b from mytable', db='mydata.db', driver='sqlite3')
|
|
81
|
+
a, b = sqlutilpy.get('select a, b from mytable', db='mydata.duckdb', driver='duckdb')
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Uploading tables and `local_join` are only available with PostgreSQL.
|
|
85
|
+
|
|
86
|
+
## Uploading your arrays as columns in a table (PostgreSQL)
|
|
71
87
|
|
|
72
88
|
You can use `sqlutilpy.upload` to upload your arrays as columns in a table.
|
|
73
89
|
|
|
@@ -78,7 +94,7 @@ sqlutilpy.upload('mytable',(x,y),('xcol','ycol'))
|
|
|
78
94
|
```
|
|
79
95
|
This will create a table called `mytable` with columns `xcol` and `ycol`
|
|
80
96
|
|
|
81
|
-
## Join query involving your local data and the database table
|
|
97
|
+
## Join query involving your local data and the database table (PostgreSQL)
|
|
82
98
|
|
|
83
99
|
Sometimes it is beneficial to run a join query involving your local data and the data in the database.
|
|
84
100
|
|
|
@@ -102,7 +118,8 @@ is that it uploads the data to the database and runs a query against it.
|
|
|
102
118
|
|
|
103
119
|
Often it is beneficial to preserve an open connection to the database. You can do that if you first
|
|
104
120
|
obtain the connection using `sqlutilpy.getConnection()` and then provide it directly
|
|
105
|
-
to `
|
|
121
|
+
to `sqlutilpy.get()` and similar commands using `conn=` keyword:
|
|
122
|
+
```python
|
|
106
123
|
conn = sqlutilpy.getConnection(db='mydb', user='meuser', password='something', host='hostname')
|
|
107
124
|
R= sqlutilpy.get('select 1', conn=conn)
|
|
108
125
|
R1= sqlutilpy.get('select 1', conn=conn)
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
numpy
|
|
1
|
+
numpy>=2
|
|
2
2
|
psycopg[binary]
|
|
@@ -728,3 +728,22 @@ def test_string_growth(setup, batched):
|
|
|
728
728
|
|
|
729
729
|
finally:
|
|
730
730
|
sqlutil.execute(f"drop table if exists {table_name}", conn=conn)
|
|
731
|
+
|
|
732
|
+
|
|
733
|
+
def test_notNamed_deprecated(setup):
|
|
734
|
+
# notNamed=True is a deprecated alias of batched=False
|
|
735
|
+
kw, conn = setup
|
|
736
|
+
with pytest.warns(DeprecationWarning, match='notNamed'):
|
|
737
|
+
a, = sqlutil.get('select sicol from sqlutil_test order by sicol',
|
|
738
|
+
notNamed=True,
|
|
739
|
+
conn=conn)
|
|
740
|
+
assert list(a) == [1, 11]
|
|
741
|
+
with pytest.warns(DeprecationWarning, match='notNamed'):
|
|
742
|
+
cur = sqlutil.getCursor(conn, driver='psycopg', notNamed=True)
|
|
743
|
+
assert not isinstance(cur, psycopg.ServerCursor)
|
|
744
|
+
cur.close()
|
|
745
|
+
with pytest.warns(DeprecationWarning, match='notNamed'):
|
|
746
|
+
cur = sqlutil.getCursor(conn, driver='psycopg', notNamed=False)
|
|
747
|
+
assert isinstance(cur, psycopg.ServerCursor)
|
|
748
|
+
cur.close()
|
|
749
|
+
conn.rollback()
|
|
@@ -71,3 +71,35 @@ def test_unknown_driver(setup):
|
|
|
71
71
|
with pytest.raises(sqlutil.SqlUtilException):
|
|
72
72
|
sqlutil.getConnection(db='sql.db', driver='nosuchdriver')
|
|
73
73
|
conn.close()
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def test_notNamed_deprecated(setup):
|
|
77
|
+
# notNamed is a deprecated alias of batched=not notNamed
|
|
78
|
+
with pytest.warns(DeprecationWarning, match='notNamed'):
|
|
79
|
+
a, b = sqlutil.get('select a,b from tmp', notNamed=True, **setup)
|
|
80
|
+
assert (len(a) == 4)
|
|
81
|
+
conn = sqlite3.connect('sql.db')
|
|
82
|
+
with pytest.warns(DeprecationWarning, match='notNamed'):
|
|
83
|
+
sqlutil.getCursor(conn, driver='sqlite3', notNamed=True)
|
|
84
|
+
conn.close()
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def test_keyword_only(setup):
|
|
88
|
+
# the options can only be given as keywords
|
|
89
|
+
import numpy as np
|
|
90
|
+
with pytest.raises(TypeError):
|
|
91
|
+
sqlutil.get('select a,b from tmp', None, 'sql.db', 'sqlite3')
|
|
92
|
+
with pytest.raises(TypeError):
|
|
93
|
+
sqlutil.execute('select 1', None, 'sql.db', 'sqlite3')
|
|
94
|
+
with pytest.raises(TypeError):
|
|
95
|
+
sqlutil.upload('tmp4', (np.arange(2), ), ('a', ), 'sql.db',
|
|
96
|
+
'sqlite3')
|
|
97
|
+
with pytest.raises(TypeError):
|
|
98
|
+
sqlutil.local_join('select 1', 'tmp5', (np.arange(2), ), ('a', ),
|
|
99
|
+
'sql.db', 'sqlite3')
|
|
100
|
+
with pytest.raises(TypeError):
|
|
101
|
+
sqlutil.getConnection('sql.db', 'sqlite3')
|
|
102
|
+
conn = sqlite3.connect('sql.db')
|
|
103
|
+
with pytest.raises(TypeError):
|
|
104
|
+
sqlutil.getCursor(conn, 'sqlite3')
|
|
105
|
+
conn.close()
|
sqlutilpy-0.29.0/MANIFEST.in
DELETED
sqlutilpy-0.29.0/pyproject.toml
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
[project]
|
|
2
|
-
name = "sqlutilpy"
|
|
3
|
-
dynamic = ["version"]
|
|
4
|
-
description = "Database query code returning numpy arrays"
|
|
5
|
-
authors = [
|
|
6
|
-
{name = "Sergey E. Koposov", email = "skoposov@ed.ac.uk"},
|
|
7
|
-
]
|
|
8
|
-
dependencies = [
|
|
9
|
-
"numpy",
|
|
10
|
-
"psycopg[binary]"
|
|
11
|
-
]
|
|
12
|
-
requires-python = ">=3.8"
|
|
13
|
-
readme = "README.md"
|
|
14
|
-
license = {text = "BSD"}
|
|
15
|
-
keywords = ["numpy postgresql query sql sqlite array"]
|
|
16
|
-
classifiers = [
|
|
17
|
-
"Development Status :: 3 - Alpha",
|
|
18
|
-
"License :: OSI Approved :: BSD License",
|
|
19
|
-
"Topic :: Utilities",
|
|
20
|
-
]
|
|
21
|
-
|
|
22
|
-
[build-system]
|
|
23
|
-
requires = ["setuptools", "wheel", "setuptools-git-versioning"]
|
|
24
|
-
build-backend = "setuptools.build_meta"
|
|
25
|
-
|
|
26
|
-
[tool.setuptools-git-versioning]
|
|
27
|
-
enabled = true
|
|
28
|
-
|
|
29
|
-
[project.urls]
|
|
30
|
-
Homepage = "https://github.com/segasai/sqlutilpy"
|
|
31
|
-
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = '0.29.0'
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|