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