pglib 5.10.0__tar.gz → 5.12.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.
- {pglib-5.10.0 → pglib-5.12.0}/MANIFEST.in +0 -2
- pglib-5.12.0/PKG-INFO +34 -0
- {pglib-5.10.0 → pglib-5.12.0}/README.rst +2 -2
- pglib-5.12.0/pglib.egg-info/PKG-INFO +34 -0
- {pglib-5.10.0 → pglib-5.12.0}/pglib.egg-info/SOURCES.txt +2 -18
- pglib-5.12.0/pglib.egg-info/requires.txt +3 -0
- pglib-5.12.0/pyproject.toml +40 -0
- pglib-5.12.0/setup.cfg +4 -0
- {pglib-5.10.0 → pglib-5.12.0}/setup.py +11 -63
- {pglib-5.10.0 → pglib-5.12.0}/src/connection.cpp +108 -0
- {pglib-5.10.0 → pglib-5.12.0}/test/test_async.py +10 -15
- {pglib-5.10.0 → pglib-5.12.0}/test/test_sync.py +33 -0
- {pglib-5.10.0 → pglib-5.12.0}/test/testutils.py +18 -16
- pglib-5.10.0/PKG-INFO +0 -25
- pglib-5.10.0/pglib.egg-info/PKG-INFO +0 -25
- pglib-5.10.0/setup.cfg +0 -10
- {pglib-5.10.0 → pglib-5.12.0}/LICENSE +0 -0
- {pglib-5.10.0 → pglib-5.12.0}/pglib/__init__.py +0 -0
- {pglib-5.10.0 → pglib-5.12.0}/pglib/asyncpglib.py +0 -0
- {pglib-5.10.0 → pglib-5.12.0}/pglib.egg-info/dependency_links.txt +0 -0
- {pglib-5.10.0 → pglib-5.12.0}/pglib.egg-info/top_level.txt +0 -0
- {pglib-5.10.0 → pglib-5.12.0}/src/byteswap.h +0 -0
- {pglib-5.10.0 → pglib-5.12.0}/src/connection.h +0 -0
- {pglib-5.10.0 → pglib-5.12.0}/src/conninfoopt.cpp +0 -0
- {pglib-5.10.0 → pglib-5.12.0}/src/conninfoopt.h +0 -0
- {pglib-5.10.0 → pglib-5.12.0}/src/datatypes.cpp +0 -0
- {pglib-5.10.0 → pglib-5.12.0}/src/datatypes.h +0 -0
- {pglib-5.10.0 → pglib-5.12.0}/src/debug.cpp +0 -0
- {pglib-5.10.0 → pglib-5.12.0}/src/debug.h +0 -0
- {pglib-5.10.0 → pglib-5.12.0}/src/enums.cpp +0 -0
- {pglib-5.10.0 → pglib-5.12.0}/src/enums.h +0 -0
- {pglib-5.10.0 → pglib-5.12.0}/src/errors.cpp +0 -0
- {pglib-5.10.0 → pglib-5.12.0}/src/errors.h +0 -0
- {pglib-5.10.0 → pglib-5.12.0}/src/getdata.cpp +0 -0
- {pglib-5.10.0 → pglib-5.12.0}/src/getdata.h +0 -0
- {pglib-5.10.0 → pglib-5.12.0}/src/juliandate.cpp +0 -0
- {pglib-5.10.0 → pglib-5.12.0}/src/juliandate.h +0 -0
- {pglib-5.10.0 → pglib-5.12.0}/src/params.cpp +0 -0
- {pglib-5.10.0 → pglib-5.12.0}/src/params.h +0 -0
- {pglib-5.10.0 → pglib-5.12.0}/src/pgarrays.cpp +0 -0
- {pglib-5.10.0 → pglib-5.12.0}/src/pgarrays.h +0 -0
- {pglib-5.10.0 → pglib-5.12.0}/src/pglib.cpp +0 -0
- {pglib-5.10.0 → pglib-5.12.0}/src/pglib.h +0 -0
- {pglib-5.10.0 → pglib-5.12.0}/src/pgtypes.h +0 -0
- {pglib-5.10.0 → pglib-5.12.0}/src/resultset.cpp +0 -0
- {pglib-5.10.0 → pglib-5.12.0}/src/resultset.h +0 -0
- {pglib-5.10.0 → pglib-5.12.0}/src/row.cpp +0 -0
- {pglib-5.10.0 → pglib-5.12.0}/src/row.h +0 -0
- {pglib-5.10.0 → pglib-5.12.0}/src/runtime.cpp +0 -0
- {pglib-5.10.0 → pglib-5.12.0}/src/runtime.h +0 -0
- {pglib-5.10.0 → pglib-5.12.0}/src/type_hstore.cpp +0 -0
- {pglib-5.10.0 → pglib-5.12.0}/src/type_hstore.h +0 -0
- {pglib-5.10.0 → pglib-5.12.0}/src/type_json.cpp +0 -0
- {pglib-5.10.0 → pglib-5.12.0}/src/type_json.h +0 -0
- {pglib-5.10.0 → pglib-5.12.0}/src/type_ltree.cpp +0 -0
- {pglib-5.10.0 → pglib-5.12.0}/src/type_ltree.h +0 -0
pglib-5.12.0/PKG-INFO
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pglib
|
|
3
|
+
Version: 5.12.0
|
|
4
|
+
Summary: A PostgreSQL interface
|
|
5
|
+
Author-email: Michael Kleehammer <michael@kleehammer.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://gitlab.com/mkleehammer/pglib
|
|
8
|
+
Project-URL: Repository, https://gitlab.com/mkleehammer/pglib
|
|
9
|
+
Keywords: postgresql,postgres
|
|
10
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Topic :: Database
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
18
|
+
Requires-Python: >=3.10
|
|
19
|
+
Description-Content-Type: text/x-rst
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Provides-Extra: test
|
|
22
|
+
Requires-Dist: pytest; extra == "test"
|
|
23
|
+
Dynamic: license-file
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
pglib is a Python 3.10+ module for working with PostgreSQL databases. It is a C extension that
|
|
27
|
+
exposes the `libpq <http://www.postgresql.org/docs/current/static/libpq.html>`_ API. It is
|
|
28
|
+
designed to be small, fast, and as convenient as possible. It provides both synchronous and
|
|
29
|
+
asynchronous APIs.
|
|
30
|
+
|
|
31
|
+
Unlike some libraries, it never modifies the SQL you pass. Parameters are passed using the
|
|
32
|
+
official libpq protocol for parameters.
|
|
33
|
+
|
|
34
|
+
Documentation is available in /docs and online at http://pglib.readthedocs.org
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
|
-
pglib is a Python 3.
|
|
3
|
-
exposes the `libpq <http://www.postgresql.org/docs/
|
|
2
|
+
pglib is a Python 3.10+ module for working with PostgreSQL databases. It is a C extension that
|
|
3
|
+
exposes the `libpq <http://www.postgresql.org/docs/current/static/libpq.html>`_ API. It is
|
|
4
4
|
designed to be small, fast, and as convenient as possible. It provides both synchronous and
|
|
5
5
|
asynchronous APIs.
|
|
6
6
|
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pglib
|
|
3
|
+
Version: 5.12.0
|
|
4
|
+
Summary: A PostgreSQL interface
|
|
5
|
+
Author-email: Michael Kleehammer <michael@kleehammer.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://gitlab.com/mkleehammer/pglib
|
|
8
|
+
Project-URL: Repository, https://gitlab.com/mkleehammer/pglib
|
|
9
|
+
Keywords: postgresql,postgres
|
|
10
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Topic :: Database
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
18
|
+
Requires-Python: >=3.10
|
|
19
|
+
Description-Content-Type: text/x-rst
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Provides-Extra: test
|
|
22
|
+
Requires-Dist: pytest; extra == "test"
|
|
23
|
+
Dynamic: license-file
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
pglib is a Python 3.10+ module for working with PostgreSQL databases. It is a C extension that
|
|
27
|
+
exposes the `libpq <http://www.postgresql.org/docs/current/static/libpq.html>`_ API. It is
|
|
28
|
+
designed to be small, fast, and as convenient as possible. It provides both synchronous and
|
|
29
|
+
asynchronous APIs.
|
|
30
|
+
|
|
31
|
+
Unlike some libraries, it never modifies the SQL you pass. Parameters are passed using the
|
|
32
|
+
official libpq protocol for parameters.
|
|
33
|
+
|
|
34
|
+
Documentation is available in /docs and online at http://pglib.readthedocs.org
|
|
@@ -1,30 +1,14 @@
|
|
|
1
1
|
LICENSE
|
|
2
2
|
MANIFEST.in
|
|
3
3
|
README.rst
|
|
4
|
-
|
|
4
|
+
pyproject.toml
|
|
5
5
|
setup.py
|
|
6
|
-
/home/mkleehammer/dev/pglib/src/connection.cpp
|
|
7
|
-
/home/mkleehammer/dev/pglib/src/conninfoopt.cpp
|
|
8
|
-
/home/mkleehammer/dev/pglib/src/datatypes.cpp
|
|
9
|
-
/home/mkleehammer/dev/pglib/src/debug.cpp
|
|
10
|
-
/home/mkleehammer/dev/pglib/src/enums.cpp
|
|
11
|
-
/home/mkleehammer/dev/pglib/src/errors.cpp
|
|
12
|
-
/home/mkleehammer/dev/pglib/src/getdata.cpp
|
|
13
|
-
/home/mkleehammer/dev/pglib/src/juliandate.cpp
|
|
14
|
-
/home/mkleehammer/dev/pglib/src/params.cpp
|
|
15
|
-
/home/mkleehammer/dev/pglib/src/pgarrays.cpp
|
|
16
|
-
/home/mkleehammer/dev/pglib/src/pglib.cpp
|
|
17
|
-
/home/mkleehammer/dev/pglib/src/resultset.cpp
|
|
18
|
-
/home/mkleehammer/dev/pglib/src/row.cpp
|
|
19
|
-
/home/mkleehammer/dev/pglib/src/runtime.cpp
|
|
20
|
-
/home/mkleehammer/dev/pglib/src/type_hstore.cpp
|
|
21
|
-
/home/mkleehammer/dev/pglib/src/type_json.cpp
|
|
22
|
-
/home/mkleehammer/dev/pglib/src/type_ltree.cpp
|
|
23
6
|
pglib/__init__.py
|
|
24
7
|
pglib/asyncpglib.py
|
|
25
8
|
pglib.egg-info/PKG-INFO
|
|
26
9
|
pglib.egg-info/SOURCES.txt
|
|
27
10
|
pglib.egg-info/dependency_links.txt
|
|
11
|
+
pglib.egg-info/requires.txt
|
|
28
12
|
pglib.egg-info/top_level.txt
|
|
29
13
|
src/byteswap.h
|
|
30
14
|
src/connection.cpp
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "pglib"
|
|
7
|
+
version = "5.12.0"
|
|
8
|
+
description = "A PostgreSQL interface"
|
|
9
|
+
readme = "README.rst"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
license-files = ["LICENSE"]
|
|
12
|
+
requires-python = ">=3.10"
|
|
13
|
+
authors = [
|
|
14
|
+
{name = "Michael Kleehammer", email = "michael@kleehammer.com"}
|
|
15
|
+
]
|
|
16
|
+
keywords = ["postgresql", "postgres"]
|
|
17
|
+
classifiers = [
|
|
18
|
+
"Development Status :: 5 - Production/Stable",
|
|
19
|
+
"Intended Audience :: Developers",
|
|
20
|
+
"Topic :: Database",
|
|
21
|
+
"Programming Language :: Python :: 3",
|
|
22
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
23
|
+
"Programming Language :: Python :: 3.10",
|
|
24
|
+
"Programming Language :: Python :: 3.12",
|
|
25
|
+
"Programming Language :: Python :: 3.14",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
[project.urls]
|
|
29
|
+
Homepage = "https://gitlab.com/mkleehammer/pglib"
|
|
30
|
+
Repository = "https://gitlab.com/mkleehammer/pglib"
|
|
31
|
+
|
|
32
|
+
[project.optional-dependencies]
|
|
33
|
+
test = ["pytest"]
|
|
34
|
+
|
|
35
|
+
[tool.setuptools.packages.find]
|
|
36
|
+
where = ["."]
|
|
37
|
+
include = ["pglib*"]
|
|
38
|
+
|
|
39
|
+
[tool.pytest.ini_options]
|
|
40
|
+
testpaths = ["test"]
|
pglib-5.12.0/setup.cfg
ADDED
|
@@ -1,36 +1,24 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
|
|
3
|
-
import sys
|
|
4
|
-
import
|
|
3
|
+
import sys
|
|
4
|
+
import os
|
|
5
|
+
import re
|
|
6
|
+
import subprocess
|
|
7
|
+
import sysconfig
|
|
5
8
|
from os.path import join, abspath, dirname
|
|
6
9
|
|
|
7
|
-
|
|
8
|
-
from setuptools import setup, Extension
|
|
9
|
-
except:
|
|
10
|
-
from distutils.core import setup, Extension
|
|
11
|
-
|
|
12
|
-
long_description = """\
|
|
13
|
-
A PostgreSQL interface for Python.
|
|
14
|
-
|
|
15
|
-
This provides an interface to the libpq library. It is not an DB API
|
|
16
|
-
library, but is instead designed to match the interface PostgreSQL
|
|
17
|
-
offers.
|
|
18
|
-
"""
|
|
10
|
+
from setuptools import setup, Extension
|
|
19
11
|
|
|
20
12
|
|
|
21
13
|
def _get_osx_sdkpath():
|
|
22
14
|
"""
|
|
23
15
|
Use xcodebuild to find the latest installed OS X SDK and return the path to it.
|
|
24
16
|
"""
|
|
25
|
-
# The output is in blank line separated sections. Find the section for the latest OS/X SDK
|
|
26
|
-
# and get the Path entry.
|
|
27
|
-
|
|
28
17
|
output = subprocess.check_output(['xcodebuild', '-version', '-sdk']).strip().decode('utf-8')
|
|
29
18
|
|
|
30
19
|
highest = (0, 0)
|
|
31
|
-
path
|
|
20
|
+
path = None
|
|
32
21
|
|
|
33
|
-
# MacOSX10.8.sdk - OS X 10.8 (macosx10.8)
|
|
34
22
|
resection = re.compile(r'^.*- (?:OS X|macOS) (\d+)\.(\d+)')
|
|
35
23
|
repath = re.compile('^Path: ([^\n]+)', re.MULTILINE)
|
|
36
24
|
|
|
@@ -50,27 +38,14 @@ def _get_osx_sdkpath():
|
|
|
50
38
|
return path
|
|
51
39
|
|
|
52
40
|
|
|
53
|
-
def getoutput(cmd):
|
|
54
|
-
pipe = os.popen(cmd, 'r')
|
|
55
|
-
text = pipe.read().rstrip('\n')
|
|
56
|
-
status = pipe.close() or 0
|
|
57
|
-
return status, text
|
|
58
|
-
|
|
59
|
-
|
|
60
41
|
def _get_files():
|
|
61
|
-
return [
|
|
42
|
+
return [join('src', f) for f in os.listdir('src') if f.endswith('.cpp')]
|
|
62
43
|
|
|
63
44
|
|
|
64
45
|
def _get_settings():
|
|
65
|
-
|
|
66
|
-
# version = get_version()
|
|
67
|
-
#
|
|
68
|
-
# settings = { 'define_macros' : [ ('PGLIB_VERSION', version) ] }
|
|
69
|
-
|
|
70
46
|
settings = {'define_macros': []}
|
|
71
47
|
|
|
72
|
-
#
|
|
73
|
-
# command.
|
|
48
|
+
# Custom build options
|
|
74
49
|
for option in ['assert', 'trace', 'leak-check']:
|
|
75
50
|
try:
|
|
76
51
|
sys.argv.remove('--%s' % option)
|
|
@@ -90,17 +65,12 @@ def _get_settings():
|
|
|
90
65
|
]
|
|
91
66
|
|
|
92
67
|
if '--debug' in sys.argv:
|
|
93
|
-
# TODO: The build command already has debug. Pass it in.
|
|
94
68
|
sys.argv.remove('--debug')
|
|
95
69
|
settings['extra_compile_args'].extend('/Od /Ge /GS /GZ /RTC1 /Wp64 /Yd'.split())
|
|
96
70
|
|
|
97
71
|
settings['libraries'] = ['libpq', 'Ws2_32']
|
|
98
72
|
|
|
99
73
|
elif sys.platform == 'darwin':
|
|
100
|
-
# Apple is not making it easy for non-Xcode builds. We'll always build with the latest
|
|
101
|
-
# SDK we can find but we'll set the version we are targeting to the same one that
|
|
102
|
-
# Python was built with.
|
|
103
|
-
|
|
104
74
|
sdkpath = _get_osx_sdkpath()
|
|
105
75
|
|
|
106
76
|
settings['include_dirs'] = [
|
|
@@ -109,7 +79,7 @@ def _get_settings():
|
|
|
109
79
|
subprocess.check_output(['pg_config', '--includedir']).strip().decode('utf-8')
|
|
110
80
|
]
|
|
111
81
|
settings['library_dirs'] = [subprocess.check_output(['pg_config', '--libdir']).strip().decode('utf-8')]
|
|
112
|
-
settings['libraries']
|
|
82
|
+
settings['libraries'] = ['pq']
|
|
113
83
|
|
|
114
84
|
settings['define_macros'].append(('MAC_OS_X_VERSION_10_7',))
|
|
115
85
|
|
|
@@ -117,37 +87,15 @@ def _get_settings():
|
|
|
117
87
|
|
|
118
88
|
else:
|
|
119
89
|
# Other posix-like: Linux, Solaris, etc.
|
|
120
|
-
|
|
121
90
|
settings['include_dirs'] = [subprocess.check_output(['pg_config', '--includedir']).strip().decode('utf-8')]
|
|
122
91
|
settings['library_dirs'] = [subprocess.check_output(['pg_config', '--libdir']).strip().decode('utf-8')]
|
|
123
|
-
settings['libraries']
|
|
92
|
+
settings['libraries'] = ['pq']
|
|
124
93
|
|
|
125
|
-
# Python functions take a lot of 'char *' that really should be const. gcc complains about this *a lot*
|
|
126
94
|
settings['extra_compile_args'] = ['-Wno-write-strings']
|
|
127
95
|
|
|
128
96
|
return settings
|
|
129
97
|
|
|
130
98
|
|
|
131
99
|
setup(
|
|
132
|
-
name='pglib',
|
|
133
|
-
version='5.10.0',
|
|
134
|
-
description='A PostgreSQL interface',
|
|
135
|
-
long_description=long_description,
|
|
136
|
-
maintainer='Michael Kleehammer',
|
|
137
|
-
maintainer_email='michael@kleehammer.com',
|
|
138
|
-
packages=['pglib'],
|
|
139
100
|
ext_modules=[Extension('_pglib', _get_files(), **_get_settings())],
|
|
140
|
-
keywords='postgresql postgres',
|
|
141
|
-
tests_require=['pytest'],
|
|
142
|
-
url='https://gitlab.com/mkleehammer/pglib',
|
|
143
|
-
license='MIT',
|
|
144
|
-
classifiers=[
|
|
145
|
-
# https://pypi.python.org/pypi?%3Aaction=list_classifiers
|
|
146
|
-
'Development Status :: 5 - Production/Stable',
|
|
147
|
-
'Intended Audience :: Developers',
|
|
148
|
-
'Topic :: Database',
|
|
149
|
-
'License :: OSI Approved :: MIT License',
|
|
150
|
-
'Programming Language :: Python :: 3',
|
|
151
|
-
'Programming Language :: Python :: 3 :: Only'
|
|
152
|
-
]
|
|
153
101
|
)
|
|
@@ -365,6 +365,113 @@ static PyObject* Connection_copy_from(PyObject* self, PyObject* args)
|
|
|
365
365
|
}
|
|
366
366
|
|
|
367
367
|
|
|
368
|
+
const char* doc_copy_to =
|
|
369
|
+
"Connection.copy_to(command, dest) --> int\n"
|
|
370
|
+
"\n"
|
|
371
|
+
"Executes the given COPY TO command and returns the number of records copied.\n"
|
|
372
|
+
"\n"
|
|
373
|
+
"command\n"
|
|
374
|
+
" The copy command which must be 'to stdout'.\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
|
+
"Examples:\n"
|
|
381
|
+
" cnxn.copy_to('copy t1 to stdout csv header', open('test.csv', 'w'))\n"
|
|
382
|
+
" cnxn.copy_to('copy t1(a, b, c) to stdout csv header', open('test.csv', 'w'))\n"
|
|
383
|
+
" cnxn.copy_to('copy (select * from t1 where id > 10) to stdout csv', f)\n";
|
|
384
|
+
|
|
385
|
+
static PyObject* Connection_copy_to(PyObject* self, PyObject* args)
|
|
386
|
+
{
|
|
387
|
+
PyObject* command;
|
|
388
|
+
PyObject* dest;
|
|
389
|
+
if (!PyArg_ParseTuple(args, "UO", &command, &dest))
|
|
390
|
+
return 0;
|
|
391
|
+
|
|
392
|
+
Connection* cnxn = CastConnection(self, REQUIRE_OPEN);
|
|
393
|
+
if (!cnxn)
|
|
394
|
+
return 0;
|
|
395
|
+
|
|
396
|
+
if (!PyObject_HasAttrString(dest, "write"))
|
|
397
|
+
return PyErr_Format(Error, "Destination must be a file-like object.");
|
|
398
|
+
Object write_method(PyObject_GetAttrString(dest, "write"));
|
|
399
|
+
|
|
400
|
+
const char* szSQL = PyUnicode_AsUTF8(command);
|
|
401
|
+
if (!szSQL)
|
|
402
|
+
return 0;
|
|
403
|
+
|
|
404
|
+
ResultHolder result;
|
|
405
|
+
Py_BEGIN_ALLOW_THREADS
|
|
406
|
+
result = PQexec(cnxn->pgconn, szSQL);
|
|
407
|
+
Py_END_ALLOW_THREADS
|
|
408
|
+
|
|
409
|
+
if (result == 0)
|
|
410
|
+
return 0;
|
|
411
|
+
|
|
412
|
+
switch (PQresultStatus(result)) {
|
|
413
|
+
case PGRES_COPY_OUT:
|
|
414
|
+
// This is what we are expecting.
|
|
415
|
+
break;
|
|
416
|
+
|
|
417
|
+
case PGRES_BAD_RESPONSE:
|
|
418
|
+
case PGRES_NONFATAL_ERROR:
|
|
419
|
+
case PGRES_FATAL_ERROR:
|
|
420
|
+
return SetResultError(result.Detach());
|
|
421
|
+
|
|
422
|
+
default:
|
|
423
|
+
return PyErr_Format(Error, "Result was not PGRES_COPY_OUT: %d", (int)PQresultStatus(result));
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
for (;;) {
|
|
428
|
+
int cb = 0;
|
|
429
|
+
char* buffer;
|
|
430
|
+
Py_BEGIN_ALLOW_THREADS
|
|
431
|
+
cb = PQgetCopyData(cnxn->pgconn, &buffer, 0);
|
|
432
|
+
Py_END_ALLOW_THREADS
|
|
433
|
+
|
|
434
|
+
if (cb == -2) {
|
|
435
|
+
return SetResultError(result.Detach());
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
if (cb == -1) {
|
|
439
|
+
// The copy is complete.
|
|
440
|
+
break;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// We have a buffer of byte data. We have the length (`cb`), but the libpq docs say
|
|
444
|
+
// that the string is also zero terminated, so we're going to try not calling 'write'.
|
|
445
|
+
|
|
446
|
+
int err = PyFile_WriteString(buffer, dest);
|
|
447
|
+
|
|
448
|
+
PQfreemem(buffer);
|
|
449
|
+
if (err) {
|
|
450
|
+
return 0;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// After a copy, you have to get another result to know if it was successful.
|
|
455
|
+
|
|
456
|
+
ResultHolder final_result;
|
|
457
|
+
ExecStatusType status = PGRES_COMMAND_OK;
|
|
458
|
+
Py_BEGIN_ALLOW_THREADS
|
|
459
|
+
final_result = PQgetResult(cnxn->pgconn);
|
|
460
|
+
status = PQresultStatus(final_result);
|
|
461
|
+
Py_END_ALLOW_THREADS
|
|
462
|
+
|
|
463
|
+
if (status != PGRES_COMMAND_OK) {
|
|
464
|
+
// SetResultError will take ownership of `result`.
|
|
465
|
+
return SetResultError(final_result.Detach());
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
const char* sz = PQcmdTuples(final_result);
|
|
469
|
+
if (sz == 0 || *sz == 0)
|
|
470
|
+
Py_RETURN_NONE;
|
|
471
|
+
return PyLong_FromLong(atoi(sz));
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
|
|
368
475
|
const char* doc_copy_to_csv =
|
|
369
476
|
"Connection.copy_to_csv(table, dest, header=0, delimiter=',', quote='\"')\n"
|
|
370
477
|
"\n"
|
|
@@ -1631,6 +1738,7 @@ static struct PyMethodDef Connection_methods[] =
|
|
|
1631
1738
|
{ "script", Connection_script, METH_VARARGS, doc_script },
|
|
1632
1739
|
{ "copy_from", (PyCFunction) Connection_copy_from, METH_VARARGS | METH_KEYWORDS, doc_copy_from },
|
|
1633
1740
|
{ "copy_from_csv", (PyCFunction) Connection_copy_from_csv, METH_VARARGS | METH_KEYWORDS, doc_copy_from_csv },
|
|
1741
|
+
{ "copy_to", (PyCFunction) Connection_copy_to, METH_VARARGS, doc_copy_to },
|
|
1634
1742
|
{ "copy_to_csv", (PyCFunction) Connection_copy_to_csv, METH_VARARGS | METH_KEYWORDS, doc_copy_to_csv},
|
|
1635
1743
|
{ "begin", Connection_begin, METH_NOARGS, doc_begin },
|
|
1636
1744
|
{ "commit", Connection_commit, METH_NOARGS, doc_commit },
|
|
@@ -44,8 +44,7 @@ def test_async():
|
|
|
44
44
|
assert row.b == 3
|
|
45
45
|
assert row[1] == 3
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
loop.run_until_complete(_t())
|
|
47
|
+
asyncio.run(_t())
|
|
49
48
|
|
|
50
49
|
|
|
51
50
|
def test_async_params():
|
|
@@ -60,8 +59,7 @@ def test_async_params():
|
|
|
60
59
|
assert row.b == 2
|
|
61
60
|
assert row[1] == 2
|
|
62
61
|
|
|
63
|
-
|
|
64
|
-
loop.run_until_complete(_t())
|
|
62
|
+
asyncio.run(_t())
|
|
65
63
|
|
|
66
64
|
|
|
67
65
|
def test_async_notify():
|
|
@@ -84,8 +82,7 @@ def test_async_notify():
|
|
|
84
82
|
assert item == sent[0]
|
|
85
83
|
sent.pop(0)
|
|
86
84
|
|
|
87
|
-
|
|
88
|
-
loop.run_until_complete(_t())
|
|
85
|
+
asyncio.run(_t())
|
|
89
86
|
|
|
90
87
|
|
|
91
88
|
def test_notify_and_command():
|
|
@@ -112,8 +109,7 @@ def test_notify_and_command():
|
|
|
112
109
|
item = ns[0]
|
|
113
110
|
assert item == ('test', 'both')
|
|
114
111
|
|
|
115
|
-
|
|
116
|
-
loop.run_until_complete(_t())
|
|
112
|
+
asyncio.run(_t())
|
|
117
113
|
|
|
118
114
|
|
|
119
115
|
def test_notification_wait():
|
|
@@ -132,9 +128,10 @@ def test_notification_wait():
|
|
|
132
128
|
cnxn = await pglib.connect_async(CONNINFO)
|
|
133
129
|
await cnxn.notify('test')
|
|
134
130
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
131
|
+
async def _run_both():
|
|
132
|
+
await asyncio.gather(_listener(), _notifier())
|
|
133
|
+
|
|
134
|
+
asyncio.run(_run_both())
|
|
138
135
|
|
|
139
136
|
|
|
140
137
|
def test_notification_timeout():
|
|
@@ -144,8 +141,7 @@ def test_notification_timeout():
|
|
|
144
141
|
ns = await cnxn.notifications(timeout=0.25)
|
|
145
142
|
assert ns == []
|
|
146
143
|
|
|
147
|
-
|
|
148
|
-
loop.run_until_complete(_t())
|
|
144
|
+
asyncio.run(_t())
|
|
149
145
|
|
|
150
146
|
|
|
151
147
|
def test_close():
|
|
@@ -156,5 +152,4 @@ def test_close():
|
|
|
156
152
|
cnxn.close()
|
|
157
153
|
cnxn = None
|
|
158
154
|
|
|
159
|
-
|
|
160
|
-
loop.run_until_complete(_t())
|
|
155
|
+
asyncio.run(_t())
|
|
@@ -176,6 +176,39 @@ def test_copytocsv(cnxn):
|
|
|
176
176
|
assert rows == [['a', 'b'], ['1', 'one'], ['2', 'two'], ['3', 'three']]
|
|
177
177
|
|
|
178
178
|
|
|
179
|
+
def test_copy_to(cnxn):
|
|
180
|
+
# Test copy_to with a full command, including a subquery.
|
|
181
|
+
cnxn.execute("create table t1(a int, b text)")
|
|
182
|
+
cnxn.execute("insert into t1 values (1, 'one'), (2, 'two'), (3, 'three')")
|
|
183
|
+
|
|
184
|
+
with tempfile.NamedTemporaryFile(mode='w', encoding='utf8') as tf:
|
|
185
|
+
# Use a subquery to only copy rows where a > 1
|
|
186
|
+
count = cnxn.copy_to('copy (select * from t1 where a > 1 order by a) to stdout csv header', tf)
|
|
187
|
+
assert count == 2
|
|
188
|
+
|
|
189
|
+
tf.flush()
|
|
190
|
+
with open(tf.name, mode='r', encoding='utf8') as fd:
|
|
191
|
+
reader = csv.reader(fd)
|
|
192
|
+
rows = list(reader)
|
|
193
|
+
assert rows == [['a', 'b'], ['2', 'two'], ['3', 'three']]
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def test_copy_to_table(cnxn):
|
|
197
|
+
# Test copy_to with a simple table copy (no subquery).
|
|
198
|
+
cnxn.execute("create table t1(a int, b text)")
|
|
199
|
+
cnxn.execute("insert into t1 values (1, 'one'), (2, 'two')")
|
|
200
|
+
|
|
201
|
+
with tempfile.NamedTemporaryFile(mode='w', encoding='utf8') as tf:
|
|
202
|
+
count = cnxn.copy_to('copy t1 to stdout csv', tf)
|
|
203
|
+
assert count == 2
|
|
204
|
+
|
|
205
|
+
tf.flush()
|
|
206
|
+
with open(tf.name, mode='r', encoding='utf8') as fd:
|
|
207
|
+
reader = csv.reader(fd)
|
|
208
|
+
rows = list(reader)
|
|
209
|
+
assert rows == [['1', 'one'], ['2', 'two']]
|
|
210
|
+
|
|
211
|
+
|
|
179
212
|
#
|
|
180
213
|
# copy from
|
|
181
214
|
#
|
|
@@ -10,21 +10,22 @@ def add_to_path():
|
|
|
10
10
|
it to be tested without installing it.
|
|
11
11
|
"""
|
|
12
12
|
# Put the root directory into the path first so we pick up the Python code from the source
|
|
13
|
-
# directories
|
|
13
|
+
# directories and any extension built via editable install.
|
|
14
14
|
|
|
15
15
|
root = dirname(dirname(abspath(__file__)))
|
|
16
16
|
sys.path.insert(0, root)
|
|
17
17
|
|
|
18
|
-
#
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
# Check if the extension is already importable (installed via pip or editable install)
|
|
19
|
+
try:
|
|
20
|
+
import _pglib
|
|
21
|
+
return
|
|
22
|
+
except ImportError:
|
|
23
|
+
pass
|
|
21
24
|
|
|
25
|
+
# Fall back to searching the build directory for legacy `python setup.py build` workflow.
|
|
22
26
|
prefix = '_pglib'
|
|
23
|
-
|
|
24
27
|
library_names = [prefix + ext for ext in EXTENSION_SUFFIXES]
|
|
25
28
|
|
|
26
|
-
# Only go into directories that match the current Python's version number.
|
|
27
|
-
|
|
28
29
|
dir_suffix = (
|
|
29
30
|
'-%s.%s' % (sys.version_info[0], sys.version_info[1]),
|
|
30
31
|
'-%s%s' % (sys.version_info[0], sys.version_info[1]),
|
|
@@ -33,17 +34,18 @@ def add_to_path():
|
|
|
33
34
|
|
|
34
35
|
build = join(dirname(dirname(abspath(__file__))), 'build')
|
|
35
36
|
|
|
36
|
-
|
|
37
|
-
for
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
if os.path.isdir(build):
|
|
38
|
+
for root, dirs, files in os.walk(build):
|
|
39
|
+
for d in dirs[:]:
|
|
40
|
+
if not d.endswith(dir_suffix):
|
|
41
|
+
dirs.remove(d)
|
|
40
42
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
for name in library_names:
|
|
44
|
+
if name in files:
|
|
45
|
+
sys.path.insert(1, root)
|
|
46
|
+
return
|
|
45
47
|
|
|
46
|
-
sys.exit('Did not find
|
|
48
|
+
sys.exit('Did not find _pglib extension. Run: pip install -e .')
|
|
47
49
|
|
|
48
50
|
|
|
49
51
|
_TESTSTR = '0123456789-abcdefghijklmnopqrstuvwxyz-'
|
pglib-5.10.0/PKG-INFO
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: pglib
|
|
3
|
-
Version: 5.10.0
|
|
4
|
-
Summary: A PostgreSQL interface
|
|
5
|
-
Home-page: https://gitlab.com/mkleehammer/pglib
|
|
6
|
-
Maintainer: Michael Kleehammer
|
|
7
|
-
Maintainer-email: michael@kleehammer.com
|
|
8
|
-
License: MIT
|
|
9
|
-
Keywords: postgresql postgres
|
|
10
|
-
Platform: UNKNOWN
|
|
11
|
-
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
-
Classifier: Intended Audience :: Developers
|
|
13
|
-
Classifier: Topic :: Database
|
|
14
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
-
Classifier: Programming Language :: Python :: 3
|
|
16
|
-
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
-
License-File: LICENSE
|
|
18
|
-
|
|
19
|
-
A PostgreSQL interface for Python.
|
|
20
|
-
|
|
21
|
-
This provides an interface to the libpq library. It is not an DB API
|
|
22
|
-
library, but is instead designed to match the interface PostgreSQL
|
|
23
|
-
offers.
|
|
24
|
-
|
|
25
|
-
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: pglib
|
|
3
|
-
Version: 5.10.0
|
|
4
|
-
Summary: A PostgreSQL interface
|
|
5
|
-
Home-page: https://gitlab.com/mkleehammer/pglib
|
|
6
|
-
Maintainer: Michael Kleehammer
|
|
7
|
-
Maintainer-email: michael@kleehammer.com
|
|
8
|
-
License: MIT
|
|
9
|
-
Keywords: postgresql postgres
|
|
10
|
-
Platform: UNKNOWN
|
|
11
|
-
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
-
Classifier: Intended Audience :: Developers
|
|
13
|
-
Classifier: Topic :: Database
|
|
14
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
-
Classifier: Programming Language :: Python :: 3
|
|
16
|
-
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
-
License-File: LICENSE
|
|
18
|
-
|
|
19
|
-
A PostgreSQL interface for Python.
|
|
20
|
-
|
|
21
|
-
This provides an interface to the libpq library. It is not an DB API
|
|
22
|
-
library, but is instead designed to match the interface PostgreSQL
|
|
23
|
-
offers.
|
|
24
|
-
|
|
25
|
-
|
pglib-5.10.0/setup.cfg
DELETED
|
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
|