Coverage Report

Created: 2026-08-13 07:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgres/src/backend/utils/adt/levenshtein.c
Line
Count
Source
1
/*-------------------------------------------------------------------------
2
 *
3
 * levenshtein.c
4
 *    Levenshtein distance implementation.
5
 *
6
 * Original author:  Joe Conway <mail@joeconway.com>
7
 *
8
 * This file is included by varlena.c twice, to provide matching code for (1)
9
 * Levenshtein distance with custom costings, and (2) Levenshtein distance with
10
 * custom costings and a "max" value above which exact distances are not
11
 * interesting.  Before the inclusion, we rely on the presence of the inline
12
 * functions rest_of_char_same() and levenshtein_result().
13
 *
14
 * Written based on a description of the algorithm by Michael Gilleland found
15
 * at http://www.merriampark.com/ld.htm.  Also looked at levenshtein.c in the
16
 * PHP 4.0.6 distribution for inspiration.  Configurable penalty costs
17
 * extension is introduced by Volkan YAZICI <volkan.yazici@gmail.com.
18
 *
19
 * Copyright (c) 2001-2026, PostgreSQL Global Development Group
20
 *
21
 * IDENTIFICATION
22
 *  src/backend/utils/adt/levenshtein.c
23
 *
24
 *-------------------------------------------------------------------------
25
 */
26
0
#define MAX_LEVENSHTEIN_STRLEN    255
27
28
/*
29
 * Calculates Levenshtein distance metric between supplied strings, which are
30
 * not necessarily null-terminated.
31
 *
32
 * source: source string, of length slen bytes.
33
 * target: target string, of length tlen bytes.
34
 * ins_c, del_c, sub_c: costs to charge for character insertion, deletion,
35
 *    and substitution respectively; (1, 1, 1) costs suffice for common
36
 *    cases, but your mileage may vary.
37
 * max_d: if provided and >= 0, maximum distance we care about; see below.
38
 * trusted: caller is trusted and need not obey MAX_LEVENSHTEIN_STRLEN.
39
 *
40
 * One way to compute Levenshtein distance is to incrementally construct
41
 * an (m+1)x(n+1) matrix where cell (i, j) represents the minimum number
42
 * of operations required to transform the first i characters of s into
43
 * the first j characters of t.  The last column of the final row is the
44
 * answer.
45
 *
46
 * We use that algorithm here with some modification.  In lieu of holding
47
 * the entire array in memory at once, we'll just use two arrays of size
48
 * m+1 for storing accumulated values. At each step one array represents
49
 * the "previous" row and one is the "current" row of the notional large
50
 * array.
51
 *
52
 * If max_d >= 0, we only need to provide an accurate answer when that answer
53
 * is less than or equal to max_d.  From any cell in the matrix, there is
54
 * theoretical "minimum residual distance" from that cell to the last column
55
 * of the final row.  This minimum residual distance is zero when the
56
 * untransformed portions of the strings are of equal length (because we might
57
 * get lucky and find all the remaining characters matching) and is otherwise
58
 * based on the minimum number of insertions or deletions needed to make them
59
 * equal length.  The residual distance grows as we move toward the upper
60
 * right or lower left corners of the matrix.  When the max_d bound is
61
 * usefully tight, we can use this property to avoid computing the entirety
62
 * of each row; instead, we maintain a start_column and stop_column that
63
 * identify the portion of the matrix close to the diagonal which can still
64
 * affect the final answer.
65
 */
66
int
67
#ifdef LEVENSHTEIN_LESS_EQUAL
68
varstr_levenshtein_less_equal(const char *source, int slen,
69
                const char *target, int tlen,
70
                int ins_c, int del_c, int sub_c,
71
                int max_d, bool trusted)
72
#else
73
varstr_levenshtein(const char *source, int slen,
74
           const char *target, int tlen,
75
           int ins_c, int del_c, int sub_c,
76
           bool trusted)
77
#endif
78
0
{
79
0
  int     m,
80
0
        n;
81
0
  int64    *prev;
82
0
  int64    *curr;
83
0
  int      *s_char_len = NULL;
84
0
  int     j;
85
0
  const char *y;
86
0
  const char *send = source + slen;
87
0
  const char *tend = target + tlen;
88
0
  int64   ins_c_64 = ins_c;
89
0
  int64   del_c_64 = del_c;
90
0
  int64   sub_c_64 = sub_c;
91
92
  /*
93
   * For varstr_levenshtein_less_equal, we have real variables called
94
   * start_column and stop_column; otherwise it's just short-hand for 0 and
95
   * m.
96
   */
97
#ifdef LEVENSHTEIN_LESS_EQUAL
98
  int     start_column,
99
        stop_column;
100
101
#undef START_COLUMN
102
#undef STOP_COLUMN
103
0
#define START_COLUMN start_column
104
0
#define STOP_COLUMN stop_column
105
#else
106
#undef START_COLUMN
107
#undef STOP_COLUMN
108
0
#define START_COLUMN 0
109
0
#define STOP_COLUMN m
110
#endif
111
112
  /* Convert string lengths (in bytes) to lengths in characters */
113
0
  m = pg_mbstrlen_with_len(source, slen);
114
0
  n = pg_mbstrlen_with_len(target, tlen);
115
116
  /*
117
   * We can transform an empty s into t with n insertions, or a non-empty t
118
   * into an empty s with m deletions.
119
   */
120
0
  if (!m)
121
0
    return levenshtein_result(n * ins_c_64);
122
0
  if (!n)
123
0
    return levenshtein_result(m * del_c_64);
124
125
  /*
126
   * For security concerns, restrict excessive CPU+RAM usage. (This
127
   * implementation uses O(m) memory and has O(mn) complexity.)  If
128
   * "trusted" is true, caller is responsible for not making excessive
129
   * requests, typically by using a small max_d along with strings that are
130
   * bounded, though not necessarily to MAX_LEVENSHTEIN_STRLEN exactly.
131
   */
132
0
  if (!trusted &&
133
0
    (m > MAX_LEVENSHTEIN_STRLEN ||
134
0
     n > MAX_LEVENSHTEIN_STRLEN))
135
0
    ereport(ERROR,
136
0
        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
137
0
         errmsg("levenshtein argument exceeds maximum length of %d characters",
138
0
            MAX_LEVENSHTEIN_STRLEN)));
139
140
#ifdef LEVENSHTEIN_LESS_EQUAL
141
  /* Initialize start and stop columns. */
142
0
  start_column = 0;
143
0
  stop_column = m + 1;
144
145
  /*
146
   * If max_d >= 0, determine whether the bound is impossibly tight.  If so,
147
   * return max_d + 1 immediately.  Otherwise, determine whether it's tight
148
   * enough to limit the computation we must perform.  If so, figure out
149
   * initial stop column.
150
   */
151
0
  if (max_d >= 0)
152
0
  {
153
0
    int64   min_theo_d; /* Theoretical minimum distance. */
154
0
    int64   max_theo_d; /* Theoretical maximum distance. */
155
0
    int     net_inserts = n - m;
156
157
0
    min_theo_d = net_inserts < 0 ?
158
0
      -net_inserts * del_c_64 : net_inserts * ins_c_64;
159
0
    if (min_theo_d > max_d)
160
0
      return levenshtein_result((int64) max_d + 1);
161
0
    if (ins_c_64 + del_c_64 < sub_c_64)
162
0
      sub_c_64 = ins_c_64 + del_c_64;
163
0
    max_theo_d = min_theo_d + sub_c_64 * Min(m, n);
164
0
    if (max_d >= max_theo_d)
165
0
      max_d = -1;
166
0
    else if (ins_c_64 + del_c_64 > 0)
167
0
    {
168
      /*
169
       * Figure out how much of the first row of the notional matrix we
170
       * need to fill in.  If the string is growing, the theoretical
171
       * minimum distance already incorporates the cost of deleting the
172
       * number of characters necessary to make the two strings equal in
173
       * length.  Each additional deletion forces another insertion, so
174
       * the best-case total cost increases by ins_c + del_c. If the
175
       * string is shrinking, the minimum theoretical cost assumes no
176
       * excess deletions; that is, we're starting no further right than
177
       * column n - m.  If we do start further right, the best-case
178
       * total cost increases by ins_c + del_c for each move right.
179
       */
180
0
      int64   slack_d = max_d - min_theo_d;
181
0
      int     best_column = net_inserts < 0 ? -net_inserts : 0;
182
      int64   tmp;
183
184
0
      tmp = best_column + (slack_d / (ins_c_64 + del_c_64)) + 1;
185
0
      stop_column = Min(tmp, m + 1);
186
0
    }
187
0
  }
188
0
#endif
189
190
  /*
191
   * In order to avoid calling pg_mblen_range() repeatedly on each character
192
   * in s, we cache all the lengths before starting the main loop -- but if
193
   * all the characters in both strings are single byte, then we skip this
194
   * and use a fast-path in the main loop.  If only one string contains
195
   * multi-byte characters, we still build the array, so that the fast-path
196
   * needn't deal with the case where the array hasn't been initialized.
197
   */
198
0
  if (m != slen || n != tlen)
199
0
  {
200
0
    int     i;
201
0
    const char *cp = source;
202
203
0
    s_char_len = (int *) palloc((m + 1) * sizeof(int));
204
0
    for (i = 0; i < m; ++i)
205
0
    {
206
0
      s_char_len[i] = pg_mblen_range(cp, send);
207
0
      cp += s_char_len[i];
208
0
    }
209
0
    s_char_len[i] = 0;
210
0
  }
211
212
  /* One more cell for initialization column and row. */
213
0
  ++m;
214
0
  ++n;
215
216
  /* Previous and current rows of notional array. */
217
0
  prev = (int64 *) palloc(2 * m * sizeof(int64));
218
0
  curr = prev + m;
219
220
  /*
221
   * To transform the first i characters of s into the first 0 characters of
222
   * t, we must perform i deletions.
223
   */
224
0
  for (int i = START_COLUMN; i < STOP_COLUMN; i++)
225
0
    prev[i] = i * del_c_64;
226
227
  /* Loop through rows of the notional array */
228
0
  for (y = target, j = 1; j < n; j++)
229
0
  {
230
0
    int64    *temp;
231
0
    const char *x = source;
232
0
    int     y_char_len = n != tlen + 1 ? pg_mblen_range(y, tend) : 1;
233
0
    int     i;
234
235
#ifdef LEVENSHTEIN_LESS_EQUAL
236
237
    /*
238
     * In the best case, values percolate down the diagonal unchanged, so
239
     * we must increment stop_column unless it's already on the right end
240
     * of the array.  The inner loop will read prev[stop_column], so we
241
     * have to initialize it even though it shouldn't affect the result.
242
     */
243
0
    if (stop_column < m)
244
0
    {
245
0
      prev[stop_column] = (int64) max_d + 1;
246
0
      ++stop_column;
247
0
    }
248
249
    /*
250
     * The main loop fills in curr, but curr[0] needs a special case: to
251
     * transform the first 0 characters of s into the first j characters
252
     * of t, we must perform j insertions.  However, if start_column > 0,
253
     * this special case does not apply.
254
     */
255
0
    if (start_column == 0)
256
0
    {
257
0
      curr[0] = j * ins_c_64;
258
0
      i = 1;
259
0
    }
260
0
    else
261
0
      i = start_column;
262
#else
263
    curr[0] = j * ins_c_64;
264
    i = 1;
265
#endif
266
267
    /*
268
     * This inner loop is critical to performance, so we include a
269
     * fast-path to handle the (fairly common) case where no multibyte
270
     * characters are in the mix.  The fast-path is entitled to assume
271
     * that if s_char_len is not initialized then BOTH strings contain
272
     * only single-byte characters.
273
     */
274
0
    if (s_char_len != NULL)
275
0
    {
276
0
      for (; i < STOP_COLUMN; i++)
277
0
      {
278
0
        int64   ins;
279
0
        int64   del;
280
0
        int64   sub;
281
0
        int     x_char_len = s_char_len[i - 1];
282
283
        /*
284
         * Calculate costs for insertion, deletion, and substitution.
285
         *
286
         * When calculating cost for substitution, we compare the last
287
         * character of each possibly-multibyte character first,
288
         * because that's enough to rule out most mis-matches.  If we
289
         * get past that test, then we compare the lengths and the
290
         * remaining bytes.
291
         */
292
0
        ins = prev[i] + ins_c_64;
293
0
        del = curr[i - 1] + del_c_64;
294
0
        if (x[x_char_len - 1] == y[y_char_len - 1]
295
0
          && x_char_len == y_char_len &&
296
0
          (x_char_len == 1 || rest_of_char_same(x, y, x_char_len)))
297
0
          sub = prev[i - 1];
298
0
        else
299
0
          sub = prev[i - 1] + sub_c_64;
300
301
        /* Take the one with minimum cost. */
302
0
        curr[i] = Min(ins, del);
303
0
        curr[i] = Min(curr[i], sub);
304
305
        /* Point to next character. */
306
0
        x += x_char_len;
307
0
      }
308
0
    }
309
0
    else
310
0
    {
311
0
      for (; i < STOP_COLUMN; i++)
312
0
      {
313
0
        int64   ins;
314
0
        int64   del;
315
0
        int64   sub;
316
317
        /* Calculate costs for insertion, deletion, and substitution. */
318
0
        ins = prev[i] + ins_c_64;
319
0
        del = curr[i - 1] + del_c_64;
320
0
        sub = prev[i - 1] + ((*x == *y) ? 0 : sub_c_64);
321
322
        /* Take the one with minimum cost. */
323
0
        curr[i] = Min(ins, del);
324
0
        curr[i] = Min(curr[i], sub);
325
326
        /* Point to next character. */
327
0
        x++;
328
0
      }
329
0
    }
330
331
    /* Swap current row with previous row. */
332
0
    temp = curr;
333
0
    curr = prev;
334
0
    prev = temp;
335
336
    /* Point to next character. */
337
0
    y += y_char_len;
338
339
#ifdef LEVENSHTEIN_LESS_EQUAL
340
341
    /*
342
     * This chunk of code represents a significant performance hit if used
343
     * in the case where there is no max_d bound.  This is probably not
344
     * because the max_d >= 0 test itself is expensive, but rather because
345
     * the possibility of needing to execute this code prevents tight
346
     * optimization of the loop as a whole.
347
     */
348
0
    if (max_d >= 0)
349
0
    {
350
      /*
351
       * The "zero point" is the column of the current row where the
352
       * remaining portions of the strings are of equal length.  There
353
       * are (n - 1) characters in the target string, of which j have
354
       * been transformed.  There are (m - 1) characters in the source
355
       * string, so we want to find the value for zp where (n - 1) - j =
356
       * (m - 1) - zp.
357
       */
358
0
      int     zp = j - (n - m);
359
360
      /* Check whether the stop column can slide left. */
361
0
      while (stop_column > 0)
362
0
      {
363
0
        int     ii = stop_column - 1;
364
0
        int     net_inserts = ii - zp;
365
366
0
        if (prev[ii] + (net_inserts > 0 ? net_inserts * ins_c_64 :
367
0
                -net_inserts * del_c_64) <= max_d)
368
0
          break;
369
0
        stop_column--;
370
0
      }
371
372
      /* Check whether the start column can slide right. */
373
0
      while (start_column < stop_column)
374
0
      {
375
0
        int     net_inserts = start_column - zp;
376
377
0
        if (prev[start_column] +
378
0
          (net_inserts > 0 ? net_inserts * ins_c_64 :
379
0
           -net_inserts * del_c_64) <= max_d)
380
0
          break;
381
382
        /*
383
         * We'll never again update these values, so we must make sure
384
         * there's nothing here that could confuse any future
385
         * iteration of the outer loop.
386
         */
387
0
        prev[start_column] = (int64) max_d + 1;
388
0
        curr[start_column] = (int64) max_d + 1;
389
0
        if (start_column != 0)
390
0
          source += (s_char_len != NULL) ? s_char_len[start_column - 1] : 1;
391
0
        start_column++;
392
0
      }
393
394
      /* If they cross, we're going to exceed the bound. */
395
0
      if (start_column >= stop_column)
396
0
        return levenshtein_result((int64) max_d + 1);
397
0
    }
398
#endif
399
0
  }
400
401
  /*
402
   * Because the final value was swapped from the previous row to the
403
   * current row, that's where we'll find it.
404
   */
405
0
  return levenshtein_result(prev[m - 1]);
406
0
}
Unexecuted instantiation: varstr_levenshtein
Unexecuted instantiation: varstr_levenshtein_less_equal