Coverage Report

Created: 2025-08-12 06:43

/src/postgres/src/backend/commands/conversioncmds.c
Line
Count
Source (jump to first uncovered line)
1
/*-------------------------------------------------------------------------
2
 *
3
 * conversioncmds.c
4
 *    conversion creation command support code
5
 *
6
 * Portions Copyright (c) 1996-2025, 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
CreateConversionCommand(CreateConversionStmt *stmt)
33
0
{
34
0
  Oid     namespaceId;
35
0
  char     *conversion_name;
36
0
  AclResult aclresult;
37
0
  int     from_encoding;
38
0
  int     to_encoding;
39
0
  Oid     funcoid;
40
0
  const char *from_encoding_name = stmt->for_encoding_name;
41
0
  const char *to_encoding_name = stmt->to_encoding_name;
42
0
  List     *func_name = stmt->func_name;
43
0
  static const Oid funcargs[] = {INT4OID, INT4OID, CSTRINGOID, INTERNALOID, INT4OID, BOOLOID};
44
0
  char    result[1];
45
0
  Datum   funcresult;
46
47
  /* Convert list of names to a name and namespace */
48
0
  namespaceId = QualifiedNameGetCreationNamespace(stmt->conversion_name,
49
0
                          &conversion_name);
50
51
  /* Check we have creation rights in target namespace */
52
0
  aclresult = object_aclcheck(NamespaceRelationId, namespaceId, GetUserId(), ACL_CREATE);
53
0
  if (aclresult != ACLCHECK_OK)
54
0
    aclcheck_error(aclresult, OBJECT_SCHEMA,
55
0
             get_namespace_name(namespaceId));
56
57
  /* Check the encoding names */
58
0
  from_encoding = pg_char_to_encoding(from_encoding_name);
59
0
  if (from_encoding < 0)
60
0
    ereport(ERROR,
61
0
        (errcode(ERRCODE_UNDEFINED_OBJECT),
62
0
         errmsg("source encoding \"%s\" does not exist",
63
0
            from_encoding_name)));
64
65
0
  to_encoding = pg_char_to_encoding(to_encoding_name);
66
0
  if (to_encoding < 0)
67
0
    ereport(ERROR,
68
0
        (errcode(ERRCODE_UNDEFINED_OBJECT),
69
0
         errmsg("destination encoding \"%s\" does not exist",
70
0
            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
0
  if (from_encoding == PG_SQL_ASCII || to_encoding == PG_SQL_ASCII)
80
0
    ereport(ERROR,
81
0
        (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
82
0
         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
0
  funcoid = LookupFuncName(func_name, sizeof(funcargs) / sizeof(Oid),
89
0
               funcargs, false);
90
91
  /* Check it returns int4, else it's probably the wrong function */
92
0
  if (get_func_rettype(funcoid) != INT4OID)
93
0
    ereport(ERROR,
94
0
        (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
95
0
         errmsg("encoding conversion function %s must return type %s",
96
0
            NameListToString(func_name), "integer")));
97
98
  /* Check we have EXECUTE rights for the function */
99
0
  aclresult = object_aclcheck(ProcedureRelationId, funcoid, GetUserId(), ACL_EXECUTE);
100
0
  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
0
  funcresult = OidFunctionCall6(funcoid,
111
0
                  Int32GetDatum(from_encoding),
112
0
                  Int32GetDatum(to_encoding),
113
0
                  CStringGetDatum(""),
114
0
                  CStringGetDatum(result),
115
0
                  Int32GetDatum(0),
116
0
                  BoolGetDatum(false));
117
118
  /*
119
   * The function should return 0 for empty input. Might as well check that,
120
   * too.
121
   */
122
0
  if (DatumGetInt32(funcresult) != 0)
123
0
    ereport(ERROR,
124
0
        (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
125
0
         errmsg("encoding conversion function %s returned incorrect result for empty input",
126
0
            NameListToString(func_name))));
127
128
  /*
129
   * All seem ok, go ahead (possible failure would be a duplicate conversion
130
   * name)
131
   */
132
0
  return ConversionCreate(conversion_name, namespaceId, GetUserId(),
133
0
              from_encoding, to_encoding, funcoid, stmt->def);
134
0
}