sqlutilpy 0.23.0__tar.gz → 0.25.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {sqlutilpy-0.23.0/src/sqlutilpy.egg-info → sqlutilpy-0.25.0}/PKG-INFO +8 -6
- {sqlutilpy-0.23.0 → sqlutilpy-0.25.0}/README.md +5 -4
- {sqlutilpy-0.23.0 → sqlutilpy-0.25.0}/src/sqlutilpy/sqlutil.py +26 -16
- sqlutilpy-0.25.0/src/sqlutilpy/version.py +1 -0
- {sqlutilpy-0.23.0 → sqlutilpy-0.25.0/src/sqlutilpy.egg-info}/PKG-INFO +8 -6
- {sqlutilpy-0.23.0 → sqlutilpy-0.25.0}/src/sqlutilpy.egg-info/SOURCES.txt +1 -0
- sqlutilpy-0.25.0/tests/test_duckdb.py +32 -0
- {sqlutilpy-0.23.0 → sqlutilpy-0.25.0}/tests/test_pg.py +6 -0
- sqlutilpy-0.23.0/src/sqlutilpy/version.py +0 -1
- {sqlutilpy-0.23.0 → sqlutilpy-0.25.0}/LICENSE.txt +0 -0
- {sqlutilpy-0.23.0 → sqlutilpy-0.25.0}/MANIFEST.in +0 -0
- {sqlutilpy-0.23.0 → sqlutilpy-0.25.0}/pyproject.toml +0 -0
- {sqlutilpy-0.23.0 → sqlutilpy-0.25.0}/setup.cfg +0 -0
- {sqlutilpy-0.23.0 → sqlutilpy-0.25.0}/src/sqlutilpy/__init__.py +0 -0
- {sqlutilpy-0.23.0 → sqlutilpy-0.25.0}/src/sqlutilpy.egg-info/dependency_links.txt +0 -0
- {sqlutilpy-0.23.0 → sqlutilpy-0.25.0}/src/sqlutilpy.egg-info/requires.txt +0 -0
- {sqlutilpy-0.23.0 → sqlutilpy-0.25.0}/src/sqlutilpy.egg-info/top_level.txt +0 -0
- {sqlutilpy-0.23.0 → sqlutilpy-0.25.0}/tests/killer.py +0 -0
- {sqlutilpy-0.23.0 → sqlutilpy-0.25.0}/tests/test_sqlite.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: sqlutilpy
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.25.0
|
|
4
4
|
Summary: Database query code returning numpy arrays
|
|
5
5
|
Author-email: "Sergey E. Koposov" <skoposov@ed.ac.uk>
|
|
6
6
|
License: BSD
|
|
@@ -16,6 +16,7 @@ Requires-Dist: numpy
|
|
|
16
16
|
Requires-Dist: psycopg[binary]
|
|
17
17
|
Requires-Dist: numpydoc
|
|
18
18
|
Requires-Dist: numpy
|
|
19
|
+
Dynamic: license-file
|
|
19
20
|
|
|
20
21
|
[](https://github.com/segasai/sqlutilpy/actions)
|
|
21
22
|
[](http://sqlutilpy.readthedocs.io/en/latest/?badge=latest)
|
|
@@ -25,8 +26,8 @@ Requires-Dist: numpy
|
|
|
25
26
|
# sqlutilpy
|
|
26
27
|
Python module to query SQL databases and return numpy arrays, upload
|
|
27
28
|
tables and run join queries involving local arrays and the tables in the DB.
|
|
28
|
-
This module is optimized to
|
|
29
|
-
The module
|
|
29
|
+
This module is optimized to deal efficiently with query results with millions of rows.
|
|
30
|
+
The module works with PostgreSQL, SQLite and DuckDB databases.
|
|
30
31
|
|
|
31
32
|
The full documentation is available [here](http://sqlutilpy.readthedocs.io/en/latest/)
|
|
32
33
|
|
|
@@ -40,8 +41,9 @@ pip install sqlutilpy
|
|
|
40
41
|
```
|
|
41
42
|
## Authentication
|
|
42
43
|
|
|
43
|
-
Throughout this readme, I will assume that
|
|
44
|
-
|
|
44
|
+
Throughout this readme, I will assume that if you are using PostgreSQL, then
|
|
45
|
+
the .pgpass file ( https://www.postgresql.org/docs/11/libpq-pgpass.html )
|
|
46
|
+
has been created with your login/password details for Postgresql. If that is not the case, many of the
|
|
45
47
|
commands given below will also need user='....' and password='...' options
|
|
46
48
|
|
|
47
49
|
## Connection information
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
# sqlutilpy
|
|
7
7
|
Python module to query SQL databases and return numpy arrays, upload
|
|
8
8
|
tables and run join queries involving local arrays and the tables in the DB.
|
|
9
|
-
This module is optimized to
|
|
10
|
-
The module
|
|
9
|
+
This module is optimized to deal efficiently with query results with millions of rows.
|
|
10
|
+
The module works with PostgreSQL, SQLite and DuckDB databases.
|
|
11
11
|
|
|
12
12
|
The full documentation is available [here](http://sqlutilpy.readthedocs.io/en/latest/)
|
|
13
13
|
|
|
@@ -21,8 +21,9 @@ pip install sqlutilpy
|
|
|
21
21
|
```
|
|
22
22
|
## Authentication
|
|
23
23
|
|
|
24
|
-
Throughout this readme, I will assume that
|
|
25
|
-
|
|
24
|
+
Throughout this readme, I will assume that if you are using PostgreSQL, then
|
|
25
|
+
the .pgpass file ( https://www.postgresql.org/docs/11/libpq-pgpass.html )
|
|
26
|
+
has been created with your login/password details for Postgresql. If that is not the case, many of the
|
|
26
27
|
commands given below will also need user='....' and password='...' options
|
|
27
28
|
|
|
28
29
|
## Connection information
|
|
@@ -92,6 +92,9 @@ def getConnection(db=None,
|
|
|
92
92
|
if timeout is None:
|
|
93
93
|
timeout = 5
|
|
94
94
|
conn = sqlite3.connect(db, timeout=timeout)
|
|
95
|
+
elif driver == 'duckdb':
|
|
96
|
+
import duckdb
|
|
97
|
+
conn = duckdb.connect(db)
|
|
95
98
|
else:
|
|
96
99
|
raise Exception("Unknown driver")
|
|
97
100
|
return conn
|
|
@@ -137,12 +140,12 @@ def __fromrecords(recList, dtype=None, intNullVal=None):
|
|
|
137
140
|
"""
|
|
138
141
|
|
|
139
142
|
shape = None
|
|
140
|
-
descr = sb.dtype((np.
|
|
143
|
+
descr = sb.dtype((np.record, dtype))
|
|
141
144
|
try:
|
|
142
145
|
retval = sb.array(recList, dtype=descr)
|
|
143
146
|
except TypeError: # list of lists instead of list of tuples
|
|
144
147
|
shape = (len(recList), )
|
|
145
|
-
_array = np.
|
|
148
|
+
_array = np.recarray(shape, descr)
|
|
146
149
|
try:
|
|
147
150
|
for k in range(_array.size):
|
|
148
151
|
_array[k] = tuple(recList[k])
|
|
@@ -170,7 +173,7 @@ def __fromrecords(recList, dtype=None, intNullVal=None):
|
|
|
170
173
|
if shape is not None and retval.shape != shape:
|
|
171
174
|
retval.shape = shape
|
|
172
175
|
|
|
173
|
-
res = retval.view(numpy.
|
|
176
|
+
res = retval.view(numpy.recarray)
|
|
174
177
|
|
|
175
178
|
return res
|
|
176
179
|
|
|
@@ -210,6 +213,7 @@ def __getDType(row, typeCodes, strLength):
|
|
|
210
213
|
1022: 'f8',
|
|
211
214
|
1700: 'f8', # numeric
|
|
212
215
|
1114: '<M8[us]', # timestamp
|
|
216
|
+
1184: '<M8[us]', # timestamp with timezone
|
|
213
217
|
1082: '<M8[us]', # date
|
|
214
218
|
25: '|U%d',
|
|
215
219
|
1042: '|U%d', # character()
|
|
@@ -339,31 +343,38 @@ def get(query,
|
|
|
339
343
|
proc = None
|
|
340
344
|
colNames = []
|
|
341
345
|
if driver == 'psycopg2':
|
|
342
|
-
warnings.warn(
|
|
343
|
-
|
|
344
|
-
)
|
|
346
|
+
warnings.warn('psycopg2 driver is not supported anymore. '
|
|
347
|
+
'We using psycopg instead')
|
|
345
348
|
driver = 'psycopg'
|
|
346
349
|
if driver == 'psycopg':
|
|
347
350
|
try:
|
|
348
|
-
while
|
|
351
|
+
while True:
|
|
349
352
|
# Iterating over the cursor, retrieving batches of results
|
|
350
353
|
# and then sending them for conversion
|
|
351
354
|
tups = cur.fetchmany()
|
|
352
355
|
desc = cur.description
|
|
353
356
|
|
|
357
|
+
# If the is just the start we need to launch the
|
|
358
|
+
# thread doing the conversion
|
|
359
|
+
no_results = tups == []
|
|
360
|
+
if nrec == 0:
|
|
361
|
+
typeCodes = [_tmp.type_code for _tmp in desc]
|
|
362
|
+
colNames = [_tmp.name for _tmp in cur.description]
|
|
363
|
+
|
|
354
364
|
# No more data
|
|
355
|
-
if
|
|
365
|
+
if no_results:
|
|
366
|
+
dtype = __getDType([None] * len(typeCodes), typeCodes,
|
|
367
|
+
strLength)
|
|
356
368
|
break
|
|
357
369
|
|
|
370
|
+
dtype = __getDType(tups[0], typeCodes, strLength)
|
|
371
|
+
|
|
358
372
|
# Send the new batch for conversion
|
|
359
373
|
qIn.put(tups)
|
|
360
374
|
|
|
361
375
|
# If the is just the start we need to launch the
|
|
362
376
|
# thread doing the conversion
|
|
363
377
|
if nrec == 0:
|
|
364
|
-
typeCodes = [_tmp.type_code for _tmp in desc]
|
|
365
|
-
colNames = [_tmp.name for _tmp in cur.description]
|
|
366
|
-
dtype = __getDType(tups[0], typeCodes, strLength)
|
|
367
378
|
proc = threading.Thread(target=__converter,
|
|
368
379
|
args=(qIn, qOut, endEvent,
|
|
369
380
|
dtype, intNullVal))
|
|
@@ -416,14 +427,14 @@ def get(query,
|
|
|
416
427
|
tups = cur.fetchall()
|
|
417
428
|
colNames = [_tmp[0] for _tmp in cur.description]
|
|
418
429
|
if len(tups) > 0:
|
|
419
|
-
res = numpy.
|
|
430
|
+
res = numpy.rec.array(tups)
|
|
420
431
|
else:
|
|
421
432
|
return [[]] * len(cur.description)
|
|
422
433
|
elif driver == 'duckdb':
|
|
423
434
|
tups = cur.fetchall()
|
|
424
435
|
colNames = [_tmp[0] for _tmp in cur.description]
|
|
425
436
|
if len(tups) > 0:
|
|
426
|
-
res = numpy.
|
|
437
|
+
res = numpy.rec.array(tups)
|
|
427
438
|
else:
|
|
428
439
|
return [[]] * len(cur.description)
|
|
429
440
|
|
|
@@ -731,9 +742,8 @@ table/pandas/dictionary or provide a separate list of arrays and their names')
|
|
|
731
742
|
names = ','.join(names)
|
|
732
743
|
for i in range(0, N, nsplit):
|
|
733
744
|
try:
|
|
734
|
-
with cur.copy(
|
|
735
|
-
|
|
736
|
-
) as copy:
|
|
745
|
+
with cur.copy(f'''copy {tableName}({names}) from stdin
|
|
746
|
+
with delimiter '{delimiter}' ''') as copy:
|
|
737
747
|
__print_arrays([_[i:i + nsplit] for _ in arrays],
|
|
738
748
|
copy,
|
|
739
749
|
delimiter=delimiter)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '0.25.0'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: sqlutilpy
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.25.0
|
|
4
4
|
Summary: Database query code returning numpy arrays
|
|
5
5
|
Author-email: "Sergey E. Koposov" <skoposov@ed.ac.uk>
|
|
6
6
|
License: BSD
|
|
@@ -16,6 +16,7 @@ Requires-Dist: numpy
|
|
|
16
16
|
Requires-Dist: psycopg[binary]
|
|
17
17
|
Requires-Dist: numpydoc
|
|
18
18
|
Requires-Dist: numpy
|
|
19
|
+
Dynamic: license-file
|
|
19
20
|
|
|
20
21
|
[](https://github.com/segasai/sqlutilpy/actions)
|
|
21
22
|
[](http://sqlutilpy.readthedocs.io/en/latest/?badge=latest)
|
|
@@ -25,8 +26,8 @@ Requires-Dist: numpy
|
|
|
25
26
|
# sqlutilpy
|
|
26
27
|
Python module to query SQL databases and return numpy arrays, upload
|
|
27
28
|
tables and run join queries involving local arrays and the tables in the DB.
|
|
28
|
-
This module is optimized to
|
|
29
|
-
The module
|
|
29
|
+
This module is optimized to deal efficiently with query results with millions of rows.
|
|
30
|
+
The module works with PostgreSQL, SQLite and DuckDB databases.
|
|
30
31
|
|
|
31
32
|
The full documentation is available [here](http://sqlutilpy.readthedocs.io/en/latest/)
|
|
32
33
|
|
|
@@ -40,8 +41,9 @@ pip install sqlutilpy
|
|
|
40
41
|
```
|
|
41
42
|
## Authentication
|
|
42
43
|
|
|
43
|
-
Throughout this readme, I will assume that
|
|
44
|
-
|
|
44
|
+
Throughout this readme, I will assume that if you are using PostgreSQL, then
|
|
45
|
+
the .pgpass file ( https://www.postgresql.org/docs/11/libpq-pgpass.html )
|
|
46
|
+
has been created with your login/password details for Postgresql. If that is not the case, many of the
|
|
45
47
|
commands given below will also need user='....' and password='...' options
|
|
46
48
|
|
|
47
49
|
## Connection information
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
import os
|
|
3
|
+
import sqlutilpy as sqlutil
|
|
4
|
+
import duckdb
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@pytest.fixture()
|
|
8
|
+
def setup():
|
|
9
|
+
fname = 'duck.db'
|
|
10
|
+
kw = dict(db=fname, driver='duckdb')
|
|
11
|
+
conn = duckdb.connect(fname)
|
|
12
|
+
cur = conn.cursor()
|
|
13
|
+
cur.execute('create table tmp (a int, b double precision)')
|
|
14
|
+
cur.execute('insert into tmp values(1,2), (2,3), (3,4), (4,5);')
|
|
15
|
+
conn.commit()
|
|
16
|
+
yield kw
|
|
17
|
+
os.unlink(fname)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_simple(setup):
|
|
21
|
+
a, b = sqlutil.get('select a,b from tmp', **setup)
|
|
22
|
+
assert (len(a) == 4)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def test_preamb(setup):
|
|
26
|
+
a, b = sqlutil.get('select a,b from tmp', preamb='select 1', **setup)
|
|
27
|
+
assert (len(a) == 4)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_empty(setup):
|
|
31
|
+
a, b = sqlutil.get('select a,b from tmp where a<0', **setup)
|
|
32
|
+
assert (len(a) == 0)
|
|
@@ -255,6 +255,12 @@ def test_get_dict(setup):
|
|
|
255
255
|
**kw)
|
|
256
256
|
for i, k in enumerate(cols.split(',')):
|
|
257
257
|
assert ((Rd[k] == R0[i]).all())
|
|
258
|
+
Rempty = sqlutil.get('select %s from sqlutil_test where sicol<-10000' %
|
|
259
|
+
(cols, ),
|
|
260
|
+
asDict=True,
|
|
261
|
+
**kw)
|
|
262
|
+
for i, k in enumerate(cols.split(',')):
|
|
263
|
+
assert k in Rempty
|
|
258
264
|
|
|
259
265
|
|
|
260
266
|
def test_get_dict_rep(setup):
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = '0.23.0'
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|