This is rebased patch of `Allow encoding specific character
incrementer'(https://commitfest.postgresql.org/action/patch_view?id=602).
Addition to the patch, increment sanity check program for new
functions pg_generic_charinc and pg_utf8_increment is attached.
--
Kyotaro Horiguchi
NTT Open Source Software Center
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index 5d999e6..b7f1922 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -5652,6 +5652,18 @@ pattern_selectivity(Const *patt, Pattern_Type ptype)
/*
+ * This function is "character increment" function for bytea used in
+ * make_greater_string() that has same interface with pg_wchar_tbl.charinc.
+ */
+static bool byte_increment(unsigned char *ptr, int len)
+{
+ if (*ptr >= 255) return false;
+
+ (*ptr)++;
+ return true;
+}
+
+/*
* Try to generate a string greater than the given string or any
* string it is a prefix of. If successful, return a palloc'd string
* in the form of a Const node; else return NULL.
@@ -5690,6 +5702,7 @@ make_greater_string(const Const *str_const, FmgrInfo *ltproc, Oid collation)
int len;
Datum cmpstr;
text *cmptxt = NULL;
+ character_incrementer charincfunc;
/*
* Get a modifiable copy of the prefix string in C-string format, and set
@@ -5751,27 +5764,38 @@ make_greater_string(const Const *str_const, FmgrInfo *ltproc, Oid collation)
}
}
+ if (datatype != BYTEAOID)
+ charincfunc = pg_database_encoding_character_incrementer();
+ else
+ charincfunc = &byte_increment;
+
while (len > 0)
{
- unsigned char *lastchar = (unsigned char *) (workstr + len - 1);
- unsigned char savelastchar = *lastchar;
+ int charlen;
+ unsigned char *lastchar;
+ unsigned char savelastbyte;
+ Const *workstr_const;
+
+ if (datatype == BYTEAOID)
+ charlen = 1;
+ else
+ charlen = len - pg_mbcliplen(workstr, len, len - 1);
+
+ lastchar = (unsigned char *) (workstr + len - charlen);
/*
- * Try to generate a larger string by incrementing the last byte.
+ * savelastbyte has meaning only for datatype == BYTEAOID
*/
- while (*lastchar < (unsigned char) 255)
- {
- Const *workstr_const;
+ savelastbyte = *lastchar;
- (*lastchar)++;
+ /*
+ * Try to generate a larger string by incrementing the last byte or
+ * character.
+ */
+ if (charincfunc(lastchar, charlen)) {
if (datatype != BYTEAOID)
- {
- /* do not generate invalid encoding sequences */
- if (!pg_verifymbstr(workstr, len, true))
- continue;
workstr_const = string_to_const(workstr, datatype);
- }
else
workstr_const = string_to_bytea_const(workstr, len);
@@ -5786,26 +5810,17 @@ make_greater_string(const Const *str_const, FmgrInfo *ltproc, Oid collation)
pfree(workstr);
return workstr_const;
}
-
+
/* No good, release unusable value and try again */
pfree(DatumGetPointer(workstr_const->constvalue));
pfree(workstr_const);
}
- /* restore last byte so we don't confuse pg_mbcliplen */
- *lastchar = savelastchar;
-
/*
- * Truncate off the last character, which might be more than 1 byte,
- * depending on the character encoding.
+ * Truncate off the last character or restore last byte for BYTEA.
*/
- if (datatype != BYTEAOID && pg_database_encoding_max_length() > 1)
- len = pg_mbcliplen(workstr, len, len - 1);
- else
- len -= 1;
-
- if (datatype != BYTEAOID)
- workstr[len] = '\0';
+ len -= charlen;
+ workstr[len] = (datatype != BYTEAOID ? '\0' : savelastbyte);
}
/* Failed... */
diff --git a/src/backend/utils/mb/wchar.c b/src/backend/utils/mb/wchar.c
index f23732f..00b3e2a 100644
--- a/src/backend/utils/mb/wchar.c
+++ b/src/backend/utils/mb/wchar.c
@@ -1,3 +1,4 @@
+
/*
* conversion functions between pg_wchar and multibyte streams.
* Tatsuo Ishii
@@ -1336,53 +1337,254 @@ pg_utf8_islegal(const unsigned char *source, int length)
/*
*-------------------------------------------------------------------
+ * character incrementer
+ *
+ * These functions accept "charptr", a pointer to the first byte of a
+ * maybe-multibyte character. Try `increment' the character and return true if
+ * successed. If these functions returns false, the character should be
+ * untouched. These functions must be implemented in correspondence with
+ * verifiers, in other words, the rewrited character by this function must pass
+ * the check by pg_*_verifier() if returns true. Returning the return value of
+ * pg_*_verifier() corresponding can finnaly avoid such a inconsistency when
+ * something wrong.
+ * -------------------------------------------------------------------
+ */
+
+#ifndef FRONTEND
+static bool pg_generic_charinc(unsigned char *charptr, int len)
+{
+ unsigned char *lastchar = (unsigned char *) (charptr + len - 1);
+ unsigned char savelastchar = *lastchar;
+ const char *const_charptr = (const char *)charptr;
+
+ while (*lastchar < (unsigned char) 255)
+ {
+ (*lastchar)++;
+ if (!pg_verifymbstr(const_charptr, len, true))
+ continue;
+ return true;
+ }
+
+ *lastchar = savelastchar;
+ return false;
+}
+
+static bool pg_utf8_increment(unsigned char *charptr, int length)
+{
+ unsigned char a;
+ unsigned char bak[4];
+ bool success;
+
+ memcpy(bak, charptr, length);
+ switch (length)
+ {
+ default:
+ /* reject lengths 5 and 6 for now */
+ return false;
+ case 4:
+ a = charptr[3];
+ if (a < 0xBF)
+ {
+ charptr[3]++;
+ break;
+ }
+ charptr[3] = 0x80;
+ /* FALL THRU */
+ case 3:
+ a = charptr[2];
+ if (a < 0xBF)
+ {
+ charptr[2]++;
+ break;
+ }
+ charptr[2] = 0x80;
+ /* FALL THRU */
+ case 2:
+ a = charptr[1];
+ if ((*charptr == 0xed && a < 0x9F) || a < 0xBF)
+ {
+ charptr[1]++;
+ break;
+ }
+ charptr[1] = 0x80;
+ /* FALL THRU */
+ case 1:
+ a = *charptr;
+ if (a == 0x7F || a == 0xDF || a == 0xEF || a == 0xF7) {
+ memcpy(charptr, bak, length);
+ return false;
+ }
+ charptr[0]++;
+ break;
+ }
+
+ /* Check the result with pg_utf8_islegal as the last resort. */
+ success = pg_utf8_islegal(charptr, length);
+ if (!success)
+ memcpy(charptr, bak, length);
+
+ return success;
+}
+
+static bool pg_eucjp_increment(unsigned char *charptr, int length) {
+ unsigned char bak[3];
+ bool success;
+ unsigned char c1, c2;
+ signed int i;
+
+ memcpy(bak, charptr, length);
+
+ c1 = *charptr;
+
+ switch (c1)
+ {
+ case SS2: /* JIS X 0201 */
+ if (length != 2) return false;
+
+ c2 = charptr[1];
+
+ if (c2 > 0xde)
+ charptr[0] = charptr[1] = 0xa1;
+ else if (c2 < 0xa1)
+ charptr[1] = 0xa1;
+ else
+ charptr[1]++;
+
+ break;
+
+ case SS3: /* JIS X 0212 */
+ if (length != 3) return false;
+
+ for (i = 2 ; i > 1 ; i--)
+ {
+ c2 = charptr[i];
+ if (c2 < 0xa1)
+ {
+ charptr[i] = 0xa1;
+ return true;
+ }
+ else if (c2 < 0xfe)
+ {
+ charptr[i]++;
+ break;
+ }
+ charptr[i] = 0xa1;
+ }
+
+
+ if (i == 0) /* Out of code region */
+ {
+ memcpy(charptr, bak, length);
+ return false;
+ }
+
+ break;
+
+ default:
+ if (IS_HIGHBIT_SET(c1)) /* JIS X 0208? */
+ {
+ if (length != 2) return false;
+
+ for (i = 1 ; i >= 0 ; i--) /* i must be signed */
+ {
+ c2 = charptr[i];
+ if (c2 < 0xa1)
+ {
+ charptr[i] = 0xa1;
+ return true;
+ }
+ else if (c2 < 0xfe)
+ {
+ charptr[i]++;
+ break;
+ }
+ charptr[i] = 0xa1;
+ }
+
+ if (i < 0) /* Out of 2 byte code region */
+ {
+ memcpy(charptr, bak, length);
+ return false;
+ }
+ }
+ else
+ { /* ASCII */
+ if (c1 > 0x7e)
+ return false;
+ (*charptr)++;
+ }
+ }
+
+
+ /* Check the result with pg_eucjp_verifier as the last resort. */
+ success = (pg_eucjp_verifier(charptr, length) == length);
+ if (!success)
+ memcpy(charptr, bak, length);
+
+ return success;
+}
+#else
+/*
+ * Character increment functions are not available on frontend. Abort on call
+ * to prevent miseuse.
+ */
+static bool pg_generic_charinc(unsigned char *charptr, int len) {
+ fputs(_("Character incrementer cannot be used in frontend.\n"), stderr);
+ abort();
+}
+#define pg_utf8_increment pg_generic_charinc
+#define pg_eucjp_increment pg_generic_charinc
+#endif
+
+/*
+ *-------------------------------------------------------------------
* encoding info table
* XXX must be sorted by the same order as enum pg_enc (in mb/pg_wchar.h)
*-------------------------------------------------------------------
*/
pg_wchar_tbl pg_wchar_table[] = {
- {pg_ascii2wchar_with_len, pg_ascii_mblen, pg_ascii_dsplen, pg_ascii_verifier, 1}, /* PG_SQL_ASCII */
- {pg_eucjp2wchar_with_len, pg_eucjp_mblen, pg_eucjp_dsplen, pg_eucjp_verifier, 3}, /* PG_EUC_JP */
- {pg_euccn2wchar_with_len, pg_euccn_mblen, pg_euccn_dsplen, pg_euccn_verifier, 2}, /* PG_EUC_CN */
- {pg_euckr2wchar_with_len, pg_euckr_mblen, pg_euckr_dsplen, pg_euckr_verifier, 3}, /* PG_EUC_KR */
- {pg_euctw2wchar_with_len, pg_euctw_mblen, pg_euctw_dsplen, pg_euctw_verifier, 4}, /* PG_EUC_TW */
- {pg_eucjp2wchar_with_len, pg_eucjp_mblen, pg_eucjp_dsplen, pg_eucjp_verifier, 3}, /* PG_EUC_JIS_2004 */
- {pg_utf2wchar_with_len, pg_utf_mblen, pg_utf_dsplen, pg_utf8_verifier, 4}, /* PG_UTF8 */
- {pg_mule2wchar_with_len, pg_mule_mblen, pg_mule_dsplen, pg_mule_verifier, 4}, /* PG_MULE_INTERNAL */
- {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_LATIN1 */
- {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_LATIN2 */
- {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_LATIN3 */
- {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_LATIN4 */
- {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_LATIN5 */
- {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_LATIN6 */
- {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_LATIN7 */
- {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_LATIN8 */
- {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_LATIN9 */
- {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_LATIN10 */
- {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN1256 */
- {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN1258 */
- {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN866 */
- {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN874 */
- {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_KOI8R */
- {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN1251 */
- {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN1252 */
- {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* ISO-8859-5 */
- {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* ISO-8859-6 */
- {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* ISO-8859-7 */
- {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* ISO-8859-8 */
- {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN1250 */
- {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN1253 */
- {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN1254 */
- {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN1255 */
- {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN1257 */
- {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_KOI8U */
- {0, pg_sjis_mblen, pg_sjis_dsplen, pg_sjis_verifier, 2}, /* PG_SJIS */
- {0, pg_big5_mblen, pg_big5_dsplen, pg_big5_verifier, 2}, /* PG_BIG5 */
- {0, pg_gbk_mblen, pg_gbk_dsplen, pg_gbk_verifier, 2}, /* PG_GBK */
- {0, pg_uhc_mblen, pg_uhc_dsplen, pg_uhc_verifier, 2}, /* PG_UHC */
- {0, pg_gb18030_mblen, pg_gb18030_dsplen, pg_gb18030_verifier, 4}, /* PG_GB18030 */
- {0, pg_johab_mblen, pg_johab_dsplen, pg_johab_verifier, 3}, /* PG_JOHAB */
- {0, pg_sjis_mblen, pg_sjis_dsplen, pg_sjis_verifier, 2} /* PG_SHIFT_JIS_2004 */
+ {pg_ascii2wchar_with_len, pg_ascii_mblen, pg_ascii_dsplen, pg_generic_charinc, pg_ascii_verifier, 1}, /* PG_SQL_ASCII */
+ {pg_eucjp2wchar_with_len, pg_eucjp_mblen, pg_eucjp_dsplen, pg_eucjp_increment, pg_eucjp_verifier, 3}, /* PG_EUC_JP */
+ {pg_euccn2wchar_with_len, pg_euccn_mblen, pg_euccn_dsplen, pg_generic_charinc, pg_euccn_verifier, 2}, /* PG_EUC_CN */
+ {pg_euckr2wchar_with_len, pg_euckr_mblen, pg_euckr_dsplen, pg_generic_charinc, pg_euckr_verifier, 3}, /* PG_EUC_KR */
+ {pg_euctw2wchar_with_len, pg_euctw_mblen, pg_euctw_dsplen, pg_generic_charinc, pg_euctw_verifier, 4}, /* PG_EUC_TW */
+ {pg_eucjp2wchar_with_len, pg_eucjp_mblen, pg_eucjp_dsplen, pg_eucjp_increment, pg_eucjp_verifier, 3}, /* PG_EUC_JIS_2004 */
+ {pg_utf2wchar_with_len, pg_utf_mblen, pg_utf_dsplen, pg_utf8_increment, pg_utf8_verifier, 4}, /* PG_UTF8 */
+ {pg_mule2wchar_with_len, pg_mule_mblen, pg_mule_dsplen, pg_generic_charinc, pg_mule_verifier, 4}, /* PG_MULE_INTERNAL */
+ {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_generic_charinc, pg_latin1_verifier, 1}, /* PG_LATIN1 */
+ {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_generic_charinc, pg_latin1_verifier, 1}, /* PG_LATIN2 */
+ {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_generic_charinc, pg_latin1_verifier, 1}, /* PG_LATIN3 */
+ {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_generic_charinc, pg_latin1_verifier, 1}, /* PG_LATIN4 */
+ {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_generic_charinc, pg_latin1_verifier, 1}, /* PG_LATIN5 */
+ {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_generic_charinc, pg_latin1_verifier, 1}, /* PG_LATIN6 */
+ {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_generic_charinc, pg_latin1_verifier, 1}, /* PG_LATIN7 */
+ {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_generic_charinc, pg_latin1_verifier, 1}, /* PG_LATIN8 */
+ {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_generic_charinc, pg_latin1_verifier, 1}, /* PG_LATIN9 */
+ {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_generic_charinc, pg_latin1_verifier, 1}, /* PG_LATIN10 */
+ {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_generic_charinc, pg_latin1_verifier, 1}, /* PG_WIN1256 */
+ {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_generic_charinc, pg_latin1_verifier, 1}, /* PG_WIN1258 */
+ {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_generic_charinc, pg_latin1_verifier, 1}, /* PG_WIN866 */
+ {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_generic_charinc, pg_latin1_verifier, 1}, /* PG_WIN874 */
+ {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_generic_charinc, pg_latin1_verifier, 1}, /* PG_KOI8R */
+ {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_generic_charinc, pg_latin1_verifier, 1}, /* PG_WIN1251 */
+ {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_generic_charinc, pg_latin1_verifier, 1}, /* PG_WIN1252 */
+ {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_generic_charinc, pg_latin1_verifier, 1}, /* ISO-8859-5 */
+ {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_generic_charinc, pg_latin1_verifier, 1}, /* ISO-8859-6 */
+ {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_generic_charinc, pg_latin1_verifier, 1}, /* ISO-8859-7 */
+ {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_generic_charinc, pg_latin1_verifier, 1}, /* ISO-8859-8 */
+ {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_generic_charinc, pg_latin1_verifier, 1}, /* PG_WIN1250 */
+ {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_generic_charinc, pg_latin1_verifier, 1}, /* PG_WIN1253 */
+ {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_generic_charinc, pg_latin1_verifier, 1}, /* PG_WIN1254 */
+ {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_generic_charinc, pg_latin1_verifier, 1}, /* PG_WIN1255 */
+ {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_generic_charinc, pg_latin1_verifier, 1}, /* PG_WIN1257 */
+ {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_generic_charinc, pg_latin1_verifier, 1}, /* PG_KOI8U */
+ {0, pg_sjis_mblen, pg_sjis_dsplen, pg_generic_charinc, pg_sjis_verifier, 2}, /* PG_SJIS */
+ {0, pg_big5_mblen, pg_big5_dsplen, pg_generic_charinc, pg_big5_verifier, 2}, /* PG_BIG5 */
+ {0, pg_gbk_mblen, pg_gbk_dsplen, pg_generic_charinc, pg_gbk_verifier, 2}, /* PG_GBK */
+ {0, pg_uhc_mblen, pg_uhc_dsplen, pg_generic_charinc, pg_uhc_verifier, 2}, /* PG_UHC */
+ {0, pg_gb18030_mblen, pg_gb18030_dsplen, pg_generic_charinc, pg_gb18030_verifier, 4}, /* PG_GB18030 */
+ {0, pg_johab_mblen, pg_johab_dsplen, pg_generic_charinc, pg_johab_verifier, 3}, /* PG_JOHAB */
+ {0, pg_sjis_mblen, pg_sjis_dsplen, pg_generic_charinc, pg_sjis_verifier, 2} /* PG_SHIFT_JIS_2004 */
};
/* returns the byte length of a word for mule internal code */
@@ -1459,6 +1661,15 @@ pg_database_encoding_max_length(void)
}
/*
+ * give the character incrementer for the encoding for the current database
+ */
+character_incrementer
+pg_database_encoding_character_incrementer(void)
+{
+ return pg_wchar_table[GetDatabaseEncoding()].charinc;
+}
+
+/*
* Verify mbstr to make sure that it is validly encoded in the current
* database encoding. Otherwise same as pg_verify_mbstr().
*/
diff --git a/src/include/mb/pg_wchar.h b/src/include/mb/pg_wchar.h
index 826c7af..356703a 100644
--- a/src/include/mb/pg_wchar.h
+++ b/src/include/mb/pg_wchar.h
@@ -284,6 +284,8 @@ typedef int (*mblen_converter) (const unsigned char *mbstr);
typedef int (*mbdisplaylen_converter) (const unsigned char *mbstr);
+typedef bool (*character_incrementer) (unsigned char *mbstr, int len);
+
typedef int (*mbverifier) (const unsigned char *mbstr, int len);
typedef struct
@@ -292,6 +294,7 @@ typedef struct
* string to a wchar */
mblen_converter mblen; /* get byte length of a char */
mbdisplaylen_converter dsplen; /* get display width of a char */
+ character_incrementer charinc; /* Character code incrementer if not null */
mbverifier mbverify; /* verify multibyte sequence */
int maxmblen; /* max bytes for a char in this encoding */
} pg_wchar_tbl;
@@ -389,6 +392,7 @@ extern int pg_encoding_mbcliplen(int encoding, const char *mbstr,
extern int pg_mbcharcliplen(const char *mbstr, int len, int imit);
extern int pg_encoding_max_length(int encoding);
extern int pg_database_encoding_max_length(void);
+extern character_incrementer pg_database_encoding_character_incrementer(void);
extern int PrepareClientEncoding(int encoding);
extern int SetClientEncoding(int encoding);
// sanity test for utf specific character incrementer.
//
// -v displays status for invalid source code.
// -m displays status for the result that the product of new
// incrementer is match to the one of the generic incrementer.
// show all status lines when both -v and -m are specified.
//
// `utftest | grep FAILED' shows remaining glitches using new
// incrementer. (4 lines)
//
// CAUTION: this program yields so much lines.
//
// `utftest' yields 17375 lines. These lines are the saved by new func
// and remaining glitches.
// `utftest -m' yields 1112064 lines.
//
//
// Sample of status lines:
// src char src utf8 dst utf8 dest char result status
// 000d7bf => ed9ebf => ed9f80 (000d7c0) successed - Don't match to generic
inc(000d7bf)
// 000d7ff => ed9fbf => ed9fbf (000d7ff) FAILED - Match to generic inc
// 000d800 => eda080 Source not valid utf8
//
// successed/FAILED in result status shows the return value of
// character increment function. Following description says that the
// result of the new incrementer was/was'nt identical to the generic
// incrementer.
#include <stdio.h>
#include <stdarg.h>
typedef int bool;
static int true = 1;
static int false = 0;
static bool pg_utf8_increment(unsigned char *mbstr, int length);
static bool pg_generic_charinc(unsigned char *charptr, int len);
void uni2utf8(unsigned int unicode, unsigned char *utf8buf);
unsigned int utf8tounicode(unsigned char *utf8buf);
int scatf(char* buf, char* format, ...);
int main(int argc, char** argv) {
unsigned char buf[4], buf2[4];
char outbuf[1024];
unsigned int i;
int dispinvalid = 0;
int dispmatch = 0;
for (i = 1 ; i < argc ; i++) {
if (strcmp(argv[i], "-v") == 0) dispinvalid = 1;
if (strcmp(argv[i], "-m") == 0) dispmatch = 1;
}
for(i = 0 ; i < 0x1010000 ; i++) {
bool prechk, successed, gensuccess, match;
uni2utf8(i, buf);
uni2utf8(i, buf2);
*outbuf = 0;
scatf(outbuf, "%07x => ", i);
int len = pg_utf_mblen(buf);
int j = 0;
while (j < len)
scatf(outbuf, "%02x", buf[j++]);
while (j < 4) {
scatf(outbuf, " ");
j++;
}
prechk = pg_utf8_islegal(buf, len);
if (! prechk) {
scatf(outbuf, "Source not valid utf8");
if (dispinvalid)
puts(outbuf);
continue;
}
successed = pg_utf8_increment(buf, len);
scatf(outbuf, " => ");
j = 0;
while (j < len)
scatf(outbuf, "%02x", buf[j++]);
while (j < 4) {
scatf(outbuf, " ");
j++;
}
gensuccess = pg_generic_charinc(buf2, len);
match = (memcmp(buf, buf2, len) == 0);
if (!gensuccess || !match || dispmatch) {
scatf(outbuf,
"(%07x) %s - %s",
utf8tounicode(buf),
(successed ? "successed" : "FAILED"),
(match ? "Match to generic inc" : "Don't match to
generic inc"));
if (!match) {
scatf(outbuf, "(%07x)", utf8tounicode(buf2));
}
puts(outbuf);
}
}
}
bool
pg_utf8_islegal(const unsigned char *source, int length)
{
unsigned char a;
switch (length)
{
default:
/* reject lengths 5 and 6 for now */
return false;
case 4:
a = source[3];
if (a < 0x80 || a > 0xBF)
return false;
/* FALL THRU */
case 3:
a = source[2];
if (a < 0x80 || a > 0xBF)
return false;
/* FALL THRU */
case 2:
a = source[1];
switch (*source)
{
case 0xE0:
if (a < 0xA0 || a > 0xBF)
return false;
break;
case 0xED:
if (a < 0x80 || a > 0x9F)
return false;
break;
case 0xF0:
if (a < 0x90 || a > 0xBF)
return false;
break;
case 0xF4:
if (a < 0x80 || a > 0x8F)
return false;
break;
default:
if (a < 0x80 || a > 0xBF)
return false;
break;
}
/* FALL THRU */
case 1:
a = *source;
if (a >= 0x80 && a < 0xC2)
return false;
if (a > 0xF4)
return false;
break;
}
return true;
}
int
pg_utf_mblen(const unsigned char *s)
{
int len;
if ((*s & 0x80) == 0)
len = 1;
else if ((*s & 0xe0) == 0xc0)
len = 2;
else if ((*s & 0xf0) == 0xe0)
len = 3;
else if ((*s & 0xf8) == 0xf0)
len = 4;
#ifdef NOT_USED
else if ((*s & 0xfc) == 0xf8)
len = 5;
else if ((*s & 0xfe) == 0xfc)
len = 6;
#endif
else
len = 1;
return len;
}
static bool pg_utf8_increment(unsigned char *charptr, int length)
{
unsigned char a;
unsigned char bak[4];
bool success;
memcpy(bak, charptr, length);
switch (length)
{
default:
/* reject lengths 5 and 6 for now */
return false;
case 4:
a = charptr[3];
if (a < 0xBF)
{
charptr[3]++;
break;
}
charptr[3] = 0x80;
/* FALL THRU */
case 3:
a = charptr[2];
if (a < 0xBF)
{
charptr[2]++;
break;
}
charptr[2] = 0x80;
/* FALL THRU */
case 2:
a = charptr[1];
if ((*charptr == 0xed && a < 0x9F) || a < 0xBF)
{
charptr[1]++;
break;
}
charptr[1] = 0x80;
/* FALL THRU */
case 1:
a = *charptr;
if (a == 0x7F || a == 0xDF || a == 0xEF || a == 0xF7) {
memcpy(charptr, bak, length);
return false;
}
charptr[0]++;
break;
}
/* Check the result with pg_utf8_islegal as the last resort. */
success = pg_utf8_islegal(charptr, length);
if (!success)
memcpy(charptr, bak, length);
return success;
}
void uni2utf8(unsigned int unicode, unsigned char *utf8buf) {
int i, len;
if (unicode < 0x80) {
len = 1;
*utf8buf = 0;
} else if (unicode < 0x800) {
len = 2;
*utf8buf = 0xc0;
} else if (unicode < 0x10000) {
len = 3;
*utf8buf = 0xe0;
} else if (unicode < 0x110000) {
len = 4;
*utf8buf = 0xf0;
} else {
printf("Uunicode of of range: %x\n", unicode);
exit(1);
}
for(i = len - 1 ; i > 0 ; i--) {
utf8buf[i] = (0x80 | (unicode & 0x3f));
unicode >>= 6;
}
*utf8buf |= unicode;
}
unsigned int utf8tounicode(unsigned char *utf8buf) {
unsigned int a = *utf8buf;
if (a < 0x80) return a;
if (a < 0xc0) return 0xfffffff;
if (a < 0xe0)
return
((utf8buf[0] - 0xc0) << 6) +
(utf8buf[1] - 0x80);
if (a < 0xf0)
return
((utf8buf[0] - 0xe0) << 12) +
((utf8buf[1] - 0x80) << 6) +
utf8buf[2] - 0x80;
if (a < 0xf8)
return
((utf8buf[0] - 0xf0) << 18) +
((utf8buf[1] - 0x80) << 12) +
((utf8buf[2] - 0x80) << 6) +
utf8buf[3] - 0x80;
return 0xfffffff;
}
static bool pg_generic_charinc(unsigned char *charptr, int len)
{
unsigned char *lastchar = (unsigned char *) (charptr + len - 1);
unsigned char savelastchar = *lastchar;
const char *const_charptr = (const char *)charptr;
while (*lastchar < (unsigned char) 255)
{
(*lastchar)++;
if (!pg_utf8_islegal(const_charptr, len)) // modified.
continue;
return true;
}
*lastchar = savelastchar;
return false;
}
int scatf(char* buf, char* format, ...) {
va_list args;
int ret;
va_start(args, format);
ret = vsprintf(buf + strlen(buf), format, args);
va_end(args);
return ret;
}
// sanity test for euc-japan specific character incrementer.
//
// -v displays status for invalid source charcode.
// -m displays status for the result that the product of new
// incrementer is match to the one of the generic incrementer.
// show all status lines when both -v and -m are specified.
//
// `euctest | grep FAILED' shows remaining glitches using new
// incrementer. (2 lines)
//
// CAUTION:
// `euctest' yields 190 lines.
// `euctest -m' yields 17863 lines.
// `euctest -m -v' yields 16843008 lines.
//
// Sample of output lines:
// src => dest result - status
// 7e => 7f successed - Match to generic inc
// 7f => 7f FAILED - Match to generic inc
// 8edf => a1a1 successed - Don't match to generic inc(8edf)
//
// successed/FAILED in result status shows the return value of
// character increment function. Following description says that the
// result of the new incrementer was/was'nt identical to the generic
// incrementer.
#include <stdio.h>
#include <stdarg.h>
#define SS2 0x8e /* single shift 2 (JIS0201) */
#define SS3 0x8f /* single shift 3 (JIS0212) */
#define HIGHBIT (0x80)
#define IS_HIGHBIT_SET(ch) ((unsigned char)(ch) & HIGHBIT)
typedef int bool;
static int false = 0;
static int true = 1;
static bool pg_generic_charinc(unsigned char *charptr, int len);
static bool pg_eucjp_increment(unsigned char *charptr, int length);
static int pg_eucjp_verifier(const unsigned char *s, int len);
void do_check(int len, unsigned char *buf, int dispinvalid, int dispmatch);
int scatf(char* buf, char* format, ...);
int main(int argc, char **argv) {
unsigned int i, j, k;
unsigned char buf[3];
int res;
int dispinvalid = 0;
int dispmatch = 0;
for (i = 1 ; i < argc ; i++) {
if (strcmp(argv[i], "-v") == 0) dispinvalid = 1;
if (strcmp(argv[i], "-m") == 0) dispmatch = 1;
}
// single byte characters
for (i = 0 ; i < 256 ; i++) {
*buf = i;
do_check(1, buf, dispinvalid, dispmatch);
}
// 2 byte characters
for (i = 0 ; i < 256 ; i++) {
for (j = 0 ; j < 256 ; j++) {
*buf = i;
buf[1] = j;
do_check(2, buf, dispinvalid, dispmatch);
}
}
// 3 byte characters
for (i = 0 ; i < 256 ; i++) {
for (j = 0 ; j < 256 ; j++) {
for (k = 0 ; k < 256 ; k++) {
*buf = i;
buf[1] = j;
buf[2] = k;
do_check(3, buf, dispinvalid, dispmatch);
}
}
}
}
void do_check(int len, unsigned char *buf, int dispinvalid, int dispmatch) {
unsigned char buf2[3];
char outbuf[1024];
int i, src_is_valid, successed, gensuccessed, match;
*outbuf = 0;
src_is_valid = (pg_eucjp_verifier(buf, len) == len);
if (!src_is_valid) {
if (dispinvalid) {
for (i = 0 ; i < len ; i++)
scatf(outbuf, "%02x", buf[i]);
strcat(outbuf, " - Src char is invalid.");
puts(outbuf);
}
return;
}
memcpy(buf2, buf, len);
for (i = 0 ; i < len ; i++)
scatf(outbuf, "%02x", ((int)buf[i] & 0xff));
strcat(outbuf, " => ");
successed = pg_eucjp_increment(buf, len);
gensuccessed = pg_generic_charinc((char*)buf2, len);
match = (memcmp(buf, buf2, len) == 0);
if (!gensuccessed || !match || dispmatch) {
for (i = 0 ; i < len ; i++)
scatf(outbuf, "%02x", ((int)buf[i] & 0xff));
scatf(outbuf, " %s - %s",
(successed ? "successed" : "FAILED"),
(match ? "Match to generic inc" : "Don't match to generic
inc"));
if (!match) {
strcat(outbuf, "(");
for (i = 0 ; i < len ; i++)
scatf(outbuf, "%02x", ((int)buf2[i] & 0xff));
strcat(outbuf, ")");
}
puts(outbuf);
}
}
static bool pg_eucjp_increment(unsigned char *charptr, int length) {
unsigned char bak[3];
bool success;
unsigned char c1, c2;
signed int i;
memcpy(bak, charptr, length);
c1 = *charptr;
switch (c1)
{
case SS2: /* JIS X 0201 */
if (length != 2) return false;
c2 = charptr[1];
if (c2 > 0xde)
charptr[0] = charptr[1] = 0xa1;
else if (c2 < 0xa1)
charptr[1] = 0xa1;
else
charptr[1]++;
break;
case SS3: /* JIS X 0212 */
if (length != 3) return false;
for (i = 2 ; i > 1 ; i--)
{
c2 = charptr[i];
if (c2 < 0xa1)
{
charptr[i] = 0xa1;
return true;
}
else if (c2 < 0xfe)
{
charptr[i]++;
break;
}
charptr[i] = 0xa1;
}
if (i == 0) /* Out of code region */
{
memcpy(charptr, bak, length);
return false;
}
break;
default:
if (IS_HIGHBIT_SET(c1)) /* JIS X 0208? */
{
if (length != 2) return false;
for (i = 1 ; i >= 0 ; i--) /* i must be
signed */
{
c2 = charptr[i];
if (c2 < 0xa1)
{
charptr[i] = 0xa1;
return true;
}
else if (c2 < 0xfe)
{
charptr[i]++;
break;
}
charptr[i] = 0xa1;
}
if (i < 0) /* Out of 2 byte code region */
{
memcpy(charptr, bak, length);
return false;
}
}
else
{ /* ASCII */
if (c1 > 0x7e)
return false;
(*charptr)++;
}
}
/* Check the result with pg_eucjp_verifier as the last resort. */
success = (pg_eucjp_verifier(charptr, length) == length);
if (!success)
memcpy(charptr, bak, length);
return success;
}
#define IS_EUC_RANGE_VALID(c) ((c) >= 0xa1 && (c) <= 0xfe)
static int
pg_eucjp_verifier(const unsigned char *s, int len)
{
int l;
unsigned char c1,
c2;
c1 = *s++;
switch (c1)
{
case SS2: /* JIS X 0201 */
l = 2;
if (l > len)
return -1;
c2 = *s++;
if (c2 < 0xa1 || c2 > 0xdf)
return -1;
break;
case SS3: /* JIS X 0212 */
l = 3;
if (l > len)
return -1;
c2 = *s++;
if (!IS_EUC_RANGE_VALID(c2))
return -1;
c2 = *s++;
if (!IS_EUC_RANGE_VALID(c2))
return -1;
break;
default:
if (IS_HIGHBIT_SET(c1)) /* JIS X 0208? */
{
l = 2;
if (l > len)
return -1;
if (!IS_EUC_RANGE_VALID(c1))
return -1;
c2 = *s++;
if (!IS_EUC_RANGE_VALID(c2))
return -1;
}
else
/* must be ASCII */
{
l = 1;
}
break;
}
return l;
}
static bool pg_generic_charinc(unsigned char *charptr, int len)
{
unsigned char *lastchar = (unsigned char *) (charptr + len - 1);
unsigned char savelastchar = *lastchar;
const char *const_charptr = (const char *)charptr;
while (*lastchar < (unsigned char) 255)
{
(*lastchar)++;
if (pg_eucjp_verifier(const_charptr, len) != len) // modified.
continue;
return true;
}
*lastchar = savelastchar;
return false;
}
int scatf(char* buf, char* format, ...) {
va_list args;
int ret;
va_start(args, format);
ret = vsprintf(buf + strlen(buf), format, args);
va_end(args);
return ret;
}
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers