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.
Files changed (25) hide show
  1. sqlutilpy-0.30.0/CHANGELOG.md +55 -0
  2. sqlutilpy-0.30.0/CITATION.cff +26 -0
  3. {sqlutilpy-0.28.0 → sqlutilpy-0.30.0}/LICENSE.txt +4 -4
  4. sqlutilpy-0.30.0/MANIFEST.in +6 -0
  5. {sqlutilpy-0.28.0/src/sqlutilpy.egg-info → sqlutilpy-0.30.0}/PKG-INFO +29 -12
  6. {sqlutilpy-0.28.0 → sqlutilpy-0.30.0}/README.md +18 -5
  7. sqlutilpy-0.30.0/pyproject.toml +35 -0
  8. {sqlutilpy-0.28.0 → sqlutilpy-0.30.0}/src/sqlutilpy/sqlutil.py +291 -146
  9. sqlutilpy-0.30.0/src/sqlutilpy/version.py +1 -0
  10. {sqlutilpy-0.28.0 → sqlutilpy-0.30.0/src/sqlutilpy.egg-info}/PKG-INFO +29 -12
  11. {sqlutilpy-0.28.0 → sqlutilpy-0.30.0}/src/sqlutilpy.egg-info/SOURCES.txt +2 -0
  12. {sqlutilpy-0.28.0 → sqlutilpy-0.30.0}/src/sqlutilpy.egg-info/requires.txt +1 -1
  13. {sqlutilpy-0.28.0 → sqlutilpy-0.30.0}/tests/test_pg.py +19 -0
  14. {sqlutilpy-0.28.0 → sqlutilpy-0.30.0}/tests/test_sqlite.py +32 -0
  15. sqlutilpy-0.28.0/MANIFEST.in +0 -9
  16. sqlutilpy-0.28.0/pyproject.toml +0 -31
  17. sqlutilpy-0.28.0/src/sqlutilpy/version.py +0 -1
  18. {sqlutilpy-0.28.0 → sqlutilpy-0.30.0}/setup.cfg +0 -0
  19. {sqlutilpy-0.28.0 → sqlutilpy-0.30.0}/src/sqlutilpy/__init__.py +0 -0
  20. {sqlutilpy-0.28.0 → sqlutilpy-0.30.0}/src/sqlutilpy.egg-info/dependency_links.txt +0 -0
  21. {sqlutilpy-0.28.0 → sqlutilpy-0.30.0}/src/sqlutilpy.egg-info/top_level.txt +0 -0
  22. {sqlutilpy-0.28.0 → sqlutilpy-0.30.0}/tests/killer.py +0 -0
  23. {sqlutilpy-0.28.0 → sqlutilpy-0.30.0}/tests/test_consistency.py +0 -0
  24. {sqlutilpy-0.28.0 → sqlutilpy-0.30.0}/tests/test_duckdb.py +0 -0
  25. {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) 2018, Sergey Koposov
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 the <organization> nor the
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 <COPYRIGHT HOLDER> BE LIABLE FOR ANY
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.
@@ -0,0 +1,6 @@
1
+ include README.md
2
+ include LICENSE.txt
3
+ include CHANGELOG.md
4
+ include CITATION.cff
5
+ include MANIFEST.in
6
+ recursive-include tests *.py
@@ -1,25 +1,29 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlutilpy
3
- Version: 0.28.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
- Keywords: numpy postgresql query sql sqlite array
9
- Classifier: Development Status :: 3 - Alpha
10
- Classifier: License :: OSI Approved :: BSD License
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.8
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
  [![Build Status](https://github.com/segasai/sqlutilpy/workflows/Testing/badge.svg)](https://github.com/segasai/sqlutilpy/actions)
20
24
  [![Documentation Status](https://readthedocs.org/projects/sqlutilpy/badge/?version=latest)](http://sqlutilpy.readthedocs.io/en/latest/?badge=latest)
21
25
  [![Coverage Status](https://coveralls.io/repos/github/segasai/sqlutilpy/badge.svg?branch=master)](https://coveralls.io/github/segasai/sqlutilpy?branch=master)
22
- [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.6867957.svg)](https://doi.org/10.5281/zenodo.6867957)
26
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.5160118.svg)](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 = squtilpy.get('select ra,dec from mytable',
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
- ## Uploading your arrays as column in a table
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 `sqlutil.get()` and similar commands using `conn=` keyword:
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
  [![Build Status](https://github.com/segasai/sqlutilpy/workflows/Testing/badge.svg)](https://github.com/segasai/sqlutilpy/actions)
2
2
  [![Documentation Status](https://readthedocs.org/projects/sqlutilpy/badge/?version=latest)](http://sqlutilpy.readthedocs.io/en/latest/?badge=latest)
3
3
  [![Coverage Status](https://coveralls.io/repos/github/segasai/sqlutilpy/badge.svg?branch=master)](https://coveralls.io/github/segasai/sqlutilpy?branch=master)
4
- [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.6867957.svg)](https://doi.org/10.5281/zenodo.6867957)
4
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.5160118.svg)](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 = squtilpy.get('select ra,dec from mytable',
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
- ## Uploading your arrays as column in a table
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 `sqlutil.get()` and similar commands using `conn=` keyword:
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
- """Sqlutilpy module to access SQL databases
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
- 'Unknown driver %r; supported drivers are %s' %
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(db=None,
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 (in case of PostgreSQL) or filename in
70
- case of sqlite db
71
- driver : string
72
- The db driver (either 'psycopg' or 'sqlite3')
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
- Host-name
79
- port : integer
80
- Connection port (by default 5432 for PostgreSQL)
81
- timeout : integer
82
- Connection timeout for sqlite
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
- Database Connection
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
- try:
401
- col = col.filled(intNullVal)
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
- col = col.astype(np.int64).filled(intNullVal)
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
- strLength=STRLEN_DEFAULT,
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
- Executes the sql query and returns the tuple or dictionary
444
- with the numpy arrays.
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
- Query you want to execute, can include question
450
- marks to refer to query parameters
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 sql driver to be used (psycopg or sqlite3)
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
- preamb : string
481
- SQL code to be executed before the query
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
- Whether to use named or not-named cursor with PostgreSQL
484
- The default setting of True leads to retrieval of results in batches
485
- and on the fly conversion of
486
- retrieved results into numpy, thus it will not use more memory than
487
- needed to store the results of the query. The
488
- The batched=False will use at least twice the amount of memory
489
- needed to store the results, but with the benefit of faster query
490
- execution, because these queries use PostgreSQL parallelism and thus
491
- can be ~ a factor of few faster
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 : Tuple or dictionary
495
- By default you get a tuple with numpy arrays for each column
496
- in your query.
497
- If you specified asDict keyword then you get an ordered dictionary with
498
- your columns.
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
- >>> a, b, c = sqlutilpy.get('select ra,dec,d25 from rc3')
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
- You can also use the parameters in your query:
569
+ >>> ra, dec = sqlutilpy.get('select ra, dec from rc3 where name = %s',
570
+ ... ('NGC 3166', ), host='myhost', db='mydb')
505
571
 
506
- >>> a, b = sqlutilpy.get('select ra,dec from rc3 where name=?', "NGC 3166")
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
- if batched:
521
- # This is related to postgresql server cursors vs
522
- # application cursors
523
- namedCursor = True
524
- else:
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 a given SQL command without returning the results
755
+ Execute an SQL statement without returning any results
680
756
 
681
757
  Parameters
682
758
  ----------
683
- query: string
684
- The query or command you are executing
685
- params: tuple, optional
686
- Optional parameters of your query
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
- Database name
689
- driver : string
690
- Driver for the DB connection ('psycopg' or 'sqlite3')
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
- user name for the DB connection
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
- noCommit: bool
700
- By default execute() will commit your command.
701
- If you say noCommit, the commit won't be issued.
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
- >>> sqlutil.execute('drop table mytab', conn=conn)
706
- >>> sqlutil.execute('create table mytab (a int)', db='mydb')
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 the tuple of arrays in the DB
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
- arrays_or_table : tuple
873
- Tuple of arrays that will be columns of the new table
874
- If names are not specified, I this parameter can be pandas or
875
- astropy table
876
- names : tuple
877
- db: string
878
- Databas name
879
- driver: string
880
- Python database driver "psycopg",
881
- user: string,
882
- password: string
883
- host: string
884
- port: integer
885
- Port of the database
886
- conn: object
887
- SQL connection
888
- preamb: string
889
- The string/query to be executed before your command
890
- noCommit: bool
891
- If true, the commit is not executed and the table will go away
892
- after the disconnect
893
- temp: bool
894
- If true a temporary table will be created
895
- analyze: bool
896
- if True, the table will be analyzed after the upload
897
- createTable: bool
898
- if True the table will be created before uploading (default)
899
- delimiter: string
900
- the string used for delimiting the input data when ingesting into
901
- the db (default is ``|``)
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
- This command first uploads the data in the DB creating a temporary table
1017
- and then runs a user specified query that can your local data.
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 : String with the query to be executed
1022
- tableName : The name of the temporary table that is going to be created
1023
- arrays : The tuple with list of arrays with the data to be loaded in the DB
1024
- names : The tuple with the column names for the user table
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 will extract the rows from the table sometable matching
1029
- to the provided array x
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
- ... SELECT s.* FROM mytable AS m LEFT JOIN sometable AS s
1035
- ... ON s.x = m.x ORDER BY m.xcol''',
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.28.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
- Keywords: numpy postgresql query sql sqlite array
9
- Classifier: Development Status :: 3 - Alpha
10
- Classifier: License :: OSI Approved :: BSD License
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.8
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
  [![Build Status](https://github.com/segasai/sqlutilpy/workflows/Testing/badge.svg)](https://github.com/segasai/sqlutilpy/actions)
20
24
  [![Documentation Status](https://readthedocs.org/projects/sqlutilpy/badge/?version=latest)](http://sqlutilpy.readthedocs.io/en/latest/?badge=latest)
21
25
  [![Coverage Status](https://coveralls.io/repos/github/segasai/sqlutilpy/badge.svg?branch=master)](https://coveralls.io/github/segasai/sqlutilpy?branch=master)
22
- [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.6867957.svg)](https://doi.org/10.5281/zenodo.6867957)
26
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.5160118.svg)](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 = squtilpy.get('select ra,dec from mytable',
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
- ## Uploading your arrays as column in a table
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 `sqlutil.get()` and similar commands using `conn=` keyword:
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,3 +1,5 @@
1
+ CHANGELOG.md
2
+ CITATION.cff
1
3
  LICENSE.txt
2
4
  MANIFEST.in
3
5
  README.md
@@ -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()
@@ -1,9 +0,0 @@
1
- include README.md
2
- include requirements.txt
3
- include setup.py
4
- include setup.cfg
5
- include LICENSE
6
- include MANIFEST.in
7
- recursive-include tests *py
8
- recursive-include py *py
9
-
@@ -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