/* * Apply encoding conversion on src and return it. The encoding * conversion function is chosen from the pg_conversion system catalog * marked as "default". If it is not found in the schema search path, * it's taken from pg_catalog schema. If it even is not in the schema, * warn and returns src. We cannot raise an error, since it will cause * an infinit loop in error message sending. * * In the case of no conversion, src is returned. * * XXX We assume that storage for converted result is 4-to-1 growth in * the worst case. The rate for currently supported encoding pares are within 3 * (SJIS JIS X0201 half width kanna -> UTF-8 is the worst case). * So "4" should be enough for the moment. */ unsigned char * pg_do_encoding_conversion(unsigned char *src, int len, int src_encoding, int dest_encoding) { unsigned char *result; Oid proc; if (!IsTransactionState()) return src; if (src_encoding == dest_encoding) return src; if (src_encoding == PG_SQL_ASCII || dest_encoding == PG_SQL_ASCII) return src; if (len <= 0) return src; proc = FindDefaultConversionProc(src_encoding, dest_encoding); if (!OidIsValid(proc)) { ereport(LOG, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("default conversion function for encoding \"%s\" to \"%s\" does not exist", pg_encoding_to_char(src_encoding), pg_encoding_to_char(dest_encoding)))); return src; } /* * XXX we should avoid throwing errors in OidFunctionCall. Otherwise * we are going into infinite loop! So we have to make sure that the * function exists before calling OidFunctionCall. */ if (!SearchSysCacheExists(PROCOID, ObjectIdGetDatum(proc), 0, 0, 0)) { elog(LOG, "cache lookup failed for function %u", proc); return src; } result = palloc(len * 4 + 1); OidFunctionCall5(proc, Int32GetDatum(src_encoding), Int32GetDatum(dest_encoding), CStringGetDatum(src), CStringGetDatum(result), Int32GetDatum(len)); return result; }
/* * CREATE CONVERSION */ ObjectAddress CreateConversionCommand(CreateConversionStmt *stmt) { Oid namespaceId; char *conversion_name; AclResult aclresult; int from_encoding; int to_encoding; Oid funcoid; const char *from_encoding_name = stmt->for_encoding_name; const char *to_encoding_name = stmt->to_encoding_name; List *func_name = stmt->func_name; static const Oid funcargs[] = {INT4OID, INT4OID, CSTRINGOID, INTERNALOID, INT4OID}; char result[1]; /* Convert list of names to a name and namespace */ namespaceId = QualifiedNameGetCreationNamespace(stmt->conversion_name, &conversion_name); /* Check we have creation rights in target namespace */ aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(), ACL_CREATE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceId)); /* Check the encoding names */ from_encoding = pg_char_to_encoding(from_encoding_name); if (from_encoding < 0) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("source encoding \"%s\" does not exist", from_encoding_name))); to_encoding = pg_char_to_encoding(to_encoding_name); if (to_encoding < 0) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("destination encoding \"%s\" does not exist", to_encoding_name))); /* * Check the existence of the conversion function. Function name could be * a qualified name. */ funcoid = LookupFuncName(func_name, sizeof(funcargs) / sizeof(Oid), funcargs, false); /* Check it returns VOID, else it's probably the wrong function */ if (get_func_rettype(funcoid) != VOIDOID) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("encoding conversion function %s must return type %s", NameListToString(func_name), "void"))); /* Check we have EXECUTE rights for the function */ aclresult = pg_proc_aclcheck(funcoid, GetUserId(), ACL_EXECUTE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_PROC, NameListToString(func_name)); /* * Check that the conversion function is suitable for the requested source * and target encodings. We do that by calling the function with an empty * string; the conversion function should throw an error if it can't * perform the requested conversion. */ OidFunctionCall5(funcoid, Int32GetDatum(from_encoding), Int32GetDatum(to_encoding), CStringGetDatum(""), CStringGetDatum(result), Int32GetDatum(0)); /* * All seem ok, go ahead (possible failure would be a duplicate conversion * name) */ return ConversionCreate(conversion_name, namespaceId, GetUserId(), from_encoding, to_encoding, funcoid, stmt->def); }
/* * Apply encoding conversion on src and return it. The encoding * conversion function is chosen from the pg_conversion system catalog * marked as "default". If it is not found in the schema search path, * it's taken from pg_catalog schema. If it even is not in the schema, * warn and return src. * * If conversion occurs, a palloc'd null-terminated string is returned. * In the case of no conversion, src is returned. * * CAUTION: although the presence of a length argument means that callers * can pass non-null-terminated strings, care is required because the same * string will be passed back if no conversion occurs. Such callers *must* * check whether result == src and handle that case differently. * * Note: we try to avoid raising error, since that could get us into * infinite recursion when this function is invoked during error message * sending. It should be OK to raise error for overlength strings though, * since the recursion will come with a shorter message. */ unsigned char * pg_do_encoding_conversion(unsigned char *src, int len, int src_encoding, int dest_encoding) { unsigned char *result; Oid proc; if (!IsTransactionState()) return src; if (src_encoding == dest_encoding) return src; if (src_encoding == PG_SQL_ASCII || dest_encoding == PG_SQL_ASCII) return src; if (len <= 0) return src; proc = FindDefaultConversionProc(src_encoding, dest_encoding); if (!OidIsValid(proc)) { ereport(LOG, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("default conversion function for encoding \"%s\" to \"%s\" does not exist", pg_encoding_to_char(src_encoding), pg_encoding_to_char(dest_encoding)))); return src; } /* * XXX we should avoid throwing errors in OidFunctionCall. Otherwise we * are going into infinite loop! So we have to make sure that the * function exists before calling OidFunctionCall. */ /* XXX: would have been function_exists() */ if (!(caql_getcount( NULL, cql("SELECT COUNT(*) FROM pg_proc " " WHERE oid = :1 ", ObjectIdGetDatum(proc))) > 0)) { elog(LOG, "cache lookup failed for function %u", proc); return src; } /* * Allocate space for conversion result, being wary of integer overflow */ if ((Size) len >= (MaxAllocSize / (Size) MAX_CONVERSION_GROWTH)) ereport(ERROR, (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), errmsg("out of memory"), errdetail("String of %d bytes is too long for encoding conversion.", len))); result = palloc(len * MAX_CONVERSION_GROWTH + 1); OidFunctionCall5(proc, Int32GetDatum(src_encoding), Int32GetDatum(dest_encoding), CStringGetDatum((char *)src), CStringGetDatum((char *)result), Int32GetDatum(len)); return result; }
/* * Execute SQL99's CONVERT function. * * CONVERT <left paren> <character value expression> * USING <form-of-use conversion name> <right paren> * * TEXT convert_using(TEXT string, TEXT conversion_name) */ Datum pg_convert_using(PG_FUNCTION_ARGS) { text *string = PG_GETARG_TEXT_P(0); text *conv_name = PG_GETARG_TEXT_P(1); text *retval; List *parsed_name; Oid convoid; HeapTuple tuple; Form_pg_conversion body; char *str; char *result; int len; /* Convert input string to null-terminated form */ len = VARSIZE(string) - VARHDRSZ; str = palloc(len + 1); memcpy(str, VARDATA(string), len); *(str + len) = '\0'; /* Look up the conversion name */ parsed_name = textToQualifiedNameList(conv_name); convoid = FindConversionByName(parsed_name); if (!OidIsValid(convoid)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("conversion \"%s\" does not exist", NameListToString(parsed_name)))); tuple = SearchSysCache(CONOID, ObjectIdGetDatum(convoid), 0, 0, 0); if (!HeapTupleIsValid(tuple)) elog(ERROR, "cache lookup failed for conversion %u", convoid); body = (Form_pg_conversion) GETSTRUCT(tuple); /* Temporary result area should be more than big enough */ result = palloc(len * 4 + 1); OidFunctionCall5(body->conproc, Int32GetDatum(body->conforencoding), Int32GetDatum(body->contoencoding), CStringGetDatum(str), CStringGetDatum(result), Int32GetDatum(len)); ReleaseSysCache(tuple); /* * build text result structure. we cannot use textin() here, since textin * assumes that input string encoding is same as database encoding. */ len = strlen(result) + VARHDRSZ; retval = palloc(len); VARATT_SIZEP(retval) = len; memcpy(VARDATA(retval), result, len - VARHDRSZ); pfree(result); pfree(str); PG_RETURN_TEXT_P(retval); }