-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.
OF ANY KIND, EXPRESS OR IMPLIED.
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-Content of the Module
+-- Content of the Module
-------------------------------------------------
This directory contains definitions for a few PostgreSQL
(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!
+ 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');
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 );
select isbn13(id) from test;
-Contact
+-- Contact
-------------------------------------------------
Please suggestions or bug reports to kronuz at users.sourceforge.net
* 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 $
*
*-------------------------------------------------------------------------
*/
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;
/***********************************************************************
**
- ** 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.
***********************************************************************/
}
/*
- * 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)
/*
- * 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)
}
/*
- * 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)
{
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 */
}
* 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.
*/
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" */
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);
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;
}
/* 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')) {
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,
}
}
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)
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:
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)
-- 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 );
-- 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;
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;