aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/varchar.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils/adt/varchar.c')
-rw-r--r--src/backend/utils/adt/varchar.c247
1 files changed, 144 insertions, 103 deletions
diff --git a/src/backend/utils/adt/varchar.c b/src/backend/utils/adt/varchar.c
index 588c735ca10..467a5cf7de3 100644
--- a/src/backend/utils/adt/varchar.c
+++ b/src/backend/utils/adt/varchar.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/adt/varchar.c,v 1.77 2001/05/03 19:00:36 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/varchar.c,v 1.78 2001/05/21 16:54:46 petere Exp $
*
*-------------------------------------------------------------------------
*/
@@ -55,9 +55,11 @@
*****************************************************************************/
/*
- * bpcharin -
- * converts a string of char() type to the internal representation.
- * len is the length specified in () plus VARHDRSZ bytes.
+ * Convert a C string to CHARACTER internal representation. atttypmod
+ * is the declared length of the type plus VARHDRSZ.
+ *
+ * If the C string is too long, raise an error, unless the extra
+ * characters are spaces, in which case they're truncated. (per SQL)
*/
Datum
bpcharin(PG_FUNCTION_ARGS)
@@ -71,30 +73,33 @@ bpcharin(PG_FUNCTION_ARGS)
int32 atttypmod = PG_GETARG_INT32(2);
BpChar *result;
char *r;
- int len;
+ size_t len, maxlen;
int i;
- if (atttypmod < (int32) VARHDRSZ)
+ len = strlen(s);
+ maxlen = atttypmod - VARHDRSZ;
+
+ if (atttypmod >= (int32) VARHDRSZ && len > maxlen)
{
- /* If typmod is -1 (or invalid), use the actual string length */
- len = strlen(s);
- atttypmod = len + VARHDRSZ;
- }
- else
#ifdef MULTIBYTE
- {
+ size_t mbmaxlen = pg_mbcliplen(s, len, maxlen);
- /*
- * truncate multi-byte string preserving multi-byte boundary
- */
- len = pg_mbcliplen(s, atttypmod - VARHDRSZ, atttypmod - VARHDRSZ);
- }
+ if (strspn(s + mbmaxlen, " ") == len - mbmaxlen)
+ len = mbmaxlen;
#else
- len = atttypmod - VARHDRSZ;
+ if (strspn(s + maxlen, " ") == len - maxlen)
+ /* clip extra spaces */
+ len = maxlen;
#endif
+ else
+ elog(ERROR, "value too long for type character(%d)", maxlen);
+ }
+ else
+ /* If typmod is -1 (or invalid), use the actual string length */
+ maxlen = len;
- result = (BpChar *) palloc(atttypmod);
- VARATT_SIZEP(result) = atttypmod;
+ result = palloc(maxlen + VARHDRSZ);
+ VARATT_SIZEP(result) = maxlen + VARHDRSZ;
r = VARDATA(result);
for (i = 0; i < len; i++, r++, s++)
{
@@ -108,16 +113,16 @@ bpcharin(PG_FUNCTION_ARGS)
#endif
/* blank pad the string if necessary */
-#ifdef MULTIBYTE
- for (; i < atttypmod - VARHDRSZ; i++)
-#else
- for (; i < len; i++)
-#endif
+ for (; i < maxlen; i++)
*r++ = ' ';
PG_RETURN_BPCHAR_P(result);
}
+
+/*
+ * Convert a CHARACTER value to a C string.
+ */
Datum
bpcharout(PG_FUNCTION_ARGS)
{
@@ -138,75 +143,70 @@ bpcharout(PG_FUNCTION_ARGS)
PG_RETURN_CSTRING(result);
}
-/* bpchar()
- * Converts a char() type to a specific internal length.
- * len is the length specified in () plus VARHDRSZ bytes.
+
+/*
+ * Converts a CHARACTER type to the specified size. maxlen is the new
+ * declared length plus VARHDRSZ bytes. Truncation
+ * rules see bpcharin() above.
*/
Datum
bpchar(PG_FUNCTION_ARGS)
{
- BpChar *str = PG_GETARG_BPCHAR_P(0);
- int32 len = PG_GETARG_INT32(1);
+ BpChar *source = PG_GETARG_BPCHAR_P(0);
+ int32 maxlen = PG_GETARG_INT32(1);
BpChar *result;
- char *r,
- *s;
- int rlen,
- slen;
+ int32 len;
+ char *r;
+ char *s;
int i;
+ len = VARSIZE(source);
/* No work if typmod is invalid or supplied data matches it already */
- if (len < (int32) VARHDRSZ || len == VARSIZE(str))
- PG_RETURN_BPCHAR_P(str);
+ if (len < (int32) VARHDRSZ || len == maxlen)
+ PG_RETURN_BPCHAR_P(source);
- rlen = len - VARHDRSZ;
-
-#ifdef STRINGDEBUG
- printf("bpchar- convert string length %d (%d) ->%d (%d)\n",
- VARSIZE(str) - VARHDRSZ, VARSIZE(str), rlen, len);
-#endif
+ if (len > maxlen)
+ {
+#ifdef MULTIBYTE
+ size_t maxmblen;
- result = (BpChar *) palloc(len);
- VARATT_SIZEP(result) = len;
- r = VARDATA(result);
+ maxmblen = pg_mbcliplen(VARDATA(source), len - VARHDRSZ,
+ maxlen - VARHDRSZ) + VARHDRSZ;
-#ifdef MULTIBYTE
+ for (i = maxmblen - VARHDRSZ; i < len - VARHDRSZ; i++)
+ if (*(VARDATA(source) + i) != ' ')
+ elog(ERROR, "value too long for type character(%d)",
+ maxlen - VARHDRSZ);
- /*
- * truncate multi-byte string in a way not to break multi-byte
- * boundary
- */
- if (VARSIZE(str) > len)
- slen = pg_mbcliplen(VARDATA(str), VARSIZE(str) - VARHDRSZ, rlen);
- else
- slen = VARSIZE(str) - VARHDRSZ;
+ len = maxmblen;
#else
- slen = VARSIZE(str) - VARHDRSZ;
-#endif
- s = VARDATA(str);
+ for (i = maxlen - VARHDRSZ; i < len - VARHDRSZ; i++)
+ if (*(VARDATA(source) + i) != ' ')
+ elog(ERROR, "value too long for type character(%d)",
+ maxlen - VARHDRSZ);
-#ifdef STRINGDEBUG
- printf("bpchar- string is '");
+ /* clip extra spaces */
+ len = maxlen;
#endif
-
- for (i = 0; (i < rlen) && (i < slen); i++)
- {
-#ifdef STRINGDEBUG
- printf("%c", *s);
-#endif
- *r++ = *s++;
}
-#ifdef STRINGDEBUG
- printf("'\n");
-#endif
+ s = VARDATA(source);
+
+ result = palloc(maxlen);
+ VARATT_SIZEP(result) = maxlen;
+ r = VARDATA(result);
+
+ for (i = 0; (i < maxlen - VARHDRSZ) && (i < len - VARHDRSZ); i++)
+ *r++ = *s++;
/* blank pad the string if necessary */
- for (; i < rlen; i++)
+ for (; i < maxlen - VARHDRSZ; i++)
*r++ = ' ';
PG_RETURN_BPCHAR_P(result);
}
+
/* _bpchar()
* Converts an array of char() elements to a specific internal length.
* len is the length specified in () plus VARHDRSZ bytes.
@@ -330,9 +330,11 @@ name_bpchar(PG_FUNCTION_ARGS)
*****************************************************************************/
/*
- * varcharin -
- * converts a string of varchar() type to the internal representation.
- * len is the length specified in () plus VARHDRSZ bytes.
+ * Convert a C string to VARCHAR internal representation. atttypmod
+ * is the declared length of the type plus VARHDRSZ.
+ *
+ * If the C string is too long, raise an error, unless the extra
+ * characters are spaces, in which case they're truncated. (per SQL)
*/
Datum
varcharin(PG_FUNCTION_ARGS)
@@ -345,37 +347,52 @@ varcharin(PG_FUNCTION_ARGS)
#endif
int32 atttypmod = PG_GETARG_INT32(2);
VarChar *result;
- int len;
+ size_t len, maxlen;
+
+ len = strlen(s);
+ maxlen = atttypmod - VARHDRSZ;
- len = strlen(s) + VARHDRSZ;
- if (atttypmod >= (int32) VARHDRSZ && len > atttypmod)
+ if (atttypmod >= (int32) VARHDRSZ && len > maxlen)
+ {
#ifdef MULTIBYTE
- len = pg_mbcliplen(s, len - VARHDRSZ, atttypmod - VARHDRSZ) + VARHDRSZ;
+ size_t mbmaxlen = pg_mbcliplen(s, len, maxlen);
+
+ if (strspn(s + mbmaxlen, " ") == len - mbmaxlen)
+ len = mbmaxlen;
#else
- len = atttypmod; /* clip the string at max length */
+ if (strspn(s + maxlen, " ") == len - maxlen)
+ /* clip extra spaces */
+ len = maxlen;
#endif
+ else
+ elog(ERROR, "value too long for type character varying(%d)", maxlen);
+ }
- result = (VarChar *) palloc(len);
- VARATT_SIZEP(result) = len;
- memcpy(VARDATA(result), s, len - VARHDRSZ);
+ result = palloc(len + VARHDRSZ);
+ VARATT_SIZEP(result) = len + VARHDRSZ;
+ memcpy(VARDATA(result), s, len);
#ifdef CYR_RECODE
- convertstr(VARDATA(result), len - VARHDRSZ, 0);
+ convertstr(VARDATA(result), len, 0);
#endif
PG_RETURN_VARCHAR_P(result);
}
+
+/*
+ * Convert a VARCHAR value to a C string.
+ */
Datum
varcharout(PG_FUNCTION_ARGS)
{
VarChar *s = PG_GETARG_VARCHAR_P(0);
char *result;
- int len;
+ int32 len;
/* copy and add null term */
len = VARSIZE(s) - VARHDRSZ;
- result = (char *) palloc(len + 1);
+ result = palloc(len + 1);
memcpy(result, VARDATA(s), len);
result[len] = '\0';
@@ -386,42 +403,60 @@ varcharout(PG_FUNCTION_ARGS)
PG_RETURN_CSTRING(result);
}
-/* varchar()
- * Converts a varchar() type to the specified size.
- * slen is the length specified in () plus VARHDRSZ bytes.
+
+/*
+ * Converts a VARCHAR type to the specified size. maxlen is the new
+ * declared length plus VARHDRSZ bytes. Truncation
+ * rules see varcharin() above.
*/
Datum
varchar(PG_FUNCTION_ARGS)
{
- VarChar *s = PG_GETARG_VARCHAR_P(0);
- int32 slen = PG_GETARG_INT32(1);
+ VarChar *source = PG_GETARG_VARCHAR_P(0);
+ int32 maxlen = PG_GETARG_INT32(1);
VarChar *result;
- int len;
+ int32 len;
+ int i;
- len = VARSIZE(s);
- if (slen < (int32) VARHDRSZ || len <= slen)
- PG_RETURN_VARCHAR_P(s);
+ len = VARSIZE(source);
+ if (maxlen < (int32) VARHDRSZ || len <= maxlen)
+ PG_RETURN_VARCHAR_P(source);
- /* only reach here if we need to truncate string... */
+ /* only reach here if string is too long... */
#ifdef MULTIBYTE
+ {
+ size_t maxmblen;
- /*
- * truncate multi-byte string preserving multi-byte boundary
- */
- len = pg_mbcliplen(VARDATA(s), slen - VARHDRSZ, slen - VARHDRSZ);
- slen = len + VARHDRSZ;
+ /* truncate multi-byte string preserving multi-byte boundary */
+ maxmblen = pg_mbcliplen(VARDATA(source), len - VARHDRSZ,
+ maxlen - VARHDRSZ) + VARHDRSZ;
+
+ for (i = maxmblen - VARHDRSZ; i < len - VARHDRSZ; i++)
+ if (*(VARDATA(source) + i) != ' ')
+ elog(ERROR, "value too long for type character varying(%d)",
+ maxlen - VARHDRSZ);
+
+ len = maxmblen;
+ }
#else
- len = slen - VARHDRSZ;
+ for (i = maxlen - VARHDRSZ; i < len - VARHDRSZ; i++)
+ if (*(VARDATA(source) + i) != ' ')
+ elog(ERROR, "value too long for type character varying(%d)",
+ maxlen - VARHDRSZ);
+
+ /* clip extra spaces */
+ len = maxlen;
#endif
- result = (VarChar *) palloc(slen);
- VARATT_SIZEP(result) = slen;
- memcpy(VARDATA(result), VARDATA(s), len);
+ result = palloc(len);
+ VARATT_SIZEP(result) = len;
+ memcpy(VARDATA(result), VARDATA(source), len - VARHDRSZ);
PG_RETURN_VARCHAR_P(result);
}
+
/* _varchar()
* Converts an array of varchar() elements to the specified size.
* len is the length specified in () plus VARHDRSZ bytes.
@@ -452,6 +487,12 @@ _varchar(PG_FUNCTION_ARGS)
return array_map(&locfcinfo, VARCHAROID, VARCHAROID);
}
+
+
+/*****************************************************************************
+ * Exported functions
+ *****************************************************************************/
+
/* "True" length (not counting trailing blanks) of a BpChar */
static int
bcTruelen(BpChar *arg)