From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Sun, 10 Sep 2006 20:45:17 +0000 (+0000)
Subject: contrib/isn updates from Jeremy Kronuz.
X-Git-Tag: REL8_2_BETA1~112
X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=5e08e49637f68c767b45c0d4091dbb3a8e72b694;p=postgresql

contrib/isn updates from Jeremy Kronuz.
---

diff --git a/contrib/isn/README.isn b/contrib/isn/README.isn
index ef249582ba..22154266f0 100644
--- a/contrib/isn/README.isn
+++ b/contrib/isn/README.isn
@@ -1,6 +1,6 @@
 
-EAN13 - UPC - ISBN (books) - ISMN (music) - ISSN (serials)
-----------------------------------------------------------
+-- EAN13 - UPC - ISBN (books) - ISMN (music) - ISSN (serials)
+-------------------------------------------------------------
 
 Copyright Germán Méndez Bravo (Kronuz), 2004 - 2006
 This module is released under the same BSD license as the rest of PostgreSQL.
@@ -24,7 +24,7 @@ THIS MODULE IS PROVIDED "AS IS" AND WITHOUT ANY WARRANTY
         OF ANY KIND, EXPRESS OR IMPLIED.
 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
-Content of the Module
+-- Content of the Module
 -------------------------------------------------
 
 This directory contains definitions for a few PostgreSQL
@@ -143,7 +143,7 @@ On success:
 
 (on failure, the functions 'ereport' the error)
 
-Testing/Playing Functions
+-- Testing/Playing Functions
 -------------------------------------------------
 isn_weak(boolean) - Sets the weak input mode. 
 This function is intended for testing use only!
@@ -173,7 +173,7 @@ To work with invalid numbers, you can use two functions:
   + make_valid(), which validates an invalid number (deleting the invalid flag)
   + is_valid(), which checks for the invalid flag presence.
 
-Examples of Use
+-- Examples of Use
 -------------------------------------------------
 --Using the types directly:
 	select isbn('978-0-393-04002-9');
@@ -181,9 +181,12 @@ Examples of Use
 	select issn('1436-4522');
 
 --Casting types:
--- note that you can't cast from ean13 to other type, thus the following
--- will NOT work: select upc(ean13('0220356483481'));
-	select ean13(upc('220356483481'));
+-- note that you can only cast from ean13 to other type when the casted 
+-- number would be valid in the realm of the casted type; 
+-- thus, the following will NOT work: select isbn(ean13('0220356483481')); 
+-- but these will: 
+    select upc(ean13('0220356483481')); 
+    select ean13(upc('220356483481')); 
 
 --Create a table with a single column to hold ISBN numbers:
 	create table test ( id isbn );
@@ -210,7 +213,7 @@ Examples of Use
 
   select isbn13(id) from test;
 
-Contact
+-- Contact
 -------------------------------------------------
 Please suggestions or bug reports to kronuz at users.sourceforge.net
 
diff --git a/contrib/isn/isn.c b/contrib/isn/isn.c
index 4c4aabb9f8..f403dca845 100644
--- a/contrib/isn/isn.c
+++ b/contrib/isn/isn.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/contrib/isn/isn.c,v 1.1 2006/09/09 04:07:52 tgl Exp $
+ *	  $PostgreSQL: pgsql/contrib/isn/isn.c,v 1.2 2006/09/10 20:45:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -31,7 +31,7 @@ PG_MODULE_MAGIC;
 
 enum isn_type { INVALID, ANY, EAN13, ISBN, ISMN, ISSN, UPC };
 
-static const char *isn_names[] = { "ISN", "ISN", "EAN13", "ISBN", "ISMN", "ISSN", "UPC" };
+static const char *isn_names[] = { "EAN13/UPC/ISxN", "EAN13/UPC/ISxN", "EAN13", "ISBN", "ISMN", "ISSN", "UPC" };
 
 static bool g_weak = false;
 static bool g_initialized = false;
@@ -43,11 +43,11 @@ static bool g_initialized = false;
 
 /***********************************************************************
  **
- **		Routines for ISNs.
+ **		Routines for EAN13/UPC/ISxNs.
  **
  ** Note:
  **  In this code, a normalized string is one that is known to be a valid 
- **  ISN number containing only digits and hyphens and with enough space 
+ **  ISxN number containing only digits and hyphens and with enough space 
  **  to hold the full 13 digits plus the maximum of four hyphens.
  ***********************************************************************/
 
@@ -217,7 +217,7 @@ unsigned hyphenate(char *bufO, char *bufI, const char *(*TABLE)[2], const unsign
 }
 
 /*
- * weight_checkdig -- Receives a buffer with a normalized ISN string number, 
+ * weight_checkdig -- Receives a buffer with a normalized ISxN string number, 
  *                     and the length to weight.
  *
  * Returns the weight of the number (the check digit value, 0-10)
@@ -239,7 +239,7 @@ unsigned weight_checkdig(char *isn, unsigned size)
 
 
 /*
- * checkdig --- Receives a buffer with a normalized ISN string number, 
+ * checkdig --- Receives a buffer with a normalized ISxN string number, 
  *               and the length to check.
  *
  * Returns the check digit value (0-9)
@@ -267,8 +267,94 @@ unsigned checkdig(char *num, unsigned size)
 }
 
 /*
- * ean2isn --- Convert in-place a normalized EAN13 string to the corresponding 
- *            ISN string number. Assumes the input string is normalized.
+ * ean2isn --- Try to convert an ean13 number to a UPC/ISxN number.
+ *             This doesn't verify for a valid check digit.
+ *
+ * If errorOK is false, ereport a useful error message if the ean13 is bad.
+ * If errorOK is true, just return "false" for bad input.
+ */
+static
+bool ean2isn(ean13 ean, bool errorOK, ean13 *result, enum isn_type accept)
+{
+	enum isn_type type = INVALID;
+
+	char buf[MAXEAN13LEN + 1];
+	char *firstdig, *aux;
+	unsigned digval;
+	unsigned search;
+	ean13 ret = ean;
+	
+	ean >>= 1;
+	/* verify it's in the EAN13 range */
+	if(ean > UINT64CONST(9999999999999))
+		goto eantoobig;
+
+	/* convert the number */
+	search = 0;
+	firstdig = aux = buf + 13;
+	*aux = '\0';	    /* terminate string; aux points to last digit */
+	do {
+	    digval = (unsigned)(ean % 10);		/* get the decimal value */ 
+	    ean /= 10;												/* get next digit */
+		*--aux = (char)(digval + '0');			/* convert to ascii and store */
+	} while(ean && search++<12);
+	while(search++<12) *--aux = '0';			/* fill the remaining EAN13 with '0' */
+	
+	/* find out the data type: */
+	if(!strncmp("978", buf, 3)) { /* ISBN */
+		type = ISBN;
+	} else if(!strncmp("977", buf, 3)) { /* ISSN */
+		type = ISSN;
+	} else if(!strncmp("9790", buf, 4)) { /* ISMN */
+		type = ISMN;
+	} else if(!strncmp("979", buf, 3)) { /* ISBN-13 */
+		type = ISBN;
+	} else if(*buf == '0') { /* UPC */
+		type = UPC;
+	} else {
+		type = EAN13;
+	}
+	if(accept != ANY && accept != EAN13 && accept != type) goto eanwrongtype;
+
+	*result = ret;
+	return true;
+	
+eanwrongtype:
+	if(!errorOK) {
+		if(type!=EAN13) {
+			ereport(ERROR,
+				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+				 errmsg("cannot cast EAN13(%s) to %s for number: \"%s\"",
+						isn_names[type], isn_names[accept], buf)));
+		} else {
+			ereport(ERROR,
+				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+				 errmsg("cannot cast %s to %s for number: \"%s\"",
+						isn_names[type], isn_names[accept], buf)));
+		}
+	}
+	return false;
+
+eantoobig:
+	if(!errorOK) {
+		char	eanbuf[64];
+
+		/*
+		 * Format the number separately to keep the machine-dependent
+		 * format code out of the translatable message text
+		 */
+		snprintf(eanbuf, sizeof(eanbuf), EAN13_FORMAT, ean);
+		ereport(ERROR,
+				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+				 errmsg("value \"%s\" is out of range for %s type",
+						eanbuf, isn_names[type])));
+	}
+	return false;
+}
+
+/*
+ * ean2UPC/ISxN --- Convert in-place a normalized EAN13 string to the corresponding 
+ *                  UPC/ISxN string number. Assumes the input string is normalized.
  */
 static inline
 void ean2ISBN(char *isn)
@@ -325,7 +411,8 @@ ean13 str2ean(const char *num)
 {
 	ean13 ean = 0;	/* current ean */
 	while(*num) {
-		ean = 10 * ean + ((*num++) - '0');
+		if(isdigit(*num)) ean = 10 * ean + (*num - '0');
+		num++;
 	}
     return (ean<<1); /* also give room to a flag */
 }
@@ -336,7 +423,7 @@ ean13 str2ean(const char *num)
  *                the string (maximum MAXEAN13LEN+1 bytes)
  *                This doesn't verify for a valid check digit.
  *
- * If shortType is true, the returned string is in the old ISN short format.
+ * If shortType is true, the returned string is in the old ISxN short format.
  * If errorOK is false, ereport a useful error message if the string is bad.
  * If errorOK is true, just return "false" for bad input.
  */
@@ -369,8 +456,8 @@ bool ean2string(ean13 ean, bool errorOK, char *result, bool shortType)
 	    digval = (unsigned)(ean % 10);		/* get the decimal value */ 
 	    ean /= 10;												/* get next digit */
 		*--aux = (char)(digval + '0');			/* convert to ascii and store */
-		if(++search == 1) *--aux = '-';			/* the check digit is always there */
-	} while(ean);
+		if(search == 0) *--aux = '-';			/* the check digit is always there */
+	} while(ean && search++<13);
 	while(search++<13) *--aux = '0';			/* fill the remaining EAN13 with '0' */
 
 	/* The string should be in this form: ???DDDDDDDDDDDD-D" */
@@ -409,7 +496,7 @@ bool ean2string(ean13 ean, bool errorOK, char *result, bool shortType)
 		TABLE_index = NULL;
 	}
 
-	/* verify it's a logically valid EAN13/ISN */
+	/* verify it's a logically valid EAN13/UPC/ISxN */
 	digval = search;
 	search = hyphenate(result+digval, result+digval+2, TABLE, TABLE_index);
 
@@ -452,8 +539,8 @@ eantoobig:
 		snprintf(eanbuf, sizeof(eanbuf), EAN13_FORMAT, ean);
 		ereport(ERROR,
 				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
-				 errmsg("value \"%s\" is out of range for ISN type",
-						eanbuf)));
+				 errmsg("value \"%s\" is out of range for %s type",
+						eanbuf, isn_names[type])));
 	}
 	return false;
 }
@@ -483,7 +570,7 @@ bool string2ean(const char *str, bool errorOK, ean13 *result,
 	/* recognize and validate the number: */
 	while(*aux2 && length <= 13) {
 		last = (*(aux2+1) == '!' || *(aux2+1) == '\0'); /* is the last character */
-		digit = isdigit(*aux2); /* is current character a digit? */
+		digit = (isdigit(*aux2)!=0); /* is current character a digit? */
 		if(*aux2=='?' && last) /* automagically calculate check digit if it's '?' */
 			magic = digit = true;
 		if(length == 0 &&  (*aux2=='M' || *aux2=='m')) {
@@ -583,19 +670,25 @@ bool string2ean(const char *str, bool errorOK, ean13 *result,
 			break;
 	}
 
-	if(!valid && !magic) goto eanbadcheck;
-			
+  /* fix the check digit: */
 	for(aux1 = buf; *aux1 && *aux1 <= ' '; aux1++);
 	aux1[12] = checkdig(aux1, 13) + '0';
 	aux1[13] = '\0';
 	
+	if(!valid && !magic) goto eanbadcheck;
+
 	*result = str2ean(aux1);
 	*result |= valid?0:1;
-
 	return true;
 
 eanbadcheck: 
-	if(!g_weak) {
+	if(g_weak) { /* weak input mode is activated: */
+	  /* set the "invalid-check-digit-on-input" flag */ 
+		*result = str2ean(aux1);
+		*result |= 1;
+	return true;
+	}
+
 		if(!errorOK) {
 			if(rcheck == (unsigned)-1) {
 				ereport(ERROR,
@@ -610,30 +703,6 @@ eanbadcheck:
 			}
 		}
 		return false;
-	}
-
-	if(accept != EAN13 && accept != ANY && type != accept) goto eanwrongtype;
-
-	/* fix the check digit: */
-	for(aux1 = buf; *aux1 && *aux1 <= ' '; aux1++);
-	aux1[12] = checkdig(aux1, 13) + '0';
-	aux1[13] = '\0';
-	*result = str2ean(aux1);
-
-	 /* set the "invalid-check-digit-on-input" flag */ 
-	*result |= 1;
-
-	/* just warn about the error when there was a real check digit error: */
-	if(check != rcheck) {
-		if(rcheck == (unsigned)-1) {
-			elog(WARNING, "invalid %s number: \"%s\"",
-				isn_names[accept], str);
-		} else {
-			elog(WARNING, "invalid check digit for %s number: \"%s\", should be %c",
-				isn_names[accept], str, (rcheck==10)?('X'):(rcheck+'0'));
-		}
-	}
-	return true;
 
 eaninvalid:
 	if(!errorOK)
@@ -647,8 +716,8 @@ eanwrongtype:
 	if(!errorOK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-				 errmsg("invalid %s type for number: \"%s\"",
-						isn_names[accept], str)));
+				 errmsg("cannot cast %s to %s for number: \"%s\"",
+						isn_names[type], isn_names[accept], str)));
 	return false;
 
 eantoobig:
@@ -804,6 +873,55 @@ isn_cast_to_text(PG_FUNCTION_ARGS)
     PG_RETURN_TEXT_P(GET_TEXT(buf));
 }
 
+PG_FUNCTION_INFO_V1(isbn_cast_from_ean13);
+Datum
+isbn_cast_from_ean13(PG_FUNCTION_ARGS)
+{
+	ean13		val = PG_GETARG_EAN13(0);
+	ean13		result;
+
+	(void) ean2isn(val, false, &result, ISBN);
+
+	PG_RETURN_EAN13(result);
+}
+
+PG_FUNCTION_INFO_V1(ismn_cast_from_ean13);
+Datum
+ismn_cast_from_ean13(PG_FUNCTION_ARGS)
+{
+	ean13		val = PG_GETARG_EAN13(0);
+	ean13		result;
+
+	(void) ean2isn(val, false, &result, ISMN);
+
+	PG_RETURN_EAN13(result);
+}
+
+PG_FUNCTION_INFO_V1(issn_cast_from_ean13);
+Datum
+issn_cast_from_ean13(PG_FUNCTION_ARGS)
+{
+	ean13		val = PG_GETARG_EAN13(0);
+	ean13		result;
+
+	(void) ean2isn(val, false, &result, ISSN);
+
+	PG_RETURN_EAN13(result);
+}
+
+PG_FUNCTION_INFO_V1(upc_cast_from_ean13);
+Datum
+upc_cast_from_ean13(PG_FUNCTION_ARGS)
+{
+	ean13		val = PG_GETARG_EAN13(0);
+	ean13		result;
+
+	(void) ean2isn(val, false, &result, UPC);
+
+	PG_RETURN_EAN13(result);
+}
+
+
 PG_FUNCTION_INFO_V1(ean13_cast_from_text);
 Datum
 ean13_cast_from_text(PG_FUNCTION_ARGS)
diff --git a/contrib/isn/isn.h b/contrib/isn/isn.h
index d4369830d9..7581f884d5 100644
--- a/contrib/isn/isn.h
+++ b/contrib/isn/isn.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/contrib/isn/isn.h,v 1.1 2006/09/09 04:07:52 tgl Exp $
+ *	  $PostgreSQL: pgsql/contrib/isn/isn.h,v 1.2 2006/09/10 20:45:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -27,8 +27,8 @@ typedef uint64 ean13;
 
 #define EAN13_FORMAT UINT64_FORMAT
 
-#define PG_GETARG_EAN13(n) PG_GETARG_INT64(n)
-#define PG_RETURN_EAN13(x) PG_RETURN_INT64(x)
+#define PG_GETARG_EAN13(n) PG_GETARG_INT64((int64)n)
+#define PG_RETURN_EAN13(x) PG_RETURN_INT64((int64)x)
 
 extern Datum isn_out(PG_FUNCTION_ARGS);
 extern Datum ean13_out(PG_FUNCTION_ARGS);
@@ -45,6 +45,10 @@ extern Datum isbn_cast_from_text(PG_FUNCTION_ARGS);
 extern Datum ismn_cast_from_text(PG_FUNCTION_ARGS);
 extern Datum issn_cast_from_text(PG_FUNCTION_ARGS);
 extern Datum upc_cast_from_text(PG_FUNCTION_ARGS);
+extern Datum isbn_cast_from_ean13(PG_FUNCTION_ARGS);
+extern Datum ismn_cast_from_ean13(PG_FUNCTION_ARGS);
+extern Datum issn_cast_from_ean13(PG_FUNCTION_ARGS);
+extern Datum upc_cast_from_ean13(PG_FUNCTION_ARGS);
 
 extern Datum is_valid(PG_FUNCTION_ARGS);
 extern Datum make_valid(PG_FUNCTION_ARGS);
diff --git a/contrib/isn/isn.sql.in b/contrib/isn/isn.sql.in
index d5ac63d135..891e98eeb3 100644
--- a/contrib/isn/isn.sql.in
+++ b/contrib/isn/isn.sql.in
@@ -2,7 +2,7 @@
 --	PostgreSQL code for ISNs (ISBN, ISMN, ISSN, EAN13, UPC)
 --  Copyright (c) 2004-2006, Germán Méndez Bravo (Kronuz)
 --
---	$PostgreSQL: pgsql/contrib/isn/isn.sql.in,v 1.1 2006/09/09 04:07:52 tgl Exp $
+--	$PostgreSQL: pgsql/contrib/isn/isn.sql.in,v 1.2 2006/09/10 20:45:17 tgl Exp $
 --
 -- Example:
 --   create table test ( id isbn );
@@ -2131,86 +2131,111 @@ CREATE OPERATOR CLASS upc_ops DEFAULT
 -- Type casts:
 --
 ---------------------------------------------------
+CREATE FUNCTION isbn13(ean13)
+RETURNS isbn13
+AS 'MODULE_PATHNAME', 'isbn_cast_from_ean13'
+LANGUAGE 'C' IMMUTABLE STRICT;
+CREATE FUNCTION ismn13(ean13)
+RETURNS ismn13
+AS 'MODULE_PATHNAME', 'ismn_cast_from_ean13'
+LANGUAGE 'C' IMMUTABLE STRICT;
+CREATE FUNCTION issn13(ean13)
+RETURNS issn13
+AS 'MODULE_PATHNAME', 'issn_cast_from_ean13'
+LANGUAGE 'C' IMMUTABLE STRICT;
+CREATE FUNCTION isbn(ean13)
+RETURNS isbn
+AS 'MODULE_PATHNAME', 'isbn_cast_from_ean13'
+LANGUAGE 'C' IMMUTABLE STRICT;
+CREATE FUNCTION ismn(ean13)
+RETURNS ismn
+AS 'MODULE_PATHNAME', 'ismn_cast_from_ean13'
+LANGUAGE 'C' IMMUTABLE STRICT;
+CREATE FUNCTION issn(ean13)
+RETURNS issn
+AS 'MODULE_PATHNAME', 'issn_cast_from_ean13'
+LANGUAGE 'C' IMMUTABLE STRICT;
+CREATE FUNCTION upc(ean13)
+RETURNS upc
+AS 'MODULE_PATHNAME', 'upc_cast_from_ean13'
+LANGUAGE 'C' IMMUTABLE STRICT;
+
+
 CREATE FUNCTION ean13(text)
 RETURNS ean13
 AS 'MODULE_PATHNAME', 'ean13_cast_from_text'
 LANGUAGE 'C' IMMUTABLE STRICT;
-
 CREATE FUNCTION isbn13(text)
 RETURNS isbn13
 AS 'MODULE_PATHNAME', 'isbn_cast_from_text'
 LANGUAGE 'C' IMMUTABLE STRICT;
-
 CREATE FUNCTION ismn13(text)
 RETURNS ismn13
 AS 'MODULE_PATHNAME', 'ismn_cast_from_text'
 LANGUAGE 'C' IMMUTABLE STRICT;
-
 CREATE FUNCTION issn13(text)
 RETURNS issn13
 AS 'MODULE_PATHNAME', 'issn_cast_from_text'
 LANGUAGE 'C' IMMUTABLE STRICT;
-
 CREATE FUNCTION isbn(text)
 RETURNS isbn
 AS 'MODULE_PATHNAME', 'isbn_cast_from_text'
 LANGUAGE 'C' IMMUTABLE STRICT;
-
 CREATE FUNCTION ismn(text)
 RETURNS ismn
 AS 'MODULE_PATHNAME', 'ismn_cast_from_text'
 LANGUAGE 'C' IMMUTABLE STRICT;
-
 CREATE FUNCTION issn(text)
 RETURNS issn
 AS 'MODULE_PATHNAME', 'issn_cast_from_text'
 LANGUAGE 'C' IMMUTABLE STRICT;
-
 CREATE FUNCTION upc(text)
 RETURNS upc
 AS 'MODULE_PATHNAME', 'upc_cast_from_text'
 LANGUAGE 'C' IMMUTABLE STRICT;
 
+
 CREATE FUNCTION text(ean13)
 RETURNS text
 AS 'MODULE_PATHNAME', 'ean13_cast_to_text'
 LANGUAGE 'C' IMMUTABLE STRICT;
-
 CREATE FUNCTION text(isbn13)
 RETURNS text
 AS 'MODULE_PATHNAME', 'ean13_cast_to_text'
 LANGUAGE 'C' IMMUTABLE STRICT;
-
 CREATE FUNCTION text(ismn13)
 RETURNS text
 AS 'MODULE_PATHNAME', 'ean13_cast_to_text'
 LANGUAGE 'C' IMMUTABLE STRICT;
-
 CREATE FUNCTION text(issn13)
 RETURNS text
 AS 'MODULE_PATHNAME', 'ean13_cast_to_text'
 LANGUAGE 'C' IMMUTABLE STRICT;
-
 CREATE FUNCTION text(isbn)
 RETURNS text
 AS 'MODULE_PATHNAME', 'isn_cast_to_text'
 LANGUAGE 'C' IMMUTABLE STRICT;
-
 CREATE FUNCTION text(ismn)
 RETURNS text
 AS 'MODULE_PATHNAME', 'isn_cast_to_text'
 LANGUAGE 'C' IMMUTABLE STRICT;
-
 CREATE FUNCTION text(issn)
 RETURNS text
 AS 'MODULE_PATHNAME', 'isn_cast_to_text'
 LANGUAGE 'C' IMMUTABLE STRICT;
-
 CREATE FUNCTION text(upc)
 RETURNS text
 AS 'MODULE_PATHNAME', 'isn_cast_to_text'
 LANGUAGE 'C' IMMUTABLE STRICT;
 
+CREATE CAST (ean13 AS isbn13) WITH FUNCTION isbn13(ean13);
+CREATE CAST (ean13 AS isbn) WITH FUNCTION isbn(ean13);
+CREATE CAST (ean13 AS ismn13) WITH FUNCTION ismn13(ean13);
+CREATE CAST (ean13 AS ismn) WITH FUNCTION ismn(ean13);
+CREATE CAST (ean13 AS issn13) WITH FUNCTION issn13(ean13);
+CREATE CAST (ean13 AS issn) WITH FUNCTION issn(ean13);
+CREATE CAST (ean13 AS upc) WITH FUNCTION upc(ean13);
+
 CREATE CAST (isbn13 AS ean13) WITHOUT FUNCTION AS ASSIGNMENT;
 CREATE CAST (isbn AS ean13) WITHOUT FUNCTION AS ASSIGNMENT;
 CREATE CAST (ismn13 AS ean13) WITHOUT FUNCTION AS ASSIGNMENT;
@@ -2218,6 +2243,7 @@ CREATE CAST (ismn AS ean13) WITHOUT FUNCTION AS ASSIGNMENT;
 CREATE CAST (issn13 AS ean13) WITHOUT FUNCTION AS ASSIGNMENT;
 CREATE CAST (issn AS ean13) WITHOUT FUNCTION AS ASSIGNMENT;
 CREATE CAST (upc AS ean13) WITHOUT FUNCTION AS ASSIGNMENT;
+
 CREATE CAST (isbn AS isbn13) WITHOUT FUNCTION AS ASSIGNMENT;
 CREATE CAST (isbn13 AS isbn) WITHOUT FUNCTION AS ASSIGNMENT;
 CREATE CAST (ismn AS ismn13) WITHOUT FUNCTION AS ASSIGNMENT;