Initial commit (Clean history)

This commit is contained in:
anhduy-tech
2025-12-30 11:27:14 +07:00
commit ef48c93de0
19255 changed files with 3248867 additions and 0 deletions

View File

@@ -0,0 +1,136 @@
"""
psycopg libpq wrapper
This package exposes the libpq functionalities as Python objects and functions.
The real implementation (the binding to the C library) is
implementation-dependant but all the implementations share the same interface.
"""
# Copyright (C) 2020 The Psycopg Team
from __future__ import annotations
import os
import logging
from collections.abc import Callable
from . import abc
from .misc import ConninfoOption, PGnotify, PGresAttDesc, error_message, version_pretty
from ._enums import ConnStatus, DiagnosticField, ExecStatus, Format, Ping
from ._enums import PipelineStatus, PollingStatus, Trace, TransactionStatus
logger = logging.getLogger(__name__)
__impl__: str
"""The currently loaded implementation of the `!psycopg.pq` package.
Possible values include ``python``, ``c``, ``binary``.
"""
__build_version__: int
"""The libpq version the C package was built with.
A number in the same format of `~psycopg.ConnectionInfo.server_version`
representing the libpq used to build the speedup module (``c``, ``binary``) if
available.
Certain features might not be available if the built version is too old.
"""
version: Callable[[], int]
PGconn: type[abc.PGconn]
PGresult: type[abc.PGresult]
Conninfo: type[abc.Conninfo]
Escaping: type[abc.Escaping]
PGcancel: type[abc.PGcancel]
PGcancelConn: type[abc.PGcancelConn]
def import_from_libpq() -> None:
"""
Import pq objects implementation from the best libpq wrapper available.
If an implementation is requested try to import only it, otherwise
try to import the best implementation available.
"""
# import these names into the module on success as side effect
global __impl__, version, __build_version__
global PGconn, PGresult, Conninfo, Escaping, PGcancel, PGcancelConn
impl = os.environ.get("PSYCOPG_IMPL", "").lower()
module = None
attempts: list[str] = []
def handle_error(name: str, e: Exception) -> None:
if not impl:
msg = f"couldn't import psycopg '{name}' implementation: {e}"
attempts.append(msg)
else:
msg = f"couldn't import requested psycopg '{name}' implementation: {e}"
raise ImportError(msg) from e
# The best implementation: fast but requires the system libpq installed
if not impl or impl == "c":
try:
from psycopg_c import pq as module # type: ignore
except Exception as e:
handle_error("c", e)
# Second best implementation: fast and stand-alone
if not module and (not impl or impl == "binary"):
try:
from psycopg_binary import pq as module # type: ignore
except Exception as e:
handle_error("binary", e)
# Pure Python implementation, slow and requires the system libpq installed.
if not module and (not impl or impl == "python"):
try:
from . import pq_ctypes as module # type: ignore[assignment]
except Exception as e:
handle_error("python", e)
if module:
__impl__ = module.__impl__
version = module.version
PGconn = module.PGconn
PGresult = module.PGresult
Conninfo = module.Conninfo
Escaping = module.Escaping
PGcancel = module.PGcancel
PGcancelConn = module.PGcancelConn
__build_version__ = module.__build_version__
elif impl:
raise ImportError(f"requested psycopg implementation '{impl}' unknown")
else:
sattempts = "\n".join(f"- {attempt}" for attempt in attempts)
raise ImportError(
f"""\
no pq wrapper available.
Attempts made:
{sattempts}"""
)
import_from_libpq()
__all__ = (
"ConnStatus",
"PipelineStatus",
"PollingStatus",
"TransactionStatus",
"ExecStatus",
"Ping",
"DiagnosticField",
"Format",
"Trace",
"PGconn",
"PGnotify",
"Conninfo",
"PGresAttDesc",
"error_message",
"ConninfoOption",
"version",
"version_pretty",
)

View File

@@ -0,0 +1,108 @@
"""
libpq debugging tools
These functionalities are exposed here for convenience, but are not part of
the public interface and are subject to change at any moment.
Suggested usage::
import logging
import psycopg
from psycopg import pq
from psycopg.pq._debug import PGconnDebug
logging.basicConfig(level=logging.INFO, format="%(message)s")
logger = logging.getLogger("psycopg.debug")
logger.setLevel(logging.INFO)
assert pq.__impl__ == "python"
pq.PGconn = PGconnDebug
with psycopg.connect("") as conn:
conn.pgconn.trace(2)
conn.pgconn.set_trace_flags(
pq.Trace.SUPPRESS_TIMESTAMPS | pq.Trace.REGRESS_MODE)
...
"""
# Copyright (C) 2022 The Psycopg Team
import inspect
import logging
from typing import Any
from functools import wraps
from collections.abc import Callable
from . import PGconn, abc
from .misc import connection_summary
from .._compat import Self, TypeVar
Func = TypeVar("Func", bound=Callable[..., Any])
logger = logging.getLogger("psycopg.debug")
class PGconnDebug:
"""Wrapper for a PQconn logging all its access."""
_pgconn: abc.PGconn
def __init__(self, pgconn: abc.PGconn):
super().__setattr__("_pgconn", pgconn)
def __repr__(self) -> str:
cls = f"{self.__class__.__module__}.{self.__class__.__qualname__}"
info = connection_summary(self._pgconn)
return f"<{cls} {info} at 0x{id(self):x}>"
def __getattr__(self, attr: str) -> Any:
if callable(value := getattr(self._pgconn, attr)):
return debugging(value)
else:
logger.info("PGconn.%s -> %s", attr, value)
return value
def __setattr__(self, attr: str, value: Any) -> None:
setattr(self._pgconn, attr, value)
logger.info("PGconn.%s <- %s", attr, value)
@classmethod
def connect(cls, conninfo: bytes) -> Self:
return cls(debugging(PGconn.connect)(conninfo))
@classmethod
def connect_start(cls, conninfo: bytes) -> Self:
return cls(debugging(PGconn.connect_start)(conninfo))
@classmethod
def ping(self, conninfo: bytes) -> int:
return debugging(PGconn.ping)(conninfo)
def debugging(f: Func) -> Func:
"""Wrap a function in order to log its arguments and return value on call."""
@wraps(f)
def debugging_(*args: Any, **kwargs: Any) -> Any:
reprs = []
for arg in args:
reprs.append(f"{arg!r}")
for k, v in kwargs.items():
reprs.append(f"{k}={v!r}")
logger.info("PGconn.%s(%s)", f.__name__, ", ".join(reprs))
try:
rv = f(*args, **kwargs)
except Exception as ex:
logger.info(" <- %r", ex)
raise
else:
# Display the return value only if the function is declared to return
# something else than None.
ra = inspect.signature(f).return_annotation
if ra is not None or rv is not None:
logger.info(" <- %r", rv)
return rv
return debugging_ # type: ignore

View File

@@ -0,0 +1,259 @@
"""
libpq enum definitions for psycopg
"""
# Copyright (C) 2020 The Psycopg Team
from enum import IntEnum, IntFlag, auto
# Check in src/interfaces/libpq/libpq-fe.h for updates.
class ConnStatus(IntEnum):
"""
Current status of the connection.
"""
__module__ = "psycopg.pq"
OK = 0
"""The connection is in a working state."""
BAD = auto()
"""The connection is closed."""
STARTED = auto()
MADE = auto()
AWAITING_RESPONSE = auto()
AUTH_OK = auto()
SETENV = auto()
SSL_STARTUP = auto()
NEEDED = auto()
CHECK_WRITABLE = auto()
CONSUME = auto()
GSS_STARTUP = auto()
CHECK_TARGET = auto()
CHECK_STANDBY = auto()
ALLOCATED = auto() # Only for cancel connections.
AUTHENTICATING = auto()
class PollingStatus(IntEnum):
"""
The status of the socket during a connection.
If ``READING`` or ``WRITING`` you may select before polling again.
"""
__module__ = "psycopg.pq"
FAILED = 0
"""Connection attempt failed."""
READING = auto()
"""Will have to wait before reading new data."""
WRITING = auto()
"""Will have to wait before writing new data."""
OK = auto()
"""Connection completed."""
ACTIVE = auto()
class ExecStatus(IntEnum):
"""
The status of a command.
"""
__module__ = "psycopg.pq"
EMPTY_QUERY = 0
"""The string sent to the server was empty."""
COMMAND_OK = auto()
"""Successful completion of a command returning no data."""
TUPLES_OK = auto()
"""
Successful completion of a command returning data (such as a SELECT or SHOW).
"""
COPY_OUT = auto()
"""Copy Out (from server) data transfer started."""
COPY_IN = auto()
"""Copy In (to server) data transfer started."""
BAD_RESPONSE = auto()
"""The server's response was not understood."""
NONFATAL_ERROR = auto()
"""A nonfatal error (a notice or warning) occurred."""
FATAL_ERROR = auto()
"""A fatal error occurred."""
COPY_BOTH = auto()
"""
Copy In/Out (to and from server) data transfer started.
This feature is currently used only for streaming replication, so this
status should not occur in ordinary applications.
"""
SINGLE_TUPLE = auto()
"""
The PGresult contains a single result tuple from the current command.
This status occurs only when single-row mode has been selected for the
query.
"""
PIPELINE_SYNC = auto()
"""
The PGresult represents a synchronization point in pipeline mode,
requested by PQpipelineSync.
This status occurs only when pipeline mode has been selected.
"""
PIPELINE_ABORTED = auto()
"""
The PGresult represents a pipeline that has received an error from the server.
PQgetResult must be called repeatedly, and each time it will return this
status code until the end of the current pipeline, at which point it will
return PGRES_PIPELINE_SYNC and normal processing can resume.
"""
TUPLES_CHUNK = auto()
"""The PGresult contains several result tuples from the current command.
This status occurs only when chunked mode has been selected for the query.
"""
class TransactionStatus(IntEnum):
"""
The transaction status of a connection.
"""
__module__ = "psycopg.pq"
IDLE = 0
"""Connection ready, no transaction active."""
ACTIVE = auto()
"""A command is in progress."""
INTRANS = auto()
"""Connection idle in an open transaction."""
INERROR = auto()
"""An error happened in the current transaction."""
UNKNOWN = auto()
"""Unknown connection state, broken connection."""
class Ping(IntEnum):
"""Response from a ping attempt."""
__module__ = "psycopg.pq"
OK = 0
"""
The server is running and appears to be accepting connections.
"""
REJECT = auto()
"""
The server is running but is in a state that disallows connections.
"""
NO_RESPONSE = auto()
"""
The server could not be contacted.
"""
NO_ATTEMPT = auto()
"""
No attempt was made to contact the server.
"""
class PipelineStatus(IntEnum):
"""Pipeline mode status of the libpq connection."""
__module__ = "psycopg.pq"
OFF = 0
"""
The libpq connection is *not* in pipeline mode.
"""
ON = auto()
"""
The libpq connection is in pipeline mode.
"""
ABORTED = auto()
"""
The libpq connection is in pipeline mode and an error occurred while
processing the current pipeline. The aborted flag is cleared when
PQgetResult returns a result of type PGRES_PIPELINE_SYNC.
"""
class DiagnosticField(IntEnum):
"""
Fields in an error report.
"""
__module__ = "psycopg.pq"
# from src/include/postgres_ext.h
SEVERITY = ord("S")
SEVERITY_NONLOCALIZED = ord("V")
SQLSTATE = ord("C")
MESSAGE_PRIMARY = ord("M")
MESSAGE_DETAIL = ord("D")
MESSAGE_HINT = ord("H")
STATEMENT_POSITION = ord("P")
INTERNAL_POSITION = ord("p")
INTERNAL_QUERY = ord("q")
CONTEXT = ord("W")
SCHEMA_NAME = ord("s")
TABLE_NAME = ord("t")
COLUMN_NAME = ord("c")
DATATYPE_NAME = ord("d")
CONSTRAINT_NAME = ord("n")
SOURCE_FILE = ord("F")
SOURCE_LINE = ord("L")
SOURCE_FUNCTION = ord("R")
class Format(IntEnum):
"""
Enum representing the format of a query argument or return value.
These values are only the ones managed by the libpq. `~psycopg` may also
support automatically-chosen values: see `psycopg.adapt.PyFormat`.
"""
__module__ = "psycopg.pq"
TEXT = 0
"""Text parameter."""
BINARY = 1
"""Binary parameter."""
class Trace(IntFlag):
"""
Enum to control tracing of the client/server communication.
"""
__module__ = "psycopg.pq"
SUPPRESS_TIMESTAMPS = 1
"""Do not include timestamps in messages."""
REGRESS_MODE = 2
"""Redact some fields, e.g. OIDs, from messages."""

View File

@@ -0,0 +1,844 @@
"""
libpq access using ctypes
"""
# Copyright (C) 2020 The Psycopg Team
from __future__ import annotations
import sys
import ctypes
import ctypes.util
from ctypes import CFUNCTYPE, POINTER, Structure, c_char, c_char_p, c_int, c_size_t
from ctypes import c_ubyte, c_uint, c_void_p
from typing import Any, NoReturn
from .misc import find_libpq_full_path, version_pretty
from ..errors import NotSupportedError
if not (libname := find_libpq_full_path()):
raise ImportError("libpq library not found")
pq = ctypes.cdll.LoadLibrary(libname)
class FILE(Structure):
pass
FILE_ptr = POINTER(FILE)
if sys.platform == "linux":
if not (libcname := ctypes.util.find_library("c")):
# Likely this is a system using musl libc, see the following bug:
# https://github.com/python/cpython/issues/65821
libcname = "libc.so"
libc = ctypes.cdll.LoadLibrary(libcname)
fdopen = libc.fdopen
fdopen.argtypes = (c_int, c_char_p)
fdopen.restype = FILE_ptr
def not_supported_before(fname: str, pgversion: int) -> Any:
def not_supported(*args: Any, **kwargs: Any) -> NoReturn:
raise NotSupportedError(
f"{fname} requires libpq from PostgreSQL {version_pretty(pgversion)} on"
f" the client; version {version_pretty(libpq_version)} available instead"
)
return not_supported
# Get the libpq version to define what functions are available.
PQlibVersion = pq.PQlibVersion
PQlibVersion.argtypes = []
PQlibVersion.restype = c_int
libpq_version = PQlibVersion()
# libpq data types
Oid = c_uint
class PGconn_struct(Structure):
_fields_ = []
class PGresult_struct(Structure):
_fields_ = []
class PQconninfoOption_struct(Structure):
_fields_ = [
("keyword", c_char_p),
("envvar", c_char_p),
("compiled", c_char_p),
("val", c_char_p),
("label", c_char_p),
("dispchar", c_char_p),
("dispsize", c_int),
]
class PGnotify_struct(Structure):
_fields_ = [
("relname", c_char_p),
("be_pid", c_int),
("extra", c_char_p),
]
class PGcancelConn_struct(Structure):
_fields_ = []
class PGcancel_struct(Structure):
_fields_ = []
class PGresAttDesc_struct(Structure):
_fields_ = [
("name", c_char_p),
("tableid", Oid),
("columnid", c_int),
("format", c_int),
("typid", Oid),
("typlen", c_int),
("atttypmod", c_int),
]
PGconn_ptr = POINTER(PGconn_struct)
PGresult_ptr = POINTER(PGresult_struct)
PQconninfoOption_ptr = POINTER(PQconninfoOption_struct)
PGnotify_ptr = POINTER(PGnotify_struct)
PGcancelConn_ptr = POINTER(PGcancelConn_struct)
PGcancel_ptr = POINTER(PGcancel_struct)
PGresAttDesc_ptr = POINTER(PGresAttDesc_struct)
# Function definitions as explained in PostgreSQL 12 documentation
# 33.1. Database Connection Control Functions
# PQconnectdbParams: doesn't seem useful, won't wrap for now
PQconnectdb = pq.PQconnectdb
PQconnectdb.argtypes = [c_char_p]
PQconnectdb.restype = PGconn_ptr
# PQsetdbLogin: not useful
# PQsetdb: not useful
# PQconnectStartParams: not useful
PQconnectStart = pq.PQconnectStart
PQconnectStart.argtypes = [c_char_p]
PQconnectStart.restype = PGconn_ptr
PQconnectPoll = pq.PQconnectPoll
PQconnectPoll.argtypes = [PGconn_ptr]
PQconnectPoll.restype = c_int
PQconndefaults = pq.PQconndefaults
PQconndefaults.argtypes = []
PQconndefaults.restype = PQconninfoOption_ptr
PQconninfoFree = pq.PQconninfoFree
PQconninfoFree.argtypes = [PQconninfoOption_ptr]
PQconninfoFree.restype = None
PQconninfo = pq.PQconninfo
PQconninfo.argtypes = [PGconn_ptr]
PQconninfo.restype = PQconninfoOption_ptr
PQconninfoParse = pq.PQconninfoParse
PQconninfoParse.argtypes = [c_char_p, POINTER(c_char_p)]
PQconninfoParse.restype = PQconninfoOption_ptr
PQfinish = pq.PQfinish
PQfinish.argtypes = [PGconn_ptr]
PQfinish.restype = None
PQreset = pq.PQreset
PQreset.argtypes = [PGconn_ptr]
PQreset.restype = None
PQresetStart = pq.PQresetStart
PQresetStart.argtypes = [PGconn_ptr]
PQresetStart.restype = c_int
PQresetPoll = pq.PQresetPoll
PQresetPoll.argtypes = [PGconn_ptr]
PQresetPoll.restype = c_int
PQping = pq.PQping
PQping.argtypes = [c_char_p]
PQping.restype = c_int
# 33.2. Connection Status Functions
PQdb = pq.PQdb
PQdb.argtypes = [PGconn_ptr]
PQdb.restype = c_char_p
PQuser = pq.PQuser
PQuser.argtypes = [PGconn_ptr]
PQuser.restype = c_char_p
PQpass = pq.PQpass
PQpass.argtypes = [PGconn_ptr]
PQpass.restype = c_char_p
PQhost = pq.PQhost
PQhost.argtypes = [PGconn_ptr]
PQhost.restype = c_char_p
if libpq_version >= 120000:
PQhostaddr = pq.PQhostaddr
PQhostaddr.argtypes = [PGconn_ptr]
PQhostaddr.restype = c_char_p
else:
PQhostaddr = not_supported_before("PQhostaddr", 120000)
PQport = pq.PQport
PQport.argtypes = [PGconn_ptr]
PQport.restype = c_char_p
PQtty = pq.PQtty
PQtty.argtypes = [PGconn_ptr]
PQtty.restype = c_char_p
PQoptions = pq.PQoptions
PQoptions.argtypes = [PGconn_ptr]
PQoptions.restype = c_char_p
PQstatus = pq.PQstatus
PQstatus.argtypes = [PGconn_ptr]
PQstatus.restype = c_int
PQtransactionStatus = pq.PQtransactionStatus
PQtransactionStatus.argtypes = [PGconn_ptr]
PQtransactionStatus.restype = c_int
PQparameterStatus = pq.PQparameterStatus
PQparameterStatus.argtypes = [PGconn_ptr, c_char_p]
PQparameterStatus.restype = c_char_p
PQprotocolVersion = pq.PQprotocolVersion
PQprotocolVersion.argtypes = [PGconn_ptr]
PQprotocolVersion.restype = c_int
if libpq_version >= 180000:
PQfullProtocolVersion = pq.PQfullProtocolVersion
PQfullProtocolVersion.argtypes = [PGconn_ptr]
PQfullProtocolVersion.restype = c_int
else:
PQfullProtocolVersion = not_supported_before("PQfullProtocolVersion", 180000)
PQserverVersion = pq.PQserverVersion
PQserverVersion.argtypes = [PGconn_ptr]
PQserverVersion.restype = c_int
PQerrorMessage = pq.PQerrorMessage
PQerrorMessage.argtypes = [PGconn_ptr]
PQerrorMessage.restype = c_char_p
PQsocket = pq.PQsocket
PQsocket.argtypes = [PGconn_ptr]
PQsocket.restype = c_int
PQbackendPID = pq.PQbackendPID
PQbackendPID.argtypes = [PGconn_ptr]
PQbackendPID.restype = c_int
PQconnectionNeedsPassword = pq.PQconnectionNeedsPassword
PQconnectionNeedsPassword.argtypes = [PGconn_ptr]
PQconnectionNeedsPassword.restype = c_int
PQconnectionUsedPassword = pq.PQconnectionUsedPassword
PQconnectionUsedPassword.argtypes = [PGconn_ptr]
PQconnectionUsedPassword.restype = c_int
if libpq_version >= 160000:
PQconnectionUsedGSSAPI = pq.PQconnectionUsedGSSAPI
PQconnectionUsedGSSAPI.argtypes = [PGconn_ptr]
PQconnectionUsedGSSAPI.restype = c_int
else:
PQconnectionUsedGSSAPI = not_supported_before("PQconnectionUsedGSSAPI", 160000)
PQsslInUse = pq.PQsslInUse
PQsslInUse.argtypes = [PGconn_ptr]
PQsslInUse.restype = c_int
# TODO: PQsslAttribute, PQsslAttributeNames, PQsslStruct, PQgetssl
# 33.3. Command Execution Functions
PQexec = pq.PQexec
PQexec.argtypes = [PGconn_ptr, c_char_p]
PQexec.restype = PGresult_ptr
PQexecParams = pq.PQexecParams
PQexecParams.argtypes = [
PGconn_ptr,
c_char_p,
c_int,
POINTER(Oid),
POINTER(c_char_p),
POINTER(c_int),
POINTER(c_int),
c_int,
]
PQexecParams.restype = PGresult_ptr
PQprepare = pq.PQprepare
PQprepare.argtypes = [PGconn_ptr, c_char_p, c_char_p, c_int, POINTER(Oid)]
PQprepare.restype = PGresult_ptr
PQexecPrepared = pq.PQexecPrepared
PQexecPrepared.argtypes = [
PGconn_ptr,
c_char_p,
c_int,
POINTER(c_char_p),
POINTER(c_int),
POINTER(c_int),
c_int,
]
PQexecPrepared.restype = PGresult_ptr
PQdescribePrepared = pq.PQdescribePrepared
PQdescribePrepared.argtypes = [PGconn_ptr, c_char_p]
PQdescribePrepared.restype = PGresult_ptr
PQdescribePortal = pq.PQdescribePortal
PQdescribePortal.argtypes = [PGconn_ptr, c_char_p]
PQdescribePortal.restype = PGresult_ptr
if libpq_version >= 170000:
PQclosePrepared = pq.PQclosePrepared
PQclosePrepared.argtypes = [PGconn_ptr, c_char_p]
PQclosePrepared.restype = PGresult_ptr
PQclosePortal = pq.PQclosePortal
PQclosePortal.argtypes = [PGconn_ptr, c_char_p]
PQclosePortal.restype = PGresult_ptr
else:
PQclosePrepared = not_supported_before("PQclosePrepared", 170000)
PQclosePortal = not_supported_before("PQclosePortal", 170000)
PQresultStatus = pq.PQresultStatus
PQresultStatus.argtypes = [PGresult_ptr]
PQresultStatus.restype = c_int
# PQresStatus: not needed, we have pretty enums
PQresultErrorMessage = pq.PQresultErrorMessage
PQresultErrorMessage.argtypes = [PGresult_ptr]
PQresultErrorMessage.restype = c_char_p
# TODO: PQresultVerboseErrorMessage
PQresultErrorField = pq.PQresultErrorField
PQresultErrorField.argtypes = [PGresult_ptr, c_int]
PQresultErrorField.restype = c_char_p
PQclear = pq.PQclear
PQclear.argtypes = [PGresult_ptr]
PQclear.restype = None
# 33.3.2. Retrieving Query Result Information
PQntuples = pq.PQntuples
PQntuples.argtypes = [PGresult_ptr]
PQntuples.restype = c_int
PQnfields = pq.PQnfields
PQnfields.argtypes = [PGresult_ptr]
PQnfields.restype = c_int
PQfname = pq.PQfname
PQfname.argtypes = [PGresult_ptr, c_int]
PQfname.restype = c_char_p
# PQfnumber: useless and hard to use
PQftable = pq.PQftable
PQftable.argtypes = [PGresult_ptr, c_int]
PQftable.restype = Oid
PQftablecol = pq.PQftablecol
PQftablecol.argtypes = [PGresult_ptr, c_int]
PQftablecol.restype = c_int
PQfformat = pq.PQfformat
PQfformat.argtypes = [PGresult_ptr, c_int]
PQfformat.restype = c_int
PQftype = pq.PQftype
PQftype.argtypes = [PGresult_ptr, c_int]
PQftype.restype = Oid
PQfmod = pq.PQfmod
PQfmod.argtypes = [PGresult_ptr, c_int]
PQfmod.restype = c_int
PQfsize = pq.PQfsize
PQfsize.argtypes = [PGresult_ptr, c_int]
PQfsize.restype = c_int
PQbinaryTuples = pq.PQbinaryTuples
PQbinaryTuples.argtypes = [PGresult_ptr]
PQbinaryTuples.restype = c_int
PQgetvalue = pq.PQgetvalue
PQgetvalue.argtypes = [PGresult_ptr, c_int, c_int]
PQgetvalue.restype = POINTER(c_char) # not a null-terminated string
PQgetisnull = pq.PQgetisnull
PQgetisnull.argtypes = [PGresult_ptr, c_int, c_int]
PQgetisnull.restype = c_int
PQgetlength = pq.PQgetlength
PQgetlength.argtypes = [PGresult_ptr, c_int, c_int]
PQgetlength.restype = c_int
PQnparams = pq.PQnparams
PQnparams.argtypes = [PGresult_ptr]
PQnparams.restype = c_int
PQparamtype = pq.PQparamtype
PQparamtype.argtypes = [PGresult_ptr, c_int]
PQparamtype.restype = Oid
# PQprint: pretty useless
# 33.3.3. Retrieving Other Result Information
PQcmdStatus = pq.PQcmdStatus
PQcmdStatus.argtypes = [PGresult_ptr]
PQcmdStatus.restype = c_char_p
PQcmdTuples = pq.PQcmdTuples
PQcmdTuples.argtypes = [PGresult_ptr]
PQcmdTuples.restype = c_char_p
PQoidValue = pq.PQoidValue
PQoidValue.argtypes = [PGresult_ptr]
PQoidValue.restype = Oid
# 33.3.4. Escaping Strings for Inclusion in SQL Commands
PQescapeLiteral = pq.PQescapeLiteral
PQescapeLiteral.argtypes = [PGconn_ptr, c_char_p, c_size_t]
PQescapeLiteral.restype = POINTER(c_char)
PQescapeIdentifier = pq.PQescapeIdentifier
PQescapeIdentifier.argtypes = [PGconn_ptr, c_char_p, c_size_t]
PQescapeIdentifier.restype = POINTER(c_char)
PQescapeStringConn = pq.PQescapeStringConn
# TODO: raises "wrong type" error
# PQescapeStringConn.argtypes = [
# PGconn_ptr, c_char_p, c_char_p, c_size_t, POINTER(c_int)
# ]
PQescapeStringConn.restype = c_size_t
PQescapeString = pq.PQescapeString
# TODO: raises "wrong type" error
# PQescapeString.argtypes = [c_char_p, c_char_p, c_size_t]
PQescapeString.restype = c_size_t
PQescapeByteaConn = pq.PQescapeByteaConn
PQescapeByteaConn.argtypes = [
PGconn_ptr,
POINTER(c_char), # actually POINTER(c_ubyte) but this is easier
c_size_t,
POINTER(c_size_t),
]
PQescapeByteaConn.restype = POINTER(c_ubyte)
PQescapeBytea = pq.PQescapeBytea
PQescapeBytea.argtypes = [
POINTER(c_char), # actually POINTER(c_ubyte) but this is easier
c_size_t,
POINTER(c_size_t),
]
PQescapeBytea.restype = POINTER(c_ubyte)
PQunescapeBytea = pq.PQunescapeBytea
PQunescapeBytea.argtypes = [
POINTER(c_char), # actually POINTER(c_ubyte) but this is easier
POINTER(c_size_t),
]
PQunescapeBytea.restype = POINTER(c_ubyte)
# 33.4. Asynchronous Command Processing
PQsendQuery = pq.PQsendQuery
PQsendQuery.argtypes = [PGconn_ptr, c_char_p]
PQsendQuery.restype = c_int
PQsendQueryParams = pq.PQsendQueryParams
PQsendQueryParams.argtypes = [
PGconn_ptr,
c_char_p,
c_int,
POINTER(Oid),
POINTER(c_char_p),
POINTER(c_int),
POINTER(c_int),
c_int,
]
PQsendQueryParams.restype = c_int
PQsendPrepare = pq.PQsendPrepare
PQsendPrepare.argtypes = [PGconn_ptr, c_char_p, c_char_p, c_int, POINTER(Oid)]
PQsendPrepare.restype = c_int
PQsendQueryPrepared = pq.PQsendQueryPrepared
PQsendQueryPrepared.argtypes = [
PGconn_ptr,
c_char_p,
c_int,
POINTER(c_char_p),
POINTER(c_int),
POINTER(c_int),
c_int,
]
PQsendQueryPrepared.restype = c_int
PQsendDescribePrepared = pq.PQsendDescribePrepared
PQsendDescribePrepared.argtypes = [PGconn_ptr, c_char_p]
PQsendDescribePrepared.restype = c_int
PQsendDescribePortal = pq.PQsendDescribePortal
PQsendDescribePortal.argtypes = [PGconn_ptr, c_char_p]
PQsendDescribePortal.restype = c_int
if libpq_version >= 170000:
PQsendClosePrepared = pq.PQsendClosePrepared
PQsendClosePrepared.argtypes = [PGconn_ptr, c_char_p]
PQsendClosePrepared.restype = c_int
PQsendClosePortal = pq.PQsendClosePortal
PQsendClosePortal.argtypes = [PGconn_ptr, c_char_p]
PQsendClosePortal.restype = c_int
else:
PQsendClosePrepared = not_supported_before("PQsendClosePrepared", 170000)
PQsendClosePortal = not_supported_before("PQsendClosePortal", 170000)
PQgetResult = pq.PQgetResult
PQgetResult.argtypes = [PGconn_ptr]
PQgetResult.restype = PGresult_ptr
PQconsumeInput = pq.PQconsumeInput
PQconsumeInput.argtypes = [PGconn_ptr]
PQconsumeInput.restype = c_int
PQisBusy = pq.PQisBusy
PQisBusy.argtypes = [PGconn_ptr]
PQisBusy.restype = c_int
PQsetnonblocking = pq.PQsetnonblocking
PQsetnonblocking.argtypes = [PGconn_ptr, c_int]
PQsetnonblocking.restype = c_int
PQisnonblocking = pq.PQisnonblocking
PQisnonblocking.argtypes = [PGconn_ptr]
PQisnonblocking.restype = c_int
PQflush = pq.PQflush
PQflush.argtypes = [PGconn_ptr]
PQflush.restype = c_int
# 32.6. Retrieving Query Results in Chunks
PQsetSingleRowMode = pq.PQsetSingleRowMode
PQsetSingleRowMode.argtypes = [PGconn_ptr]
PQsetSingleRowMode.restype = c_int
if libpq_version >= 170000:
PQsetChunkedRowsMode = pq.PQsetChunkedRowsMode
PQsetChunkedRowsMode.argtypes = [PGconn_ptr, c_int]
PQsetChunkedRowsMode.restype = c_int
else:
PQsetChunkedRowsMode = not_supported_before("PQsetChunkedRowsMode", 170000)
# 33.6. Canceling Queries in Progress
if libpq_version >= 170000:
PQcancelCreate = pq.PQcancelCreate
PQcancelCreate.argtypes = [PGconn_ptr]
PQcancelCreate.restype = PGcancelConn_ptr
PQcancelStart = pq.PQcancelStart
PQcancelStart.argtypes = [PGcancelConn_ptr]
PQcancelStart.restype = c_int
PQcancelBlocking = pq.PQcancelBlocking
PQcancelBlocking.argtypes = [PGcancelConn_ptr]
PQcancelBlocking.restype = c_int
PQcancelPoll = pq.PQcancelPoll
PQcancelPoll.argtypes = [PGcancelConn_ptr]
PQcancelPoll.restype = c_int
PQcancelStatus = pq.PQcancelStatus
PQcancelStatus.argtypes = [PGcancelConn_ptr]
PQcancelStatus.restype = c_int
PQcancelSocket = pq.PQcancelSocket
PQcancelSocket.argtypes = [PGcancelConn_ptr]
PQcancelSocket.restype = c_int
PQcancelErrorMessage = pq.PQcancelErrorMessage
PQcancelErrorMessage.argtypes = [PGcancelConn_ptr]
PQcancelErrorMessage.restype = c_char_p
PQcancelReset = pq.PQcancelReset
PQcancelReset.argtypes = [PGcancelConn_ptr]
PQcancelReset.restype = None
PQcancelFinish = pq.PQcancelFinish
PQcancelFinish.argtypes = [PGcancelConn_ptr]
PQcancelFinish.restype = None
else:
PQcancelCreate = not_supported_before("PQcancelCreate", 170000)
PQcancelStart = not_supported_before("PQcancelStart", 170000)
PQcancelBlocking = not_supported_before("PQcancelBlocking", 170000)
PQcancelPoll = not_supported_before("PQcancelPoll", 170000)
PQcancelStatus = not_supported_before("PQcancelStatus", 170000)
PQcancelSocket = not_supported_before("PQcancelSocket", 170000)
PQcancelErrorMessage = not_supported_before("PQcancelErrorMessage", 170000)
PQcancelReset = not_supported_before("PQcancelReset", 170000)
PQcancelFinish = not_supported_before("PQcancelFinish", 170000)
PQgetCancel = pq.PQgetCancel
PQgetCancel.argtypes = [PGconn_ptr]
PQgetCancel.restype = PGcancel_ptr
PQfreeCancel = pq.PQfreeCancel
PQfreeCancel.argtypes = [PGcancel_ptr]
PQfreeCancel.restype = None
PQcancel = pq.PQcancel
# TODO: raises "wrong type" error
# PQcancel.argtypes = [PGcancel_ptr, POINTER(c_char), c_int]
PQcancel.restype = c_int
# 33.8. Asynchronous Notification
PQnotifies = pq.PQnotifies
PQnotifies.argtypes = [PGconn_ptr]
PQnotifies.restype = PGnotify_ptr
# 33.9. Functions Associated with the COPY Command
PQputCopyData = pq.PQputCopyData
PQputCopyData.argtypes = [PGconn_ptr, c_char_p, c_int]
PQputCopyData.restype = c_int
PQputCopyEnd = pq.PQputCopyEnd
PQputCopyEnd.argtypes = [PGconn_ptr, c_char_p]
PQputCopyEnd.restype = c_int
PQgetCopyData = pq.PQgetCopyData
PQgetCopyData.argtypes = [PGconn_ptr, POINTER(c_char_p), c_int]
PQgetCopyData.restype = c_int
# 33.10. Control Functions
PQtrace = pq.PQtrace
PQtrace.argtypes = [PGconn_ptr, FILE_ptr]
PQtrace.restype = None
if libpq_version >= 140000:
PQsetTraceFlags = pq.PQsetTraceFlags
PQsetTraceFlags.argtypes = [PGconn_ptr, c_int]
PQsetTraceFlags.restype = None
else:
PQsetTraceFlags = not_supported_before("PQsetTraceFlags", 140000)
PQuntrace = pq.PQuntrace
PQuntrace.argtypes = [PGconn_ptr]
PQuntrace.restype = None
# 33.11. Miscellaneous Functions
PQfreemem = pq.PQfreemem
PQfreemem.argtypes = [c_void_p]
PQfreemem.restype = None
if libpq_version >= 100000:
PQencryptPasswordConn = pq.PQencryptPasswordConn
PQencryptPasswordConn.argtypes = [PGconn_ptr, c_char_p, c_char_p, c_char_p]
PQencryptPasswordConn.restype = POINTER(c_char)
else:
PQencryptPasswordConn = not_supported_before("PQencryptPasswordConn", 100000)
if libpq_version >= 170000:
PQchangePassword = pq.PQchangePassword
PQchangePassword.argtypes = [PGconn_ptr, c_char_p, c_char_p]
PQchangePassword.restype = PGresult_ptr
else:
PQchangePassword = not_supported_before("PQchangePassword", 170000)
PQmakeEmptyPGresult = pq.PQmakeEmptyPGresult
PQmakeEmptyPGresult.argtypes = [PGconn_ptr, c_int]
PQmakeEmptyPGresult.restype = PGresult_ptr
PQsetResultAttrs = pq.PQsetResultAttrs
PQsetResultAttrs.argtypes = [PGresult_ptr, c_int, PGresAttDesc_ptr]
PQsetResultAttrs.restype = c_int
# 33.12. Notice Processing
PQnoticeReceiver = CFUNCTYPE(None, c_void_p, PGresult_ptr)
PQsetNoticeReceiver = pq.PQsetNoticeReceiver
PQsetNoticeReceiver.argtypes = [PGconn_ptr, PQnoticeReceiver, c_void_p]
PQsetNoticeReceiver.restype = PQnoticeReceiver
# 34.5 Pipeline Mode
if libpq_version >= 140000:
PQpipelineStatus = pq.PQpipelineStatus
PQpipelineStatus.argtypes = [PGconn_ptr]
PQpipelineStatus.restype = c_int
PQenterPipelineMode = pq.PQenterPipelineMode
PQenterPipelineMode.argtypes = [PGconn_ptr]
PQenterPipelineMode.restype = c_int
PQexitPipelineMode = pq.PQexitPipelineMode
PQexitPipelineMode.argtypes = [PGconn_ptr]
PQexitPipelineMode.restype = c_int
PQpipelineSync = pq.PQpipelineSync
PQpipelineSync.argtypes = [PGconn_ptr]
PQpipelineSync.restype = c_int
PQsendFlushRequest = pq.PQsendFlushRequest
PQsendFlushRequest.argtypes = [PGconn_ptr]
PQsendFlushRequest.restype = c_int
else:
PQpipelineStatus = not_supported_before("PQpipelineStatus", 140000)
PQenterPipelineMode = not_supported_before("PQenterPipelineMode", 140000)
PQexitPipelineMode = not_supported_before("PQexitPipelineMode", 140000)
PQpipelineSync = not_supported_before("PQpipelineSync", 140000)
PQsendFlushRequest = not_supported_before("PQsendFlushRequest", 140000)
# 33.18. SSL Support
PQinitOpenSSL = pq.PQinitOpenSSL
PQinitOpenSSL.argtypes = [c_int, c_int]
PQinitOpenSSL.restype = None
def generate_stub() -> None:
import re
from ctypes import _CFuncPtr # type: ignore[attr-defined]
def type2str(fname: str, narg: int | None, t: Any) -> str:
if t is None:
return "None"
elif t is c_void_p:
return "Any"
elif t is c_int or t is c_uint or t is c_size_t:
return "int"
elif t is c_char_p or t.__name__ == "LP_c_char":
if narg is not None:
return "bytes"
else:
return "bytes | None"
elif t.__name__ in (
"LP_PGconn_struct",
"LP_PGresult_struct",
"LP_PGcancelConn_struct",
"LP_PGcancel_struct",
):
if narg is not None:
return f"{t.__name__[3:]} | None"
else:
return str(t.__name__[3:])
elif t.__name__ in ("LP_PQconninfoOption_struct",):
return f"Sequence[{t.__name__[3:]}]"
elif t.__name__ in (
"LP_c_ubyte",
"LP_c_char_p",
"LP_c_int",
"LP_c_uint",
"LP_c_ulong",
"LP_FILE",
):
return f"_Pointer[{t.__name__[3:]}]"
else:
assert False, f"can't deal with {t} in {fname}"
fn = __file__ + "i"
with open(fn) as f:
lines = f.read().splitlines()
istart, iend = (
i
for i, line in enumerate(lines)
if re.match(r"\s*#\s*autogenerated:\s+(start|end)", line)
)
known = {
line[4:].split("(", 1)[0] for line in lines[:istart] if line.startswith("def ")
}
signatures = []
for name, obj in globals().items():
if name in known:
continue
if not isinstance(obj, _CFuncPtr):
continue
params = []
for i, t in enumerate(obj.argtypes):
params.append(f"arg{i + 1}: {type2str(name, i, t)}")
resname = type2str(name, None, obj.restype)
signatures.append(f"def {name}({', '.join(params)}) -> {resname}: ...")
lines[istart + 1 : iend] = signatures
with open(fn, "w") as f:
f.write("\n".join(lines))
f.write("\n")
if __name__ == "__main__":
generate_stub()

View File

@@ -0,0 +1,229 @@
"""
types stub for ctypes functions
"""
# Copyright (C) 2020 The Psycopg Team
from ctypes import Array, _Pointer, c_char, c_char_p, c_int, c_ubyte, c_uint, c_ulong
from ctypes import pointer
from typing import Any, Callable, Sequence
class FILE: ...
def fdopen(fd: int, mode: bytes) -> _Pointer[FILE]: ... # type: ignore[type-var]
Oid = c_uint
class PGconn_struct: ...
class PGresult_struct: ...
class PGcancelConn_struct: ...
class PGcancel_struct: ...
class PQconninfoOption_struct:
keyword: bytes
envvar: bytes
compiled: bytes
val: bytes
label: bytes
dispchar: bytes
dispsize: int
class PGnotify_struct:
be_pid: int
relname: bytes
extra: bytes
class PGresAttDesc_struct:
name: bytes
tableid: int
columnid: int
format: int
typid: int
typlen: int
atttypmod: int
def PQhostaddr(arg1: PGconn_struct | None) -> bytes: ...
def PQerrorMessage(arg1: PGconn_struct | None) -> bytes: ...
def PQresultErrorMessage(arg1: PGresult_struct | None) -> bytes: ...
def PQexecPrepared(
arg1: PGconn_struct | None,
arg2: bytes,
arg3: int,
arg4: Array[c_char_p] | None,
arg5: Array[c_int] | None,
arg6: Array[c_int] | None,
arg7: int,
) -> PGresult_struct: ...
def PQprepare(
arg1: PGconn_struct | None,
arg2: bytes,
arg3: bytes,
arg4: int,
arg5: Array[c_uint] | None,
) -> PGresult_struct: ...
def PQgetvalue(
arg1: PGresult_struct | None, arg2: int, arg3: int
) -> _Pointer[c_char]: ...
def PQcmdTuples(arg1: PGresult_struct | None) -> bytes: ...
def PQescapeStringConn(
arg1: PGconn_struct | None,
arg2: c_char_p,
arg3: bytes,
arg4: int,
arg5: _Pointer[c_int],
) -> int: ...
def PQescapeString(arg1: c_char_p, arg2: bytes, arg3: int) -> int: ...
def PQsendPrepare(
arg1: PGconn_struct | None,
arg2: bytes,
arg3: bytes,
arg4: int,
arg5: Array[c_uint] | None,
) -> int: ...
def PQsendQueryPrepared(
arg1: PGconn_struct | None,
arg2: bytes,
arg3: int,
arg4: Array[c_char_p] | None,
arg5: Array[c_int] | None,
arg6: Array[c_int] | None,
arg7: int,
) -> int: ...
def PQcancelCreate(arg1: PGconn_struct | None) -> PGcancelConn_struct: ...
def PQcancelBlocking(arg1: PGcancelConn_struct | None) -> int: ...
def PQcancelStart(arg1: PGcancelConn_struct | None) -> int: ...
def PQcancelPoll(arg1: PGcancelConn_struct | None) -> int: ...
def PQcancelStatus(arg1: PGcancelConn_struct | None) -> int: ...
def PQcancelSocket(arg1: PGcancelConn_struct | None) -> int: ...
def PQcancelErrorMessage(arg1: PGcancelConn_struct | None) -> bytes: ...
def PQcancelReset(arg1: PGcancelConn_struct | None) -> None: ...
def PQcancelFinish(arg1: PGcancelConn_struct | None) -> None: ...
def PQcancel(arg1: PGcancel_struct | None, arg2: c_char_p, arg3: int) -> int: ...
def PQsetNoticeReceiver(
arg1: PGconn_struct, arg2: Callable[[Any], PGresult_struct], arg3: Any
) -> Callable[[Any], PGresult_struct]: ...
# TODO: Ignoring type as getting an error on mypy/ctypes:
# Type argument "psycopg.pq._pq_ctypes.PGnotify_struct" of "pointer" must be
# a subtype of "ctypes._CData"
def PQnotifies(
arg1: PGconn_struct | None,
) -> _Pointer[PGnotify_struct] | None: ... # type: ignore
def PQputCopyEnd(arg1: PGconn_struct | None, arg2: bytes | None) -> int: ...
# Arg 2 is a _Pointer, reported as _CArgObject by mypy
def PQgetCopyData(arg1: PGconn_struct | None, arg2: Any, arg3: int) -> int: ...
def PQsetResultAttrs(
arg1: PGresult_struct | None,
arg2: int,
arg3: Array[PGresAttDesc_struct], # type: ignore
) -> int: ...
def PQtrace(
arg1: PGconn_struct | None,
arg2: _Pointer[FILE], # type: ignore[type-var]
) -> None: ...
def PQsetTraceFlags(arg1: PGconn_struct | None, arg2: int) -> None: ...
def PQencryptPasswordConn(
arg1: PGconn_struct | None,
arg2: bytes,
arg3: bytes,
arg4: bytes | None,
) -> bytes: ...
def PQpipelineStatus(pgconn: PGconn_struct | None) -> int: ...
def PQenterPipelineMode(pgconn: PGconn_struct | None) -> int: ...
def PQexitPipelineMode(pgconn: PGconn_struct | None) -> int: ...
def PQpipelineSync(pgconn: PGconn_struct | None) -> int: ...
def PQsendFlushRequest(pgconn: PGconn_struct | None) -> int: ...
# Autogenerated section.
# In order to refresh, run:
# python -m psycopg.pq._pq_ctypes
# fmt: off
# autogenerated: start
def PQlibVersion() -> int: ...
def PQconnectdb(arg1: bytes) -> PGconn_struct: ...
def PQconnectStart(arg1: bytes) -> PGconn_struct: ...
def PQconnectPoll(arg1: PGconn_struct | None) -> int: ...
def PQconndefaults() -> Sequence[PQconninfoOption_struct]: ...
def PQconninfoFree(arg1: Sequence[PQconninfoOption_struct]) -> None: ...
def PQconninfo(arg1: PGconn_struct | None) -> Sequence[PQconninfoOption_struct]: ...
def PQconninfoParse(arg1: bytes, arg2: _Pointer[c_char_p]) -> Sequence[PQconninfoOption_struct]: ...
def PQfinish(arg1: PGconn_struct | None) -> None: ...
def PQreset(arg1: PGconn_struct | None) -> None: ...
def PQresetStart(arg1: PGconn_struct | None) -> int: ...
def PQresetPoll(arg1: PGconn_struct | None) -> int: ...
def PQping(arg1: bytes) -> int: ...
def PQdb(arg1: PGconn_struct | None) -> bytes | None: ...
def PQuser(arg1: PGconn_struct | None) -> bytes | None: ...
def PQpass(arg1: PGconn_struct | None) -> bytes | None: ...
def PQhost(arg1: PGconn_struct | None) -> bytes | None: ...
def PQport(arg1: PGconn_struct | None) -> bytes | None: ...
def PQtty(arg1: PGconn_struct | None) -> bytes | None: ...
def PQoptions(arg1: PGconn_struct | None) -> bytes | None: ...
def PQstatus(arg1: PGconn_struct | None) -> int: ...
def PQtransactionStatus(arg1: PGconn_struct | None) -> int: ...
def PQparameterStatus(arg1: PGconn_struct | None, arg2: bytes) -> bytes | None: ...
def PQprotocolVersion(arg1: PGconn_struct | None) -> int: ...
def PQfullProtocolVersion(arg1: PGconn_struct | None) -> int: ...
def PQserverVersion(arg1: PGconn_struct | None) -> int: ...
def PQsocket(arg1: PGconn_struct | None) -> int: ...
def PQbackendPID(arg1: PGconn_struct | None) -> int: ...
def PQconnectionNeedsPassword(arg1: PGconn_struct | None) -> int: ...
def PQconnectionUsedPassword(arg1: PGconn_struct | None) -> int: ...
def PQconnectionUsedGSSAPI(arg1: PGconn_struct | None) -> int: ...
def PQsslInUse(arg1: PGconn_struct | None) -> int: ...
def PQexec(arg1: PGconn_struct | None, arg2: bytes) -> PGresult_struct: ...
def PQexecParams(arg1: PGconn_struct | None, arg2: bytes, arg3: int, arg4: _Pointer[c_uint], arg5: _Pointer[c_char_p], arg6: _Pointer[c_int], arg7: _Pointer[c_int], arg8: int) -> PGresult_struct: ...
def PQdescribePrepared(arg1: PGconn_struct | None, arg2: bytes) -> PGresult_struct: ...
def PQdescribePortal(arg1: PGconn_struct | None, arg2: bytes) -> PGresult_struct: ...
def PQclosePrepared(arg1: PGconn_struct | None, arg2: bytes) -> PGresult_struct: ...
def PQclosePortal(arg1: PGconn_struct | None, arg2: bytes) -> PGresult_struct: ...
def PQresultStatus(arg1: PGresult_struct | None) -> int: ...
def PQresultErrorField(arg1: PGresult_struct | None, arg2: int) -> bytes | None: ...
def PQclear(arg1: PGresult_struct | None) -> None: ...
def PQntuples(arg1: PGresult_struct | None) -> int: ...
def PQnfields(arg1: PGresult_struct | None) -> int: ...
def PQfname(arg1: PGresult_struct | None, arg2: int) -> bytes | None: ...
def PQftable(arg1: PGresult_struct | None, arg2: int) -> int: ...
def PQftablecol(arg1: PGresult_struct | None, arg2: int) -> int: ...
def PQfformat(arg1: PGresult_struct | None, arg2: int) -> int: ...
def PQftype(arg1: PGresult_struct | None, arg2: int) -> int: ...
def PQfmod(arg1: PGresult_struct | None, arg2: int) -> int: ...
def PQfsize(arg1: PGresult_struct | None, arg2: int) -> int: ...
def PQbinaryTuples(arg1: PGresult_struct | None) -> int: ...
def PQgetisnull(arg1: PGresult_struct | None, arg2: int, arg3: int) -> int: ...
def PQgetlength(arg1: PGresult_struct | None, arg2: int, arg3: int) -> int: ...
def PQnparams(arg1: PGresult_struct | None) -> int: ...
def PQparamtype(arg1: PGresult_struct | None, arg2: int) -> int: ...
def PQcmdStatus(arg1: PGresult_struct | None) -> bytes | None: ...
def PQoidValue(arg1: PGresult_struct | None) -> int: ...
def PQescapeLiteral(arg1: PGconn_struct | None, arg2: bytes, arg3: int) -> bytes | None: ...
def PQescapeIdentifier(arg1: PGconn_struct | None, arg2: bytes, arg3: int) -> bytes | None: ...
def PQescapeByteaConn(arg1: PGconn_struct | None, arg2: bytes, arg3: int, arg4: _Pointer[c_ulong]) -> _Pointer[c_ubyte]: ...
def PQescapeBytea(arg1: bytes, arg2: int, arg3: _Pointer[c_ulong]) -> _Pointer[c_ubyte]: ...
def PQunescapeBytea(arg1: bytes, arg2: _Pointer[c_ulong]) -> _Pointer[c_ubyte]: ...
def PQsendQuery(arg1: PGconn_struct | None, arg2: bytes) -> int: ...
def PQsendQueryParams(arg1: PGconn_struct | None, arg2: bytes, arg3: int, arg4: _Pointer[c_uint], arg5: _Pointer[c_char_p], arg6: _Pointer[c_int], arg7: _Pointer[c_int], arg8: int) -> int: ...
def PQsendDescribePrepared(arg1: PGconn_struct | None, arg2: bytes) -> int: ...
def PQsendDescribePortal(arg1: PGconn_struct | None, arg2: bytes) -> int: ...
def PQsendClosePrepared(arg1: PGconn_struct | None, arg2: bytes) -> int: ...
def PQsendClosePortal(arg1: PGconn_struct | None, arg2: bytes) -> int: ...
def PQgetResult(arg1: PGconn_struct | None) -> PGresult_struct: ...
def PQconsumeInput(arg1: PGconn_struct | None) -> int: ...
def PQisBusy(arg1: PGconn_struct | None) -> int: ...
def PQsetnonblocking(arg1: PGconn_struct | None, arg2: int) -> int: ...
def PQisnonblocking(arg1: PGconn_struct | None) -> int: ...
def PQflush(arg1: PGconn_struct | None) -> int: ...
def PQsetSingleRowMode(arg1: PGconn_struct | None) -> int: ...
def PQsetChunkedRowsMode(arg1: PGconn_struct | None, arg2: int) -> int: ...
def PQgetCancel(arg1: PGconn_struct | None) -> PGcancel_struct: ...
def PQfreeCancel(arg1: PGcancel_struct | None) -> None: ...
def PQputCopyData(arg1: PGconn_struct | None, arg2: bytes, arg3: int) -> int: ...
def PQuntrace(arg1: PGconn_struct | None) -> None: ...
def PQfreemem(arg1: Any) -> None: ...
def PQchangePassword(arg1: PGconn_struct | None, arg2: bytes, arg3: bytes) -> PGresult_struct: ...
def PQmakeEmptyPGresult(arg1: PGconn_struct | None, arg2: int) -> PGresult_struct: ...
def PQinitOpenSSL(arg1: int, arg2: int) -> None: ...
# autogenerated: end
# fmt: on

View File

@@ -0,0 +1,345 @@
"""
Protocol objects to represent objects exposed by different pq implementations.
"""
# Copyright (C) 2020 The Psycopg Team
from __future__ import annotations
from typing import TYPE_CHECKING, Any, Protocol, TypeAlias, Union
from collections.abc import Callable, Sequence
from ._enums import Format, Trace
from .._compat import Self
if TYPE_CHECKING:
from .misc import ConninfoOption, PGnotify, PGresAttDesc
# An object implementing the buffer protocol (ish)
Buffer: TypeAlias = Union[bytes, bytearray, memoryview]
class PGconn(Protocol):
notice_handler: Callable[[PGresult], None] | None
notify_handler: Callable[[PGnotify], None] | None
@classmethod
def connect(cls, conninfo: bytes) -> Self: ...
@classmethod
def connect_start(cls, conninfo: bytes) -> Self: ...
def connect_poll(self) -> int: ...
def finish(self) -> None: ...
@property
def info(self) -> list[ConninfoOption]: ...
def reset(self) -> None: ...
def reset_start(self) -> None: ...
def reset_poll(self) -> int: ...
@classmethod
def ping(self, conninfo: bytes) -> int: ...
@property
def db(self) -> bytes: ...
@property
def user(self) -> bytes: ...
@property
def password(self) -> bytes: ...
@property
def host(self) -> bytes: ...
@property
def hostaddr(self) -> bytes: ...
@property
def port(self) -> bytes: ...
@property
def tty(self) -> bytes: ...
@property
def options(self) -> bytes: ...
@property
def status(self) -> int: ...
@property
def transaction_status(self) -> int: ...
def parameter_status(self, name: bytes) -> bytes | None: ...
@property
def error_message(self) -> bytes: ...
def get_error_message(self, encoding: str = ...) -> str: ...
@property
def _encoding(self) -> str: ...
@property
def protocol_version(self) -> int: ...
@property
def full_protocol_version(self) -> int: ...
@property
def server_version(self) -> int: ...
@property
def socket(self) -> int: ...
@property
def backend_pid(self) -> int: ...
@property
def needs_password(self) -> bool: ...
@property
def used_password(self) -> bool: ...
@property
def used_gssapi(self) -> bool: ...
@property
def ssl_in_use(self) -> bool: ...
def exec_(self, command: bytes) -> PGresult: ...
def send_query(self, command: bytes) -> None: ...
def exec_params(
self,
command: bytes,
param_values: Sequence[Buffer | None] | None,
param_types: Sequence[int] | None = None,
param_formats: Sequence[int] | None = None,
result_format: int = Format.TEXT,
) -> PGresult: ...
def send_query_params(
self,
command: bytes,
param_values: Sequence[Buffer | None] | None,
param_types: Sequence[int] | None = None,
param_formats: Sequence[int] | None = None,
result_format: int = Format.TEXT,
) -> None: ...
def send_prepare(
self,
name: bytes,
command: bytes,
param_types: Sequence[int] | None = None,
) -> None: ...
def send_query_prepared(
self,
name: bytes,
param_values: Sequence[Buffer | None] | None,
param_formats: Sequence[int] | None = None,
result_format: int = Format.TEXT,
) -> None: ...
def prepare(
self,
name: bytes,
command: bytes,
param_types: Sequence[int] | None = None,
) -> PGresult: ...
def exec_prepared(
self,
name: bytes,
param_values: Sequence[Buffer] | None,
param_formats: Sequence[int] | None = None,
result_format: int = 0,
) -> PGresult: ...
def describe_prepared(self, name: bytes) -> PGresult: ...
def send_describe_prepared(self, name: bytes) -> None: ...
def describe_portal(self, name: bytes) -> PGresult: ...
def send_describe_portal(self, name: bytes) -> None: ...
def close_prepared(self, name: bytes) -> PGresult: ...
def send_close_prepared(self, name: bytes) -> None: ...
def close_portal(self, name: bytes) -> PGresult: ...
def send_close_portal(self, name: bytes) -> None: ...
def get_result(self) -> PGresult | None: ...
def consume_input(self) -> None: ...
def is_busy(self) -> int: ...
@property
def nonblocking(self) -> int: ...
@nonblocking.setter
def nonblocking(self, arg: int) -> None: ...
def flush(self) -> int: ...
def set_single_row_mode(self) -> None: ...
def set_chunked_rows_mode(self, size: int) -> None: ...
def cancel_conn(self) -> PGcancelConn: ...
def get_cancel(self) -> PGcancel: ...
def notifies(self) -> PGnotify | None: ...
def put_copy_data(self, buffer: Buffer) -> int: ...
def put_copy_end(self, error: bytes | None = None) -> int: ...
def get_copy_data(self, async_: int) -> tuple[int, memoryview]: ...
def trace(self, fileno: int) -> None: ...
def set_trace_flags(self, flags: Trace) -> None: ...
def untrace(self) -> None: ...
def encrypt_password(
self, passwd: bytes, user: bytes, algorithm: bytes | None = None
) -> bytes: ...
def change_password(self, user: bytes, passwd: bytes) -> None: ...
def make_empty_result(self, exec_status: int) -> PGresult: ...
@property
def pipeline_status(self) -> int: ...
def enter_pipeline_mode(self) -> None: ...
def exit_pipeline_mode(self) -> None: ...
def pipeline_sync(self) -> None: ...
def send_flush_request(self) -> None: ...
class PGresult(Protocol):
def clear(self) -> None: ...
@property
def status(self) -> int: ...
@property
def error_message(self) -> bytes: ...
def get_error_message(self, encoding: str = ...) -> str: ...
def error_field(self, fieldcode: int) -> bytes | None: ...
@property
def ntuples(self) -> int: ...
@property
def nfields(self) -> int: ...
def fname(self, column_number: int) -> bytes | None: ...
def ftable(self, column_number: int) -> int: ...
def ftablecol(self, column_number: int) -> int: ...
def fformat(self, column_number: int) -> int: ...
def ftype(self, column_number: int) -> int: ...
def fmod(self, column_number: int) -> int: ...
def fsize(self, column_number: int) -> int: ...
@property
def binary_tuples(self) -> int: ...
def get_value(self, row_number: int, column_number: int) -> bytes | None: ...
@property
def nparams(self) -> int: ...
def param_type(self, param_number: int) -> int: ...
@property
def command_status(self) -> bytes | None: ...
@property
def command_tuples(self) -> int | None: ...
@property
def oid_value(self) -> int: ...
def set_attributes(self, descriptions: list[PGresAttDesc]) -> None: ...
class PGcancelConn(Protocol):
def start(self) -> None: ...
def blocking(self) -> None: ...
def poll(self) -> int: ...
@property
def status(self) -> int: ...
@property
def socket(self) -> int: ...
@property
def error_message(self) -> bytes: ...
def get_error_message(self, encoding: str = ...) -> str: ...
def reset(self) -> None: ...
def finish(self) -> None: ...
class PGcancel(Protocol):
def free(self) -> None: ...
def cancel(self) -> None: ...
class Conninfo(Protocol):
@classmethod
def get_defaults(cls) -> list[ConninfoOption]: ...
@classmethod
def parse(cls, conninfo: bytes) -> list[ConninfoOption]: ...
@classmethod
def _options_from_array(cls, opts: Sequence[Any]) -> list[ConninfoOption]: ...
class Escaping(Protocol):
def __init__(self, conn: PGconn | None = None): ...
def escape_literal(self, data: Buffer) -> bytes: ...
def escape_identifier(self, data: Buffer) -> bytes: ...
def escape_string(self, data: Buffer) -> bytes: ...
def escape_bytea(self, data: Buffer) -> bytes: ...
def unescape_bytea(self, data: Buffer) -> bytes: ...

View File

@@ -0,0 +1,198 @@
"""
Various functionalities to make easier to work with the libpq.
"""
# Copyright (C) 2020 The Psycopg Team
from __future__ import annotations
import os
import re
import sys
import logging
import ctypes.util
from typing import NamedTuple
from pathlib import Path
from functools import cache
from . import abc
from ._enums import ConnStatus, PipelineStatus, TransactionStatus
logger = logging.getLogger("psycopg.pq")
OK = ConnStatus.OK
class PGnotify(NamedTuple):
relname: bytes
be_pid: int
extra: bytes
class ConninfoOption(NamedTuple):
keyword: bytes
envvar: bytes | None
compiled: bytes | None
val: bytes | None
label: bytes
dispchar: bytes
dispsize: int
class PGresAttDesc(NamedTuple):
name: bytes
tableid: int
columnid: int
format: int
typid: int
typlen: int
atttypmod: int
@cache
def find_libpq_full_path() -> str | None:
if sys.platform == "win32":
if (libname := ctypes.util.find_library("libpq.dll")) is None:
return None
libname = str(Path(libname).resolve())
elif sys.platform == "darwin":
libname = ctypes.util.find_library("libpq.dylib")
# (hopefully) temporary hack: libpq not in a standard place
# https://github.com/orgs/Homebrew/discussions/3595
# If pg_config is available and agrees, let's use its indications.
if not libname:
try:
import subprocess as sp
libdir = sp.check_output(["pg_config", "--libdir"]).strip().decode()
if not os.path.exists(libname := os.path.join(libdir, "libpq.dylib")):
libname = None
except Exception as ex:
logger.debug("couldn't use pg_config to find libpq: %s", ex)
else:
libname = ctypes.util.find_library("pq")
return libname
def error_message(
obj: abc.PGconn | abc.PGresult | abc.PGcancelConn, encoding: str = ""
) -> str:
"""
Return an error message from a `PGconn`, `PGresult`, `PGcancelConn`.
The return value is a `!str` (unlike pq data which is usually `!bytes`):
use the connection encoding if available, otherwise the `!encoding`
parameter as a fallback for decoding. Don't raise exceptions on decoding
errors.
"""
# Note: this function is exposed by the pq module and was documented, therefore
# we are not going to remove it, but we don't use it internally.
# Don't pass the encoding if not specified, because different classes have
# different defaults (conn has its own encoding. others default to utf8).
return obj.get_error_message(encoding) if encoding else obj.get_error_message()
# Possible prefixes to strip for error messages, in the known localizations.
# This regular expression is generated from PostgreSQL sources using the
# `tools/update_error_prefixes.py` script
PREFIXES = re.compile(
# autogenerated: start
r"""
^ (?:
DEBUG | INFO | HINWEIS | WARNUNG | FEHLER | LOG | FATAL | PANIK # de
| DEBUG | INFO | NOTICE | WARNING | ERROR | LOG | FATAL | PANIC # en
| DEBUG | INFO | NOTICE | WARNING | ERROR | LOG | FATAL | PANIC # es
| DEBUG | INFO | NOTICE | ATTENTION | ERREUR | LOG | FATAL | PANIC # fr
| DEBUG | INFO | NOTICE | PERINGATAN | ERROR | LOG | FATAL | PANIK # id
| DEBUG | INFO | NOTIFICA | ATTENZIONE | ERRORE | LOG | FATALE | PANICO # it
| DEBUG | INFO | NOTICE | WARNING | ERROR | LOG | FATAL | PANIC # ja
| 디버그 | 정보 | 알림 | 경고 | 오류 | 로그 | 치명적오류 | 손상 # ko
| DEBUG | INFORMACJA | UWAGA | OSTRZEŻENIE | BŁĄD | DZIENNIK | KATASTROFALNY | PANIKA # pl
| DEPURAÇÃO | INFO | NOTA | AVISO | ERRO | LOG | FATAL | PÂNICO # pt_BR
| ОТЛАДКА | ИНФОРМАЦИЯ | ЗАМЕЧАНИЕ | ПРЕДУПРЕЖДЕНИЕ | ОШИБКА | СООБЩЕНИЕ | ВАЖНО | ПАНИКА # ru
| DEBUG | INFO | NOTIS | VARNING | FEL | LOGG | FATALT | PANIK # sv
| DEBUG | BİLGİ | NOT | UYARI | HATA | LOG | ÖLÜMCÜL\ \(FATAL\) | KRİTİK # tr
| НАЛАГОДЖЕННЯ | ІНФОРМАЦІЯ | ПОВІДОМЛЕННЯ | ПОПЕРЕДЖЕННЯ | ПОМИЛКА | ЗАПИСУВАННЯ | ФАТАЛЬНО | ПАНІКА # uk
| 调试 | 信息 | 注意 | 警告 | 错误 | 日志 | 致命错误 | 比致命错误还过分的错误 # zh_CN
) : \s+
""", # noqa: E501
# autogenerated: end
re.VERBOSE | re.MULTILINE,
)
def strip_severity(msg: str) -> str:
"""Strip severity and whitespaces from error message."""
if m := PREFIXES.match(msg):
msg = msg[m.span()[1] :]
return msg.strip()
def _clean_error_message(msg: bytes, encoding: str) -> str:
if smsg := msg.decode(encoding, "replace"):
return strip_severity(smsg)
else:
return "no error details available"
def connection_summary(pgconn: abc.PGconn) -> str:
"""
Return summary information on a connection.
Useful for __repr__
"""
parts = []
if pgconn.status == OK:
# Put together the [STATUS]
status: str = TransactionStatus(pgconn.transaction_status).name
if pgconn.pipeline_status:
status += f", pipeline={PipelineStatus(pgconn.pipeline_status).name}"
# Put together the (CONNECTION)
if not pgconn.host.startswith(b"/"):
parts.append(("host", pgconn.host.decode()))
if (port := pgconn.port.decode() or get_compiled_port()) != "5432":
parts.append(("port", port))
if pgconn.user != pgconn.db:
parts.append(("user", pgconn.user.decode()))
parts.append(("database", pgconn.db.decode()))
else:
try:
status = ConnStatus(pgconn.status).name
except ValueError:
# It might happen if a new status on connection appears
# before upgrading the ConnStatus enum.
status = f"status={pgconn.status} (unkndown)"
if sparts := " ".join("%s=%s" % part for part in parts):
sparts = f" ({sparts})"
return f"[{status}]{sparts}"
def version_pretty(version: int) -> str:
"""
Return a pretty representation of a PostgreSQL version
For instance: 140002 -> 14.2, 90610 -> 9.6.10
"""
version, patch = divmod(version, 100)
major, minor = divmod(version, 100)
if major >= 10 and minor == 0:
return f"{major}.{patch}"
else:
return f"{major}.{minor}.{patch}"
@cache
def get_compiled_port() -> str:
"""Return the default port compiled with the libpq."""
from psycopg._conninfo_utils import get_param_def
info = get_param_def("port")
return info.compiled if info and info.compiled else ""

File diff suppressed because it is too large Load Diff