mirror of
https://github.com/psycopg/psycopg2.git
synced 2024-11-25 10:23:43 +03:00
Escape table and column names in cursor.copy_from() and .copy_to()
This commit is contained in:
parent
e5ad0ab2d9
commit
8a2deb39ed
2
NEWS
2
NEWS
|
@ -7,6 +7,8 @@ What's new in psycopg 2.9
|
||||||
- Dropped support for Python 2.7, 3.4, 3.5 (:tickets:`#1198, #1000, #1197`).
|
- Dropped support for Python 2.7, 3.4, 3.5 (:tickets:`#1198, #1000, #1197`).
|
||||||
- ``with connection`` starts a transaction on autocommit transactions too
|
- ``with connection`` starts a transaction on autocommit transactions too
|
||||||
(:ticket:`#941`).
|
(:ticket:`#941`).
|
||||||
|
- Escape table and column names in `~cursor.copy_from()` and
|
||||||
|
`~cursor.copy_to()`.
|
||||||
- Connection exceptions with sqlstate ``08XXX`` reclassified as
|
- Connection exceptions with sqlstate ``08XXX`` reclassified as
|
||||||
`~psycopg2.OperationalError` (a subclass of the previously used
|
`~psycopg2.OperationalError` (a subclass of the previously used
|
||||||
`~psycopg2.DatabaseError`) (:ticket:`#1148`).
|
`~psycopg2.DatabaseError`) (:ticket:`#1148`).
|
||||||
|
|
|
@ -1303,11 +1303,9 @@ exit:
|
||||||
/* Return a newly allocated buffer containing the list of columns to be
|
/* Return a newly allocated buffer containing the list of columns to be
|
||||||
* copied. On error return NULL and set an exception.
|
* copied. On error return NULL and set an exception.
|
||||||
*/
|
*/
|
||||||
static char *_psyco_curs_copy_columns(PyObject *columns)
|
static char *_psyco_curs_copy_columns(cursorObject *self, PyObject *columns)
|
||||||
{
|
{
|
||||||
PyObject *col, *coliter;
|
PyObject *col, *coliter;
|
||||||
Py_ssize_t collen;
|
|
||||||
char *colname;
|
|
||||||
char *columnlist = NULL;
|
char *columnlist = NULL;
|
||||||
Py_ssize_t bufsize = 512;
|
Py_ssize_t bufsize = 512;
|
||||||
Py_ssize_t offset = 1;
|
Py_ssize_t offset = 1;
|
||||||
|
@ -1333,15 +1331,28 @@ static char *_psyco_curs_copy_columns(PyObject *columns)
|
||||||
columnlist[0] = '(';
|
columnlist[0] = '(';
|
||||||
|
|
||||||
while ((col = PyIter_Next(coliter)) != NULL) {
|
while ((col = PyIter_Next(coliter)) != NULL) {
|
||||||
|
Py_ssize_t collen;
|
||||||
|
char *colname;
|
||||||
|
char *quoted_colname;
|
||||||
|
|
||||||
if (!(col = psyco_ensure_bytes(col))) {
|
if (!(col = psyco_ensure_bytes(col))) {
|
||||||
Py_DECREF(coliter);
|
Py_DECREF(coliter);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
Bytes_AsStringAndSize(col, &colname, &collen);
|
Bytes_AsStringAndSize(col, &colname, &collen);
|
||||||
|
if (!(quoted_colname = psyco_escape_identifier(
|
||||||
|
self->conn, colname, collen))) {
|
||||||
|
Py_DECREF(col);
|
||||||
|
Py_DECREF(coliter);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
collen = strlen(quoted_colname);
|
||||||
|
|
||||||
while (offset + collen > bufsize - 2) {
|
while (offset + collen > bufsize - 2) {
|
||||||
char *tmp;
|
char *tmp;
|
||||||
bufsize *= 2;
|
bufsize *= 2;
|
||||||
if (NULL == (tmp = PyMem_Realloc(columnlist, bufsize))) {
|
if (NULL == (tmp = PyMem_Realloc(columnlist, bufsize))) {
|
||||||
|
PQfreemem(quoted_colname);
|
||||||
Py_DECREF(col);
|
Py_DECREF(col);
|
||||||
Py_DECREF(coliter);
|
Py_DECREF(coliter);
|
||||||
PyErr_NoMemory();
|
PyErr_NoMemory();
|
||||||
|
@ -1349,10 +1360,11 @@ static char *_psyco_curs_copy_columns(PyObject *columns)
|
||||||
}
|
}
|
||||||
columnlist = tmp;
|
columnlist = tmp;
|
||||||
}
|
}
|
||||||
strncpy(&columnlist[offset], colname, collen);
|
strncpy(&columnlist[offset], quoted_colname, collen);
|
||||||
offset += collen;
|
offset += collen;
|
||||||
columnlist[offset++] = ',';
|
columnlist[offset++] = ',';
|
||||||
Py_DECREF(col);
|
Py_DECREF(col);
|
||||||
|
PQfreemem(quoted_colname);
|
||||||
}
|
}
|
||||||
Py_DECREF(coliter);
|
Py_DECREF(coliter);
|
||||||
|
|
||||||
|
@ -1399,8 +1411,9 @@ curs_copy_from(cursorObject *self, PyObject *args, PyObject *kwargs)
|
||||||
char *columnlist = NULL;
|
char *columnlist = NULL;
|
||||||
char *quoted_delimiter = NULL;
|
char *quoted_delimiter = NULL;
|
||||||
char *quoted_null = NULL;
|
char *quoted_null = NULL;
|
||||||
|
char *quoted_table_name = NULL;
|
||||||
const char *table_name;
|
const char *table_name;
|
||||||
|
|
||||||
Py_ssize_t bufsize = DEFAULT_COPYBUFF;
|
Py_ssize_t bufsize = DEFAULT_COPYBUFF;
|
||||||
PyObject *file, *columns = NULL, *res = NULL;
|
PyObject *file, *columns = NULL, *res = NULL;
|
||||||
|
|
||||||
|
@ -1421,8 +1434,9 @@ curs_copy_from(cursorObject *self, PyObject *args, PyObject *kwargs)
|
||||||
EXC_IF_GREEN(copy_from);
|
EXC_IF_GREEN(copy_from);
|
||||||
EXC_IF_TPC_PREPARED(self->conn, copy_from);
|
EXC_IF_TPC_PREPARED(self->conn, copy_from);
|
||||||
|
|
||||||
if (NULL == (columnlist = _psyco_curs_copy_columns(columns)))
|
if (!(columnlist = _psyco_curs_copy_columns(self, columns))) {
|
||||||
goto exit;
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
if (!(quoted_delimiter = psyco_escape_string(
|
if (!(quoted_delimiter = psyco_escape_string(
|
||||||
self->conn, sep, -1, NULL, NULL))) {
|
self->conn, sep, -1, NULL, NULL))) {
|
||||||
|
@ -1434,7 +1448,12 @@ curs_copy_from(cursorObject *self, PyObject *args, PyObject *kwargs)
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
query_size = strlen(command) + strlen(table_name) + strlen(columnlist)
|
if (!(quoted_table_name = psyco_escape_identifier(
|
||||||
|
self->conn, table_name, -1))) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
query_size = strlen(command) + strlen(quoted_table_name) + strlen(columnlist)
|
||||||
+ strlen(quoted_delimiter) + strlen(quoted_null) + 1;
|
+ strlen(quoted_delimiter) + strlen(quoted_null) + 1;
|
||||||
if (!(query = PyMem_New(char, query_size))) {
|
if (!(query = PyMem_New(char, query_size))) {
|
||||||
PyErr_NoMemory();
|
PyErr_NoMemory();
|
||||||
|
@ -1442,7 +1461,7 @@ curs_copy_from(cursorObject *self, PyObject *args, PyObject *kwargs)
|
||||||
}
|
}
|
||||||
|
|
||||||
PyOS_snprintf(query, query_size, command,
|
PyOS_snprintf(query, query_size, command,
|
||||||
table_name, columnlist, quoted_delimiter, quoted_null);
|
quoted_table_name, columnlist, quoted_delimiter, quoted_null);
|
||||||
|
|
||||||
Dprintf("curs_copy_from: query = %s", query);
|
Dprintf("curs_copy_from: query = %s", query);
|
||||||
|
|
||||||
|
@ -1469,6 +1488,9 @@ curs_copy_from(cursorObject *self, PyObject *args, PyObject *kwargs)
|
||||||
Py_CLEAR(self->copyfile);
|
Py_CLEAR(self->copyfile);
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
|
if (quoted_table_name) {
|
||||||
|
PQfreemem(quoted_table_name);
|
||||||
|
}
|
||||||
PyMem_Free(columnlist);
|
PyMem_Free(columnlist);
|
||||||
PyMem_Free(quoted_delimiter);
|
PyMem_Free(quoted_delimiter);
|
||||||
PyMem_Free(quoted_null);
|
PyMem_Free(quoted_null);
|
||||||
|
@ -1499,6 +1521,7 @@ curs_copy_to(cursorObject *self, PyObject *args, PyObject *kwargs)
|
||||||
char *quoted_null = NULL;
|
char *quoted_null = NULL;
|
||||||
|
|
||||||
const char *table_name;
|
const char *table_name;
|
||||||
|
char *quoted_table_name = NULL;
|
||||||
PyObject *file = NULL, *columns = NULL, *res = NULL;
|
PyObject *file = NULL, *columns = NULL, *res = NULL;
|
||||||
|
|
||||||
if (!PyArg_ParseTupleAndKeywords(
|
if (!PyArg_ParseTupleAndKeywords(
|
||||||
|
@ -1518,8 +1541,14 @@ curs_copy_to(cursorObject *self, PyObject *args, PyObject *kwargs)
|
||||||
EXC_IF_GREEN(copy_to);
|
EXC_IF_GREEN(copy_to);
|
||||||
EXC_IF_TPC_PREPARED(self->conn, copy_to);
|
EXC_IF_TPC_PREPARED(self->conn, copy_to);
|
||||||
|
|
||||||
if (NULL == (columnlist = _psyco_curs_copy_columns(columns)))
|
if (!(quoted_table_name = psyco_escape_identifier(
|
||||||
|
self->conn, table_name, -1))) {
|
||||||
goto exit;
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(columnlist = _psyco_curs_copy_columns(self, columns))) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
if (!(quoted_delimiter = psyco_escape_string(
|
if (!(quoted_delimiter = psyco_escape_string(
|
||||||
self->conn, sep, -1, NULL, NULL))) {
|
self->conn, sep, -1, NULL, NULL))) {
|
||||||
|
@ -1531,7 +1560,7 @@ curs_copy_to(cursorObject *self, PyObject *args, PyObject *kwargs)
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
query_size = strlen(command) + strlen(table_name) + strlen(columnlist)
|
query_size = strlen(command) + strlen(quoted_table_name) + strlen(columnlist)
|
||||||
+ strlen(quoted_delimiter) + strlen(quoted_null) + 1;
|
+ strlen(quoted_delimiter) + strlen(quoted_null) + 1;
|
||||||
if (!(query = PyMem_New(char, query_size))) {
|
if (!(query = PyMem_New(char, query_size))) {
|
||||||
PyErr_NoMemory();
|
PyErr_NoMemory();
|
||||||
|
@ -1539,7 +1568,7 @@ curs_copy_to(cursorObject *self, PyObject *args, PyObject *kwargs)
|
||||||
}
|
}
|
||||||
|
|
||||||
PyOS_snprintf(query, query_size, command,
|
PyOS_snprintf(query, query_size, command,
|
||||||
table_name, columnlist, quoted_delimiter, quoted_null);
|
quoted_table_name, columnlist, quoted_delimiter, quoted_null);
|
||||||
|
|
||||||
Dprintf("curs_copy_to: query = %s", query);
|
Dprintf("curs_copy_to: query = %s", query);
|
||||||
|
|
||||||
|
@ -1560,6 +1589,9 @@ curs_copy_to(cursorObject *self, PyObject *args, PyObject *kwargs)
|
||||||
Py_CLEAR(self->copyfile);
|
Py_CLEAR(self->copyfile);
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
|
if (quoted_table_name) {
|
||||||
|
PQfreemem(quoted_table_name);
|
||||||
|
}
|
||||||
PyMem_Free(columnlist);
|
PyMem_Free(columnlist);
|
||||||
PyMem_Free(quoted_delimiter);
|
PyMem_Free(quoted_delimiter);
|
||||||
PyMem_Free(quoted_null);
|
PyMem_Free(quoted_null);
|
||||||
|
|
|
@ -263,6 +263,24 @@ class CopyTests(ConnectingTestCase):
|
||||||
curs.execute("select count(*) from manycols;")
|
curs.execute("select count(*) from manycols;")
|
||||||
self.assertEqual(curs.fetchone()[0], 2)
|
self.assertEqual(curs.fetchone()[0], 2)
|
||||||
|
|
||||||
|
def test_copy_funny_names(self):
|
||||||
|
cols = ["select", "insert", "group"]
|
||||||
|
|
||||||
|
curs = self.conn.cursor()
|
||||||
|
curs.execute('CREATE TEMPORARY TABLE "select" (%s)' % ',\n'.join(
|
||||||
|
['"%s" int' % c for c in cols]))
|
||||||
|
curs.execute('INSERT INTO "select" DEFAULT VALUES')
|
||||||
|
|
||||||
|
f = StringIO()
|
||||||
|
curs.copy_to(f, "select", columns=cols)
|
||||||
|
f.seek(0)
|
||||||
|
self.assertEqual(f.read().split(), ['\\N'] * len(cols))
|
||||||
|
|
||||||
|
f.seek(0)
|
||||||
|
curs.copy_from(f, "select", columns=cols)
|
||||||
|
curs.execute('select count(*) from "select";')
|
||||||
|
self.assertEqual(curs.fetchone()[0], 2)
|
||||||
|
|
||||||
@skip_before_postgres(8, 2) # they don't send the count
|
@skip_before_postgres(8, 2) # they don't send the count
|
||||||
def test_copy_rowcount(self):
|
def test_copy_rowcount(self):
|
||||||
curs = self.conn.cursor()
|
curs = self.conn.cursor()
|
||||||
|
|
Loading…
Reference in New Issue
Block a user