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