sqlutilpy 0.28.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.28.0 → sqlutilpy-0.30.0}/LICENSE.txt +4 -4
- sqlutilpy-0.30.0/MANIFEST.in +6 -0
- {sqlutilpy-0.28.0/src/sqlutilpy.egg-info → sqlutilpy-0.30.0}/PKG-INFO +29 -12
- {sqlutilpy-0.28.0 → sqlutilpy-0.30.0}/README.md +18 -5
- sqlutilpy-0.30.0/pyproject.toml +35 -0
- {sqlutilpy-0.28.0 → sqlutilpy-0.30.0}/src/sqlutilpy/sqlutil.py +291 -146
- sqlutilpy-0.30.0/src/sqlutilpy/version.py +1 -0
- {sqlutilpy-0.28.0 → sqlutilpy-0.30.0/src/sqlutilpy.egg-info}/PKG-INFO +29 -12
- {sqlutilpy-0.28.0 → sqlutilpy-0.30.0}/src/sqlutilpy.egg-info/SOURCES.txt +2 -0
- {sqlutilpy-0.28.0 → sqlutilpy-0.30.0}/src/sqlutilpy.egg-info/requires.txt +1 -1
- {sqlutilpy-0.28.0 → sqlutilpy-0.30.0}/tests/test_pg.py +19 -0
- {sqlutilpy-0.28.0 → sqlutilpy-0.30.0}/tests/test_sqlite.py +32 -0
- sqlutilpy-0.28.0/MANIFEST.in +0 -9
- sqlutilpy-0.28.0/pyproject.toml +0 -31
- sqlutilpy-0.28.0/src/sqlutilpy/version.py +0 -1
- {sqlutilpy-0.28.0 → sqlutilpy-0.30.0}/setup.cfg +0 -0
- {sqlutilpy-0.28.0 → sqlutilpy-0.30.0}/src/sqlutilpy/__init__.py +0 -0
- {sqlutilpy-0.28.0 → sqlutilpy-0.30.0}/src/sqlutilpy.egg-info/dependency_links.txt +0 -0
- {sqlutilpy-0.28.0 → sqlutilpy-0.30.0}/src/sqlutilpy.egg-info/top_level.txt +0 -0
- {sqlutilpy-0.28.0 → sqlutilpy-0.30.0}/tests/killer.py +0 -0
- {sqlutilpy-0.28.0 → sqlutilpy-0.30.0}/tests/test_consistency.py +0 -0
- {sqlutilpy-0.28.0 → sqlutilpy-0.30.0}/tests/test_duckdb.py +0 -0
- {sqlutilpy-0.28.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)
|
|
@@ -397,13 +437,15 @@ def __process_duckdb_column(col, intNullVal, strNullVal):
|
|
|
397
437
|
if isinstance(col, np.ma.MaskedArray):
|
|
398
438
|
kind = col.dtype.kind
|
|
399
439
|
if kind in ('i', 'u') and intNullVal is not None:
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
except (TypeError, OverflowError):
|
|
440
|
+
info = np.iinfo(col.dtype)
|
|
441
|
+
if not (info.min <= intNullVal <= info.max):
|
|
403
442
|
# intNullVal does not fit into the column type (e.g. a
|
|
404
443
|
# negative value for an unsigned or a tinyint column),
|
|
405
|
-
# so the column is widened to int64 first
|
|
406
|
-
|
|
444
|
+
# so the column is widened to int64 first.
|
|
445
|
+
# (we check explicitly rather than catch an exception,
|
|
446
|
+
# because numpy<2 silently wraps the value instead)
|
|
447
|
+
col = col.astype(np.int64)
|
|
448
|
+
col = col.filled(intNullVal)
|
|
407
449
|
elif kind == 'f':
|
|
408
450
|
col = col.filled(np.nan)
|
|
409
451
|
elif kind == 'M':
|
|
@@ -424,88 +466,123 @@ def __process_duckdb_column(col, intNullVal, strNullVal):
|
|
|
424
466
|
|
|
425
467
|
def get(query,
|
|
426
468
|
params=None,
|
|
469
|
+
*,
|
|
427
470
|
db="wsdb",
|
|
428
471
|
driver="psycopg",
|
|
429
472
|
user=None,
|
|
430
473
|
password=None,
|
|
431
474
|
host=None,
|
|
432
|
-
preamb=None,
|
|
433
|
-
conn=None,
|
|
434
475
|
port=None,
|
|
435
|
-
|
|
476
|
+
conn=None,
|
|
477
|
+
preamb=None,
|
|
436
478
|
timeout=None,
|
|
479
|
+
strLength=STRLEN_DEFAULT,
|
|
437
480
|
batched=True,
|
|
438
481
|
asDict=False,
|
|
439
482
|
intNullVal=-9999,
|
|
440
483
|
strNullVal='None',
|
|
441
|
-
nthreads=1
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
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.
|
|
445
489
|
|
|
446
490
|
Parameters
|
|
447
491
|
----------
|
|
448
492
|
query : string
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
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
|
|
452
496
|
Query parameters
|
|
453
|
-
conn : object
|
|
454
|
-
The connection object to the DB (optional) to avoid reconnecting
|
|
455
|
-
asDict : boolean
|
|
456
|
-
Flag whether to retrieve the results as a dictionary with column
|
|
457
|
-
names as keys
|
|
458
|
-
strLength : integer
|
|
459
|
-
The minimum length allocated for string columns. If longer
|
|
460
|
-
strings are encountered, the allocated length is extended
|
|
461
|
-
to accommodate them
|
|
462
|
-
intNullVal : integer, optional
|
|
463
|
-
All the integer columns with nulls will have null replaced by
|
|
464
|
-
this value
|
|
465
|
-
strNullVal : string, optional
|
|
466
|
-
All the string columns with nulls will have null replaced by
|
|
467
|
-
this value
|
|
468
497
|
db : string
|
|
469
|
-
The name of the database
|
|
498
|
+
The name of the database (PostgreSQL) or the filename of the
|
|
499
|
+
database (SQLite, DuckDB)
|
|
470
500
|
driver : string, optional
|
|
471
|
-
The
|
|
501
|
+
The database driver: 'psycopg' (PostgreSQL, the default),
|
|
502
|
+
'sqlite3' or 'duckdb'
|
|
472
503
|
user : string, optional
|
|
473
|
-
User name for the DB connection
|
|
504
|
+
User name for the DB connection (PostgreSQL only)
|
|
474
505
|
password : string, optional
|
|
475
|
-
DB connection password
|
|
506
|
+
DB connection password (PostgreSQL only)
|
|
476
507
|
host : string, optional
|
|
477
|
-
Hostname of the database
|
|
508
|
+
Hostname of the database (PostgreSQL only)
|
|
478
509
|
port : integer, optional
|
|
479
|
-
Port of the database
|
|
480
|
-
|
|
481
|
-
|
|
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
|
|
482
524
|
batched : bool, optional
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
and on the fly
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
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
|
+
|
|
492
553
|
Returns
|
|
493
554
|
-------
|
|
494
|
-
ret :
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
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
|
|
499
561
|
|
|
500
562
|
Examples
|
|
501
563
|
--------
|
|
502
|
-
>>>
|
|
564
|
+
>>> ra, dec, d25 = sqlutilpy.get('select ra, dec, d25 from rc3',
|
|
565
|
+
... host='myhost', db='mydb')
|
|
566
|
+
|
|
567
|
+
With query parameters:
|
|
503
568
|
|
|
504
|
-
|
|
569
|
+
>>> ra, dec = sqlutilpy.get('select ra, dec from rc3 where name = %s',
|
|
570
|
+
... ('NGC 3166', ), host='myhost', db='mydb')
|
|
505
571
|
|
|
506
|
-
|
|
507
|
-
|
|
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
|
+
"""
|
|
508
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
|
|
509
586
|
driver = __check_driver(driver)
|
|
510
587
|
connSupplied = (conn is not None)
|
|
511
588
|
if not connSupplied:
|
|
@@ -517,13 +594,11 @@ def get(query,
|
|
|
517
594
|
port=port,
|
|
518
595
|
timeout=timeout)
|
|
519
596
|
try:
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
namedCursor = False
|
|
526
|
-
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)
|
|
527
602
|
|
|
528
603
|
if params is None:
|
|
529
604
|
res = cur.execute(query)
|
|
@@ -665,6 +740,7 @@ def get(query,
|
|
|
665
740
|
|
|
666
741
|
def execute(query,
|
|
667
742
|
params=None,
|
|
743
|
+
*,
|
|
668
744
|
db='wsdb',
|
|
669
745
|
driver="psycopg",
|
|
670
746
|
user=None,
|
|
@@ -676,35 +752,45 @@ def execute(query,
|
|
|
676
752
|
timeout=None,
|
|
677
753
|
noCommit=False):
|
|
678
754
|
"""
|
|
679
|
-
Execute
|
|
755
|
+
Execute an SQL statement without returning any results
|
|
680
756
|
|
|
681
757
|
Parameters
|
|
682
758
|
----------
|
|
683
|
-
query: string
|
|
684
|
-
The
|
|
685
|
-
|
|
686
|
-
|
|
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
|
|
687
764
|
db : string
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
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'
|
|
691
770
|
user : string, optional
|
|
692
|
-
|
|
771
|
+
User name for the DB connection (PostgreSQL only)
|
|
693
772
|
password : string, optional
|
|
694
|
-
DB connection password
|
|
773
|
+
DB connection password (PostgreSQL only)
|
|
695
774
|
host : string, optional
|
|
696
|
-
Hostname of the database
|
|
775
|
+
Hostname of the database (PostgreSQL only)
|
|
697
776
|
port : integer, optional
|
|
698
|
-
Port of the database
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
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
|
|
702
788
|
|
|
703
789
|
Examples
|
|
704
790
|
--------
|
|
705
|
-
>>>
|
|
706
|
-
>>>
|
|
707
|
-
|
|
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')
|
|
708
794
|
"""
|
|
709
795
|
driver = __check_driver(driver)
|
|
710
796
|
connSupplied = (conn is not None)
|
|
@@ -848,6 +934,7 @@ def failure_cleanup(conn, connSupplied):
|
|
|
848
934
|
def upload(tableName,
|
|
849
935
|
arrays,
|
|
850
936
|
names=None,
|
|
937
|
+
*,
|
|
851
938
|
db="wsdb",
|
|
852
939
|
driver="psycopg",
|
|
853
940
|
user=None,
|
|
@@ -863,42 +950,59 @@ def upload(tableName,
|
|
|
863
950
|
createTable=True,
|
|
864
951
|
delimiter='|'):
|
|
865
952
|
"""
|
|
866
|
-
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.
|
|
867
955
|
|
|
868
956
|
Parameters
|
|
869
957
|
----------
|
|
870
958
|
tableName : string
|
|
871
959
|
The name of the table where the data will be uploaded
|
|
872
|
-
|
|
873
|
-
|
|
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
|
-
|
|
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
|
|
902
1006
|
|
|
903
1007
|
Examples
|
|
904
1008
|
--------
|
|
@@ -906,7 +1010,7 @@ def upload(tableName,
|
|
|
906
1010
|
>>> y = x**.5
|
|
907
1011
|
>>> sqlutilpy.upload('mytable', (x, y), ('xcol', 'ycol'))
|
|
908
1012
|
|
|
909
|
-
>>> T = astropy.Table({'x':[1, 2, 3], 'y':['a', 'b', 'c'])
|
|
1013
|
+
>>> T = astropy.table.Table({'x': [1, 2, 3], 'y': ['a', 'b', 'c']})
|
|
910
1014
|
>>> sqlutilpy.upload('mytable', T)
|
|
911
1015
|
"""
|
|
912
1016
|
driver = __check_driver(driver)
|
|
@@ -998,6 +1102,7 @@ def local_join(query,
|
|
|
998
1102
|
tableName,
|
|
999
1103
|
arrays,
|
|
1000
1104
|
names,
|
|
1105
|
+
*,
|
|
1001
1106
|
db=None,
|
|
1002
1107
|
driver="psycopg",
|
|
1003
1108
|
user=None,
|
|
@@ -1008,31 +1113,71 @@ def local_join(query,
|
|
|
1008
1113
|
preamb=None,
|
|
1009
1114
|
timeout=None,
|
|
1010
1115
|
strLength=STRLEN_DEFAULT,
|
|
1116
|
+
asDict=False,
|
|
1011
1117
|
intNullVal=-9999,
|
|
1012
|
-
strNullVal='None'
|
|
1013
|
-
asDict=False):
|
|
1118
|
+
strNullVal='None'):
|
|
1014
1119
|
"""
|
|
1015
|
-
Join your local data in python with the data in the database
|
|
1016
|
-
|
|
1017
|
-
|
|
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.
|
|
1018
1126
|
|
|
1019
1127
|
Parameters
|
|
1020
1128
|
----------
|
|
1021
|
-
query :
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
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()
|
|
1025
1170
|
|
|
1026
1171
|
Examples
|
|
1027
1172
|
--------
|
|
1028
|
-
This
|
|
1029
|
-
|
|
1173
|
+
This extracts the rows of sometable matching the values of the
|
|
1174
|
+
array x:
|
|
1030
1175
|
|
|
1031
1176
|
>>> x = np.arange(10)
|
|
1032
1177
|
>>> y = x**.5
|
|
1033
1178
|
>>> sqlutilpy.local_join('''
|
|
1034
|
-
...
|
|
1035
|
-
...
|
|
1179
|
+
... select s.* from mytable as m left join sometable as s
|
|
1180
|
+
... on s.x = m.x order by m.x''',
|
|
1036
1181
|
... 'mytable', (x, y), ('x', 'y'))
|
|
1037
1182
|
"""
|
|
1038
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.28.0/MANIFEST.in
DELETED
sqlutilpy-0.28.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.28.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
|