/* * varchar_input -- common guts of varcharin and varcharrecv * * s is the input text of length len (may not be null-terminated) * atttypmod is the typmod value to apply * * Note that atttypmod is measured in characters, which * is not necessarily the same as the number of bytes. * * If the input string is too long, raise an error, unless the extra * characters are spaces, in which case they're truncated. (per SQL) */ static VarChar * varchar_input(const char *s, size_t len, int32 atttypmod) { VarChar *result; size_t maxlen; /* verify encoding */ pg_verifymbstr(s, len, false); maxlen = atttypmod - VARHDRSZ; if (atttypmod >= (int32) VARHDRSZ && len > maxlen) { /* Verify that extra characters are spaces, and clip them off */ size_t mbmaxlen = pg_mbcharcliplen(s, len, maxlen); size_t j; for (j = mbmaxlen; j < len; j++) { if (s[j] != ' ') ereport(ERROR, (errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION), errmsg("value too long for type character varying(%d)", (int) maxlen))); } len = mbmaxlen; } result = (VarChar *) palloc(len + VARHDRSZ); VARATT_SIZEP(result) = len + VARHDRSZ; memcpy(VARDATA(result), s, len); return result; }
static PyObject * PLy_cursor_query(const char *query) { PLyCursorObject *cursor; volatile MemoryContext oldcontext; volatile ResourceOwner oldowner; if ((cursor = PyObject_New(PLyCursorObject, &PLy_CursorType)) == NULL) return NULL; cursor->portalname = NULL; cursor->closed = false; cursor->mcxt = AllocSetContextCreate(TopMemoryContext, "PL/Python cursor context", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); PLy_typeinfo_init(&cursor->result, cursor->mcxt); oldcontext = CurrentMemoryContext; oldowner = CurrentResourceOwner; PLy_spi_subtransaction_begin(oldcontext, oldowner); PG_TRY(); { PLyExecutionContext *exec_ctx = PLy_current_execution_context(); SPIPlanPtr plan; Portal portal; pg_verifymbstr(query, strlen(query), false); plan = SPI_prepare(query, 0, NULL); if (plan == NULL) elog(ERROR, "SPI_prepare failed: %s", SPI_result_code_string(SPI_result)); portal = SPI_cursor_open(NULL, plan, NULL, NULL, exec_ctx->curr_proc->fn_readonly); SPI_freeplan(plan); if (portal == NULL) elog(ERROR, "SPI_cursor_open() failed: %s", SPI_result_code_string(SPI_result)); cursor->portalname = MemoryContextStrdup(cursor->mcxt, portal->name); PLy_spi_subtransaction_commit(oldcontext, oldowner); } PG_CATCH(); { PLy_spi_subtransaction_abort(oldcontext, oldowner); return NULL; } PG_END_TRY(); Assert(cursor->portalname != NULL); return (PyObject *) cursor; }
/* * Generic conversion function: Convert PyObject to cstring and * cstring into PostgreSQL type. */ static Datum PLyObject_ToDatum(PLyObToDatum *arg, int32 typmod, PyObject *plrv) { PyObject *volatile plrv_bo = NULL; Datum rv; Assert(plrv != Py_None); if (PyUnicode_Check(plrv)) plrv_bo = PLyUnicode_Bytes(plrv); else { #if PY_MAJOR_VERSION >= 3 PyObject *s = PyObject_Str(plrv); plrv_bo = PLyUnicode_Bytes(s); Py_XDECREF(s); #else plrv_bo = PyObject_Str(plrv); #endif } if (!plrv_bo) PLy_elog(ERROR, "could not create string representation of Python object"); PG_TRY(); { char *plrv_sc = PyBytes_AsString(plrv_bo); size_t plen = PyBytes_Size(plrv_bo); size_t slen = strlen(plrv_sc); if (slen < plen) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("could not convert Python object into cstring: Python string representation appears to contain null bytes"))); else if (slen > plen) elog(ERROR, "could not convert Python object into cstring: Python string longer than reported length"); pg_verifymbstr(plrv_sc, slen, false); rv = InputFunctionCall(&arg->typfunc, plrv_sc, arg->typioparam, typmod); } PG_CATCH(); { Py_XDECREF(plrv_bo); PG_RE_THROW(); } PG_END_TRY(); Py_XDECREF(plrv_bo); return rv; }
/* * Convert Python object to C string in server encoding. */ char * PLyObject_AsString(PyObject *plrv) { PyObject *plrv_bo; char *plrv_sc; size_t plen; size_t slen; if (PyUnicode_Check(plrv)) plrv_bo = PLyUnicode_Bytes(plrv); else if (PyFloat_Check(plrv)) { /* use repr() for floats, str() is lossy */ #if PY_MAJOR_VERSION >= 3 PyObject *s = PyObject_Repr(plrv); plrv_bo = PLyUnicode_Bytes(s); Py_XDECREF(s); #else plrv_bo = PyObject_Repr(plrv); #endif } else { #if PY_MAJOR_VERSION >= 3 PyObject *s = PyObject_Str(plrv); plrv_bo = PLyUnicode_Bytes(s); Py_XDECREF(s); #else plrv_bo = PyObject_Str(plrv); #endif } if (!plrv_bo) PLy_elog(ERROR, "could not create string representation of Python object"); plrv_sc = pstrdup(PyBytes_AsString(plrv_bo)); plen = PyBytes_Size(plrv_bo); slen = strlen(plrv_sc); Py_XDECREF(plrv_bo); if (slen < plen) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("could not convert Python object into cstring: Python string representation appears to contain null bytes"))); else if (slen > plen) elog(ERROR, "could not convert Python object into cstring: Python string longer than reported length"); pg_verifymbstr(plrv_sc, slen, false); return plrv_sc; }
/* * Similar to read_binary_file, but we verify that the contents are valid * in the database encoding. */ static text * read_text_file(const char *filename, int64 seek_offset, int64 bytes_to_read) { bytea *buf; buf = read_binary_file(filename, seek_offset, bytes_to_read); /* Make sure the input is valid */ pg_verifymbstr(VARDATA(buf), VARSIZE(buf) - VARHDRSZ, false); /* OK, we can cast it to text safely */ return (text *) buf; }
static PyObject * PLy_spi_execute_query(char *query, long limit) { int rv; volatile MemoryContext oldcontext; volatile ResourceOwner oldowner; PyObject *ret = NULL; oldcontext = CurrentMemoryContext; oldowner = CurrentResourceOwner; PLy_spi_subtransaction_begin(oldcontext, oldowner); PG_TRY(); { PLyExecutionContext *exec_ctx = PLy_current_execution_context(); pg_verifymbstr(query, strlen(query), false); rv = SPI_execute(query, exec_ctx->curr_proc->fn_readonly, limit); ret = PLy_spi_execute_fetch_result(SPI_tuptable, SPI_processed, rv); PLy_spi_subtransaction_commit(oldcontext, oldowner); } PG_CATCH(); { PLy_spi_subtransaction_abort(oldcontext, oldowner); return NULL; } PG_END_TRY(); if (rv < 0) { Py_XDECREF(ret); PLy_exception_set(PLy_exc_spi_error, "SPI_execute failed: %s", SPI_result_code_string(rv)); return NULL; } return ret; }
size_t char2wchar(wchar_t *to, const char *from, size_t len) { if (len == 0) return 0; #ifdef WIN32 if (GetDatabaseEncoding() == PG_UTF8) { int r; r = MultiByteToWideChar(CP_UTF8, 0, from, len, to, len); if (!r) { pg_verifymbstr(from, strlen(from), false); ereport(ERROR, (errcode(ERRCODE_CHARACTER_NOT_IN_REPERTOIRE), errmsg("invalid multibyte character for locale"), errhint("The server's LC_CTYPE locale is probably incompatible with the database encoding."))); } Assert(r <= len); return r; } else #endif /* WIN32 */ if ( lc_ctype_is_c() ) { /* * pg_mb2wchar_with_len always adds trailing '\0', so * 'to' should be allocated with sufficient space */ return pg_mb2wchar_with_len(from, (pg_wchar *)to, len); } return mbstowcs(to, from, len); }
/* * char2wchar --- convert multibyte characters to wide characters * * This has almost the API of mbstowcs_l(), except that *from need not be * null-terminated; instead, the number of input bytes is specified as * fromlen. Also, we ereport() rather than returning -1 for invalid * input encoding. tolen is the maximum number of wchar_t's to store at *to. * The output will be zero-terminated iff there is room. */ size_t char2wchar(wchar_t *to, size_t tolen, const char *from, size_t fromlen, pg_locale_t locale) { size_t result; if (tolen == 0) return 0; #ifdef WIN32 /* See WIN32 "Unicode" comment above */ if (GetDatabaseEncoding() == PG_UTF8) { /* Win32 API does not work for zero-length input */ if (fromlen == 0) result = 0; else { result = MultiByteToWideChar(CP_UTF8, 0, from, fromlen, to, tolen - 1); /* A zero return is failure */ if (result == 0) result = -1; } if (result != -1) { Assert(result < tolen); /* Append trailing null wchar (MultiByteToWideChar() does not) */ to[result] = 0; } } else #endif /* WIN32 */ { /* mbstowcs requires ending '\0' */ char *str = pnstrdup(from, fromlen); if (locale == (pg_locale_t) 0) { /* Use mbstowcs directly for the default locale */ result = mbstowcs(to, str, tolen); } else { #ifdef HAVE_LOCALE_T #ifdef HAVE_MBSTOWCS_L /* Use mbstowcs_l for nondefault locales */ result = mbstowcs_l(to, str, tolen, locale); #else /* !HAVE_MBSTOWCS_L */ /* We have to temporarily set the locale as current ... ugh */ locale_t save_locale = uselocale(locale); result = mbstowcs(to, str, tolen); uselocale(save_locale); #endif /* HAVE_MBSTOWCS_L */ #else /* !HAVE_LOCALE_T */ /* Can't have locale != 0 without HAVE_LOCALE_T */ elog(ERROR, "mbstowcs_l is not available"); result = 0; /* keep compiler quiet */ #endif /* HAVE_LOCALE_T */ } pfree(str); } if (result == -1) { /* * Invalid multibyte character encountered. We try to give a useful * error message by letting pg_verifymbstr check the string. But it's * possible that the string is OK to us, and not OK to mbstowcs --- * this suggests that the LC_CTYPE locale is different from the * database encoding. Give a generic error message if verifymbstr * can't find anything wrong. */ pg_verifymbstr(from, fromlen, false); /* might not return */ /* but if it does ... */ ereport(ERROR, (errcode(ERRCODE_CHARACTER_NOT_IN_REPERTOIRE), errmsg("invalid multibyte character for locale"), errhint("The server's LC_CTYPE locale is probably incompatible with the database encoding."))); } return result; }
/* prepare(query="select * from foo") * prepare(query="select * from foo where bar = $1", params=["text"]) * prepare(query="select * from foo where bar = $1", params=["text"], limit=5) */ PyObject * PLy_spi_prepare(PyObject *self, PyObject *args) { PLyPlanObject *plan; PyObject *list = NULL; PyObject *volatile optr = NULL; char *query; volatile MemoryContext oldcontext; volatile ResourceOwner oldowner; volatile int nargs; if (!PyArg_ParseTuple(args, "s|O", &query, &list)) return NULL; if (list && (!PySequence_Check(list))) { PLy_exception_set(PyExc_TypeError, "second argument of plpy.prepare must be a sequence"); return NULL; } if ((plan = (PLyPlanObject *) PLy_plan_new()) == NULL) return NULL; nargs = list ? PySequence_Length(list) : 0; plan->nargs = nargs; plan->types = nargs ? PLy_malloc(sizeof(Oid) * nargs) : NULL; plan->values = nargs ? PLy_malloc(sizeof(Datum) * nargs) : NULL; plan->args = nargs ? PLy_malloc(sizeof(PLyTypeInfo) * nargs) : NULL; oldcontext = CurrentMemoryContext; oldowner = CurrentResourceOwner; PLy_spi_subtransaction_begin(oldcontext, oldowner); PG_TRY(); { int i; /* * the other loop might throw an exception, if PLyTypeInfo member * isn't properly initialized the Py_DECREF(plan) will go boom */ for (i = 0; i < nargs; i++) { PLy_typeinfo_init(&plan->args[i]); plan->values[i] = PointerGetDatum(NULL); } for (i = 0; i < nargs; i++) { char *sptr; HeapTuple typeTup; Oid typeId; int32 typmod; Form_pg_type typeStruct; optr = PySequence_GetItem(list, i); if (PyString_Check(optr)) sptr = PyString_AsString(optr); else if (PyUnicode_Check(optr)) sptr = PLyUnicode_AsString(optr); else { ereport(ERROR, (errmsg("plpy.prepare: type name at ordinal position %d is not a string", i))); sptr = NULL; /* keep compiler quiet */ } /******************************************************** * Resolve argument type names and then look them up by * oid in the system cache, and remember the required *information for input conversion. ********************************************************/ parseTypeString(sptr, &typeId, &typmod); typeTup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typeId)); if (!HeapTupleIsValid(typeTup)) elog(ERROR, "cache lookup failed for type %u", typeId); Py_DECREF(optr); /* * set optr to NULL, so we won't try to unref it again in case of * an error */ optr = NULL; plan->types[i] = typeId; typeStruct = (Form_pg_type) GETSTRUCT(typeTup); if (typeStruct->typtype != TYPTYPE_COMPOSITE) PLy_output_datum_func(&plan->args[i], typeTup); else ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("plpy.prepare does not support composite types"))); ReleaseSysCache(typeTup); } pg_verifymbstr(query, strlen(query), false); plan->plan = SPI_prepare(query, plan->nargs, plan->types); if (plan->plan == NULL) elog(ERROR, "SPI_prepare failed: %s", SPI_result_code_string(SPI_result)); /* transfer plan from procCxt to topCxt */ if (SPI_keepplan(plan->plan)) elog(ERROR, "SPI_keepplan failed"); PLy_spi_subtransaction_commit(oldcontext, oldowner); } PG_CATCH(); { Py_DECREF(plan); Py_XDECREF(optr); PLy_spi_subtransaction_abort(oldcontext, oldowner); return NULL; } PG_END_TRY(); Assert(plan->plan != NULL); return (PyObject *) plan; }
Datum spg_text_leaf_consistent(PG_FUNCTION_ARGS) { spgLeafConsistentIn *in = (spgLeafConsistentIn *) PG_GETARG_POINTER(0); spgLeafConsistentOut *out = (spgLeafConsistentOut *) PG_GETARG_POINTER(1); StrategyNumber strategy = in->strategy; text *query = DatumGetTextPP(in->query); int level = in->level; text *leafValue, *reconstrValue = NULL; char *fullValue; int fullLen; int queryLen; int r; bool res; /* all tests are exact */ out->recheck = false; leafValue = DatumGetTextPP(in->leafDatum); if (DatumGetPointer(in->reconstructedValue)) reconstrValue = DatumGetTextP(in->reconstructedValue); Assert(level == 0 ? reconstrValue == NULL : VARSIZE_ANY_EXHDR(reconstrValue) == level); fullLen = level + VARSIZE_ANY_EXHDR(leafValue); queryLen = VARSIZE_ANY_EXHDR(query); /* * For an equality check, we needn't reconstruct fullValue if not same * length; it can't match */ if (strategy == BTEqualStrategyNumber && queryLen != fullLen) PG_RETURN_BOOL(false); /* Else, reconstruct the full string represented by this leaf tuple */ if (VARSIZE_ANY_EXHDR(leafValue) == 0 && level > 0) { fullValue = VARDATA(reconstrValue); out->leafValue = PointerGetDatum(reconstrValue); } else { text *fullText = palloc(VARHDRSZ + fullLen); SET_VARSIZE(fullText, VARHDRSZ + fullLen); fullValue = VARDATA(fullText); if (level) memcpy(fullValue, VARDATA(reconstrValue), level); if (VARSIZE_ANY_EXHDR(leafValue) > 0) memcpy(fullValue + level, VARDATA_ANY(leafValue), VARSIZE_ANY_EXHDR(leafValue)); out->leafValue = PointerGetDatum(fullText); } /* Run the appropriate type of comparison */ if (strategy > 10) { /* Collation-aware comparison */ strategy -= 10; /* If asserts are enabled, verify encoding of reconstructed string */ Assert(pg_verifymbstr(fullValue, fullLen, false)); r = varstr_cmp(fullValue, Min(queryLen, fullLen), VARDATA_ANY(query), Min(queryLen, fullLen), PG_GET_COLLATION()); } else { /* Non-collation-aware comparison */ r = memcmp(fullValue, VARDATA_ANY(query), Min(queryLen, fullLen)); } if (r == 0) { if (queryLen > fullLen) r = -1; else if (queryLen < fullLen) r = 1; } switch (strategy) { case BTLessStrategyNumber: res = (r < 0); break; case BTLessEqualStrategyNumber: res = (r <= 0); break; case BTEqualStrategyNumber: res = (r == 0); break; case BTGreaterEqualStrategyNumber: res = (r >= 0); break; case BTGreaterStrategyNumber: res = (r > 0); break; default: elog(ERROR, "unrecognized strategy number: %d", in->strategy); res = false; break; } PG_RETURN_BOOL(res); }
/* * char2wchar --- convert multibyte characters to wide characters * * This has almost the API of mbstowcs(), except that *from need not be * null-terminated; instead, the number of input bytes is specified as * fromlen. Also, we ereport() rather than returning -1 for invalid * input encoding. tolen is the maximum number of wchar_t's to store at *to. * The output will be zero-terminated iff there is room. */ size_t char2wchar(wchar_t *to, size_t tolen, const char *from, size_t fromlen) { size_t result; if (tolen == 0) return 0; #ifdef WIN32 /* See WIN32 "Unicode" comment above */ if (GetDatabaseEncoding() == PG_UTF8) { /* Win32 API does not work for zero-length input */ if (fromlen == 0) result = 0; else { result = MultiByteToWideChar(CP_UTF8, 0, from, fromlen, to, tolen - 1); /* A zero return is failure */ if (result == 0) result = -1; } if (result != -1) { Assert(result < tolen); /* Append trailing null wchar (MultiByteToWideChar() does not) */ to[result] = 0; } } else #endif /* WIN32 */ { /* mbstowcs requires ending '\0' */ char *str = pnstrdup(from, fromlen); Assert(!lc_ctype_is_c()); result = mbstowcs(to, str, tolen); pfree(str); } if (result == -1) { /* * Invalid multibyte character encountered. We try to give a useful * error message by letting pg_verifymbstr check the string. But it's * possible that the string is OK to us, and not OK to mbstowcs --- * this suggests that the LC_CTYPE locale is different from the * database encoding. Give a generic error message if verifymbstr * can't find anything wrong. */ pg_verifymbstr(from, fromlen, false); /* might not return */ /* but if it does ... */ ereport(ERROR, (errcode(ERRCODE_CHARACTER_NOT_IN_REPERTOIRE), errmsg("invalid multibyte character for locale"), errhint("The server's LC_CTYPE locale is probably incompatible with the database encoding."))); } return result; }
static PyObject * PLy_output(volatile int level, PyObject *self, PyObject *args, PyObject *kw) { int sqlstate = 0; char *volatile sqlstatestr = NULL; char *volatile message = NULL; char *volatile detail = NULL; char *volatile hint = NULL; char *volatile column_name = NULL; char *volatile constraint_name = NULL; char *volatile datatype_name = NULL; char *volatile table_name = NULL; char *volatile schema_name = NULL; volatile MemoryContext oldcontext; PyObject *key, *value; PyObject *volatile so; Py_ssize_t pos = 0; if (PyTuple_Size(args) == 1) { /* * Treat single argument specially to avoid undesirable ('tuple',) * decoration. */ PyObject *o; if (!PyArg_UnpackTuple(args, "plpy.elog", 1, 1, &o)) PLy_elog(ERROR, "could not unpack arguments in plpy.elog"); so = PyObject_Str(o); } else so = PyObject_Str(args); if (so == NULL || ((message = PyString_AsString(so)) == NULL)) { level = ERROR; message = dgettext(TEXTDOMAIN, "could not parse error message in plpy.elog"); } message = pstrdup(message); Py_XDECREF(so); if (kw != NULL) { while (PyDict_Next(kw, &pos, &key, &value)) { char *keyword = PyString_AsString(key); if (strcmp(keyword, "message") == 0) { /* the message should not be overwriten */ if (PyTuple_Size(args) != 0) { PLy_exception_set(PyExc_TypeError, "Argument 'message' given by name and position"); return NULL; } if (message) pfree(message); message = object_to_string(value); } else if (strcmp(keyword, "detail") == 0) detail = object_to_string(value); else if (strcmp(keyword, "hint") == 0) hint = object_to_string(value); else if (strcmp(keyword, "sqlstate") == 0) sqlstatestr = object_to_string(value); else if (strcmp(keyword, "schema_name") == 0) schema_name = object_to_string(value); else if (strcmp(keyword, "table_name") == 0) table_name = object_to_string(value); else if (strcmp(keyword, "column_name") == 0) column_name = object_to_string(value); else if (strcmp(keyword, "datatype_name") == 0) datatype_name = object_to_string(value); else if (strcmp(keyword, "constraint_name") == 0) constraint_name = object_to_string(value); else { PLy_exception_set(PyExc_TypeError, "'%s' is an invalid keyword argument for this function", keyword); return NULL; } } } if (sqlstatestr != NULL) { if (strlen(sqlstatestr) != 5) { PLy_exception_set(PyExc_ValueError, "invalid SQLSTATE code"); return NULL; } if (strspn(sqlstatestr, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") != 5) { PLy_exception_set(PyExc_ValueError, "invalid SQLSTATE code"); return NULL; } sqlstate = MAKE_SQLSTATE(sqlstatestr[0], sqlstatestr[1], sqlstatestr[2], sqlstatestr[3], sqlstatestr[4]); } oldcontext = CurrentMemoryContext; PG_TRY(); { if (message != NULL) pg_verifymbstr(message, strlen(message), false); if (detail != NULL) pg_verifymbstr(detail, strlen(detail), false); if (hint != NULL) pg_verifymbstr(hint, strlen(hint), false); if (schema_name != NULL) pg_verifymbstr(schema_name, strlen(schema_name), false); if (table_name != NULL) pg_verifymbstr(table_name, strlen(table_name), false); if (column_name != NULL) pg_verifymbstr(column_name, strlen(column_name), false); if (datatype_name != NULL) pg_verifymbstr(datatype_name, strlen(datatype_name), false); if (constraint_name != NULL) pg_verifymbstr(constraint_name, strlen(constraint_name), false); ereport(level, ((sqlstate != 0) ? errcode(sqlstate) : 0, (message != NULL) ? errmsg_internal("%s", message) : 0, (detail != NULL) ? errdetail_internal("%s", detail) : 0, (hint != NULL) ? errhint("%s", hint) : 0, (column_name != NULL) ? err_generic_string(PG_DIAG_COLUMN_NAME, column_name) : 0, (constraint_name != NULL) ? err_generic_string(PG_DIAG_CONSTRAINT_NAME, constraint_name) : 0, (datatype_name != NULL) ? err_generic_string(PG_DIAG_DATATYPE_NAME, datatype_name) : 0, (table_name != NULL) ? err_generic_string(PG_DIAG_TABLE_NAME, table_name) : 0, (schema_name != NULL) ? err_generic_string(PG_DIAG_SCHEMA_NAME, schema_name) : 0)); } PG_CATCH(); { ErrorData *edata; MemoryContextSwitchTo(oldcontext); edata = CopyErrorData(); FlushErrorState(); PLy_exception_set_with_details(PLy_exc_error, edata); FreeErrorData(edata); return NULL; } PG_END_TRY(); /* * return a legal object so the interpreter will continue on its merry way */ Py_INCREF(Py_None); return Py_None; }
/* * bpchar_input -- common guts of bpcharin and bpcharrecv * * s is the input text of length len (may not be null-terminated) * atttypmod is the typmod value to apply * * Note that atttypmod is measured in characters, which * is not necessarily the same as the number of bytes. * * If the input string is too long, raise an error, unless the extra * characters are spaces, in which case they're truncated. (per SQL) */ static BpChar * bpchar_input(const char *s, size_t len, int32 atttypmod) { BpChar *result; char *r; size_t maxlen; /* verify encoding */ pg_verifymbstr(s, len, false); /* If typmod is -1 (or invalid), use the actual string length */ if (atttypmod < (int32) VARHDRSZ) maxlen = len; else { size_t charlen; /* number of CHARACTERS in the input */ maxlen = atttypmod - VARHDRSZ; charlen = pg_mbstrlen_with_len(s, len); if (charlen > maxlen) { /* Verify that extra characters are spaces, and clip them off */ size_t mbmaxlen = pg_mbcharcliplen(s, len, maxlen); size_t j; /* * at this point, len is the actual BYTE length of the input * string, maxlen is the max number of CHARACTERS allowed for this * bpchar type, mbmaxlen is the length in BYTES of those chars. */ for (j = mbmaxlen; j < len; j++) { if (s[j] != ' ') ereport(ERROR, (errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION), errmsg("value too long for type character(%d)", (int) maxlen))); } /* * Now we set maxlen to the necessary byte length, not the number * of CHARACTERS! */ maxlen = len = mbmaxlen; } else { /* * Now we set maxlen to the necessary byte length, not the number * of CHARACTERS! */ maxlen = len + (maxlen - charlen); } } result = (BpChar *) palloc(maxlen + VARHDRSZ); VARATT_SIZEP(result) = maxlen + VARHDRSZ; r = VARDATA(result); memcpy(r, s, len); /* blank pad the string if necessary */ if (maxlen > len) memset(r + len, ' ', maxlen - len); return result; }
Datum spg_text_leaf_consistent(PG_FUNCTION_ARGS) { spgLeafConsistentIn *in = (spgLeafConsistentIn *) PG_GETARG_POINTER(0); spgLeafConsistentOut *out = (spgLeafConsistentOut *) PG_GETARG_POINTER(1); int level = in->level; text *leafValue, *reconstrValue = NULL; char *fullValue; int fullLen; bool res; int j; /* all tests are exact */ out->recheck = false; leafValue = DatumGetTextPP(in->leafDatum); if (DatumGetPointer(in->reconstructedValue)) reconstrValue = DatumGetTextP(in->reconstructedValue); Assert(level == 0 ? reconstrValue == NULL : VARSIZE_ANY_EXHDR(reconstrValue) == level); /* Reconstruct the full string represented by this leaf tuple */ fullLen = level + VARSIZE_ANY_EXHDR(leafValue); if (VARSIZE_ANY_EXHDR(leafValue) == 0 && level > 0) { fullValue = VARDATA(reconstrValue); out->leafValue = PointerGetDatum(reconstrValue); } else { text *fullText = palloc(VARHDRSZ + fullLen); SET_VARSIZE(fullText, VARHDRSZ + fullLen); fullValue = VARDATA(fullText); if (level) memcpy(fullValue, VARDATA(reconstrValue), level); if (VARSIZE_ANY_EXHDR(leafValue) > 0) memcpy(fullValue + level, VARDATA_ANY(leafValue), VARSIZE_ANY_EXHDR(leafValue)); out->leafValue = PointerGetDatum(fullText); } /* Perform the required comparison(s) */ res = true; for (j = 0; j < in->nkeys; j++) { StrategyNumber strategy = in->scankeys[j].sk_strategy; text *query = DatumGetTextPP(in->scankeys[j].sk_argument); int queryLen = VARSIZE_ANY_EXHDR(query); int r; if (strategy > 10) { /* Collation-aware comparison */ strategy -= 10; /* If asserts enabled, verify encoding of reconstructed string */ Assert(pg_verifymbstr(fullValue, fullLen, false)); r = varstr_cmp(fullValue, Min(queryLen, fullLen), VARDATA_ANY(query), Min(queryLen, fullLen), PG_GET_COLLATION()); } else { /* Non-collation-aware comparison */ r = memcmp(fullValue, VARDATA_ANY(query), Min(queryLen, fullLen)); } if (r == 0) { if (queryLen > fullLen) r = -1; else if (queryLen < fullLen) r = 1; } switch (strategy) { case BTLessStrategyNumber: res = (r < 0); break; case BTLessEqualStrategyNumber: res = (r <= 0); break; case BTEqualStrategyNumber: res = (r == 0); break; case BTGreaterEqualStrategyNumber: res = (r >= 0); break; case BTGreaterStrategyNumber: res = (r > 0); break; default: elog(ERROR, "unrecognized strategy number: %d", in->scankeys[j].sk_strategy); res = false; break; } if (!res) break; /* no need to consider remaining conditions */ } PG_RETURN_BOOL(res); }