Skip to content

Commit

Permalink
Rewrite Row.__repr__ to eliminate Unicode error
Browse files Browse the repository at this point in the history
The debug build of Python generates an assertion that the resulting string we created has the
wrong type.  I think the issue was that we had a 1-byte string but it was not marked as ASCII
even though it had no values > 127.  Therefore I don't think it was causing problems or any
exploitable issues, but I'm not sure.

I replaced the implementation with simply building a temporary tuple with the values and
calling repr on them.  (Perhaps I should consider storing the rows in a Tuple instead of a
handcoded array?)

I also closed some popen pipes in the setup.py to eliminate some errors when PYTHONDEVMODE=1.
  • Loading branch information
mkleehammer committed Apr 1, 2023
1 parent 4829107 commit 615ebdd
Show file tree
Hide file tree
Showing 2 changed files with 13 additions and 115 deletions.
8 changes: 6 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,10 +227,14 @@ def get_compiler_settings(version_str):
# Python functions take a lot of 'char *' that really should be const. gcc complains about this *a lot*
settings['extra_compile_args'].append('-Wno-write-strings')

cflags = os.popen('odbc_config --cflags 2>/dev/null').read().strip()
fd = os.popen('odbc_config --cflags 2>/dev/null')
cflags = fd.read().strip()
fd.close()
if cflags:
settings['extra_compile_args'].extend(cflags.split())
ldflags = os.popen('odbc_config --libs 2>/dev/null').read().strip()
fd = os.popen('odbc_config --libs 2>/dev/null')
ldflags = fd.read().strip()
fd.close()
if ldflags:
settings['extra_link_args'].extend(ldflags.split())

Expand Down
120 changes: 7 additions & 113 deletions src/row.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -256,129 +256,23 @@ static int Row_setattro(PyObject* o, PyObject *name, PyObject* v)
}


#if PY_MAJOR_VERSION < 3
static PyObject* Row_repr(PyObject* o)
{
Row* self = (Row*)o;

if (self->cValues == 0)
return PyString_FromString("()");

Object pieces(PyTuple_New(self->cValues));
if (!pieces)
return 0;

Py_ssize_t length = 2 + (2 * (self->cValues-1)); // parens + ', ' separators

for (Py_ssize_t i = 0; i < self->cValues; i++)
{
PyObject* piece = PyObject_Repr(self->apValues[i]);
if (!piece)
return 0;

length += Text_Size(piece);

PyTuple_SET_ITEM(pieces.Get(), i, piece);
}

if (self->cValues == 1)
{
// Need a trailing comma: (value,)
length += 2;
}

PyObject* result = Text_New(length);
if (!result)
return 0;
TEXT_T* buffer = Text_Buffer(result);
Py_ssize_t offset = 0;
buffer[offset++] = '(';
for (Py_ssize_t i = 0; i < self->cValues; i++)
{
PyObject* item = PyTuple_GET_ITEM(pieces.Get(), i);
memcpy(&buffer[offset], Text_Buffer(item), Text_Size(item) * sizeof(TEXT_T));
offset += Text_Size(item);

if (i != self->cValues-1 || self->cValues == 1)
{
buffer[offset++] = ',';
buffer[offset++] = ' ';
}
}
buffer[offset++] = ')';

I(offset == length);
// We want to return the same representation as a tuple. The easiest way is to create a
// temporary tuple. I do not consider this something normally used in high performance
// areas.

return result;
}
#else // >= Python 3.3
static PyObject* Row_repr(PyObject* o)
{
Row* self = (Row*)o;

if (self->cValues == 0)
return PyUnicode_FromString("()");

Object pieces(PyTuple_New(self->cValues));
if (!pieces)
Object tmp(PyTuple_New(self->cValues));
if (!tmp)
return 0;

Py_ssize_t length = 2 + (2 * (self->cValues-1)); // parens + ', ' separators
int result_kind = PyUnicode_1BYTE_KIND;

for (Py_ssize_t i = 0; i < self->cValues; i++)
{
PyObject* piece = PyObject_Repr(self->apValues[i]);
if (!piece)
return 0;

length += PyUnicode_GET_LENGTH(piece);
int kind = PyUnicode_KIND(piece);
if (result_kind < kind)
result_kind = kind;

PyTuple_SET_ITEM(pieces.Get(), i, piece);
}

if (self->cValues == 1)
{
// Need a trailing comma: (value,)
length += 2;
}
Py_UCS4 maxchar = 0x10ffff;
if (result_kind == PyUnicode_2BYTE_KIND)
maxchar = 0xffff;
else if (result_kind == PyUnicode_1BYTE_KIND)
maxchar = 0xff;
PyObject* result = PyUnicode_New(length, maxchar);
if (!result)
return 0;
Py_ssize_t offset = 0;
PyUnicode_WriteChar(result, offset++, (Py_UCS4)'(');
for (Py_ssize_t i = 0; i < self->cValues; i++)
{
PyObject* item = PyTuple_GET_ITEM(pieces.Get(), i);
Py_ssize_t count = PyUnicode_GET_LENGTH(item);
Py_ssize_t n = PyUnicode_CopyCharacters(result, offset, item, 0, count);
if (n < 0)
return 0;
offset += count;

if (i != self->cValues-1 || self->cValues == 1)
{
PyUnicode_WriteChar(result, offset++, (Py_UCS4)',');
PyUnicode_WriteChar(result, offset++, (Py_UCS4)' ');
}
}
PyUnicode_WriteChar(result, offset++, (Py_UCS4)')');
if (PyUnicode_READY(result) < 0)
return 0;

I(offset == length);
PyTuple_SET_ITEM(tmp.Get(), i, self->apValues[i]);

return result;
return PyObject_Repr(tmp);
}
#endif

static PyObject* Row_richcompare(PyObject* olhs, PyObject* orhs, int op)
{
Expand Down

0 comments on commit 615ebdd

Please sign in to comment.