1 /*-------------------------------------------------------------------------
2  *
3  * conversioncmds.c
4  *	  conversion creation command support code
5  *
6  * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *	  src/backend/commands/conversioncmds.c
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include "postgres.h"
16 
17 #include "access/htup_details.h"
18 #include "catalog/dependency.h"
19 #include "catalog/indexing.h"
20 #include "catalog/pg_conversion.h"
21 #include "catalog/pg_type.h"
22 #include "commands/alter.h"
23 #include "commands/conversioncmds.h"
24 #include "mb/pg_wchar.h"
25 #include "miscadmin.h"
26 #include "parser/parse_func.h"
27 #include "utils/builtins.h"
28 #include "utils/lsyscache.h"
29 #include "utils/rel.h"
30 #include "utils/syscache.h"
31 
32 /*
33  * CREATE CONVERSION
34  */
35 ObjectAddress
CreateConversionCommand(CreateConversionStmt * stmt)36 CreateConversionCommand(CreateConversionStmt *stmt)
37 {
38 	Oid			namespaceId;
39 	char	   *conversion_name;
40 	AclResult	aclresult;
41 	int			from_encoding;
42 	int			to_encoding;
43 	Oid			funcoid;
44 	const char *from_encoding_name = stmt->for_encoding_name;
45 	const char *to_encoding_name = stmt->to_encoding_name;
46 	List	   *func_name = stmt->func_name;
47 	static const Oid funcargs[] = {INT4OID, INT4OID, CSTRINGOID, INTERNALOID, INT4OID};
48 	char		result[1];
49 
50 	/* Convert list of names to a name and namespace */
51 	namespaceId = QualifiedNameGetCreationNamespace(stmt->conversion_name,
52 													&conversion_name);
53 
54 	/* Check we have creation rights in target namespace */
55 	aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(), ACL_CREATE);
56 	if (aclresult != ACLCHECK_OK)
57 		aclcheck_error(aclresult, OBJECT_SCHEMA,
58 					   get_namespace_name(namespaceId));
59 
60 	/* Check the encoding names */
61 	from_encoding = pg_char_to_encoding(from_encoding_name);
62 	if (from_encoding < 0)
63 		ereport(ERROR,
64 				(errcode(ERRCODE_UNDEFINED_OBJECT),
65 				 errmsg("source encoding \"%s\" does not exist",
66 						from_encoding_name)));
67 
68 	to_encoding = pg_char_to_encoding(to_encoding_name);
69 	if (to_encoding < 0)
70 		ereport(ERROR,
71 				(errcode(ERRCODE_UNDEFINED_OBJECT),
72 				 errmsg("destination encoding \"%s\" does not exist",
73 						to_encoding_name)));
74 
75 	/*
76 	 * Check the existence of the conversion function. Function name could be
77 	 * a qualified name.
78 	 */
79 	funcoid = LookupFuncName(func_name, sizeof(funcargs) / sizeof(Oid),
80 							 funcargs, false);
81 
82 	/* Check it returns VOID, else it's probably the wrong function */
83 	if (get_func_rettype(funcoid) != VOIDOID)
84 		ereport(ERROR,
85 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
86 				 errmsg("encoding conversion function %s must return type %s",
87 						NameListToString(func_name), "void")));
88 
89 	/* Check we have EXECUTE rights for the function */
90 	aclresult = pg_proc_aclcheck(funcoid, GetUserId(), ACL_EXECUTE);
91 	if (aclresult != ACLCHECK_OK)
92 		aclcheck_error(aclresult, OBJECT_FUNCTION,
93 					   NameListToString(func_name));
94 
95 	/*
96 	 * Check that the conversion function is suitable for the requested source
97 	 * and target encodings. We do that by calling the function with an empty
98 	 * string; the conversion function should throw an error if it can't
99 	 * perform the requested conversion.
100 	 */
101 	OidFunctionCall5(funcoid,
102 					 Int32GetDatum(from_encoding),
103 					 Int32GetDatum(to_encoding),
104 					 CStringGetDatum(""),
105 					 CStringGetDatum(result),
106 					 Int32GetDatum(0));
107 
108 	/*
109 	 * All seem ok, go ahead (possible failure would be a duplicate conversion
110 	 * name)
111 	 */
112 	return ConversionCreate(conversion_name, namespaceId, GetUserId(),
113 							from_encoding, to_encoding, funcoid, stmt->def);
114 }
115