Skip to content

Commit cddb1a1

Browse files
committed
Array quoting (not completely working.)
1 parent def14d5 commit cddb1a1

File tree

3 files changed

+71
-30
lines changed

3 files changed

+71
-30
lines changed

psycopg/typecast_array.c

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,27 @@
2222

2323
/** typecast_array_scan - scan a string looking for array items **/
2424

25-
#define ASCAN_EOF 0
26-
#define ASCAN_BEGIN 1
27-
#define ASCAN_END 2
28-
#define ASCAN_TOKEN 3
25+
#define ASCAN_EOF 0
26+
#define ASCAN_BEGIN 1
27+
#define ASCAN_END 2
28+
#define ASCAN_TOKEN 3
29+
#define ASCAN_QUOTED 4
2930

3031
static int
3132
typecast_array_tokenize(unsigned char *str, int strlength,
3233
int *pos, unsigned char** token, int *length)
3334
{
34-
int i = *pos;
35+
int i;
36+
int quoted = 0;
37+
38+
/* first we check for quotes, used when the content of the item contains
39+
special or quoted characters */
40+
if (str[*pos] == '"') {
41+
quoted = 1;
42+
*pos += 1;
43+
}
3544

36-
Dprintf("TOKENIZE for %d, pos = %d", strlength, *pos);
37-
38-
while (i < strlength) {
45+
for (i = *pos ; i < strlength ; i++) {
3946
switch (str[i]) {
4047
case '{':
4148
*pos = i+1;
@@ -48,17 +55,21 @@ typecast_array_tokenize(unsigned char *str, int strlength,
4855
case ',':
4956
*token = &str[*pos];
5057
*length = i - *pos;
58+
if (quoted == 1)
59+
*length -= 1;
5160
*pos = i+1;
52-
Dprintf("TOKENIZE pos = %d, length = %d", *pos, *length);
5361
return ASCAN_TOKEN;
5462

5563
default:
56-
i++;
64+
/* nothing to do right now */
65+
break;
5766
}
5867
}
5968

6069
*token = &str[*pos];
6170
*length = i - *pos;
71+
if (quoted == 1)
72+
*length -= 1;
6273

6374
return ASCAN_EOF;
6475
}
@@ -89,10 +100,12 @@ typecast_array_scan(unsigned char *str, int strlength,
89100
return 1;
90101
}
91102

92-
/** LONGINTEGERARRAY and INTEGERARRAY - cast integers arrays **/
93103

104+
/** GENERIC - a generic typecaster that can be used when no special actions
105+
have to be taken on the single items **/
106+
94107
static PyObject *
95-
typecast_INTEGERARRAY_cast(unsigned char *str, int len, PyObject *curs)
108+
typecast_GENERIC_ARRAY_cast(unsigned char *str, int len, PyObject *curs)
96109
{
97110
PyObject *obj = NULL;
98111
PyObject *base = ((typecastObject*)((cursorObject*)curs)->caster)->bcast;
@@ -114,16 +127,12 @@ typecast_INTEGERARRAY_cast(unsigned char *str, int len, PyObject *curs)
114127
return obj;
115128
}
116129

117-
#define typecast_LONGINTEGERARRAY_cast typecast_INTEGERARRAY_cast
130+
/** LONGINTEGERARRAY and INTEGERARRAY - cast integers arrays **/
131+
132+
#define typecast_LONGINTEGERARRAY_cast typecast_GENERIC_ARRAY_cast
133+
#define typecast_INTEGERARRAY_cast typecast_GENERIC_ARRAY_cast
118134

119135
/** STRINGARRAY - cast integers arrays **/
120136

121-
static PyObject *
122-
typecast_STRINGARRAY_cast(unsigned char *str, int len, PyObject *curs)
123-
{
124-
PyObject* obj = NULL;
125-
126-
if (str == NULL) {Py_INCREF(Py_None); return Py_None;}
137+
#define typecast_STRINGARRAY_cast typecast_GENERIC_ARRAY_cast
127138

128-
return obj;
129-
}

psycopg/typecast_basic.c

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,11 @@ typecast_INTEGER_cast(unsigned char *s, int len, PyObject *curs)
3030
unsigned char buffer[12];
3131

3232
if (s == NULL) {Py_INCREF(Py_None); return Py_None;}
33-
strncpy(buffer, s, len); buffer[len] = '\0';
34-
return PyInt_FromString(buffer, NULL, 0);
33+
if (s[len] != '\0') {
34+
strncpy(buffer, s, len); buffer[len] = '\0';
35+
s = buffer;
36+
}
37+
return PyInt_FromString(s, NULL, 0);
3538
}
3639

3740
/** LONGINTEGER - cast long integers (8 bytes) to python long **/
@@ -42,7 +45,10 @@ typecast_LONGINTEGER_cast(unsigned char *s, int len, PyObject *curs)
4245
unsigned char buffer[24];
4346

4447
if (s == NULL) {Py_INCREF(Py_None); return Py_None;}
45-
strncpy(buffer, s, len); buffer[len] = '\0';
48+
if (s[len] != '\0') {
49+
strncpy(buffer, s, len); buffer[len] = '\0';
50+
s = buffer;
51+
}
4652
return PyLong_FromString(s, NULL, 0);
4753
}
4854

@@ -51,10 +57,14 @@ typecast_LONGINTEGER_cast(unsigned char *s, int len, PyObject *curs)
5157
static PyObject *
5258
typecast_FLOAT_cast(unsigned char *s, int len, PyObject *curs)
5359
{
60+
/* FIXME: is 64 large enough for any float? */
5461
unsigned char buffer[64];
5562

5663
if (s == NULL) {Py_INCREF(Py_None); return Py_None;}
57-
strncpy(buffer, s, len); buffer[len] = '\0';
64+
if (s[len] != '\0') {
65+
strncpy(buffer, s, len); buffer[len] = '\0';
66+
s = buffer;
67+
}
5868
return PyFloat_FromDouble(atof(s));
5969
}
6070

@@ -144,17 +154,23 @@ static PyObject *
144154
typecast_BINARY_cast(unsigned char *s, int l, PyObject *curs)
145155
{
146156
PyObject *res;
147-
unsigned char *str, saved;
157+
unsigned char *str, *buffer = NULL;
148158
size_t len;
149159

150160
if (s == NULL) {Py_INCREF(Py_None); return Py_None;}
151161

152162
/* PQunescapeBytea absolutely wants a 0-terminated string and we don't
153-
want to copy the whole buffer, right? Wrong... :/ */
154-
saved = s[l]; s[l] = '\0';
163+
want to copy the whole buffer, right? Wrong, but there isn't any other
164+
way :/ */
165+
if (s[l] != '\0') {
166+
if ((buffer = PyMem_Malloc(l+1)) == NULL)
167+
PyErr_NoMemory();
168+
strncpy(buffer, s, l); buffer[l] = '\0';
169+
s = buffer;
170+
}
155171
str = PQunescapeBytea(s, &len);
156172
Dprintf("typecast_BINARY_cast: unescaped %d bytes", len);
157-
s[l] = saved;
173+
if (buffer) PyMem_Free(buffer);
158174

159175
/* TODO: using a PyBuffer would make this a zero-copy operation but we'll
160176
need to define our own buffer-derived object to keep a reference to the
@@ -196,7 +212,8 @@ typecast_DECIMAL_cast(unsigned char *s, int len, PyObject *curs)
196212

197213
if (s == NULL) {Py_INCREF(Py_None); return Py_None;}
198214

199-
buffer = PyMem_Malloc(len+1);
215+
if ((buffer = PyMem_Malloc(len+1)) == NULL)
216+
PyErr_NoMemory();
200217
strncpy(buffer, s, len); buffer[len] = '\0';
201218
res = PyObject_CallFunction(decimalType, "s", buffer);
202219
PyMem_Free(buffer);

sandbox/array.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import psycopg
2+
3+
conn = psycopg.connect("dbname=test")
4+
curs = conn.cursor()
5+
6+
curs.execute("SELECT ARRAY[1,2,3] AS foo")
7+
print curs.fetchone()
8+
9+
curs.execute("SELECT ARRAY['1','2','3'] AS foo")
10+
print curs.fetchone()
11+
12+
curs.execute("""SELECT ARRAY['','"',''] AS foo""")
13+
d = curs.fetchone()
14+
print d, '->', d[0][0], d[0][1], d[0][2]
15+

0 commit comments

Comments
 (0)