aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/varlena.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils/adt/varlena.c')
-rw-r--r--src/backend/utils/adt/varlena.c110
1 files changed, 64 insertions, 46 deletions
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index 989d6986399..d9acec50617 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -839,29 +839,38 @@ text_substring(Datum str, int32 start, int32 length, bool length_not_specified)
int32 S = start; /* start position */
int32 S1; /* adjusted start position */
int32 L1; /* adjusted substring length */
+ int32 E; /* end position */
+
+ /*
+ * SQL99 says S can be zero or negative, but we still must fetch from the
+ * start of the string.
+ */
+ S1 = Max(S, 1);
/* life is easy if the encoding max length is 1 */
if (eml == 1)
{
- S1 = Max(S, 1);
-
if (length_not_specified) /* special case - get length to end of
* string */
L1 = -1;
- else
+ else if (length < 0)
+ {
+ /* SQL99 says to throw an error for E < S, i.e., negative length */
+ ereport(ERROR,
+ (errcode(ERRCODE_SUBSTRING_ERROR),
+ errmsg("negative substring length not allowed")));
+ L1 = -1; /* silence stupider compilers */
+ }
+ else if (pg_add_s32_overflow(S, length, &E))
{
- /* end position */
- int E = S + length;
-
/*
- * A negative value for L is the only way for the end position to
- * be before the start. SQL99 says to throw an error.
+ * L could be large enough for S + L to overflow, in which case
+ * the substring must run to end of string.
*/
- if (E < S)
- ereport(ERROR,
- (errcode(ERRCODE_SUBSTRING_ERROR),
- errmsg("negative substring length not allowed")));
-
+ L1 = -1;
+ }
+ else
+ {
/*
* A zero or negative value for the end position can happen if the
* start was negative or one. SQL99 says to return a zero-length
@@ -875,8 +884,8 @@ text_substring(Datum str, int32 start, int32 length, bool length_not_specified)
/*
* If the start position is past the end of the string, SQL99 says to
- * return a zero-length string -- PG_GETARG_TEXT_P_SLICE() will do
- * that for us. Convert to zero-based starting position
+ * return a zero-length string -- DatumGetTextPSlice() will do that
+ * for us. We need only convert S1 to zero-based starting position.
*/
return DatumGetTextPSlice(str, S1 - 1, L1);
}
@@ -898,12 +907,6 @@ text_substring(Datum str, int32 start, int32 length, bool length_not_specified)
text *ret;
/*
- * if S is past the end of the string, the tuple toaster will return a
- * zero-length string to us
- */
- S1 = Max(S, 1);
-
- /*
* We need to start at position zero because there is no way to know
* in advance which byte offset corresponds to the supplied start
* position.
@@ -913,19 +916,24 @@ text_substring(Datum str, int32 start, int32 length, bool length_not_specified)
if (length_not_specified) /* special case - get length to end of
* string */
slice_size = L1 = -1;
- else
+ else if (length < 0)
+ {
+ /* SQL99 says to throw an error for E < S, i.e., negative length */
+ ereport(ERROR,
+ (errcode(ERRCODE_SUBSTRING_ERROR),
+ errmsg("negative substring length not allowed")));
+ slice_size = L1 = -1; /* silence stupider compilers */
+ }
+ else if (pg_add_s32_overflow(S, length, &E))
{
- int E = S + length;
-
/*
- * A negative value for L is the only way for the end position to
- * be before the start. SQL99 says to throw an error.
+ * L could be large enough for S + L to overflow, in which case
+ * the substring must run to end of string.
*/
- if (E < S)
- ereport(ERROR,
- (errcode(ERRCODE_SUBSTRING_ERROR),
- errmsg("negative substring length not allowed")));
-
+ slice_size = L1 = -1;
+ }
+ else
+ {
/*
* A zero or negative value for the end position can happen if the
* start was negative or one. SQL99 says to return a zero-length
@@ -943,8 +951,10 @@ text_substring(Datum str, int32 start, int32 length, bool length_not_specified)
/*
* Total slice size in bytes can't be any longer than the start
* position plus substring length times the encoding max length.
+ * If that overflows, we can just use -1.
*/
- slice_size = (S1 + L1) * eml;
+ if (pg_mul_s32_overflow(E, eml, &slice_size))
+ slice_size = -1;
}
/*
@@ -3246,9 +3256,13 @@ bytea_substring(Datum str,
int L,
bool length_not_specified)
{
- int S1; /* adjusted start position */
- int L1; /* adjusted substring length */
+ int32 S1; /* adjusted start position */
+ int32 L1; /* adjusted substring length */
+ int32 E; /* end position */
+ /*
+ * The logic here should generally match text_substring().
+ */
S1 = Max(S, 1);
if (length_not_specified)
@@ -3259,20 +3273,24 @@ bytea_substring(Datum str,
*/
L1 = -1;
}
- else
+ else if (L < 0)
+ {
+ /* SQL99 says to throw an error for E < S, i.e., negative length */
+ ereport(ERROR,
+ (errcode(ERRCODE_SUBSTRING_ERROR),
+ errmsg("negative substring length not allowed")));
+ L1 = -1; /* silence stupider compilers */
+ }
+ else if (pg_add_s32_overflow(S, L, &E))
{
- /* end position */
- int E = S + L;
-
/*
- * A negative value for L is the only way for the end position to be
- * before the start. SQL99 says to throw an error.
+ * L could be large enough for S + L to overflow, in which case the
+ * substring must run to end of string.
*/
- if (E < S)
- ereport(ERROR,
- (errcode(ERRCODE_SUBSTRING_ERROR),
- errmsg("negative substring length not allowed")));
-
+ L1 = -1;
+ }
+ else
+ {
/*
* A zero or negative value for the end position can happen if the
* start was negative or one. SQL99 says to return a zero-length
@@ -3287,7 +3305,7 @@ bytea_substring(Datum str,
/*
* If the start position is past the end of the string, SQL99 says to
* return a zero-length string -- DatumGetByteaPSlice() will do that for
- * us. Convert to zero-based starting position
+ * us. We need only convert S1 to zero-based starting position.
*/
return DatumGetByteaPSlice(str, S1 - 1, L1);
}