Logo Search packages:      
Sourcecode: postgresql-8.4 version File versions

tsearch2.c

/*-------------------------------------------------------------------------
 *
 * tsearch2.c
 *          Backwards-compatibility package for old contrib/tsearch2 API
 *
 * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
 *
 *
 * IDENTIFICATION
 *      $PostgreSQL: pgsql/contrib/tsearch2/tsearch2.c,v 1.9 2009/01/28 18:31:32 teodor Exp $
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "catalog/namespace.h"
#include "catalog/pg_type.h"
#include "commands/trigger.h"
#include "fmgr.h"
#include "tsearch/ts_utils.h"
#include "utils/builtins.h"
#include "utils/guc.h"
#include "utils/syscache.h"

PG_MODULE_MAGIC;

static Oid  current_dictionary_oid = InvalidOid;
static Oid  current_parser_oid = InvalidOid;

/* insert given value at argument position 0 */
#define INSERT_ARGUMENT0(argument, isnull)                        \
      do {                                                                    \
            int i;                                                                  \
            for (i = fcinfo->nargs; i > 0; i--)                   \
            {                                                                       \
                  fcinfo->arg[i] = fcinfo->arg[i-1];              \
                  fcinfo->argnull[i] = fcinfo->argnull[i-1];      \
            }                                                                       \
            fcinfo->arg[0] = (argument);                          \
            fcinfo->argnull[0] = (isnull);                              \
            fcinfo->nargs++;                                            \
      } while (0)

#define TextGetObjectId(infunction, text) \
      DatumGetObjectId(DirectFunctionCall1(infunction, \
                               CStringGetDatum(text_to_cstring(text))))

#define UNSUPPORTED_FUNCTION(name)                                \
      Datum name(PG_FUNCTION_ARGS);                               \
      Datum                                                                   \
      name(PG_FUNCTION_ARGS)                                            \
      {                                                                             \
            ereport(ERROR,                                                    \
                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\
                         errmsg("function %s is no longer supported", \
                                    format_procedure(fcinfo->flinfo->fn_oid)), \
                         errhint("Switch to new tsearch functionality."))); \
            /* keep compiler quiet */                                   \
            PG_RETURN_NULL();                                           \
      }                                                                             \
      PG_FUNCTION_INFO_V1(name)

static Oid  GetCurrentDict(void);
static Oid  GetCurrentParser(void);

Datum       tsa_lexize_byname(PG_FUNCTION_ARGS);
Datum       tsa_lexize_bycurrent(PG_FUNCTION_ARGS);
Datum       tsa_set_curdict(PG_FUNCTION_ARGS);
Datum       tsa_set_curdict_byname(PG_FUNCTION_ARGS);
Datum       tsa_token_type_current(PG_FUNCTION_ARGS);
Datum       tsa_set_curprs(PG_FUNCTION_ARGS);
Datum       tsa_set_curprs_byname(PG_FUNCTION_ARGS);
Datum       tsa_parse_current(PG_FUNCTION_ARGS);
Datum       tsa_set_curcfg(PG_FUNCTION_ARGS);
Datum       tsa_set_curcfg_byname(PG_FUNCTION_ARGS);
Datum       tsa_to_tsvector_name(PG_FUNCTION_ARGS);
Datum       tsa_to_tsquery_name(PG_FUNCTION_ARGS);
Datum       tsa_plainto_tsquery_name(PG_FUNCTION_ARGS);
Datum       tsa_headline_byname(PG_FUNCTION_ARGS);
Datum       tsa_ts_stat(PG_FUNCTION_ARGS);
Datum       tsa_tsearch2(PG_FUNCTION_ARGS);
Datum       tsa_rewrite_accum(PG_FUNCTION_ARGS);
Datum       tsa_rewrite_finish(PG_FUNCTION_ARGS);

PG_FUNCTION_INFO_V1(tsa_lexize_byname);
PG_FUNCTION_INFO_V1(tsa_lexize_bycurrent);
PG_FUNCTION_INFO_V1(tsa_set_curdict);
PG_FUNCTION_INFO_V1(tsa_set_curdict_byname);
PG_FUNCTION_INFO_V1(tsa_token_type_current);
PG_FUNCTION_INFO_V1(tsa_set_curprs);
PG_FUNCTION_INFO_V1(tsa_set_curprs_byname);
PG_FUNCTION_INFO_V1(tsa_parse_current);
PG_FUNCTION_INFO_V1(tsa_set_curcfg);
PG_FUNCTION_INFO_V1(tsa_set_curcfg_byname);
PG_FUNCTION_INFO_V1(tsa_to_tsvector_name);
PG_FUNCTION_INFO_V1(tsa_to_tsquery_name);
PG_FUNCTION_INFO_V1(tsa_plainto_tsquery_name);
PG_FUNCTION_INFO_V1(tsa_headline_byname);
PG_FUNCTION_INFO_V1(tsa_ts_stat);
PG_FUNCTION_INFO_V1(tsa_tsearch2);
PG_FUNCTION_INFO_V1(tsa_rewrite_accum);
PG_FUNCTION_INFO_V1(tsa_rewrite_finish);


/*
 * List of unsupported functions
 *
 * The parser and dictionary functions are defined only so that the former
 * contents of pg_ts_parser and pg_ts_dict can be loaded into the system,
 * for ease of reference while creating the new tsearch configuration.
 */

UNSUPPORTED_FUNCTION(tsa_dex_init);
UNSUPPORTED_FUNCTION(tsa_dex_lexize);

UNSUPPORTED_FUNCTION(tsa_snb_en_init);
UNSUPPORTED_FUNCTION(tsa_snb_lexize);
UNSUPPORTED_FUNCTION(tsa_snb_ru_init_koi8);
UNSUPPORTED_FUNCTION(tsa_snb_ru_init_utf8);
UNSUPPORTED_FUNCTION(tsa_snb_ru_init);

UNSUPPORTED_FUNCTION(tsa_spell_init);
UNSUPPORTED_FUNCTION(tsa_spell_lexize);

UNSUPPORTED_FUNCTION(tsa_syn_init);
UNSUPPORTED_FUNCTION(tsa_syn_lexize);

UNSUPPORTED_FUNCTION(tsa_thesaurus_init);
UNSUPPORTED_FUNCTION(tsa_thesaurus_lexize);

UNSUPPORTED_FUNCTION(tsa_prsd_start);
UNSUPPORTED_FUNCTION(tsa_prsd_getlexeme);
UNSUPPORTED_FUNCTION(tsa_prsd_end);
UNSUPPORTED_FUNCTION(tsa_prsd_lextype);
UNSUPPORTED_FUNCTION(tsa_prsd_headline);

UNSUPPORTED_FUNCTION(tsa_reset_tsearch);
UNSUPPORTED_FUNCTION(tsa_get_covers);


/*
 * list of redefined functions
 */

/* lexize(text, text) */
Datum
tsa_lexize_byname(PG_FUNCTION_ARGS)
{
      text     *dictname = PG_GETARG_TEXT_PP(0);
      Datum       arg1 = PG_GETARG_DATUM(1);

      return DirectFunctionCall2(ts_lexize,
                        ObjectIdGetDatum(TextGetObjectId(regdictionaryin, dictname)),
                                             arg1);
}

/* lexize(text) */
Datum
tsa_lexize_bycurrent(PG_FUNCTION_ARGS)
{
      Datum       arg0 = PG_GETARG_DATUM(0);
      Oid               id = GetCurrentDict();

      return DirectFunctionCall2(ts_lexize,
                                             ObjectIdGetDatum(id),
                                             arg0);
}

/* set_curdict(int) */
Datum
tsa_set_curdict(PG_FUNCTION_ARGS)
{
      Oid               dict_oid = PG_GETARG_OID(0);

      if (!SearchSysCacheExists(TSDICTOID,
                                            ObjectIdGetDatum(dict_oid),
                                            0, 0, 0))
            elog(ERROR, "cache lookup failed for text search dictionary %u",
                   dict_oid);

      current_dictionary_oid = dict_oid;

      PG_RETURN_VOID();
}

/* set_curdict(text) */
Datum
tsa_set_curdict_byname(PG_FUNCTION_ARGS)
{
      text     *name = PG_GETARG_TEXT_PP(0);
      Oid               dict_oid;

      dict_oid = TSDictionaryGetDictid(stringToQualifiedNameList(text_to_cstring(name)), false);

      current_dictionary_oid = dict_oid;

      PG_RETURN_VOID();
}

/* token_type() */
Datum
tsa_token_type_current(PG_FUNCTION_ARGS)
{
      INSERT_ARGUMENT0(ObjectIdGetDatum(GetCurrentParser()), false);
      return ts_token_type_byid(fcinfo);
}

/* set_curprs(int) */
Datum
tsa_set_curprs(PG_FUNCTION_ARGS)
{
      Oid               parser_oid = PG_GETARG_OID(0);

      if (!SearchSysCacheExists(TSPARSEROID,
                                            ObjectIdGetDatum(parser_oid),
                                            0, 0, 0))
            elog(ERROR, "cache lookup failed for text search parser %u",
                   parser_oid);

      current_parser_oid = parser_oid;

      PG_RETURN_VOID();
}

/* set_curprs(text) */
Datum
tsa_set_curprs_byname(PG_FUNCTION_ARGS)
{
      text     *name = PG_GETARG_TEXT_PP(0);
      Oid               parser_oid;

      parser_oid = TSParserGetPrsid(stringToQualifiedNameList(text_to_cstring(name)), false);

      current_parser_oid = parser_oid;

      PG_RETURN_VOID();
}

/* parse(text) */
Datum
tsa_parse_current(PG_FUNCTION_ARGS)
{
      INSERT_ARGUMENT0(ObjectIdGetDatum(GetCurrentParser()), false);
      return ts_parse_byid(fcinfo);
}

/* set_curcfg(int) */
Datum
tsa_set_curcfg(PG_FUNCTION_ARGS)
{
      Oid               arg0 = PG_GETARG_OID(0);
      char     *name;

      name = DatumGetCString(DirectFunctionCall1(regconfigout,
                                                                     ObjectIdGetDatum(arg0)));

      set_config_option("default_text_search_config", name,
                                PGC_USERSET,
                                PGC_S_SESSION,
                                GUC_ACTION_SET,
                                true);

      PG_RETURN_VOID();
}

/* set_curcfg(text) */
Datum
tsa_set_curcfg_byname(PG_FUNCTION_ARGS)
{
      text     *arg0 = PG_GETARG_TEXT_PP(0);
      char     *name;

      name = text_to_cstring(arg0);

      set_config_option("default_text_search_config", name,
                                PGC_USERSET,
                                PGC_S_SESSION,
                                GUC_ACTION_SET,
                                true);

      PG_RETURN_VOID();
}

/* to_tsvector(text, text) */
Datum
tsa_to_tsvector_name(PG_FUNCTION_ARGS)
{
      text     *cfgname = PG_GETARG_TEXT_PP(0);
      Datum       arg1 = PG_GETARG_DATUM(1);
      Oid               config_oid;

      config_oid = TextGetObjectId(regconfigin, cfgname);

      return DirectFunctionCall2(to_tsvector_byid,
                                             ObjectIdGetDatum(config_oid), arg1);
}

/* to_tsquery(text, text) */
Datum
tsa_to_tsquery_name(PG_FUNCTION_ARGS)
{
      text     *cfgname = PG_GETARG_TEXT_PP(0);
      Datum       arg1 = PG_GETARG_DATUM(1);
      Oid               config_oid;

      config_oid = TextGetObjectId(regconfigin, cfgname);

      return DirectFunctionCall2(to_tsquery_byid,
                                             ObjectIdGetDatum(config_oid), arg1);
}


/* plainto_tsquery(text, text) */
Datum
tsa_plainto_tsquery_name(PG_FUNCTION_ARGS)
{
      text     *cfgname = PG_GETARG_TEXT_PP(0);
      Datum       arg1 = PG_GETARG_DATUM(1);
      Oid               config_oid;

      config_oid = TextGetObjectId(regconfigin, cfgname);

      return DirectFunctionCall2(plainto_tsquery_byid,
                                             ObjectIdGetDatum(config_oid), arg1);
}

/* headline(text, text, tsquery [,text]) */
Datum
tsa_headline_byname(PG_FUNCTION_ARGS)
{
      Datum       arg0 = PG_GETARG_DATUM(0);
      Datum       arg1 = PG_GETARG_DATUM(1);
      Datum       arg2 = PG_GETARG_DATUM(2);
      Datum       result;
      Oid               config_oid;

      /* first parameter has to be converted to oid */
      config_oid = DatumGetObjectId(DirectFunctionCall1(regconfigin,
                                                CStringGetDatum(TextDatumGetCString(arg0))));

      if (PG_NARGS() == 3)
            result = DirectFunctionCall3(ts_headline_byid,
                                                   ObjectIdGetDatum(config_oid), arg1, arg2);
      else
      {
            Datum       arg3 = PG_GETARG_DATUM(3);

            result = DirectFunctionCall4(ts_headline_byid_opt,
                                                       ObjectIdGetDatum(config_oid),
                                                       arg1, arg2, arg3);
      }

      return result;
}

/*
 * tsearch2 version of update trigger
 *
 * We pass this on to the core trigger after inserting the default text
 * search configuration name as the second argument.  Note that this isn't
 * a complete implementation of the original functionality; tsearch2 allowed
 * transformation function names to be included in the list.  However, that
 * is deliberately removed as being a security risk.
 */
Datum
tsa_tsearch2(PG_FUNCTION_ARGS)
{
      TriggerData *trigdata;
      Trigger    *trigger;
      char    **tgargs, 
                    **tgargs_old;
      int               i;
      Datum       res;

      /* Check call context */
      if (!CALLED_AS_TRIGGER(fcinfo))           /* internal error */
            elog(ERROR, "tsvector_update_trigger: not fired by trigger manager");

      trigdata = (TriggerData *) fcinfo->context;
      trigger = trigdata->tg_trigger;

      if (trigger->tgnargs < 2)
            elog(ERROR, "TSearch: format tsearch2(tsvector_field, text_field1,...)");

      /* create space for configuration name */
      tgargs = (char **) palloc((trigger->tgnargs + 1) * sizeof(char *));
      tgargs[0] = trigger->tgargs[0];
      for (i = 1; i < trigger->tgnargs; i++)
            tgargs[i + 1] = trigger->tgargs[i];

      tgargs[1] = pstrdup(GetConfigOptionByName("default_text_search_config",
                                                                    NULL));
      tgargs_old = trigger->tgargs;
      trigger->tgargs = tgargs;
      trigger->tgnargs++;

      res = tsvector_update_trigger_byid(fcinfo);

      /* restore old trigger data */
      trigger->tgargs = tgargs_old;
      trigger->tgnargs--;

      pfree(tgargs[1]);
      pfree(tgargs);

      return res;
}


Datum
tsa_rewrite_accum(PG_FUNCTION_ARGS)
{
      TSQuery           acc;
      ArrayType  *qa;
      TSQuery           q;
      QTNode         *qex = NULL,
                     *subs = NULL,
                     *acctree = NULL;
      bool        isfind = false;
      Datum    *elemsp;
      int               nelemsp;
      MemoryContext aggcontext;
      MemoryContext oldcontext;

      if (fcinfo->context && IsA(fcinfo->context, AggState))
            aggcontext = ((AggState *) fcinfo->context)->aggcontext;
      else if (fcinfo->context && IsA(fcinfo->context, WindowAggState))
            aggcontext = ((WindowAggState *) fcinfo->context)->wincontext;
      else
      {
            elog(ERROR, "tsa_rewrite_accum called in non-aggregate context");
            aggcontext = NULL;            /* keep compiler quiet */
      }

      if (PG_ARGISNULL(0) || PG_GETARG_POINTER(0) == NULL)
      {
            acc = (TSQuery) MemoryContextAlloc(aggcontext, HDRSIZETQ);
            SET_VARSIZE(acc, HDRSIZETQ);
            acc->size = 0;
      }
      else
            acc = PG_GETARG_TSQUERY(0);

      if (PG_ARGISNULL(1) || PG_GETARG_POINTER(1) == NULL)
            PG_RETURN_TSQUERY(acc);
      else
            qa = PG_GETARG_ARRAYTYPE_P_COPY(1);

      if (ARR_NDIM(qa) != 1)
            elog(ERROR, "array must be one-dimensional, not %d dimensions",
                   ARR_NDIM(qa));
      if (ArrayGetNItems(ARR_NDIM(qa), ARR_DIMS(qa)) != 3)
            elog(ERROR, "array must have three elements");
      if (ARR_ELEMTYPE(qa) != TSQUERYOID)
            elog(ERROR, "array must contain tsquery elements");

      deconstruct_array(qa, TSQUERYOID, -1, false, 'i', &elemsp, NULL, &nelemsp);

      q = DatumGetTSQuery(elemsp[0]);
      if (q->size == 0)
      {
            pfree(elemsp);
            PG_RETURN_POINTER(acc);
      }

      if (!acc->size)
      {
            if (VARSIZE(acc) > HDRSIZETQ)
            {
                  pfree(elemsp);
                  PG_RETURN_POINTER(acc);
            }
            else
                  acctree = QT2QTN(GETQUERY(q), GETOPERAND(q));
      }
      else
            acctree = QT2QTN(GETQUERY(acc), GETOPERAND(acc));

      QTNTernary(acctree);
      QTNSort(acctree);

      q = DatumGetTSQuery(elemsp[1]);
      if (q->size == 0)
      {
            pfree(elemsp);
            PG_RETURN_POINTER(acc);
      }
      qex = QT2QTN(GETQUERY(q), GETOPERAND(q));
      QTNTernary(qex);
      QTNSort(qex);

      q = DatumGetTSQuery(elemsp[2]);
      if (q->size)
            subs = QT2QTN(GETQUERY(q), GETOPERAND(q));

      acctree = findsubquery(acctree, qex, subs, &isfind);

      if (isfind || !acc->size)
      {
            /* pfree( acc ); do not pfree(p), because nodeAgg.c will */
            if (acctree)
            {
                  QTNBinary(acctree);
                  oldcontext = MemoryContextSwitchTo(aggcontext);
                  acc = QTN2QT(acctree);
                  MemoryContextSwitchTo(oldcontext);
            }
            else
            {
                  acc = (TSQuery) MemoryContextAlloc(aggcontext, HDRSIZETQ);
                  SET_VARSIZE(acc, HDRSIZETQ);
                  acc->size = 0;
            }
      }

      pfree(elemsp);
      QTNFree(qex);
      QTNFree(subs);
      QTNFree(acctree);

      PG_RETURN_TSQUERY(acc);
}

Datum
tsa_rewrite_finish(PG_FUNCTION_ARGS)
{
      TSQuery           acc = PG_GETARG_TSQUERY(0);
      TSQuery           rewrited;

      if (acc == NULL || PG_ARGISNULL(0) || acc->size == 0)
      {
            rewrited = (TSQuery) palloc(HDRSIZETQ);
            SET_VARSIZE(rewrited, HDRSIZETQ);
            rewrited->size = 0;
      }
      else
      {
            rewrited = (TSQuery) palloc(VARSIZE(acc));
            memcpy(rewrited, acc, VARSIZE(acc));
            pfree(acc);
      }

      PG_RETURN_POINTER(rewrited);
}


/*
 * Get Oid of current dictionary
 */
static Oid
GetCurrentDict(void)
{
      if (current_dictionary_oid == InvalidOid)
            ereport(ERROR,
                        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                         errmsg("no current dictionary"),
                         errhint("Execute SELECT set_curdict(...).")));

      return current_dictionary_oid;
}

/*
 * Get Oid of current parser
 *
 * Here, it seems reasonable to select the "default" parser if none has been
 * set.
 */
static Oid
GetCurrentParser(void)
{
      if (current_parser_oid == InvalidOid)
            current_parser_oid = TSParserGetPrsid(stringToQualifiedNameList("pg_catalog.default"), false);
      return current_parser_oid;
}

Generated by  Doxygen 1.6.0   Back to index