blob: 8e8b97e48fe4e32bb6f322141104d73ddff80dd4 [file] [log] [blame]
/*
This code implements one part of functonality of
free available library PL/Vision. Please look www.quest.com
Original author: Steven Feuerstein, 1996 - 2002
PostgreSQL implementation author: Pavel Stehule, 2006
This module is under BSD Licence
History:
1.0. first public version 13. March 2006
*/
#include "postgres.h"
#include "utils/builtins.h"
#include "utils/numeric.h"
#include "string.h"
#include "stdlib.h"
#include "utils/pg_locale.h"
#include "mb/pg_wchar.h"
#include "nodes/execnodes.h"
#include "catalog/pg_type.h"
#include "libpq/pqformat.h"
#include "orafunc.h"
#include "builtins.h"
PG_FUNCTION_INFO_V1(plvstr_rvrs);
PG_FUNCTION_INFO_V1(plvstr_normalize);
PG_FUNCTION_INFO_V1(plvstr_is_prefix);
PG_FUNCTION_INFO_V1(plvstr_is_prefix_text);
PG_FUNCTION_INFO_V1(plvstr_is_prefix_int);
PG_FUNCTION_INFO_V1(plvstr_is_prefix_int64);
PG_FUNCTION_INFO_V1(plvstr_lpart);
PG_FUNCTION_INFO_V1(plvstr_rpart);
PG_FUNCTION_INFO_V1(plvstr_lstrip);
PG_FUNCTION_INFO_V1(plvstr_rstrip);
PG_FUNCTION_INFO_V1(plvstr_left);
PG_FUNCTION_INFO_V1(plvstr_right);
PG_FUNCTION_INFO_V1(plvstr_substr2);
PG_FUNCTION_INFO_V1(plvstr_substr3);
PG_FUNCTION_INFO_V1(plvstr_instr2);
PG_FUNCTION_INFO_V1(plvstr_instr3);
PG_FUNCTION_INFO_V1(plvstr_instr4);
PG_FUNCTION_INFO_V1(plvstr_betwn_i);
PG_FUNCTION_INFO_V1(plvstr_betwn_c);
PG_FUNCTION_INFO_V1(plvstr_swap);
PG_FUNCTION_INFO_V1(plvchr_nth);
PG_FUNCTION_INFO_V1(plvchr_first);
PG_FUNCTION_INFO_V1(plvchr_last);
PG_FUNCTION_INFO_V1(plvchr_is_kind_i);
PG_FUNCTION_INFO_V1(plvchr_is_kind_a);
PG_FUNCTION_INFO_V1(plvchr_char_name);
PG_FUNCTION_INFO_V1(oracle_substr2);
PG_FUNCTION_INFO_V1(oracle_substr3);
static text *ora_substr(Datum str, int start, int len);
#define ora_substr_text(str, start, len) \
ora_substr(PointerGetDatum((str)), (start), (len))
static const char* char_names[] = {
"NULL","SOH","STX","ETX","EOT","ENQ","ACK","DEL",
"BS", "HT", "NL", "VT", "NP", "CR", "SO", "SI",
"DLE", "DC1","DC2","DC3","DC4","NAK","SYN","ETB",
"CAN", "EM","SUB","ESC","FS","GS","RS","US","SP"
};
#define NON_EMPTY_CHECK(str) \
if (VARSIZE_ANY_EXHDR(str) == 0) \
ereport(ERROR, \
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), \
errmsg("invalid parameter"), \
errdetail("Empty string is not allowed.")));
#define PARAMETER_ERROR(detail) \
ereport(ERROR, \
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), \
errmsg("invalid parameter"), \
errdetail(detail)));
#ifndef _pg_mblen
#define _pg_mblen pg_mblen
#endif
typedef enum
{
POSITION,
FIRST,
LAST
} position_mode;
#if PG_VERSION_NUM < 80400
text *
cstring_to_text_with_len(const char *c, int n)
{
text *result;
result = palloc(n + VARHDRSZ);
SET_VARSIZE(result, n + VARHDRSZ);
memcpy(VARDATA(result), c, n);
return result;
}
#endif
/*
* Make substring, can handle negative start
*
*/
int
ora_mb_strlen(text *str, char **sizes, int **positions)
{
int r_len;
int cur_size = 0;
int sz;
char *p;
int cur = 0;
p = VARDATA_ANY(str);
r_len = VARSIZE_ANY_EXHDR(str);
if (NULL != sizes)
*sizes = palloc(r_len * sizeof(char));
if (NULL != positions)
*positions = palloc(r_len * sizeof(int));
while (cur < r_len)
{
sz = _pg_mblen(p);
if (sizes)
(*sizes)[cur_size] = sz;
if (positions)
(*positions)[cur_size] = cur;
cur += sz;
p += sz;
cur_size += 1;
}
return cur_size;
}
int
ora_mb_strlen1(text *str)
{
int r_len;
int c;
char *p;
r_len = VARSIZE_ANY_EXHDR(str);
if (pg_database_encoding_max_length() == 1)
return r_len;
p = VARDATA_ANY(str);
c = 0;
while (r_len > 0)
{
int sz;
sz = _pg_mblen(p);
p += sz;
r_len -= sz;
c += 1;
}
return c;
}
/*
* len < 0 means "length is not specified".
*/
static text *
ora_substr(Datum str, int start, int len)
{
if (start == 0)
start = 1; /* 0 is interpreted as 1 */
else if (start < 0)
{
text *t;
int32 n;
t = DatumGetTextPP(str);
n = pg_mbstrlen_with_len(VARDATA_ANY(t), VARSIZE_ANY_EXHDR(t));
start = n + start + 1;
if (start <= 0)
return cstring_to_text("");
str = PointerGetDatum(t); /* save detoasted text */
}
if (len < 0)
return DatumGetTextP(DirectFunctionCall2(text_substr_no_len,
str, Int32GetDatum(start)));
else
return DatumGetTextP(DirectFunctionCall3(text_substr,
str, Int32GetDatum(start), Int32GetDatum(len)));
}
/*
* Simply search algorithm. Could be greatly improved if we
* used KMP. Morris-Boyer would be more efficient but it might
* give us head aches in the presence of multibyte. For example, see
* `CHINESE STRING SEARCHING USING THE KMP ALGORITHM' by R. Luk.
*/
static int
ora_instr_mb(text *txt, text *pattern, int start, int nth)
{
int c_len_txt, c_len_pat;
int b_len_pat;
int *pos_txt;
const char *str_txt, *str_pat;
int beg, end, i, dx;
str_txt = VARDATA_ANY(txt);
c_len_txt = ora_mb_strlen(txt, NULL, &pos_txt);
str_pat = VARDATA_ANY(pattern);
b_len_pat = VARSIZE_ANY_EXHDR(pattern);
c_len_pat = pg_mbstrlen_with_len(str_pat, b_len_pat);
if (start > 0)
{
dx = 1;
beg = start - 1;
end = c_len_txt - c_len_pat + 1;
if (beg >= end)
return 0; /* out of range */
}
else
{
dx = -1;
beg = Min(c_len_txt + start, c_len_txt - c_len_pat);
end = -1;
if (beg <= end)
return 0; /* out of range */
}
for (i = beg; i != end; i += dx)
{
if (memcmp(str_txt + pos_txt[i], str_pat, b_len_pat) == 0)
{
if (--nth == 0)
return i + 1;
}
}
return 0;
}
int
ora_instr(text *txt, text *pattern, int start, int nth)
{
int len_txt, len_pat;
const char *str_txt, *str_pat;
int beg, end, i, dx;
if (nth <= 0)
PARAMETER_ERROR("Fourth parameter must be a positive integer.");
/* Forward for multibyte strings */
if (pg_database_encoding_max_length() > 1)
return ora_instr_mb(txt, pattern, start, nth);
str_txt = VARDATA_ANY(txt);
len_txt = VARSIZE_ANY_EXHDR(txt);
str_pat = VARDATA_ANY(pattern);
len_pat = VARSIZE_ANY_EXHDR(pattern);
if (start > 0)
{
dx = 1;
beg = start - 1;
end = len_txt - len_pat + 1;
if (beg >= end)
return 0; /* out of range */
}
else
{
dx = -1;
beg = Min(len_txt + start, len_txt - len_pat);
end = -1;
if (beg <= end)
return 0; /* out of range */
}
for (i = beg; i != end; i += dx)
{
if (memcmp(str_txt + i, str_pat, len_pat) == 0)
{
if (--nth == 0)
return i + 1;
}
}
return 0;
}
/****************************************************************
* PLVstr.normalize
*
* Syntax:
* FUNCTION plvstr.normalize (string_in IN VARCHAR)
* RETURN VARCHAR;
*
* Purpose:
* Normalize string - replace white chars by space, replace
* spaces by space
*
****************************************************************/
Datum
plvstr_normalize(PG_FUNCTION_ARGS)
{
text *str = PG_GETARG_TEXT_PP(0);
text *result;
char *aux, *aux_cur;
int i, l;
char c, *cur;
bool write_spc = false;
bool ignore_stsp = true;
bool mb_encode;
int sz;
mb_encode = pg_database_encoding_max_length() > 1;
l = VARSIZE_ANY_EXHDR(str);
aux_cur = aux = palloc(l);
write_spc = false;
cur = VARDATA_ANY(str);
for (i = 0; i < l; i++)
{
switch ((c = *cur))
{
case '\t':
case '\n':
case '\r':
case ' ':
write_spc = ignore_stsp ? false : true;
break;
default:
/* ignore all other unvisible chars */
if (mb_encode)
{
sz = _pg_mblen(cur);
if (sz > 1 || (sz == 1 && c > 32))
{
int j;
if (write_spc)
{
*aux_cur++ = ' ';
write_spc = false;
}
for (j = 0; j < sz; j++)
{
*aux_cur++ = *cur++;
}
ignore_stsp = false;
i += sz - 1;
}
continue;
}
else
if (c > 32)
{
if (write_spc)
{
*aux_cur++ = ' ';
write_spc = false;
}
*aux_cur++ = c;
ignore_stsp = false;
continue;
}
}
cur += 1;
}
l = aux_cur - aux;
result = palloc(l+VARHDRSZ);
SET_VARSIZE(result, l + VARHDRSZ);
memcpy(VARDATA(result), aux, l);
PG_RETURN_TEXT_P(result);
}
/****************************************************************
* PLVstr.instr
*
* Syntax:
* FUNCTION plvstr.instr (string_in VARCHAR, pattern VARCHAR)
* FUNCTION plvstr.instr (string_in VARCHAR, pattern VARCHAR,
* start_in INTEGER)
* FUNCTION plvstr.instr (string_in VARCHAR, pattern VARCHAR,
* start_in INTEGER, nth INTEGER)
* RETURN INT;
*
* Purpose:
* Search pattern in string.
*
****************************************************************/
Datum
plvstr_instr2 (PG_FUNCTION_ARGS)
{
text *arg1 = PG_GETARG_TEXT_PP(0);
text *arg2 = PG_GETARG_TEXT_PP(1);
PG_RETURN_INT32(ora_instr(arg1, arg2, 1, 1));
}
Datum
plvstr_instr3 (PG_FUNCTION_ARGS)
{
text *arg1 = PG_GETARG_TEXT_PP(0);
text *arg2 = PG_GETARG_TEXT_PP(1);
int arg3 = PG_GETARG_INT32(2);
PG_RETURN_INT32(ora_instr(arg1, arg2, arg3, 1));
}
Datum
plvstr_instr4 (PG_FUNCTION_ARGS)
{
text *arg1 = PG_GETARG_TEXT_PP(0);
text *arg2 = PG_GETARG_TEXT_PP(1);
int arg3 = PG_GETARG_INT32(2);
int arg4 = PG_GETARG_INT32(3);
PG_RETURN_INT32(ora_instr(arg1, arg2, arg3, arg4));
}
/****************************************************************
* PLVstr.is_prefix
*
* Syntax:
* FUNCTION plvstr.is_prefix (string_in IN VARCHAR,
* prefix_in VARCHAR,
* case_sensitive BOOL := true)
* RETURN bool;
* FUNCTION plvstr.is_prefix (num_in IN NUMERIC,
* prefix_in NUMERIC) RETURN bool;
* FUNCTION plvstr.is_prefix (int_in IN INT,
* prefix_in INT) RETURN bool;
*
* Purpose:
* Returns true, if prefix_in is prefix of string_in
*
****************************************************************/
Datum
plvstr_is_prefix_text (PG_FUNCTION_ARGS)
{
text *str = PG_GETARG_TEXT_PP(0);
text *prefix = PG_GETARG_TEXT_PP(1);
bool case_sens = PG_GETARG_BOOL(2);
bool mb_encode;
int str_len = VARSIZE_ANY_EXHDR(str);
int pref_len = VARSIZE_ANY_EXHDR(prefix);
int i;
char *ap, *bp;
mb_encode = pg_database_encoding_max_length() > 1;
if (mb_encode && !case_sens)
{
str = (text*)DatumGetPointer(DirectFunctionCall1(lower, PointerGetDatum(str)));
prefix = (text*)DatumGetPointer(DirectFunctionCall1(lower, PointerGetDatum(prefix)));
}
ap = VARDATA_ANY(str);
bp = VARDATA_ANY(prefix);
for (i = 0; i < pref_len; i++)
{
if (i >= str_len)
break;
if (case_sens || mb_encode)
{
if (*ap++ != *bp++)
break;
}
else if (!mb_encode)
{
if (pg_toupper((unsigned char) *ap++) != pg_toupper((unsigned char) *bp++))
break;
}
}
PG_RETURN_BOOL(i == pref_len);
}
Datum
plvstr_is_prefix_int (PG_FUNCTION_ARGS)
{
int n = PG_GETARG_INT32(0);
int prefix = PG_GETARG_INT32(1);
bool result = false;
do
{
if (n == prefix)
{
result = true;
break;
}
n = n / 10;
} while (n >= prefix);
PG_RETURN_BOOL(result);
}
Datum
plvstr_is_prefix_int64 (PG_FUNCTION_ARGS)
{
int64 n = PG_GETARG_INT64(0);
int64 prefix = PG_GETARG_INT64(1);
bool result = false;
do
{
if (n == prefix)
{
result = true;
break;
}
n = n / 10;
} while (n >= prefix);
PG_RETURN_BOOL(result);
}
/****************************************************************
* PLVstr.rvrs
*
* Syntax:
* FUNCTION plvstr.rvrs (string_in IN VARCHAR,
* start_in IN INTEGER := 1,
* end_in IN INTEGER := NULL)
* RETURN VARCHAR2;
*
* Purpose:
* Reverse string or part of string
*
****************************************************************/
Datum
plvstr_rvrs(PG_FUNCTION_ARGS)
{
text *str = PG_GETARG_TEXT_PP(0);
int start = PG_GETARG_INT32(1);
int end = PG_GETARG_INT32(2);
int len, aux;
int i;
int new_len;
text *result;
char *data;
char *sizes = NULL;
int *positions = NULL;
bool mb_encode;
if (PG_ARGISNULL(0))
PG_RETURN_NULL();
mb_encode = pg_database_encoding_max_length() > 1;
if (!mb_encode)
len = VARSIZE_ANY_EXHDR(str);
else
len = ora_mb_strlen(str, &sizes, &positions);
start = PG_ARGISNULL(1) ? 1 : start;
end = PG_ARGISNULL(2) ? (start < 0 ? -len : len) : end;
if ((start > end && start > 0) || (start < end && start < 0))
PARAMETER_ERROR("Third parameter must be greater than second.");
if (start < 0)
{
aux = len + end + 1;
end = len + start + 1;
start = end;
}
new_len = end - start + 1;
if (mb_encode)
{
int max_size;
int cur_size;
char *p;
int j;
int fz_size;
fz_size = VARSIZE_ANY_EXHDR(str);
if ((max_size = (new_len*pg_database_encoding_max_length())) > fz_size)
result = palloc(fz_size + VARHDRSZ);
else
result = palloc(max_size + VARHDRSZ);
data = (char*) VARDATA(result);
cur_size = 0;
p = VARDATA_ANY(str);
for (i = end - 1; i>= start - 1; i--)
{
for (j=0; j<sizes[i]; j++)
*data++ = *(p+positions[i]+j);
cur_size += sizes[i];
}
SET_VARSIZE(result, cur_size + VARHDRSZ);
}
else
{
char *p = VARDATA_ANY(str);
result = palloc(new_len + VARHDRSZ);
data = (char*) VARDATA(result);
SET_VARSIZE(result, new_len + VARHDRSZ);
for (i = end - 1; i >= start - 1; i--)
*data++ = p[i];
}
PG_RETURN_TEXT_P(result);
}
/****************************************************************
* PLVstr.lpart
*
* Syntax:
* FUNCTION PLVstr.lpart (string_in IN VARCHAR,
* divider_in IN VARCHAR,
* start_in IN INTEGER := 1,
* nth_in IN INTEGER := 1,
* all_if_notfound_in IN BOOLEAN := FALSE)
* RETURN VARCHAR2;
*
* Purpose:
* Call this function to return the left part of a string.
*
****************************************************************/
Datum
plvstr_lpart (PG_FUNCTION_ARGS)
{
text *str = PG_GETARG_TEXT_P(0);
text *div = PG_GETARG_TEXT_P(1);
int start = PG_GETARG_INT32(2);
int nth = PG_GETARG_INT32(3);
bool all_if_notfound = PG_GETARG_BOOL(4);
int loc;
loc = ora_instr(str, div, start, nth);
if (loc == 0)
{
if (all_if_notfound)
PG_RETURN_TEXT_P(TextPCopy(str));
else
PG_RETURN_NULL();
}
else
PG_RETURN_TEXT_P(ora_substr_text(str, 1, loc-1));
}
/****************************************************************
* PLVstr.rpart
*
* Syntax:
* FUNCTION PLVstr.rpart (string_in IN VARCHAR,
* divider_in IN VARCHAR,
* start_in IN INTEGER := 1,
* nth_in IN INTEGER := 1,
* all_if_notfound_in IN BOOLEAN := FALSE)
* RETURN VARCHAR2;
*
* Purpose:
* Call this function to return the right part of a string.
*
****************************************************************/
Datum
plvstr_rpart (PG_FUNCTION_ARGS)
{
text *str = PG_GETARG_TEXT_P(0);
text *div = PG_GETARG_TEXT_P(1);
int start = PG_GETARG_INT32(2);
int nth = PG_GETARG_INT32(3);
bool all_if_notfound = PG_GETARG_BOOL(4);
int loc;
loc = ora_instr(str, div, start, nth);
if (loc == 0)
{
if (all_if_notfound)
PG_RETURN_TEXT_P(TextPCopy(str));
else
PG_RETURN_NULL();
}
else
PG_RETURN_TEXT_P(ora_substr_text(str, loc+1, -1));
}
/****************************************************************
* PLVstr.lstrip
*
* Syntax:
* FUNCTION plvstr.lstrip (string_in IN VARCHAR,
* substring_in IN VARCHAR,
* num_in IN INTEGER := 1)
* RETURN VARCHAR;
*
* Purpose:
* Call this function to remove characters from the beginning
* (left) of a string.
*
****************************************************************/
Datum
plvstr_lstrip (PG_FUNCTION_ARGS)
{
text *str = PG_GETARG_TEXT_PP(0);
text *pat = PG_GETARG_TEXT_PP(1);
int num = PG_GETARG_INT32(2);
int count = 0;
int len_p, len_s, i;
char *str_p, *aux_str_p, *pat_p;
len_p = VARSIZE_ANY_EXHDR(pat);
len_s = VARSIZE_ANY_EXHDR(str);
str_p = VARDATA_ANY(str);
while (count < num)
{
pat_p = VARDATA_ANY(pat);
aux_str_p = str_p;
if (len_s < len_p)
break;
for (i = 0; i < len_p; i++)
if (*aux_str_p++ != *pat_p++)
break;
if (i >= len_p)
{
count++;
/* found */
str_p = aux_str_p;
len_s -= len_p;
continue;
}
break;
}
PG_RETURN_TEXT_P(cstring_to_text_with_len(str_p,len_s));
}
/****************************************************************
* PLVstr.rstrip
*
* Syntax:
* FUNCTION plvstr.rstrip (string_in IN VARCHAR,
* substring_in IN VARCHAR,
* num_in IN INTEGER := 1)
* RETURN VARCHAR;
*
* Purpose:
* Call this function to remove characters from the end
* (right) of a string.
*
****************************************************************/
Datum
plvstr_rstrip (PG_FUNCTION_ARGS)
{
text *str = PG_GETARG_TEXT_PP(0);
text *pat = PG_GETARG_TEXT_PP(1);
int num = PG_GETARG_INT32(2);
int count = 0;
int len_p, len_s, i;
char *str_p, *aux_str_p, *pat_p;
len_p = VARSIZE_ANY_EXHDR(pat);
len_s = VARSIZE_ANY_EXHDR(str);
str_p = VARDATA_ANY(str) + len_s - 1;
while (count < num)
{
pat_p = VARDATA_ANY(pat) + len_p - 1;
aux_str_p = str_p;
if (len_s < len_p)
break;
for (i = 0; i < len_p; i++)
if (*aux_str_p-- != *pat_p--)
break;
if (i >= len_p)
{
count++;
/* found */
str_p = aux_str_p;
len_s -= len_p;
continue;
}
break;
}
PG_RETURN_TEXT_P(cstring_to_text_with_len(VARDATA_ANY(str),len_s));
}
/****************************************************************
* PLVstr.left
*
* Syntax:
* FUNCTION plvstr.left (string_in IN VARCHAR,
* num_in INTEGER)
* RETURN VARCHAR;
*
* Purpose:
* Returns firs num_in charaters. You can use negative num_in
* left('abcde', -2) -> abc
*
****************************************************************/
Datum
plvstr_left (PG_FUNCTION_ARGS)
{
text *str = PG_GETARG_TEXT_P(0);
int n = PG_GETARG_INT32(1);
if (n < 0)
n = ora_mb_strlen1(str) + n;
n = n < 0 ? 0 : n;
PG_RETURN_TEXT_P(ora_substr_text(str, 1, n));
}
/****************************************************************
* PLVstr.right
*
* Syntax:
* FUNCTION plvstr.right (string_in IN VARCHAR,
* num_in INTEGER)
* RETURN VARCHAR;
*
* Purpose:
* Returns last (right) num_in characters.
*
****************************************************************/
Datum
plvstr_right (PG_FUNCTION_ARGS)
{
text *str = PG_GETARG_TEXT_P(0);
int n = PG_GETARG_INT32(1);
if (n < 0)
n = ora_mb_strlen1(str) + n;
n = (n < 0) ? 0 : n;
PG_RETURN_TEXT_P(ora_substr_text(str, -n, -1));
}
/****************************************************************
* PLVstr.substr2
*
* Syntax:
* FUNCTION plvstr.substr (string_in IN VARCHAR,
* start INTEGER)
* RETURN VARCHAR;
*
* Purpose:
* Returns substring started on start_in to end
*
****************************************************************/
Datum
plvstr_substr2 (PG_FUNCTION_ARGS)
{
return oracle_substr2(fcinfo);
}
/****************************************************************
* PLVstr.substr3
*
* Syntax:
* FUNCTION plvstr.substr (string_in IN VARCHAR,
* start INTEGER, len INTEGER)
* RETURN VARCHAR;
*
* Purpose:
* Returns len chars from start_in position
*
****************************************************************/
Datum
plvstr_substr3 (PG_FUNCTION_ARGS)
{
return oracle_substr3(fcinfo);
}
/****************************************************************
* PLVchr.nth
*
* Syntax:
* FUNCTION plvchr.nth (string_in IN VARCHAR,
* nth_in IN INTEGER)
* RETURN VARCHAR;
*
* Purpose:
* Call this function to return the Nth character in a string.
*
****************************************************************/
Datum
plvchr_nth (PG_FUNCTION_ARGS)
{
PG_RETURN_TEXT_P(ora_substr(PG_GETARG_DATUM(0), PG_GETARG_INT32(1), 1));
}
/****************************************************************
* PLVchr.first
*
* Syntax:
* FUNCTION plvchr.first (string_in IN VARCHAR,
* RETURN VARCHAR;
*
* Purpose:
* Call this function to return the first character in a string.
*
****************************************************************/
Datum
plvchr_first (PG_FUNCTION_ARGS)
{
PG_RETURN_TEXT_P(ora_substr(PG_GETARG_DATUM(0), 1, 1));
}
/****************************************************************
* PLVchr.last
*
* Syntax:
* FUNCTION plvchr.last (string_in IN VARCHAR,
* RETURN VARCHAR;
*
* Purpose:
* Call this function to return the last character in a string.
*
****************************************************************/
Datum
plvchr_last (PG_FUNCTION_ARGS)
{
PG_RETURN_TEXT_P(ora_substr(PG_GETARG_DATUM(0), -1, 1));
}
/****************************************************************
* PLVchr.is_blank, plvchr.is_digit, ...
*
* Syntax:
* FUNCTION plvchr.is_kind (string_in IN VARCHAR,
* kind INT)
* RETURN VARCHAR;
*
* Purpose:
* Call this function to see if a character is blank, ...
* 1 blank, 2 digit, 3 quote, 4 other, 5 letter
*
****************************************************************/
static bool
is_kind(char c, int kind)
{
switch (kind)
{
case 1:
return c ==' ';
case 2:
return !!(isdigit((unsigned char)c));
case 3:
return c == '\'';
case 4:
return
(32 <= c && c <= 47) ||
(58 <= c && c <= 64) ||
(91 <= c && c <= 96) || (123 <= c && c <= 126);
case 5:
return !!(isalpha((unsigned char)c));
default:
PARAMETER_ERROR("Second parameter isn't in enum {1,2,3,4,5}");
return false;
}
}
Datum
plvchr_is_kind_i (PG_FUNCTION_ARGS)
{
int32 c = PG_GETARG_INT32(0);
int32 k = PG_GETARG_INT32(1);
PG_RETURN_INT32(is_kind((char)c,k));
}
Datum
plvchr_is_kind_a (PG_FUNCTION_ARGS)
{
text *str = PG_GETARG_TEXT_PP(0);
int32 k = PG_GETARG_INT32(1);
char c;
NON_EMPTY_CHECK(str);
if (pg_database_encoding_max_length() > 1)
{
if (_pg_mblen(VARDATA_ANY(str)) > 1)
PG_RETURN_INT32( (k == 5) );
}
c = *VARDATA_ANY(str);
PG_RETURN_INT32(is_kind(c,k));
}
/****************************************************************
* PLVchr.char_name
*
* Syntax:
* FUNCTION plvchr.char_name (letter_in IN VARCHAR)
* RETURN VARCHAR;
*
* Purpose:
* Returns the name of the character to ascii code as a VARCHAR.
*
****************************************************************/
Datum
plvchr_char_name(PG_FUNCTION_ARGS)
{
text *str = PG_GETARG_TEXT_PP(0);
text *result;
unsigned char c;
NON_EMPTY_CHECK(str);
c = (unsigned char)*(VARDATA_ANY(str));
if (c >= lengthof(char_names))
result = ora_substr_text(str, 1, 1);
else
result = cstring_to_text(char_names[c]);
PG_RETURN_TEXT_P(result);
}
/****************************************************************
* substr
*
* Syntax:
* FUNCTION substr (string, start_position, [length])
* RETURN VARCHAR;
*
* Purpose:
* Returns len chars from start_in position, compatible with Oracle
*
****************************************************************/
Datum
oracle_substr3(PG_FUNCTION_ARGS)
{
int32 len = PG_GETARG_INT32(2);
if (len < 0)
PG_RETURN_NULL();
PG_RETURN_TEXT_P(ora_substr(PG_GETARG_DATUM(0), PG_GETARG_INT32(1), len));
}
Datum
oracle_substr2(PG_FUNCTION_ARGS)
{
PG_RETURN_TEXT_P(ora_substr(PG_GETARG_DATUM(0), PG_GETARG_INT32(1), -1));
}
static text*
ora_concat2(text *str1, text *str2)
{
int l1;
int l2;
text *result;
l1 = VARSIZE_ANY_EXHDR(str1);
l2 = VARSIZE_ANY_EXHDR(str2);
result = palloc(l1+l2+VARHDRSZ);
memcpy(VARDATA(result), VARDATA_ANY(str1), l1);
memcpy(VARDATA(result) + l1, VARDATA_ANY(str2), l2);
SET_VARSIZE(result, l1 + l2 + VARHDRSZ);
return result;
}
static text*
ora_concat3(text *str1, text *str2, text *str3)
{
int l1;
int l2;
int l3;
text *result;
l1 = VARSIZE_ANY_EXHDR(str1);
l2 = VARSIZE_ANY_EXHDR(str2);
l3 = VARSIZE_ANY_EXHDR(str3);
result = palloc(l1+l2+l3+VARHDRSZ);
memcpy(VARDATA(result), VARDATA_ANY(str1), l1);
memcpy(VARDATA(result) + l1, VARDATA_ANY(str2), l2);
memcpy(VARDATA(result) + l1+l2, VARDATA_ANY(str3), l3);
SET_VARSIZE(result, l1 + l2 + l3 + VARHDRSZ);
return result;
}
/****************************************************************
* PLVchr.swap
*
* Syntax:
* FUNCTION swap
* (string_in IN VARCHAR2,
* replace_in IN VARCHAR2,
* start_in IN INTEGER := 1,
* oldlen_in IN INTEGER := NULL)
* RETURN VARCHAR2
*
* Purpose:
* Replace a substring in a string with a specified string.
*
****************************************************************/
Datum
plvstr_swap(PG_FUNCTION_ARGS)
{
text *string_in;
text *replace_in;
int start_in = 1;
int oldlen_in;
int v_len;
if (PG_ARGISNULL(0))
PG_RETURN_NULL();
else
string_in = PG_GETARG_TEXT_P(0);
if (PG_ARGISNULL(1))
PG_RETURN_NULL();
else
replace_in = PG_GETARG_TEXT_P(1);
if (!PG_ARGISNULL(2))
start_in = PG_GETARG_INT32(2);
if (PG_ARGISNULL(3))
oldlen_in = ora_mb_strlen1(replace_in);
else
oldlen_in = PG_GETARG_INT32(3);
v_len = ora_mb_strlen1(string_in);
start_in = start_in > 0 ? start_in : v_len + start_in + 1;
if (start_in == 0 || start_in > v_len)
PG_RETURN_TEXT_P(TextPCopy(string_in));
else if (start_in == 1)
PG_RETURN_TEXT_P(ora_concat2(
replace_in, ora_substr_text(string_in, oldlen_in+1, -1)));
else
PG_RETURN_TEXT_P(ora_concat3(
ora_substr_text(string_in, 1, start_in - 1),
replace_in,
ora_substr_text(string_in, start_in + oldlen_in, -1)));
}
/****************************************************************
* PLVchr.betwn
*
* Find the Substring Between Start and End Locations
*
* Syntax:
* FUNCTION plvstr.betwn (string_in IN VARCHAR2,
* start_in IN INTEGER,
* end_in IN INTEGER,
* inclusive IN BOOLEAN := TRUE)
* RETURN VARCHAR2;
*
* FUNCTION plvstr.betwn (string_in IN VARCHAR2,
* start_in IN VARCHAR2,
* end_in IN VARCHAR2 := NULL,
* startnth_in IN INTEGER := 1,
* endnth_in IN INTEGER := 1,
* inclusive IN BOOLEAN := TRUE,
* gotoend IN BOOLEAN := FALSE)
* RETURN VARCHAR2;
*
* Purpose:
* Call this function to extract a sub-string from a string. This
* function is overloaded. You can either provide the start and end
* locations or you can provide start and end substrings.
*
****************************************************************/
Datum
plvstr_betwn_i(PG_FUNCTION_ARGS)
{
text *string_in = PG_GETARG_TEXT_P(0);
int start_in = PG_GETARG_INT32(1);
int end_in = PG_GETARG_INT32(2);
bool inclusive = PG_GETARG_BOOL(3);
if ((start_in < 0 && end_in > 0) ||
(start_in > 0 && end_in < 0) ||
(start_in > end_in))
PARAMETER_ERROR("Wrong positions.");
if (start_in < 0)
{
int v_len = ora_mb_strlen1(string_in);
start_in = v_len + start_in + 1;
end_in = v_len + start_in + 1;
}
if (!inclusive)
{
start_in += 1;
end_in -= 1;
if (start_in > end_in)
PG_RETURN_TEXT_P(cstring_to_text(""));
}
PG_RETURN_TEXT_P(ora_substr_text(string_in,
start_in,
end_in - start_in + 1));
}
Datum
plvstr_betwn_c(PG_FUNCTION_ARGS)
{
text *string_in;
text *start_in;
text *end_in;
int startnth_in;
int endnth_in;
bool inclusive;
bool gotoend;
int v_start;
int v_end;
if (PG_ARGISNULL(0) || PG_ARGISNULL(1) ||
PG_ARGISNULL(3) || PG_ARGISNULL(4) ||
PG_ARGISNULL(5) || PG_ARGISNULL(6))
PG_RETURN_NULL();
string_in = PG_GETARG_TEXT_P(0);
start_in = PG_GETARG_TEXT_P(1);
end_in = PG_ARGISNULL(2) ? start_in : PG_GETARG_TEXT_P(2);
startnth_in = PG_GETARG_INT32(3);
endnth_in = PG_GETARG_INT32(4);
inclusive = PG_GETARG_BOOL(5);
gotoend = PG_GETARG_BOOL(6);
if (startnth_in == 0)
{
v_start = 1;
v_end = ora_instr(string_in, end_in, 1, endnth_in);
}
else
{
v_start = ora_instr(string_in, start_in, 1, startnth_in);
v_end = ora_instr(string_in, end_in, v_start + 1, endnth_in);
}
if (v_start == 0)
PG_RETURN_NULL();
if (!inclusive)
{
if (startnth_in > 0)
v_start += ora_mb_strlen1(start_in);
v_end -= 1;
}
else
v_end += (ora_mb_strlen1(end_in) - 1);
if (((v_start > v_end) && (v_end > 0)) ||
(v_end <= 0 && !gotoend))
PG_RETURN_NULL();
if (v_end <= 0)
v_end = ora_mb_strlen1(string_in);
PG_RETURN_TEXT_P(ora_substr_text(string_in,
v_start,
v_end - v_start + 1));
}