PostgreSQL Source Code git master
isn.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * isn.c
4 * PostgreSQL type definitions for ISNs (ISBN, ISMN, ISSN, EAN13, UPC)
5 *
6 * Author: German Mendez Bravo (Kronuz)
7 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
8 *
9 * IDENTIFICATION
10 * contrib/isn/isn.c
11 *
12 *-------------------------------------------------------------------------
13 */
14
15#include "postgres.h"
16
17#include "EAN13.h"
18#include "ISBN.h"
19#include "ISMN.h"
20#include "ISSN.h"
21#include "UPC.h"
22#include "fmgr.h"
23#include "isn.h"
24#include "utils/guc.h"
25
27 .name = "isn",
28 .version = PG_VERSION
29);
30
31#ifdef USE_ASSERT_CHECKING
32#define ISN_DEBUG 1
33#else
34#define ISN_DEBUG 0
35#endif
36
37#define MAXEAN13LEN 18
38
40{
42};
43
44static const char *const isn_names[] = {"EAN13/UPC/ISxN", "EAN13/UPC/ISxN", "EAN13", "ISBN", "ISMN", "ISSN", "UPC"};
45
46/* GUC value */
47static bool g_weak = false;
48
49
50/***********************************************************************
51 **
52 ** Routines for EAN13/UPC/ISxNs.
53 **
54 ** Note:
55 ** In this code, a normalized string is one that is known to be a valid
56 ** ISxN number containing only digits and hyphens and with enough space
57 ** to hold the full 13 digits plus the maximum of four hyphens.
58 ***********************************************************************/
59
60/*----------------------------------------------------------
61 * Debugging routines.
62 *---------------------------------------------------------*/
63
64/*
65 * Check if the table and its index is correct (just for debugging)
66 */
68static bool
69check_table(const char *(*TABLE)[2], const unsigned TABLE_index[10][2])
70{
71 const char *aux1,
72 *aux2;
73 int a,
75 x = 0,
76 y = -1,
77 i = 0,
79 init = 0;
80
81 if (TABLE == NULL || TABLE_index == NULL)
82 return true;
83
84 while (TABLE[i][0] && TABLE[i][1])
85 {
86 aux1 = TABLE[i][0];
87 aux2 = TABLE[i][1];
88
89 /* must always start with a digit: */
90 if (!isdigit((unsigned char) *aux1) || !isdigit((unsigned char) *aux2))
91 goto invalidtable;
92 a = *aux1 - '0';
93 b = *aux2 - '0';
94
95 /* must always have the same format and length: */
96 while (*aux1 && *aux2)
97 {
98 if (!(isdigit((unsigned char) *aux1) &&
99 isdigit((unsigned char) *aux2)) &&
100 (*aux1 != *aux2 || *aux1 != '-'))
101 goto invalidtable;
102 aux1++;
103 aux2++;
104 }
105 if (*aux1 != *aux2)
106 goto invalidtable;
107
108 /* found a new range */
109 if (a > y)
110 {
111 /* check current range in the index: */
112 for (j = x; j <= y; j++)
113 {
114 if (TABLE_index[j][0] != init)
115 goto invalidindex;
116 if (TABLE_index[j][1] != i - init)
117 goto invalidindex;
118 }
119 init = i;
120 x = a;
121 }
122
123 /* Always get the new limit */
124 y = b;
125 if (y < x)
126 goto invalidtable;
127 i++;
128 }
129
130 return true;
131
132invalidtable:
133 elog(DEBUG1, "invalid table near {\"%s\", \"%s\"} (pos: %d)",
134 TABLE[i][0], TABLE[i][1], i);
135 return false;
136
137invalidindex:
138 elog(DEBUG1, "index %d is invalid", j);
139 return false;
140}
141
142/*----------------------------------------------------------
143 * Formatting and conversion routines.
144 *---------------------------------------------------------*/
145
146static unsigned
147dehyphenate(char *bufO, char *bufI)
148{
149 unsigned ret = 0;
150
151 while (*bufI)
152 {
153 if (isdigit((unsigned char) *bufI))
154 {
155 *bufO++ = *bufI;
156 ret++;
157 }
158 bufI++;
159 }
160 *bufO = '\0';
161 return ret;
162}
163
164/*
165 * hyphenate --- Try to hyphenate, in-place, the string starting at bufI
166 * into bufO using the given hyphenation range TABLE.
167 * Assumes the input string to be used is of only digits.
168 *
169 * Returns the number of characters actually hyphenated.
170 */
171static unsigned
172hyphenate(char *bufO, char *bufI, const char *(*TABLE)[2], const unsigned TABLE_index[10][2])
173{
174 unsigned ret = 0;
175 const char *ean_aux1,
176 *ean_aux2,
177 *ean_p;
178 char *firstdig,
179 *aux1,
180 *aux2;
181 unsigned search,
182 upper,
183 lower,
184 step;
185 bool ean_in1,
186 ean_in2;
187
188 /* just compress the string if no further hyphenation is required */
189 if (TABLE == NULL || TABLE_index == NULL)
190 {
191 while (*bufI)
192 {
193 *bufO++ = *bufI++;
194 ret++;
195 }
196 *bufO = '\0';
197 return (ret + 1);
198 }
199
200 /* add remaining hyphenations */
201
202 search = *bufI - '0';
203 upper = lower = TABLE_index[search][0];
204 upper += TABLE_index[search][1];
205 lower--;
206
207 step = (upper - lower) / 2;
208 if (step == 0)
209 return 0;
210 search = lower + step;
211
212 firstdig = bufI;
213 ean_in1 = ean_in2 = false;
214 ean_aux1 = TABLE[search][0];
215 ean_aux2 = TABLE[search][1];
216 do
217 {
218 if ((ean_in1 || *firstdig >= *ean_aux1) && (ean_in2 || *firstdig <= *ean_aux2))
219 {
220 if (*firstdig > *ean_aux1)
221 ean_in1 = true;
222 if (*firstdig < *ean_aux2)
223 ean_in2 = true;
224 if (ean_in1 && ean_in2)
225 break;
226
227 firstdig++, ean_aux1++, ean_aux2++;
228 if (!(*ean_aux1 && *ean_aux2 && *firstdig))
229 break;
230 if (!isdigit((unsigned char) *ean_aux1))
231 ean_aux1++, ean_aux2++;
232 }
233 else
234 {
235 /*
236 * check in what direction we should go and move the pointer
237 * accordingly
238 */
239 if (*firstdig < *ean_aux1 && !ean_in1)
240 upper = search;
241 else
242 lower = search;
243
244 step = (upper - lower) / 2;
245 search = lower + step;
246
247 /* Initialize stuff again: */
248 firstdig = bufI;
249 ean_in1 = ean_in2 = false;
250 ean_aux1 = TABLE[search][0];
251 ean_aux2 = TABLE[search][1];
252 }
253 } while (step);
254
255 if (step)
256 {
257 aux1 = bufO;
258 aux2 = bufI;
259 ean_p = TABLE[search][0];
260 while (*ean_p && *aux2)
261 {
262 if (*ean_p++ != '-')
263 *aux1++ = *aux2++;
264 else
265 *aux1++ = '-';
266 ret++;
267 }
268 *aux1++ = '-';
269 *aux1 = *aux2; /* add a lookahead char */
270 return (ret + 1);
271 }
272 return ret;
273}
274
275/*
276 * weight_checkdig -- Receives a buffer with a normalized ISxN string number,
277 * and the length to weight.
278 *
279 * Returns the weight of the number (the check digit value, 0-10)
280 */
281static unsigned
282weight_checkdig(char *isn, unsigned size)
283{
284 unsigned weight = 0;
285
286 while (*isn && size > 1)
287 {
288 if (isdigit((unsigned char) *isn))
289 {
290 weight += size-- * (*isn - '0');
291 }
292 isn++;
293 }
294 weight = weight % 11;
295 if (weight != 0)
296 weight = 11 - weight;
297 return weight;
298}
299
300
301/*
302 * checkdig --- Receives a buffer with a normalized ISxN string number,
303 * and the length to check.
304 *
305 * Returns the check digit value (0-9)
306 */
307static unsigned
308checkdig(char *num, unsigned size)
309{
310 unsigned check = 0,
311 check3 = 0;
312 unsigned pos = 0;
313
314 if (*num == 'M')
315 { /* ISMN start with 'M' */
316 check3 = 3;
317 pos = 1;
318 }
319 while (*num && size > 1)
320 {
321 if (isdigit((unsigned char) *num))
322 {
323 if (pos++ % 2)
324 check3 += *num - '0';
325 else
326 check += *num - '0';
327 size--;
328 }
329 num++;
330 }
331 check = (check + 3 * check3) % 10;
332 if (check != 0)
333 check = 10 - check;
334 return check;
335}
336
337/*
338 * ean2isn --- Try to convert an ean13 number to a UPC/ISxN number.
339 * This doesn't verify for a valid check digit.
340 *
341 * If errorOK is false, ereport a useful error message if the ean13 is bad.
342 * If errorOK is true, just return "false" for bad input.
343 */
344static bool
345ean2isn(ean13 ean, bool errorOK, ean13 *result, enum isn_type accept)
346{
347 enum isn_type type = INVALID;
348
349 char buf[MAXEAN13LEN + 1];
350 char *aux;
351 unsigned digval;
352 unsigned search;
353 ean13 ret = ean;
354
355 ean >>= 1;
356 /* verify it's in the EAN13 range */
357 if (ean > UINT64CONST(9999999999999))
358 goto eantoobig;
359
360 /* convert the number */
361 search = 0;
362 aux = buf + 13;
363 *aux = '\0'; /* terminate string; aux points to last digit */
364 do
365 {
366 digval = (unsigned) (ean % 10); /* get the decimal value */
367 ean /= 10; /* get next digit */
368 *--aux = (char) (digval + '0'); /* convert to ascii and store */
369 } while (ean && search++ < 12);
370 while (search++ < 12)
371 *--aux = '0'; /* fill the remaining EAN13 with '0' */
372
373 /* find out the data type: */
374 if (strncmp("978", buf, 3) == 0)
375 { /* ISBN */
376 type = ISBN;
377 }
378 else if (strncmp("977", buf, 3) == 0)
379 { /* ISSN */
380 type = ISSN;
381 }
382 else if (strncmp("9790", buf, 4) == 0)
383 { /* ISMN */
384 type = ISMN;
385 }
386 else if (strncmp("979", buf, 3) == 0)
387 { /* ISBN-13 */
388 type = ISBN;
389 }
390 else if (*buf == '0')
391 { /* UPC */
392 type = UPC;
393 }
394 else
395 {
396 type = EAN13;
397 }
398 if (accept != ANY && accept != EAN13 && accept != type)
399 goto eanwrongtype;
400
401 *result = ret;
402 return true;
403
404eanwrongtype:
405 if (!errorOK)
406 {
407 if (type != EAN13)
408 {
410 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
411 errmsg("cannot cast EAN13(%s) to %s for number: \"%s\"",
413 }
414 else
415 {
417 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
418 errmsg("cannot cast %s to %s for number: \"%s\"",
420 }
421 }
422 return false;
423
424eantoobig:
425 if (!errorOK)
427 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
428 errmsg("value \"%" PRIu64 "\" is out of range for %s type",
429 ean, isn_names[type])));
430 return false;
431}
432
433/*
434 * ean2UPC/ISxN --- Convert in-place a normalized EAN13 string to the corresponding
435 * UPC/ISxN string number. Assumes the input string is normalized.
436 */
437static inline void
438ean2ISBN(char *isn)
439{
440 char *aux;
441 unsigned check;
442
443 /*
444 * The number should come in this format: 978-0-000-00000-0 or may be an
445 * ISBN-13 number, 979-..., which does not have a short representation. Do
446 * the short output version if possible.
447 */
448 if (strncmp("978-", isn, 4) == 0)
449 {
450 /* Strip the first part and calculate the new check digit */
451 hyphenate(isn, isn + 4, NULL, NULL);
452 check = weight_checkdig(isn, 10);
453 aux = strchr(isn, '\0');
454 while (!isdigit((unsigned char) *--aux));
455 if (check == 10)
456 *aux = 'X';
457 else
458 *aux = check + '0';
459 }
460}
461
462static inline void
463ean2ISMN(char *isn)
464{
465 /* the number should come in this format: 979-0-000-00000-0 */
466 /* Just strip the first part and change the first digit ('0') to 'M' */
467 hyphenate(isn, isn + 4, NULL, NULL);
468 isn[0] = 'M';
469}
470
471static inline void
472ean2ISSN(char *isn)
473{
474 unsigned check;
475
476 /* the number should come in this format: 977-0000-000-00-0 */
477 /* Strip the first part, crop, and calculate the new check digit */
478 hyphenate(isn, isn + 4, NULL, NULL);
479 check = weight_checkdig(isn, 8);
480 if (check == 10)
481 isn[8] = 'X';
482 else
483 isn[8] = check + '0';
484 isn[9] = '\0';
485}
486
487static inline void
488ean2UPC(char *isn)
489{
490 /* the number should come in this format: 000-000000000-0 */
491 /* Strip the first part, crop, and dehyphenate */
492 dehyphenate(isn, isn + 1);
493 isn[12] = '\0';
494}
495
496/*
497 * ean2* --- Converts a string of digits into an ean13 number.
498 * Assumes the input string is a string with only digits
499 * on it, and that it's within the range of ean13.
500 *
501 * Returns the ean13 value of the string.
502 */
503static ean13
504str2ean(const char *num)
505{
506 ean13 ean = 0; /* current ean */
507
508 while (*num)
509 {
510 if (isdigit((unsigned char) *num))
511 ean = 10 * ean + (*num - '0');
512 num++;
513 }
514 return (ean << 1); /* also give room to a flag */
515}
516
517/*
518 * ean2string --- Try to convert an ean13 number to a hyphenated string.
519 * Assumes there's enough space in result to hold
520 * the string (maximum MAXEAN13LEN+1 bytes)
521 * This doesn't verify for a valid check digit.
522 *
523 * If shortType is true, the returned string is in the old ISxN short format.
524 * If errorOK is false, ereport a useful error message if the string is bad.
525 * If errorOK is true, just return "false" for bad input.
526 */
527static bool
528ean2string(ean13 ean, bool errorOK, char *result, bool shortType)
529{
530 const char *(*TABLE)[2];
531 const unsigned (*TABLE_index)[2];
532 enum isn_type type = INVALID;
533
534 char *aux;
535 unsigned digval;
536 unsigned search;
537 char valid = '\0'; /* was the number initially written with a
538 * valid check digit? */
539
541
542 if ((ean & 1) != 0)
543 valid = '!';
544 ean >>= 1;
545 /* verify it's in the EAN13 range */
546 if (ean > UINT64CONST(9999999999999))
547 goto eantoobig;
548
549 /* convert the number */
550 search = 0;
551 aux = result + MAXEAN13LEN;
552 *aux = '\0'; /* terminate string; aux points to last digit */
553 *--aux = valid; /* append '!' for numbers with invalid but
554 * corrected check digit */
555 do
556 {
557 digval = (unsigned) (ean % 10); /* get the decimal value */
558 ean /= 10; /* get next digit */
559 *--aux = (char) (digval + '0'); /* convert to ascii and store */
560 if (search == 0)
561 *--aux = '-'; /* the check digit is always there */
562 } while (ean && search++ < 13);
563 while (search++ < 13)
564 *--aux = '0'; /* fill the remaining EAN13 with '0' */
565
566 /* The string should be in this form: ???DDDDDDDDDDDD-D" */
567 search = hyphenate(result, result + 3, EAN13_range, EAN13_index);
568
569 /* verify it's a logically valid EAN13 */
570 if (search == 0)
571 {
572 search = hyphenate(result, result + 3, NULL, NULL);
573 goto okay;
574 }
575
576 /* find out what type of hyphenation is needed: */
577 if (strncmp("978-", result, search) == 0)
578 { /* ISBN -13 978-range */
579 /* The string should be in this form: 978-??000000000-0" */
580 type = ISBN;
581 TABLE = ISBN_range;
583 }
584 else if (strncmp("977-", result, search) == 0)
585 { /* ISSN */
586 /* The string should be in this form: 977-??000000000-0" */
587 type = ISSN;
588 TABLE = ISSN_range;
590 }
591 else if (strncmp("979-0", result, search + 1) == 0)
592 { /* ISMN */
593 /* The string should be in this form: 979-0?000000000-0" */
594 type = ISMN;
595 TABLE = ISMN_range;
597 }
598 else if (strncmp("979-", result, search) == 0)
599 { /* ISBN-13 979-range */
600 /* The string should be in this form: 979-??000000000-0" */
601 type = ISBN;
602 TABLE = ISBN_range_new;
604 }
605 else if (*result == '0')
606 { /* UPC */
607 /* The string should be in this form: 000-00000000000-0" */
608 type = UPC;
609 TABLE = UPC_range;
611 }
612 else
613 {
614 type = EAN13;
615 TABLE = NULL;
616 TABLE_index = NULL;
617 }
618
619 /* verify it's a logically valid EAN13/UPC/ISxN */
620 digval = search;
621 search = hyphenate(result + digval, result + digval + 2, TABLE, TABLE_index);
622
623 /* verify it's a valid EAN13 */
624 if (search == 0)
625 {
626 search = hyphenate(result + digval, result + digval + 2, NULL, NULL);
627 goto okay;
628 }
629
630okay:
631 /* convert to the old short type: */
632 if (shortType)
633 switch (type)
634 {
635 case ISBN:
636 ean2ISBN(result);
637 break;
638 case ISMN:
639 ean2ISMN(result);
640 break;
641 case ISSN:
642 ean2ISSN(result);
643 break;
644 case UPC:
645 ean2UPC(result);
646 break;
647 default:
648 break;
649 }
650 return true;
651
652eantoobig:
653 if (!errorOK)
655 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
656 errmsg("value \"%" PRIu64 "\" is out of range for %s type",
657 ean, isn_names[type])));
658 return false;
659}
660
661/*
662 * string2ean --- try to parse a string into an ean13.
663 *
664 * ereturn false with a useful error message if the string is bad.
665 * Otherwise return true.
666 *
667 * if the input string ends with '!' it will always be treated as invalid
668 * (even if the check digit is valid)
669 */
670static bool
671string2ean(const char *str, struct Node *escontext, ean13 *result,
672 enum isn_type accept)
673{
674 bool digit,
675 last;
676 char buf[17] = " ";
677 char *aux1 = buf + 3; /* leave space for the first part, in case
678 * it's needed */
679 const char *aux2 = str;
680 enum isn_type type = INVALID;
681 unsigned check = 0,
682 rcheck = (unsigned) -1;
683 unsigned length = 0;
684 bool magic = false,
685 valid = true;
686
687 /* recognize and validate the number: */
688 while (*aux2 && length <= 13)
689 {
690 last = (*(aux2 + 1) == '!' || *(aux2 + 1) == '\0'); /* is the last character */
691 digit = (isdigit((unsigned char) *aux2) != 0); /* is current character
692 * a digit? */
693 if (*aux2 == '?' && last) /* automagically calculate check digit if
694 * it's '?' */
695 magic = digit = true;
696 if (length == 0 && (*aux2 == 'M' || *aux2 == 'm'))
697 {
698 /* only ISMN can be here */
699 if (type != INVALID)
700 goto eaninvalid;
701 type = ISMN;
702 *aux1++ = 'M';
703 length++;
704 }
705 else if (length == 7 && (digit || *aux2 == 'X' || *aux2 == 'x') && last)
706 {
707 /* only ISSN can be here */
708 if (type != INVALID)
709 goto eaninvalid;
710 type = ISSN;
711 *aux1++ = pg_ascii_toupper((unsigned char) *aux2);
712 length++;
713 }
714 else if (length == 9 && (digit || *aux2 == 'X' || *aux2 == 'x') && last)
715 {
716 /* only ISBN and ISMN can be here */
717 if (type != INVALID && type != ISMN)
718 goto eaninvalid;
719 if (type == INVALID)
720 type = ISBN; /* ISMN must start with 'M' */
721 *aux1++ = pg_ascii_toupper((unsigned char) *aux2);
722 length++;
723 }
724 else if (length == 11 && digit && last)
725 {
726 /* only UPC can be here */
727 if (type != INVALID)
728 goto eaninvalid;
729 type = UPC;
730 *aux1++ = *aux2;
731 length++;
732 }
733 else if (*aux2 == '-' || *aux2 == ' ')
734 {
735 /* skip, we could validate but I think it's worthless */
736 }
737 else if (*aux2 == '!' && *(aux2 + 1) == '\0')
738 {
739 /* the invalid check digit suffix was found, set it */
740 if (!magic)
741 valid = false;
742 magic = true;
743 }
744 else if (!digit)
745 {
746 goto eaninvalid;
747 }
748 else
749 {
750 *aux1++ = *aux2;
751 if (++length > 13)
752 goto eantoobig;
753 }
754 aux2++;
755 }
756 *aux1 = '\0'; /* terminate the string */
757
758 /* find the current check digit value */
759 if (length == 13)
760 {
761 /* only EAN13 can be here */
762 if (type != INVALID)
763 goto eaninvalid;
764 type = EAN13;
765 check = buf[15] - '0';
766 }
767 else if (length == 12)
768 {
769 /* only UPC can be here */
770 if (type != UPC)
771 goto eaninvalid;
772 check = buf[14] - '0';
773 }
774 else if (length == 10)
775 {
776 if (type != ISBN && type != ISMN)
777 goto eaninvalid;
778 if (buf[12] == 'X')
779 check = 10;
780 else
781 check = buf[12] - '0';
782 }
783 else if (length == 8)
784 {
785 if (type != INVALID && type != ISSN)
786 goto eaninvalid;
787 type = ISSN;
788 if (buf[10] == 'X')
789 check = 10;
790 else
791 check = buf[10] - '0';
792 }
793 else
794 goto eaninvalid;
795
796 if (type == INVALID)
797 goto eaninvalid;
798
799 /* obtain the real check digit value, validate, and convert to ean13: */
800 if (accept == EAN13 && type != accept)
801 goto eanwrongtype;
802 if (accept != ANY && type != EAN13 && type != accept)
803 goto eanwrongtype;
804 switch (type)
805 {
806 case EAN13:
807 valid = (valid && ((rcheck = checkdig(buf + 3, 13)) == check || magic));
808 /* now get the subtype of EAN13: */
809 if (buf[3] == '0')
810 type = UPC;
811 else if (strncmp("977", buf + 3, 3) == 0)
812 type = ISSN;
813 else if (strncmp("978", buf + 3, 3) == 0)
814 type = ISBN;
815 else if (strncmp("9790", buf + 3, 4) == 0)
816 type = ISMN;
817 else if (strncmp("979", buf + 3, 3) == 0)
818 type = ISBN;
819 if (accept != EAN13 && accept != ANY && type != accept)
820 goto eanwrongtype;
821 break;
822 case ISMN:
823 memcpy(buf, "9790", 4); /* this isn't for sure yet, for now ISMN
824 * it's only 9790 */
825 valid = (valid && ((rcheck = checkdig(buf, 13)) == check || magic));
826 break;
827 case ISBN:
828 memcpy(buf, "978", 3);
829 valid = (valid && ((rcheck = weight_checkdig(buf + 3, 10)) == check || magic));
830 break;
831 case ISSN:
832 memcpy(buf + 10, "00", 2); /* append 00 as the normal issue
833 * publication code */
834 memcpy(buf, "977", 3);
835 valid = (valid && ((rcheck = weight_checkdig(buf + 3, 8)) == check || magic));
836 break;
837 case UPC:
838 buf[2] = '0';
839 valid = (valid && ((rcheck = checkdig(buf + 2, 13)) == check || magic));
840 default:
841 break;
842 }
843
844 /* fix the check digit: */
845 for (aux1 = buf; *aux1 && *aux1 <= ' '; aux1++);
846 aux1[12] = checkdig(aux1, 13) + '0';
847 aux1[13] = '\0';
848
849 if (!valid && !magic)
850 goto eanbadcheck;
851
852 *result = str2ean(aux1);
853 *result |= valid ? 0 : 1;
854 return true;
855
856eanbadcheck:
857 if (g_weak)
858 { /* weak input mode is activated: */
859 /* set the "invalid-check-digit-on-input" flag */
860 *result = str2ean(aux1);
861 *result |= 1;
862 return true;
863 }
864
865 if (rcheck == (unsigned) -1)
866 {
867 ereturn(escontext, false,
868 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
869 errmsg("invalid %s number: \"%s\"",
870 isn_names[accept], str)));
871 }
872 else
873 {
874 ereturn(escontext, false,
875 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
876 errmsg("invalid check digit for %s number: \"%s\", should be %c",
877 isn_names[accept], str, (rcheck == 10) ? ('X') : (rcheck + '0'))));
878 }
879
880eaninvalid:
881 ereturn(escontext, false,
882 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
883 errmsg("invalid input syntax for %s number: \"%s\"",
884 isn_names[accept], str)));
885
886eanwrongtype:
887 ereturn(escontext, false,
888 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
889 errmsg("cannot cast %s to %s for number: \"%s\"",
891
892eantoobig:
893 ereturn(escontext, false,
894 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
895 errmsg("value \"%s\" is out of range for %s type",
896 str, isn_names[accept])));
897}
898
899/*----------------------------------------------------------
900 * Exported routines.
901 *---------------------------------------------------------*/
902
903void
905{
906 if (ISN_DEBUG)
907 {
908 if (!check_table(EAN13_range, EAN13_index))
909 elog(ERROR, "EAN13 failed check");
910 if (!check_table(ISBN_range, ISBN_index))
911 elog(ERROR, "ISBN failed check");
912 if (!check_table(ISMN_range, ISMN_index))
913 elog(ERROR, "ISMN failed check");
914 if (!check_table(ISSN_range, ISSN_index))
915 elog(ERROR, "ISSN failed check");
916 if (!check_table(UPC_range, UPC_index))
917 elog(ERROR, "UPC failed check");
918 }
919
920 /* Define a GUC variable for weak mode. */
921 DefineCustomBoolVariable("isn.weak",
922 "Accept input with invalid ISN check digits.",
923 NULL,
924 &g_weak,
925 false,
927 0,
928 NULL,
929 NULL,
930 NULL);
931
933}
934
935/* isn_out
936 */
938Datum
940{
942 char *result;
943 char buf[MAXEAN13LEN + 1];
944
945 (void) ean2string(val, false, buf, true);
946
947 result = pstrdup(buf);
948 PG_RETURN_CSTRING(result);
949}
950
951/* ean13_out
952 */
954Datum
956{
958 char *result;
959 char buf[MAXEAN13LEN + 1];
960
961 (void) ean2string(val, false, buf, false);
962
963 result = pstrdup(buf);
964 PG_RETURN_CSTRING(result);
965}
966
967/* ean13_in
968 */
970Datum
972{
973 const char *str = PG_GETARG_CSTRING(0);
974 ean13 result;
975
976 if (!string2ean(str, fcinfo->context, &result, EAN13))
978 PG_RETURN_EAN13(result);
979}
980
981/* isbn_in
982 */
984Datum
986{
987 const char *str = PG_GETARG_CSTRING(0);
988 ean13 result;
989
990 if (!string2ean(str, fcinfo->context, &result, ISBN))
992 PG_RETURN_EAN13(result);
993}
994
995/* ismn_in
996 */
998Datum
1000{
1001 const char *str = PG_GETARG_CSTRING(0);
1002 ean13 result;
1003
1004 if (!string2ean(str, fcinfo->context, &result, ISMN))
1006 PG_RETURN_EAN13(result);
1007}
1008
1009/* issn_in
1010 */
1012Datum
1014{
1015 const char *str = PG_GETARG_CSTRING(0);
1016 ean13 result;
1017
1018 if (!string2ean(str, fcinfo->context, &result, ISSN))
1020 PG_RETURN_EAN13(result);
1021}
1022
1023/* upc_in
1024 */
1026Datum
1028{
1029 const char *str = PG_GETARG_CSTRING(0);
1030 ean13 result;
1031
1032 if (!string2ean(str, fcinfo->context, &result, UPC))
1034 PG_RETURN_EAN13(result);
1035}
1036
1037/* casting functions
1038*/
1040Datum
1042{
1044 ean13 result;
1045
1046 (void) ean2isn(val, false, &result, ISBN);
1047
1048 PG_RETURN_EAN13(result);
1049}
1050
1052Datum
1054{
1056 ean13 result;
1057
1058 (void) ean2isn(val, false, &result, ISMN);
1059
1060 PG_RETURN_EAN13(result);
1061}
1062
1064Datum
1066{
1068 ean13 result;
1069
1070 (void) ean2isn(val, false, &result, ISSN);
1071
1072 PG_RETURN_EAN13(result);
1073}
1074
1076Datum
1078{
1080 ean13 result;
1081
1082 (void) ean2isn(val, false, &result, UPC);
1083
1084 PG_RETURN_EAN13(result);
1085}
1086
1087
1088/* is_valid - returns false if the "invalid-check-digit-on-input" is set
1089 */
1091Datum
1093{
1095
1096 PG_RETURN_BOOL((val & 1) == 0);
1097}
1098
1099/* make_valid - unsets the "invalid-check-digit-on-input" flag
1100 */
1102Datum
1104{
1106
1107 val &= ~((ean13) 1);
1109}
1110
1111/* this function temporarily sets weak input flag
1112 * (to lose the strictness of check digit acceptance)
1113 */
1115Datum
1117{
1118 bool newvalue = PG_GETARG_BOOL(0);
1119
1120 (void) set_config_option("isn.weak", newvalue ? "on" : "off",
1122 GUC_ACTION_SET, true, 0, false);
1124}
1125
1127Datum
1129{
1131}
static const unsigned EAN13_index[10][2]
Definition: EAN13.h:14
static const char * EAN13_range[][2]
Definition: EAN13.h:26
static const char * ISBN_range[][2]
Definition: ISBN.h:50
static const unsigned ISBN_index[10][2]
Definition: ISBN.h:37
static const unsigned ISBN_index_new[10][2]
Definition: ISBN.h:970
static const char * ISBN_range_new[][2]
Definition: ISBN.h:983
static const char * ISMN_range[][2]
Definition: ISMN.h:45
static const unsigned ISMN_index[10][2]
Definition: ISMN.h:33
static const unsigned ISSN_index[10][2]
Definition: ISSN.h:34
static const char * ISSN_range[][2]
Definition: ISSN.h:46
static const unsigned UPC_index[10][2]
Definition: UPC.h:14
static const char * UPC_range[][2]
Definition: UPC.h:26
#define UINT64CONST(x)
Definition: c.h:567
int errcode(int sqlerrcode)
Definition: elog.c:863
int errmsg(const char *fmt,...)
Definition: elog.c:1080
#define ereturn(context, dummy_value,...)
Definition: elog.h:278
#define DEBUG1
Definition: elog.h:30
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:226
#define ereport(elevel,...)
Definition: elog.h:150
#define PG_RETURN_CSTRING(x)
Definition: fmgr.h:362
#define PG_GETARG_CSTRING(n)
Definition: fmgr.h:277
#define PG_RETURN_NULL()
Definition: fmgr.h:345
#define PG_GETARG_BOOL(n)
Definition: fmgr.h:274
#define PG_FUNCTION_ARGS
Definition: fmgr.h:193
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:359
void DefineCustomBoolVariable(const char *name, const char *short_desc, const char *long_desc, bool *valueAddr, bool bootValue, GucContext context, int flags, GucBoolCheckHook check_hook, GucBoolAssignHook assign_hook, GucShowHook show_hook)
Definition: guc.c:5011
void MarkGUCPrefixReserved(const char *className)
Definition: guc.c:5148
int set_config_option(const char *name, const char *value, GucContext context, GucSource source, GucAction action, bool changeVal, int elevel, bool is_reload)
Definition: guc.c:3216
@ GUC_ACTION_SET
Definition: guc.h:203
@ PGC_S_SESSION
Definition: guc.h:126
@ PGC_USERSET
Definition: guc.h:79
const char * str
long val
Definition: informix.c:689
Datum upc_cast_from_ean13(PG_FUNCTION_ARGS)
Definition: isn.c:1077
int y
Definition: isn.c:76
Datum ean13_out(PG_FUNCTION_ARGS)
Definition: isn.c:955
int b
Definition: isn.c:74
Datum issn_in(PG_FUNCTION_ARGS)
Definition: isn.c:1013
void _PG_init(void)
Definition: isn.c:904
PG_MODULE_MAGIC_EXT(.name="isn",.version=PG_VERSION)
Datum isn_out(PG_FUNCTION_ARGS)
Definition: isn.c:939
static bool string2ean(const char *str, struct Node *escontext, ean13 *result, enum isn_type accept)
Definition: isn.c:671
Datum make_valid(PG_FUNCTION_ARGS)
Definition: isn.c:1103
static void ean2ISBN(char *isn)
Definition: isn.c:438
Datum ismn_in(PG_FUNCTION_ARGS)
Definition: isn.c:999
int x
Definition: isn.c:75
Datum accept_weak_input(PG_FUNCTION_ARGS)
Definition: isn.c:1116
static bool ean2string(ean13 ean, bool errorOK, char *result, bool shortType)
Definition: isn.c:528
const unsigned TABLE_index[10][2]
Definition: isn.c:70
Datum weak_input_status(PG_FUNCTION_ARGS)
Definition: isn.c:1128
Datum upc_in(PG_FUNCTION_ARGS)
Definition: isn.c:1027
int init
Definition: isn.c:79
static bool ean2isn(ean13 ean, bool errorOK, ean13 *result, enum isn_type accept)
Definition: isn.c:345
static const char *const isn_names[]
Definition: isn.c:44
#define ISN_DEBUG
Definition: isn.c:34
Datum isbn_in(PG_FUNCTION_ARGS)
Definition: isn.c:985
Datum issn_cast_from_ean13(PG_FUNCTION_ARGS)
Definition: isn.c:1065
static void ean2UPC(char *isn)
Definition: isn.c:488
static bool g_weak
Definition: isn.c:47
Datum ean13_in(PG_FUNCTION_ARGS)
Definition: isn.c:971
int a
Definition: isn.c:73
Datum ismn_cast_from_ean13(PG_FUNCTION_ARGS)
Definition: isn.c:1053
int j
Definition: isn.c:78
#define MAXEAN13LEN
Definition: isn.c:37
isn_type
Definition: isn.c:40
@ ISBN
Definition: isn.c:41
@ EAN13
Definition: isn.c:41
@ ISMN
Definition: isn.c:41
@ ANY
Definition: isn.c:41
@ UPC
Definition: isn.c:41
@ ISSN
Definition: isn.c:41
@ INVALID
Definition: isn.c:41
Datum isbn_cast_from_ean13(PG_FUNCTION_ARGS)
Definition: isn.c:1041
static unsigned hyphenate(char *bufO, char *bufI, const char *(*TABLE)[2], const unsigned TABLE_index[10][2])
Definition: isn.c:172
static unsigned checkdig(char *num, unsigned size)
Definition: isn.c:308
int i
Definition: isn.c:77
static void ean2ISMN(char *isn)
Definition: isn.c:463
Datum is_valid(PG_FUNCTION_ARGS)
Definition: isn.c:1092
PG_FUNCTION_INFO_V1(isn_out)
static unsigned dehyphenate(char *bufO, char *bufI)
Definition: isn.c:147
static unsigned weight_checkdig(char *isn, unsigned size)
Definition: isn.c:282
static ean13 str2ean(const char *num)
Definition: isn.c:504
pg_attribute_unused() static bool check_table(const char *(*TABLE)[2]
static void ean2ISSN(char *isn)
Definition: isn.c:472
#define PG_GETARG_EAN13(n)
Definition: isn.h:27
uint64 ean13
Definition: isn.h:25
#define PG_RETURN_EAN13(x)
Definition: isn.h:28
char * pstrdup(const char *in)
Definition: mcxt.c:1759
Datum lower(PG_FUNCTION_ARGS)
Definition: oracle_compat.c:49
Datum upper(PG_FUNCTION_ARGS)
Definition: oracle_compat.c:80
static char buf[DEFAULT_XLOG_SEG_SIZE]
Definition: pg_test_fsync.c:71
static unsigned char pg_ascii_toupper(unsigned char ch)
Definition: port.h:177
uint64_t Datum
Definition: postgres.h:70
Definition: nodes.h:135
const char * type
const char * name
#define accept(s, addr, addrlen)
Definition: win32_port.h:498