/*---------------------------------------------------------------------------- * * charpad.c * LPAD and RPAD SQL functions for PostgreSQL. * *---------------------------------------------------------------------------- */ #include "postgres.h" #include "utils/builtins.h" #include "utils/formatting.h" #include "mb/pg_wchar.h" #include "fmgr.h" #include "orafce.h" #include "builtins.h" /* flags */ #define ON true #define OFF false /* Upper limit on total width of the padded output of *pad functions */ #define PAD_MAX 4000 PG_FUNCTION_INFO_V1(orafce_lpad); PG_FUNCTION_INFO_V1(orafce_rpad); /* * orafce_lpad(string text, length int32 [, fill text]) * * Fill up the string to length 'length' by prepending * the characters fill (a half-width space by default) */ Datum orafce_lpad(PG_FUNCTION_ARGS) { text *string1 = PG_GETARG_TEXT_PP(0); int32 output_width = PG_GETARG_INT32(1); text *string2 = PG_GETARG_TEXT_PP(2); text *ret; char *ptr1, *ptr2 = NULL, *ptr2start = NULL, *ptr2end = NULL, *ptr_ret, *spc = " "; int mlen, dsplen, s1blen, s2blen, hslen, total_blen = 0, s1_width = 0, s2_add_width = 0, s1_add_blen = 0, s2_add_blen = 0; bool s2_operate = ON, half_space = OFF, init_ptr = ON; /* validate output width (the 2nd argument) */ if (output_width < 0) output_width = 0; if (output_width > PAD_MAX) output_width = PAD_MAX; /* get byte-length of the 1st and 3rd argument strings */ s1blen = VARSIZE_ANY_EXHDR(string1); s2blen = VARSIZE_ANY_EXHDR(string2); /* validate the lengths */ if (s1blen < 0) s1blen = 0; if (s2blen < 0) s2blen = 0; /* if the filler length is zero disable filling */ if (s2blen == 0) { s2_operate = OFF; /* turn off string2 processing flag */ output_width = 0; /* same behavior as Oracle database */ } /* byte-length of half-width space */ hslen = pg_mblen(spc); /* * Calculate the length of the portion of string1 to include in * the final output */ ptr1 = VARDATA_ANY(string1); while (s1blen > 0) { /* byte-length and display length per character of string1 */ mlen = pg_mblen(ptr1); dsplen = pg_dsplen(ptr1); /* accumulate display length of string1 */ s1_width += dsplen; /* * if string1 is longer/wider than the requested output_width, * discard this character and prepend a half-width space instead */ if(s1_width >= output_width) { if(s1_width != output_width) { /* secure bytes for a half-width space in the final output */ if (output_width != 0) { s1_add_blen += hslen; half_space = ON; } } else /* exactly fits, so include this chracter */ { s1_add_blen += mlen; } /* * turn off string2 processing because string1 already * consumed output_width */ s2_operate = OFF; /* done with string1 */ break; } /* accumulate string1's portion of byte-length of the output */ s1_add_blen += mlen; /* advance one character within string1 */ ptr1 += mlen; /* loop counter */ s1blen -= mlen; } /* Calculate the length of the portion composed of string2 to use for padding */ if (s2_operate) { /* remaining part of output_width is composed of string2 */ s2_add_width = output_width - s1_width; ptr2 = ptr2start = VARDATA_ANY(string2); ptr2end = ptr2 + s2blen; while (s2_add_width > 0) { /* byte-length and display length per character of string2 */ mlen = pg_mblen(ptr2); dsplen = pg_dsplen(ptr2); /* * output_width can not fit this character of string2, so discard it and * prepend a half-width space instead */ if(dsplen > s2_add_width) { s2_add_blen += hslen; half_space = ON; /* done with string2 */ break; } /* accumulate string2's portion of byte-length of the output */ s2_add_blen += mlen; /* loop counter */ s2_add_width -= dsplen; /* advance one character within string2 */ ptr2 += mlen; /* when get to the end of string2, reset ptr2 to the start */ if (ptr2 == ptr2end) ptr2 = ptr2start; } } /* allocate enough space to contain output_width worth of characters */ total_blen = s1_add_blen + s2_add_blen; ret = (text *) palloc(VARHDRSZ + total_blen); ptr_ret = VARDATA(ret); /* * add a half-width space as a padding necessary to satisfy the required * output_width * * (memory already allocated as reserved by either s1_add_blen * or s2_add_blen) */ if (half_space) { memcpy(ptr_ret, spc, hslen); ptr_ret += hslen; } /* prepend string2 padding */ while(s2_add_blen > 0) { /* reset ptr2 to the string2 start */ if(init_ptr) { init_ptr = OFF; ptr2 = ptr2start; } mlen = pg_mblen(ptr2); if ( s2_add_blen < mlen ) break; memcpy(ptr_ret, ptr2, mlen); ptr_ret += mlen; ptr2 += mlen; /* loop counter */ s2_add_blen -= mlen; /* when get to the end of string2, reset ptr2 back to the start */ if (ptr2 == ptr2end) ptr2 = ptr2start; } init_ptr = ON; /* string1 */ while(s1_add_blen > 0) { /* reset ptr1 back to the start of string1 */ if(init_ptr) { init_ptr = OFF; ptr1 = VARDATA_ANY(string1); } mlen = pg_mblen(ptr1); if( s1_add_blen < mlen ) break; memcpy(ptr_ret, ptr1, mlen); ptr_ret += mlen; ptr1 += mlen; /* loop counter */ s1_add_blen -= mlen; } SET_VARSIZE(ret, ptr_ret - (char *) ret); PG_RETURN_TEXT_P(ret); } /* * orafce_rpad(string text, length int32 [, fill text]) * * Fill up the string to length 'length' by appending * the characters fill (a half-width space by default) */ Datum orafce_rpad(PG_FUNCTION_ARGS) { text *string1 = PG_GETARG_TEXT_PP(0); int32 output_width = PG_GETARG_INT32(1); text *string2 = PG_GETARG_TEXT_PP(2); text *ret; char *ptr1, *ptr2 = NULL, *ptr2start = NULL, *ptr2end = NULL, *ptr_ret, *spc = " "; int mlen, dsplen, s1blen, s2blen, hslen, total_blen = 0, s1_width = 0, s2_add_width = 0, s1_add_blen = 0, s2_add_blen = 0; bool s2_operate = ON, half_space = OFF, init_ptr = ON; /* validate output width (the 2nd argument) */ if (output_width < 0) output_width = 0; if (output_width > PAD_MAX) output_width = PAD_MAX; /* get byte-length of the 1st and 3rd argument strings */ s1blen = VARSIZE_ANY_EXHDR(string1); s2blen = VARSIZE_ANY_EXHDR(string2); /* validate the lengths */ if (s1blen < 0) s1blen = 0; if (s2blen < 0) s2blen = 0; /* if the filler length is zero disable filling */ if (s2blen == 0) { s2_operate = OFF; /* turn off string2 processing flag */ output_width = 0; /* same behavior as Oracle database */ } /* byte-length of half-width space */ hslen = pg_mblen(spc); /* * Calculate the length of the portion of string1 to include in * the final output */ ptr1 = VARDATA_ANY(string1); while (s1blen > 0) { /* byte-length and display length per character of string1 */ mlen = pg_mblen(ptr1); dsplen = pg_dsplen(ptr1); /* accumulate display length of string1 */ s1_width += dsplen; /* * if string1 is longer/wider than the requested output_width, * discard this character and prepend a half-width space instead */ if(s1_width >= output_width) { if(s1_width != output_width) { /* secure bytes for a half-width space in the final output */ if (output_width != 0) { s1_add_blen += hslen; half_space = ON; } } else /* exactly fits, so include this chracter */ { s1_add_blen += mlen; } /* * turn off string2 processing because string1 already * consumed output_width */ s2_operate = OFF; /* done with string1 */ break; } /* accumulate string1's portion of byte-length of the output */ s1_add_blen += mlen; /* advance one character within string1 */ ptr1 += mlen; /* loop counter */ s1blen -= mlen; } /* Calculate the length of the portion composed of string2 to use for padding */ if (s2_operate) { /* remaining part of output_width is composed of string2 */ s2_add_width = output_width - s1_width; ptr2 = ptr2start = VARDATA_ANY(string2); ptr2end = ptr2 + s2blen; while (s2_add_width > 0) { /* byte-length and display length per character of string2 */ mlen = pg_mblen(ptr2); dsplen = pg_dsplen(ptr2); /* * output_width can not fit this character of string2, so discard it and * prepend a half-width space instead */ if(dsplen > s2_add_width) { s2_add_blen += hslen; half_space = ON; /* done with string2 */ break; } /* accumulate string2's portion of byte-length of the output */ s2_add_blen += mlen; /* loop counter */ s2_add_width -= dsplen; /* advance one character within string2 */ ptr2 += mlen; /* when get to the end of string2, reset ptr2 to the start */ if (ptr2 == ptr2end) ptr2 = ptr2start; } } /* allocate enough space to contain output_width worth of characters */ total_blen = s1_add_blen + s2_add_blen; ret = (text *) palloc(VARHDRSZ + total_blen); ptr_ret = VARDATA(ret); /* string1 */ while(s1_add_blen > 0) { /* reset ptr1 back to the start of string1 */ if(init_ptr) { init_ptr = OFF; ptr1 = VARDATA_ANY(string1); } mlen = pg_mblen(ptr1); if( s1_add_blen < mlen ) break; memcpy(ptr_ret, ptr1, mlen); ptr_ret += mlen; ptr1 += mlen; /* loop counter */ s1_add_blen -= mlen; } init_ptr = ON; /* append string2 padding */ while(s2_add_blen > 0) { /* reset ptr2 to the string2 start */ if(init_ptr) { init_ptr = OFF; ptr2 = ptr2start; } mlen = pg_mblen(ptr2); if ( s2_add_blen < mlen ) break; memcpy(ptr_ret, ptr2, mlen); ptr_ret += mlen; ptr2 += mlen; /* loop counter */ s2_add_blen -= mlen; /* when get to the end of string2, reset ptr2 back to the start */ if (ptr2 == ptr2end) ptr2 = ptr2start; } /* * add a half-width space as a padding necessary to satisfy the required * output_width * * (memory already allocated as reserved by either s1_add_blen * or s2_add_blen) */ if (half_space) { memcpy(ptr_ret, spc, hslen); ptr_ret += hslen; } SET_VARSIZE(ret, ptr_ret - (char *) ret); PG_RETURN_TEXT_P(ret); }