1 /*-------------------------------------------------------------------------
2 *
3 * conversioncmds.c
4 * conversion creation command support code
5 *
6 * Portions Copyright (c) 1996-2020, 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/acl.h"
28 #include "utils/builtins.h"
29 #include "utils/lsyscache.h"
30 #include "utils/rel.h"
31 #include "utils/syscache.h"
32
33 /*
34 * CREATE CONVERSION
35 */
36 ObjectAddress
CreateConversionCommand(CreateConversionStmt * stmt)37 CreateConversionCommand(CreateConversionStmt *stmt)
38 {
39 Oid namespaceId;
40 char *conversion_name;
41 AclResult aclresult;
42 int from_encoding;
43 int to_encoding;
44 Oid funcoid;
45 const char *from_encoding_name = stmt->for_encoding_name;
46 const char *to_encoding_name = stmt->to_encoding_name;
47 List *func_name = stmt->func_name;
48 static const Oid funcargs[] = {INT4OID, INT4OID, CSTRINGOID, INTERNALOID, INT4OID};
49 char result[1];
50
51 /* Convert list of names to a name and namespace */
52 namespaceId = QualifiedNameGetCreationNamespace(stmt->conversion_name,
53 &conversion_name);
54
55 /* Check we have creation rights in target namespace */
56 aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(), ACL_CREATE);
57 if (aclresult != ACLCHECK_OK)
58 aclcheck_error(aclresult, OBJECT_SCHEMA,
59 get_namespace_name(namespaceId));
60
61 /* Check the encoding names */
62 from_encoding = pg_char_to_encoding(from_encoding_name);
63 if (from_encoding < 0)
64 ereport(ERROR,
65 (errcode(ERRCODE_UNDEFINED_OBJECT),
66 errmsg("source encoding \"%s\" does not exist",
67 from_encoding_name)));
68
69 to_encoding = pg_char_to_encoding(to_encoding_name);
70 if (to_encoding < 0)
71 ereport(ERROR,
72 (errcode(ERRCODE_UNDEFINED_OBJECT),
73 errmsg("destination encoding \"%s\" does not exist",
74 to_encoding_name)));
75
76 /*
77 * We consider conversions to or from SQL_ASCII to be meaningless. (If
78 * you wish to change this, note that pg_do_encoding_conversion() and its
79 * sister functions have hard-wired fast paths for any conversion in which
80 * the source or target encoding is SQL_ASCII, so that an encoding
81 * conversion function declared for such a case will never be used.)
82 */
83 if (from_encoding == PG_SQL_ASCII || to_encoding == PG_SQL_ASCII)
84 ereport(ERROR,
85 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
86 errmsg("encoding conversion to or from \"SQL_ASCII\" is not supported")));
87
88 /*
89 * Check the existence of the conversion function. Function name could be
90 * a qualified name.
91 */
92 funcoid = LookupFuncName(func_name, sizeof(funcargs) / sizeof(Oid),
93 funcargs, false);
94
95 /* Check it returns VOID, else it's probably the wrong function */
96 if (get_func_rettype(funcoid) != VOIDOID)
97 ereport(ERROR,
98 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
99 errmsg("encoding conversion function %s must return type %s",
100 NameListToString(func_name), "void")));
101
102 /* Check we have EXECUTE rights for the function */
103 aclresult = pg_proc_aclcheck(funcoid, GetUserId(), ACL_EXECUTE);
104 if (aclresult != ACLCHECK_OK)
105 aclcheck_error(aclresult, OBJECT_FUNCTION,
106 NameListToString(func_name));
107
108 /*
109 * Check that the conversion function is suitable for the requested source
110 * and target encodings. We do that by calling the function with an empty
111 * string; the conversion function should throw an error if it can't
112 * perform the requested conversion.
113 */
114 OidFunctionCall5(funcoid,
115 Int32GetDatum(from_encoding),
116 Int32GetDatum(to_encoding),
117 CStringGetDatum(""),
118 CStringGetDatum(result),
119 Int32GetDatum(0));
120
121 /*
122 * All seem ok, go ahead (possible failure would be a duplicate conversion
123 * name)
124 */
125 return ConversionCreate(conversion_name, namespaceId, GetUserId(),
126 from_encoding, to_encoding, funcoid, stmt->def);
127 }
128