gtin14.patch

text/plain

Filename: gtin14.patch
Type: text/plain
Part: 0
Message: GTIN14 support for contrib/isn

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: unified
File+
expected/isn.out 75 1
isn--1.1.sql 153 0
isn.c 258 7
sql/isn.sql 29 0
diff --git a/expected/isn.out b/expected/isn.out
index 18fe37a..b313641 100644
--- a/expected/isn.out
+++ b/expected/isn.out
@@ -18,6 +18,8 @@ INFO:  operator family "isn_ops" of access method btree is missing cross-type op
 INFO:  operator family "isn_ops" of access method btree is missing cross-type operator(s)
 INFO:  operator family "isn_ops" of access method btree is missing cross-type operator(s)
 INFO:  operator family "isn_ops" of access method btree is missing cross-type operator(s)
+INFO:  operator family "isn_ops" of access method btree is missing cross-type operator(s)
+INFO:  operator family "isn_ops" of access method hash is missing cross-type operator(s)
 INFO:  operator family "isn_ops" of access method hash is missing cross-type operator(s)
 INFO:  operator family "isn_ops" of access method hash is missing cross-type operator(s)
 INFO:  operator family "isn_ops" of access method hash is missing cross-type operator(s)
@@ -29,6 +31,7 @@ INFO:  operator family "isn_ops" of access method hash is missing cross-type ope
  amname |  opcname   
 --------+------------
  btree  | ean13_ops
+ btree  | gtin14_ops
  btree  | isbn13_ops
  btree  | isbn_ops
  btree  | ismn13_ops
@@ -37,6 +40,7 @@ INFO:  operator family "isn_ops" of access method hash is missing cross-type ope
  btree  | issn_ops
  btree  | upc_ops
  hash   | ean13_ops
+ hash   | gtin14_ops
  hash   | isbn13_ops
  hash   | isbn_ops
  hash   | ismn13_ops
@@ -44,11 +48,20 @@ INFO:  operator family "isn_ops" of access method hash is missing cross-type ope
  hash   | issn13_ops
  hash   | issn_ops
  hash   | upc_ops
-(16 rows)
+(18 rows)
 
 --
 -- test valid conversions
 --
+SELECT '1234567890128'::GTIN14, -- EAN is a GTIN14
+       '123456789012?'::GTIN14, -- compute check digit for me
+       '11234567890125'::GTIN14,
+       '01234567890128'::GTIN14;
+     gtin14      |     gtin14      |     gtin14      |     gtin14      
+-----------------+-----------------+-----------------+-----------------
+ 0123456789012-8 | 0123456789012-8 | 1123456789012-5 | 0123456789012-8
+(1 row)
+
 SELECT '9780123456786'::EAN13, -- old book
        '9790123456785'::EAN13, -- music
        '9791234567896'::EAN13, -- new book
@@ -249,6 +262,67 @@ SELECT 9780123456786::ISBN;
 ERROR:  cannot cast type bigint to isbn
 LINE 1: SELECT 9780123456786::ISBN;
                             ^
+SELECT '91234567890125'::GTIN14; -- invalid indicator
+ERROR:  Indicator digit out of range for GTIN14 number: "91234567890125"
+LINE 1: SELECT '91234567890125'::GTIN14;
+               ^
+SELECT '123456789012'::GTIN14; -- too short
+ERROR:  invalid input syntax for GTIN14 number: "123456789012"
+LINE 1: SELECT '123456789012'::GTIN14;
+               ^
+SELECT '1234567890127'::GTIN14; -- wrong checkdigit
+ERROR:  invalid check digit for GTIN14 number: "1234567890127", should be 8
+LINE 1: SELECT '1234567890127'::GTIN14;
+               ^
+--
+-- test validity helpers
+--
+SELECT make_valid('1234567890120!'::GTIN14); -- EAN-13
+   make_valid    
+-----------------
+ 0123456789012-8
+(1 row)
+
+SELECT make_valid('11234567890120!'::GTIN14); -- GTIN-14
+   make_valid    
+-----------------
+ 1123456789012-5
+(1 row)
+
+SELECT is_valid(make_valid('1234567890120!'::GTIN14)); -- EAN-13
+ is_valid 
+----------
+ t
+(1 row)
+
+SELECT is_valid(make_valid('11234567890120!'::GTIN14)); -- GTIN-14
+ is_valid 
+----------
+ t
+(1 row)
+
+CREATE TABLE gtin_valid (gtin GTIN14 NOT NULL);
+INSERT INTO gtin_valid VALUES
+-- all invalid because of ! marking
+	('1234567890120!'), -- invalid EAN-13
+	('1234567890128!'), -- valid EAN-13
+	('11234567890120!'), -- invalid GTIN-14
+	('11234567890125!'), -- valid GTIN-14
+-- valid
+	('1234567890128'::GTIN14), -- valid EAN-13
+	('11234567890125'::GTIN14); -- valid GTIN-14
+SELECT gtin, is_valid(gtin) FROM gtin_valid;
+       gtin       | is_valid 
+------------------+----------
+ 0123456789012-8! | f
+ 0123456789012-8! | f
+ 1123456789012-5! | f
+ 1123456789012-5! | f
+ 0123456789012-8  | t
+ 1123456789012-5  | t
+(6 rows)
+
+DROP TABLE gtin_valid;
 --
 -- test some comparisons, must yield true
 --
diff --git a/isn--1.1.sql b/isn--1.1.sql
index 5206961..fd3f157 100644
--- a/isn--1.1.sql
+++ b/isn--1.1.sql
@@ -15,6 +15,26 @@
 --	Input and output functions and data types:
 --
 ---------------------------------------------------
+CREATE FUNCTION gtin14_in(cstring)
+	RETURNS gtin14
+	AS 'MODULE_PATHNAME'
+	LANGUAGE C
+	IMMUTABLE STRICT
+	PARALLEL SAFE;
+CREATE FUNCTION gtin14_out(gtin14)
+	RETURNS cstring
+	AS 'MODULE_PATHNAME'
+	LANGUAGE C
+	IMMUTABLE STRICT
+	PARALLEL SAFE;
+CREATE TYPE gtin14 (
+	INPUT = gtin14_in,
+	OUTPUT = gtin14_out,
+	LIKE = pg_catalog.int8
+);
+COMMENT ON TYPE gtin14
+	IS 'Global Trade Item Number (GTIN-14)';
+
 CREATE FUNCTION ean13_in(cstring)
 	RETURNS ean13
 	AS 'MODULE_PATHNAME'
@@ -181,6 +201,44 @@ COMMENT ON TYPE upc
 -- Operator functions:
 --
 ---------------------------------------------------
+-- GTIN-14:
+CREATE FUNCTION isnlt(gtin14, gtin14)
+	RETURNS boolean
+	AS 'int8lt'
+	LANGUAGE 'internal'
+	IMMUTABLE STRICT
+	PARALLEL SAFE;
+CREATE FUNCTION isnle(gtin14, gtin14)
+	RETURNS boolean
+	AS 'int8le'
+	LANGUAGE 'internal'
+	IMMUTABLE STRICT
+	PARALLEL SAFE;
+CREATE FUNCTION isneq(gtin14, gtin14)
+	RETURNS boolean
+	AS 'int8eq'
+	LANGUAGE 'internal'
+	IMMUTABLE STRICT
+	PARALLEL SAFE;
+CREATE FUNCTION isnge(gtin14, gtin14)
+	RETURNS boolean
+	AS 'int8ge'
+	LANGUAGE 'internal'
+	IMMUTABLE STRICT
+	PARALLEL SAFE;
+CREATE FUNCTION isngt(gtin14, gtin14)
+	RETURNS boolean
+	AS 'int8gt'
+	LANGUAGE 'internal'
+	IMMUTABLE STRICT
+	PARALLEL SAFE;
+CREATE FUNCTION isnne(gtin14, gtin14)
+	RETURNS boolean
+	AS 'int8ne'
+	LANGUAGE 'internal'
+	IMMUTABLE STRICT
+	PARALLEL SAFE;
+
 -- EAN13:
 CREATE FUNCTION isnlt(ean13, ean13)
 	RETURNS boolean
@@ -1236,6 +1294,61 @@ CREATE FUNCTION isnne(upc, ean13)
 -- Now the operators:
 --
 
+--
+-- GTIN-14 operators:
+--
+---------------------------------------------------
+CREATE OPERATOR < (
+	PROCEDURE = isnlt,
+	LEFTARG = gtin14,
+	RIGHTARG = gtin14,
+	COMMUTATOR = >,
+	NEGATOR = >=,
+	RESTRICT = scalarltsel,
+	JOIN = scalarltjoinsel);
+CREATE OPERATOR <= (
+	PROCEDURE = isnle,
+	LEFTARG = gtin14,
+	RIGHTARG = gtin14,
+	COMMUTATOR = >=,
+	NEGATOR = >,
+	RESTRICT = scalarltsel,
+	JOIN = scalarltjoinsel);
+CREATE OPERATOR = (
+	PROCEDURE = isneq,
+	LEFTARG = gtin14,
+	RIGHTARG = gtin14,
+	COMMUTATOR = =,
+	NEGATOR = <>,
+	RESTRICT = eqsel,
+	JOIN = eqjoinsel,
+	MERGES,
+	HASHES);
+CREATE OPERATOR >= (
+	PROCEDURE = isnge,
+	LEFTARG = gtin14,
+	RIGHTARG = gtin14,
+	COMMUTATOR = <=,
+	NEGATOR = <,
+	RESTRICT = scalargtsel,
+	JOIN = scalargtjoinsel );
+CREATE OPERATOR > (
+	PROCEDURE = isngt,
+	LEFTARG = gtin14,
+	RIGHTARG = gtin14,
+	COMMUTATOR = <,
+	NEGATOR = <=,
+	RESTRICT = scalargtsel,
+	JOIN = scalargtjoinsel );
+CREATE OPERATOR <> (
+	PROCEDURE = isnne,
+	LEFTARG = gtin14,
+	RIGHTARG = gtin14,
+	COMMUTATOR = <>,
+	NEGATOR = =,
+	RESTRICT = neqsel,
+	JOIN = neqjoinsel);
+
 --
 -- EAN13 operators:
 --
@@ -2708,6 +2821,34 @@ CREATE OPERATOR FAMILY isn_ops USING hash;
 -- Operator classes:
 --
 ---------------------------------------------------
+-- GTIN-14:
+CREATE FUNCTION btgtin14cmp(gtin14, gtin14)
+	RETURNS int4
+	AS 'btint8cmp'
+	LANGUAGE 'internal'
+	IMMUTABLE STRICT
+	PARALLEL SAFE;
+
+CREATE OPERATOR CLASS gtin14_ops DEFAULT
+	FOR TYPE gtin14 USING btree FAMILY isn_ops AS
+	OPERATOR 1  <,
+	OPERATOR 2  <=,
+	OPERATOR 3  =,
+	OPERATOR 4  >=,
+	OPERATOR 5  >,
+	FUNCTION 1  btgtin14cmp(gtin14, gtin14);
+
+CREATE FUNCTION hashgtin14(gtin14)
+	RETURNS int4
+	AS 'hashint8'
+	LANGUAGE 'internal' IMMUTABLE STRICT
+	PARALLEL SAFE;
+
+CREATE OPERATOR CLASS gtin14_ops DEFAULT
+	FOR TYPE gtin14 USING hash FAMILY isn_ops AS
+	OPERATOR 1  =,
+	FUNCTION 1  hashgtin14(gtin14);
+
 -- EAN13:
 CREATE FUNCTION btean13cmp(ean13, ean13)
 	RETURNS int4
@@ -3314,6 +3455,12 @@ CREATE CAST (issn13 AS issn) WITHOUT FUNCTION AS ASSIGNMENT;
 --
 -- Validation stuff for lose types:
 --
+CREATE FUNCTION make_valid(gtin14)
+	RETURNS gtin14
+	AS 'MODULE_PATHNAME'
+	LANGUAGE C
+	IMMUTABLE STRICT
+	PARALLEL SAFE;
 CREATE FUNCTION make_valid(ean13)
 	RETURNS ean13
 	AS 'MODULE_PATHNAME'
@@ -3363,6 +3510,12 @@ CREATE FUNCTION make_valid(upc)
 	IMMUTABLE STRICT
 	PARALLEL SAFE;
 
+CREATE FUNCTION is_valid(gtin14)
+	RETURNS boolean
+	AS 'MODULE_PATHNAME'
+	LANGUAGE C
+	IMMUTABLE STRICT
+	PARALLEL SAFE;
 CREATE FUNCTION is_valid(ean13)
 	RETURNS boolean
 	AS 'MODULE_PATHNAME'
diff --git a/isn.c b/isn.c
index 0c2cac7..d7baa78 100644
--- a/isn.c
+++ b/isn.c
@@ -36,10 +36,10 @@ PG_MODULE_MAGIC;
 
 enum isn_type
 {
-	INVALID, ANY, EAN13, ISBN, ISMN, ISSN, UPC
+	INVALID, ANY, EAN13, ISBN, ISMN, ISSN, UPC, GTIN14
 };
 
-static const char *const isn_names[] = {"EAN13/UPC/ISxN", "EAN13/UPC/ISxN", "EAN13", "ISBN", "ISMN", "ISSN", "UPC"};
+static const char *const isn_names[] = {"EAN13/UPC/ISxN", "EAN13/UPC/ISxN", "EAN13", "ISBN", "ISMN", "ISSN", "UPC", "GTIN14"};
 
 static bool g_weak = false;
 
@@ -325,7 +325,10 @@ checkdig(char *num, unsigned size)
 		}
 		num++;
 	}
-	check = (check + 3 * check3) % 10;
+	if (pos % 2 == 0)  /* for even length strings */
+		check = (check + 3 * check3) % 10;
+	else
+		check = (check3 + 3 * check) % 10;
 	if (check != 0)
 		check = 10 - check;
 	return check;
@@ -500,11 +503,9 @@ ean2UPC(char *isn)
 }
 
 /*
- * ean2* --- Converts a string of digits into an ean13 number.
- *			  Assumes the input string is a string with only digits
- *			  on it, and that it's within the range of ean13.
+ * str2ean --- Converts a string of digits into binary storage format.
  *
- * Returns the ean13 value of the string.
+ * Returns the ean13 with valid-flag set to true.
  */
 static ean13
 str2ean(const char *num)
@@ -520,6 +521,71 @@ str2ean(const char *num)
 	return (ean << 1);			/* also give room to a flag */
 }
 
+/*
+ * gtin14_2string --- Try to convert GTINumber to a hyphenated string.
+ *				  Assumes there's enough space in result to hold
+ *				  the string (maximum MAXEAN13LEN+1 bytes)
+ *				  This doesn't verify for a valid check digit.
+ *
+ * If errorOK is false, ereport a useful error message if the string is bad.
+ * If errorOK is true, just return "false" for bad input.
+ */
+static bool
+gtin14_2string(ean13 ean, bool errorOK, char *result)
+{
+	enum isn_type type = INVALID;
+
+	char	   *aux;
+	unsigned	digval;
+	unsigned	search;
+	char		valid = '\0';	/* was the number initially written with a
+								 * valid check digit? */
+
+	if ((ean & 1) != 0)
+		valid = '!';
+	ean >>= 1;
+	/* verify it's in the EAN13 range */
+	if (ean > UINT64CONST(99999999999999))
+		goto eantoobig;
+
+	/* convert the number */
+	search = 0;
+	aux = result + MAXEAN13LEN;
+	*aux = '\0';				/* terminate string; aux points to last digit */
+	*--aux = valid;				/* append '!' for numbers with invalid but
+								 * corrected check digit */
+	do
+	{
+		digval = (unsigned) (ean % 10); /* get the decimal value */
+		ean /= 10;				/* get next digit */
+		*--aux = (char) (digval + '0'); /* convert to ascii and store */
+		if (search == 0)
+			*--aux = '-';		/* the check digit is always there */
+	} while (ean && search++ < 14);
+	while (search++ < 14)
+		*--aux = '0';			/* fill the remaining GTIN14 with '0' */
+
+	search = hyphenate(result, result + 2, NULL, NULL);
+
+	return true;
+
+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;
+}
 /*
  * ean2string --- Try to convert an ean13 number to a hyphenated string.
  *				  Assumes there's enough space in result to hold
@@ -673,6 +739,162 @@ eantoobig:
 	return false;
 }
 
+/*
+ * string2gtin14 --- try to parse a string into an gtin14.
+ *
+ * If errorOK is false, ereport a useful error message if the string is bad.
+ * If errorOK is true, just return "false" for bad input.
+ *
+ * if the input string ends with '!' it will always be treated as invalid
+ * (even if the check digit is valid)
+ */
+static bool
+string2gtin14(const char *str, bool errorOK, ean13 *result,
+		   enum isn_type accept)
+{
+	bool		digit,
+				last;
+	char		buf[17] = "                ";
+	char	   *aux1 = buf + 2; /* leave space for the first part, in case
+								 * it's needed */
+	const char *aux2 = str;
+	enum isn_type type = INVALID;
+	unsigned	check = 0,
+				rcheck = (unsigned) -1;
+	unsigned	length = 0;
+	bool		magic = false,
+				valid = true;
+
+	/* recognize and validate the number: */
+	while (*aux2 && length <= 14)
+	{
+		last = (*(aux2 + 1) == '!' || *(aux2 + 1) == '\0'); /* is the last character */
+		digit = (isdigit((unsigned char) *aux2) != 0);	/* is current character
+														 * a digit? */
+		if (*aux2 == '?' && last)	/* automagically calculate check digit if
+									 * it's '?' */
+			magic = digit = true;
+		if (*aux2 == '!' && *(aux2 + 1) == '\0')
+		{
+			/* the invalid check digit suffix was found, set it */
+			if (!magic)
+				valid = false;
+			magic = true;
+		}
+		else if (!digit)
+		{
+			goto eaninvalid;
+		}
+		else
+		{
+			*aux1++ = *aux2;
+			if (++length > 14)
+				goto eantoobig;
+		}
+		aux2++;
+	}
+	*aux1 = '\0';				/* terminate the string */
+
+	/* find the current check digit value */
+	if (length == 13)
+	{
+		/* is likely EAN13, prefix with 0 to make valid GTIN14 */
+		type = EAN13;
+		buf[1] = '0';  /* prefix 0 */
+		check = buf[14] - '0';
+	}
+	else if (length == 14)
+	{
+		/* is likely GTIN14 */
+		type = GTIN14;
+		check = buf[15] - '0';
+
+		if (buf[2] - '0' == 9) /* valid range of indicator digit is 0-8 */
+			goto indicatorinvalid;
+	}
+	else
+		goto eaninvalid;
+
+	if (type == INVALID)
+		goto eaninvalid;
+
+	/* compute check digit: */
+	for (aux1 = buf; *aux1 && *aux1 <= ' '; aux1++);
+	rcheck = checkdig(aux1, 14);
+	/* validate check digit and convert to gtin14: */
+	switch (type)
+	{
+		case GTIN14:
+		case EAN13:
+			valid = (valid && (rcheck == check || magic));
+			break;
+		default:
+			break;
+	}
+	/* always fix the check digit: */
+	aux1[13] = rcheck + '0';
+	aux1[14] = '\0';
+
+	if (!valid && !magic)
+		goto eanbadcheck;
+
+	*result = str2ean(aux1);
+	*result |= valid ? 0 : 1;
+	return true;
+
+eanbadcheck:
+	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,
+					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+					 errmsg("invalid %s number: \"%s\"",
+							isn_names[accept], str)));
+		}
+		else
+		{
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+					 errmsg("invalid check digit for %s number: \"%s\", should be %c",
+							isn_names[accept], str, (rcheck == 10) ? ('X') : (rcheck + '0'))));
+		}
+	}
+	return false;
+
+eaninvalid:
+	if (!errorOK)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+				 errmsg("invalid input syntax for %s number: \"%s\"",
+						isn_names[accept], str)));
+	return false;
+
+indicatorinvalid:
+	if (!errorOK)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+				 errmsg("Indicator digit out of range for %s number: \"%s\"",
+						isn_names[accept], str)));
+	return false;
+
+eantoobig:
+	if (!errorOK)
+		ereport(ERROR,
+				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+				 errmsg("value \"%s\" is out of range for %s type",
+						str, isn_names[accept])));
+	return false;
+}
+
 /*
  * string2ean --- try to parse a string into an ean13.
  *
@@ -990,6 +1212,35 @@ ean13_in(PG_FUNCTION_ARGS)
 	PG_RETURN_EAN13(result);
 }
 
+/* gtin14_out
+ */
+PG_FUNCTION_INFO_V1(gtin14_out);
+Datum
+gtin14_out(PG_FUNCTION_ARGS)
+{
+	ean13		val = PG_GETARG_EAN13(0);
+	char	   *result;
+	char		buf[MAXEAN13LEN + 1];
+
+	(void) gtin14_2string(val, false, buf);
+
+	result = pstrdup(buf);
+	PG_RETURN_CSTRING(result);
+}
+
+/* gtin14_in
+ */
+PG_FUNCTION_INFO_V1(gtin14_in);
+Datum
+gtin14_in(PG_FUNCTION_ARGS)
+{
+	const char *str = PG_GETARG_CSTRING(0);
+	ean13		result;
+
+	(void) string2gtin14(str, false, &result, GTIN14);
+	PG_RETURN_EAN13(result);
+}
+
 /* isbn_in
  */
 PG_FUNCTION_INFO_V1(isbn_in);
diff --git a/sql/isn.sql b/sql/isn.sql
index 71577d5..ede461f 100644
--- a/sql/isn.sql
+++ b/sql/isn.sql
@@ -16,6 +16,11 @@ WHERE NOT amvalidate(oid);
 --
 -- test valid conversions
 --
+SELECT '1234567890128'::GTIN14, -- EAN is a GTIN14
+       '123456789012?'::GTIN14, -- compute check digit for me
+       '11234567890125'::GTIN14,
+       '01234567890128'::GTIN14;
+
 SELECT '9780123456786'::EAN13, -- old book
        '9790123456785'::EAN13, -- music
        '9791234567896'::EAN13, -- new book
@@ -100,6 +105,30 @@ SELECT 'postgresql...'::ISBN;
 SELECT 9780123456786::EAN13;
 SELECT 9780123456786::ISBN;
 
+SELECT '91234567890125'::GTIN14; -- invalid indicator
+SELECT '123456789012'::GTIN14; -- too short
+SELECT '1234567890127'::GTIN14; -- wrong checkdigit
+
+--
+-- test validity helpers
+--
+SELECT make_valid('1234567890120!'::GTIN14); -- EAN-13
+SELECT make_valid('11234567890120!'::GTIN14); -- GTIN-14
+SELECT is_valid(make_valid('1234567890120!'::GTIN14)); -- EAN-13
+SELECT is_valid(make_valid('11234567890120!'::GTIN14)); -- GTIN-14
+
+CREATE TABLE gtin_valid (gtin GTIN14 NOT NULL);
+INSERT INTO gtin_valid VALUES
+-- all invalid because of ! marking
+	('1234567890120!'), -- invalid EAN-13
+	('1234567890128!'), -- valid EAN-13
+	('11234567890120!'), -- invalid GTIN-14
+	('11234567890125!'), -- valid GTIN-14
+-- valid
+	('1234567890128'::GTIN14), -- valid EAN-13
+	('11234567890125'::GTIN14); -- valid GTIN-14
+SELECT gtin, is_valid(gtin) FROM gtin_valid;
+DROP TABLE gtin_valid;
 --
 -- test some comparisons, must yield true
 --