Changeset 655 for branches/4.x


Ignore:
Timestamp:
Nov 28, 2015, 7:32:31 AM (4 years ago)
Author:
cito
Message:

Fix garbage collection issues

This patch fixes memory management problems, particularly on Windows.

The query() method and the handling of object references inside the method has
been greatly simplified and corrected. Note that we don't calculate and pass
the paramLengths any more since this is only needed when also passing info about
binary data in paramFormats. We also use PyMem_Malloc() instead of malloc() to
allocate memory to make sure the memory is allocated in the Python heap.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • branches/4.x/module/pgmodule.c

    r652 r655  
    355355        int j;
    356356
    357         if (!(typ = malloc(sizeof(int) * nfields)))
     357        if (!(typ = PyMem_Malloc(sizeof(int) * nfields)))
    358358        {
    359359                PyErr_SetString(PyExc_MemoryError, "memory error in getresult().");
     
    415415        if (n > 0)
    416416        {
    417                 char * const aligns = (char *) malloc(n * sizeof(char));
    418                 int * const sizes = (int *) malloc(n * sizeof(int));
     417                char * const aligns = (char *) PyMem_Malloc(n * sizeof(char));
     418                int * const sizes = (int *) PyMem_Malloc(n * sizeof(int));
    419419
    420420                if (aligns && sizes)
     
    485485                        size += 40;
    486486                        /* is the buffer size that needs to be allocated */
    487                         buffer = (char *) malloc(size);
     487                        buffer = (char *) PyMem_Malloc(size);
    488488                        if (buffer)
    489489                        {
     
    542542                                }
    543543                                /* free memory */
    544                                 free(aligns);
    545                                 free(sizes);
     544                                PyMem_Free(aligns);
     545                                PyMem_Free(sizes);
    546546                                /* create the footer */
    547547                                sprintf(p, "(%d row%s)", m, m == 1 ? "" : "s");
    548548                                /* return the result */
    549549                                result = PyString_FromString(buffer);
    550                                 free(buffer);
     550                                PyMem_Free(buffer);
    551551                                return result;
    552552                        }
     
    558558                        }
    559559                } else {
    560                         if (aligns)
    561                                 free(aligns);
    562                         if (sizes)
    563                                 free(aligns);
     560                        PyMem_Free(aligns);
     561                        PyMem_Free(sizes);
    564562                        PyErr_SetString(PyExc_MemoryError,
    565563                                "Not enough memory for formatting the query result.");
     
    22472245
    22482246exit:
    2249         free(typ);
     2247        PyMem_Free(typ);
    22502248
    22512249        /* returns list */
     
    23892387
    23902388exit:
    2391         free(typ);
     2389        PyMem_Free(typ);
    23922390
    23932391        /* returns list */
     
    25302528{
    25312529        char            *query;
    2532         PyObject        *oargs = NULL;
     2530        PyObject        *param_obj = NULL;
    25332531        PGresult        *result;
    25342532        pgqueryobject *npgobj;
     
    25432541
    25442542        /* get query args */
    2545         if (!PyArg_ParseTuple(args, "s|O", &query, &oargs))
     2543        if (!PyArg_ParseTuple(args, "s|O", &query, &param_obj))
    25462544        {
    25472545                PyErr_SetString(PyExc_TypeError, "query(sql, [args]), with sql (string).");
     
    25492547        }
    25502548
    2551         /* If oargs is passed, ensure it's a non-empty tuple. We want to treat
     2549        /* If param_obj is passed, ensure it's a non-empty tuple. We want to treat
    25522550         * an empty tuple the same as no argument since we'll get that when the
    25532551         * caller passes no arguments to db.query(), and historic behaviour was
    25542552         * to call PQexec() in that case, which can execute multiple commands. */
    2555         if (oargs)
    2556         {
    2557                 if (!PyTuple_Check(oargs) && !PyList_Check(oargs))
     2553        if (param_obj)
     2554        {
     2555                param_obj = PySequence_Fast(param_obj,
     2556                        "query parameters must be a sequence.");
     2557                if (!param_obj)
     2558                        return NULL;
     2559                nparms = (int)PySequence_Fast_GET_SIZE(param_obj);
     2560
     2561                /* if there's a single argument and it's a list or tuple, it
     2562                 * contains the positional arguments. */
     2563                if (nparms == 1)
    25582564                {
    2559                         PyErr_SetString(PyExc_TypeError, "query parameters must be a tuple or list.");
     2565                        PyObject *first_obj = PySequence_Fast_GET_ITEM(param_obj, 0);
     2566                        if (PyList_Check(first_obj) || PyTuple_Check(first_obj))
     2567                        {
     2568                                Py_DECREF(param_obj);
     2569                                param_obj = PySequence_Fast(first_obj, NULL);
     2570                                nparms = (int)PySequence_Fast_GET_SIZE(param_obj);
     2571                        }
     2572                }
     2573        }
     2574
     2575        /* gets result */
     2576        if (nparms)
     2577        {
     2578                /* prepare arguments */
     2579                PyObject        **str, **s;
     2580                char            **parms, **p, *enc=NULL;
     2581                register int i;
     2582
     2583                str = (PyObject **)PyMem_Malloc(nparms * sizeof(*str));
     2584                parms = (char **)PyMem_Malloc(nparms * sizeof(*parms));
     2585                if (!str || !parms) {
     2586                        PyMem_Free(parms);
     2587                        PyMem_Free(str);
     2588                        Py_XDECREF(param_obj);
     2589                        PyErr_SetString(PyExc_MemoryError, "memory error in query().");
    25602590                        return NULL;
    25612591                }
    2562 
    2563                 nparms = (int)PySequence_Size(oargs);
    2564         }
    2565 
    2566         /* gets result */
    2567         if (nparms)
    2568         {
    2569                 /* prepare arguments */
    2570                 PyObject        **str, **s, *obj = PySequence_GetItem(oargs, 0);
    2571                 char            **parms, **p, *enc=NULL;
    2572                 int                     *lparms, *l;
    2573                 register int i;
    2574 
    2575                 /* if there's a single argument and it's a list or tuple, it
    2576                  * contains the positional aguments. */
    2577                 if (nparms == 1 && (PyList_Check(obj) || PyTuple_Check(obj)))
    2578                 {
    2579                         oargs = obj;
    2580                         nparms = (int)PySequence_Size(oargs);
    2581                 }
    2582                 str = (PyObject **)malloc(nparms * sizeof(*str));
    2583                 parms = (char **)malloc(nparms * sizeof(*parms));
    2584                 lparms = (int *)malloc(nparms * sizeof(*lparms));
    25852592
    25862593                /* convert optional args to a list of strings -- this allows
    25872594                 * the caller to pass whatever they like, and prevents us
    25882595                 * from having to map types to OIDs */
    2589                 for (i = 0, s=str, p=parms, l=lparms; i < nparms; i++, s++, p++, l++)
     2596                for (i = 0, s=str, p=parms; i < nparms; i++, p++)
    25902597                {
    2591                         obj = PySequence_GetItem(oargs, i);
     2598                        PyObject *obj = PySequence_Fast_GET_ITEM(param_obj, i);
    25922599
    25932600                        if (obj == Py_None)
    25942601                        {
    2595                                 *s = NULL;
    25962602                                *p = NULL;
    2597                                 *l = 0;
    25982603                        }
    25992604                        else if (PyUnicode_Check(obj))
    26002605                        {
     2606                                PyObject *str_obj;
    26012607                                if (!enc)
    26022608                                        enc = (char *)pg_encoding_to_char(
    26032609                                                PQclientEncoding(self->cnx));
    26042610                                if (!strcmp(enc, "UTF8"))
    2605                                         *s = PyUnicode_AsUTF8String(obj);
     2611                                        str_obj = PyUnicode_AsUTF8String(obj);
    26062612                                else if (!strcmp(enc, "LATIN1"))
    2607                                         *s = PyUnicode_AsLatin1String(obj);
     2613                                        str_obj = PyUnicode_AsLatin1String(obj);
    26082614                                else if (!strcmp(enc, "SQL_ASCII"))
    2609                                         *s = PyUnicode_AsASCIIString(obj);
     2615                                        str_obj = PyUnicode_AsASCIIString(obj);
    26102616                                else
    2611                                         *s = PyUnicode_AsEncodedString(obj, enc, "strict");
    2612                                 if (*s == NULL)
     2617                                        str_obj = PyUnicode_AsEncodedString(obj, enc, "strict");
     2618                                if (!str_obj)
    26132619                                {
    2614                                         free(lparms); free(parms);
    2615                                         while (i--)
    2616                                         {
    2617                                                 if (*--s)
    2618                                                 {
    2619                                                         Py_DECREF(*s);
    2620                                                 }
    2621                                         }
    2622                                         free(str);
     2620                                        PyMem_Free(parms);
     2621                                        while (s != str) { s--; Py_DECREF(*s); }
     2622                                        PyMem_Free(str);
     2623                                        Py_XDECREF(param_obj);
    26232624                                        PyErr_SetString(PyExc_UnicodeError, "query parameter"
    26242625                                                " could not be decoded (bad client encoding)");
    26252626                                        return NULL;
    26262627                                }
    2627                                 *p = PyString_AsString(*s);
    2628                                 *l = (int)PyString_Size(*s);
     2628                                *s++ = str_obj;
     2629                                *p = PyString_AsString(str_obj);
    26292630                        }
    26302631                        else
    26312632                        {
    2632                                 *s = PyObject_Str(obj);
    2633                                 if (*s == NULL)
     2633                                PyObject *str_obj = PyObject_Str(obj);
     2634                                if (!str_obj)
    26342635                                {
    2635                                         free(lparms); free(parms);
    2636                                         while (i--)
    2637                                         {
    2638                                                 if (*--s)
    2639                                                 {
    2640                                                         Py_DECREF(*s);
    2641                                                 }
    2642                                         }
    2643                                         free(str);
     2636                                        PyMem_Free(parms);
     2637                                        while (s != str) { s--; Py_DECREF(*s); }
     2638                                        PyMem_Free(str);
     2639                                        Py_XDECREF(param_obj);
    26442640                                        PyErr_SetString(PyExc_TypeError,
    26452641                                                "query parameter has no string representation");
    26462642                                        return NULL;
    26472643                                }
    2648                                 *p = PyString_AsString(*s);
    2649                                 *l = (int)PyString_Size(*s);
     2644                                *s++ = str_obj;
     2645                                *p = PyString_AsString(str_obj);
    26502646                        }
    26512647                }
     
    26532649                Py_BEGIN_ALLOW_THREADS
    26542650                result = PQexecParams(self->cnx, query, nparms,
    2655                         NULL, (const char * const *)parms, lparms, NULL, 0);
     2651                        NULL, (const char * const *)parms, NULL, NULL, 0);
    26562652                Py_END_ALLOW_THREADS
    26572653
    2658                 free(lparms); free(parms);
    2659                 for (i = 0, s=str; i < nparms; i++, s++)
    2660                 {
    2661                         if (*s)
    2662                         {
    2663                                 Py_DECREF(*s);
    2664                         }
    2665                 }
    2666                 free(str);
     2654                PyMem_Free(parms);
     2655                while (s != str) { s--; Py_DECREF(*s); }
     2656                PyMem_Free(str);
    26672657        }
    26682658        else
     
    26722662                Py_END_ALLOW_THREADS
    26732663        }
     2664
     2665        /* we don't need the params any more */
     2666        Py_XDECREF(param_obj);
    26742667
    26752668        /* checks result validity */
     
    29132906
    29142907        /* allocate buffer */
    2915         if (!(buffer = malloc(MAX_BUFFER_SIZE)))
     2908        if (!(buffer = PyMem_Malloc(MAX_BUFFER_SIZE)))
    29162909        {
    29172910                PyErr_SetString(PyExc_MemoryError,
     
    29292922        if (!result)
    29302923        {
    2931                 free(buffer);
     2924                PyMem_Free(buffer);
    29322925                PyErr_SetString(PyExc_ValueError, PQerrorMessage(self->cnx));
    29332926                return NULL;
     
    29622955                        if (j != n)
    29632956                        {
    2964                                 free(buffer);
     2957                                PyMem_Free(buffer);
    29652958                                PyErr_SetString(PyExc_TypeError,
    29662959                                        "arrays contained in second arg must have same size.");
     
    30383031                        if (bufsiz <= 0)
    30393032                        {
    3040                                 free(buffer);
     3033                                PyMem_Free(buffer);
    30413034                                PyErr_SetString(PyExc_MemoryError,
    30423035                                        "insert buffer overflow.");
     
    30533046                        PyErr_SetString(PyExc_IOError, PQerrorMessage(self->cnx));
    30543047                        PQendcopy(self->cnx);
    3055                         free(buffer);
     3048                        PyMem_Free(buffer);
    30563049                        return NULL;
    30573050                }
     
    30633056                PyErr_SetString(PyExc_IOError, PQerrorMessage(self->cnx));
    30643057                PQendcopy(self->cnx);
    3065                 free(buffer);
     3058                PyMem_Free(buffer);
    30663059                return NULL;
    30673060        }
     
    30703063        {
    30713064                PyErr_SetString(PyExc_IOError, PQerrorMessage(self->cnx));
    3072                 free(buffer);
    3073                 return NULL;
    3074         }
    3075 
    3076         free(buffer);
     3065                PyMem_Free(buffer);
     3066                return NULL;
     3067        }
     3068
     3069        PyMem_Free(buffer);
    30773070
    30783071        /* no error : returns nothing */
     
    32083201                from_length = (from_length - 1)/2;
    32093202        }
    3210         to = (char *)malloc(to_length);
     3203        to = (char *)PyMem_Malloc(to_length);
    32113204        to_length = (int)PQescapeStringConn(self->cnx,
    32123205                to, from, (size_t)from_length, NULL);
    32133206        ret = Py_BuildValue("s#", to, to_length);
    3214         if (to)
    3215                 free(to);
     3207        PyMem_Free(to);
    32163208        if (!ret) /* pass on exception */
    32173209                return NULL;
     
    36803672                from_length = (from_length - 1)/2;
    36813673        }
    3682         to = (char *)malloc(to_length);
     3674        to = (char *)PyMem_Malloc(to_length);
    36833675        to_length = (int)PQescapeString(to, from, (size_t)from_length);
    36843676        ret = Py_BuildValue("s#", to, to_length);
    3685         if (to)
    3686                 free(to);
     3677        PyMem_Free(to);
    36873678        if (!ret) /* pass on exception */
    36883679                return NULL;
Note: See TracChangeset for help on using the changeset viewer.