summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog6
-rw-r--r--psycopg/typecast.c93
-rw-r--r--psycopg/typecast_datetime.c102
-rw-r--r--psycopg/typecast_mxdatetime.c49
-rw-r--r--sandbox/test.py36
-rw-r--r--setup.cfg2
6 files changed, 211 insertions, 77 deletions
diff --git a/ChangeLog b/ChangeLog
index 08d5ba3..5f66535 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2005-11-14 Federico Di Gregorio <fog@initd.org>
+
+ * psycopg/typecast.c: added typecast_parse_date and typecast_parse_time
+ functions to do locale-safe date/time parsing. This would probably also
+ speed-up psycopg a little bit.
+
2005-11-07 Federico Di Gregorio <fog@initd.org>
* psycopg/pqpath.c: fixed problem with uninitialized value (all this was
diff --git a/psycopg/typecast.c b/psycopg/typecast.c
index c2cf548..4ea839b 100644
--- a/psycopg/typecast.c
+++ b/psycopg/typecast.c
@@ -47,6 +47,99 @@ skip_until_space2(char *s, int *len)
return s;
}
+static int
+typecast_parse_date(char* s, char** t, int* len,
+ int* year, int* month, int* day)
+{
+ int acc = -1, cz = 0;
+
+ Dprintf("typecast_parse_date: len = %d, s = %s", *len, s);
+
+ while (cz < 3 && *len > 0 && *s) {
+ switch (*s) {
+ case '-':
+ case ' ':
+ case 'T':
+ if (cz == 0) *year = acc;
+ else if (cz == 1) *month = acc;
+ else if (cz == 2) *day = acc;
+ acc = -1; cz++;
+ break;
+ default:
+ acc = (acc == -1 ? 0 : acc*10) + ((int)*s - (int)'0');
+ break;
+ }
+
+ s++; (*len)--;
+ }
+
+ if (acc != -1) {
+ *day = acc;
+ cz += 1;
+ }
+ if (t != NULL) *t = s;
+
+ return cz;
+}
+
+static int
+typecast_parse_time(char* s, char** t, int* len,
+ int* hh, int* mm, int* ss, int* us, int* tz)
+{
+ int acc = -1, cz = 0;
+ int tzs = 1, tzhh = 0, tzmm = 0;
+
+ /* sets microseconds and timezone to 0 because they may be missing */
+ *us = *tz = 0;
+
+ Dprintf("typecast_parse_time: len = %d, s = %s", *len, s);
+
+ while (cz < 5 && *len > 0 && *s) {
+ switch (*s) {
+ case ':':
+ if (cz == 0) *hh = acc;
+ else if (cz == 1) *mm = acc;
+ else if (cz == 2) *ss = acc;
+ else if (cz == 3) *us = acc;
+ else if (cz == 4) tzhh = acc;
+ acc = -1; cz++;
+ break;
+ case '.':
+ /* we expect seconds and if we don't get them we return an error */
+ if (cz != 2) return -1;
+ *ss = acc;
+ acc = -1; cz++;
+ break;
+ case '+':
+ case '-':
+ /* seconds or microseconds here, anything else is an error */
+ if (cz < 2 || cz > 3) return -1;
+ if (*s == '-') tzs = -1;
+ if (cz == 2) *ss = acc;
+ else if (cz == 3) *us = acc;
+ else if (cz == 4) *tz = acc;
+ acc = -1; cz++;
+ break;
+ default:
+ acc = (acc == -1 ? 0 : acc*10) + ((int)*s - (int)'0');
+ break;
+ }
+
+ s++; (*len)--;
+ }
+
+ if (acc != -1) {
+ if (cz == 2) { *ss = acc; cz += 1; }
+ else if (cz == 3) { *us = acc; cz += 1; }
+ else if (cz == 4) { tzhh = acc; cz += 1; }
+ else if (cz == 5) tzmm = acc;
+ }
+ if (t != NULL) *t = s;
+
+ *tz = tzs * tzhh*60 + tzmm;
+
+ return cz;
+}
/** include casting objects **/
#include "psycopg/typecast_basic.c"
diff --git a/psycopg/typecast_datetime.c b/psycopg/typecast_datetime.c
index 7f963d9..3a09f07 100644
--- a/psycopg/typecast_datetime.c
+++ b/psycopg/typecast_datetime.c
@@ -41,7 +41,6 @@ typecast_PYDATE_cast(char *str, int len, PyObject *curs)
if (str == NULL) {Py_INCREF(Py_None); return Py_None;}
- /* check for infinity */
if (!strcmp(str, "infinity") || !strcmp(str, "-infinity")) {
if (str[0] == '-') {
obj = PyObject_GetAttrString(pyDateTypeP, "min");
@@ -52,8 +51,10 @@ typecast_PYDATE_cast(char *str, int len, PyObject *curs)
}
else {
- n = sscanf(str, "%d-%d-%d", &y, &m, &d);
-
+ n = typecast_parse_date(str, NULL, &len, &y, &m, &d);
+ Dprintf("typecast_PYDATE_cast: "
+ "n = %d, len = %d, y = %d, m = %d, d = %d",
+ n, len, y, m, d);
if (n != 3) {
PyErr_SetString(DataError, "unable to parse date");
}
@@ -71,10 +72,8 @@ typecast_PYDATETIME_cast(char *str, int len, PyObject *curs)
{
PyObject* obj = NULL;
int n, y=0, m=0, d=0;
- int hh=0, mm=0;
- int tzh=0, tzm=0;
- double ss=0.0;
- char tzs=0;
+ int hh=0, mm=0, ss=0, us=0, tz=0;
+ char *tp = NULL;
if (str == NULL) {Py_INCREF(Py_None); return Py_None;}
@@ -90,40 +89,46 @@ typecast_PYDATETIME_cast(char *str, int len, PyObject *curs)
else {
Dprintf("typecast_PYDATETIME_cast: s = %s", str);
- n = sscanf(str, "%d-%d-%d %d:%d:%lf%c%d:%d",
- &y, &m, &d, &hh, &mm, &ss, &tzs, &tzh, &tzm);
- Dprintf("typecast_PYDATETIME_cast: date parsed, %d components", n);
-
- if (n != 3 && n != 6 && n <= 7) {
+ n = typecast_parse_date(str, &tp, &len, &y, &m, &d);
+ Dprintf("typecast_PYDATE_cast: tp = %p "
+ "n = %d, len = %d, y = %d, m = %d, d = %d",
+ tp, n, len, y, m, d);
+ if (n != 3) {
PyErr_SetString(DataError, "unable to parse date");
}
- else {
- double micro = (ss - floor(ss)) * 1000000.0;
- int sec = (int)floor(ss);
- if (sec > 59) {
- mm += 1;
- sec -= 60;
- }
- if (tzs && ((cursorObject*)curs)->tzinfo_factory != Py_None) {
- /* we have a time zone, calculate minutes and create
- appropriate tzinfo object calling the factory */
- PyObject *tzinfo;
- tzm += tzh*60;
- if (tzs == '-') tzm = -tzm;
- Dprintf("typecast_PYDATETIME_cast: UTC offset = %dm", tzm);
- tzinfo = PyObject_CallFunction(
- ((cursorObject*)curs)->tzinfo_factory, "i", tzm);
- obj = PyObject_CallFunction(pyDateTimeTypeP, "iiiiiiiO",
- y, m, d, hh, mm, sec, (int)round(micro), tzinfo);
- Dprintf("typecast_PYDATETIME_cast: tzinfo: %p, refcnt = %d",
- tzinfo, tzinfo->ob_refcnt);
- Py_XDECREF(tzinfo);
- }
- else {
- obj = PyObject_CallFunction(pyDateTimeTypeP, "iiiiiii",
- y, m, d, hh, mm, sec, (int)round(micro));
+
+ if (len > 0) {
+ n = typecast_parse_time(tp, NULL, &len, &hh, &mm, &ss, &us, &tz);
+ Dprintf("typecast_PYDATETIME_cast: n = %d, len = %d, "
+ "hh = %d, mm = %d, ss = %d, us = %d, tz = %d",
+ n, len, hh, mm, ss, us, tz);
+ if (n < 3 || n > 5) {
+ PyErr_SetString(DataError, "unable to parse time");
}
}
+
+ if (ss > 59) {
+ mm += 1;
+ ss -= 60;
+ }
+
+ if (n == 5 && ((cursorObject*)curs)->tzinfo_factory != Py_None) {
+ /* we have a time zone, calculate minutes and create
+ appropriate tzinfo object calling the factory */
+ PyObject *tzinfo;
+ Dprintf("typecast_PYDATETIME_cast: UTC offset = %dm", tz);
+ tzinfo = PyObject_CallFunction(
+ ((cursorObject*)curs)->tzinfo_factory, "i", tz);
+ obj = PyObject_CallFunction(pyDateTimeTypeP, "iiiiiiiO",
+ y, m, d, hh, mm, ss, us, tzinfo);
+ Dprintf("typecast_PYDATETIME_cast: tzinfo: %p, refcnt = %d",
+ tzinfo, tzinfo->ob_refcnt);
+ Py_XDECREF(tzinfo);
+ }
+ else {
+ obj = PyObject_CallFunction(pyDateTimeTypeP, "iiiiiii",
+ y, m, d, hh, mm, ss, us);
+ }
}
return obj;
}
@@ -134,25 +139,24 @@ static PyObject *
typecast_PYTIME_cast(char *str, int len, PyObject *curs)
{
PyObject* obj = NULL;
- int n, hh=0, mm=0;
- double ss=0.0;
+ int n, hh=0, mm=0, ss=0, us=0, tz=0;
if (str == NULL) {Py_INCREF(Py_None); return Py_None;}
- n = sscanf(str, "%d:%d:%lf", &hh, &mm, &ss);
-
- if (n != 3) {
+ n = typecast_parse_time(str, NULL, &len, &hh, &mm, &ss, &us, &tz);
+ Dprintf("typecast_PYTIME_cast: n = %d, len = %d, "
+ "hh = %d, mm = %d, ss = %d, us = %d, tz = %d",
+ n, len, hh, mm, ss, us, tz);
+
+ if (n < 3 || n > 5) {
PyErr_SetString(DataError, "unable to parse time");
}
else {
- double micro = (ss - floor(ss)) * 1000000.0;
- int sec = (int)floor(ss);
- if (sec > 59) {
+ if (ss > 59) {
mm += 1;
- sec -= 60;
+ ss -= 60;
}
- obj = PyObject_CallFunction(pyTimeTypeP, "iiii",
- hh, mm, sec, (int)round(micro));
+ obj = PyObject_CallFunction(pyTimeTypeP, "iiii", hh, mm, ss, us);
}
return obj;
}
@@ -256,7 +260,7 @@ typecast_PYINTERVAL_cast(char *str, int len, PyObject *curs)
seconds += hundredths + minutes*60 + hours*3600;
}
- /* calculates days */
+ /* calculates days */
days += years*365 + months*30;
micro = (seconds - floor(seconds)) * 1000000.0;
diff --git a/psycopg/typecast_mxdatetime.c b/psycopg/typecast_mxdatetime.c
index b71a15e..52b1ce7 100644
--- a/psycopg/typecast_mxdatetime.c
+++ b/psycopg/typecast_mxdatetime.c
@@ -31,10 +31,12 @@ static PyObject *
typecast_MXDATE_cast(char *str, int len, PyObject *curs)
{
int n, y=0, m=0, d=0;
- int hh=0, mm=0;
- double ss=0.0;
-
+ int hh=0, mm=0, ss=0, us=0, tz=0;
+ char *tp = NULL;
+
if (str == NULL) {Py_INCREF(Py_None); return Py_None;}
+
+ Dprintf("typecast_MXDATE_cast: s = %s", str);
/* check for infinity */
if (!strcmp(str, "infinity") || !strcmp(str, "-infinity")) {
@@ -46,15 +48,27 @@ typecast_MXDATE_cast(char *str, int len, PyObject *curs)
}
}
- Dprintf("typecast_MXDATE_cast: s = %s", str);
- n = sscanf(str, "%d-%d-%d %d:%d:%lf", &y, &m, &d, &hh, &mm, &ss);
- Dprintf("typecast_MXDATE_cast: date parsed, %d components", n);
-
- if (n != 3 && n != 6) {
+ n = typecast_parse_date(str, &tp, &len, &y, &m, &d);
+ Dprintf("typecast_MXDATE_cast: tp = %p n = %d, len = %d, "
+ "y = %d, m = %d, d = %d", tp, n, len, y, m, d);
+ if (n != 3) {
PyErr_SetString(DataError, "unable to parse date");
- return NULL;
}
- return mxDateTimeP->DateTime_FromDateAndTime(y, m, d, hh, mm, ss);
+
+ if (len > 0) {
+ n = typecast_parse_time(tp, NULL, &len, &hh, &mm, &ss, &us, &tz);
+ Dprintf("typecast_MXDATE_cast: n = %d, len = %d, "
+ "hh = %d, mm = %d, ss = %d, us = %d, tz = %d",
+ n, len, hh, mm, ss, us, tz);
+ if (n < 3 || n > 5) {
+ PyErr_SetString(DataError, "unable to parse time");
+ }
+ }
+
+ Dprintf("typecast_MXDATE_cast: fractionary seconds: %lf",
+ (double)ss + (double)us/(double)1000000.0);
+ return mxDateTimeP->DateTime_FromDateAndTime(y, m, d, hh, mm,
+ (double)ss + (double)us/(double)1000000.0);
}
/** TIME - parse time into an mx.DateTime object **/
@@ -62,23 +76,26 @@ typecast_MXDATE_cast(char *str, int len, PyObject *curs)
static PyObject *
typecast_MXTIME_cast(char *str, int len, PyObject *curs)
{
- int n, hh=0, mm=0;
- double ss=0.0;
+ int n, hh=0, mm=0, ss=0, us=0, tz=0;
if (str == NULL) {Py_INCREF(Py_None); return Py_None;}
Dprintf("typecast_MXTIME_cast: s = %s", str);
- n = sscanf(str, "%d:%d:%lf", &hh, &mm, &ss);
+ n = typecast_parse_time(str, NULL, &len, &hh, &mm, &ss, &us, &tz);
Dprintf("typecast_MXTIME_cast: time parsed, %d components", n);
- Dprintf("typecast_MXTIME_cast: hh = %d, mm = %d, ss = %f", hh, mm, ss);
+ Dprintf("typecast_MXTIME_cast: hh = %d, mm = %d, ss = %d, us = %d",
+ hh, mm, ss, us);
- if (n != 3) {
+ if (n < 3 || n > 5) {
PyErr_SetString(DataError, "unable to parse time");
return NULL;
}
- return mxDateTimeP->DateTimeDelta_FromTime(hh, mm ,ss);
+ Dprintf("typecast_MXTIME_cast: fractionary seconds: %lf",
+ (double)ss + (double)us/(double)1000000.0);
+ return mxDateTimeP->DateTimeDelta_FromTime(hh, mm,
+ (double)ss + (double)us/(double)1000000.0);
}
/** INTERVAL - parse an interval into an mx.DateTimeDelta **/
diff --git a/sandbox/test.py b/sandbox/test.py
index 17e96d0..e3d31a7 100644
--- a/sandbox/test.py
+++ b/sandbox/test.py
@@ -6,10 +6,24 @@ import psycopg2
#print d.days, d.seconds, d.microseconds
#print psycopg.adapt(d).getquoted()
-conn = psycopg2.connect("dbname=test_unicode")
-conn.set_client_encoding("xxx")
+conn = psycopg2.connect("dbname=test")
+#conn.set_client_encoding("xxx")
curs = conn.cursor()
-#curs.execute("SELECT 1.0 AS foo")
+curs.execute("SELECT '2005-2-12'::date AS foo")
+print curs.fetchall()
+curs.execute("SELECT '10:23:60'::time AS foo")
+print curs.fetchall()
+curs.execute("SELECT '10:23:59.895342'::time AS foo")
+print curs.fetchall()
+curs.execute("SELECT '0:0:12.31423'::time with time zone AS foo")
+print curs.fetchall()
+curs.execute("SELECT '0:0:12+01:30'::time with time zone AS foo")
+print curs.fetchall()
+curs.execute("SELECT '2005-2-12 10:23:59.895342'::timestamp AS foo")
+print curs.fetchall()
+curs.execute("SELECT '2005-2-12 10:23:59.895342'::timestamp with time zone AS foo")
+print curs.fetchall()
+
#print curs.fetchmany(2)
#print curs.fetchall()
@@ -17,20 +31,20 @@ def sleep(curs):
while not curs.isready():
print "."
time.sleep(.1)
-
+
#curs.execute("""
# DECLARE zz INSENSITIVE SCROLL CURSOR WITH HOLD FOR
# SELECT now();
# FOR READ ONLY;""", async = 1)
-curs.execute("SELECT now() AS foo", async=1);
-sleep(curs)
-print curs.fetchall()
+#curs.execute("SELECT now() AS foo", async=1);
+#sleep(curs)
+#print curs.fetchall()
#curs.execute("""
# FETCH FORWARD 1 FROM zz;""", async = 1)
-curs.execute("SELECT now() AS bar", async=1);
-print curs.fetchall()
+#curs.execute("SELECT now() AS bar", async=1);
+#print curs.fetchall()
-curs.execute("SELECT now() AS bar");
-sleep(curs)
+#curs.execute("SELECT now() AS bar");
+#sleep(curs)
diff --git a/setup.cfg b/setup.cfg
index f278e99..54a2edf 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,5 +1,5 @@
[build_ext]
-define=PSYCOPG_EXTENSIONS,PSYCOPG_DISPLAY_SIZE,HAVE_PQFREEMEM,HAVE_PQPROTOCOL3
+define=PSYCOPG_DEBUG,PSYCOPG_EXTENSIONS,PSYCOPG_DISPLAY_SIZE,HAVE_PQFREEMEM,HAVE_PQPROTOCOL3
# PSYCOPG_EXTENSIONS enables extensions to PEP-249 (you really want this)
# PSYCOPG_DISPLAY_SIZE enable display size calculation (a little slower)
# HAVE_PQFREEMEM should be defined on PostgreSQL >= 7.4