diff options
Diffstat (limited to 'src/backend/commands/conversioncmds.c')
-rw-r--r-- | src/backend/commands/conversioncmds.c | 139 |
1 files changed, 139 insertions, 0 deletions
diff --git a/src/backend/commands/conversioncmds.c b/src/backend/commands/conversioncmds.c new file mode 100644 index 0000000..5fed97a --- /dev/null +++ b/src/backend/commands/conversioncmds.c @@ -0,0 +1,139 @@ +/*------------------------------------------------------------------------- + * + * conversioncmds.c + * conversion creation command support code + * + * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/commands/conversioncmds.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/htup_details.h" +#include "catalog/dependency.h" +#include "catalog/indexing.h" +#include "catalog/pg_conversion.h" +#include "catalog/pg_type.h" +#include "commands/alter.h" +#include "commands/conversioncmds.h" +#include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "parser/parse_func.h" +#include "utils/acl.h" +#include "utils/builtins.h" +#include "utils/lsyscache.h" +#include "utils/rel.h" +#include "utils/syscache.h" + +/* + * 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, BOOLOID}; + char result[1]; + Datum funcresult; + + /* 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, OBJECT_SCHEMA, + 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))); + + /* + * We consider conversions to or from SQL_ASCII to be meaningless. (If + * you wish to change this, note that pg_do_encoding_conversion() and its + * sister functions have hard-wired fast paths for any conversion in which + * the source or target encoding is SQL_ASCII, so that an encoding + * conversion function declared for such a case will never be used.) + */ + if (from_encoding == PG_SQL_ASCII || to_encoding == PG_SQL_ASCII) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("encoding conversion to or from \"SQL_ASCII\" is not supported"))); + + /* + * 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 int4, else it's probably the wrong function */ + if (get_func_rettype(funcoid) != INT4OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("encoding conversion function %s must return type %s", + NameListToString(func_name), "integer"))); + + /* Check we have EXECUTE rights for the function */ + aclresult = pg_proc_aclcheck(funcoid, GetUserId(), ACL_EXECUTE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, OBJECT_FUNCTION, + 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. + */ + funcresult = OidFunctionCall6(funcoid, + Int32GetDatum(from_encoding), + Int32GetDatum(to_encoding), + CStringGetDatum(""), + CStringGetDatum(result), + Int32GetDatum(0), + BoolGetDatum(false)); + + /* + * The function should return 0 for empty input. Might as well check that, + * too. + */ + if (DatumGetInt32(funcresult) != 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("encoding conversion function %s returned incorrect result for empty input", + NameListToString(func_name)))); + + /* + * 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); +} |