Ejemplo n.º 1
0
/*
 * hstoreUpgrade: PG_DETOAST_DATUM plus support for conversion of old hstores
 */
HStore *
hstoreUpgrade(Datum orig)
{
    HStore	   *hs = (HStore *) PG_DETOAST_DATUM(orig);
    int			valid_new;
    int			valid_old;
    bool		writable;

    /* Return immediately if no conversion needed */
    if ((hs->size_ & HS_FLAG_NEWVERSION) ||
            hs->size_ == 0 ||
            (VARSIZE(hs) < 32768 && HSE_ISFIRST((ARRPTR(hs)[0]))))
        return hs;

    valid_new = hstoreValidNewFormat(hs);
    valid_old = hstoreValidOldFormat(hs);
    /* Do we have a writable copy? */
    writable = ((void *) hs != (void *) DatumGetPointer(orig));

    if (!valid_old || hs->size_ == 0)
    {
        if (valid_new)
        {
            /*
             * force the "new version" flag and the correct varlena length,
             * but only if we have a writable copy already (which we almost
             * always will, since short new-format values won't come through
             * here)
             */
            if (writable)
            {
                HS_SETCOUNT(hs, HS_COUNT(hs));
                HS_FIXSIZE(hs, HS_COUNT(hs));
            }
            return hs;
        }
        else
        {
            elog(ERROR, "invalid hstore value found");
        }
    }

    /*
     * this is the tricky edge case. It is only possible in some quite extreme
     * cases (the hstore must have had a lot of wasted padding space at the
     * end). But the only way a "new" hstore value could get here is if we're
     * upgrading in place from a pre-release version of hstore-new (NOT
     * contrib/hstore), so we work off the following assumptions: 1. If you're
     * moving from old contrib/hstore to hstore-new, you're required to fix up
     * any potential conflicts first, e.g. by running ALTER TABLE ... USING
     * col::text::hstore; on all hstore columns before upgrading. 2. If you're
     * moving from old contrib/hstore to new contrib/hstore, then "new" values
     * are impossible here 3. If you're moving from pre-release hstore-new to
     * hstore-new, then "old" values are impossible here 4. If you're moving
     * from pre-release hstore-new to new contrib/hstore, you're not doing so
     * as an in-place upgrade, so there is no issue So the upshot of all this
     * is that we can treat all the edge cases as "new" if we're being built
     * as hstore-new, and "old" if we're being built as contrib/hstore.
     *
     * XXX the WARNING can probably be downgraded to DEBUG1 once this has been
     * beta-tested. But for now, it would be very useful to know if anyone can
     * actually reach this case in a non-contrived setting.
     */

    if (valid_new)
    {
#if HSTORE_IS_HSTORE_NEW
        elog(WARNING, "ambiguous hstore value resolved as hstore-new");

        /*
         * force the "new version" flag and the correct varlena length, but
         * only if we have a writable copy already (which we almost always
         * will, since short new-format values won't come through here)
         */
        if (writable)
        {
            HS_SETCOUNT(hs, HS_COUNT(hs));
            HS_FIXSIZE(hs, HS_COUNT(hs));
        }
        return hs;
#else
        elog(WARNING, "ambiguous hstore value resolved as hstore-old");
#endif
    }

    /*
     * must have an old-style value. Overwrite it in place as a new-style one,
     * making sure we have a writable copy first.
     */

    if (!writable)
        hs = (HStore *) PG_DETOAST_DATUM_COPY(orig);

    {
        int			count = hs->size_;
        HEntry	   *new_entries = ARRPTR(hs);
        HOldEntry  *old_entries = (HOldEntry *) ARRPTR(hs);
        int			i;

        for (i = 0; i < count; ++i)
        {
            uint32		pos = old_entries[i].pos;
            uint32		keylen = old_entries[i].keylen;
            uint32		vallen = old_entries[i].vallen;
            bool		isnull = old_entries[i].valisnull;

            if (isnull)
                vallen = 0;

            new_entries[2 * i].entry = (pos + keylen) & HENTRY_POSMASK;
            new_entries[2 * i + 1].entry = (((pos + keylen + vallen) & HENTRY_POSMASK)
                                            | ((isnull) ? HENTRY_ISNULL : 0));
        }

        if (count)
            new_entries[0].entry |= HENTRY_ISFIRST;
        HS_SETCOUNT(hs, count);
        HS_FIXSIZE(hs, count);
    }

    return hs;
}
Ejemplo n.º 2
0
Datum
hstore_delete_hstore(PG_FUNCTION_ARGS)
{
    HStore	   *hs = PG_GETARG_HS(0);
    HStore	   *hs2 = PG_GETARG_HS(1);
    HStore	   *out = palloc(VARSIZE(hs));
    int			hs_count = HS_COUNT(hs);
    int			hs2_count = HS_COUNT(hs2);
    char	   *ps,
               *ps2,
               *bufd,
               *pd;
    HEntry	   *es,
               *es2,
               *ed;
    int			i,
                j;
    int			outcount = 0;

    SET_VARSIZE(out, VARSIZE(hs));
    HS_SETCOUNT(out, hs_count); /* temporary! */

    ps = STRPTR(hs);
    es = ARRPTR(hs);
    ps2 = STRPTR(hs2);
    es2 = ARRPTR(hs2);
    bufd = pd = STRPTR(out);
    ed = ARRPTR(out);

    if (hs2_count == 0)
    {
        /* return a copy of the input, unchanged */
        memcpy(out, hs, VARSIZE(hs));
        HS_FIXSIZE(out, hs_count);
        HS_SETCOUNT(out, hs_count);
        PG_RETURN_POINTER(out);
    }

    /*
     * this is in effect a merge between hs and hs2, both of which are already
     * sorted by (keylen,key); we take keys from hs only; for equal keys, we
     * take the value from hs unless the values are equal
     */

    for (i = j = 0; i < hs_count;)
    {
        int			difference;

        if (j >= hs2_count)
            difference = -1;
        else
        {
            int			skeylen = HS_KEYLEN(es, i);
            int			s2keylen = HS_KEYLEN(es2, j);

            if (skeylen == s2keylen)
                difference = memcmp(HS_KEY(es, ps, i),
                                    HS_KEY(es2, ps2, j),
                                    skeylen);
            else
                difference = (skeylen > s2keylen) ? 1 : -1;
        }

        if (difference > 0)
            ++j;
        else if (difference == 0)
        {
            int			svallen = HS_VALLEN(es, i);
            int			snullval = HS_VALISNULL(es, i);

            if (snullval != HS_VALISNULL(es2, j)
                    || (!snullval
                        && (svallen != HS_VALLEN(es2, j)
                            || memcmp(HS_VAL(es, ps, i), HS_VAL(es2, ps2, j), svallen) != 0)))
            {
                HS_COPYITEM(ed, bufd, pd,
                            HS_KEY(es, ps, i), HS_KEYLEN(es, i),
                            svallen, snullval);
                ++outcount;
            }
            ++i, ++j;
        }
        else
        {
            HS_COPYITEM(ed, bufd, pd,
                        HS_KEY(es, ps, i), HS_KEYLEN(es, i),
                        HS_VALLEN(es, i), HS_VALISNULL(es, i));
            ++outcount;
            ++i;
        }
    }

    HS_FINALIZE(out, outcount, bufd, pd);

    PG_RETURN_POINTER(out);
}
Ejemplo n.º 3
0
Datum
hstore_concat(PG_FUNCTION_ARGS)
{
    HStore	   *s1 = PG_GETARG_HS(0);
    HStore	   *s2 = PG_GETARG_HS(1);
    HStore	   *out = palloc(VARSIZE(s1) + VARSIZE(s2));
    char	   *ps1,
               *ps2,
               *bufd,
               *pd;
    HEntry	   *es1,
               *es2,
               *ed;
    int			s1idx;
    int			s2idx;
    int			s1count = HS_COUNT(s1);
    int			s2count = HS_COUNT(s2);
    int			outcount = 0;

    SET_VARSIZE(out, VARSIZE(s1) + VARSIZE(s2) - HSHRDSIZE);
    HS_SETCOUNT(out, s1count + s2count);

    if (s1count == 0)
    {
        /* return a copy of the input, unchanged */
        memcpy(out, s2, VARSIZE(s2));
        HS_FIXSIZE(out, s2count);
        HS_SETCOUNT(out, s2count);
        PG_RETURN_POINTER(out);
    }

    if (s2count == 0)
    {
        /* return a copy of the input, unchanged */
        memcpy(out, s1, VARSIZE(s1));
        HS_FIXSIZE(out, s1count);
        HS_SETCOUNT(out, s1count);
        PG_RETURN_POINTER(out);
    }

    ps1 = STRPTR(s1);
    ps2 = STRPTR(s2);
    bufd = pd = STRPTR(out);
    es1 = ARRPTR(s1);
    es2 = ARRPTR(s2);
    ed = ARRPTR(out);

    /*
     * this is in effect a merge between s1 and s2, both of which are already
     * sorted by (keylen,key); we take s2 for equal keys
     */

    for (s1idx = s2idx = 0; s1idx < s1count || s2idx < s2count; ++outcount)
    {
        int			difference;

        if (s1idx >= s1count)
            difference = 1;
        else if (s2idx >= s2count)
            difference = -1;
        else
        {
            int			s1keylen = HS_KEYLEN(es1, s1idx);
            int			s2keylen = HS_KEYLEN(es2, s2idx);

            if (s1keylen == s2keylen)
                difference = memcmp(HS_KEY(es1, ps1, s1idx),
                                    HS_KEY(es2, ps2, s2idx),
                                    s1keylen);
            else
                difference = (s1keylen > s2keylen) ? 1 : -1;
        }

        if (difference >= 0)
        {
            HS_COPYITEM(ed, bufd, pd,
                        HS_KEY(es2, ps2, s2idx), HS_KEYLEN(es2, s2idx),
                        HS_VALLEN(es2, s2idx), HS_VALISNULL(es2, s2idx));
            ++s2idx;
            if (difference == 0)
                ++s1idx;
        }
        else
        {
            HS_COPYITEM(ed, bufd, pd,
                        HS_KEY(es1, ps1, s1idx), HS_KEYLEN(es1, s1idx),
                        HS_VALLEN(es1, s1idx), HS_VALISNULL(es1, s1idx));
            ++s1idx;
        }
    }

    HS_FINALIZE(out, outcount, bufd, pd);

    PG_RETURN_POINTER(out);
}
Ejemplo n.º 4
0
Datum
hstore_delete_array(PG_FUNCTION_ARGS)
{
    HStore	   *hs = PG_GETARG_HS(0);
    HStore	   *out = palloc(VARSIZE(hs));
    int			hs_count = HS_COUNT(hs);
    char	   *ps,
               *bufd,
               *pd;
    HEntry	   *es,
               *ed;
    int			i,
                j;
    int			outcount = 0;
    ArrayType  *key_array = PG_GETARG_ARRAYTYPE_P(1);
    int			nkeys;
    Pairs	   *key_pairs = hstoreArrayToPairs(key_array, &nkeys);

    SET_VARSIZE(out, VARSIZE(hs));
    HS_SETCOUNT(out, hs_count); /* temporary! */

    ps = STRPTR(hs);
    es = ARRPTR(hs);
    bufd = pd = STRPTR(out);
    ed = ARRPTR(out);

    if (nkeys == 0)
    {
        /* return a copy of the input, unchanged */
        memcpy(out, hs, VARSIZE(hs));
        HS_FIXSIZE(out, hs_count);
        HS_SETCOUNT(out, hs_count);
        PG_RETURN_POINTER(out);
    }

    /*
     * this is in effect a merge between hs and key_pairs, both of which are
     * already sorted by (keylen,key); we take keys from hs only
     */

    for (i = j = 0; i < hs_count;)
    {
        int			difference;

        if (j >= nkeys)
            difference = -1;
        else
        {
            int			skeylen = HS_KEYLEN(es, i);

            if (skeylen == key_pairs[j].keylen)
                difference = memcmp(HS_KEY(es, ps, i),
                                    key_pairs[j].key,
                                    key_pairs[j].keylen);
            else
                difference = (skeylen > key_pairs[j].keylen) ? 1 : -1;
        }

        if (difference > 0)
            ++j;
        else if (difference == 0)
            ++i, ++j;
        else
        {
            HS_COPYITEM(ed, bufd, pd,
                        HS_KEY(es, ps, i), HS_KEYLEN(es, i),
                        HS_VALLEN(es, i), HS_VALISNULL(es, i));
            ++outcount;
            ++i;
        }
    }

    HS_FINALIZE(out, outcount, bufd, pd);

    PG_RETURN_POINTER(out);
}