pglib 5.7.1__tar.gz → 5.9.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.9.1}/PKG-INFO +1 -1
- {pglib-5.7.1 → pglib-5.9.1}/pglib/__init__.py +0 -4
- {pglib-5.7.1 → pglib-5.9.1/pglib.egg-info}/PKG-INFO +1 -1
- {pglib-5.7.1 → pglib-5.9.1}/pglib.egg-info/SOURCES.txt +0 -1
- {pglib-5.7.1 → pglib-5.9.1}/setup.py +1 -1
- {pglib-5.7.1 → pglib-5.9.1}/src/connection.cpp +178 -8
- {pglib-5.7.1 → pglib-5.9.1}/src/getdata.cpp +7 -2
- {pglib-5.7.1 → pglib-5.9.1}/src/params.cpp +1 -1
- {pglib-5.7.1 → pglib-5.9.1}/src/pgarrays.cpp +172 -59
- {pglib-5.7.1 → pglib-5.9.1}/src/pgarrays.h +1 -0
- {pglib-5.7.1 → pglib-5.9.1}/src/pglib.h +3 -2
- {pglib-5.7.1 → pglib-5.9.1}/test/test_sync.py +67 -11
- pglib-5.7.1/pglib/_version.py +0 -484
- {pglib-5.7.1 → pglib-5.9.1}/LICENSE +0 -0
- {pglib-5.7.1 → pglib-5.9.1}/MANIFEST.in +0 -0
- {pglib-5.7.1 → pglib-5.9.1}/README.rst +0 -0
- {pglib-5.7.1 → pglib-5.9.1}/pglib/asyncpglib.py +0 -0
- {pglib-5.7.1 → pglib-5.9.1}/pglib.egg-info/dependency_links.txt +0 -0
- {pglib-5.7.1 → pglib-5.9.1}/pglib.egg-info/top_level.txt +0 -0
- {pglib-5.7.1 → pglib-5.9.1}/setup.cfg +0 -0
- {pglib-5.7.1 → pglib-5.9.1}/src/byteswap.h +0 -0
- {pglib-5.7.1 → pglib-5.9.1}/src/connection.h +0 -0
- {pglib-5.7.1 → pglib-5.9.1}/src/conninfoopt.cpp +0 -0
- {pglib-5.7.1 → pglib-5.9.1}/src/conninfoopt.h +0 -0
- {pglib-5.7.1 → pglib-5.9.1}/src/datatypes.cpp +0 -0
- {pglib-5.7.1 → pglib-5.9.1}/src/datatypes.h +0 -0
- {pglib-5.7.1 → pglib-5.9.1}/src/debug.cpp +0 -0
- {pglib-5.7.1 → pglib-5.9.1}/src/debug.h +0 -0
- {pglib-5.7.1 → pglib-5.9.1}/src/enums.cpp +0 -0
- {pglib-5.7.1 → pglib-5.9.1}/src/enums.h +0 -0
- {pglib-5.7.1 → pglib-5.9.1}/src/errors.cpp +0 -0
- {pglib-5.7.1 → pglib-5.9.1}/src/errors.h +0 -0
- {pglib-5.7.1 → pglib-5.9.1}/src/getdata.h +0 -0
- {pglib-5.7.1 → pglib-5.9.1}/src/juliandate.cpp +0 -0
- {pglib-5.7.1 → pglib-5.9.1}/src/juliandate.h +0 -0
- {pglib-5.7.1 → pglib-5.9.1}/src/params.h +0 -0
- {pglib-5.7.1 → pglib-5.9.1}/src/pglib.cpp +0 -0
- {pglib-5.7.1 → pglib-5.9.1}/src/pgtypes.h +0 -0
- {pglib-5.7.1 → pglib-5.9.1}/src/resultset.cpp +0 -0
- {pglib-5.7.1 → pglib-5.9.1}/src/resultset.h +0 -0
- {pglib-5.7.1 → pglib-5.9.1}/src/row.cpp +0 -0
- {pglib-5.7.1 → pglib-5.9.1}/src/row.h +0 -0
- {pglib-5.7.1 → pglib-5.9.1}/src/runtime.cpp +0 -0
- {pglib-5.7.1 → pglib-5.9.1}/src/runtime.h +0 -0
- {pglib-5.7.1 → pglib-5.9.1}/src/type_hstore.cpp +0 -0
- {pglib-5.7.1 → pglib-5.9.1}/src/type_hstore.h +0 -0
- {pglib-5.7.1 → pglib-5.9.1}/src/type_json.cpp +0 -0
- {pglib-5.7.1 → pglib-5.9.1}/src/type_json.h +0 -0
- {pglib-5.7.1 → pglib-5.9.1}/src/type_ltree.cpp +0 -0
- {pglib-5.7.1 → pglib-5.9.1}/src/type_ltree.h +0 -0
- {pglib-5.7.1 → pglib-5.9.1}/test/test_async.py +0 -0
- {pglib-5.7.1 → pglib-5.9.1}/test/testutils.py +0 -0
|
@@ -150,6 +150,8 @@ static PGresult* internal_execute(PyObject* self, PyObject* args)
|
|
|
150
150
|
return 0;
|
|
151
151
|
}
|
|
152
152
|
const char* szSQL = PyUnicode_AsUTF8(pSql);
|
|
153
|
+
if (!szSQL)
|
|
154
|
+
return 0;
|
|
153
155
|
|
|
154
156
|
Params params(cParams);
|
|
155
157
|
if (!BindParams(cnxn, params, args))
|
|
@@ -176,6 +178,7 @@ static PGresult* internal_execute(PyObject* self, PyObject* args)
|
|
|
176
178
|
return result;
|
|
177
179
|
}
|
|
178
180
|
|
|
181
|
+
|
|
179
182
|
static const char doc_script[] = "Connection.script(sql) --> None\n\n"
|
|
180
183
|
"Executes a script which can contain multiple statements separated by semicolons.";
|
|
181
184
|
|
|
@@ -189,7 +192,11 @@ static PyObject* Connection_script(PyObject* self, PyObject* args)
|
|
|
189
192
|
if (!PyArg_ParseTuple(args, "U", &pScript))
|
|
190
193
|
return 0;
|
|
191
194
|
|
|
192
|
-
|
|
195
|
+
const char* szScript = PyUnicode_AsUTF8(pScript);
|
|
196
|
+
if (!szScript)
|
|
197
|
+
return 0;
|
|
198
|
+
|
|
199
|
+
ResultHolder result = PQexec(cnxn->pgconn, szScript);
|
|
193
200
|
if (result == 0)
|
|
194
201
|
return 0;
|
|
195
202
|
|
|
@@ -204,6 +211,7 @@ static PyObject* Connection_script(PyObject* self, PyObject* args)
|
|
|
204
211
|
}
|
|
205
212
|
}
|
|
206
213
|
|
|
214
|
+
|
|
207
215
|
const char* doc_copy_from =
|
|
208
216
|
"Connection.copy_from(command, source) --> int\n"
|
|
209
217
|
"\n"
|
|
@@ -234,7 +242,7 @@ static PyObject* Connection_copy_from(PyObject* self, PyObject* args)
|
|
|
234
242
|
// an object with a read method (e.g. file).
|
|
235
243
|
const char* buffer = 0;
|
|
236
244
|
Py_ssize_t buffer_size = 0;
|
|
237
|
-
|
|
245
|
+
Object read_method;
|
|
238
246
|
|
|
239
247
|
if (PyUnicode_Check(source))
|
|
240
248
|
{
|
|
@@ -246,7 +254,7 @@ static PyObject* Connection_copy_from(PyObject* self, PyObject* args)
|
|
|
246
254
|
{
|
|
247
255
|
if (!PyObject_HasAttrString(source, "read"))
|
|
248
256
|
return PyErr_Format(Error, "CSV source must be a string or file-like object.");
|
|
249
|
-
read_method
|
|
257
|
+
read_method.Attach(PyObject_GetAttrString(source, "read"));
|
|
250
258
|
}
|
|
251
259
|
|
|
252
260
|
Connection* cnxn = CastConnection(self, REQUIRE_OPEN);
|
|
@@ -254,6 +262,9 @@ static PyObject* Connection_copy_from(PyObject* self, PyObject* args)
|
|
|
254
262
|
return 0;
|
|
255
263
|
|
|
256
264
|
const char* szSQL = PyUnicode_AsUTF8(command);
|
|
265
|
+
if (!szSQL)
|
|
266
|
+
return 0;
|
|
267
|
+
|
|
257
268
|
ResultHolder result;
|
|
258
269
|
Py_BEGIN_ALLOW_THREADS
|
|
259
270
|
result = PQexec(cnxn->pgconn, szSQL);
|
|
@@ -354,6 +365,145 @@ static PyObject* Connection_copy_from(PyObject* self, PyObject* args)
|
|
|
354
365
|
}
|
|
355
366
|
|
|
356
367
|
|
|
368
|
+
const char* doc_copy_to_csv =
|
|
369
|
+
"Connection.copy_to_csv(table, dest, header=0, delimiter=',', quote='\"')\n"
|
|
370
|
+
"\n"
|
|
371
|
+
"Execute a COPY TO command and return the number of records copied.\n"
|
|
372
|
+
"\n"
|
|
373
|
+
"table\n"
|
|
374
|
+
" The table to copy from.\n"
|
|
375
|
+
"\n"
|
|
376
|
+
"dest\n"
|
|
377
|
+
" The file-like object to write to. Strings will be written, not bytes, so\n"
|
|
378
|
+
" open in text mode.\n"
|
|
379
|
+
"\n"
|
|
380
|
+
"header\n"
|
|
381
|
+
" If non-zero, a CSV header will be written.\n";
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
static PyObject* Connection_copy_to_csv(PyObject* self, PyObject* args, PyObject* kwargs)
|
|
385
|
+
{
|
|
386
|
+
// This is not nearly as efficient as I'd like since newer Python versions no longer give
|
|
387
|
+
// us access to underlying file objects. We have to write strings through a write method
|
|
388
|
+
// since there are io layers involved.
|
|
389
|
+
//
|
|
390
|
+
// For maximum performance, we should probably offer an option where we open the file given
|
|
391
|
+
// a filename. We can either check the parameter type here or we could make a separate
|
|
392
|
+
// method with "file" in the name like copy_to_file.
|
|
393
|
+
|
|
394
|
+
static const char* kwlist[] = {"table", "dest", "header", "delimiter", "quote", 0};
|
|
395
|
+
|
|
396
|
+
PyObject* table;
|
|
397
|
+
PyObject* dest;
|
|
398
|
+
int header = 0;
|
|
399
|
+
char* szDelimiter = 0;
|
|
400
|
+
char* szQuote = 0;
|
|
401
|
+
|
|
402
|
+
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "UO|pzz", (char**)kwlist, &table, &dest, &header,
|
|
403
|
+
&szDelimiter, &szQuote)) {
|
|
404
|
+
return 0;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
Connection* cnxn = CastConnection(self, REQUIRE_OPEN);
|
|
408
|
+
if (!cnxn)
|
|
409
|
+
return 0;
|
|
410
|
+
|
|
411
|
+
if (!PyObject_HasAttrString(dest, "write"))
|
|
412
|
+
return PyErr_Format(Error, "CSV destination must be a file-like object.");
|
|
413
|
+
Object write_method(PyObject_GetAttrString(dest, "write"));
|
|
414
|
+
|
|
415
|
+
char header_token[] = "header";
|
|
416
|
+
if (header == 0) {
|
|
417
|
+
header_token[0] = 0;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
const char* pszDelimiter = szDelimiter ? szDelimiter : ",";
|
|
421
|
+
const char* pszQuote = szQuote ? szQuote : "\"";
|
|
422
|
+
|
|
423
|
+
Object sql(PyUnicode_FromFormat("copy %U to stdout with csv %s delimiter '%s' quote '%s'",
|
|
424
|
+
table, header_token, pszDelimiter, pszQuote));
|
|
425
|
+
if (!sql)
|
|
426
|
+
return 0;
|
|
427
|
+
|
|
428
|
+
const char* szSQL = PyUnicode_AsUTF8(sql);
|
|
429
|
+
if (!szSQL)
|
|
430
|
+
return 0;
|
|
431
|
+
|
|
432
|
+
ResultHolder result;
|
|
433
|
+
Py_BEGIN_ALLOW_THREADS
|
|
434
|
+
result = PQexec(cnxn->pgconn, szSQL);
|
|
435
|
+
Py_END_ALLOW_THREADS
|
|
436
|
+
|
|
437
|
+
if (result == 0)
|
|
438
|
+
return 0;
|
|
439
|
+
|
|
440
|
+
switch (PQresultStatus(result)) {
|
|
441
|
+
case PGRES_COPY_OUT:
|
|
442
|
+
// This is what we are expecting.
|
|
443
|
+
break;
|
|
444
|
+
|
|
445
|
+
case PGRES_BAD_RESPONSE:
|
|
446
|
+
case PGRES_NONFATAL_ERROR:
|
|
447
|
+
case PGRES_FATAL_ERROR:
|
|
448
|
+
return SetResultError(result.Detach());
|
|
449
|
+
|
|
450
|
+
default:
|
|
451
|
+
return PyErr_Format(Error, "Result was not PGRES_COPY_IN: %d", (int)PQresultStatus(result));
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
for (;;) {
|
|
456
|
+
int cb = 0;
|
|
457
|
+
char* buffer;
|
|
458
|
+
Py_BEGIN_ALLOW_THREADS
|
|
459
|
+
cb = PQgetCopyData(cnxn->pgconn, &buffer, 0);
|
|
460
|
+
Py_END_ALLOW_THREADS
|
|
461
|
+
|
|
462
|
+
if (cb == -2) {
|
|
463
|
+
return SetResultError(result.Detach());
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
if (cb == -1) {
|
|
467
|
+
// The copy is complete.
|
|
468
|
+
break;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// We have a buffer of byte data. We have the length, but the libpq docs say that the
|
|
472
|
+
// string is also zero terminated, so we're going to try not calling 'write'.
|
|
473
|
+
|
|
474
|
+
int err = PyFile_WriteString(buffer, dest);
|
|
475
|
+
|
|
476
|
+
// while (cb > 0) {
|
|
477
|
+
// PyObject* res = PyObject_CallObject(write_method)
|
|
478
|
+
// }
|
|
479
|
+
|
|
480
|
+
PQfreemem(buffer);
|
|
481
|
+
if (err) {
|
|
482
|
+
return 0;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
// After a copy, you have to get another result to know if it was successful.
|
|
487
|
+
|
|
488
|
+
ResultHolder final_result;
|
|
489
|
+
ExecStatusType status = PGRES_COMMAND_OK;
|
|
490
|
+
Py_BEGIN_ALLOW_THREADS
|
|
491
|
+
final_result = PQgetResult(cnxn->pgconn);
|
|
492
|
+
status = PQresultStatus(final_result);
|
|
493
|
+
Py_END_ALLOW_THREADS
|
|
494
|
+
|
|
495
|
+
if (status != PGRES_COMMAND_OK) {
|
|
496
|
+
// SetResultError will take ownership of `result`.
|
|
497
|
+
return SetResultError(final_result.Detach());
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
const char* sz = PQcmdTuples(final_result);
|
|
501
|
+
if (sz == 0 || *sz == 0)
|
|
502
|
+
Py_RETURN_NONE;
|
|
503
|
+
return PyLong_FromLong(atoi(sz));
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
|
|
357
507
|
const char* doc_copy_from_csv =
|
|
358
508
|
"Connection.copy_from_csv(table, source, header=0) --> int\n"
|
|
359
509
|
"\n"
|
|
@@ -390,15 +540,16 @@ static PyObject* Connection_copy_from_csv(PyObject* self, PyObject* args, PyObje
|
|
|
390
540
|
|
|
391
541
|
const char* pszDelimiter = szDelimiter ? szDelimiter : ",";
|
|
392
542
|
const char* pszQuote = szQuote ? szQuote : "\"";
|
|
393
|
-
|
|
394
|
-
|
|
543
|
+
Object sql(PyUnicode_FromFormat("copy %U from stdin with csv %s delimiter '%s' quote '%s'",
|
|
544
|
+
table, header_token, pszDelimiter, pszQuote));
|
|
395
545
|
|
|
396
546
|
// If source is a string (Unicode), store the UTF-encoded value in buffer. If a byte
|
|
397
547
|
// object, store directly in buffer. Otherwise, buffer will be zero and `source` must be
|
|
398
548
|
// an object with a read method (e.g. file).
|
|
399
549
|
const char* buffer = 0;
|
|
400
550
|
Py_ssize_t buffer_size = 0;
|
|
401
|
-
|
|
551
|
+
Object read_method;
|
|
552
|
+
// PyObject* read_method = 0;
|
|
402
553
|
|
|
403
554
|
if (PyUnicode_Check(source))
|
|
404
555
|
{
|
|
@@ -410,7 +561,7 @@ static PyObject* Connection_copy_from_csv(PyObject* self, PyObject* args, PyObje
|
|
|
410
561
|
{
|
|
411
562
|
if (!PyObject_HasAttrString(source, "read"))
|
|
412
563
|
return PyErr_Format(Error, "CSV source must be a string or file-like object.");
|
|
413
|
-
read_method
|
|
564
|
+
read_method.Attach(PyObject_GetAttrString(source, "read"));
|
|
414
565
|
}
|
|
415
566
|
|
|
416
567
|
Connection* cnxn = CastConnection(self, REQUIRE_OPEN);
|
|
@@ -418,6 +569,9 @@ static PyObject* Connection_copy_from_csv(PyObject* self, PyObject* args, PyObje
|
|
|
418
569
|
return 0;
|
|
419
570
|
|
|
420
571
|
const char* szSQL = PyUnicode_AsUTF8(sql);
|
|
572
|
+
if (!szSQL)
|
|
573
|
+
return 0;
|
|
574
|
+
|
|
421
575
|
ResultHolder result;
|
|
422
576
|
Py_BEGIN_ALLOW_THREADS
|
|
423
577
|
result = PQexec(cnxn->pgconn, szSQL);
|
|
@@ -1000,6 +1154,9 @@ static PyObject* Connection_sendQuery(PyObject* self, PyObject* args)
|
|
|
1000
1154
|
|
|
1001
1155
|
int sent;
|
|
1002
1156
|
const char* szSQL = PyUnicode_AsUTF8(pScript);
|
|
1157
|
+
if (!szSQL)
|
|
1158
|
+
return 0;
|
|
1159
|
+
|
|
1003
1160
|
Py_BEGIN_ALLOW_THREADS
|
|
1004
1161
|
sent = PQsendQuery(cnxn->pgconn, szSQL);
|
|
1005
1162
|
Py_END_ALLOW_THREADS
|
|
@@ -1039,7 +1196,10 @@ static PyObject* Connection_sendQueryParams(PyObject* self, PyObject* args)
|
|
|
1039
1196
|
PyErr_SetString(PyExc_TypeError, "The first argument must be the SQL string.");
|
|
1040
1197
|
return 0;
|
|
1041
1198
|
}
|
|
1199
|
+
|
|
1042
1200
|
const char* szSQL = PyUnicode_AsUTF8(pSql);
|
|
1201
|
+
if (!szSQL)
|
|
1202
|
+
return 0;
|
|
1043
1203
|
|
|
1044
1204
|
Params params(cParams);
|
|
1045
1205
|
if (!BindParams(cnxn, params, args))
|
|
@@ -1218,7 +1378,16 @@ static PyObject* Connection_notifications(PyObject* self, PyObject* args, PyObje
|
|
|
1218
1378
|
Py_END_ALLOW_THREADS
|
|
1219
1379
|
|
|
1220
1380
|
if (retval == -1) {
|
|
1221
|
-
|
|
1381
|
+
// Allow the Python (or application) signal handler deal with Ctrl-C. If it raises an
|
|
1382
|
+
// exception, -1 is returned. This only works on the main Python thread, but it is
|
|
1383
|
+
// just a convenience for command line utilities to raise a KeyboardInterrupt instead a
|
|
1384
|
+
// pglib error that *then* raises a KeyboardInterrupt. So it is OK if we call on
|
|
1385
|
+
// another thread and it is a no-op.
|
|
1386
|
+
|
|
1387
|
+
if (PyErr_CheckSignals() != -1) {
|
|
1388
|
+
SetStringError(Error, "An error occurred waiting for notifications");
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1222
1391
|
return 0;
|
|
1223
1392
|
}
|
|
1224
1393
|
|
|
@@ -1462,6 +1631,7 @@ static struct PyMethodDef Connection_methods[] =
|
|
|
1462
1631
|
{ "script", Connection_script, METH_VARARGS, doc_script },
|
|
1463
1632
|
{ "copy_from", (PyCFunction) Connection_copy_from, METH_VARARGS | METH_KEYWORDS, doc_copy_from },
|
|
1464
1633
|
{ "copy_from_csv", (PyCFunction) Connection_copy_from_csv, METH_VARARGS | METH_KEYWORDS, doc_copy_from_csv },
|
|
1634
|
+
{ "copy_to_csv", (PyCFunction) Connection_copy_to_csv, METH_VARARGS | METH_KEYWORDS, doc_copy_to_csv},
|
|
1465
1635
|
{ "begin", Connection_begin, METH_NOARGS, doc_begin },
|
|
1466
1636
|
{ "commit", Connection_commit, METH_NOARGS, doc_commit },
|
|
1467
1637
|
{ "rollback", Connection_rollback, METH_NOARGS, doc_rollback },
|
|
@@ -286,8 +286,10 @@ static PyObject* GetInterval(const char* p) // , bool integer_datetimes)
|
|
|
286
286
|
uint32_t month = swaps4(pinterval->month);
|
|
287
287
|
uint32_t year = month / 12;
|
|
288
288
|
|
|
289
|
-
if (month || year)
|
|
290
|
-
|
|
289
|
+
if (month || year) {
|
|
290
|
+
PyErr_SetString(Error, "Years and months are not supported in intervals");
|
|
291
|
+
return 0;
|
|
292
|
+
}
|
|
291
293
|
|
|
292
294
|
int seconds = (second) + (60 * minute) + (3600 * hour);
|
|
293
295
|
int days = day;
|
|
@@ -399,6 +401,9 @@ PyObject* ConvertValue(PGresult* result, int iRow, int iCol, bool integer_dateti
|
|
|
399
401
|
case TEXTARRAYOID:
|
|
400
402
|
return GetTextArray(p);
|
|
401
403
|
|
|
404
|
+
case JSONBARRAYOID:
|
|
405
|
+
return GetJSONBArray(p);
|
|
406
|
+
|
|
402
407
|
case DATEARRAYOID:
|
|
403
408
|
return GetDateArray(p);
|
|
404
409
|
|