Changeset 791 for trunk/pgmodule.c


Ignore:
Timestamp:
Jan 27, 2016, 6:09:18 PM (4 years ago)
Author:
cito
Message:

Add support for composite types

Added a fast parser for the composite type input/output syntax, which is
similar to the already existing parser for the array input/output syntax.

The pgdb module now makes use of this parser, converting in both directions
between PostgreSQL records (composite types) and Python (named) tuples.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/pgmodule.c

    r785 r791  
    556556                        buf[j] = '\0'; s = buf;
    557557                        /* FALLTHROUGH */ /* no break here */
    558        
     558
    559559                case PYGRES_DECIMAL:
    560560                        if (decimal)
     
    598598        && (s[3] == 'l' || s[3] == 'L'))
    599599
    600 /* Cast string s with size and encoding to a Python list.
    601    Use cast function if specified or basetype to cast elements.
     600/* Cast string s with size and encoding to a Python list,
     601   using the input and output syntax for arrays.
     602   Use internal type or cast function to cast elements.
    602603   The parameter delim specifies the delimiter for the elements,
    603604   since some types do not use the default delimiter of a comma. */
     
    615616                if (!type) type = PYGRES_TEXT;
    616617        }
    617         if (!delim) delim = ',';
     618        if (!delim)
     619                delim = ',';
     620        else if (delim == '{' || delim =='}' || delim=='\\')
     621        {
     622                PyErr_SetString(PyExc_ValueError, "Invalid array delimiter");
     623                return NULL;
     624        }
    618625
    619626        /* strip blanks at the beginning */
     
    654661        {
    655662                PyErr_SetString(PyExc_ValueError,
    656                         "Array must start with an opening brace");
     663                        "Array must start with a left brace");
    657664                return NULL;
    658665        }
     
    690697                                        PyErr_SetString(PyExc_ValueError,
    691698                                                "Subarray expected but not found");
    692                                         return NULL;
     699                                        Py_DECREF(result); return NULL;
    693700                                }
    694701                        }
     
    696703                        subresult = result;
    697704                        result = stack[--level];
    698                         if (PyList_Append(result, subresult)) return NULL;
     705                        if (PyList_Append(result, subresult))
     706                        {
     707                                Py_DECREF(result); return NULL;
     708                        }
    699709                }
    700710                else if (level == depth) /* we expect elements at this level */
     
    709719                                PyErr_SetString(PyExc_ValueError,
    710720                                        "Subarray found where not expected");
    711                                 return NULL;
     721                                Py_DECREF(result); return NULL;
    712722                        }
    713723                        if (*s == '"') /* quoted element */
     
    753763                                if (escaped)
    754764                                {
    755                                         char   *r;
    756                                         int             i;
     765                                        char       *r;
     766                                        Py_ssize_t      i;
    757767
    758768                                        /* create unescaped string */
    759769                                        t = estr;
    760770                                        estr = (char *) PyMem_Malloc(esize);
    761                                         if (!estr) return PyErr_NoMemory();
     771                                        if (!estr)
     772                                        {
     773                                                Py_DECREF(result); return PyErr_NoMemory();
     774                                        }
    762775                                        for (i = 0, r = estr; i < esize; ++i)
    763776                                        {
     
    789802                                }
    790803                                if (escaped) PyMem_Free(estr);
    791                                 if (!element) return NULL;
     804                                if (!element)
     805                                {
     806                                        Py_DECREF(result); return NULL;
     807                                }
    792808                        }
    793809                        else
    794810                        {
    795                                 Py_INCREF(Py_None);
    796                                 element = Py_None;
     811                                Py_INCREF(Py_None); element = Py_None;
    797812                        }
    798                         if (PyList_Append(result, element)) return NULL;
     813                        if (PyList_Append(result, element))
     814                        {
     815                                Py_DECREF(element); Py_DECREF(result); return NULL;
     816                        }
     817                        Py_DECREF(element);
    799818                        if (*s == delim)
    800819                        {
     
    809828                        {
    810829                                PyErr_SetString(PyExc_ValueError,
    811                                         "Subarray must start with an opening brace");
    812                                 return NULL;
     830                                        "Subarray must start with a left brace");
     831                                Py_DECREF(result); return NULL;
    813832                        }
    814833                        do ++s; while (s != end && *s == ' ');
     
    822841                PyErr_SetString(PyExc_ValueError,
    823842                        "Unexpected end of array");
    824                 return NULL;
     843                Py_DECREF(result); return NULL;
    825844        }
    826845        do ++s; while (s != end && *s == ' ');
     
    829848                PyErr_SetString(PyExc_ValueError,
    830849                        "Unexpected characters after end of array");
    831                 return NULL;
     850                Py_DECREF(result); return NULL;
    832851        }
    833852        return result;
     853}
     854
     855/* Cast string s with size and encoding to a Python tuple.
     856   using the input and output syntax for composite types.
     857   Use array of internal types or cast function or sequence of cast
     858   functions to cast elements. The parameter len is the record size.
     859   The parameter delim can specify a delimiter for the elements,
     860   although composite types always use a comma as delimiter. */
     861
     862static PyObject *
     863cast_record(char *s, Py_ssize_t size, int encoding,
     864         int *type, PyObject *cast, Py_ssize_t len, char delim)
     865{
     866        PyObject   *result, *ret;
     867        char       *end = s + size, *t;
     868        Py_ssize_t      i;
     869
     870        if (!delim)
     871                delim = ',';
     872        else if (delim == '(' || delim ==')' || delim=='\\')
     873        {
     874                PyErr_SetString(PyExc_ValueError, "Invalid record delimiter");
     875                return NULL;
     876        }
     877
     878        /* strip blanks at the beginning */
     879        while (s != end && *s == ' ') ++s;
     880        if (s == end || *s != '(')
     881        {
     882                PyErr_SetString(PyExc_ValueError,
     883                        "Record must start with a left parenthesis");
     884                return NULL;
     885        }
     886        result = PyList_New(0);
     887        if (!result) return NULL;
     888        i = 0;
     889        /* everything is set up, start parsing the record */
     890        while (++s != end)
     891        {
     892                PyObject   *element;
     893
     894                if (*s == ')' || *s == delim)
     895                {
     896                        Py_INCREF(Py_None); element = Py_None;
     897                }
     898                else
     899                {
     900                        char       *estr;
     901                        Py_ssize_t      esize;
     902                        int quoted = 0, escaped =0;
     903
     904                        estr = s;
     905                        quoted = *s == '"';
     906                        if (quoted) ++s;
     907                        esize = 0;
     908                        while (s != end)
     909                        {
     910                                if (!quoted && (*s == ')' || *s == delim))
     911                                        break;
     912                                if (*s == '"')
     913                                {
     914                                        ++s; if (s == end) break;
     915                                        if (!(quoted && *s == '"'))
     916                                        {
     917                                                quoted = !quoted; continue;
     918                                        }
     919                                }
     920                                if (*s == '\\')
     921                                {
     922                                        ++s; if (s == end) break;
     923                                }
     924                                ++s, ++esize;
     925                        }
     926                        if (s == end) break; /* error */
     927                        if (estr + esize != s)
     928                        {
     929                                char       *r;
     930
     931                                escaped = 1;
     932                                /* create unescaped string */
     933                                t = estr;
     934                                estr = (char *) PyMem_Malloc(esize);
     935                                if (!estr)
     936                                {
     937                                        Py_DECREF(result); return PyErr_NoMemory();
     938                                }
     939                                quoted = 0;
     940                                r = estr;
     941                                while (t != s)
     942                                {
     943                                        if (*t == '"')
     944                                        {
     945                                                ++t;
     946                                                if (!(quoted && *t == '"'))
     947                                                {
     948                                                        quoted = !quoted; continue;
     949                                                }
     950                                        }
     951                                        if (*t == '\\') ++t;
     952                                        *r++ = *t++;
     953                                }
     954                        }
     955                        if (type) /* internal casting of element type */
     956                        {
     957                                int etype = type[i];
     958
     959                                if (etype & PYGRES_ARRAY)
     960                                        element = cast_array(
     961                                                estr, esize, encoding, etype, NULL, 0);
     962                                else if (etype & PYGRES_TEXT)
     963                                        element = cast_sized_text(estr, esize, encoding, etype);
     964                                else
     965                                        element = cast_sized_simple(estr, esize, etype);
     966                        }
     967                        else /* external casting of base type */
     968                        {
     969#if IS_PY3
     970                                element = encoding == pg_encoding_ascii ? NULL :
     971                                        get_decoded_string(estr, esize, encoding);
     972                                if (!element) /* no decoding necessary or possible */
     973#endif
     974                                element = PyBytes_FromStringAndSize(estr, esize);
     975                                if (element && cast)
     976                                {
     977                                        if (len)
     978                                        {
     979                                                PyObject *ecast = PySequence_GetItem(cast, i);
     980
     981                                                if (ecast)
     982                                                {
     983                                                        if (ecast != Py_None)
     984                                                                element = PyObject_CallFunctionObjArgs(
     985                                                                        ecast, element, NULL);
     986                                                }
     987                                                else
     988                                                {
     989                                                        Py_DECREF(element); element = NULL;
     990                                                }
     991                                        }
     992                                        else
     993                                                element = PyObject_CallFunctionObjArgs(
     994                                                        cast, element, NULL);
     995                                }
     996                        }
     997                        if (escaped) PyMem_Free(estr);
     998                        if (!element)
     999                        {
     1000                                Py_DECREF(result); return NULL;
     1001                        }
     1002                }
     1003                if (PyList_Append(result, element))
     1004                {
     1005                        Py_DECREF(element); Py_DECREF(result); return NULL;
     1006                }
     1007                Py_DECREF(element);
     1008                if (len) ++i;
     1009                if (*s != delim) break; /* no next record */
     1010                if (len && i >= len)
     1011                {
     1012                        PyErr_SetString(PyExc_ValueError, "Too many columns");
     1013                        Py_DECREF(result); return NULL;
     1014                }
     1015        }
     1016        if (s == end || *s != ')')
     1017        {
     1018                PyErr_SetString(PyExc_ValueError, "Unexpected end of record");
     1019                Py_DECREF(result); return NULL;
     1020        }
     1021        do ++s; while (s != end && *s == ' ');
     1022        if (s != end)
     1023        {
     1024                PyErr_SetString(PyExc_ValueError,
     1025                        "Unexpected characters after end of record");
     1026                Py_DECREF(result); return NULL;
     1027        }
     1028        if (len && i < len)
     1029        {
     1030                PyErr_SetString(PyExc_ValueError, "Too few columns");
     1031                Py_DECREF(result); return NULL;
     1032        }
     1033
     1034        ret = PyList_AsTuple(result);
     1035        Py_DECREF(result);
     1036        return ret;
    8341037}
    8351038
     
    51785381{
    51795382        static const char *kwlist[] = {"string", "cast", "delim", NULL};
    5180         PyObject   *string_obj, *cast_obj = NULL;
    5181         char       *string;
     5383        PyObject   *string_obj, *cast_obj = NULL, *ret;
     5384        char       *string, delim = ',';
    51825385        Py_ssize_t      size;
    51835386        int                     encoding;
    5184         char            delim = ',';
    51855387
    51865388        if (!PyArg_ParseTupleAndKeywords(args, dict, "O|Oc",
     
    52085410
    52095411        if (!cast_obj || cast_obj == Py_None)
    5210                 cast_obj = NULL;
     5412        {
     5413                if (cast_obj)
     5414                {
     5415                        Py_DECREF(cast_obj); cast_obj = NULL;
     5416                }
     5417        }
    52115418        else if (!PyCallable_Check(cast_obj))
    52125419        {
     
    52155422        }
    52165423
    5217         return cast_array(string, size, encoding, 0, cast_obj, delim);
     5424        ret = cast_array(string, size, encoding, 0, cast_obj, delim);
     5425
     5426        Py_XDECREF(string_obj);
     5427
     5428        return ret;
     5429}
     5430
     5431/* cast a string with a text representation of a record to a tuple */
     5432static char pgCastRecord__doc__[] =
     5433"cast_record(string, cast=None, delim=',') -- cast a string as a record";
     5434
     5435PyObject *
     5436pgCastRecord(PyObject *self, PyObject *args, PyObject *dict)
     5437{
     5438        static const char *kwlist[] = {"string", "cast", "delim", NULL};
     5439        PyObject   *string_obj, *cast_obj = NULL, *ret;
     5440        char       *string, delim = ',';
     5441        Py_ssize_t      size, len;
     5442        int                     encoding;
     5443
     5444        if (!PyArg_ParseTupleAndKeywords(args, dict, "O|Oc",
     5445                        (char **) kwlist, &string_obj, &cast_obj, &delim))
     5446                return NULL;
     5447
     5448        if (PyBytes_Check(string_obj))
     5449        {
     5450                encoding = pg_encoding_ascii;
     5451                PyBytes_AsStringAndSize(string_obj, &string, &size);
     5452                string_obj = NULL;
     5453        }
     5454        else if (PyUnicode_Check(string_obj))
     5455        {
     5456                encoding = pg_encoding_utf8;
     5457                string_obj = get_encoded_string(string_obj, encoding);
     5458                if (!string_obj) return NULL; /* pass the UnicodeEncodeError */
     5459                PyBytes_AsStringAndSize(string_obj, &string, &size);
     5460        }
     5461        else
     5462        {
     5463                PyErr_SetString(PyExc_TypeError, "cast_record() expects a string");
     5464                return NULL;
     5465        }
     5466
     5467        if (!cast_obj || PyCallable_Check(cast_obj))
     5468        {
     5469                len = 0;
     5470        }
     5471        else if (cast_obj == Py_None)
     5472        {
     5473                Py_DECREF(cast_obj); cast_obj = NULL; len = 0;
     5474        }
     5475        else if (PyTuple_Check(cast_obj) || PyList_Check(cast_obj))
     5476        {
     5477                len = PySequence_Size(cast_obj);
     5478                if (!len)
     5479                {
     5480                        Py_DECREF(cast_obj); cast_obj = NULL;
     5481                }
     5482        }
     5483        else
     5484        {
     5485                PyErr_SetString(PyExc_TypeError,
     5486                        "The cast argument must be callable or a tuple or list of such");
     5487                return NULL;
     5488        }
     5489
     5490        ret = cast_record(string, size, encoding, 0, cast_obj, len, delim);
     5491
     5492        Py_XDECREF(string_obj);
     5493
     5494        return ret;
    52185495}
    52195496
     
    52505527        {"cast_array", (PyCFunction) pgCastArray, METH_VARARGS|METH_KEYWORDS,
    52515528                        pgCastArray__doc__},
     5529        {"cast_record", (PyCFunction) pgCastRecord, METH_VARARGS|METH_KEYWORDS,
     5530                        pgCastRecord__doc__},
    52525531
    52535532#ifdef DEFAULT_VARS
Note: See TracChangeset for help on using the changeset viewer.