pglib 5.7.1__tar.gz → 5.8.1__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.
- {pglib-5.7.1/pglib.egg-info → pglib-5.8.1}/PKG-INFO +1 -1
- {pglib-5.7.1 → pglib-5.8.1/pglib.egg-info}/PKG-INFO +1 -1
- {pglib-5.7.1 → pglib-5.8.1}/setup.py +1 -1
- {pglib-5.7.1 → pglib-5.8.1}/src/connection.cpp +144 -6
- {pglib-5.7.1 → pglib-5.8.1}/src/pglib.h +1 -1
- {pglib-5.7.1 → pglib-5.8.1}/test/test_sync.py +57 -11
- {pglib-5.7.1 → pglib-5.8.1}/LICENSE +0 -0
- {pglib-5.7.1 → pglib-5.8.1}/MANIFEST.in +0 -0
- {pglib-5.7.1 → pglib-5.8.1}/README.rst +0 -0
- {pglib-5.7.1 → pglib-5.8.1}/pglib/__init__.py +0 -0
- {pglib-5.7.1 → pglib-5.8.1}/pglib/_version.py +0 -0
- {pglib-5.7.1 → pglib-5.8.1}/pglib/asyncpglib.py +0 -0
- {pglib-5.7.1 → pglib-5.8.1}/pglib.egg-info/SOURCES.txt +0 -0
- {pglib-5.7.1 → pglib-5.8.1}/pglib.egg-info/dependency_links.txt +0 -0
- {pglib-5.7.1 → pglib-5.8.1}/pglib.egg-info/top_level.txt +0 -0
- {pglib-5.7.1 → pglib-5.8.1}/setup.cfg +0 -0
- {pglib-5.7.1 → pglib-5.8.1}/src/byteswap.h +0 -0
- {pglib-5.7.1 → pglib-5.8.1}/src/connection.h +0 -0
- {pglib-5.7.1 → pglib-5.8.1}/src/conninfoopt.cpp +0 -0
- {pglib-5.7.1 → pglib-5.8.1}/src/conninfoopt.h +0 -0
- {pglib-5.7.1 → pglib-5.8.1}/src/datatypes.cpp +0 -0
- {pglib-5.7.1 → pglib-5.8.1}/src/datatypes.h +0 -0
- {pglib-5.7.1 → pglib-5.8.1}/src/debug.cpp +0 -0
- {pglib-5.7.1 → pglib-5.8.1}/src/debug.h +0 -0
- {pglib-5.7.1 → pglib-5.8.1}/src/enums.cpp +0 -0
- {pglib-5.7.1 → pglib-5.8.1}/src/enums.h +0 -0
- {pglib-5.7.1 → pglib-5.8.1}/src/errors.cpp +0 -0
- {pglib-5.7.1 → pglib-5.8.1}/src/errors.h +0 -0
- {pglib-5.7.1 → pglib-5.8.1}/src/getdata.cpp +0 -0
- {pglib-5.7.1 → pglib-5.8.1}/src/getdata.h +0 -0
- {pglib-5.7.1 → pglib-5.8.1}/src/juliandate.cpp +0 -0
- {pglib-5.7.1 → pglib-5.8.1}/src/juliandate.h +0 -0
- {pglib-5.7.1 → pglib-5.8.1}/src/params.cpp +0 -0
- {pglib-5.7.1 → pglib-5.8.1}/src/params.h +0 -0
- {pglib-5.7.1 → pglib-5.8.1}/src/pgarrays.cpp +0 -0
- {pglib-5.7.1 → pglib-5.8.1}/src/pgarrays.h +0 -0
- {pglib-5.7.1 → pglib-5.8.1}/src/pglib.cpp +0 -0
- {pglib-5.7.1 → pglib-5.8.1}/src/pgtypes.h +0 -0
- {pglib-5.7.1 → pglib-5.8.1}/src/resultset.cpp +0 -0
- {pglib-5.7.1 → pglib-5.8.1}/src/resultset.h +0 -0
- {pglib-5.7.1 → pglib-5.8.1}/src/row.cpp +0 -0
- {pglib-5.7.1 → pglib-5.8.1}/src/row.h +0 -0
- {pglib-5.7.1 → pglib-5.8.1}/src/runtime.cpp +0 -0
- {pglib-5.7.1 → pglib-5.8.1}/src/runtime.h +0 -0
- {pglib-5.7.1 → pglib-5.8.1}/src/type_hstore.cpp +0 -0
- {pglib-5.7.1 → pglib-5.8.1}/src/type_hstore.h +0 -0
- {pglib-5.7.1 → pglib-5.8.1}/src/type_json.cpp +0 -0
- {pglib-5.7.1 → pglib-5.8.1}/src/type_json.h +0 -0
- {pglib-5.7.1 → pglib-5.8.1}/src/type_ltree.cpp +0 -0
- {pglib-5.7.1 → pglib-5.8.1}/src/type_ltree.h +0 -0
- {pglib-5.7.1 → pglib-5.8.1}/test/test_async.py +0 -0
- {pglib-5.7.1 → pglib-5.8.1}/test/testutils.py +0 -0
|
@@ -176,6 +176,7 @@ static PGresult* internal_execute(PyObject* self, PyObject* args)
|
|
|
176
176
|
return result;
|
|
177
177
|
}
|
|
178
178
|
|
|
179
|
+
|
|
179
180
|
static const char doc_script[] = "Connection.script(sql) --> None\n\n"
|
|
180
181
|
"Executes a script which can contain multiple statements separated by semicolons.";
|
|
181
182
|
|
|
@@ -204,6 +205,7 @@ static PyObject* Connection_script(PyObject* self, PyObject* args)
|
|
|
204
205
|
}
|
|
205
206
|
}
|
|
206
207
|
|
|
208
|
+
|
|
207
209
|
const char* doc_copy_from =
|
|
208
210
|
"Connection.copy_from(command, source) --> int\n"
|
|
209
211
|
"\n"
|
|
@@ -234,7 +236,7 @@ static PyObject* Connection_copy_from(PyObject* self, PyObject* args)
|
|
|
234
236
|
// an object with a read method (e.g. file).
|
|
235
237
|
const char* buffer = 0;
|
|
236
238
|
Py_ssize_t buffer_size = 0;
|
|
237
|
-
|
|
239
|
+
Object read_method;
|
|
238
240
|
|
|
239
241
|
if (PyUnicode_Check(source))
|
|
240
242
|
{
|
|
@@ -246,7 +248,7 @@ static PyObject* Connection_copy_from(PyObject* self, PyObject* args)
|
|
|
246
248
|
{
|
|
247
249
|
if (!PyObject_HasAttrString(source, "read"))
|
|
248
250
|
return PyErr_Format(Error, "CSV source must be a string or file-like object.");
|
|
249
|
-
read_method
|
|
251
|
+
read_method.Attach(PyObject_GetAttrString(source, "read"));
|
|
250
252
|
}
|
|
251
253
|
|
|
252
254
|
Connection* cnxn = CastConnection(self, REQUIRE_OPEN);
|
|
@@ -354,6 +356,140 @@ static PyObject* Connection_copy_from(PyObject* self, PyObject* args)
|
|
|
354
356
|
}
|
|
355
357
|
|
|
356
358
|
|
|
359
|
+
const char* doc_copy_to_csv =
|
|
360
|
+
"Connection.copy_to_csv(table, dest, header=0, delimiter=',', quote='\"')\n"
|
|
361
|
+
"\n"
|
|
362
|
+
"Execute a COPY TO command and return the number of records copied.\n"
|
|
363
|
+
"\n"
|
|
364
|
+
"table\n"
|
|
365
|
+
" The table to copy from.\n"
|
|
366
|
+
"\n"
|
|
367
|
+
"dest\n"
|
|
368
|
+
" The file-like object to write to. Strings will be written, not bytes, so\n"
|
|
369
|
+
" open in text mode.\n"
|
|
370
|
+
"\n"
|
|
371
|
+
"header\n"
|
|
372
|
+
" If non-zero, a CSV header will be written.\n";
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
static PyObject* Connection_copy_to_csv(PyObject* self, PyObject* args, PyObject* kwargs)
|
|
376
|
+
{
|
|
377
|
+
// This is not nearly as efficient as I'd like since newer Python versions no longer give
|
|
378
|
+
// us access to underlying file objects. We have to write strings through a write method
|
|
379
|
+
// since there are io layers involved.
|
|
380
|
+
//
|
|
381
|
+
// For maximum performance, we should probably offer an option where we open the file given
|
|
382
|
+
// a filename. We can either check the parameter type here or we could make a separate
|
|
383
|
+
// method with "file" in the name like copy_to_file.
|
|
384
|
+
|
|
385
|
+
static const char* kwlist[] = {"table", "dest", "header", "delimiter", "quote", 0};
|
|
386
|
+
|
|
387
|
+
PyObject* table;
|
|
388
|
+
PyObject* dest;
|
|
389
|
+
int header = 0;
|
|
390
|
+
char* szDelimiter = 0;
|
|
391
|
+
char* szQuote = 0;
|
|
392
|
+
|
|
393
|
+
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "UO|pzz", (char**)kwlist, &table, &dest, &header,
|
|
394
|
+
&szDelimiter, &szQuote)) {
|
|
395
|
+
return 0;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
Connection* cnxn = CastConnection(self, REQUIRE_OPEN);
|
|
399
|
+
if (!cnxn)
|
|
400
|
+
return 0;
|
|
401
|
+
|
|
402
|
+
if (!PyObject_HasAttrString(dest, "write"))
|
|
403
|
+
return PyErr_Format(Error, "CSV destination must be a file-like object.");
|
|
404
|
+
Object write_method(PyObject_GetAttrString(dest, "write"));
|
|
405
|
+
|
|
406
|
+
char header_token[] = "header";
|
|
407
|
+
if (header == 0) {
|
|
408
|
+
header_token[0] = 0;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
const char* pszDelimiter = szDelimiter ? szDelimiter : ",";
|
|
412
|
+
const char* pszQuote = szQuote ? szQuote : "\"";
|
|
413
|
+
|
|
414
|
+
Object sql(PyUnicode_FromFormat("copy %U to stdout with csv %s delimiter '%s' quote '%s'",
|
|
415
|
+
table, header_token, pszDelimiter, pszQuote));
|
|
416
|
+
|
|
417
|
+
const char* szSQL = PyUnicode_AsUTF8(sql);
|
|
418
|
+
ResultHolder result;
|
|
419
|
+
Py_BEGIN_ALLOW_THREADS
|
|
420
|
+
result = PQexec(cnxn->pgconn, szSQL);
|
|
421
|
+
Py_END_ALLOW_THREADS
|
|
422
|
+
|
|
423
|
+
if (result == 0)
|
|
424
|
+
return 0;
|
|
425
|
+
|
|
426
|
+
switch (PQresultStatus(result)) {
|
|
427
|
+
case PGRES_COPY_OUT:
|
|
428
|
+
// This is what we are expecting.
|
|
429
|
+
break;
|
|
430
|
+
|
|
431
|
+
case PGRES_BAD_RESPONSE:
|
|
432
|
+
case PGRES_NONFATAL_ERROR:
|
|
433
|
+
case PGRES_FATAL_ERROR:
|
|
434
|
+
return SetResultError(result.Detach());
|
|
435
|
+
|
|
436
|
+
default:
|
|
437
|
+
return PyErr_Format(Error, "Result was not PGRES_COPY_IN: %d", (int)PQresultStatus(result));
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
for (;;) {
|
|
442
|
+
int cb = 0;
|
|
443
|
+
char* buffer;
|
|
444
|
+
Py_BEGIN_ALLOW_THREADS
|
|
445
|
+
cb = PQgetCopyData(cnxn->pgconn, &buffer, 0);
|
|
446
|
+
Py_END_ALLOW_THREADS
|
|
447
|
+
|
|
448
|
+
if (cb == -2) {
|
|
449
|
+
return SetResultError(result.Detach());
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
if (cb == -1) {
|
|
453
|
+
// The copy is complete.
|
|
454
|
+
break;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// We have a buffer of byte data. We have the length, but the libpq docs say that the
|
|
458
|
+
// string is also zero terminated, so we're going to try not calling 'write'.
|
|
459
|
+
|
|
460
|
+
int err = PyFile_WriteString(buffer, dest);
|
|
461
|
+
|
|
462
|
+
// while (cb > 0) {
|
|
463
|
+
// PyObject* res = PyObject_CallObject(write_method)
|
|
464
|
+
// }
|
|
465
|
+
|
|
466
|
+
PQfreemem(buffer);
|
|
467
|
+
if (err) {
|
|
468
|
+
return 0;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// After a copy, you have to get another result to know if it was successful.
|
|
473
|
+
|
|
474
|
+
ResultHolder final_result;
|
|
475
|
+
ExecStatusType status = PGRES_COMMAND_OK;
|
|
476
|
+
Py_BEGIN_ALLOW_THREADS
|
|
477
|
+
final_result = PQgetResult(cnxn->pgconn);
|
|
478
|
+
status = PQresultStatus(final_result);
|
|
479
|
+
Py_END_ALLOW_THREADS
|
|
480
|
+
|
|
481
|
+
if (status != PGRES_COMMAND_OK) {
|
|
482
|
+
// SetResultError will take ownership of `result`.
|
|
483
|
+
return SetResultError(final_result.Detach());
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
const char* sz = PQcmdTuples(final_result);
|
|
487
|
+
if (sz == 0 || *sz == 0)
|
|
488
|
+
Py_RETURN_NONE;
|
|
489
|
+
return PyLong_FromLong(atoi(sz));
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
|
|
357
493
|
const char* doc_copy_from_csv =
|
|
358
494
|
"Connection.copy_from_csv(table, source, header=0) --> int\n"
|
|
359
495
|
"\n"
|
|
@@ -390,15 +526,16 @@ static PyObject* Connection_copy_from_csv(PyObject* self, PyObject* args, PyObje
|
|
|
390
526
|
|
|
391
527
|
const char* pszDelimiter = szDelimiter ? szDelimiter : ",";
|
|
392
528
|
const char* pszQuote = szQuote ? szQuote : "\"";
|
|
393
|
-
|
|
394
|
-
|
|
529
|
+
Object sql(PyUnicode_FromFormat("copy %U from stdin with csv %s delimiter '%s' quote '%s'",
|
|
530
|
+
table, header_token, pszDelimiter, pszQuote));
|
|
395
531
|
|
|
396
532
|
// If source is a string (Unicode), store the UTF-encoded value in buffer. If a byte
|
|
397
533
|
// object, store directly in buffer. Otherwise, buffer will be zero and `source` must be
|
|
398
534
|
// an object with a read method (e.g. file).
|
|
399
535
|
const char* buffer = 0;
|
|
400
536
|
Py_ssize_t buffer_size = 0;
|
|
401
|
-
|
|
537
|
+
Object read_method;
|
|
538
|
+
// PyObject* read_method = 0;
|
|
402
539
|
|
|
403
540
|
if (PyUnicode_Check(source))
|
|
404
541
|
{
|
|
@@ -410,7 +547,7 @@ static PyObject* Connection_copy_from_csv(PyObject* self, PyObject* args, PyObje
|
|
|
410
547
|
{
|
|
411
548
|
if (!PyObject_HasAttrString(source, "read"))
|
|
412
549
|
return PyErr_Format(Error, "CSV source must be a string or file-like object.");
|
|
413
|
-
read_method
|
|
550
|
+
read_method.Attach(PyObject_GetAttrString(source, "read"));
|
|
414
551
|
}
|
|
415
552
|
|
|
416
553
|
Connection* cnxn = CastConnection(self, REQUIRE_OPEN);
|
|
@@ -1462,6 +1599,7 @@ static struct PyMethodDef Connection_methods[] =
|
|
|
1462
1599
|
{ "script", Connection_script, METH_VARARGS, doc_script },
|
|
1463
1600
|
{ "copy_from", (PyCFunction) Connection_copy_from, METH_VARARGS | METH_KEYWORDS, doc_copy_from },
|
|
1464
1601
|
{ "copy_from_csv", (PyCFunction) Connection_copy_from_csv, METH_VARARGS | METH_KEYWORDS, doc_copy_from_csv },
|
|
1602
|
+
{ "copy_to_csv", (PyCFunction) Connection_copy_to_csv, METH_VARARGS | METH_KEYWORDS, doc_copy_to_csv},
|
|
1465
1603
|
{ "begin", Connection_begin, METH_NOARGS, doc_begin },
|
|
1466
1604
|
{ "commit", Connection_commit, METH_NOARGS, doc_commit },
|
|
1467
1605
|
{ "rollback", Connection_rollback, METH_NOARGS, doc_rollback },
|
|
@@ -135,7 +135,7 @@ struct Object
|
|
|
135
135
|
{
|
|
136
136
|
PyObject* p;
|
|
137
137
|
|
|
138
|
-
// Borrows the reference (takes ownership without adding a new
|
|
138
|
+
// Borrows the reference (takes ownership without adding a new reference).
|
|
139
139
|
Object(PyObject* _p = 0) { p = _p; }
|
|
140
140
|
|
|
141
141
|
// If it still has the pointer, it dereferences it.
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
|
|
4
4
|
# pylint: disable=missing-function-docstring,redefined-outer-name,unidiomatic-typecheck
|
|
5
5
|
|
|
6
|
-
import sys, threading, gzip, uuid, locale
|
|
6
|
+
import sys, threading, gzip, uuid, locale, tempfile, csv
|
|
7
7
|
from time import sleep
|
|
8
8
|
from os.path import join, dirname, exists
|
|
9
9
|
from decimal import Decimal
|
|
10
|
-
from datetime import date, time, datetime, timedelta
|
|
10
|
+
from datetime import date, time, datetime, timedelta
|
|
11
11
|
from pathlib import Path
|
|
12
12
|
import pytest
|
|
13
13
|
|
|
@@ -32,7 +32,7 @@ def cnxn():
|
|
|
32
32
|
cnxn = pglib.connect(CONNINFO)
|
|
33
33
|
for i in range(3):
|
|
34
34
|
try:
|
|
35
|
-
cnxn.execute("drop table t
|
|
35
|
+
cnxn.execute(f"drop table t{i}")
|
|
36
36
|
except: # noqa
|
|
37
37
|
pass
|
|
38
38
|
yield cnxn
|
|
@@ -44,9 +44,9 @@ def _test_strtype(cnxn, sqltype, value, resulttype=None, colsize=None):
|
|
|
44
44
|
assert colsize is None or (value is None or colsize >= len(value))
|
|
45
45
|
|
|
46
46
|
if colsize:
|
|
47
|
-
sql = "create table t1(s
|
|
47
|
+
sql = f"create table t1(s {sqltype}({colsize}))"
|
|
48
48
|
else:
|
|
49
|
-
sql = "create table t1(s
|
|
49
|
+
sql = f"create table t1(s {sqltype})"
|
|
50
50
|
|
|
51
51
|
if resulttype is None:
|
|
52
52
|
resulttype = type(value)
|
|
@@ -148,7 +148,37 @@ def test_script(cnxn):
|
|
|
148
148
|
|
|
149
149
|
|
|
150
150
|
#
|
|
151
|
-
# copy
|
|
151
|
+
# copy to
|
|
152
|
+
#
|
|
153
|
+
|
|
154
|
+
def test_copytocsv(cnxn):
|
|
155
|
+
# Write a table to a CSV file.
|
|
156
|
+
#
|
|
157
|
+
# It isn't as efficient as I'd like since we can no longer get ahold of the file
|
|
158
|
+
# descriptor. I think we'll want to make this accept a string filename for a more
|
|
159
|
+
# efficient write. For now, make sure the output file is opened in text mode.
|
|
160
|
+
|
|
161
|
+
cnxn.execute("create table t1(a text, b text)")
|
|
162
|
+
cnxn.execute("insert into t1 values ('1', 'one'), ('2', 'two'), ('3', 'three')")
|
|
163
|
+
|
|
164
|
+
with tempfile.NamedTemporaryFile(mode='w', encoding='utf8') as tf:
|
|
165
|
+
print(tf.name)
|
|
166
|
+
count = cnxn.copy_to_csv('t1', tf, header=1)
|
|
167
|
+
assert count == 3
|
|
168
|
+
|
|
169
|
+
tf.seek(0)
|
|
170
|
+
with open(tf.name, mode='r', encoding='utf8') as fd:
|
|
171
|
+
reader = csv.reader(fd)
|
|
172
|
+
rows = []
|
|
173
|
+
for row in reader:
|
|
174
|
+
print('ROW=', row)
|
|
175
|
+
rows.append(row)
|
|
176
|
+
|
|
177
|
+
assert rows == [['a', 'b'], ['1', 'one'], ['2', 'two'], ['3', 'three']]
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
#
|
|
181
|
+
# copy from
|
|
152
182
|
#
|
|
153
183
|
|
|
154
184
|
def _datapath(filename):
|
|
@@ -159,7 +189,8 @@ def _datapath(filename):
|
|
|
159
189
|
|
|
160
190
|
def test_copyfromcsv_csv(cnxn):
|
|
161
191
|
cnxn.execute("create table t1(a int, b varchar(20))")
|
|
162
|
-
|
|
192
|
+
with open(_datapath('test-noheader.csv')) as fd:
|
|
193
|
+
count = cnxn.copy_from_csv("t1", fd)
|
|
163
194
|
assert count == 2
|
|
164
195
|
assert cnxn.fetchval("select count(*) from t1") == 2
|
|
165
196
|
row = cnxn.fetchrow("select a,b from t1 where a=2")
|
|
@@ -169,7 +200,8 @@ def test_copyfromcsv_csv(cnxn):
|
|
|
169
200
|
|
|
170
201
|
def test_copyfromcsv_csv_header(cnxn):
|
|
171
202
|
cnxn.execute("create table t1(a int, b varchar(20))")
|
|
172
|
-
|
|
203
|
+
with open(_datapath('test-header.csv')) as fd:
|
|
204
|
+
count = cnxn.copy_from_csv("t1", fd, header=True)
|
|
173
205
|
assert count == 2
|
|
174
206
|
assert cnxn.fetchval("select count(*) from t1") == 2
|
|
175
207
|
row = cnxn.fetchrow("select a,b from t1 where a=2")
|
|
@@ -184,13 +216,15 @@ def test_copyfromcsv_csv_error(cnxn):
|
|
|
184
216
|
# We'll make the second column too small.
|
|
185
217
|
cnxn.execute("create table t1(a int, b varchar(1) not null)")
|
|
186
218
|
with pytest.raises(pglib.Error):
|
|
187
|
-
|
|
219
|
+
with open(_datapath('test-noheader.csv')) as fd:
|
|
220
|
+
cnxn.copy_from_csv("t1", fd)
|
|
188
221
|
|
|
189
222
|
|
|
190
223
|
def test_copyfromcsv_csv_gzip(cnxn):
|
|
191
224
|
# I don't remember why this test is here. We're feeding it unzipped data.
|
|
192
225
|
cnxn.execute("create table t1(a int, b varchar(20))")
|
|
193
|
-
|
|
226
|
+
with gzip.open(_datapath('test-header.csv.gz')) as fd:
|
|
227
|
+
cnxn.copy_from_csv("t1", fd, header=True)
|
|
194
228
|
assert cnxn.fetchval("select count(*) from t1") == 2
|
|
195
229
|
row = cnxn.fetchrow("select a,b from t1 where a=2")
|
|
196
230
|
assert row.a == 2
|
|
@@ -1063,7 +1097,7 @@ def test_closed_error(cnxn):
|
|
|
1063
1097
|
cnxn.rollback()
|
|
1064
1098
|
|
|
1065
1099
|
|
|
1066
|
-
def
|
|
1100
|
+
def test_connection_count():
|
|
1067
1101
|
before = pglib.connection_count()
|
|
1068
1102
|
cnxn = pglib.connect(CONNINFO)
|
|
1069
1103
|
assert pglib.connection_count() == (before + 1)
|
|
@@ -1073,6 +1107,18 @@ def test_count():
|
|
|
1073
1107
|
assert pglib.connection_count() == before
|
|
1074
1108
|
|
|
1075
1109
|
|
|
1110
|
+
def test_count(cnxn):
|
|
1111
|
+
"Ensure delete statements return affected row count."
|
|
1112
|
+
cnxn.execute(
|
|
1113
|
+
"""
|
|
1114
|
+
select generate_series(1, 3) as id
|
|
1115
|
+
into t1
|
|
1116
|
+
""")
|
|
1117
|
+
|
|
1118
|
+
count = cnxn.execute("delete from t1 where id in (1, 2, 3)")
|
|
1119
|
+
assert count == 3
|
|
1120
|
+
|
|
1121
|
+
|
|
1076
1122
|
def test_hstore(cnxn):
|
|
1077
1123
|
cnxn.execute("create extension if not exists hstore")
|
|
1078
1124
|
row = cnxn.fetchrow("select oid, typname from pg_type where typname='hstore'")
|
|
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
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|