sqlutilpy 0.29.0__tar.gz → 0.30.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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.29.0 → sqlutilpy-0.30.0}/LICENSE.txt +4 -4
  4. sqlutilpy-0.30.0/MANIFEST.in +6 -0
  5. {sqlutilpy-0.29.0/src/sqlutilpy.egg-info → sqlutilpy-0.30.0}/PKG-INFO +29 -12
  6. {sqlutilpy-0.29.0 → sqlutilpy-0.30.0}/README.md +18 -5
  7. sqlutilpy-0.30.0/pyproject.toml +35 -0
  8. {sqlutilpy-0.29.0 → sqlutilpy-0.30.0}/src/sqlutilpy/sqlutil.py +284 -141
  9. sqlutilpy-0.30.0/src/sqlutilpy/version.py +1 -0
  10. {sqlutilpy-0.29.0 → sqlutilpy-0.30.0/src/sqlutilpy.egg-info}/PKG-INFO +29 -12
  11. {sqlutilpy-0.29.0 → sqlutilpy-0.30.0}/src/sqlutilpy.egg-info/SOURCES.txt +2 -0
  12. {sqlutilpy-0.29.0 → sqlutilpy-0.30.0}/src/sqlutilpy.egg-info/requires.txt +1 -1
  13. {sqlutilpy-0.29.0 → sqlutilpy-0.30.0}/tests/test_pg.py +19 -0
  14. {sqlutilpy-0.29.0 → sqlutilpy-0.30.0}/tests/test_sqlite.py +32 -0
  15. sqlutilpy-0.29.0/MANIFEST.in +0 -9
  16. sqlutilpy-0.29.0/pyproject.toml +0 -31
  17. sqlutilpy-0.29.0/src/sqlutilpy/version.py +0 -1
  18. {sqlutilpy-0.29.0 → sqlutilpy-0.30.0}/setup.cfg +0 -0
  19. {sqlutilpy-0.29.0 → sqlutilpy-0.30.0}/src/sqlutilpy/__init__.py +0 -0
  20. {sqlutilpy-0.29.0 → sqlutilpy-0.30.0}/src/sqlutilpy.egg-info/dependency_links.txt +0 -0
  21. {sqlutilpy-0.29.0 → sqlutilpy-0.30.0}/src/sqlutilpy.egg-info/top_level.txt +0 -0
  22. {sqlutilpy-0.29.0 → sqlutilpy-0.30.0}/tests/killer.py +0 -0
  23. {sqlutilpy-0.29.0 → sqlutilpy-0.30.0}/tests/test_consistency.py +0 -0
  24. {sqlutilpy-0.29.0 → sqlutilpy-0.30.0}/tests/test_duckdb.py +0 -0
  25. {sqlutilpy-0.29.0 → sqlutilpy-0.30.0}/tests/test_fix_numpy.py +0 -0
@@ -0,0 +1,55 @@
1
+ Changelog
2
+
3
+ # 0.30.0
4
+ * All the options of get(), execute(), upload(), local_join(), getConnection() and getCursor() are now keyword-only and listed in the same order in every function (db, driver, user, password, host, port, conn, preamb, timeout, then the function specific options). Only the query/parameters and the table name/arrays/column names can be given positionally. This stops the argument order changes between releases (e.g. the port argument added in 0.28.0 before conn in execute() and upload()) from silently shifting positional arguments
5
+ * get() and getCursor() accept again the notNamed option removed in 0.27.0, as a deprecated alias of batched=not notNamed / named=not notNamed. It emits a DeprecationWarning and will be removed in a future release
6
+
7
+ # 0.29.0
8
+ * get() with duckdb: fix the null replacement in narrow and unsigned integer columns (tinyint, utinyint, uinteger, ubigint) with numpy<2, where intNullVal was silently wrapped (e.g. -9999 became -15 in a tinyint column) instead of the column being widened to int64
9
+ * This is the last release supporting numpy<2
10
+ * An unknown driver name now raises SqlUtilException from get(), execute(), upload(), local_join(), getConnection() and getCursor() before any connection is attempted (it was added in 0.28.0)
11
+
12
+ # 0.28.0
13
+ * upload(): strings containing the delimiter, backslashes, newlines, tabs or carriage returns are now escaped and survive the upload unchanged
14
+ * upload(): numpy bytes arrays (dtype S) are now decoded as utf-8 and uploaded as strings, rather than being stored as the b'...' python literals
15
+ * get(): intNullVal=None keeps the nulls in integer columns as None (the column is returned as an object array) instead of failing
16
+ * get(): statements that do not return a result set no longer fail with sqlite3/duckdb
17
+ * get(): use a unique server-side cursor name so that several batched queries can be run over the same connection
18
+ * get() with duckdb now uses the much faster fetchnumpy() interface. As a result the integer/float columns keep the width of the database type (e.g. int -> int32, float -> float32, rather than always int64/float64), dates, timestamps with time zone and intervals are returned as datetime64/timedelta64, decimals and hugeints as float64, blobs as object arrays of bytearrays, and list columns are now supported
19
+ * add the port argument to execute() and upload()
20
+ * upload(): analyze is now run inside the try block, so a failure there rolls back the transaction
21
+ * fix the strLength docstring (strings are not truncated, the allocated length is extended as needed)
22
+ * packaging: remove numpydoc and a duplicate numpy from the dependencies; the readthedocs build uses docs/requirements.txt and python 3.12
23
+
24
+ # 0.27.0
25
+ * Introduce the non-batched mode with batched=False that allows (at the cost of more memory use) using the parallel PostgreSQL plans, which can speed up queries by a factor of few
26
+ * remove the notNamed option of sqlutil.get
27
+ * refactor the threading for converting data, to use the threadpool
28
+ * I added more tests when strings are read, so hopefully the truncation into 20 characters should be more rare now.
29
+
30
+ # 0.26.0
31
+ * Preserve uppercase columns when ingestng data
32
+ * Remove obsolet numpy functionality
33
+
34
+ # 0.25.0
35
+ * Allow querying timestamp with timezone types
36
+ * Allow sqlutilpy.connect() to the duckdb db
37
+
38
+ # 0.24.0
39
+ * In the case sqlutilpy.get() returns no rows and asDict=True, the column names will still be there
40
+
41
+ # 0.23.0
42
+ * Switch to psycopg (i.e. third version of psycopg)
43
+ * Make the code compatible with numpy 2.0
44
+
45
+ # 0.22.0
46
+ * This version migrated to pyproject.toml
47
+
48
+ # 0.21.0
49
+ * Allow uploading int8 columns
50
+ * Allow uploading data where some columns have arrays
51
+
52
+ # 0.20.0
53
+
54
+ * Increase default string length in local_join
55
+ * add intNullVal option to local_join
@@ -0,0 +1,26 @@
1
+ cff-version: 1.2.0
2
+ message: "If you use this software, please cite it using the metadata from this file."
3
+ type: software
4
+ title: "sqlutilpy"
5
+ abstract: "Database query code returning numpy arrays. A thin layer over psycopg, sqlite3 and duckdb that runs SQL queries and returns the results as numpy arrays, and uploads numpy arrays into PostgreSQL tables."
6
+ authors:
7
+ - family-names: "Koposov"
8
+ given-names: "Sergey E."
9
+ email: "skoposov@ed.ac.uk"
10
+ orcid: "https://orcid.org/0000-0003-2644-135X"
11
+ affiliation: "Institute for Astronomy, University of Edinburgh"
12
+ license: "BSD-3-Clause"
13
+ repository-code: "https://github.com/segasai/sqlutilpy"
14
+ url: "https://sqlutilpy.readthedocs.io"
15
+ doi: "10.5281/zenodo.5160118"
16
+ identifiers:
17
+ - type: doi
18
+ value: "10.5281/zenodo.5160118"
19
+ description: "Zenodo concept DOI resolving to the latest release"
20
+ keywords:
21
+ - numpy
22
+ - postgresql
23
+ - sqlite
24
+ - duckdb
25
+ - sql
26
+ - astronomy
@@ -1,4 +1,4 @@
1
- Copyright (c) 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.29.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)
@@ -426,88 +466,123 @@ def __process_duckdb_column(col, intNullVal, strNullVal):
426
466
 
427
467
  def get(query,
428
468
  params=None,
469
+ *,
429
470
  db="wsdb",
430
471
  driver="psycopg",
431
472
  user=None,
432
473
  password=None,
433
474
  host=None,
434
- preamb=None,
435
- conn=None,
436
475
  port=None,
437
- strLength=STRLEN_DEFAULT,
476
+ conn=None,
477
+ preamb=None,
438
478
  timeout=None,
479
+ strLength=STRLEN_DEFAULT,
439
480
  batched=True,
440
481
  asDict=False,
441
482
  intNullVal=-9999,
442
483
  strNullVal='None',
443
- nthreads=1):
444
- '''
445
- Executes the sql query and returns the tuple or dictionary
446
- 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.
447
489
 
448
490
  Parameters
449
491
  ----------
450
492
  query : string
451
- Query you want to execute, can include question
452
- marks to refer to query parameters
453
- params : tuple
493
+ The query to execute. It can contain placeholders for the query
494
+ parameters: %s for PostgreSQL, ? for SQLite and DuckDB
495
+ params : tuple, optional
454
496
  Query parameters
455
- conn : object
456
- The connection object to the DB (optional) to avoid reconnecting
457
- asDict : boolean
458
- Flag whether to retrieve the results as a dictionary with column
459
- names as keys
460
- strLength : integer
461
- The minimum length allocated for string columns. If longer
462
- strings are encountered, the allocated length is extended
463
- to accommodate them
464
- intNullVal : integer, optional
465
- All the integer columns with nulls will have null replaced by
466
- this value
467
- strNullVal : string, optional
468
- All the string columns with nulls will have null replaced by
469
- this value
470
497
  db : string
471
- The name of the database
498
+ The name of the database (PostgreSQL) or the filename of the
499
+ database (SQLite, DuckDB)
472
500
  driver : string, optional
473
- The sql driver to be used (psycopg or sqlite3)
501
+ The database driver: 'psycopg' (PostgreSQL, the default),
502
+ 'sqlite3' or 'duckdb'
474
503
  user : string, optional
475
- User name for the DB connection
504
+ User name for the DB connection (PostgreSQL only)
476
505
  password : string, optional
477
- DB connection password
506
+ DB connection password (PostgreSQL only)
478
507
  host : string, optional
479
- Hostname of the database
508
+ Hostname of the database (PostgreSQL only)
480
509
  port : integer, optional
481
- Port of the database
482
- preamb : string
483
- 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
484
524
  batched : bool, optional
485
- Whether to use named or not-named cursor with PostgreSQL
486
- The default setting of True leads to retrieval of results in batches
487
- and on the fly conversion of
488
- retrieved results into numpy, thus it will not use more memory than
489
- needed to store the results of the query. The
490
- The batched=False will use at least twice the amount of memory
491
- needed to store the results, but with the benefit of faster query
492
- execution, because these queries use PostgreSQL parallelism and thus
493
- 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
+
494
553
  Returns
495
554
  -------
496
- ret : Tuple or dictionary
497
- By default you get a tuple with numpy arrays for each column
498
- in your query.
499
- If you specified asDict keyword then you get an ordered dictionary with
500
- 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
501
561
 
502
562
  Examples
503
563
  --------
504
- >>> 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:
505
568
 
506
- 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')
507
571
 
508
- >>> a, b = sqlutilpy.get('select ra,dec from rc3 where name=?', "NGC 3166")
509
- '''
572
+ From a SQLite file:
573
+
574
+ >>> a, b = sqlutilpy.get('select a, b from tmp where a > ?', (3, ),
575
+ ... db='mydata.db', driver='sqlite3')
576
+ """
510
577
 
578
+ if notNamed is not None:
579
+ # the pre-0.27 spelling: notNamed=True asked for a client-side
580
+ # cursor, which is what batched=False does now
581
+ warnings.warn(
582
+ 'notNamed is deprecated; use batched=not notNamed instead',
583
+ DeprecationWarning,
584
+ stacklevel=2)
585
+ batched = not notNamed
511
586
  driver = __check_driver(driver)
512
587
  connSupplied = (conn is not None)
513
588
  if not connSupplied:
@@ -519,13 +594,11 @@ def get(query,
519
594
  port=port,
520
595
  timeout=timeout)
521
596
  try:
522
- if batched:
523
- # This is related to postgresql server cursors vs
524
- # application cursors
525
- namedCursor = True
526
- else:
527
- namedCursor = False
528
- cur = getCursor(conn, driver=driver, preamb=preamb, named=namedCursor)
597
+ # batched=True means a server-side (named) PostgreSQL cursor,
598
+ # read with fetchmany() in chunks of config.arraysize rows;
599
+ # batched=False means a client-side cursor read with one
600
+ # fetchall(). The cursor type is irrelevant for the other drivers
601
+ cur = getCursor(conn, driver=driver, preamb=preamb, named=batched)
529
602
 
530
603
  if params is None:
531
604
  res = cur.execute(query)
@@ -667,6 +740,7 @@ def get(query,
667
740
 
668
741
  def execute(query,
669
742
  params=None,
743
+ *,
670
744
  db='wsdb',
671
745
  driver="psycopg",
672
746
  user=None,
@@ -678,35 +752,45 @@ def execute(query,
678
752
  timeout=None,
679
753
  noCommit=False):
680
754
  """
681
- Execute a given SQL command without returning the results
755
+ Execute an SQL statement without returning any results
682
756
 
683
757
  Parameters
684
758
  ----------
685
- query: string
686
- The query or command you are executing
687
- params: tuple, optional
688
- 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
689
764
  db : string
690
- Database name
691
- driver : string
692
- 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'
693
770
  user : string, optional
694
- user name for the DB connection
771
+ User name for the DB connection (PostgreSQL only)
695
772
  password : string, optional
696
- DB connection password
773
+ DB connection password (PostgreSQL only)
697
774
  host : string, optional
698
- Hostname of the database
775
+ Hostname of the database (PostgreSQL only)
699
776
  port : integer, optional
700
- Port of the database
701
- noCommit: bool
702
- By default execute() will commit your command.
703
- 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
704
788
 
705
789
  Examples
706
790
  --------
707
- >>> sqlutil.execute('drop table mytab', conn=conn)
708
- >>> sqlutil.execute('create table mytab (a int)', db='mydb')
709
-
791
+ >>> sqlutilpy.execute('drop table mytab', conn=conn)
792
+ >>> sqlutilpy.execute('create table mytab (a int)', db='mydb')
793
+ >>> sqlutilpy.execute('insert into mytab values (%s)', (1, ), db='mydb')
710
794
  """
711
795
  driver = __check_driver(driver)
712
796
  connSupplied = (conn is not None)
@@ -850,6 +934,7 @@ def failure_cleanup(conn, connSupplied):
850
934
  def upload(tableName,
851
935
  arrays,
852
936
  names=None,
937
+ *,
853
938
  db="wsdb",
854
939
  driver="psycopg",
855
940
  user=None,
@@ -865,42 +950,59 @@ def upload(tableName,
865
950
  createTable=True,
866
951
  delimiter='|'):
867
952
  """
868
- Upload the data stored in 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.
869
955
 
870
956
  Parameters
871
957
  ----------
872
958
  tableName : string
873
959
  The name of the table where the data will be uploaded
874
- arrays_or_table : tuple
875
- Tuple of arrays that will be columns of the new table
876
- If names are not specified, I this parameter can be pandas or
877
- astropy table
878
- names : tuple
879
- db: string
880
- Databas name
881
- driver: string
882
- Python database driver "psycopg",
883
- user: string,
884
- password: string
885
- host: string
886
- port: integer
887
- Port of the database
888
- conn: object
889
- SQL connection
890
- preamb: string
891
- The string/query to be executed before your command
892
- noCommit: bool
893
- If true, the commit is not executed and the table will go away
894
- after the disconnect
895
- temp: bool
896
- If true a temporary table will be created
897
- analyze: bool
898
- if True, the table will be analyzed after the upload
899
- createTable: bool
900
- if True the table will be created before uploading (default)
901
- delimiter: string
902
- the string used for delimiting the input data when ingesting into
903
- 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
904
1006
 
905
1007
  Examples
906
1008
  --------
@@ -908,7 +1010,7 @@ def upload(tableName,
908
1010
  >>> y = x**.5
909
1011
  >>> sqlutilpy.upload('mytable', (x, y), ('xcol', 'ycol'))
910
1012
 
911
- >>> T = astropy.Table({'x':[1, 2, 3], 'y':['a', 'b', 'c'])
1013
+ >>> T = astropy.table.Table({'x': [1, 2, 3], 'y': ['a', 'b', 'c']})
912
1014
  >>> sqlutilpy.upload('mytable', T)
913
1015
  """
914
1016
  driver = __check_driver(driver)
@@ -1000,6 +1102,7 @@ def local_join(query,
1000
1102
  tableName,
1001
1103
  arrays,
1002
1104
  names,
1105
+ *,
1003
1106
  db=None,
1004
1107
  driver="psycopg",
1005
1108
  user=None,
@@ -1010,31 +1113,71 @@ def local_join(query,
1010
1113
  preamb=None,
1011
1114
  timeout=None,
1012
1115
  strLength=STRLEN_DEFAULT,
1116
+ asDict=False,
1013
1117
  intNullVal=-9999,
1014
- strNullVal='None',
1015
- asDict=False):
1118
+ strNullVal='None'):
1016
1119
  """
1017
- Join your local data in python with the data in the database
1018
- This command first uploads the data in the DB creating a temporary table
1019
- 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.
1020
1126
 
1021
1127
  Parameters
1022
1128
  ----------
1023
- query : String with the query to be executed
1024
- tableName : The name of the temporary table that is going to be created
1025
- arrays : The tuple with list of arrays with the data to be loaded in the DB
1026
- 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()
1027
1170
 
1028
1171
  Examples
1029
1172
  --------
1030
- This will extract the rows from the table sometable matching
1031
- to the provided array x
1173
+ This extracts the rows of sometable matching the values of the
1174
+ array x:
1032
1175
 
1033
1176
  >>> x = np.arange(10)
1034
1177
  >>> y = x**.5
1035
1178
  >>> sqlutilpy.local_join('''
1036
- ... SELECT s.* FROM mytable AS m LEFT JOIN sometable AS s
1037
- ... 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''',
1038
1181
  ... 'mytable', (x, y), ('x', 'y'))
1039
1182
  """
1040
1183
 
@@ -0,0 +1 @@
1
+ __version__ = '0.30.0'
@@ -1,25 +1,29 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlutilpy
3
- Version: 0.29.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.29.0'
File without changes
File without changes