]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/sql/Pg/020.schema.functions.sql
LP#1772955: Only include xacts with balance in summary
[Evergreen.git] / Open-ILS / src / sql / Pg / 020.schema.functions.sql
1 /*
2  * Copyright (C) 2004-2008  Georgia Public Library Service
3  * Copyright (C) 2007-2008  Equinox Software, Inc.
4  * Mike Rylander <miker@esilibrary.com> 
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  */
17
18 CREATE OR REPLACE FUNCTION public.non_filing_normalize ( TEXT, "char" ) RETURNS TEXT AS $$
19         SELECT  SUBSTRING(
20                         REGEXP_REPLACE(
21                                 REGEXP_REPLACE(
22                                         $1,
23                                         E'\W*$',
24                                         ''
25                                 ),
26                                 '  ',
27                                 ' '
28                         ),
29                         CASE
30                                 WHEN $2::INT NOT BETWEEN 48 AND 57 THEN 1
31                                 ELSE $2::TEXT::INT + 1
32                         END
33                 );
34 $$ LANGUAGE SQL STRICT IMMUTABLE;
35
36 CREATE OR REPLACE FUNCTION public.first_word ( TEXT ) RETURNS TEXT AS $$
37         SELECT COALESCE(SUBSTRING( $1 FROM $_$^\S+$_$), '');
38 $$ LANGUAGE SQL STRICT IMMUTABLE;
39
40 CREATE OR REPLACE FUNCTION public.normalize_space( TEXT ) RETURNS TEXT AS $$
41     SELECT regexp_replace(regexp_replace(regexp_replace($1, E'\\n', ' ', 'g'), E'(?:^\\s+)|(\\s+$)', '', 'g'), E'\\s+', ' ', 'g');
42 $$ LANGUAGE SQL STRICT IMMUTABLE;
43
44 CREATE OR REPLACE FUNCTION public.remove_commas( TEXT ) RETURNS TEXT AS $$
45     SELECT regexp_replace($1, ',', '', 'g');
46 $$ LANGUAGE SQL STRICT IMMUTABLE;
47
48 CREATE OR REPLACE FUNCTION public.remove_paren_substring( TEXT ) RETURNS TEXT AS $func$
49     SELECT regexp_replace($1, $$\([^)]+\)$$, '', 'g');
50 $func$ LANGUAGE SQL STRICT IMMUTABLE;
51
52 CREATE OR REPLACE FUNCTION public.remove_whitespace( TEXT ) RETURNS TEXT AS $$
53     SELECT regexp_replace(normalize_space($1), E'\\s+', '', 'g');
54 $$ LANGUAGE SQL STRICT IMMUTABLE;
55
56 CREATE OR REPLACE FUNCTION public.lowercase( TEXT ) RETURNS TEXT AS $$
57     return lc(shift);
58 $$ LANGUAGE PLPERLU STRICT IMMUTABLE;
59
60 CREATE OR REPLACE FUNCTION public.uppercase( TEXT ) RETURNS TEXT AS $$
61     return uc(shift);
62 $$ LANGUAGE PLPERLU STRICT IMMUTABLE;
63
64 CREATE OR REPLACE FUNCTION public.remove_diacritics( TEXT ) RETURNS TEXT AS $$
65     use Unicode::Normalize;
66
67     my $x = NFD(shift);
68     $x =~ s/\pM+//go;
69     return $x;
70
71 $$ LANGUAGE PLPERLU STRICT IMMUTABLE;
72
73 CREATE OR REPLACE FUNCTION public.entityize( TEXT ) RETURNS TEXT AS $$
74     use Unicode::Normalize;
75
76     my $x = NFC(shift);
77     $x =~ s/([\x{0080}-\x{fffd}])/sprintf('&#x%X;',ord($1))/sgoe;
78     return $x;
79
80 $$ LANGUAGE PLPERLU STRICT IMMUTABLE;
81
82 CREATE OR REPLACE FUNCTION public.call_number_dewey( TEXT ) RETURNS TEXT AS $$
83         my $txt = shift;
84         $txt =~ s/^\s+//o;
85         $txt =~ s/[\[\]\{\}\(\)`'"#<>\*\?\-\+\$\\]+//og;
86         $txt =~ s/\s+$//o;
87         if ($txt =~ /(\d{3}(?:\.\d+)?)/o) {
88                 return $1;
89         } else {
90                 return (split /\s+/, $txt)[0];
91         }
92 $$ LANGUAGE 'plperlu' STRICT IMMUTABLE;
93
94 CREATE OR REPLACE FUNCTION public.call_number_dewey( TEXT, INT ) RETURNS TEXT AS $$
95         SELECT SUBSTRING(call_number_dewey($1) FROM 1 FOR $2);
96 $$ LANGUAGE SQL STRICT IMMUTABLE;
97
98 CREATE OR REPLACE FUNCTION tableoid2name ( oid ) RETURNS TEXT AS $$
99         BEGIN
100                 RETURN $1::regclass;
101         END;
102 $$ language 'plpgsql';
103
104 CREATE OR REPLACE FUNCTION actor.org_unit_descendants( INT, INT ) RETURNS SETOF actor.org_unit AS $$
105     WITH RECURSIVE descendant_depth AS (
106         SELECT  ou.id,
107                 ou.parent_ou,
108                 out.depth
109           FROM  actor.org_unit ou
110                 JOIN actor.org_unit_type out ON (out.id = ou.ou_type)
111                 JOIN anscestor_depth ad ON (ad.id = ou.id)
112           WHERE ad.depth = $2
113             UNION ALL
114         SELECT  ou.id,
115                 ou.parent_ou,
116                 out.depth
117           FROM  actor.org_unit ou
118                 JOIN actor.org_unit_type out ON (out.id = ou.ou_type)
119                 JOIN descendant_depth ot ON (ot.id = ou.parent_ou)
120     ), anscestor_depth AS (
121         SELECT  ou.id,
122                 ou.parent_ou,
123                 out.depth
124           FROM  actor.org_unit ou
125                 JOIN actor.org_unit_type out ON (out.id = ou.ou_type)
126           WHERE ou.id = $1
127             UNION ALL
128         SELECT  ou.id,
129                 ou.parent_ou,
130                 out.depth
131           FROM  actor.org_unit ou
132                 JOIN actor.org_unit_type out ON (out.id = ou.ou_type)
133                 JOIN anscestor_depth ot ON (ot.parent_ou = ou.id)
134     ) SELECT ou.* FROM actor.org_unit ou JOIN descendant_depth USING (id);
135 $$ LANGUAGE SQL ROWS 1;
136
137 CREATE OR REPLACE FUNCTION actor.org_unit_descendants( INT ) RETURNS SETOF actor.org_unit AS $$
138     WITH RECURSIVE descendant_depth AS (
139         SELECT  ou.id,
140                 ou.parent_ou,
141                 out.depth
142           FROM  actor.org_unit ou
143                 JOIN actor.org_unit_type out ON (out.id = ou.ou_type)
144           WHERE ou.id = $1
145             UNION ALL
146         SELECT  ou.id,
147                 ou.parent_ou,
148                 out.depth
149           FROM  actor.org_unit ou
150                 JOIN actor.org_unit_type out ON (out.id = ou.ou_type)
151                 JOIN descendant_depth ot ON (ot.id = ou.parent_ou)
152     ) SELECT ou.* FROM actor.org_unit ou JOIN descendant_depth USING (id);
153 $$ LANGUAGE SQL ROWS 1;
154
155 CREATE OR REPLACE FUNCTION actor.org_unit_descendants_distance( INT ) RETURNS TABLE (id INT, distance INT) AS $$
156     WITH RECURSIVE org_unit_descendants_distance(id, distance) AS (
157             SELECT $1, 0
158         UNION
159             SELECT ou.id, oudd.distance+1
160             FROM actor.org_unit ou JOIN org_unit_descendants_distance oudd ON (ou.parent_ou = oudd.id)
161     )
162     SELECT * FROM org_unit_descendants_distance;
163 $$ LANGUAGE SQL STABLE ROWS 1;
164
165 CREATE OR REPLACE FUNCTION actor.org_unit_ancestors( INT ) RETURNS SETOF actor.org_unit AS $$
166     WITH RECURSIVE org_unit_ancestors_distance(id, distance) AS (
167             SELECT $1, 0
168         UNION
169             SELECT ou.parent_ou, ouad.distance+1
170             FROM actor.org_unit ou JOIN org_unit_ancestors_distance ouad ON (ou.id = ouad.id)
171             WHERE ou.parent_ou IS NOT NULL
172     )
173     SELECT ou.* FROM actor.org_unit ou JOIN org_unit_ancestors_distance ouad USING (id) ORDER BY ouad.distance DESC;
174 $$ LANGUAGE SQL ROWS 1;
175
176 CREATE OR REPLACE FUNCTION actor.org_unit_ancestor_at_depth ( INT,INT ) RETURNS actor.org_unit AS $$
177         SELECT  a.*
178           FROM  actor.org_unit a
179           WHERE id = ( SELECT FIRST(x.id)
180                          FROM   actor.org_unit_ancestors($1) x
181                                 JOIN actor.org_unit_type y
182                                         ON x.ou_type = y.id AND y.depth = $2);
183 $$ LANGUAGE SQL STABLE;
184
185 CREATE OR REPLACE FUNCTION actor.org_unit_ancestors_distance( INT ) RETURNS TABLE (id INT, distance INT) AS $$
186     WITH RECURSIVE org_unit_ancestors_distance(id, distance) AS (
187             SELECT $1, 0
188         UNION
189             SELECT ou.parent_ou, ouad.distance+1
190             FROM actor.org_unit ou JOIN org_unit_ancestors_distance ouad ON (ou.id = ouad.id)
191             WHERE ou.parent_ou IS NOT NULL
192     )
193     SELECT * FROM org_unit_ancestors_distance;
194 $$ LANGUAGE SQL STABLE ROWS 1;
195
196 CREATE OR REPLACE FUNCTION actor.org_unit_ancestors_distance( INT ) RETURNS TABLE (id INT, distance INT) AS $$
197     WITH RECURSIVE org_unit_ancestors_distance(id, distance) AS (
198             SELECT $1, 0
199         UNION
200             SELECT ou.parent_ou, ouad.distance+1
201             FROM actor.org_unit ou JOIN org_unit_ancestors_distance ouad ON (ou.id = ouad.id)
202             WHERE ou.parent_ou IS NOT NULL
203     )
204     SELECT * FROM org_unit_ancestors_distance;
205 $$ LANGUAGE SQL STABLE ROWS 1;
206
207 CREATE OR REPLACE FUNCTION actor.org_unit_full_path ( INT ) RETURNS SETOF actor.org_unit AS $$
208     SELECT  aou.*
209       FROM  actor.org_unit AS aou
210             JOIN (
211                 (SELECT au.id, t.depth FROM actor.org_unit_ancestors($1) AS au JOIN actor.org_unit_type t ON (au.ou_type = t.id))
212                     UNION
213                 (SELECT au.id, t.depth FROM actor.org_unit_descendants($1) AS au JOIN actor.org_unit_type t ON (au.ou_type = t.id))
214             ) AS ad ON (aou.id=ad.id)
215       ORDER BY ad.depth;
216 $$ LANGUAGE SQL STABLE ROWS 1;
217
218 CREATE OR REPLACE FUNCTION actor.org_unit_full_path ( INT, INT ) RETURNS SETOF actor.org_unit AS $$
219         SELECT  * FROM actor.org_unit_full_path((actor.org_unit_ancestor_at_depth($1, $2)).id)
220 $$ LANGUAGE SQL STABLE ROWS 1;
221
222 CREATE OR REPLACE FUNCTION actor.org_unit_combined_ancestors ( INT, INT ) RETURNS SETOF actor.org_unit AS $$
223         SELECT  *
224           FROM  actor.org_unit_ancestors($1)
225                         UNION
226         SELECT  *
227           FROM  actor.org_unit_ancestors($2);
228 $$ LANGUAGE SQL STABLE ROWS 1;
229
230 CREATE OR REPLACE FUNCTION actor.org_unit_common_ancestors ( INT, INT ) RETURNS SETOF actor.org_unit AS $$
231         SELECT  *
232           FROM  actor.org_unit_ancestors($1)
233                         INTERSECT
234         SELECT  *
235           FROM  actor.org_unit_ancestors($2);
236 $$ LANGUAGE SQL STABLE ROWS 1;
237
238 -- Given the IDs of two rows in actor.org_unit, *the second being an ancestor
239 -- of the first*, return in array form the path from the ancestor to the
240 -- descendant, with each point in the path being an org_unit ID.  This is
241 -- useful for sorting org_units by their position in a depth-first (display
242 -- order) representation of the tree.
243 --
244 -- This breaks with the precedent set by actor.org_unit_full_path() and others,
245 -- and gets the parameters "backwards," but otherwise this function would
246 -- not be very usable within json_query.
247 CREATE OR REPLACE FUNCTION actor.org_unit_simple_path(INT, INT)
248 RETURNS INT[] AS $$
249     WITH RECURSIVE descendant_depth(id, path) AS (
250         SELECT  aou.id,
251                 ARRAY[aou.id]
252           FROM  actor.org_unit aou
253                 JOIN actor.org_unit_type aout ON (aout.id = aou.ou_type)
254           WHERE aou.id = $2
255             UNION ALL
256         SELECT  aou.id,
257                 dd.path || ARRAY[aou.id]
258           FROM  actor.org_unit aou
259                 JOIN actor.org_unit_type aout ON (aout.id = aou.ou_type)
260                 JOIN descendant_depth dd ON (dd.id = aou.parent_ou)
261     ) SELECT dd.path
262         FROM actor.org_unit aou
263         JOIN descendant_depth dd USING (id)
264         WHERE aou.id = $1 ORDER BY dd.path;
265 $$ LANGUAGE SQL STABLE;
266
267 CREATE OR REPLACE FUNCTION actor.org_unit_proximity ( INT, INT ) RETURNS INT AS $$
268         SELECT COUNT(id)::INT FROM (
269                 SELECT id FROM actor.org_unit_combined_ancestors($1, $2)
270                         EXCEPT
271                 SELECT id FROM actor.org_unit_common_ancestors($1, $2)
272         ) z;
273 $$ LANGUAGE SQL STABLE;
274
275 CREATE OR REPLACE FUNCTION actor.org_unit_ancestor_setting( setting_name TEXT, org_id INT ) RETURNS SETOF actor.org_unit_setting AS $$
276 DECLARE
277     setting RECORD;
278     cur_org INT;
279 BEGIN
280     cur_org := org_id;
281     LOOP
282         SELECT INTO setting * FROM actor.org_unit_setting WHERE org_unit = cur_org AND name = setting_name;
283         IF FOUND THEN
284             RETURN NEXT setting;
285             EXIT;
286         END IF;
287         SELECT INTO cur_org parent_ou FROM actor.org_unit WHERE id = cur_org;
288         EXIT WHEN cur_org IS NULL;
289     END LOOP;
290     RETURN;
291 END;
292 $$ LANGUAGE plpgsql STABLE ROWS 1;
293
294 COMMENT ON FUNCTION actor.org_unit_ancestor_setting( TEXT, INT) IS $$
295 Search "up" the org_unit tree until we find the first occurrence of an 
296 org_unit_setting with the given name.
297 $$;
298
299 CREATE OR REPLACE FUNCTION actor.org_unit_ancestor_setting_batch( org_id INT, setting_names TEXT[] ) RETURNS SETOF actor.org_unit_setting AS $$
300 DECLARE
301     setting RECORD;
302     setting_name TEXT;
303     cur_org INT;
304 BEGIN
305     FOREACH setting_name IN ARRAY setting_names
306     LOOP
307         cur_org := org_id;
308         LOOP
309             SELECT INTO setting * FROM actor.org_unit_setting WHERE org_unit = cur_org AND name = setting_name;
310             IF FOUND THEN
311                 RETURN NEXT setting;
312                 EXIT;
313             END IF;
314             SELECT INTO cur_org parent_ou FROM actor.org_unit WHERE id = cur_org;
315             EXIT WHEN cur_org IS NULL;
316         END LOOP;
317     END LOOP;
318     RETURN;
319 END;
320 $$ LANGUAGE plpgsql STABLE;
321
322 COMMENT ON FUNCTION actor.org_unit_ancestor_setting_batch( INT,  TEXT[] ) IS $$
323 For each setting name passed, search "up" the org_unit tree until
324 we find the first occurrence of an org_unit_setting with the given name.
325 $$;
326
327 CREATE OR REPLACE FUNCTION actor.org_unit_ancestor_setting_batch_by_org(
328     setting_name TEXT, org_ids INTEGER[]) 
329     RETURNS SETOF actor.org_unit_setting AS 
330 $FUNK$
331 DECLARE
332     setting RECORD;
333     org_id INTEGER;
334 BEGIN
335     /*  Returns one actor.org_unit_setting row per org unit ID provided.
336         When no setting exists for a given org unit, the setting row
337         will contain all empty values. */
338     FOREACH org_id IN ARRAY org_ids LOOP
339         SELECT INTO setting * FROM 
340             actor.org_unit_ancestor_setting(setting_name, org_id);
341         RETURN NEXT setting;
342     END LOOP;
343     RETURN;
344 END;
345 $FUNK$ LANGUAGE plpgsql STABLE;
346
347 CREATE OR REPLACE FUNCTION evergreen.get_barcodes(select_ou INT, type TEXT, in_barcode TEXT) RETURNS SETOF evergreen.barcode_set AS $$
348 DECLARE
349     cur_barcode TEXT;
350     barcode_len INT;
351     completion_len  INT;
352     asset_barcodes  TEXT[];
353     actor_barcodes  TEXT[];
354     do_asset    BOOL = false;
355     do_serial   BOOL = false;
356     do_booking  BOOL = false;
357     do_actor    BOOL = false;
358     completion_set  config.barcode_completion%ROWTYPE;
359 BEGIN
360
361     IF position('asset' in type) > 0 THEN
362         do_asset = true;
363     END IF;
364     IF position('serial' in type) > 0 THEN
365         do_serial = true;
366     END IF;
367     IF position('booking' in type) > 0 THEN
368         do_booking = true;
369     END IF;
370     IF do_asset OR do_serial OR do_booking THEN
371         asset_barcodes = asset_barcodes || in_barcode;
372     END IF;
373     IF position('actor' in type) > 0 THEN
374         do_actor = true;
375         actor_barcodes = actor_barcodes || in_barcode;
376     END IF;
377
378     barcode_len := length(in_barcode);
379
380     FOR completion_set IN
381       SELECT * FROM config.barcode_completion
382         WHERE active
383         AND org_unit IN (SELECT aou.id FROM actor.org_unit_ancestors(select_ou) aou)
384         LOOP
385         IF completion_set.prefix IS NULL THEN
386             completion_set.prefix := '';
387         END IF;
388         IF completion_set.suffix IS NULL THEN
389             completion_set.suffix := '';
390         END IF;
391         IF completion_set.length = 0 OR completion_set.padding IS NULL OR length(completion_set.padding) = 0 THEN
392             cur_barcode = completion_set.prefix || in_barcode || completion_set.suffix;
393         ELSE
394             completion_len = completion_set.length - length(completion_set.prefix) - length(completion_set.suffix);
395             IF completion_len >= barcode_len THEN
396                 IF completion_set.padding_end THEN
397                     cur_barcode = rpad(in_barcode, completion_len, completion_set.padding);
398                 ELSE
399                     cur_barcode = lpad(in_barcode, completion_len, completion_set.padding);
400                 END IF;
401                 cur_barcode = completion_set.prefix || cur_barcode || completion_set.suffix;
402             END IF;
403         END IF;
404         IF completion_set.actor THEN
405             actor_barcodes = actor_barcodes || cur_barcode;
406         END IF;
407         IF completion_set.asset THEN
408             asset_barcodes = asset_barcodes || cur_barcode;
409         END IF;
410     END LOOP;
411
412     IF do_asset AND do_serial THEN
413         RETURN QUERY SELECT 'asset'::TEXT, id, barcode FROM ONLY asset.copy WHERE barcode = ANY(asset_barcodes) AND deleted = false;
414         RETURN QUERY SELECT 'serial'::TEXT, id, barcode FROM serial.unit WHERE barcode = ANY(asset_barcodes) AND deleted = false;
415     ELSIF do_asset THEN
416         RETURN QUERY SELECT 'asset'::TEXT, id, barcode FROM asset.copy WHERE barcode = ANY(asset_barcodes) AND deleted = false;
417     ELSIF do_serial THEN
418         RETURN QUERY SELECT 'serial'::TEXT, id, barcode FROM serial.unit WHERE barcode = ANY(asset_barcodes) AND deleted = false;
419     END IF;
420     IF do_booking THEN
421         RETURN QUERY SELECT 'booking'::TEXT, id::BIGINT, barcode FROM booking.resource WHERE barcode = ANY(asset_barcodes);
422     END IF;
423     IF do_actor THEN
424         RETURN QUERY SELECT 'actor'::TEXT, c.usr::BIGINT, c.barcode FROM actor.card c JOIN actor.usr u ON c.usr = u.id WHERE
425             ((c.barcode = ANY(actor_barcodes) AND c.active) OR c.barcode = in_barcode) AND NOT u.deleted ORDER BY usr;
426     END IF;
427     RETURN;
428 END;
429 $$ LANGUAGE plpgsql;
430
431 COMMENT ON FUNCTION evergreen.get_barcodes(INT, TEXT, TEXT) IS $$
432 Given user input, find an appropriate barcode in the proper class.
433
434 Will add prefix/suffix information to do so, and return all results.
435 $$;
436
437 CREATE OR REPLACE FUNCTION actor.org_unit_prox_update () RETURNS TRIGGER as $$
438 BEGIN
439
440
441 IF TG_OP = 'DELETE' THEN
442
443     DELETE FROM actor.org_unit_proximity WHERE (from_org = OLD.id or to_org= OLD.id);
444
445 END IF;
446
447 IF TG_OP = 'UPDATE' THEN
448
449     IF NEW.parent_ou <> OLD.parent_ou THEN
450
451         DELETE FROM actor.org_unit_proximity WHERE (from_org = OLD.id or to_org= OLD.id);
452             INSERT INTO actor.org_unit_proximity (from_org, to_org, prox)
453             SELECT  l.id, r.id, actor.org_unit_proximity(l.id,r.id)
454                 FROM  actor.org_unit l, actor.org_unit r
455                 WHERE (l.id = NEW.id or r.id = NEW.id);
456
457     END IF;
458
459 END IF;
460
461 IF TG_OP = 'INSERT' THEN
462
463      INSERT INTO actor.org_unit_proximity (from_org, to_org, prox)
464      SELECT  l.id, r.id, actor.org_unit_proximity(l.id,r.id)
465          FROM  actor.org_unit l, actor.org_unit r
466          WHERE (l.id = NEW.id or r.id = NEW.id);
467
468 END IF;
469
470 RETURN null;
471
472 END;
473 $$ LANGUAGE plpgsql;
474
475
476 CREATE TRIGGER proximity_update_tgr AFTER INSERT OR UPDATE OR DELETE ON actor.org_unit FOR EACH ROW EXECUTE PROCEDURE actor.org_unit_prox_update ();
477
478 CREATE OR REPLACE FUNCTION evergreen.get_locale_name(
479     IN locale TEXT,
480     OUT name TEXT,
481     OUT description TEXT
482 ) AS $$
483 DECLARE
484     eg_locale TEXT;
485 BEGIN
486     eg_locale := LOWER(SUBSTRING(locale FROM 1 FOR 2)) || '-' || UPPER(SUBSTRING(locale FROM 4 FOR 2));
487         
488     SELECT i18nc.string INTO name
489     FROM config.i18n_locale i18nl
490        INNER JOIN config.i18n_core i18nc ON i18nl.code = i18nc.translation
491     WHERE i18nc.identity_value = eg_locale
492        AND code = eg_locale
493        AND i18nc.fq_field = 'i18n_l.name';
494
495     IF name IS NULL THEN
496        SELECT i18nl.name INTO name
497        FROM config.i18n_locale i18nl
498        WHERE code = eg_locale;
499     END IF;
500
501     SELECT i18nc.string INTO description
502     FROM config.i18n_locale i18nl
503        INNER JOIN config.i18n_core i18nc ON i18nl.code = i18nc.translation
504     WHERE i18nc.identity_value = eg_locale
505        AND code = eg_locale
506        AND i18nc.fq_field = 'i18n_l.description';
507
508     IF description IS NULL THEN
509        SELECT i18nl.description INTO description
510        FROM config.i18n_locale i18nl
511        WHERE code = eg_locale;
512     END IF;
513 END;
514 $$ LANGUAGE PLPGSQL COST 1 STABLE;