Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  unaccent.c   Sprache: unbekannt

 
/*-------------------------------------------------------------------------
 *
 * unaccent.c
 *   Text search unaccent dictionary
 *
 * Copyright (c) 2009-2025, PostgreSQL Global Development Group
 *
 * IDENTIFICATION
 *   contrib/unaccent/unaccent.c
 *
 *-------------------------------------------------------------------------
 */


#include "postgres.h"

#include "catalog/pg_ts_dict.h"
#include "commands/defrem.h"
#include "lib/stringinfo.h"
#include "tsearch/ts_cache.h"
#include "tsearch/ts_locale.h"
#include "tsearch/ts_public.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"

PG_MODULE_MAGIC_EXT(
     .name = "unaccent",
     .version = PG_VERSION
);

/*
 * An unaccent dictionary uses a trie to find a string to replace.  Each node
 * of the trie is an array of 256 TrieChar structs; the N-th element of the
 * array corresponds to next byte value N.  That element can contain both a
 * replacement string (to be used if the source string ends with this byte)
 * and a link to another trie node (to be followed if there are more bytes).
 *
 * Note that the trie search logic pays no attention to multibyte character
 * boundaries.  This is OK as long as both the data entered into the trie and
 * the data we're trying to look up are validly encoded; no partial-character
 * matches will occur.
 */

typedef struct TrieChar
{
 struct TrieChar *nextChar;
 char    *replaceTo;
 int   replacelen;
} TrieChar;

/*
 * placeChar - put str into trie's structure, byte by byte.
 *
 * If node is NULL, we need to make a new node, which will be returned;
 * otherwise the return value is the same as node.
 */

static TrieChar *
placeChar(TrieChar *node, const unsigned char *str, int lenstr,
    const char *replaceTo, int replacelen)
{
 TrieChar   *curnode;

 if (!node)
  node = (TrieChar *) palloc0(sizeof(TrieChar) * 256);

 Assert(lenstr > 0);   /* else str[0] doesn't exist */

 curnode = node + *str;

 if (lenstr <= 1)
 {
  if (curnode->replaceTo)
   ereport(WARNING,
     (errcode(ERRCODE_CONFIG_FILE_ERROR),
      errmsg("duplicate source strings, first one will be used")));
  else
  {
   curnode->replacelen = replacelen;
   curnode->replaceTo = (char *) palloc(replacelen);
   memcpy(curnode->replaceTo, replaceTo, replacelen);
  }
 }
 else
 {
  curnode->nextChar = placeChar(curnode->nextChar, str + 1, lenstr - 1,
           replaceTo, replacelen);
 }

 return node;
}

/*
 * initTrie  - create trie from file.
 *
 * Function converts UTF8-encoded file into current encoding.
 */

static TrieChar *
initTrie(const char *filename)
{
 TrieChar   *volatile rootTrie = NULL;
 MemoryContext ccxt = CurrentMemoryContext;
 tsearch_readline_state trst;
 volatile bool skip;

 filename = get_tsearch_config_filename(filename, "rules");
 if (!tsearch_readline_begin(&trst, filename))
  ereport(ERROR,
    (errcode(ERRCODE_CONFIG_FILE_ERROR),
     errmsg("could not open unaccent file \"%s\": %m",
      filename)));

 do
 {
  /*
   * pg_do_encoding_conversion() (called by tsearch_readline()) will
   * emit exception if it finds untranslatable characters in current
   * locale. We just skip such lines, continuing with the next.
 */

  skip = true;

  PG_TRY();
  {
   char    *line;

   while ((line = tsearch_readline(&trst)) != NULL)
   {
    /*----------
     * The format of each line must be "src" or "src trg", where
     * src and trg are sequences of one or more non-whitespace
     * characters, separated by whitespace.  Whitespace at start
     * or end of line is ignored.  If trg is omitted, an empty
     * string is used as the replacement.  trg can be optionally
     * quoted, in which case whitespaces are included in it.
     *
     * We use a simple state machine, with states
     * 0 initial (before src)
     * 1 in src
     * 2 in whitespace after src
     * 3 in trg (non-quoted)
     * 4 in trg (quoted)
     * 5 in whitespace after trg
     * -1 syntax error detected (two strings)
     * -2 syntax error detected (unfinished quoted string)
     *----------
 */

    int   state;
    char    *ptr;
    char    *src = NULL;
    char    *trg = NULL;
    char    *trgstore = NULL;
    int   ptrlen;
    int   srclen = 0;
    int   trglen = 0;
    int   trgstorelen = 0;
    bool  trgquoted = false;

    state = 0;
    for (ptr = line; *ptr; ptr += ptrlen)
    {
     ptrlen = pg_mblen_cstr(ptr);
     /* ignore whitespace, but end src or trg */
     if (isspace((unsigned char) *ptr))
     {
      if (state == 1)
       state = 2;
      else if (state == 3)
       state = 5;
      /* whitespaces are OK in quoted area */
      if (state != 4)
       continue;
     }
     switch (state)
     {
      case 0:
       /* start of src */
       src = ptr;
       srclen = ptrlen;
       state = 1;
       break;
      case 1:
       /* continue src */
       srclen += ptrlen;
       break;
      case 2:
       /* start of trg */
       if (*ptr == '"')
       {
        trgquoted = true;
        state = 4;
       }
       else
        state = 3;

       trg = ptr;
       trglen = ptrlen;
       break;
      case 3:
       /* continue non-quoted trg */
       trglen += ptrlen;
       break;
      case 4:
       /* continue quoted trg */
       trglen += ptrlen;

       /*
        * If this is a quote, consider it as the end of
        * trg except if the follow-up character is itself
        * a quote.
 */

       if (*ptr == '"')
       {
        if (*(ptr + 1) == '"')
        {
         ptr++;
         trglen += 1;
        }
        else
         state = 5;
       }
       break;
      default:
       /* bogus line format */
       state = -1;
       break;
     }
    }

    if (state == 1 || state == 2)
    {
     /* trg was omitted, so use "" */
     trg = "";
     trglen = 0;
    }

    /* If still in a quoted area, fallback to an error */
    if (state == 4)
     state = -2;

    /* If trg was quoted, remove its quotes and unescape it */
    if (trgquoted && state > 0)
    {
     /* Ignore first and end quotes */
     trgstore = (char *) palloc(sizeof(char) * (trglen - 2));
     trgstorelen = 0;
     for (int i = 1; i < trglen - 1; i++)
     {
      trgstore[trgstorelen] = trg[i];
      trgstorelen++;
      /* skip second double quotes */
      if (trg[i] == '"' && trg[i + 1] == '"')
       i++;
     }
    }
    else
    {
     trgstore = (char *) palloc(sizeof(char) * trglen);
     trgstorelen = trglen;
     memcpy(trgstore, trg, trgstorelen);
    }

    if (state > 0)
     rootTrie = placeChar(rootTrie,
           (unsigned char *) src, srclen,
           trgstore, trgstorelen);
    else if (state == -1)
     ereport(WARNING,
       (errcode(ERRCODE_CONFIG_FILE_ERROR),
        errmsg("invalid syntax: more than two strings in unaccent rule")));
    else if (state == -2)
     ereport(WARNING,
       (errcode(ERRCODE_CONFIG_FILE_ERROR),
        errmsg("invalid syntax: unfinished quoted string in unaccent rule")));

    pfree(trgstore);
    pfree(line);
   }
   skip = false;
  }
  PG_CATCH();
  {
   ErrorData  *errdata;
   MemoryContext ecxt;

   ecxt = MemoryContextSwitchTo(ccxt);
   errdata = CopyErrorData();
   if (errdata->sqlerrcode == ERRCODE_UNTRANSLATABLE_CHARACTER)
   {
    FlushErrorState();
   }
   else
   {
    MemoryContextSwitchTo(ecxt);
    PG_RE_THROW();
   }
  }
  PG_END_TRY();
 }
 while (skip);

 tsearch_readline_end(&trst);

 return rootTrie;
}

/*
 * findReplaceTo - find longest possible match in trie
 *
 * On success, returns pointer to ending subnode, plus length of matched
 * source string in *p_matchlen.  On failure, returns NULL.
 */

static TrieChar *
findReplaceTo(TrieChar *node, const unsigned char *src, int srclen,
     int *p_matchlen)
{
 TrieChar   *result = NULL;
 int   matchlen = 0;

 *p_matchlen = 0;   /* prevent uninitialized-variable warnings */

 while (node && matchlen < srclen)
 {
  node = node + src[matchlen];
  matchlen++;

  if (node->replaceTo)
  {
   result = node;
   *p_matchlen = matchlen;
  }

  node = node->nextChar;
 }

 return result;
}

PG_FUNCTION_INFO_V1(unaccent_init);
Datum
unaccent_init(PG_FUNCTION_ARGS)
{
 List    *dictoptions = (List *) PG_GETARG_POINTER(0);
 TrieChar   *rootTrie = NULL;
 bool  fileloaded = false;
 ListCell   *l;

 foreach(l, dictoptions)
 {
  DefElem    *defel = (DefElem *) lfirst(l);

  if (strcmp(defel->defname, "rules") == 0)
  {
   if (fileloaded)
    ereport(ERROR,
      (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
       errmsg("multiple Rules parameters")));
   rootTrie = initTrie(defGetString(defel));
   fileloaded = true;
  }
  else
  {
   ereport(ERROR,
     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
      errmsg("unrecognized Unaccent parameter: \"%s\"",
       defel->defname)));
  }
 }

 if (!fileloaded)
 {
  ereport(ERROR,
    (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     errmsg("missing Rules parameter")));
 }

 PG_RETURN_POINTER(rootTrie);
}

PG_FUNCTION_INFO_V1(unaccent_lexize);
Datum
unaccent_lexize(PG_FUNCTION_ARGS)
{
 TrieChar   *rootTrie = (TrieChar *) PG_GETARG_POINTER(0);
 char    *srcchar = (char *) PG_GETARG_POINTER(1);
 int32  len = PG_GETARG_INT32(2);
 char    *srcstart = srcchar;
 const char *srcend = srcstart + len;
 TSLexeme   *res;
 StringInfoData buf;

 /* we allocate storage for the buffer only if needed */
 buf.data = NULL;

 while (len > 0)
 {
  TrieChar   *node;
  int   matchlen;

  node = findReplaceTo(rootTrie, (unsigned char *) srcchar, len,
        &matchlen);
  if (node && node->replaceTo)
  {
   if (buf.data == NULL)
   {
    /* initialize buffer */
    initStringInfo(&buf);
    /* insert any data we already skipped over */
    if (srcchar != srcstart)
     appendBinaryStringInfo(&buf, srcstart, srcchar - srcstart);
   }
   appendBinaryStringInfo(&buf, node->replaceTo, node->replacelen);
  }
  else
  {
   matchlen = pg_mblen_range(srcchar, srcend);
   if (buf.data != NULL)
    appendBinaryStringInfo(&buf, srcchar, matchlen);
  }

  srcchar += matchlen;
  len -= matchlen;
 }

 /* return a result only if we made at least one substitution */
 if (buf.data != NULL)
 {
  res = (TSLexeme *) palloc0(sizeof(TSLexeme) * 2);
  res->lexeme = buf.data;
  res->flags = TSL_FILTER;
 }
 else
  res = NULL;

 PG_RETURN_POINTER(res);
}

/*
 * Function-like wrapper for dictionary
 */

PG_FUNCTION_INFO_V1(unaccent_dict);
Datum
unaccent_dict(PG_FUNCTION_ARGS)
{
 text    *str;
 int   strArg;
 Oid   dictOid;
 TSDictionaryCacheEntry *dict;
 TSLexeme   *res;

 if (PG_NARGS() == 1)
 {
  /*
   * Use the "unaccent" dictionary that is in the same schema that this
   * function is in.
 */

  Oid   procnspid = get_func_namespace(fcinfo->flinfo->fn_oid);
  const char *dictname = "unaccent";

  dictOid = GetSysCacheOid2(TSDICTNAMENSP, Anum_pg_ts_dict_oid,
          PointerGetDatum(dictname),
          ObjectIdGetDatum(procnspid));
  if (!OidIsValid(dictOid))
   ereport(ERROR,
     (errcode(ERRCODE_UNDEFINED_OBJECT),
      errmsg("text search dictionary \"%s.%s\" does not exist",
       get_namespace_name(procnspid), dictname)));
  strArg = 0;
 }
 else
 {
  dictOid = PG_GETARG_OID(0);
  strArg = 1;
 }
 str = PG_GETARG_TEXT_PP(strArg);

 dict = lookup_ts_dictionary_cache(dictOid);

 res = (TSLexeme *) DatumGetPointer(FunctionCall4(&(dict->lexize),
              PointerGetDatum(dict->dictData),
              PointerGetDatum(VARDATA_ANY(str)),
              Int32GetDatum(VARSIZE_ANY_EXHDR(str)),
              PointerGetDatum(NULL)));

 PG_FREE_IF_COPY(str, strArg);

 if (res == NULL)
 {
  PG_RETURN_TEXT_P(PG_GETARG_TEXT_P_COPY(strArg));
 }
 else if (res->lexeme == NULL)
 {
  pfree(res);
  PG_RETURN_TEXT_P(PG_GETARG_TEXT_P_COPY(strArg));
 }
 else
 {
  text    *txt = cstring_to_text(res->lexeme);

  pfree(res->lexeme);
  pfree(res);

  PG_RETURN_TEXT_P(txt);
 }
}

Messung V0.5 in Prozent
C=95 H=90 G=92

[Verzeichnis aufwärts0.21unsichere VerbindungÜbersetzung europäischer Sprachen durch Browser2026-08-04]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=141584
#Domains=752002