typedef struct
{
bool is_multibyte; /* T if multibyte encoding */
- bool is_multibyte_char_in_char;
+ bool is_multibyte_char_in_char; /* need to check char boundaries? */
char *str1; /* haystack string */
char *str2; /* needle string */
/*
* Return a pointer to the current match.
*
- * The returned pointer points into correct position in the original
- * the haystack string.
+ * The returned pointer points into the original haystack string.
*/
static char *
text_position_get_match_ptr(TextPositionState *state)
}
}
+/*
+ * Reset search state to the initial state installed by text_position_setup.
+ *
+ * The next call to text_position_next will search from the beginning
+ * of the string.
+ */
+static void
+text_position_reset(TextPositionState *state)
+{
+ state->last_match = NULL;
+ state->refpoint = state->str1;
+ state->refpos = 0;
+}
+
static void
text_position_cleanup(TextPositionState *state)
{
/* no cleanup needed */
}
+
static void
check_collation_set(Oid collid)
{
/*
* split_part
- * parse input string
- * return ord item (1 based)
- * based on provided field separator
+ * parse input string based on provided field separator
+ * return N'th item (1 based, negative counts from end)
*/
Datum
split_part(PG_FUNCTION_ARGS)
bool found;
/* field number is 1 based */
- if (fldnum < 1)
+ if (fldnum == 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("field position must be greater than zero")));
+ errmsg("field position must not be zero")));
inputstring_len = VARSIZE_ANY_EXHDR(inputstring);
fldsep_len = VARSIZE_ANY_EXHDR(fldsep);
if (inputstring_len < 1)
PG_RETURN_TEXT_P(cstring_to_text(""));
- /* empty field separator */
+ /* handle empty field separator */
if (fldsep_len < 1)
{
- /* if first field, return input string, else empty string */
- if (fldnum == 1)
+ /* if first or last field, return input string, else empty string */
+ if (fldnum == 1 || fldnum == -1)
PG_RETURN_TEXT_P(inputstring);
else
PG_RETURN_TEXT_P(cstring_to_text(""));
}
+ /* find the first field separator */
text_position_setup(inputstring, fldsep, PG_GET_COLLATION(), &state);
- /* identify bounds of first field */
- start_ptr = VARDATA_ANY(inputstring);
found = text_position_next(&state);
/* special case if fldsep not found at all */
if (!found)
{
text_position_cleanup(&state);
- /* if field 1 requested, return input string, else empty string */
- if (fldnum == 1)
+ /* if first or last field, return input string, else empty string */
+ if (fldnum == 1 || fldnum == -1)
PG_RETURN_TEXT_P(inputstring);
else
PG_RETURN_TEXT_P(cstring_to_text(""));
}
+
+ /*
+ * take care of a negative field number (i.e. count from the right) by
+ * converting to a positive field number; we need total number of fields
+ */
+ if (fldnum < 0)
+ {
+ /* we found a fldsep, so there are at least two fields */
+ int numfields = 2;
+
+ while (text_position_next(&state))
+ numfields++;
+
+ /* special case of last field does not require an extra pass */
+ if (fldnum == -1)
+ {
+ start_ptr = text_position_get_match_ptr(&state) + fldsep_len;
+ end_ptr = VARDATA_ANY(inputstring) + inputstring_len;
+ text_position_cleanup(&state);
+ PG_RETURN_TEXT_P(cstring_to_text_with_len(start_ptr,
+ end_ptr - start_ptr));
+ }
+
+ /* else, convert fldnum to positive notation */
+ fldnum += numfields + 1;
+
+ /* if nonexistent field, return empty string */
+ if (fldnum <= 0)
+ {
+ text_position_cleanup(&state);
+ PG_RETURN_TEXT_P(cstring_to_text(""));
+ }
+
+ /* reset to pointing at first match, but now with positive fldnum */
+ text_position_reset(&state);
+ found = text_position_next(&state);
+ Assert(found);
+ }
+
+ /* identify bounds of first field */
+ start_ptr = VARDATA_ANY(inputstring);
end_ptr = text_position_get_match_ptr(&state);
while (found && --fldnum > 0)
--
-- test split_part
--
+select split_part('','@',1) AS "empty string";
+ empty string
+--------------
+
+(1 row)
+
+select split_part('','@',-1) AS "empty string";
+ empty string
+--------------
+
+(1 row)
+
+select split_part('joeuser@mydatabase','',1) AS "joeuser@mydatabase";
+ joeuser@mydatabase
+--------------------
+ joeuser@mydatabase
+(1 row)
+
+select split_part('joeuser@mydatabase','',2) AS "empty string";
+ empty string
+--------------
+
+(1 row)
+
+select split_part('joeuser@mydatabase','',-1) AS "joeuser@mydatabase";
+ joeuser@mydatabase
+--------------------
+ joeuser@mydatabase
+(1 row)
+
+select split_part('joeuser@mydatabase','',-2) AS "empty string";
+ empty string
+--------------
+
+(1 row)
+
select split_part('joeuser@mydatabase','@',0) AS "an error";
-ERROR: field position must be greater than zero
+ERROR: field position must not be zero
+select split_part('joeuser@mydatabase','@@',1) AS "joeuser@mydatabase";
+ joeuser@mydatabase
+--------------------
+ joeuser@mydatabase
+(1 row)
+
+select split_part('joeuser@mydatabase','@@',2) AS "empty string";
+ empty string
+--------------
+
+(1 row)
+
select split_part('joeuser@mydatabase','@',1) AS "joeuser";
joeuser
---------
joeuser
(1 row)
+select split_part('joeuser@mydatabase','@',-1) AS "mydatabase";
+ mydatabase
+------------
+ mydatabase
+(1 row)
+
+select split_part('joeuser@mydatabase','@',-2) AS "joeuser";
+ joeuser
+---------
+ joeuser
+(1 row)
+
+select split_part('joeuser@mydatabase','@',-3) AS "empty string";
+ empty string
+--------------
+
+(1 row)
+
+select split_part('@joeuser@mydatabase@','@',-2) AS "mydatabase";
+ mydatabase
+------------
+ mydatabase
+(1 row)
+
--
-- test to_hex
--
--
-- test split_part
--
+select split_part('','@',1) AS "empty string";
+
+select split_part('','@',-1) AS "empty string";
+
+select split_part('joeuser@mydatabase','',1) AS "joeuser@mydatabase";
+
+select split_part('joeuser@mydatabase','',2) AS "empty string";
+
+select split_part('joeuser@mydatabase','',-1) AS "joeuser@mydatabase";
+
+select split_part('joeuser@mydatabase','',-2) AS "empty string";
+
select split_part('joeuser@mydatabase','@',0) AS "an error";
+select split_part('joeuser@mydatabase','@@',1) AS "joeuser@mydatabase";
+
+select split_part('joeuser@mydatabase','@@',2) AS "empty string";
+
select split_part('joeuser@mydatabase','@',1) AS "joeuser";
select split_part('joeuser@mydatabase','@',2) AS "mydatabase";
select split_part('@joeuser@mydatabase@','@',2) AS "joeuser";
+select split_part('joeuser@mydatabase','@',-1) AS "mydatabase";
+
+select split_part('joeuser@mydatabase','@',-2) AS "joeuser";
+
+select split_part('joeuser@mydatabase','@',-3) AS "empty string";
+
+select split_part('@joeuser@mydatabase@','@',-2) AS "mydatabase";
+
--
-- test to_hex
--