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