From: Billy G. A. <bal...@us...> - 2001-09-10 04:41:36
|
Update of /cvsroot/pypgsql/pypgsql In directory usw-pr-cvs1:/tmp/cvs-serv21787 Modified Files: pgversion.c Log Message: 10SEP2001 bga Fixed problem when comparing against a PgVersion object. The wanted behaviour was to convert the string (i.e. '7.1.3') or number (i.e. 70103) to a PgVersion object and compare them. Problems arise if the string or number can't be coerced into a PgVersion object. In this case, Python would ignore any excep- tions raised in ver_coerce and attempt coerce the PgVersion object into a string or number and compare the results. This would invariably be incorrect. In order to get the desired be- haviour, I had to 'trick' Python by having ver_coerce() always succeeded, even if the coerce to a PgVersion object failed. ver_coerce() would create a 'special' PgVersion object that contained the error information when the coercsion failed. PgVersion_cmp() recognizes the 'special' PgVersion object and raises the exception that actually occurred in ver_coerce(). NOTE: Using Python's builtin coerce() funtion to coerce an object to a PgVersion will always seem to succeed. You must check the resulting PgVersion object to see if an error occured. If the post70 attribute is None, then an error occured and the version attribute contains the error message. ---------------------------------------------------------------------- Index: pgversion.c =================================================================== RCS file: /cvsroot/pypgsql/pypgsql/pgversion.c,v retrieving revision 1.6 retrieving revision 1.7 diff -C2 -d -r1.6 -r1.7 *** pgversion.c 2001/09/08 16:12:51 1.6 --- pgversion.c 2001/09/10 04:41:32 1.7 *************** *** 32,35 **** --- 32,55 ---- | Date Ini Description | | --------- --- ------------------------------------------------------- | + | 09SEP2001 bga Fixed the broken comparison function. This proved to | + | be a not so easy task since any errors set in the ex- | + | tensions module's coerce() function are ignored (unless | + | called by Python's builtin coerce() function). Python | + | will clear the error and try to convert the PgVersion | + | object to the other type if the coersion to PgVersion | + | fails. I do not want that to happen. If the object | + | can't be coerced to a PgVersion, then the comparison | + | does not make sense, so an exception should occur. To | + | get this behaviour required a bit sneaky-ness. See the | + | comments in the code for details. A concequence of | + | this change is that coerce will always succeed. I can | + | not see any way around this :-( | + | --- Having a __dict__ attribute and calling PyMember_Get() | + | in the PyVersion_getattr function causes dir() to do | + | strange things (like list members twice). I've removed | + | the __dict__ attribute and adding methods to emulate | + | a mapping object to PgVersion. A PgVersion object will | + | now act like a dictionary, so use version[key] instead | + | of version.__dict__[key]. | | 08SEP2001 gh Make PgVersion_New safe for arbitrary input strings. | | --- Make the repr method really return the version string. | *************** *** 74,77 **** --- 94,143 ---- } PgVersion; + /***********************************************************************\ + | Name: parseToken | + | | + | Synopsis: if (parseToken(token, result)) {...error handling...} | + | | + | Description: parseToken will extract an integer from token. If it | + | can't get an integer or if the token contains something | + | other than a number, raise an execption. | + | | + | Returns: 0 if successful, 1 if it failed to parse an integer. | + \***********************************************************************/ + + static int parseToken(char *token, long *result) + { + char *last; + + *result = strtol(token, &last, 0); + + if ((token == (char *)NULL) || + (*token == '\0') || + isspace(*token) || + (*last != 0)) + { + PyErr_SetString(PyExc_ValueError, + "Invalid format for PgVersion construction."); + return (1); + } + + return (0); + } + + /*--------------------------------------------------------------------------*/ + + static void PgVersion_dealloc(PgVersion *self) + { + Py_XDECREF(self->version); + Py_XDECREF(self->major); + Py_XDECREF(self->minor); + Py_XDECREF(self->patch); + Py_XDECREF(self->post70); + Py_XDECREF(self->value); + PyObject_DEL(self); + } + + /*--------------------------------------------------------------------------*/ + PyObject *PgVersion_New(char *version) { *************** *** 95,136 **** PyErr_SetString(PyExc_MemoryError, "Can't allocate memory in PgVersion_New()."); ! PyMem_Free(s); ! Py_XDECREF(self->version); ! Py_XDECREF(self); ! return NULL; } - /***************************************************************\ - | Parse out the version information from the version string. | - | The expected format is 'PostgreSQL M.m.p other information' | - | where M is the major number, m is the minor number and p is | - | the patch level. | - \***************************************************************/ ! (void)strtok_r(s, " \t", &save_ptr); ! vstr = strtok_r((char *)NULL, " \t", &save_ptr); ! /***************************************************************\ ! | We now have the version number (as M.m.p) in vstr. Parse it. | ! | Note: The patch level may not be present. | ! \***************************************************************/ save_ptr = (char *)NULL; ! token = strtok_r(vstr, ".", &save_ptr); ! if ((token == (char *)NULL) || (*token == 0)) ! goto parse_error; ! major = atol(token); ! ! token = strtok_r((char *)NULL, ".", &save_ptr); ! if ((token == (char *)NULL) || (*token == 0)) ! goto parse_error; ! minor = atol(token); ! token = strtok_r((char *)NULL, ".", &save_ptr); ! if ((token == (char *)NULL) || (*token == 0)) ! patch = 0; ! else ! patch = atol(token); value = (((major * 100) + minor) * 100) + patch; --- 161,217 ---- PyErr_SetString(PyExc_MemoryError, "Can't allocate memory in PgVersion_New()."); ! goto new_error; } ! self->major = self->minor = self->patch = (PyObject *)NULL; ! self->value = self->post70 = (PyObject *)NULL; ! /***************************************************************\ ! | Parse out the version information from the version string. | ! | The expected format is 'PostgreSQL M.m.p on ...' where M is | ! | the major number, m is the minor number and p is the patch | ! | level. | ! \***************************************************************/ ! major = minor = patch = value = 0; ! ! token = strtok_r(s, " \t", &save_ptr); ! if (strcmp(token, "PostgreSQL") != 0) ! { ! PyErr_SetString(PyExc_ValueError, ! "Ivalid format for PgVersion construction."); ! goto new_error; ! } ! ! vstr = strtok_r((char *)NULL, " \t", &save_ptr); ! ! token = strtok_r((char *)NULL, " \t", &save_ptr); ! if (strcmp(token, "on") != 0) ! { ! PyErr_SetString(PyExc_ValueError, ! "Ivalid format for PgVersion construction."); ! goto new_error; ! } ! ! /***************************************************************\ ! | We now have the version number (as M.m.p) in vstr. Parse it. | ! | Note: The minor number and patch level may not be present. | ! \***************************************************************/ save_ptr = (char *)NULL; ! token = strtok_r(vstr, ".", &save_ptr); ! if (parseToken(token, &major)) ! goto new_error; ! token = strtok_r((char *)NULL, ".", &save_ptr); ! if ((token != (char *)NULL) && (*token != '\0') && ! (parseToken(token, &minor))) ! goto new_error; ! ! token = strtok_r((char *)NULL, ".", &save_ptr); ! if ((token != (char *)NULL) && (*token != '\0') && ! (parseToken(token, &patch))) ! goto new_error; value = (((major * 100) + minor) * 100) + patch; *************** *** 141,163 **** self->value = Py_BuildValue("i", value); self->post70 = Py_BuildValue("i", ((value >= 70100) ? 1l : 0l)); ! ! PyMem_Free(s); } return (PyObject *)self; ! parse_error: PyMem_Free(s); ! PyErr_SetString(PyExc_ValueError, ! "Illegal format for PgVersion construction."); return (PyObject *)NULL; - } - - static void PgVersion_dealloc(PgVersion *self) - { - PyMem_Free(self->version); - PyMem_Free(self); } static PyObject *PgVersion_repr(PgVersion *self) { --- 222,241 ---- self->value = Py_BuildValue("i", value); self->post70 = Py_BuildValue("i", ((value >= 70100) ? 1l : 0l)); ! if (PyErr_Occurred()) ! goto new_error; } + PyMem_Free(s); + return (PyObject *)self; ! new_error: PyMem_Free(s); ! PgVersion_dealloc(self); return (PyObject *)NULL; } + /*--------------------------------------------------------------------------*/ + static PyObject *PgVersion_repr(PgVersion *self) { *************** *** 166,169 **** --- 244,249 ---- } + /*--------------------------------------------------------------------------*/ + static int ver_coerce(PyObject **l, PyObject **r) { *************** *** 180,184 **** return (1); } ! sprintf(vstr, "PostgreSQL %s Dummy Version", PyString_AsString(*r)); } else --- 260,264 ---- return (1); } ! sprintf(vstr, "PostgreSQL %s on Dummy", PyString_AsString(*r)); } else *************** *** 188,200 **** if ((vstr = PyMem_Malloc(128)) == (char *)NULL) { ! PyErr_SetString(PyExc_MemoryError, ! "Can't allocate memory in PgVersion_coerce()."); return (1); } ! if (PyInt_Check(*r)) { value = PyInt_AsLong(*r); } else if (PyLong_Check(*r)) { --- 268,287 ---- if ((vstr = PyMem_Malloc(128)) == (char *)NULL) { ! PyErr_NoMemory(); return (1); } ! if (PgInt2_Check(*r)) ! { ! value = PgInt2_AsLong((PgInt2Object *)(*r)); ! } ! else if (PyInt_Check(*r)) { value = PyInt_AsLong(*r); } + if (PgInt8_Check(*r)) + { + value = PgInt8_AsLong((PgInt8Object *)(*r)); + } else if (PyLong_Check(*r)) { *************** *** 214,238 **** } ! sprintf(vstr, "PostgreSQL %ld.%ld.%ld Dummy Version", (value / 10000), ((value / 100) % 100), (value % 100)); } s = (PgVersion *)PgVersion_New(vstr); PyMem_Free(vstr); ! if (PyErr_Occurred() != (PyObject *)NULL) { ! if (s != (PgVersion *)NULL) ! PyObject_Del(s); return (1); } *r = (PyObject *)s; ! ! Py_INCREF(*l); return (0); } static int PgVersion_coerce(PyObject **l, PyObject **r) { --- 301,362 ---- } ! if (PyErr_Occurred()) ! goto coerce_error; ! ! sprintf(vstr, "PostgreSQL %ld.%ld.%ld on Dummy", (value / 10000), ((value / 100) % 100), (value % 100)); } s = (PgVersion *)PgVersion_New(vstr); + if (PyErr_Occurred()) + { + Py_XDECREF(s); + goto coerce_error; + } + PyMem_Free(vstr); ! *r = (PyObject *)s; ! Py_XINCREF(*l); ! ! return (0); ! ! coerce_error: ! /*******************************************************************\ ! | A bit of explaination is in order here. | ! | | ! | Python will ignore any exceptions raised from the call to coerce | ! | types. In order to work around this problem, coerce will always | ! | convert the other type to a PgVersion object, but if an error oc- | ! | curs, that object will contain the saved error information. When | ! | the compare function for PgVersion is call, it will re-raise the | ! | saved exception. | ! \*******************************************************************/ ! ! PyMem_Free(vstr); ! ! s = (PgVersion *)PyObject_New(PgVersion, &PgVersion_Type); ! if (s == (PgVersion *)NULL) ! return (1); ! ! PyErr_Fetch(&s->major, &s->version, &s->minor); ! s->value = Py_BuildValue("i", -1); ! s->patch = Py_None; Py_INCREF(Py_None); ! s->post70 = Py_None; Py_INCREF(Py_None); ! ! if (PyErr_Occurred()) { ! PgVersion_dealloc(s); return (1); } *r = (PyObject *)s; ! Py_XINCREF(*l); return (0); } + /*--------------------------------------------------------------------------*/ + static int PgVersion_coerce(PyObject **l, PyObject **r) { *************** *** 243,272 **** } - static PyNumberMethods ver_as_number = { - (binaryfunc)NULL, /* nb_add */ - (binaryfunc)NULL, /* nb_subtract */ - (binaryfunc)NULL, /* nb_multiply */ - (binaryfunc)NULL, /* nb_divide */ - (binaryfunc)NULL, /* nb_remainder */ - (binaryfunc)NULL, /* nb_divmod */ - (ternaryfunc)NULL, /* nb_power */ - (unaryfunc)NULL, /* nb_negative */ - (unaryfunc)NULL, /* nb_positive */ - (unaryfunc)NULL, /* nb_absolute */ - (inquiry)NULL, /* nb_nonzero */ - (unaryfunc)NULL, /* nb_invert */ - (binaryfunc)NULL, /* nb_lshift */ - (binaryfunc)NULL, /* nb_rshift */ - (binaryfunc)NULL, /* nb_and */ - (binaryfunc)NULL, /* nb_xor */ - (binaryfunc)NULL, /* nb_or */ - (coercion)PgVersion_coerce, /* nb_coerce */ - (unaryfunc)NULL, /* nb_int */ - (unaryfunc)NULL, /* nb_long */ - (unaryfunc)NULL, /* nb_float */ - (unaryfunc)NULL, /* nb_oct */ - (unaryfunc)NULL /* nb_hex */ - }; - /*--------------------------------------------------------------------------*/ --- 367,370 ---- *************** *** 282,321 **** }; static PyObject *PgVersion_getattr(PgVersion *self, char *attr) { ! if (!strcmp(attr, "__dict__")) { ! PyObject *dict; ! if ((dict = PyDict_New()) == (PyObject *)NULL) ! return NULL; ! PyDict_SetItemString(dict, "version", self->version); ! PyDict_SetItemString(dict, "major", self->major); ! PyDict_SetItemString(dict, "minor", self->minor); ! PyDict_SetItemString(dict, "level", self->patch); ! PyDict_SetItemString(dict, "post70", self->post70); ! if (PyErr_Occurred() != (PyObject *)NULL) ! { ! Py_XDECREF(dict); ! return (PyObject *)NULL; ! } ! return dict; } ! else ! return PyMember_Get((char *)self, PgVersion_members, attr); } /*--------------------------------------------------------------------------*/ static int PgVersion_cmp(PgVersion *s, PgVersion *o) { ! return (s->value < o->value) ? -1 : (s->value > o->value) ? 1 : 0; } /*--------------------------------------------------------------------------*/ static char PgVersion_Type_Doc[] = "This is the type of PgVersion objects"; --- 380,510 ---- }; + /*--------------------------------------------------------------------------*/ + static PyObject *PgVersion_getattr(PgVersion *self, char *attr) { ! return PyMember_Get((char *)self, PgVersion_members, attr); ! } ! ! /*--------------------------------------------------------------------------*/ ! ! static int PgVersion_setattr(PgVersion *self, char* attr, PyObject *value) ! { ! if (value == NULL) { ! PyErr_SetString(PyExc_AttributeError, ! "can't delete PgVersion attributes"); ! return -1; ! } ! return PyMember_Set((char *)self, PgVersion_members, attr, value); ! } ! /*--------------------------------------------------------------------------*/ ! static int PgVersion_length(PgVersion *self) ! { ! return 5; ! } ! /*--------------------------------------------------------------------------*/ ! static PyObject *PgVersion_getitem(PgVersion *self, PyObject *attr) ! { ! char *key; ! PyObject *value; ! ! if (!PyArg_Parse(attr, "s", &key)) ! { ! return (PyObject *)NULL; } ! ! value = PyMember_Get((char *)self, PgVersion_members, key); ! if (value == (PyObject *)NULL) ! { ! PyErr_SetString(PyExc_KeyError, key); ! } ! ! return value; } + + /*--------------------------------------------------------------------------*/ + static int PgVersion_setitem(PgVersion *self, PyObject *attr, PyObject *value) + { + if (value == NULL) + { + PyErr_SetString(PyExc_AttributeError, + "can't delete PgVersion attributes"); + } + else + { + PyErr_SetString(PyExc_AttributeError, + "PgVersion attributes are read-only"); + } + return -1; + } + /*--------------------------------------------------------------------------*/ static int PgVersion_cmp(PgVersion *s, PgVersion *o) { ! long left = PyInt_AS_LONG(s->value); ! long right = PyInt_AS_LONG(o->value); ! ! /*******************************************************************\ ! | Check to see if one of the PgVersion objects contain error infor- | ! | mation. If it does, restore the error condition. | ! \*******************************************************************/ ! ! if (left < 0 || right < 0) ! { ! PgVersion *t = (left < 0 ? s : o); ! ! PyErr_Restore(t->major, t->version, t->minor); ! Py_XINCREF(t->major); ! Py_XINCREF(t->version); ! Py_XINCREF(t->minor); ! } ! return (left < right) ? -1 : (left > right) ? 1 : 0; } /*--------------------------------------------------------------------------*/ + static PyMappingMethods ver_as_mapping = { + (inquiry)PgVersion_length, /*mp_length*/ + (binaryfunc)PgVersion_getitem, /*mp_subscript*/ + (objobjargproc)PgVersion_setitem, /*mp_ass_subscript*/ + }; + + /*--------------------------------------------------------------------------*/ + + static PyNumberMethods ver_as_number = { + (binaryfunc)NULL, /* nb_add */ + (binaryfunc)NULL, /* nb_subtract */ + (binaryfunc)NULL, /* nb_multiply */ + (binaryfunc)NULL, /* nb_divide */ + (binaryfunc)NULL, /* nb_remainder */ + (binaryfunc)NULL, /* nb_divmod */ + (ternaryfunc)NULL, /* nb_power */ + (unaryfunc)NULL, /* nb_negative */ + (unaryfunc)NULL, /* nb_positive */ + (unaryfunc)NULL, /* nb_absolute */ + (inquiry)NULL, /* nb_nonzero */ + (unaryfunc)NULL, /* nb_invert */ + (binaryfunc)NULL, /* nb_lshift */ + (binaryfunc)NULL, /* nb_rshift */ + (binaryfunc)NULL, /* nb_and */ + (binaryfunc)NULL, /* nb_xor */ + (binaryfunc)NULL, /* nb_or */ + (coercion)PgVersion_coerce, /* nb_coerce */ + (unaryfunc)NULL, /* nb_int */ + (unaryfunc)NULL, /* nb_long */ + (unaryfunc)NULL, /* nb_float */ + (unaryfunc)NULL, /* nb_oct */ + (unaryfunc)NULL /* nb_hex */ + }; + + /*--------------------------------------------------------------------------*/ + static char PgVersion_Type_Doc[] = "This is the type of PgVersion objects"; *************** *** 329,338 **** (printfunc)NULL, /* tp_print */ (getattrfunc)PgVersion_getattr, /* tp_getattr */ ! (setattrfunc)NULL, /* tp_setattr */ (cmpfunc)PgVersion_cmp, /* tp_compare */ (reprfunc)PgVersion_repr, /* tp_repr */ &ver_as_number, /* tp_as_number */ NULL, /* tp_as_sequence */ ! NULL, /* tp_as_mapping */ (hashfunc)NULL, /* tp_hash */ (ternaryfunc)NULL, /* tp_call */ --- 518,527 ---- (printfunc)NULL, /* tp_print */ (getattrfunc)PgVersion_getattr, /* tp_getattr */ ! (setattrfunc)PgVersion_setattr, /* tp_setattr */ (cmpfunc)PgVersion_cmp, /* tp_compare */ (reprfunc)PgVersion_repr, /* tp_repr */ &ver_as_number, /* tp_as_number */ NULL, /* tp_as_sequence */ ! &ver_as_mapping, /* tp_as_mapping */ (hashfunc)NULL, /* tp_hash */ (ternaryfunc)NULL, /* tp_call */ |