Merge branch 'master' into named-callproc

This commit is contained in:
Daniele Varrazzo 2016-12-26 03:16:11 +01:00
commit 1957389bea
5 changed files with 44 additions and 71 deletions

View File

@ -80,6 +80,7 @@ struct cursorObject {
char *qattr; /* quoting attr, used when quoting strings */ char *qattr; /* quoting attr, used when quoting strings */
char *notice; /* a notice from the backend */ char *notice; /* a notice from the backend */
char *name; /* this cursor name */ char *name; /* this cursor name */
char *qname; /* this cursor name, quoted */
PyObject *string_types; /* a set of typecasters for string types */ PyObject *string_types; /* a set of typecasters for string types */
PyObject *binary_types; /* a set of typecasters for binary types */ PyObject *binary_types; /* a set of typecasters for binary types */

View File

@ -55,7 +55,7 @@ psyco_curs_close(cursorObject *self)
goto exit; goto exit;
} }
if (self->name != NULL) { if (self->qname != NULL) {
char buffer[128]; char buffer[128];
PGTransactionStatusType status; PGTransactionStatusType status;
@ -68,7 +68,7 @@ psyco_curs_close(cursorObject *self)
if (!(status == PQTRANS_UNKNOWN || status == PQTRANS_INERROR)) { if (!(status == PQTRANS_UNKNOWN || status == PQTRANS_INERROR)) {
EXC_IF_NO_MARK(self); EXC_IF_NO_MARK(self);
PyOS_snprintf(buffer, 127, "CLOSE \"%s\"", self->name); PyOS_snprintf(buffer, 127, "CLOSE %s", self->qname);
if (pq_execute(self, buffer, 0, 0, 1) == -1) return NULL; if (pq_execute(self, buffer, 0, 0, 1) == -1) return NULL;
} }
else { else {
@ -422,10 +422,10 @@ _psyco_curs_execute(cursorObject *self,
goto exit; goto exit;
} }
if (self->name != NULL) { if (self->qname != NULL) {
self->query = Bytes_FromFormat( self->query = Bytes_FromFormat(
"DECLARE \"%s\" %sCURSOR %s HOLD FOR %s", "DECLARE %s %sCURSOR %s HOLD FOR %s",
self->name, self->qname,
scroll, scroll,
self->withhold ? "WITH" : "WITHOUT", self->withhold ? "WITH" : "WITHOUT",
Bytes_AS_STRING(fquery)); Bytes_AS_STRING(fquery));
@ -436,10 +436,10 @@ _psyco_curs_execute(cursorObject *self,
} }
} }
else { else {
if (self->name != NULL) { if (self->qname != NULL) {
self->query = Bytes_FromFormat( self->query = Bytes_FromFormat(
"DECLARE \"%s\" %sCURSOR %s HOLD FOR %s", "DECLARE %s %sCURSOR %s HOLD FOR %s",
self->name, self->qname,
scroll, scroll,
self->withhold ? "WITH" : "WITHOUT", self->withhold ? "WITH" : "WITHOUT",
Bytes_AS_STRING(operation)); Bytes_AS_STRING(operation));
@ -768,13 +768,13 @@ psyco_curs_fetchone(cursorObject *self)
if (_psyco_curs_prefetch(self) < 0) return NULL; if (_psyco_curs_prefetch(self) < 0) return NULL;
EXC_IF_NO_TUPLES(self); EXC_IF_NO_TUPLES(self);
if (self->name != NULL) { if (self->qname != NULL) {
char buffer[128]; char buffer[128];
EXC_IF_NO_MARK(self); EXC_IF_NO_MARK(self);
EXC_IF_ASYNC_IN_PROGRESS(self, fetchone); EXC_IF_ASYNC_IN_PROGRESS(self, fetchone);
EXC_IF_TPC_PREPARED(self->conn, fetchone); EXC_IF_TPC_PREPARED(self->conn, fetchone);
PyOS_snprintf(buffer, 127, "FETCH FORWARD 1 FROM \"%s\"", self->name); PyOS_snprintf(buffer, 127, "FETCH FORWARD 1 FROM %s", self->qname);
if (pq_execute(self, buffer, 0, 0, self->withhold) == -1) return NULL; if (pq_execute(self, buffer, 0, 0, self->withhold) == -1) return NULL;
if (_psyco_curs_prefetch(self) < 0) return NULL; if (_psyco_curs_prefetch(self) < 0) return NULL;
} }
@ -823,8 +823,8 @@ psyco_curs_next_named(cursorObject *self)
if (self->row >= self->rowcount) { if (self->row >= self->rowcount) {
char buffer[128]; char buffer[128];
PyOS_snprintf(buffer, 127, "FETCH FORWARD %ld FROM \"%s\"", PyOS_snprintf(buffer, 127, "FETCH FORWARD %ld FROM %s",
self->itersize, self->name); self->itersize, self->qname);
if (pq_execute(self, buffer, 0, 0, self->withhold) == -1) return NULL; if (pq_execute(self, buffer, 0, 0, self->withhold) == -1) return NULL;
if (_psyco_curs_prefetch(self) < 0) return NULL; if (_psyco_curs_prefetch(self) < 0) return NULL;
} }
@ -886,14 +886,14 @@ psyco_curs_fetchmany(cursorObject *self, PyObject *args, PyObject *kwords)
if (_psyco_curs_prefetch(self) < 0) return NULL; if (_psyco_curs_prefetch(self) < 0) return NULL;
EXC_IF_NO_TUPLES(self); EXC_IF_NO_TUPLES(self);
if (self->name != NULL) { if (self->qname != NULL) {
char buffer[128]; char buffer[128];
EXC_IF_NO_MARK(self); EXC_IF_NO_MARK(self);
EXC_IF_ASYNC_IN_PROGRESS(self, fetchmany); EXC_IF_ASYNC_IN_PROGRESS(self, fetchmany);
EXC_IF_TPC_PREPARED(self->conn, fetchone); EXC_IF_TPC_PREPARED(self->conn, fetchone);
PyOS_snprintf(buffer, 127, "FETCH FORWARD %d FROM \"%s\"", PyOS_snprintf(buffer, 127, "FETCH FORWARD %d FROM %s",
(int)size, self->name); (int)size, self->qname);
if (pq_execute(self, buffer, 0, 0, self->withhold) == -1) { goto exit; } if (pq_execute(self, buffer, 0, 0, self->withhold) == -1) { goto exit; }
if (_psyco_curs_prefetch(self) < 0) { goto exit; } if (_psyco_curs_prefetch(self) < 0) { goto exit; }
} }
@ -962,13 +962,13 @@ psyco_curs_fetchall(cursorObject *self)
if (_psyco_curs_prefetch(self) < 0) return NULL; if (_psyco_curs_prefetch(self) < 0) return NULL;
EXC_IF_NO_TUPLES(self); EXC_IF_NO_TUPLES(self);
if (self->name != NULL) { if (self->qname != NULL) {
char buffer[128]; char buffer[128];
EXC_IF_NO_MARK(self); EXC_IF_NO_MARK(self);
EXC_IF_ASYNC_IN_PROGRESS(self, fetchall); EXC_IF_ASYNC_IN_PROGRESS(self, fetchall);
EXC_IF_TPC_PREPARED(self->conn, fetchall); EXC_IF_TPC_PREPARED(self->conn, fetchall);
PyOS_snprintf(buffer, 127, "FETCH FORWARD ALL FROM \"%s\"", self->name); PyOS_snprintf(buffer, 127, "FETCH FORWARD ALL FROM %s", self->qname);
if (pq_execute(self, buffer, 0, 0, self->withhold) == -1) { goto exit; } if (pq_execute(self, buffer, 0, 0, self->withhold) == -1) { goto exit; }
if (_psyco_curs_prefetch(self) < 0) { goto exit; } if (_psyco_curs_prefetch(self) < 0) { goto exit; }
} }
@ -1079,7 +1079,7 @@ psyco_curs_callproc(cursorObject *self, PyObject *args)
if (!(cpname = Bytes_AsString(pname))) { goto exit; } if (!(cpname = Bytes_AsString(pname))) { goto exit; }
if (!(scpnames[i] = psycopg_escape_identifier( if (!(scpnames[i] = psycopg_escape_identifier(
self->conn->pgconn, cpname, strlen(cpname)))) { self->conn, cpname, 0))) {
goto exit; goto exit;
} }
@ -1236,7 +1236,7 @@ psyco_curs_scroll(cursorObject *self, PyObject *args, PyObject *kwargs)
/* if the cursor is not named we have the full result set and we can do /* if the cursor is not named we have the full result set and we can do
our own calculations to scroll; else we just delegate the scrolling our own calculations to scroll; else we just delegate the scrolling
to the MOVE SQL statement */ to the MOVE SQL statement */
if (self->name == NULL) { if (self->qname == NULL) {
if (strcmp(mode, "relative") == 0) { if (strcmp(mode, "relative") == 0) {
newpos = self->row + value; newpos = self->row + value;
} else if (strcmp( mode, "absolute") == 0) { } else if (strcmp( mode, "absolute") == 0) {
@ -1264,11 +1264,11 @@ psyco_curs_scroll(cursorObject *self, PyObject *args, PyObject *kwargs)
EXC_IF_TPC_PREPARED(self->conn, scroll); EXC_IF_TPC_PREPARED(self->conn, scroll);
if (strcmp(mode, "absolute") == 0) { if (strcmp(mode, "absolute") == 0) {
PyOS_snprintf(buffer, 127, "MOVE ABSOLUTE %d FROM \"%s\"", PyOS_snprintf(buffer, 127, "MOVE ABSOLUTE %d FROM %s",
value, self->name); value, self->qname);
} }
else { else {
PyOS_snprintf(buffer, 127, "MOVE %d FROM \"%s\"", value, self->name); PyOS_snprintf(buffer, 127, "MOVE %d FROM %s", value, self->qname);
} }
if (pq_execute(self, buffer, 0, 0, self->withhold) == -1) return NULL; if (pq_execute(self, buffer, 0, 0, self->withhold) == -1) return NULL;
if (_psyco_curs_prefetch(self) < 0) return NULL; if (_psyco_curs_prefetch(self) < 0) return NULL;
@ -1898,7 +1898,10 @@ cursor_setup(cursorObject *self, connectionObject *conn, const char *name)
Dprintf("cursor_setup: parameters: name = %s, conn = %p", name, conn); Dprintf("cursor_setup: parameters: name = %s, conn = %p", name, conn);
if (name) { if (name) {
if (!(self->name = psycopg_escape_identifier_easy(name, 0))) { if (0 > psycopg_strdup(&self->name, name, 0)) {
return -1;
}
if (!(self->qname = psycopg_escape_identifier(conn, name, 0))) {
return -1; return -1;
} }
} }
@ -1974,6 +1977,7 @@ cursor_dealloc(PyObject* obj)
cursor_clear(self); cursor_clear(self);
PyMem_Free(self->name); PyMem_Free(self->name);
PQfreemem(self->qname);
CLEARPGRES(self->pgres); CLEARPGRES(self->pgres);

View File

@ -128,8 +128,8 @@ RAISES HIDDEN PyObject *psyco_set_error(PyObject *exc, cursorObject *curs, const
HIDDEN char *psycopg_escape_string(connectionObject *conn, HIDDEN char *psycopg_escape_string(connectionObject *conn,
const char *from, Py_ssize_t len, char *to, Py_ssize_t *tolen); const char *from, Py_ssize_t len, char *to, Py_ssize_t *tolen);
HIDDEN char *psycopg_escape_identifier_easy(const char *from, Py_ssize_t len); HIDDEN char *psycopg_escape_identifier(connectionObject *conn,
HIDDEN char *psycopg_escape_identifier(PGconn *pgconn, const char *str, size_t length); const char *str, size_t len);
HIDDEN int psycopg_strdup(char **to, const char *from, Py_ssize_t len); HIDDEN int psycopg_strdup(char **to, const char *from, Py_ssize_t len);
HIDDEN int psycopg_is_text_file(PyObject *f); HIDDEN int psycopg_is_text_file(PyObject *f);

View File

@ -190,7 +190,7 @@ psyco_quote_ident(PyObject *self, PyObject *args, PyObject *kwargs)
str = Bytes_AS_STRING(ident); str = Bytes_AS_STRING(ident);
quoted = psycopg_escape_identifier(conn->pgconn, str, strlen(str)); quoted = psycopg_escape_identifier(conn, str, strlen(str));
if (!quoted) { if (!quoted) {
goto exit; goto exit;
} }

View File

@ -90,68 +90,36 @@ psycopg_escape_string(connectionObject *conn, const char *from, Py_ssize_t len,
return to; return to;
} }
/* Escape a string to build a valid PostgreSQL identifier. /* Escape a string for inclusion in a query as identifier.
* *
* Allocate a new buffer on the Python heap containing the new string.
* 'len' is optional: if 0 the length is calculated. * 'len' is optional: if 0 the length is calculated.
* *
* The returned string doesn't include quotes. * Return a string allocated by Postgres: free it using PQfreemem
*
* WARNING: this function is not so safe to allow untrusted input: it does no
* check for multibyte chars. Functions otherwise reliant on PostgreSQL 9.0
* and above should use the below function psycopg_escape_identifier instead.
*/
char *
psycopg_escape_identifier_easy(const char *from, Py_ssize_t len)
{
char *rv;
const char *src;
char *dst;
if (!len) { len = strlen(from); }
if (!(rv = PyMem_New(char, 1 + 2 * len))) {
PyErr_NoMemory();
return NULL;
}
/* The only thing to do is double quotes */
for (src = from, dst = rv; *src; ++src, ++dst) {
*dst = *src;
if ('"' == *src) {
*++dst = '"';
}
}
*dst = '\0';
return rv;
}
/* Call PostgreSQL 9.0+ function PQescapeIdentifier.
*
* In case of error set a Python exception. * In case of error set a Python exception.
*/ */
char * char *
psycopg_escape_identifier(PGconn *pgconn, const char *str, size_t length) psycopg_escape_identifier(connectionObject *conn, const char *str, size_t len)
{ {
char *rv = NULL; char *rv = NULL;
#if PG_VERSION_NUM >= 90000 if (!conn || !conn->pgconn) {
rv = PQescapeIdentifier(pgconn, str, length); PyErr_SetString(InterfaceError, "connection not valid");
goto exit;
}
if (!len) { len = strlen(str); }
rv = PQescapeIdentifier(conn->pgconn, str, len);
if (!rv) { if (!rv) {
char *msg; char *msg;
msg = PQerrorMessage(pgconn); msg = PQerrorMessage(conn->pgconn);
if (!msg || !msg[0]) { if (!msg || !msg[0]) {
msg = "no message provided"; msg = "no message provided";
} }
PyErr_Format(InterfaceError, "failed to escape identifier: %s", msg); PyErr_Format(InterfaceError, "failed to escape identifier: %s", msg);
} }
#else
PyErr_Format(PyExc_NotImplementedError,
"PQescapeIdentifier requires psycopg2 compiled against libpq 9.0+");
#endif
exit:
return rv; return rv;
} }