LCOV - differential code coverage report
Current view: top level - contrib/fuzzystrmatch - fuzzystrmatch.c (source / functions) Coverage Total Hit UBC CBC EUB ECB
Current: c70b6db34ffeab48beef1fb4ce61bcad3772b8dd vs 06473f5a344df8c9594ead90a609b86f6724cff8 Lines: 44.8 % 279 125 154 125
Current Date: 2025-09-06 07:49:51 +0900 Functions: 90.0 % 20 18 2 18
Baseline: lcov-20250906-005545-baseline Branches: 25.1 % 227 57 170 57 78 18
Baseline Date: 2025-09-05 08:21:35 +0100 Line coverage date bins:
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
(30,360] days: 100.0 % 1 1 1
(360..) days: 44.6 % 278 124 154 124
Function coverage date bins:
(30,360] days: 100.0 % 1 1 1
(360..) days: 89.5 % 19 17 2 17
Branch coverage date bins:
(360..) days: 17.6 % 323 57 170 57 78 18

 Age         Owner                    Branch data    TLA  Line data    Source code
                                  1                 :                : /*
                                  2                 :                :  * fuzzystrmatch.c
                                  3                 :                :  *
                                  4                 :                :  * Functions for "fuzzy" comparison of strings
                                  5                 :                :  *
                                  6                 :                :  * Joe Conway <mail@joeconway.com>
                                  7                 :                :  *
                                  8                 :                :  * contrib/fuzzystrmatch/fuzzystrmatch.c
                                  9                 :                :  * Copyright (c) 2001-2025, PostgreSQL Global Development Group
                                 10                 :                :  * ALL RIGHTS RESERVED;
                                 11                 :                :  *
                                 12                 :                :  * metaphone()
                                 13                 :                :  * -----------
                                 14                 :                :  * Modified for PostgreSQL by Joe Conway.
                                 15                 :                :  * Based on CPAN's "Text-Metaphone-1.96" by Michael G Schwern <schwern@pobox.com>
                                 16                 :                :  * Code slightly modified for use as PostgreSQL function (palloc, elog, etc).
                                 17                 :                :  * Metaphone was originally created by Lawrence Philips and presented in article
                                 18                 :                :  * in "Computer Language" December 1990 issue.
                                 19                 :                :  *
                                 20                 :                :  * Permission to use, copy, modify, and distribute this software and its
                                 21                 :                :  * documentation for any purpose, without fee, and without a written agreement
                                 22                 :                :  * is hereby granted, provided that the above copyright notice and this
                                 23                 :                :  * paragraph and the following two paragraphs appear in all copies.
                                 24                 :                :  *
                                 25                 :                :  * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY FOR
                                 26                 :                :  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
                                 27                 :                :  * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
                                 28                 :                :  * DOCUMENTATION, EVEN IF THE AUTHOR OR DISTRIBUTORS HAVE BEEN ADVISED OF THE
                                 29                 :                :  * POSSIBILITY OF SUCH DAMAGE.
                                 30                 :                :  *
                                 31                 :                :  * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
                                 32                 :                :  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
                                 33                 :                :  * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
                                 34                 :                :  * ON AN "AS IS" BASIS, AND THE AUTHOR AND DISTRIBUTORS HAS NO OBLIGATIONS TO
                                 35                 :                :  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
                                 36                 :                :  *
                                 37                 :                :  */
                                 38                 :                : 
                                 39                 :                : #include "postgres.h"
                                 40                 :                : 
                                 41                 :                : #include <ctype.h>
                                 42                 :                : 
                                 43                 :                : #include "utils/builtins.h"
                                 44                 :                : #include "utils/varlena.h"
                                 45                 :                : #include "varatt.h"
                                 46                 :                : 
  164 tgl@sss.pgh.pa.us          47                 :CBC           2 : PG_MODULE_MAGIC_EXT(
                                 48                 :                :                     .name = "fuzzystrmatch",
                                 49                 :                :                     .version = PG_VERSION
                                 50                 :                : );
                                 51                 :                : 
                                 52                 :                : /*
                                 53                 :                :  * Soundex
                                 54                 :                :  */
                                 55                 :                : static void _soundex(const char *instr, char *outstr);
                                 56                 :                : 
                                 57                 :                : #define SOUNDEX_LEN 4
                                 58                 :                : 
                                 59                 :                : /*                                  ABCDEFGHIJKLMNOPQRSTUVWXYZ */
                                 60                 :                : static const char *const soundex_table = "01230120022455012623010202";
                                 61                 :                : 
                                 62                 :                : static char
 5996                            63                 :            127 : soundex_code(char letter)
                                 64                 :                : {
                                 65                 :            127 :     letter = toupper((unsigned char) letter);
                                 66                 :                :     /* Defend against non-ASCII letters */
                                 67   [ +  +  +  - ]:            127 :     if (letter >= 'A' && letter <= 'Z')
                                 68                 :            126 :         return soundex_table[letter - 'A'];
                                 69                 :              1 :     return letter;
                                 70                 :                : }
                                 71                 :                : 
                                 72                 :                : /*
                                 73                 :                :  * Metaphone
                                 74                 :                :  */
                                 75                 :                : #define MAX_METAPHONE_STRLEN        255
                                 76                 :                : 
                                 77                 :                : /*
                                 78                 :                :  * Original code by Michael G Schwern starts here.
                                 79                 :                :  * Code slightly modified for use as PostgreSQL function.
                                 80                 :                :  */
                                 81                 :                : 
                                 82                 :                : 
                                 83                 :                : /**************************************************************************
                                 84                 :                :     metaphone -- Breaks english phrases down into their phonemes.
                                 85                 :                : 
                                 86                 :                :     Input
                                 87                 :                :         word            --  An english word to be phonized
                                 88                 :                :         max_phonemes    --  How many phonemes to calculate.  If 0, then it
                                 89                 :                :                             will phonize the entire phrase.
                                 90                 :                :         phoned_word     --  The final phonized word.  (We'll allocate the
                                 91                 :                :                             memory.)
                                 92                 :                :     Output
                                 93                 :                :         error   --  A simple error flag, returns true or false
                                 94                 :                : 
                                 95                 :                :     NOTES:  ALL non-alpha characters are ignored, this includes whitespace,
                                 96                 :                :     although non-alpha characters will break up phonemes.
                                 97                 :                : ****************************************************************************/
                                 98                 :                : 
                                 99                 :                : 
                                100                 :                : /*  I add modifications to the traditional metaphone algorithm that you
                                101                 :                :     might find in books.  Define this if you want metaphone to behave
                                102                 :                :     traditionally */
                                103                 :                : #undef USE_TRADITIONAL_METAPHONE
                                104                 :                : 
                                105                 :                : /* Special encodings */
                                106                 :                : #define  SH     'X'
                                107                 :                : #define  TH     '0'
                                108                 :                : 
                                109                 :                : static char Lookahead(char *word, int how_far);
                                110                 :                : static void _metaphone(char *word, int max_phonemes, char **phoned_word);
                                111                 :                : 
                                112                 :                : /* Metachar.h ... little bits about characters for metaphone */
                                113                 :                : 
                                114                 :                : 
                                115                 :                : /*-- Character encoding array & accessing macros --*/
                                116                 :                : /* Stolen directly out of the book... */
                                117                 :                : static const char _codes[26] = {
                                118                 :                :     1, 16, 4, 16, 9, 2, 4, 16, 9, 2, 0, 2, 2, 2, 1, 4, 0, 2, 4, 4, 1, 0, 0, 0, 8, 0
                                119                 :                : /*  a  b c  d e f g  h i j k l m n o p q r s t u v w x y z */
                                120                 :                : };
                                121                 :                : 
                                122                 :                : static int
                                123                 :              1 : getcode(char c)
                                124                 :                : {
                                125         [ +  - ]:              1 :     if (isalpha((unsigned char) c))
                                126                 :                :     {
                                127                 :              1 :         c = toupper((unsigned char) c);
                                128                 :                :         /* Defend against non-ASCII letters */
                                129   [ +  -  +  - ]:              1 :         if (c >= 'A' && c <= 'Z')
                                130                 :              1 :             return _codes[c - 'A'];
                                131                 :                :     }
 5996 tgl@sss.pgh.pa.us         132                 :UBC           0 :     return 0;
                                133                 :                : }
                                134                 :                : 
                                135                 :                : #define isvowel(c)  (getcode(c) & 1)    /* AEIOU */
                                136                 :                : 
                                137                 :                : /* These letters are passed through unchanged */
                                138                 :                : #define NOCHANGE(c) (getcode(c) & 2)    /* FJMNR */
                                139                 :                : 
                                140                 :                : /* These form diphthongs when preceding H */
                                141                 :                : #define AFFECTH(c)  (getcode(c) & 4)    /* CGPST */
                                142                 :                : 
                                143                 :                : /* These make C and G soft */
                                144                 :                : #define MAKESOFT(c) (getcode(c) & 8)    /* EIY */
                                145                 :                : 
                                146                 :                : /* These prevent GH from becoming F */
                                147                 :                : #define NOGHTOF(c)  (getcode(c) & 16)   /* BDH */
                                148                 :                : 
 6365 tgl@sss.pgh.pa.us         149                 :CBC           2 : PG_FUNCTION_INFO_V1(levenshtein_with_costs);
                                150                 :                : Datum
                                151                 :              1 : levenshtein_with_costs(PG_FUNCTION_ARGS)
                                152                 :                : {
 5518 rhaas@postgresql.org      153                 :              1 :     text       *src = PG_GETARG_TEXT_PP(0);
                                154                 :              1 :     text       *dst = PG_GETARG_TEXT_PP(1);
 5931 bruce@momjian.us          155                 :              1 :     int         ins_c = PG_GETARG_INT32(2);
                                156                 :              1 :     int         del_c = PG_GETARG_INT32(3);
                                157                 :              1 :     int         sub_c = PG_GETARG_INT32(4);
                                158                 :                :     const char *s_data;
                                159                 :                :     const char *t_data;
                                160                 :                :     int         s_bytes,
                                161                 :                :                 t_bytes;
                                162                 :                : 
                                163                 :                :     /* Extract a pointer to the actual character data */
 3950 rhaas@postgresql.org      164         [ -  + ]:              1 :     s_data = VARDATA_ANY(src);
                                165         [ -  + ]:              1 :     t_data = VARDATA_ANY(dst);
                                166                 :                :     /* Determine length of each string in bytes */
                                167   [ -  +  -  -  :              1 :     s_bytes = VARSIZE_ANY_EXHDR(src);
                                     -  -  -  -  -  
                                                 + ]
                                168   [ -  +  -  -  :              1 :     t_bytes = VARSIZE_ANY_EXHDR(dst);
                                     -  -  -  -  -  
                                                 + ]
                                169                 :                : 
 3515 tgl@sss.pgh.pa.us         170                 :              1 :     PG_RETURN_INT32(varstr_levenshtein(s_data, s_bytes, t_data, t_bytes,
                                171                 :                :                                        ins_c, del_c, sub_c, false));
                                172                 :                : }
                                173                 :                : 
                                174                 :                : 
 6365                           175                 :              2 : PG_FUNCTION_INFO_V1(levenshtein);
                                176                 :                : Datum
                                177                 :              1 : levenshtein(PG_FUNCTION_ARGS)
                                178                 :                : {
 5518 rhaas@postgresql.org      179                 :              1 :     text       *src = PG_GETARG_TEXT_PP(0);
                                180                 :              1 :     text       *dst = PG_GETARG_TEXT_PP(1);
                                181                 :                :     const char *s_data;
                                182                 :                :     const char *t_data;
                                183                 :                :     int         s_bytes,
                                184                 :                :                 t_bytes;
                                185                 :                : 
                                186                 :                :     /* Extract a pointer to the actual character data */
 3950                           187         [ -  + ]:              1 :     s_data = VARDATA_ANY(src);
                                188         [ -  + ]:              1 :     t_data = VARDATA_ANY(dst);
                                189                 :                :     /* Determine length of each string in bytes */
                                190   [ -  +  -  -  :              1 :     s_bytes = VARSIZE_ANY_EXHDR(src);
                                     -  -  -  -  -  
                                                 + ]
                                191   [ -  +  -  -  :              1 :     t_bytes = VARSIZE_ANY_EXHDR(dst);
                                     -  -  -  -  -  
                                                 + ]
                                192                 :                : 
 3515 tgl@sss.pgh.pa.us         193                 :              1 :     PG_RETURN_INT32(varstr_levenshtein(s_data, s_bytes, t_data, t_bytes,
                                194                 :                :                                        1, 1, 1, false));
                                195                 :                : }
                                196                 :                : 
                                197                 :                : 
 5436 rhaas@postgresql.org      198                 :              1 : PG_FUNCTION_INFO_V1(levenshtein_less_equal_with_costs);
                                199                 :                : Datum
 5436 rhaas@postgresql.org      200                 :UBC           0 : levenshtein_less_equal_with_costs(PG_FUNCTION_ARGS)
                                201                 :                : {
                                202                 :              0 :     text       *src = PG_GETARG_TEXT_PP(0);
                                203                 :              0 :     text       *dst = PG_GETARG_TEXT_PP(1);
                                204                 :              0 :     int         ins_c = PG_GETARG_INT32(2);
                                205                 :              0 :     int         del_c = PG_GETARG_INT32(3);
                                206                 :              0 :     int         sub_c = PG_GETARG_INT32(4);
                                207                 :              0 :     int         max_d = PG_GETARG_INT32(5);
                                208                 :                :     const char *s_data;
                                209                 :                :     const char *t_data;
                                210                 :                :     int         s_bytes,
                                211                 :                :                 t_bytes;
                                212                 :                : 
                                213                 :                :     /* Extract a pointer to the actual character data */
 3950                           214         [ #  # ]:              0 :     s_data = VARDATA_ANY(src);
                                215         [ #  # ]:              0 :     t_data = VARDATA_ANY(dst);
                                216                 :                :     /* Determine length of each string in bytes */
                                217   [ #  #  #  #  :              0 :     s_bytes = VARSIZE_ANY_EXHDR(src);
                                     #  #  #  #  #  
                                                 # ]
                                218   [ #  #  #  #  :              0 :     t_bytes = VARSIZE_ANY_EXHDR(dst);
                                     #  #  #  #  #  
                                                 # ]
                                219                 :                : 
 3515 tgl@sss.pgh.pa.us         220                 :              0 :     PG_RETURN_INT32(varstr_levenshtein_less_equal(s_data, s_bytes,
                                221                 :                :                                                   t_data, t_bytes,
                                222                 :                :                                                   ins_c, del_c, sub_c,
                                223                 :                :                                                   max_d, false));
                                224                 :                : }
                                225                 :                : 
                                226                 :                : 
 5436 rhaas@postgresql.org      227                 :CBC           2 : PG_FUNCTION_INFO_V1(levenshtein_less_equal);
                                228                 :                : Datum
                                229                 :              2 : levenshtein_less_equal(PG_FUNCTION_ARGS)
                                230                 :                : {
                                231                 :              2 :     text       *src = PG_GETARG_TEXT_PP(0);
                                232                 :              2 :     text       *dst = PG_GETARG_TEXT_PP(1);
                                233                 :              2 :     int         max_d = PG_GETARG_INT32(2);
                                234                 :                :     const char *s_data;
                                235                 :                :     const char *t_data;
                                236                 :                :     int         s_bytes,
                                237                 :                :                 t_bytes;
                                238                 :                : 
                                239                 :                :     /* Extract a pointer to the actual character data */
 3950                           240         [ -  + ]:              2 :     s_data = VARDATA_ANY(src);
                                241         [ -  + ]:              2 :     t_data = VARDATA_ANY(dst);
                                242                 :                :     /* Determine length of each string in bytes */
                                243   [ -  +  -  -  :              2 :     s_bytes = VARSIZE_ANY_EXHDR(src);
                                     -  -  -  -  -  
                                                 + ]
                                244   [ -  +  -  -  :              2 :     t_bytes = VARSIZE_ANY_EXHDR(dst);
                                     -  -  -  -  -  
                                                 + ]
                                245                 :                : 
 3515 tgl@sss.pgh.pa.us         246                 :              2 :     PG_RETURN_INT32(varstr_levenshtein_less_equal(s_data, s_bytes,
                                247                 :                :                                                   t_data, t_bytes,
                                248                 :                :                                                   1, 1, 1,
                                249                 :                :                                                   max_d, false));
                                250                 :                : }
                                251                 :                : 
                                252                 :                : 
                                253                 :                : /*
                                254                 :                :  * Calculates the metaphone of an input string.
                                255                 :                :  * Returns number of characters requested
                                256                 :                :  * (suggested value is 4)
                                257                 :                :  */
 8796 bruce@momjian.us          258                 :              2 : PG_FUNCTION_INFO_V1(metaphone);
                                259                 :                : Datum
                                260                 :              1 : metaphone(PG_FUNCTION_ARGS)
                                261                 :                : {
 6374 tgl@sss.pgh.pa.us         262                 :              1 :     char       *str_i = TextDatumGetCString(PG_GETARG_DATUM(0));
                                263                 :              1 :     size_t      str_i_len = strlen(str_i);
                                264                 :                :     int         reqlen;
                                265                 :                :     char       *metaph;
                                266                 :                : 
                                267                 :                :     /* return an empty string if we receive one */
 7737 mail@joeconway.com        268         [ -  + ]:              1 :     if (!(str_i_len > 0))
 6374 tgl@sss.pgh.pa.us         269                 :UBC           0 :         PG_RETURN_TEXT_P(cstring_to_text(""));
                                270                 :                : 
 8796 bruce@momjian.us          271         [ -  + ]:CBC           1 :     if (str_i_len > MAX_METAPHONE_STRLEN)
 8080 tgl@sss.pgh.pa.us         272         [ #  # ]:UBC           0 :         ereport(ERROR,
                                273                 :                :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                274                 :                :                  errmsg("argument exceeds the maximum length of %d bytes",
                                275                 :                :                         MAX_METAPHONE_STRLEN)));
                                276                 :                : 
 8796 bruce@momjian.us          277                 :CBC           1 :     reqlen = PG_GETARG_INT32(1);
                                278         [ -  + ]:              1 :     if (reqlen > MAX_METAPHONE_STRLEN)
 8080 tgl@sss.pgh.pa.us         279         [ #  # ]:UBC           0 :         ereport(ERROR,
                                280                 :                :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                281                 :                :                  errmsg("output exceeds the maximum length of %d bytes",
                                282                 :                :                         MAX_METAPHONE_STRLEN)));
                                283                 :                : 
 8796 bruce@momjian.us          284         [ -  + ]:CBC           1 :     if (!(reqlen > 0))
 8080 tgl@sss.pgh.pa.us         285         [ #  # ]:UBC           0 :         ereport(ERROR,
                                286                 :                :                 (errcode(ERRCODE_ZERO_LENGTH_CHARACTER_STRING),
                                287                 :                :                  errmsg("output cannot be empty string")));
                                288                 :                : 
 2942 peter_e@gmx.net           289                 :CBC           1 :     _metaphone(str_i, reqlen, &metaph);
                                290                 :              1 :     PG_RETURN_TEXT_P(cstring_to_text(metaph));
                                291                 :                : }
                                292                 :                : 
                                293                 :                : 
                                294                 :                : /*
                                295                 :                :  * Original code by Michael G Schwern starts here.
                                296                 :                :  * Code slightly modified for use as PostgreSQL
                                297                 :                :  * function (palloc, etc).
                                298                 :                :  */
                                299                 :                : 
                                300                 :                : /* I suppose I could have been using a character pointer instead of
                                301                 :                :  * accessing the array directly... */
                                302                 :                : 
                                303                 :                : /* Look at the next letter in the word */
                                304                 :                : #define Next_Letter (toupper((unsigned char) word[w_idx+1]))
                                305                 :                : /* Look at the current letter in the word */
                                306                 :                : #define Curr_Letter (toupper((unsigned char) word[w_idx]))
                                307                 :                : /* Go N letters back. */
                                308                 :                : #define Look_Back_Letter(n) \
                                309                 :                :     (w_idx >= (n) ? toupper((unsigned char) word[w_idx-(n)]) : '\0')
                                310                 :                : /* Previous letter.  I dunno, should this return null on failure? */
                                311                 :                : #define Prev_Letter (Look_Back_Letter(1))
                                312                 :                : /* Look two letters down.  It makes sure you don't walk off the string. */
                                313                 :                : #define After_Next_Letter \
                                314                 :                :     (Next_Letter != '\0' ? toupper((unsigned char) word[w_idx+2]) : '\0')
                                315                 :                : #define Look_Ahead_Letter(n) toupper((unsigned char) Lookahead(word+w_idx, n))
                                316                 :                : 
                                317                 :                : 
                                318                 :                : /* Allows us to safely look ahead an arbitrary # of letters */
                                319                 :                : /* I probably could have just used strlen... */
                                320                 :                : static char
 8717 bruce@momjian.us          321                 :UBC           0 : Lookahead(char *word, int how_far)
                                322                 :                : {
                                323                 :              0 :     char        letter_ahead = '\0';    /* null by default */
                                324                 :                :     int         idx;
                                325                 :                : 
                                326   [ #  #  #  # ]:              0 :     for (idx = 0; word[idx] != '\0' && idx < how_far; idx++);
                                327                 :                :     /* Edge forward in the string... */
                                328                 :                : 
 7266                           329                 :              0 :     letter_ahead = word[idx];   /* idx will be either == to how_far or at the
                                330                 :                :                                  * end of the string */
 8796                           331                 :              0 :     return letter_ahead;
                                332                 :                : }
                                333                 :                : 
                                334                 :                : 
                                335                 :                : /* phonize one letter */
                                336                 :                : #define Phonize(c)  do {(*phoned_word)[p_idx++] = c;} while (0)
                                337                 :                : /* Slap a null character on the end of the phoned word */
                                338                 :                : #define End_Phoned_Word do {(*phoned_word)[p_idx] = '\0';} while (0)
                                339                 :                : /* How long is the phoned word? */
                                340                 :                : #define Phone_Len   (p_idx)
                                341                 :                : 
                                342                 :                : /* Note is a letter is a 'break' in the word */
                                343                 :                : #define Isbreak(c)  (!isalpha((unsigned char) (c)))
                                344                 :                : 
                                345                 :                : 
                                346                 :                : static void
 5931 bruce@momjian.us          347                 :CBC           1 : _metaphone(char *word,          /* IN */
                                348                 :                :            int max_phonemes,
                                349                 :                :            char **phoned_word)  /* OUT */
                                350                 :                : {
 8717                           351                 :              1 :     int         w_idx = 0;      /* point in the phonization we're at. */
                                352                 :              1 :     int         p_idx = 0;      /* end of the phoned phrase */
                                353                 :                : 
                                354                 :                :     /*-- Parameter checks --*/
                                355                 :                : 
                                356                 :                :     /*
                                357                 :                :      * Shouldn't be necessary, but left these here anyway jec Aug 3, 2001
                                358                 :                :      */
                                359                 :                : 
                                360                 :                :     /* Negative phoneme length is meaningless */
 8796                           361         [ -  + ]:              1 :     if (!(max_phonemes > 0))
                                362                 :                :         /* internal error */
 8796 bruce@momjian.us          363         [ #  # ]:UBC           0 :         elog(ERROR, "metaphone: Requested output length must be > 0");
                                364                 :                : 
                                365                 :                :     /* Empty/null string is meaningless */
 8796 bruce@momjian.us          366   [ +  -  -  + ]:CBC           1 :     if ((word == NULL) || !(strlen(word) > 0))
                                367                 :                :         /* internal error */
 8796 bruce@momjian.us          368         [ #  # ]:UBC           0 :         elog(ERROR, "metaphone: Input string length must be > 0");
                                369                 :                : 
                                370                 :                :     /*-- Allocate memory for our phoned_phrase --*/
 8717 bruce@momjian.us          371         [ -  + ]:CBC           1 :     if (max_phonemes == 0)
                                372                 :                :     {                           /* Assume largest possible */
 2999 tgl@sss.pgh.pa.us         373                 :UBC           0 :         *phoned_word = palloc(sizeof(char) * strlen(word) + 1);
                                374                 :                :     }
                                375                 :                :     else
                                376                 :                :     {
 8796 bruce@momjian.us          377                 :CBC           1 :         *phoned_word = palloc(sizeof(char) * max_phonemes + 1);
                                378                 :                :     }
                                379                 :                : 
                                380                 :                :     /*-- The first phoneme has to be processed specially. --*/
                                381                 :                :     /* Find our first letter */
 8651 tgl@sss.pgh.pa.us         382         [ -  + ]:              1 :     for (; !isalpha((unsigned char) (Curr_Letter)); w_idx++)
                                383                 :                :     {
                                384                 :                :         /* On the off chance we were given nothing but crap... */
 8717 bruce@momjian.us          385         [ #  # ]:UBC           0 :         if (Curr_Letter == '\0')
                                386                 :                :         {
 8713                           387                 :              0 :             End_Phoned_Word;
 2942 peter_e@gmx.net           388                 :              0 :             return;
                                389                 :                :         }
                                390                 :                :     }
                                391                 :                : 
 8717 bruce@momjian.us          392   [ -  +  -  -  :CBC           1 :     switch (Curr_Letter)
                                              -  - ]
                                393                 :                :     {
                                394                 :                :             /* AE becomes E */
 8796 bruce@momjian.us          395                 :UBC           0 :         case 'A':
 8717                           396         [ #  # ]:              0 :             if (Next_Letter == 'E')
                                397                 :                :             {
 8796                           398                 :              0 :                 Phonize('E');
 8717                           399                 :              0 :                 w_idx += 2;
                                400                 :                :             }
                                401                 :                :             /* Remember, preserve vowels at the beginning */
                                402                 :                :             else
                                403                 :                :             {
 8796                           404                 :              0 :                 Phonize('A');
                                405                 :              0 :                 w_idx++;
                                406                 :                :             }
                                407                 :              0 :             break;
                                408                 :                :             /* [GKP]N becomes N */
 8796 bruce@momjian.us          409                 :CBC           1 :         case 'G':
                                410                 :                :         case 'K':
                                411                 :                :         case 'P':
 8717                           412         [ -  + ]:              1 :             if (Next_Letter == 'N')
                                413                 :                :             {
 8796 bruce@momjian.us          414                 :UBC           0 :                 Phonize('N');
 8717                           415                 :              0 :                 w_idx += 2;
                                416                 :                :             }
 8796 bruce@momjian.us          417                 :CBC           1 :             break;
                                418                 :                : 
                                419                 :                :             /*
                                420                 :                :              * WH becomes H, WR becomes R W if followed by a vowel
                                421                 :                :              */
 8796 bruce@momjian.us          422                 :UBC           0 :         case 'W':
 8717                           423         [ #  # ]:              0 :             if (Next_Letter == 'H' ||
                                424         [ #  # ]:              0 :                 Next_Letter == 'R')
                                425                 :                :             {
                                426                 :              0 :                 Phonize(Next_Letter);
                                427                 :              0 :                 w_idx += 2;
                                428                 :                :             }
                                429         [ #  # ]:              0 :             else if (isvowel(Next_Letter))
                                430                 :                :             {
                                431                 :              0 :                 Phonize('W');
                                432                 :              0 :                 w_idx += 2;
                                433                 :                :             }
                                434                 :                :             /* else ignore */
 8796                           435                 :              0 :             break;
                                436                 :                :             /* X becomes S */
                                437                 :              0 :         case 'X':
                                438                 :              0 :             Phonize('S');
                                439                 :              0 :             w_idx++;
                                440                 :              0 :             break;
                                441                 :                :             /* Vowels are kept */
                                442                 :                : 
                                443                 :                :             /*
                                444                 :                :              * We did A already case 'A': case 'a':
                                445                 :                :              */
                                446                 :              0 :         case 'E':
                                447                 :                :         case 'I':
                                448                 :                :         case 'O':
                                449                 :                :         case 'U':
                                450                 :              0 :             Phonize(Curr_Letter);
                                451                 :              0 :             w_idx++;
                                452                 :              0 :             break;
                                453                 :              0 :         default:
                                454                 :                :             /* do nothing */
                                455                 :              0 :             break;
                                456                 :                :     }
                                457                 :                : 
                                458                 :                : 
                                459                 :                : 
                                460                 :                :     /* On to the metaphoning */
 8717 bruce@momjian.us          461   [ +  +  -  + ]:CBC           6 :     for (; Curr_Letter != '\0' &&
                                462         [ +  - ]:              5 :          (max_phonemes == 0 || Phone_Len < max_phonemes);
                                463                 :              5 :          w_idx++)
                                464                 :                :     {
                                465                 :                :         /*
                                466                 :                :          * How many letters to skip because an earlier encoding handled
                                467                 :                :          * multiple letters
                                468                 :                :          */
                                469                 :              5 :         unsigned short int skip_letter = 0;
                                470                 :                : 
                                471                 :                : 
                                472                 :                :         /*
                                473                 :                :          * THOUGHT:  It would be nice if, rather than having things like...
                                474                 :                :          * well, SCI.  For SCI you encode the S, then have to remember to skip
                                475                 :                :          * the C.  So the phonome SCI invades both S and C.  It would be
                                476                 :                :          * better, IMHO, to skip the C from the S part of the encoding. Hell,
                                477                 :                :          * I'm trying it.
                                478                 :                :          */
                                479                 :                : 
                                480                 :                :         /* Ignore non-alphas */
 8651 tgl@sss.pgh.pa.us         481         [ -  + ]:              5 :         if (!isalpha((unsigned char) (Curr_Letter)))
 8796 bruce@momjian.us          482                 :UBC           0 :             continue;
                                483                 :                : 
                                484                 :                :         /* Drop duplicates, except CC */
 8717 bruce@momjian.us          485   [ +  +  -  + ]:CBC           5 :         if (Curr_Letter == Prev_Letter &&
 8717 bruce@momjian.us          486         [ #  # ]:UBC           0 :             Curr_Letter != 'C')
 8796                           487                 :              0 :             continue;
                                488                 :                : 
 8717 bruce@momjian.us          489   [ +  -  -  +  :CBC           5 :         switch (Curr_Letter)
                                     -  -  -  -  -  
                                     -  -  -  -  -  
                                           -  +  + ]
                                490                 :                :         {
                                491                 :                :                 /* B -> B unless in MB */
 8796                           492                 :              1 :             case 'B':
 8717                           493   [ +  -  -  + ]:              1 :                 if (Prev_Letter != 'M')
 8796 bruce@momjian.us          494                 :UBC           0 :                     Phonize('B');
 8796 bruce@momjian.us          495                 :CBC           1 :                 break;
                                496                 :                : 
                                497                 :                :                 /*
                                498                 :                :                  * 'sh' if -CIA- or -CH, but not SCH, except SCHW. (SCHW is
                                499                 :                :                  * handled in S) S if -CI-, -CE- or -CY- dropped if -SCI-,
                                500                 :                :                  * SCE-, -SCY- (handed in S) else K
                                501                 :                :                  */
 8796 bruce@momjian.us          502                 :UBC           0 :             case 'C':
 8717                           503         [ #  # ]:              0 :                 if (MAKESOFT(Next_Letter))
                                504                 :                :                 {               /* C[IEY] */
                                505   [ #  #  #  # ]:              0 :                     if (After_Next_Letter == 'A' &&
                                506         [ #  # ]:              0 :                         Next_Letter == 'I')
                                507                 :                :                     {           /* CIA */
 8796                           508                 :              0 :                         Phonize(SH);
                                509                 :                :                     }
                                510                 :                :                     /* SC[IEY] */
 8717                           511   [ #  #  #  # ]:              0 :                     else if (Prev_Letter == 'S')
                                512                 :                :                     {
                                513                 :                :                         /* Dropped */
                                514                 :                :                     }
                                515                 :                :                     else
                                516                 :              0 :                         Phonize('S');
                                517                 :                :                 }
                                518         [ #  # ]:              0 :                 else if (Next_Letter == 'H')
                                519                 :                :                 {
                                520                 :                : #ifndef USE_TRADITIONAL_METAPHONE
                                521   [ #  #  #  #  :              0 :                     if (After_Next_Letter == 'R' ||
                                              #  # ]
                                522         [ #  # ]:              0 :                         Prev_Letter == 'S')
                                523                 :                :                     {           /* Christ, School */
 8796                           524                 :              0 :                         Phonize('K');
                                525                 :                :                     }
                                526                 :                :                     else
                                527                 :              0 :                         Phonize(SH);
                                528                 :                : #else
                                529                 :                :                     Phonize(SH);
                                530                 :                : #endif
                                531                 :              0 :                     skip_letter++;
                                532                 :                :                 }
                                533                 :                :                 else
                                534                 :              0 :                     Phonize('K');
                                535                 :              0 :                 break;
                                536                 :                : 
                                537                 :                :                 /*
                                538                 :                :                  * J if in -DGE-, -DGI- or -DGY- else T
                                539                 :                :                  */
                                540                 :              0 :             case 'D':
 8717                           541         [ #  # ]:              0 :                 if (Next_Letter == 'G' &&
                                542   [ #  #  #  # ]:              0 :                     MAKESOFT(After_Next_Letter))
                                543                 :                :                 {
 8796                           544                 :              0 :                     Phonize('J');
                                545                 :              0 :                     skip_letter++;
                                546                 :                :                 }
                                547                 :                :                 else
                                548                 :              0 :                     Phonize('T');
                                549                 :              0 :                 break;
                                550                 :                : 
                                551                 :                :                 /*
                                552                 :                :                  * F if in -GH and not B--GH, D--GH, -H--GH, -H---GH else
                                553                 :                :                  * dropped if -GNED, -GN, else dropped if -DGE-, -DGI- or
                                554                 :                :                  * -DGY- (handled in D) else J if in -GE-, -GI, -GY and not GG
                                555                 :                :                  * else K
                                556                 :                :                  */
 8796 bruce@momjian.us          557                 :CBC           1 :             case 'G':
 8717                           558         [ -  + ]:              1 :                 if (Next_Letter == 'H')
                                559                 :                :                 {
 8717 bruce@momjian.us          560   [ #  #  #  #  :UBC           0 :                     if (!(NOGHTOF(Look_Back_Letter(3)) ||
                                              #  # ]
                                561         [ #  # ]:              0 :                           Look_Back_Letter(4) == 'H'))
                                562                 :                :                     {
 8796                           563                 :              0 :                         Phonize('F');
                                564                 :              0 :                         skip_letter++;
                                565                 :                :                     }
                                566                 :                :                     else
                                567                 :                :                     {
                                568                 :                :                         /* silent */
                                569                 :                :                     }
                                570                 :                :                 }
 8717 bruce@momjian.us          571         [ -  + ]:CBC           1 :                 else if (Next_Letter == 'N')
                                572                 :                :                 {
 8717 bruce@momjian.us          573   [ #  #  #  # ]:UBC           0 :                     if (Isbreak(After_Next_Letter) ||
                                574   [ #  #  #  # ]:              0 :                         (After_Next_Letter == 'E' &&
                                575         [ #  # ]:              0 :                          Look_Ahead_Letter(3) == 'D'))
                                576                 :                :                     {
                                577                 :                :                         /* dropped */
                                578                 :                :                     }
                                579                 :                :                     else
 8796                           580                 :              0 :                         Phonize('K');
                                581                 :                :                 }
 8717 bruce@momjian.us          582   [ -  +  -  - ]:CBC           1 :                 else if (MAKESOFT(Next_Letter) &&
 8717 bruce@momjian.us          583         [ #  # ]:UBC           0 :                          Prev_Letter != 'G')
 8796                           584                 :              0 :                     Phonize('J');
                                585                 :                :                 else
 8796 bruce@momjian.us          586                 :CBC           1 :                     Phonize('K');
                                587                 :              1 :                 break;
                                588                 :                :                 /* H if before a vowel and not after C,G,P,S,T */
 8796 bruce@momjian.us          589                 :UBC           0 :             case 'H':
 8717                           590         [ #  # ]:              0 :                 if (isvowel(Next_Letter) &&
                                591   [ #  #  #  # ]:              0 :                     !AFFECTH(Prev_Letter))
 8796                           592                 :              0 :                     Phonize('H');
                                593                 :              0 :                 break;
                                594                 :                : 
                                595                 :                :                 /*
                                596                 :                :                  * dropped if after C else K
                                597                 :                :                  */
                                598                 :              0 :             case 'K':
 8717                           599   [ #  #  #  # ]:              0 :                 if (Prev_Letter != 'C')
 8796                           600                 :              0 :                     Phonize('K');
                                601                 :              0 :                 break;
                                602                 :                : 
                                603                 :                :                 /*
                                604                 :                :                  * F if before H else P
                                605                 :                :                  */
                                606                 :              0 :             case 'P':
 8717                           607         [ #  # ]:              0 :                 if (Next_Letter == 'H')
 8796                           608                 :              0 :                     Phonize('F');
                                609                 :                :                 else
                                610                 :              0 :                     Phonize('P');
                                611                 :              0 :                 break;
                                612                 :                : 
                                613                 :                :                 /*
                                614                 :                :                  * K
                                615                 :                :                  */
                                616                 :              0 :             case 'Q':
                                617                 :              0 :                 Phonize('K');
                                618                 :              0 :                 break;
                                619                 :                : 
                                620                 :                :                 /*
                                621                 :                :                  * 'sh' in -SH-, -SIO- or -SIA- or -SCHW- else S
                                622                 :                :                  */
                                623                 :              0 :             case 'S':
 8717                           624         [ #  # ]:              0 :                 if (Next_Letter == 'I' &&
                                625   [ #  #  #  # ]:              0 :                     (After_Next_Letter == 'O' ||
                                626   [ #  #  #  # ]:              0 :                      After_Next_Letter == 'A'))
 8796                           627                 :              0 :                     Phonize(SH);
 8717                           628         [ #  # ]:              0 :                 else if (Next_Letter == 'H')
                                629                 :                :                 {
 8796                           630                 :              0 :                     Phonize(SH);
                                631                 :              0 :                     skip_letter++;
                                632                 :                :                 }
                                633                 :                : #ifndef USE_TRADITIONAL_METAPHONE
 8717                           634         [ #  # ]:              0 :                 else if (Next_Letter == 'C' &&
                                635         [ #  # ]:              0 :                          Look_Ahead_Letter(2) == 'H' &&
                                636         [ #  # ]:              0 :                          Look_Ahead_Letter(3) == 'W')
                                637                 :                :                 {
 8796                           638                 :              0 :                     Phonize(SH);
                                639                 :              0 :                     skip_letter += 2;
                                640                 :                :                 }
                                641                 :                : #endif
                                642                 :                :                 else
                                643                 :              0 :                     Phonize('S');
                                644                 :              0 :                 break;
                                645                 :                : 
                                646                 :                :                 /*
                                647                 :                :                  * 'sh' in -TIA- or -TIO- else 'th' before H else T
                                648                 :                :                  */
                                649                 :              0 :             case 'T':
 8717                           650         [ #  # ]:              0 :                 if (Next_Letter == 'I' &&
                                651   [ #  #  #  # ]:              0 :                     (After_Next_Letter == 'O' ||
                                652   [ #  #  #  # ]:              0 :                      After_Next_Letter == 'A'))
 8796                           653                 :              0 :                     Phonize(SH);
 8717                           654         [ #  # ]:              0 :                 else if (Next_Letter == 'H')
                                655                 :                :                 {
 8796                           656                 :              0 :                     Phonize(TH);
                                657                 :              0 :                     skip_letter++;
                                658                 :                :                 }
                                659                 :                :                 else
                                660                 :              0 :                     Phonize('T');
                                661                 :              0 :                 break;
                                662                 :                :                 /* F */
                                663                 :              0 :             case 'V':
                                664                 :              0 :                 Phonize('F');
                                665                 :              0 :                 break;
                                666                 :                :                 /* W before a vowel, else dropped */
                                667                 :              0 :             case 'W':
 8717                           668         [ #  # ]:              0 :                 if (isvowel(Next_Letter))
 8796                           669                 :              0 :                     Phonize('W');
                                670                 :              0 :                 break;
                                671                 :                :                 /* KS */
                                672                 :              0 :             case 'X':
                                673                 :              0 :                 Phonize('K');
 8110                           674   [ #  #  #  # ]:              0 :                 if (max_phonemes == 0 || Phone_Len < max_phonemes)
                                675                 :              0 :                     Phonize('S');
 8796                           676                 :              0 :                 break;
                                677                 :                :                 /* Y if followed by a vowel */
                                678                 :              0 :             case 'Y':
 8717                           679         [ #  # ]:              0 :                 if (isvowel(Next_Letter))
 8796                           680                 :              0 :                     Phonize('Y');
                                681                 :              0 :                 break;
                                682                 :                :                 /* S */
                                683                 :              0 :             case 'Z':
                                684                 :              0 :                 Phonize('S');
                                685                 :              0 :                 break;
                                686                 :                :                 /* No transformation */
 8796 bruce@momjian.us          687                 :CBC           1 :             case 'F':
                                688                 :                :             case 'J':
                                689                 :                :             case 'L':
                                690                 :                :             case 'M':
                                691                 :                :             case 'N':
                                692                 :                :             case 'R':
                                693                 :              1 :                 Phonize(Curr_Letter);
                                694                 :              1 :                 break;
                                695                 :              2 :             default:
                                696                 :                :                 /* nothing */
                                697                 :              2 :                 break;
                                698                 :                :         }                       /* END SWITCH */
                                699                 :                : 
                                700                 :              5 :         w_idx += skip_letter;
                                701                 :                :     }                           /* END FOR */
                                702                 :                : 
                                703                 :              1 :     End_Phoned_Word;
                                704                 :                : }                               /* END metaphone */
                                705                 :                : 
                                706                 :                : 
                                707                 :                : /*
                                708                 :                :  * SQL function: soundex(text) returns text
                                709                 :                :  */
                                710                 :              3 : PG_FUNCTION_INFO_V1(soundex);
                                711                 :                : 
                                712                 :                : Datum
                                713                 :              8 : soundex(PG_FUNCTION_ARGS)
                                714                 :                : {
                                715                 :                :     char        outstr[SOUNDEX_LEN + 1];
                                716                 :                :     char       *arg;
                                717                 :                : 
 3100 noah@leadboat.com         718                 :              8 :     arg = text_to_cstring(PG_GETARG_TEXT_PP(0));
                                719                 :                : 
 8796 bruce@momjian.us          720                 :              8 :     _soundex(arg, outstr);
                                721                 :                : 
 6374 tgl@sss.pgh.pa.us         722                 :              8 :     PG_RETURN_TEXT_P(cstring_to_text(outstr));
                                723                 :                : }
                                724                 :                : 
                                725                 :                : static void
 8796 bruce@momjian.us          726                 :             16 : _soundex(const char *instr, char *outstr)
                                727                 :                : {
                                728                 :                :     int         count;
                                729                 :                : 
 1044 peter@eisentraut.org      730         [ -  + ]:             16 :     Assert(instr);
                                731         [ -  + ]:             16 :     Assert(outstr);
                                732                 :                : 
                                733                 :                :     /* Skip leading non-alphabetic characters */
  844 tgl@sss.pgh.pa.us         734   [ +  +  -  + ]:             16 :     while (*instr && !isalpha((unsigned char) *instr))
 8796 bruce@momjian.us          735                 :UBC           0 :         ++instr;
                                736                 :                : 
                                737                 :                :     /* If no string left, return all-zeroes buffer */
  844 tgl@sss.pgh.pa.us         738         [ +  + ]:CBC          16 :     if (!*instr)
                                739                 :                :     {
                                740                 :              3 :         memset(outstr, '\0', SOUNDEX_LEN + 1);
 8796 bruce@momjian.us          741                 :              3 :         return;
                                742                 :                :     }
                                743                 :                : 
                                744                 :                :     /* Take the first letter as is */
                                745                 :             13 :     *outstr++ = (char) toupper((unsigned char) *instr++);
                                746                 :                : 
                                747                 :             13 :     count = 1;
                                748   [ +  +  +  + ]:             60 :     while (*instr && count < SOUNDEX_LEN)
                                749                 :                :     {
                                750   [ +  +  +  + ]:             93 :         if (isalpha((unsigned char) *instr) &&
                                751                 :             46 :             soundex_code(*instr) != soundex_code(*(instr - 1)))
                                752                 :                :         {
  844 tgl@sss.pgh.pa.us         753                 :             35 :             *outstr = soundex_code(*instr);
 8796 bruce@momjian.us          754         [ +  + ]:             35 :             if (*outstr != '0')
                                755                 :                :             {
                                756                 :             23 :                 ++outstr;
                                757                 :             23 :                 ++count;
                                758                 :                :             }
                                759                 :                :         }
                                760                 :             47 :         ++instr;
                                761                 :                :     }
                                762                 :                : 
                                763                 :                :     /* Fill with 0's */
                                764         [ +  + ]:             29 :     while (count < SOUNDEX_LEN)
                                765                 :                :     {
                                766                 :             16 :         *outstr = '0';
                                767                 :             16 :         ++outstr;
                                768                 :             16 :         ++count;
                                769                 :                :     }
                                770                 :                : 
                                771                 :                :     /* And null-terminate */
  844 tgl@sss.pgh.pa.us         772                 :             13 :     *outstr = '\0';
                                773                 :                : }
                                774                 :                : 
 7528 neilc@samurai.com         775                 :              2 : PG_FUNCTION_INFO_V1(difference);
                                776                 :                : 
                                777                 :                : Datum
                                778                 :              4 : difference(PG_FUNCTION_ARGS)
                                779                 :                : {
                                780                 :                :     char        sndx1[SOUNDEX_LEN + 1],
                                781                 :                :                 sndx2[SOUNDEX_LEN + 1];
                                782                 :                :     int         i,
                                783                 :                :                 result;
                                784                 :                : 
 3100 noah@leadboat.com         785                 :              4 :     _soundex(text_to_cstring(PG_GETARG_TEXT_PP(0)), sndx1);
                                786                 :              4 :     _soundex(text_to_cstring(PG_GETARG_TEXT_PP(1)), sndx2);
                                787                 :                : 
 7528 neilc@samurai.com         788                 :              4 :     result = 0;
 7266 bruce@momjian.us          789         [ +  + ]:             20 :     for (i = 0; i < SOUNDEX_LEN; i++)
                                790                 :                :     {
 7528 neilc@samurai.com         791         [ +  + ]:             16 :         if (sndx1[i] == sndx2[i])
                                792                 :             10 :             result++;
                                793                 :                :     }
                                794                 :                : 
                                795                 :              4 :     PG_RETURN_INT32(result);
                                796                 :                : }
        

Generated by: LCOV version 2.4-beta