| /* |
| 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 22. September 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 "lib/stringinfo.h" |
| |
| #include "catalog/pg_type.h" |
| #include "libpq/pqformat.h" |
| #include "utils/array.h" |
| #include "utils/memutils.h" |
| #include "utils/lsyscache.h" |
| #include "access/tupmacs.h" |
| #include "orafunc.h" |
| #include "builtins.h" |
| |
| PG_FUNCTION_INFO_V1(plvsubst_string_array); |
| PG_FUNCTION_INFO_V1(plvsubst_string_string); |
| PG_FUNCTION_INFO_V1(plvsubst_setsubst); |
| PG_FUNCTION_INFO_V1(plvsubst_setsubst_default); |
| PG_FUNCTION_INFO_V1(plvsubst_subst); |
| |
| #define C_SUBST "%s" |
| |
| |
| text *c_subst = NULL; |
| |
| static void |
| init_c_subst() |
| { |
| if (!c_subst) |
| { |
| MemoryContext oldctx; |
| |
| oldctx = MemoryContextSwitchTo(TopMemoryContext); |
| c_subst = cstring_to_text(C_SUBST); |
| MemoryContextSwitchTo(oldctx); |
| } |
| } |
| |
| static void |
| set_c_subst(text *sc) |
| { |
| MemoryContext oldctx; |
| |
| if (c_subst) |
| pfree(c_subst); |
| |
| oldctx = MemoryContextSwitchTo(TopMemoryContext); |
| c_subst = sc ? TextPCopy(sc) : cstring_to_text(C_SUBST); |
| MemoryContextSwitchTo(oldctx); |
| } |
| |
| static text* |
| plvsubst_string(text *template_in, ArrayType *vals_in, text *c_subst, FunctionCallInfo fcinfo) |
| { |
| ArrayType *v = vals_in; |
| int nitems, |
| *dims, |
| ndims; |
| char *p; |
| int16 typlen; |
| bool typbyval; |
| char typalign; |
| char typdelim; |
| Oid typelem; |
| Oid typiofunc; |
| FmgrInfo proc; |
| int i = 0, items = 0; |
| StringInfo sinfo; |
| const char *template_str; |
| int template_len; |
| char *sizes; |
| int *positions; |
| int subst_mb_len; |
| int subst_len; |
| const bits8 *bitmap; |
| int bitmask; |
| |
| if (v != NULL && (ndims = ARR_NDIM(v)) > 0) |
| { |
| if (ndims != 1) |
| ereport(ERROR, |
| (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
| errmsg("invalid parameter"), |
| errdetail("Array of arguments has wrong dimension: %d", ndims))); |
| |
| p = ARR_DATA_PTR(v); |
| dims = ARR_DIMS(v); |
| nitems = ArrayGetNItems(ndims, dims); |
| bitmap = ARR_NULLBITMAP(v); |
| get_type_io_data(ARR_ELEMTYPE(v), IOFunc_output, |
| &typlen, &typbyval, |
| &typalign, &typdelim, |
| &typelem, &typiofunc); |
| fmgr_info_cxt(typiofunc, &proc, fcinfo->flinfo->fn_mcxt); |
| } |
| else |
| { |
| nitems = 0; |
| p = NULL; |
| bitmap = NULL; |
| } |
| |
| template_str = VARDATA(template_in); |
| template_len = ora_mb_strlen(template_in, &sizes, &positions); |
| subst_mb_len = ora_mb_strlen1(c_subst); |
| subst_len = VARSIZE_ANY_EXHDR(c_subst); |
| sinfo = makeStringInfo(); |
| |
| bitmask = 1; |
| for (i = 0; i < template_len; i++) |
| { |
| if (strncmp(&template_str[positions[i]], VARDATA(c_subst), subst_len) == 0) |
| { |
| Datum itemvalue; |
| char *value; |
| |
| if (items++ < nitems) |
| { |
| if (bitmap && (*bitmap & bitmask) == 0) |
| value = pstrdup("NULL"); |
| else |
| { |
| itemvalue = fetch_att(p, typbyval, typlen); |
| value = DatumGetCString(FunctionCall3(&proc, |
| itemvalue, |
| ObjectIdGetDatum(typelem), |
| Int32GetDatum(-1))); |
| |
| p = att_addlength_pointer(p, typlen, p); |
| p = (char *) att_align_nominal(p, typalign); |
| } |
| appendStringInfoString(sinfo, value); |
| pfree(value); |
| |
| if (bitmap) |
| { |
| bitmask <<= 1; |
| if (bitmask == 0x100) |
| { |
| bitmap++; |
| bitmask = 1; |
| } |
| } |
| } |
| else |
| ereport(ERROR, |
| (errcode(ERRCODE_SYNTAX_ERROR), |
| errmsg("too few parameters specified for template string"))); |
| |
| i += subst_mb_len - 1; |
| } |
| else |
| appendBinaryStringInfo(sinfo, &template_str[positions[i]], sizes[i]); |
| } |
| |
| return cstring_to_text(sinfo->data); |
| } |
| |
| |
| Datum |
| plvsubst_string_array(PG_FUNCTION_ARGS) |
| { |
| init_c_subst(); |
| |
| if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) |
| PG_RETURN_NULL(); |
| |
| PG_RETURN_TEXT_P(plvsubst_string(PG_GETARG_TEXT_P(0), |
| PG_GETARG_ARRAYTYPE_P(1), |
| PG_ARGISNULL(2) ? c_subst : PG_GETARG_TEXT_P(2), |
| fcinfo)); |
| } |
| |
| Datum |
| plvsubst_string_string(PG_FUNCTION_ARGS) |
| { |
| Datum r; |
| ArrayType *array; |
| FunctionCallInfoData locfcinfo; |
| |
| init_c_subst(); |
| |
| if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) |
| PG_RETURN_NULL(); |
| |
| /* |
| * I can't use DirectFunctionCall2 |
| */ |
| |
| InitFunctionCallInfoData(locfcinfo, fcinfo->flinfo, 2, NULL, NULL); |
| locfcinfo.arg[0] = PG_GETARG_DATUM(1); |
| locfcinfo.arg[1] = PG_GETARG_IF_EXISTS(2, DATUM, CStringGetTextDatum(",")); |
| locfcinfo.argnull[0] = false; |
| locfcinfo.argnull[1] = false; |
| r = text_to_array(&locfcinfo); |
| |
| if (locfcinfo.isnull || r == (Datum) 0) |
| array = NULL; |
| else |
| array = DatumGetArrayTypeP(r); |
| |
| PG_RETURN_TEXT_P(plvsubst_string(PG_GETARG_TEXT_P(0), |
| array, |
| PG_GETARG_IF_EXISTS(3, TEXT_P, c_subst), |
| fcinfo)); |
| } |
| |
| Datum |
| plvsubst_setsubst(PG_FUNCTION_ARGS) |
| { |
| if (PG_ARGISNULL(0)) |
| ereport(ERROR, |
| (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), |
| errmsg("substition is NULL"), |
| errdetail("Substitution keyword may not be NULL."))); |
| |
| set_c_subst(PG_GETARG_TEXT_P(0)); |
| PG_RETURN_VOID(); |
| } |
| |
| Datum |
| plvsubst_setsubst_default(PG_FUNCTION_ARGS) |
| { |
| set_c_subst(NULL); |
| PG_RETURN_VOID(); |
| } |
| |
| |
| Datum |
| plvsubst_subst(PG_FUNCTION_ARGS) |
| { |
| init_c_subst(); |
| PG_RETURN_TEXT_P(TextPCopy(c_subst)); |
| } |