1"""
2libpq access using ctypes
3"""
4
5# Copyright (C) 2020-2021 The Psycopg Team
6
7import ctypes
8import ctypes.util
9import sys
10from ctypes import Structure, CFUNCTYPE, POINTER
11from ctypes import c_char, c_char_p, c_int, c_size_t, c_ubyte, c_uint, c_void_p
12from typing import List, Optional, Tuple
13
14from ..errors import NotSupportedError
15
16if sys.platform == "win32":
17    libname = ctypes.util.find_library("libpq.dll")
18elif sys.platform == "darwin":
19    libname = ctypes.util.find_library("libpq.dylib")
20else:
21    libname = ctypes.util.find_library("pq")
22if not libname:
23    raise ImportError("libpq library not found")
24
25pq = ctypes.cdll.LoadLibrary(libname)
26
27# Get the libpq version to define what functions are available.
28
29PQlibVersion = pq.PQlibVersion
30PQlibVersion.argtypes = []
31PQlibVersion.restype = c_int
32
33libpq_version = PQlibVersion()
34
35
36# libpq data types
37
38
39Oid = c_uint
40
41
42class PGconn_struct(Structure):
43    _fields_: List[Tuple[str, type]] = []
44
45
46class PGresult_struct(Structure):
47    _fields_: List[Tuple[str, type]] = []
48
49
50class PQconninfoOption_struct(Structure):
51    _fields_ = [
52        ("keyword", c_char_p),
53        ("envvar", c_char_p),
54        ("compiled", c_char_p),
55        ("val", c_char_p),
56        ("label", c_char_p),
57        ("dispchar", c_char_p),
58        ("dispsize", c_int),
59    ]
60
61
62class PGnotify_struct(Structure):
63    _fields_ = [
64        ("relname", c_char_p),
65        ("be_pid", c_int),
66        ("extra", c_char_p),
67    ]
68
69
70class PGcancel_struct(Structure):
71    _fields_: List[Tuple[str, type]] = []
72
73
74class PGresAttDesc_struct(Structure):
75    _fields_ = [
76        ("name", c_char_p),
77        ("tableid", Oid),
78        ("columnid", c_int),
79        ("format", c_int),
80        ("typid", Oid),
81        ("typlen", c_int),
82        ("atttypmod", c_int),
83    ]
84
85
86PGconn_ptr = POINTER(PGconn_struct)
87PGresult_ptr = POINTER(PGresult_struct)
88PQconninfoOption_ptr = POINTER(PQconninfoOption_struct)
89PGnotify_ptr = POINTER(PGnotify_struct)
90PGcancel_ptr = POINTER(PGcancel_struct)
91PGresAttDesc_ptr = POINTER(PGresAttDesc_struct)
92
93
94# Function definitions as explained in PostgreSQL 12 documentation
95
96# 33.1. Database Connection Control Functions
97
98# PQconnectdbParams: doesn't seem useful, won't wrap for now
99
100PQconnectdb = pq.PQconnectdb
101PQconnectdb.argtypes = [c_char_p]
102PQconnectdb.restype = PGconn_ptr
103
104# PQsetdbLogin: not useful
105# PQsetdb: not useful
106
107# PQconnectStartParams: not useful
108
109PQconnectStart = pq.PQconnectStart
110PQconnectStart.argtypes = [c_char_p]
111PQconnectStart.restype = PGconn_ptr
112
113PQconnectPoll = pq.PQconnectPoll
114PQconnectPoll.argtypes = [PGconn_ptr]
115PQconnectPoll.restype = c_int
116
117PQconndefaults = pq.PQconndefaults
118PQconndefaults.argtypes = []
119PQconndefaults.restype = PQconninfoOption_ptr
120
121PQconninfoFree = pq.PQconninfoFree
122PQconninfoFree.argtypes = [PQconninfoOption_ptr]
123PQconninfoFree.restype = None
124
125PQconninfo = pq.PQconninfo
126PQconninfo.argtypes = [PGconn_ptr]
127PQconninfo.restype = PQconninfoOption_ptr
128
129PQconninfoParse = pq.PQconninfoParse
130PQconninfoParse.argtypes = [c_char_p, POINTER(c_char_p)]
131PQconninfoParse.restype = PQconninfoOption_ptr
132
133PQfinish = pq.PQfinish
134PQfinish.argtypes = [PGconn_ptr]
135PQfinish.restype = None
136
137PQreset = pq.PQreset
138PQreset.argtypes = [PGconn_ptr]
139PQreset.restype = None
140
141PQresetStart = pq.PQresetStart
142PQresetStart.argtypes = [PGconn_ptr]
143PQresetStart.restype = c_int
144
145PQresetPoll = pq.PQresetPoll
146PQresetPoll.argtypes = [PGconn_ptr]
147PQresetPoll.restype = c_int
148
149PQping = pq.PQping
150PQping.argtypes = [c_char_p]
151PQping.restype = c_int
152
153
154# 33.2. Connection Status Functions
155
156PQdb = pq.PQdb
157PQdb.argtypes = [PGconn_ptr]
158PQdb.restype = c_char_p
159
160PQuser = pq.PQuser
161PQuser.argtypes = [PGconn_ptr]
162PQuser.restype = c_char_p
163
164PQpass = pq.PQpass
165PQpass.argtypes = [PGconn_ptr]
166PQpass.restype = c_char_p
167
168PQhost = pq.PQhost
169PQhost.argtypes = [PGconn_ptr]
170PQhost.restype = c_char_p
171
172_PQhostaddr = None
173
174if libpq_version >= 120000:
175    _PQhostaddr = pq.PQhostaddr
176    _PQhostaddr.argtypes = [PGconn_ptr]
177    _PQhostaddr.restype = c_char_p
178
179
180def PQhostaddr(pgconn: PGconn_struct) -> bytes:
181    if not _PQhostaddr:
182        raise NotSupportedError(
183            f"PQhostaddr requires libpq from PostgreSQL 12,"
184            f" {libpq_version} available instead"
185        )
186
187    return _PQhostaddr(pgconn)
188
189
190PQport = pq.PQport
191PQport.argtypes = [PGconn_ptr]
192PQport.restype = c_char_p
193
194PQtty = pq.PQtty
195PQtty.argtypes = [PGconn_ptr]
196PQtty.restype = c_char_p
197
198PQoptions = pq.PQoptions
199PQoptions.argtypes = [PGconn_ptr]
200PQoptions.restype = c_char_p
201
202PQstatus = pq.PQstatus
203PQstatus.argtypes = [PGconn_ptr]
204PQstatus.restype = c_int
205
206PQtransactionStatus = pq.PQtransactionStatus
207PQtransactionStatus.argtypes = [PGconn_ptr]
208PQtransactionStatus.restype = c_int
209
210PQparameterStatus = pq.PQparameterStatus
211PQparameterStatus.argtypes = [PGconn_ptr, c_char_p]
212PQparameterStatus.restype = c_char_p
213
214PQprotocolVersion = pq.PQprotocolVersion
215PQprotocolVersion.argtypes = [PGconn_ptr]
216PQprotocolVersion.restype = c_int
217
218PQserverVersion = pq.PQserverVersion
219PQserverVersion.argtypes = [PGconn_ptr]
220PQserverVersion.restype = c_int
221
222PQerrorMessage = pq.PQerrorMessage
223PQerrorMessage.argtypes = [PGconn_ptr]
224PQerrorMessage.restype = c_char_p
225
226PQsocket = pq.PQsocket
227PQsocket.argtypes = [PGconn_ptr]
228PQsocket.restype = c_int
229
230PQbackendPID = pq.PQbackendPID
231PQbackendPID.argtypes = [PGconn_ptr]
232PQbackendPID.restype = c_int
233
234PQconnectionNeedsPassword = pq.PQconnectionNeedsPassword
235PQconnectionNeedsPassword.argtypes = [PGconn_ptr]
236PQconnectionNeedsPassword.restype = c_int
237
238PQconnectionUsedPassword = pq.PQconnectionUsedPassword
239PQconnectionUsedPassword.argtypes = [PGconn_ptr]
240PQconnectionUsedPassword.restype = c_int
241
242PQsslInUse = pq.PQsslInUse
243PQsslInUse.argtypes = [PGconn_ptr]
244PQsslInUse.restype = c_int
245
246# TODO: PQsslAttribute, PQsslAttributeNames, PQsslStruct, PQgetssl
247
248
249# 33.3. Command Execution Functions
250
251PQexec = pq.PQexec
252PQexec.argtypes = [PGconn_ptr, c_char_p]
253PQexec.restype = PGresult_ptr
254
255PQexecParams = pq.PQexecParams
256PQexecParams.argtypes = [
257    PGconn_ptr,
258    c_char_p,
259    c_int,
260    POINTER(Oid),
261    POINTER(c_char_p),
262    POINTER(c_int),
263    POINTER(c_int),
264    c_int,
265]
266PQexecParams.restype = PGresult_ptr
267
268PQprepare = pq.PQprepare
269PQprepare.argtypes = [PGconn_ptr, c_char_p, c_char_p, c_int, POINTER(Oid)]
270PQprepare.restype = PGresult_ptr
271
272PQexecPrepared = pq.PQexecPrepared
273PQexecPrepared.argtypes = [
274    PGconn_ptr,
275    c_char_p,
276    c_int,
277    POINTER(c_char_p),
278    POINTER(c_int),
279    POINTER(c_int),
280    c_int,
281]
282PQexecPrepared.restype = PGresult_ptr
283
284PQdescribePrepared = pq.PQdescribePrepared
285PQdescribePrepared.argtypes = [PGconn_ptr, c_char_p]
286PQdescribePrepared.restype = PGresult_ptr
287
288PQdescribePortal = pq.PQdescribePortal
289PQdescribePortal.argtypes = [PGconn_ptr, c_char_p]
290PQdescribePortal.restype = PGresult_ptr
291
292PQresultStatus = pq.PQresultStatus
293PQresultStatus.argtypes = [PGresult_ptr]
294PQresultStatus.restype = c_int
295
296# PQresStatus: not needed, we have pretty enums
297
298PQresultErrorMessage = pq.PQresultErrorMessage
299PQresultErrorMessage.argtypes = [PGresult_ptr]
300PQresultErrorMessage.restype = c_char_p
301
302# TODO: PQresultVerboseErrorMessage
303
304PQresultErrorField = pq.PQresultErrorField
305PQresultErrorField.argtypes = [PGresult_ptr, c_int]
306PQresultErrorField.restype = c_char_p
307
308PQclear = pq.PQclear
309PQclear.argtypes = [PGresult_ptr]
310PQclear.restype = None
311
312
313# 33.3.2. Retrieving Query Result Information
314
315PQntuples = pq.PQntuples
316PQntuples.argtypes = [PGresult_ptr]
317PQntuples.restype = c_int
318
319PQnfields = pq.PQnfields
320PQnfields.argtypes = [PGresult_ptr]
321PQnfields.restype = c_int
322
323PQfname = pq.PQfname
324PQfname.argtypes = [PGresult_ptr, c_int]
325PQfname.restype = c_char_p
326
327# PQfnumber: useless and hard to use
328
329PQftable = pq.PQftable
330PQftable.argtypes = [PGresult_ptr, c_int]
331PQftable.restype = Oid
332
333PQftablecol = pq.PQftablecol
334PQftablecol.argtypes = [PGresult_ptr, c_int]
335PQftablecol.restype = c_int
336
337PQfformat = pq.PQfformat
338PQfformat.argtypes = [PGresult_ptr, c_int]
339PQfformat.restype = c_int
340
341PQftype = pq.PQftype
342PQftype.argtypes = [PGresult_ptr, c_int]
343PQftype.restype = Oid
344
345PQfmod = pq.PQfmod
346PQfmod.argtypes = [PGresult_ptr, c_int]
347PQfmod.restype = c_int
348
349PQfsize = pq.PQfsize
350PQfsize.argtypes = [PGresult_ptr, c_int]
351PQfsize.restype = c_int
352
353PQbinaryTuples = pq.PQbinaryTuples
354PQbinaryTuples.argtypes = [PGresult_ptr]
355PQbinaryTuples.restype = c_int
356
357PQgetvalue = pq.PQgetvalue
358PQgetvalue.argtypes = [PGresult_ptr, c_int, c_int]
359PQgetvalue.restype = POINTER(c_char)  # not a null-terminated string
360
361PQgetisnull = pq.PQgetisnull
362PQgetisnull.argtypes = [PGresult_ptr, c_int, c_int]
363PQgetisnull.restype = c_int
364
365PQgetlength = pq.PQgetlength
366PQgetlength.argtypes = [PGresult_ptr, c_int, c_int]
367PQgetlength.restype = c_int
368
369PQnparams = pq.PQnparams
370PQnparams.argtypes = [PGresult_ptr]
371PQnparams.restype = c_int
372
373PQparamtype = pq.PQparamtype
374PQparamtype.argtypes = [PGresult_ptr, c_int]
375PQparamtype.restype = Oid
376
377# PQprint: pretty useless
378
379# 33.3.3. Retrieving Other Result Information
380
381PQcmdStatus = pq.PQcmdStatus
382PQcmdStatus.argtypes = [PGresult_ptr]
383PQcmdStatus.restype = c_char_p
384
385PQcmdTuples = pq.PQcmdTuples
386PQcmdTuples.argtypes = [PGresult_ptr]
387PQcmdTuples.restype = c_char_p
388
389PQoidValue = pq.PQoidValue
390PQoidValue.argtypes = [PGresult_ptr]
391PQoidValue.restype = Oid
392
393
394# 33.3.4. Escaping Strings for Inclusion in SQL Commands
395
396PQescapeLiteral = pq.PQescapeLiteral
397PQescapeLiteral.argtypes = [PGconn_ptr, c_char_p, c_size_t]
398PQescapeLiteral.restype = POINTER(c_char)
399
400PQescapeIdentifier = pq.PQescapeIdentifier
401PQescapeIdentifier.argtypes = [PGconn_ptr, c_char_p, c_size_t]
402PQescapeIdentifier.restype = POINTER(c_char)
403
404PQescapeStringConn = pq.PQescapeStringConn
405# TODO: raises "wrong type" error
406# PQescapeStringConn.argtypes = [
407#     PGconn_ptr, c_char_p, c_char_p, c_size_t, POINTER(c_int)
408# ]
409PQescapeStringConn.restype = c_size_t
410
411PQescapeString = pq.PQescapeString
412# TODO: raises "wrong type" error
413# PQescapeString.argtypes = [c_char_p, c_char_p, c_size_t]
414PQescapeString.restype = c_size_t
415
416PQescapeByteaConn = pq.PQescapeByteaConn
417PQescapeByteaConn.argtypes = [
418    PGconn_ptr,
419    POINTER(c_char),  # actually POINTER(c_ubyte) but this is easier
420    c_size_t,
421    POINTER(c_size_t),
422]
423PQescapeByteaConn.restype = POINTER(c_ubyte)
424
425PQescapeBytea = pq.PQescapeBytea
426PQescapeBytea.argtypes = [
427    POINTER(c_char),  # actually POINTER(c_ubyte) but this is easier
428    c_size_t,
429    POINTER(c_size_t),
430]
431PQescapeBytea.restype = POINTER(c_ubyte)
432
433
434PQunescapeBytea = pq.PQunescapeBytea
435PQunescapeBytea.argtypes = [
436    POINTER(c_char),  # actually POINTER(c_ubyte) but this is easier
437    POINTER(c_size_t),
438]
439PQunescapeBytea.restype = POINTER(c_ubyte)
440
441
442# 33.4. Asynchronous Command Processing
443
444PQsendQuery = pq.PQsendQuery
445PQsendQuery.argtypes = [PGconn_ptr, c_char_p]
446PQsendQuery.restype = c_int
447
448PQsendQueryParams = pq.PQsendQueryParams
449PQsendQueryParams.argtypes = [
450    PGconn_ptr,
451    c_char_p,
452    c_int,
453    POINTER(Oid),
454    POINTER(c_char_p),
455    POINTER(c_int),
456    POINTER(c_int),
457    c_int,
458]
459PQsendQueryParams.restype = c_int
460
461PQsendPrepare = pq.PQsendPrepare
462PQsendPrepare.argtypes = [PGconn_ptr, c_char_p, c_char_p, c_int, POINTER(Oid)]
463PQsendPrepare.restype = c_int
464
465PQsendQueryPrepared = pq.PQsendQueryPrepared
466PQsendQueryPrepared.argtypes = [
467    PGconn_ptr,
468    c_char_p,
469    c_int,
470    POINTER(c_char_p),
471    POINTER(c_int),
472    POINTER(c_int),
473    c_int,
474]
475PQsendQueryPrepared.restype = c_int
476
477PQsendDescribePrepared = pq.PQsendDescribePrepared
478PQsendDescribePrepared.argtypes = [PGconn_ptr, c_char_p]
479PQsendDescribePrepared.restype = c_int
480
481PQsendDescribePortal = pq.PQsendDescribePortal
482PQsendDescribePortal.argtypes = [PGconn_ptr, c_char_p]
483PQsendDescribePortal.restype = c_int
484
485PQgetResult = pq.PQgetResult
486PQgetResult.argtypes = [PGconn_ptr]
487PQgetResult.restype = PGresult_ptr
488
489PQconsumeInput = pq.PQconsumeInput
490PQconsumeInput.argtypes = [PGconn_ptr]
491PQconsumeInput.restype = c_int
492
493PQisBusy = pq.PQisBusy
494PQisBusy.argtypes = [PGconn_ptr]
495PQisBusy.restype = c_int
496
497PQsetnonblocking = pq.PQsetnonblocking
498PQsetnonblocking.argtypes = [PGconn_ptr, c_int]
499PQsetnonblocking.restype = c_int
500
501PQisnonblocking = pq.PQisnonblocking
502PQisnonblocking.argtypes = [PGconn_ptr]
503PQisnonblocking.restype = c_int
504
505PQflush = pq.PQflush
506PQflush.argtypes = [PGconn_ptr]
507PQflush.restype = c_int
508
509
510# 33.5. Retrieving Query Results Row-by-Row
511PQsetSingleRowMode = pq.PQsetSingleRowMode
512PQsetSingleRowMode.argtypes = [PGconn_ptr]
513PQsetSingleRowMode.restype = c_int
514
515
516# 33.6. Canceling Queries in Progress
517
518PQgetCancel = pq.PQgetCancel
519PQgetCancel.argtypes = [PGconn_ptr]
520PQgetCancel.restype = PGcancel_ptr
521
522PQfreeCancel = pq.PQfreeCancel
523PQfreeCancel.argtypes = [PGcancel_ptr]
524PQfreeCancel.restype = None
525
526PQcancel = pq.PQcancel
527# TODO: raises "wrong type" error
528# PQcancel.argtypes = [PGcancel_ptr, POINTER(c_char), c_int]
529PQcancel.restype = c_int
530
531
532# 33.8. Asynchronous Notification
533
534PQnotifies = pq.PQnotifies
535PQnotifies.argtypes = [PGconn_ptr]
536PQnotifies.restype = PGnotify_ptr
537
538
539# 33.9. Functions Associated with the COPY Command
540
541PQputCopyData = pq.PQputCopyData
542PQputCopyData.argtypes = [PGconn_ptr, c_char_p, c_int]
543PQputCopyData.restype = c_int
544
545PQputCopyEnd = pq.PQputCopyEnd
546PQputCopyEnd.argtypes = [PGconn_ptr, c_char_p]
547PQputCopyEnd.restype = c_int
548
549PQgetCopyData = pq.PQgetCopyData
550PQgetCopyData.argtypes = [PGconn_ptr, POINTER(c_char_p), c_int]
551PQgetCopyData.restype = c_int
552
553
554# 33.11. Miscellaneous Functions
555
556PQfreemem = pq.PQfreemem
557PQfreemem.argtypes = [c_void_p]
558PQfreemem.restype = None
559
560if libpq_version >= 100000:
561    _PQencryptPasswordConn = pq.PQencryptPasswordConn
562    _PQencryptPasswordConn.argtypes = [
563        PGconn_ptr,
564        c_char_p,
565        c_char_p,
566        c_char_p,
567    ]
568    _PQencryptPasswordConn.restype = POINTER(c_char)
569
570
571def PQencryptPasswordConn(
572    pgconn: PGconn_struct, passwd: bytes, user: bytes, algorithm: bytes
573) -> Optional[bytes]:
574    if not _PQencryptPasswordConn:
575        raise NotSupportedError(
576            f"PQencryptPasswordConn requires libpq from PostgreSQL 10,"
577            f" {libpq_version} available instead"
578        )
579
580    return _PQencryptPasswordConn(pgconn, passwd, user, algorithm)
581
582
583PQmakeEmptyPGresult = pq.PQmakeEmptyPGresult
584PQmakeEmptyPGresult.argtypes = [PGconn_ptr, c_int]
585PQmakeEmptyPGresult.restype = PGresult_ptr
586
587PQsetResultAttrs = pq.PQsetResultAttrs
588PQsetResultAttrs.argtypes = [PGresult_ptr, c_int, PGresAttDesc_ptr]
589PQsetResultAttrs.restype = c_int
590
591
592# 33.12. Notice Processing
593
594PQnoticeReceiver = CFUNCTYPE(None, c_void_p, PGresult_ptr)
595
596PQsetNoticeReceiver = pq.PQsetNoticeReceiver
597PQsetNoticeReceiver.argtypes = [PGconn_ptr, PQnoticeReceiver, c_void_p]
598PQsetNoticeReceiver.restype = PQnoticeReceiver
599
600# 34.5 Pipeline Mode
601
602_PQpipelineStatus = None
603_PQenterPipelineMode = None
604_PQexitPipelineMode = None
605_PQpipelineSync = None
606_PQsendFlushRequest = None
607
608if libpq_version >= 140000:
609    _PQpipelineStatus = pq.PQpipelineStatus
610    _PQpipelineStatus.argtypes = [PGconn_ptr]
611    _PQpipelineStatus.restype = c_int
612
613    _PQenterPipelineMode = pq.PQenterPipelineMode
614    _PQenterPipelineMode.argtypes = [PGconn_ptr]
615    _PQenterPipelineMode.restype = c_int
616
617    _PQexitPipelineMode = pq.PQexitPipelineMode
618    _PQexitPipelineMode.argtypes = [PGconn_ptr]
619    _PQexitPipelineMode.restype = c_int
620
621    _PQpipelineSync = pq.PQpipelineSync
622    _PQpipelineSync.argtypes = [PGconn_ptr]
623    _PQpipelineSync.restype = c_int
624
625    _PQsendFlushRequest = pq.PQsendFlushRequest
626    _PQsendFlushRequest.argtypes = [PGconn_ptr]
627    _PQsendFlushRequest.restype = c_int
628
629
630def PQpipelineStatus(pgconn: PGconn_struct) -> int:
631    if not _PQpipelineStatus:
632        raise NotSupportedError(
633            f"PQpipelineStatus requires libpq from PostgreSQL 14,"
634            f" {libpq_version} available instead"
635        )
636    return _PQpipelineStatus(pgconn)
637
638
639def PQenterPipelineMode(pgconn: PGconn_struct) -> int:
640    if not _PQenterPipelineMode:
641        raise NotSupportedError(
642            f"PQenterPipelineMode requires libpq from PostgreSQL 14,"
643            f" {libpq_version} available instead"
644        )
645    return _PQenterPipelineMode(pgconn)
646
647
648def PQexitPipelineMode(pgconn: PGconn_struct) -> int:
649    if not _PQexitPipelineMode:
650        raise NotSupportedError(
651            f"PQexitPipelineMode requires libpq from PostgreSQL 14,"
652            f" {libpq_version} available instead"
653        )
654    return _PQexitPipelineMode(pgconn)
655
656
657def PQpipelineSync(pgconn: PGconn_struct) -> int:
658    if not _PQpipelineSync:
659        raise NotSupportedError(
660            f"PQpipelineSync requires libpq from PostgreSQL 14,"
661            f" {libpq_version} available instead"
662        )
663    return _PQpipelineSync(pgconn)
664
665
666def PQsendFlushRequest(pgconn: PGconn_struct) -> int:
667    if not _PQsendFlushRequest:
668        raise NotSupportedError(
669            f"PQsendFlushRequest requires libpq from PostgreSQL 14,"
670            f" {libpq_version} available instead"
671        )
672    return _PQsendFlushRequest(pgconn)
673
674
675# 33.18. SSL Support
676
677PQinitOpenSSL = pq.PQinitOpenSSL
678PQinitOpenSSL.argtypes = [c_int, c_int]
679PQinitOpenSSL.restype = None
680
681
682def generate_stub() -> None:
683    import re
684    from ctypes import _CFuncPtr  # type: ignore
685
686    def type2str(fname, narg, t):
687        if t is None:
688            return "None"
689        elif t is c_void_p:
690            return "Any"
691        elif t is c_int or t is c_uint or t is c_size_t:
692            return "int"
693        elif t is c_char_p or t.__name__ == "LP_c_char":
694            if narg is not None:
695                return "bytes"
696            else:
697                return "Optional[bytes]"
698
699        elif t.__name__ in (
700            "LP_PGconn_struct",
701            "LP_PGresult_struct",
702            "LP_PGcancel_struct",
703        ):
704            if narg is not None:
705                return f"Optional[{t.__name__[3:]}]"
706            else:
707                return t.__name__[3:]
708
709        elif t.__name__ in ("LP_PQconninfoOption_struct",):
710            return f"Sequence[{t.__name__[3:]}]"
711
712        elif t.__name__ in (
713            "LP_c_ubyte",
714            "LP_c_char_p",
715            "LP_c_int",
716            "LP_c_uint",
717            "LP_c_ulong",
718        ):
719            return f"pointer[{t.__name__[3:]}]"
720
721        else:
722            assert False, f"can't deal with {t} in {fname}"
723
724    fn = __file__ + "i"
725    with open(fn) as f:
726        lines = f.read().splitlines()
727
728    istart, iend = (
729        i
730        for i, line in enumerate(lines)
731        if re.match(r"\s*#\s*autogenerated:\s+(start|end)", line)
732    )
733
734    known = {
735        line[4:].split("(", 1)[0]
736        for line in lines[:istart]
737        if line.startswith("def ")
738    }
739
740    signatures = []
741
742    for name, obj in globals().items():
743        if name in known:
744            continue
745        if not isinstance(obj, _CFuncPtr):
746            continue
747
748        params = []
749        for i, t in enumerate(obj.argtypes):
750            params.append(f"arg{i + 1}: {type2str(name, i, t)}")
751
752        resname = type2str(name, None, obj.restype)
753
754        signatures.append(f"def {name}({', '.join(params)}) -> {resname}: ...")
755
756    lines[istart + 1 : iend] = signatures
757
758    with open(fn, "w") as f:
759        f.write("\n".join(lines))
760        f.write("\n")
761
762
763if __name__ == "__main__":
764    generate_stub()
765