Profiling some time ago showed that these functions take relevant time.
Other paths will be reviewed.
https://www.varrazzo.com/blog/2020/05/19/a-trip-into-optimisation/
cdef libpq.PGconn *pgconn_ptr = pgconn.pgconn_ptr
cdef int status
cdef libpq.PGnotify *notify
+ cdef libpq.PGresult *pgres
+ cdef int cires, ibres
# Start the generator by sending the connection fd, which won't change
# during the query process.
status = yield WAIT_RW
if status & READY_R:
- # This call may read notifies which will be saved in the
- # PGconn buffer and passed to Python later.
- if 1 != libpq.PQconsumeInput(pgconn_ptr):
+ with nogil:
+ # This call may read notifies which will be saved in the
+ # PGconn buffer and passed to Python later.
+ cires = libpq.PQconsumeInput(pgconn_ptr)
+ if 1 != cires:
raise pq.PQerror(
f"consuming input failed: {pq.error_message(pgconn)}")
continue
# Fetching the result
while 1:
- if 1 != libpq.PQconsumeInput(pgconn_ptr):
+ with nogil:
+ cires = libpq.PQconsumeInput(pgconn_ptr)
+ if cires == 1:
+ ibres = libpq.PQisBusy(pgconn_ptr)
+
+ if 1 != cires:
raise pq.PQerror(
f"consuming input failed: {pq.error_message(pgconn)}")
- if libpq.PQisBusy(pgconn_ptr):
+ if ibres:
yield WAIT_R
continue
break
libpq.PQfreemem(notify)
- res = libpq.PQgetResult(pgconn_ptr)
- if res is NULL:
+ pgres = libpq.PQgetResult(pgconn_ptr)
+ if pgres is NULL:
break
- results.append(PGresult._from_ptr(res))
+ results.append(PGresult._from_ptr(pgres))
- status = libpq.PQresultStatus(res)
+ status = libpq.PQresultStatus(pgres)
if status in (libpq.PGRES_COPY_IN, libpq.PGRES_COPY_OUT, libpq.PGRES_COPY_BOTH):
# After entering copy mode the libpq will create a phony result
# for every request so let's break the endless loop.
int PQsendDescribePrepared(PGconn *conn, const char *stmtName)
int PQsendDescribePortal(PGconn *conn, const char *portalName)
PGresult *PQgetResult(PGconn *conn)
- int PQconsumeInput(PGconn *conn)
- int PQisBusy(PGconn *conn)
+ int PQconsumeInput(PGconn *conn) nogil
+ int PQisBusy(PGconn *conn) nogil
int PQsetnonblocking(PGconn *conn, int arg)
int PQisnonblocking(const PGconn *conn)
int PQflush(PGconn *conn)
int PQcancel(PGcancel *cancel, char *errbuf, int errbufsize)
# 33.8. Asynchronous Notification
- PGnotify *PQnotifies(PGconn *conn)
+ PGnotify *PQnotifies(PGconn *conn) nogil
# 33.9. Functions Associated with the COPY Command
int PQputCopyData(PGconn *conn, const char *buffer, int nbytes)
int PQgetCopyData(PGconn *conn, char **buffer, int async)
# 33.11. Miscellaneous Functions
- void PQfreemem(void *ptr)
+ void PQfreemem(void *ptr) nogil
void PQconninfoFree(PQconninfoOption *connOptions)
PGresult *PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
int PQsetResultAttrs(PGresult *res, int numAttributes, PGresAttDesc *attDescs)
return impl.PQlibVersion()
-cdef void notice_receiver(void *arg, const impl.PGresult *res_ptr):
+cdef void notice_receiver(void *arg, const impl.PGresult *res_ptr) with gil:
cdef PGconn pgconn = <object>arg
if pgconn.notice_handler is None:
return
raise PQerror(f"consuming input failed: {error_message(self)}")
def is_busy(self) -> int:
- return impl.PQisBusy(self.pgconn_ptr)
+ cdef int rv
+ with nogil:
+ rv = impl.PQisBusy(self.pgconn_ptr)
+ return rv
@property
def nonblocking(self) -> int:
return PGcancel._from_ptr(ptr)
def notifies(self) -> Optional[PGnotify]:
- cdef impl.PGnotify *ptr = impl.PQnotifies(self.pgconn_ptr)
+ cdef impl.PGnotify *ptr
+ with nogil:
+ ptr = impl.PQnotifies(self.pgconn_ptr)
+
if ptr:
ret = PGnotify(ptr.relname, ptr.be_pid, ptr.extra)
impl.PQfreemem(ptr)