File: _build_libpq.py

package info (click to toggle)
python-psycopg2cffi 2.9.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 824 kB
  • sloc: python: 10,828; makefile: 17
file content (490 lines) | stat: -rw-r--r-- 16,758 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
''' CFFI interface to libpq the library '''

from __future__ import print_function

from distutils import sysconfig
import os.path
import re
import sys
import subprocess

from cffi import FFI


PLATFORM_IS_WINDOWS = sys.platform.lower().startswith('win')
LIBRARY_NAME = 'pq' if not PLATFORM_IS_WINDOWS else 'libpq'


class PostgresConfig:

    def __init__(self):
        try:
            from psycopg2cffi import _config
        except ImportError:
            self.pg_config_exe = None
            if not self.pg_config_exe:
                self.pg_config_exe = self.autodetect_pg_config_path()
            if self.pg_config_exe is None:
                # FIXME - do we need some way to set it?
                sys.stderr.write("""\
Error: pg_config executable not found.
Please add the directory containing pg_config to the PATH.
""")
                sys.exit(1)
            self.libpq_include_dir = self.query('includedir') or None
            self.libpq_lib_dir = self.query('libdir') or None
            self.libpq_version = self.find_version()
        else:
            self.libpq_include_dir = _config.PG_INCLUDE_DIR
            self.libpq_lib_dir = _config.PG_LIB_DIR
            self.libpq_version = _config.PG_VERSION

    def query(self, attr_name):
        """Spawn the pg_config executable, querying for the given config
        name, and return the printed value, sanitized. """
        try:
            pg_config_process = subprocess.Popen(
                [self.pg_config_exe, "--" + attr_name],
                stdin=subprocess.PIPE,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE)
        except OSError:
            raise Warning("Unable to find 'pg_config' file in '%s'" %
                          self.pg_config_exe)
        pg_config_process.stdin.close()
        result = pg_config_process.stdout.readline().strip()
        if not result:
            raise Warning(pg_config_process.stderr.readline())
        if not isinstance(result, str):
            result = result.decode('ascii')
        return result

    def find_on_path(self, exename, path_directories=None):
        if not path_directories:
            path_directories = os.environ['PATH'].split(os.pathsep)
        for dir_name in path_directories:
            fullpath = os.path.join(dir_name, exename)
            if os.path.isfile(fullpath):
                return fullpath
        return None

    def autodetect_pg_config_path(self):
        """Find and return the path to the pg_config executable."""
        if PLATFORM_IS_WINDOWS:
            return self.autodetect_pg_config_path_windows()
        else:
            return self.find_on_path('pg_config')

    def autodetect_pg_config_path_windows(self):
        """Attempt several different ways of finding the pg_config
        executable on Windows, and return its full path, if found."""

        # This code only runs if they have not specified a pg_config option
        # in the config file or via the commandline.

        # First, check for pg_config.exe on the PATH, and use that if found.
        pg_config_exe = self.find_on_path('pg_config.exe')
        if pg_config_exe:
            return pg_config_exe

        # Now, try looking in the Windows Registry to find a PostgreSQL
        # installation, and infer the path from that.
        pg_config_exe = self._get_pg_config_from_registry()
        if pg_config_exe:
            return pg_config_exe

        return None

    def _get_pg_config_from_registry(self):
        try:
            import winreg
        except ImportError:
            import _winreg as winreg

        reg = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE)
        try:
            pg_inst_list_key = winreg.OpenKey(reg,
                'SOFTWARE\\PostgreSQL\\Installations')
        except EnvironmentError:
            # No PostgreSQL installation, as best as we can tell.
            return None

        try:
            # Determine the name of the first subkey, if any:
            try:
                first_sub_key_name = winreg.EnumKey(pg_inst_list_key, 0)
            except EnvironmentError:
                return None

            pg_first_inst_key = winreg.OpenKey(reg,
                'SOFTWARE\\PostgreSQL\\Installations\\'
                + first_sub_key_name)
            try:
                pg_inst_base_dir = winreg.QueryValueEx(
                    pg_first_inst_key, 'Base Directory')[0]
            finally:
                winreg.CloseKey(pg_first_inst_key)

        finally:
            winreg.CloseKey(pg_inst_list_key)

        pg_config_path = os.path.join(
            pg_inst_base_dir, 'bin', 'pg_config.exe')
        if not os.path.exists(pg_config_path):
            return None

        # Support unicode paths, if this version of Python provides the
        # necessary infrastructure:
        if sys.version_info[0] < 3 \
                and hasattr(sys, 'getfilesystemencoding'):
            pg_config_path = pg_config_path.encode(
                sys.getfilesystemencoding())

        return pg_config_path

    def find_version(self):
        try:
            # Here we take a conservative approach: we suppose that
            # *at least* PostgreSQL 7.4 is available (this is the only
            # 7.x series supported by psycopg 2)
            pgversion = self.query('version').split()[1]
        except:
            pgversion = '7.4.0'

        verre = re.compile(
            r'(\d+)\.(\d+)(?:(?:\.(\d+))|(devel|(alpha|beta|rc)\d+)?)')
        m = verre.match(pgversion)
        if m:
            pgmajor, pgminor, pgpatch = m.group(1, 2, 3)
            if pgpatch is None or not pgpatch.isdigit():
                pgpatch = 0
        else:
            sys.stderr.write(
                "Error: could not determine PostgreSQL version from '%s'"
                % pgversion)
            sys.exit(1)

        return int(
            '%02X%02X%02X' % (int(pgmajor), int(pgminor), int(pgpatch)), 16)


_config = PostgresConfig()

ffi = FFI()

# order and comments taken from libpq (ctypes impl)

ffi.cdef('''

static int const _PG_VERSION;

// postgres_ext.h

typedef unsigned int Oid;

// See comment below.

static int const LIBPQ_DIAG_SEVERITY;
static int const LIBPQ_DIAG_SQLSTATE;
static int const LIBPQ_DIAG_MESSAGE_PRIMARY;
static int const LIBPQ_DIAG_MESSAGE_DETAIL;
static int const LIBPQ_DIAG_MESSAGE_HINT;
static int const LIBPQ_DIAG_STATEMENT_POSITION;
static int const LIBPQ_DIAG_INTERNAL_POSITION;
static int const LIBPQ_DIAG_INTERNAL_QUERY;
static int const LIBPQ_DIAG_CONTEXT;
static int const LIBPQ_DIAG_SOURCE_FILE;
static int const LIBPQ_DIAG_SCHEMA_NAME;
static int const LIBPQ_DIAG_TABLE_NAME;
static int const LIBPQ_DIAG_COLUMN_NAME;
static int const LIBPQ_DIAG_DATATYPE_NAME ;
static int const LIBPQ_DIAG_CONSTRAINT_NAME;
static int const LIBPQ_DIAG_SOURCE_LINE;
static int const LIBPQ_DIAG_SOURCE_FUNCTION;

// libpq-fe.h

typedef enum
{
 /*
  * Although it is okay to add to this list, values which become unused
  * should never be removed, nor should constants be redefined - that would
  * break compatibility with existing code.
  */
 CONNECTION_OK,
 CONNECTION_BAD,
 /* Non-blocking mode only below here */

 /*
  * The existence of these should never be relied upon - they should only
  * be used for user feedback or similar purposes.
  */
 CONNECTION_STARTED,   /* Waiting for connection to be made.  */
 CONNECTION_MADE,   /* Connection OK; waiting to send.    */
 CONNECTION_AWAITING_RESPONSE,  /* Waiting for a response from the
           * postmaster.    */
 CONNECTION_AUTH_OK,   /* Received authentication; waiting for
         * backend startup. */
 CONNECTION_SETENV,   /* Negotiating environment. */
 CONNECTION_SSL_STARTUP,  /* Negotiating SSL. */
 CONNECTION_NEEDED   /* Internal state: connect() needed */
} ConnStatusType;

typedef enum
{
 PGRES_POLLING_FAILED = 0,
 PGRES_POLLING_READING,  /* These two indicate that one may   */
 PGRES_POLLING_WRITING,  /* use select before polling again.   */
 PGRES_POLLING_OK,
 PGRES_POLLING_ACTIVE  /* unused; keep for awhile for backwards
         * compatibility */
} PostgresPollingStatusType;

typedef enum
{
 PGRES_EMPTY_QUERY = 0,  /* empty query string was executed */
 PGRES_COMMAND_OK,   /* a query command that doesn't return
         * anything was executed properly by the
         * backend */
 PGRES_TUPLES_OK,   /* a query command that returns tuples was
         * executed properly by the backend, PGresult
         * contains the result tuples */
 PGRES_COPY_OUT,    /* Copy Out data transfer in progress */
 PGRES_COPY_IN,    /* Copy In data transfer in progress */
 PGRES_BAD_RESPONSE,   /* an unexpected response was recv'd from the
         * backend */
 PGRES_NONFATAL_ERROR,  /* notice or warning message */
 PGRES_FATAL_ERROR,   /* query failed */
} ExecStatusType;

typedef enum
{
 PQTRANS_IDLE,    /* connection idle */
 PQTRANS_ACTIVE,    /* command in progress */
 PQTRANS_INTRANS,   /* idle, within transaction block */
 PQTRANS_INERROR,   /* idle, within failed transaction */
 PQTRANS_UNKNOWN    /* cannot determine status */
} PGTransactionStatusType;

typedef ... PGconn;
typedef ... PGresult;
typedef ... PGcancel;

typedef struct pgNotify
{
    char       *relname;  /* notification condition name */
    int        be_pid;    /* process ID of notifying server process */
    char       *extra;    /* notification parameter */
    ...;
} PGnotify;

// Database connection control functions

extern PGconn *PQconnectdb(const char *conninfo);
extern PGconn *PQconnectStart(const char *conninfo);
extern /*PostgresPollingStatusType*/ int PQconnectPoll(PGconn *conn);
extern void PQfinish(PGconn *conn);

// Connection status functions

extern /*ConnStatusType*/ int PQstatus(const PGconn *conn);
extern /*PGTransactionStatusType*/ int PQtransactionStatus(const PGconn *conn);
extern const char *PQparameterStatus(const PGconn *conn, const char *paramName);
extern int PQprotocolVersion(const PGconn *conn);
extern int PQserverVersion(const PGconn *conn);
extern char *PQerrorMessage(const PGconn *conn);
extern int PQsocket(const PGconn *conn);
extern int PQbackendPID(const PGconn *conn);

// Command execution functions

extern PGresult *PQexec(PGconn *conn, const char *query);
extern /*ExecStatusType*/ int PQresultStatus(const PGresult *res);
extern char *PQresultErrorMessage(const PGresult *res);
extern char *PQresultErrorField(const PGresult *res, int fieldcode);
extern void PQclear(PGresult *res);

// Retrieving query result information

extern int PQntuples(const PGresult *res);
extern int PQnfields(const PGresult *res);
extern char *PQfname(const PGresult *res, int field_num);
extern Oid PQftype(const PGresult *res, int field_num);
extern int PQfsize(const PGresult *res, int field_num);
extern int PQfmod(const PGresult *res, int field_num);
extern int PQgetisnull(const PGresult *res, int tup_num, int field_num);
extern int PQgetlength(const PGresult *res, int tup_num, int field_num);
extern char *PQgetvalue(const PGresult *res, int tup_num, int field_num);

// direct parsers - not part of libpq

int PQEgetlong(int64_t *val, const PGresult *res, int tup_num, int field_num);
int PQEgetint(int32_t *val, const PGresult *res, int tup_num, int field_num);
int PQEgetfloat(float *val, const PGresult *res, int tup_num, int field_num);
int PQEgetdouble(double *val, const PGresult *res, int tup_num, int field_num);

// Retrieving other result information

extern char *PQcmdStatus(PGresult *res);
extern char *PQcmdTuples(PGresult *res);
extern Oid PQoidValue(const PGresult *res); /* new and improved */

''')

if _config.libpq_version >= 0x090000:
    ffi.cdef('''
// Escaping string for inclusion in sql commands
extern char *PQescapeLiteral(PGconn *conn, const char *str, size_t len);
    ''')

ffi.cdef('''
// Escaping string for inclusion in sql commands
extern size_t PQescapeStringConn(PGconn *conn,
    char *to, const char *from, size_t length,
    int *error);
extern size_t PQescapeString(char *to, const char *from, size_t length);
extern unsigned char *PQescapeByteaConn(PGconn *conn,
    const unsigned char *from, size_t from_length,
    size_t *to_length);
extern unsigned char *PQescapeBytea(const unsigned char *from, size_t from_length,
    size_t *to_length);
extern unsigned char *PQunescapeBytea(const unsigned char *strtext,
    size_t *retbuflen);

// Asynchronous Command Processing

extern int PQsendQuery(PGconn *conn, const char *query);
extern PGresult *PQgetResult(PGconn *conn);
extern int PQconsumeInput(PGconn *conn);
extern int PQisBusy(PGconn *conn);
extern int PQsetnonblocking(PGconn *conn, int arg);
extern int PQflush(PGconn *conn);

// Cancelling queries in progress

extern PGcancel *PQgetCancel(PGconn *conn);
extern void PQfreeCancel(PGcancel *cancel);
extern int PQcancel(PGcancel *cancel, char *errbuf, int errbufsize);
extern int PQrequestCancel(PGconn *conn);

// Functions Associated with the COPY Command

extern int PQgetCopyData(PGconn *conn, char **buffer, int async);
extern int PQputCopyEnd(PGconn *conn, const char *errormsg);
extern int PQputCopyData(PGconn *conn, const char *buffer, int nbytes);

// Miscellaneous functions

extern void PQfreemem(void *ptr);

// Notice processing

typedef void (*PQnoticeProcessor) (void *arg, const char *message);
extern PQnoticeProcessor PQsetNoticeProcessor(PGconn *conn,
    PQnoticeProcessor proc,
    void *arg);
extern PGnotify *PQnotifies(PGconn *conn);

// Large object
extern int lo_open(PGconn *conn, Oid lobjId, int mode);
extern Oid lo_create(PGconn *conn, Oid lobjId);
extern Oid lo_import(PGconn *conn, const char *filename);
extern int lo_read(PGconn *conn, int fd, char *buf, size_t len);
extern int lo_write(PGconn *conn, int fd, const char *buf, size_t len);
extern int lo_tell(PGconn *conn, int fd);
extern int lo_lseek(PGconn *conn, int fd, int offset, int whence);
extern int lo_close(PGconn *conn, int fd);
extern int lo_unlink(PGconn *conn, Oid lobjId);
extern int lo_export(PGconn *conn, Oid lobjId, const char *filename);
extern int lo_truncate(PGconn *conn, int fd, size_t len);

''')



C_SOURCE = '''
#if (defined(_MSC_VER) && _MSC_VER < 1600)
    typedef __int32  int32_t;
    typedef __int64  int64_t;
#else
    #include <stdint.h>
#endif
#include <postgres_ext.h>
#include <libpq-fe.h>

int PQEgetlong(int64_t *raw_res, const PGresult *res, int tup_num, int field_num) {
    char *val = PQgetvalue(res, tup_num, field_num);
    if (!val) return -1;
    sscanf(val, "%ld", (long *)raw_res);
    return 0;
}

int PQEgetint(int32_t *raw_res, const PGresult *res, int tup_num, int field_num) {
    char *val = PQgetvalue(res, tup_num, field_num);
    if (!val) return -1;
    sscanf(val, "%d", (int *)raw_res);
    return 0;
}

int PQEgetfloat(float *raw_res, const PGresult *res, int tup_num, int field_num) {
    char *val = PQgetvalue(res, tup_num, field_num);
    if (!val) return -1;
    sscanf(val, "%f", raw_res);
    return 0;
}

int PQEgetdouble(double *raw_res, const PGresult *res, int tup_num, int field_num) {
    char *val = PQgetvalue(res, tup_num, field_num);
    if (!val) return -1;
    sscanf(val, "%lf", raw_res);
    return 0;
}

// Real names start with PG_DIAG_, but here we define our prefixes,
// because some are defined and some are not depending on pg version.

static int const LIBPQ_DIAG_SEVERITY = 'S';
static int const LIBPQ_DIAG_SQLSTATE = 'C';
static int const LIBPQ_DIAG_MESSAGE_PRIMARY = 'M';
static int const LIBPQ_DIAG_MESSAGE_DETAIL = 'D';
static int const LIBPQ_DIAG_MESSAGE_HINT = 'H';
static int const LIBPQ_DIAG_STATEMENT_POSITION = 'P';
static int const LIBPQ_DIAG_INTERNAL_POSITION = 'p';
static int const LIBPQ_DIAG_INTERNAL_QUERY = 'q';
static int const LIBPQ_DIAG_CONTEXT = 'W';
static int const LIBPQ_DIAG_SCHEMA_NAME = 's';
static int const LIBPQ_DIAG_TABLE_NAME = 't';
static int const LIBPQ_DIAG_COLUMN_NAME = 'c';
static int const LIBPQ_DIAG_DATATYPE_NAME  = 'd';
static int const LIBPQ_DIAG_CONSTRAINT_NAME = 'n';
static int const LIBPQ_DIAG_SOURCE_FILE = 'F';
static int const LIBPQ_DIAG_SOURCE_LINE = 'L';
static int const LIBPQ_DIAG_SOURCE_FUNCTION = 'R';
''' + '''

static int const _PG_VERSION = {libpq_version};
'''.format(libpq_version=_config.libpq_version)


_or_empty = lambda x: [x] if x else []


C_SOURCE_KWARGS = dict(
    libraries=[LIBRARY_NAME],
    library_dirs=(
        _or_empty(sysconfig.get_config_var('LIBDIR')) +
        _or_empty(_config.libpq_lib_dir)
        ),
    include_dirs=(
        _or_empty(sysconfig.get_python_inc()) +
        _or_empty(_config.libpq_include_dir)
        )
    )


if hasattr(ffi, 'set_source'):
    ffi.set_source('psycopg2cffi._impl._libpq', C_SOURCE, **C_SOURCE_KWARGS)


if __name__ == '__main__':
    ffi.compile()