Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * mbutils.c
4 : : * This file contains functions for encoding conversion.
5 : : *
6 : : * The string-conversion functions in this file share some API quirks.
7 : : * Note the following:
8 : : *
9 : : * The functions return a palloc'd, null-terminated string if conversion
10 : : * is required. However, if no conversion is performed, the given source
11 : : * string pointer is returned as-is.
12 : : *
13 : : * Although the presence of a length argument means that callers can pass
14 : : * non-null-terminated strings, care is required because the same string
15 : : * will be passed back if no conversion occurs. Such callers *must* check
16 : : * whether result == src and handle that case differently.
17 : : *
18 : : * If the source and destination encodings are the same, the source string
19 : : * is returned without any verification; it's assumed to be valid data.
20 : : * If that might not be the case, the caller is responsible for validating
21 : : * the string using a separate call to pg_verify_mbstr(). Whenever the
22 : : * source and destination encodings are different, the functions ensure that
23 : : * the result is validly encoded according to the destination encoding.
24 : : *
25 : : *
26 : : * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
27 : : * Portions Copyright (c) 1994, Regents of the University of California
28 : : *
29 : : *
30 : : * IDENTIFICATION
31 : : * src/backend/utils/mb/mbutils.c
32 : : *
33 : : *-------------------------------------------------------------------------
34 : : */
35 : : #include "postgres.h"
36 : :
37 : : #include "access/xact.h"
38 : : #include "catalog/namespace.h"
39 : : #include "mb/pg_wchar.h"
40 : : #include "utils/fmgrprotos.h"
41 : : #include "utils/memutils.h"
42 : : #include "utils/relcache.h"
43 : : #include "varatt.h"
44 : :
45 : : /*
46 : : * We maintain a simple linked list caching the fmgr lookup info for the
47 : : * currently selected conversion functions, as well as any that have been
48 : : * selected previously in the current session. (We remember previous
49 : : * settings because we must be able to restore a previous setting during
50 : : * transaction rollback, without doing any fresh catalog accesses.)
51 : : *
52 : : * Since we'll never release this data, we just keep it in TopMemoryContext.
53 : : */
54 : : typedef struct ConvProcInfo
55 : : {
56 : : int s_encoding; /* server and client encoding IDs */
57 : : int c_encoding;
58 : : FmgrInfo to_server_info; /* lookup info for conversion procs */
59 : : FmgrInfo to_client_info;
60 : : } ConvProcInfo;
61 : :
62 : : static List *ConvProcList = NIL; /* List of ConvProcInfo */
63 : :
64 : : /*
65 : : * These variables point to the currently active conversion functions,
66 : : * or are NULL when no conversion is needed.
67 : : */
68 : : static FmgrInfo *ToServerConvProc = NULL;
69 : : static FmgrInfo *ToClientConvProc = NULL;
70 : :
71 : : /*
72 : : * This variable stores the conversion function to convert from UTF-8
73 : : * to the server encoding. It's NULL if the server encoding *is* UTF-8,
74 : : * or if we lack a conversion function for this.
75 : : */
76 : : static FmgrInfo *Utf8ToServerConvProc = NULL;
77 : :
78 : : /*
79 : : * These variables track the currently-selected encodings.
80 : : */
81 : : static const pg_enc2name *ClientEncoding = &pg_enc2name_tbl[PG_SQL_ASCII];
82 : : static const pg_enc2name *DatabaseEncoding = &pg_enc2name_tbl[PG_SQL_ASCII];
83 : : static const pg_enc2name *MessageEncoding = &pg_enc2name_tbl[PG_SQL_ASCII];
84 : :
85 : : /*
86 : : * During backend startup we can't set client encoding because we (a)
87 : : * can't look up the conversion functions, and (b) may not know the database
88 : : * encoding yet either. So SetClientEncoding() just accepts anything and
89 : : * remembers it for InitializeClientEncoding() to apply later.
90 : : */
91 : : static bool backend_startup_complete = false;
92 : : static int pending_client_encoding = PG_SQL_ASCII;
93 : :
94 : :
95 : : /* Internal functions */
96 : : static char *perform_default_encoding_conversion(const char *src,
97 : : int len, bool is_client_to_server);
98 : : static int cliplen(const char *str, int len, int limit);
99 : :
100 : :
101 : : /*
102 : : * Prepare for a future call to SetClientEncoding. Success should mean
103 : : * that SetClientEncoding is guaranteed to succeed for this encoding request.
104 : : *
105 : : * (But note that success before backend_startup_complete does not guarantee
106 : : * success after ...)
107 : : *
108 : : * Returns 0 if okay, -1 if not (bad encoding or can't support conversion)
109 : : */
110 : : int
5368 tgl@sss.pgh.pa.us 111 :CBC 30052 : PrepareClientEncoding(int encoding)
112 : : {
113 : : int current_server_encoding;
114 : : ListCell *lc;
115 : :
8868 ishii@postgresql.org 116 [ + - - + ]: 30052 : if (!PG_VALID_FE_ENCODING(encoding))
7280 neilc@samurai.com 117 :UBC 0 : return -1;
118 : :
119 : : /* Can't do anything during startup, per notes above */
8270 tgl@sss.pgh.pa.us 120 [ + + ]:CBC 30052 : if (!backend_startup_complete)
8553 ishii@postgresql.org 121 : 15184 : return 0;
122 : :
8270 tgl@sss.pgh.pa.us 123 : 14868 : current_server_encoding = GetDatabaseEncoding();
124 : :
125 : : /*
126 : : * Check for cases that require no conversion function.
127 : : */
128 [ + + + + ]: 14868 : if (current_server_encoding == encoding ||
7280 neilc@samurai.com 129 [ + + ]: 1458 : current_server_encoding == PG_SQL_ASCII ||
130 : : encoding == PG_SQL_ASCII)
8270 tgl@sss.pgh.pa.us 131 : 14858 : return 0;
132 : :
6103 133 [ + - ]: 10 : if (IsTransactionState())
134 : : {
135 : : /*
136 : : * If we're in a live transaction, it's safe to access the catalogs,
137 : : * so look up the functions. We repeat the lookup even if the info is
138 : : * already cached, so that we can react to changes in the contents of
139 : : * pg_conversion.
140 : : */
141 : : Oid to_server_proc,
142 : : to_client_proc;
143 : : ConvProcInfo *convinfo;
144 : : MemoryContext oldcontext;
145 : :
146 : 10 : to_server_proc = FindDefaultConversionProc(encoding,
147 : : current_server_encoding);
148 [ - + ]: 10 : if (!OidIsValid(to_server_proc))
6103 tgl@sss.pgh.pa.us 149 :UBC 0 : return -1;
6103 tgl@sss.pgh.pa.us 150 :CBC 10 : to_client_proc = FindDefaultConversionProc(current_server_encoding,
151 : : encoding);
152 [ - + ]: 10 : if (!OidIsValid(to_client_proc))
6103 tgl@sss.pgh.pa.us 153 :UBC 0 : return -1;
154 : :
155 : : /*
156 : : * Load the fmgr info into TopMemoryContext (could still fail here)
157 : : */
6103 tgl@sss.pgh.pa.us 158 :CBC 10 : convinfo = (ConvProcInfo *) MemoryContextAlloc(TopMemoryContext,
159 : : sizeof(ConvProcInfo));
160 : 10 : convinfo->s_encoding = current_server_encoding;
161 : 10 : convinfo->c_encoding = encoding;
162 : 10 : fmgr_info_cxt(to_server_proc, &convinfo->to_server_info,
163 : : TopMemoryContext);
164 : 10 : fmgr_info_cxt(to_client_proc, &convinfo->to_client_info,
165 : : TopMemoryContext);
166 : :
167 : : /* Attach new info to head of list */
168 : 10 : oldcontext = MemoryContextSwitchTo(TopMemoryContext);
169 : 10 : ConvProcList = lcons(convinfo, ConvProcList);
170 : 10 : MemoryContextSwitchTo(oldcontext);
171 : :
172 : : /*
173 : : * We cannot yet remove any older entry for the same encoding pair,
174 : : * since it could still be in use. SetClientEncoding will clean up.
175 : : */
176 : :
177 : 10 : return 0; /* success */
178 : : }
179 : : else
180 : : {
181 : : /*
182 : : * If we're not in a live transaction, the only thing we can do is
183 : : * restore a previous setting using the cache. This covers all
184 : : * transaction-rollback cases. The only case it might not work for is
185 : : * trying to change client_encoding on the fly by editing
186 : : * postgresql.conf and SIGHUP'ing. Which would probably be a stupid
187 : : * thing to do anyway.
188 : : */
6103 tgl@sss.pgh.pa.us 189 [ # # # # :UBC 0 : foreach(lc, ConvProcList)
# # ]
190 : : {
191 : 0 : ConvProcInfo *oldinfo = (ConvProcInfo *) lfirst(lc);
192 : :
193 [ # # ]: 0 : if (oldinfo->s_encoding == current_server_encoding &&
194 [ # # ]: 0 : oldinfo->c_encoding == encoding)
195 : 0 : return 0;
196 : : }
197 : :
198 : 0 : return -1; /* it's not cached, so fail */
199 : : }
200 : : }
201 : :
202 : : /*
203 : : * Set the active client encoding and set up the conversion-function pointers.
204 : : * PrepareClientEncoding should have been called previously for this encoding.
205 : : *
206 : : * Returns 0 if okay, -1 if not (bad encoding or can't support conversion)
207 : : */
208 : : int
5368 tgl@sss.pgh.pa.us 209 :CBC 31398 : SetClientEncoding(int encoding)
210 : : {
211 : : int current_server_encoding;
212 : : bool found;
213 : : ListCell *lc;
214 : :
215 [ + - - + ]: 31398 : if (!PG_VALID_FE_ENCODING(encoding))
5368 tgl@sss.pgh.pa.us 216 :UBC 0 : return -1;
217 : :
218 : : /* Can't do anything during startup, per notes above */
5368 tgl@sss.pgh.pa.us 219 [ + + ]:CBC 31398 : if (!backend_startup_complete)
220 : : {
221 : 15096 : pending_client_encoding = encoding;
222 : 15096 : return 0;
223 : : }
224 : :
225 : 16302 : current_server_encoding = GetDatabaseEncoding();
226 : :
227 : : /*
228 : : * Check for cases that require no conversion function.
229 : : */
230 [ + + + + ]: 16302 : if (current_server_encoding == encoding ||
231 [ + + ]: 1458 : current_server_encoding == PG_SQL_ASCII ||
232 : : encoding == PG_SQL_ASCII)
233 : : {
234 : 16292 : ClientEncoding = &pg_enc2name_tbl[encoding];
235 : 16292 : ToServerConvProc = NULL;
236 : 16292 : ToClientConvProc = NULL;
237 : 16292 : return 0;
238 : : }
239 : :
240 : : /*
241 : : * Search the cache for the entry previously prepared by
242 : : * PrepareClientEncoding; if there isn't one, we lose. While at it,
243 : : * release any duplicate entries so that repeated Prepare/Set cycles don't
244 : : * leak memory.
245 : : */
246 : 10 : found = false;
2347 247 [ + - + + : 23 : foreach(lc, ConvProcList)
+ + ]
248 : : {
5368 249 : 13 : ConvProcInfo *convinfo = (ConvProcInfo *) lfirst(lc);
250 : :
251 [ + - ]: 13 : if (convinfo->s_encoding == current_server_encoding &&
252 [ + + ]: 13 : convinfo->c_encoding == encoding)
253 : : {
254 [ + - ]: 10 : if (!found)
255 : : {
256 : : /* Found newest entry, so set up */
257 : 10 : ClientEncoding = &pg_enc2name_tbl[encoding];
258 : 10 : ToServerConvProc = &convinfo->to_server_info;
259 : 10 : ToClientConvProc = &convinfo->to_client_info;
260 : 10 : found = true;
261 : : }
262 : : else
263 : : {
264 : : /* Duplicate entry, release it */
2347 tgl@sss.pgh.pa.us 265 :UBC 0 : ConvProcList = foreach_delete_current(ConvProcList, lc);
5368 266 : 0 : pfree(convinfo);
267 : : }
268 : : }
269 : : }
270 : :
5368 tgl@sss.pgh.pa.us 271 [ + - ]:CBC 10 : if (found)
272 : 10 : return 0; /* success */
273 : : else
5368 tgl@sss.pgh.pa.us 274 :UBC 0 : return -1; /* it's not cached, so fail */
275 : : }
276 : :
277 : : /*
278 : : * Initialize client encoding conversions.
279 : : * Called from InitPostgres() once during backend startup.
280 : : */
281 : : void
8270 tgl@sss.pgh.pa.us 282 :CBC 14534 : InitializeClientEncoding(void)
283 : : {
284 : : int current_server_encoding;
285 : :
286 [ - + ]: 14534 : Assert(!backend_startup_complete);
287 : 14534 : backend_startup_complete = true;
288 : :
5368 289 [ + - - + ]: 29068 : if (PrepareClientEncoding(pending_client_encoding) < 0 ||
290 : 14534 : SetClientEncoding(pending_client_encoding) < 0)
291 : : {
292 : : /*
293 : : * Oops, the requested conversion is not available. We couldn't fail
294 : : * before, but we can now.
295 : : */
8181 tgl@sss.pgh.pa.us 296 [ # # ]:UBC 0 : ereport(FATAL,
297 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
298 : : errmsg("conversion between %s and %s is not supported",
299 : : pg_enc2name_tbl[pending_client_encoding].name,
300 : : GetDatabaseEncodingName())));
301 : : }
302 : :
303 : : /*
304 : : * Also look up the UTF8-to-server conversion function if needed. Since
305 : : * the server encoding is fixed within any one backend process, we don't
306 : : * have to do this more than once.
307 : : */
2112 tgl@sss.pgh.pa.us 308 :CBC 14534 : current_server_encoding = GetDatabaseEncoding();
309 [ + + + + ]: 14534 : if (current_server_encoding != PG_UTF8 &&
310 : : current_server_encoding != PG_SQL_ASCII)
311 : : {
312 : : Oid utf8_to_server_proc;
313 : :
244 noah@leadboat.com 314 : 100 : AssertCouldGetRelation();
315 : : utf8_to_server_proc =
2112 tgl@sss.pgh.pa.us 316 : 100 : FindDefaultConversionProc(PG_UTF8,
317 : : current_server_encoding);
318 : : /* If there's no such conversion, just leave the pointer as NULL */
319 [ + - ]: 100 : if (OidIsValid(utf8_to_server_proc))
320 : : {
321 : : FmgrInfo *finfo;
322 : :
323 : 100 : finfo = (FmgrInfo *) MemoryContextAlloc(TopMemoryContext,
324 : : sizeof(FmgrInfo));
325 : 100 : fmgr_info_cxt(utf8_to_server_proc, finfo,
326 : : TopMemoryContext);
327 : : /* Set Utf8ToServerConvProc only after data is fully valid */
328 : 100 : Utf8ToServerConvProc = finfo;
329 : : }
330 : : }
8337 ishii@postgresql.org 331 : 14534 : }
332 : :
333 : : /*
334 : : * returns the current client encoding
335 : : */
336 : : int
8853 tgl@sss.pgh.pa.us 337 : 5568 : pg_get_client_encoding(void)
338 : : {
7280 neilc@samurai.com 339 : 5568 : return ClientEncoding->encoding;
340 : : }
341 : :
342 : : /*
343 : : * returns the current client encoding name
344 : : */
345 : : const char *
8853 tgl@sss.pgh.pa.us 346 :UBC 0 : pg_get_client_encoding_name(void)
347 : : {
7280 neilc@samurai.com 348 : 0 : return ClientEncoding->name;
349 : : }
350 : :
351 : : /*
352 : : * Convert src string to another encoding (general case).
353 : : *
354 : : * See the notes about string conversion functions at the top of this file.
355 : : */
356 : : unsigned char *
8853 tgl@sss.pgh.pa.us 357 :CBC 1509 : pg_do_encoding_conversion(unsigned char *src, int len,
358 : : int src_encoding, int dest_encoding)
359 : : {
360 : : unsigned char *result;
361 : : Oid proc;
362 : :
4315 363 [ + + ]: 1509 : if (len <= 0)
364 : 15 : return src; /* empty string is always valid */
365 : :
8553 ishii@postgresql.org 366 [ + + ]: 1494 : if (src_encoding == dest_encoding)
4315 tgl@sss.pgh.pa.us 367 : 1102 : return src; /* no conversion required, assume valid */
368 : :
369 [ - + ]: 392 : if (dest_encoding == PG_SQL_ASCII)
4315 tgl@sss.pgh.pa.us 370 :UBC 0 : return src; /* any string is valid in SQL_ASCII */
371 : :
4315 tgl@sss.pgh.pa.us 372 [ + + ]:CBC 392 : if (src_encoding == PG_SQL_ASCII)
373 : : {
374 : : /* No conversion is possible, but we must validate the result */
375 : 8 : (void) pg_verify_mbstr(dest_encoding, (const char *) src, len, false);
8422 ishii@postgresql.org 376 : 8 : return src;
377 : : }
378 : :
4315 tgl@sss.pgh.pa.us 379 [ - + ]: 384 : if (!IsTransactionState()) /* shouldn't happen */
4315 tgl@sss.pgh.pa.us 380 [ # # ]:UBC 0 : elog(ERROR, "cannot perform encoding conversion outside a transaction");
381 : :
8553 ishii@postgresql.org 382 :CBC 384 : proc = FindDefaultConversionProc(src_encoding, dest_encoding);
383 [ - + ]: 384 : if (!OidIsValid(proc))
4315 tgl@sss.pgh.pa.us 384 [ # # ]:UBC 0 : ereport(ERROR,
385 : : (errcode(ERRCODE_UNDEFINED_FUNCTION),
386 : : errmsg("default conversion function for encoding \"%s\" to \"%s\" does not exist",
387 : : pg_encoding_to_char(src_encoding),
388 : : pg_encoding_to_char(dest_encoding))));
389 : :
390 : : /*
391 : : * Allocate space for conversion result, being wary of integer overflow.
392 : : *
393 : : * len * MAX_CONVERSION_GROWTH is typically a vast overestimate of the
394 : : * required space, so it might exceed MaxAllocSize even though the result
395 : : * would actually fit. We do not want to hand back a result string that
396 : : * exceeds MaxAllocSize, because callers might not cope gracefully --- but
397 : : * if we just allocate more than that, and don't use it, that's fine.
398 : : */
2267 tgl@sss.pgh.pa.us 399 [ - + ]:CBC 384 : if ((Size) len >= (MaxAllocHugeSize / (Size) MAX_CONVERSION_GROWTH))
6778 tgl@sss.pgh.pa.us 400 [ # # ]:UBC 0 : ereport(ERROR,
401 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
402 : : errmsg("out of memory"),
403 : : errdetail("String of %d bytes is too long for encoding conversion.",
404 : : len)));
405 : :
406 : : result = (unsigned char *)
2267 tgl@sss.pgh.pa.us 407 :CBC 384 : MemoryContextAllocHuge(CurrentMemoryContext,
408 : 384 : (Size) len * MAX_CONVERSION_GROWTH + 1);
409 : :
1721 heikki.linnakangas@i 410 : 384 : (void) OidFunctionCall6(proc,
411 : : Int32GetDatum(src_encoding),
412 : : Int32GetDatum(dest_encoding),
413 : : CStringGetDatum((char *) src),
414 : : CStringGetDatum((char *) result),
415 : : Int32GetDatum(len),
416 : : BoolGetDatum(false));
417 : :
418 : : /*
419 : : * If the result is large, it's worth repalloc'ing to release any extra
420 : : * space we asked for. The cutoff here is somewhat arbitrary, but we
421 : : * *must* check when len * MAX_CONVERSION_GROWTH exceeds MaxAllocSize.
422 : : */
2267 tgl@sss.pgh.pa.us 423 [ - + ]: 384 : if (len > 1000000)
424 : : {
2267 tgl@sss.pgh.pa.us 425 :UBC 0 : Size resultlen = strlen((char *) result);
426 : :
427 [ # # ]: 0 : if (resultlen >= MaxAllocSize)
428 [ # # ]: 0 : ereport(ERROR,
429 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
430 : : errmsg("out of memory"),
431 : : errdetail("String of %d bytes is too long for encoding conversion.",
432 : : len)));
433 : :
434 : 0 : result = (unsigned char *) repalloc(result, resultlen + 1);
435 : : }
436 : :
9594 tgl@sss.pgh.pa.us 437 :CBC 384 : return result;
438 : : }
439 : :
440 : : /*
441 : : * Convert src string to another encoding.
442 : : *
443 : : * This function has a different API than the other conversion functions.
444 : : * The caller should've looked up the conversion function using
445 : : * FindDefaultConversionProc(). Unlike the other functions, the converted
446 : : * result is not palloc'd. It is written to the caller-supplied buffer
447 : : * instead.
448 : : *
449 : : * src_encoding - encoding to convert from
450 : : * dest_encoding - encoding to convert to
451 : : * src, srclen - input buffer and its length in bytes
452 : : * dest, destlen - destination buffer and its size in bytes
453 : : *
454 : : * The output is null-terminated.
455 : : *
456 : : * If destlen < srclen * MAX_CONVERSION_INPUT_LENGTH + 1, the converted output
457 : : * wouldn't necessarily fit in the output buffer, and the function will not
458 : : * convert the whole input.
459 : : *
460 : : * TODO: The conversion function interface is not great. Firstly, it
461 : : * would be nice to pass through the destination buffer size to the
462 : : * conversion function, so that if you pass a shorter destination buffer, it
463 : : * could still continue to fill up the whole buffer. Currently, we have to
464 : : * assume worst case expansion and stop the conversion short, even if there
465 : : * is in fact space left in the destination buffer. Secondly, it would be
466 : : * nice to return the number of bytes written to the caller, to avoid a call
467 : : * to strlen().
468 : : */
469 : : int
1721 heikki.linnakangas@i 470 : 2904 : pg_do_encoding_conversion_buf(Oid proc,
471 : : int src_encoding,
472 : : int dest_encoding,
473 : : unsigned char *src, int srclen,
474 : : unsigned char *dest, int destlen,
475 : : bool noError)
476 : : {
477 : : Datum result;
478 : :
479 : : /*
480 : : * If the destination buffer is not large enough to hold the result in the
481 : : * worst case, limit the input size passed to the conversion function.
482 : : */
483 [ + + ]: 2904 : if ((Size) srclen >= ((destlen - 1) / (Size) MAX_CONVERSION_GROWTH))
484 : 2880 : srclen = ((destlen - 1) / (Size) MAX_CONVERSION_GROWTH);
485 : :
486 : 2904 : result = OidFunctionCall6(proc,
487 : : Int32GetDatum(src_encoding),
488 : : Int32GetDatum(dest_encoding),
489 : : CStringGetDatum((char *) src),
490 : : CStringGetDatum((char *) dest),
491 : : Int32GetDatum(srclen),
492 : : BoolGetDatum(noError));
493 : 1719 : return DatumGetInt32(result);
494 : : }
495 : :
496 : : /*
497 : : * Convert string to encoding encoding_name. The source
498 : : * encoding is the DB encoding.
499 : : *
500 : : * BYTEA convert_to(TEXT string, NAME encoding_name)
501 : : */
502 : : Datum
6665 andrew@dunslane.net 503 : 198 : pg_convert_to(PG_FUNCTION_ARGS)
504 : : {
8505 bruce@momjian.us 505 : 198 : Datum string = PG_GETARG_DATUM(0);
506 : 198 : Datum dest_encoding_name = PG_GETARG_DATUM(1);
6552 tgl@sss.pgh.pa.us 507 : 198 : Datum src_encoding_name = DirectFunctionCall1(namein,
508 : : CStringGetDatum(DatabaseEncoding->name));
509 : : Datum result;
510 : :
511 : : /*
512 : : * pg_convert expects a bytea as its first argument. We're passing it a
513 : : * text argument here, relying on the fact that they are both in fact
514 : : * varlena types, and thus structurally identical.
515 : : */
516 : 198 : result = DirectFunctionCall3(pg_convert, string,
517 : : src_encoding_name, dest_encoding_name);
518 : :
6458 519 : 198 : PG_RETURN_DATUM(result);
520 : : }
521 : :
522 : : /*
523 : : * Convert string from encoding encoding_name. The destination
524 : : * encoding is the DB encoding.
525 : : *
526 : : * TEXT convert_from(BYTEA string, NAME encoding_name)
527 : : */
528 : : Datum
6665 andrew@dunslane.net 529 : 290 : pg_convert_from(PG_FUNCTION_ARGS)
530 : : {
531 : 290 : Datum string = PG_GETARG_DATUM(0);
532 : 290 : Datum src_encoding_name = PG_GETARG_DATUM(1);
6552 tgl@sss.pgh.pa.us 533 : 290 : Datum dest_encoding_name = DirectFunctionCall1(namein,
534 : : CStringGetDatum(DatabaseEncoding->name));
535 : : Datum result;
536 : :
537 : 290 : result = DirectFunctionCall3(pg_convert, string,
538 : : src_encoding_name, dest_encoding_name);
539 : :
540 : : /*
541 : : * pg_convert returns a bytea, which we in turn return as text, relying on
542 : : * the fact that they are both in fact varlena types, and thus
543 : : * structurally identical. Although not all bytea values are valid text,
544 : : * in this case it will be because we've told pg_convert to return one
545 : : * that is valid as text in the current database encoding.
546 : : */
6458 547 : 287 : PG_RETURN_DATUM(result);
548 : : }
549 : :
550 : : /*
551 : : * Convert string between two arbitrary encodings.
552 : : *
553 : : * BYTEA convert(BYTEA string, NAME src_encoding_name, NAME dest_encoding_name)
554 : : */
555 : : Datum
6665 andrew@dunslane.net 556 : 872 : pg_convert(PG_FUNCTION_ARGS)
557 : : {
5493 itagaki.takahiro@gma 558 : 872 : bytea *string = PG_GETARG_BYTEA_PP(0);
8819 bruce@momjian.us 559 : 872 : char *src_encoding_name = NameStr(*PG_GETARG_NAME(1));
560 : 872 : int src_encoding = pg_char_to_encoding(src_encoding_name);
561 : 872 : char *dest_encoding_name = NameStr(*PG_GETARG_NAME(2));
562 : 872 : int dest_encoding = pg_char_to_encoding(dest_encoding_name);
563 : : const char *src_str;
564 : : char *dest_str;
565 : : bytea *retval;
566 : : int len;
567 : :
8890 ishii@postgresql.org 568 [ - + ]: 872 : if (src_encoding < 0)
8181 tgl@sss.pgh.pa.us 569 [ # # ]:UBC 0 : ereport(ERROR,
570 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
571 : : errmsg("invalid source encoding name \"%s\"",
572 : : src_encoding_name)));
8890 ishii@postgresql.org 573 [ - + ]:CBC 872 : if (dest_encoding < 0)
8181 tgl@sss.pgh.pa.us 574 [ # # ]:UBC 0 : ereport(ERROR,
575 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
576 : : errmsg("invalid destination encoding name \"%s\"",
577 : : dest_encoding_name)));
578 : :
579 : : /* make sure that source string is valid */
5493 itagaki.takahiro@gma 580 [ - + - - :CBC 872 : len = VARSIZE_ANY_EXHDR(string);
- - - - +
+ ]
581 [ + + ]: 872 : src_str = VARDATA_ANY(string);
1784 heikki.linnakangas@i 582 : 872 : (void) pg_verify_mbstr(src_encoding, src_str, len, false);
583 : :
584 : : /* perform conversion */
2514 peter@eisentraut.org 585 : 869 : dest_str = (char *) pg_do_encoding_conversion((unsigned char *) unconstify(char *, src_str),
586 : : len,
587 : : src_encoding,
588 : : dest_encoding);
589 : :
590 : :
591 : : /* return source string if no conversion happened */
744 nathan@postgresql.or 592 [ + + ]: 869 : if (dest_str == src_str)
593 : 485 : PG_RETURN_BYTEA_P(string);
594 : :
595 : : /*
596 : : * build bytea data type structure.
597 : : */
598 : 384 : len = strlen(dest_str);
5493 itagaki.takahiro@gma 599 : 384 : retval = (bytea *) palloc(len + VARHDRSZ);
600 : 384 : SET_VARSIZE(retval, len + VARHDRSZ);
601 : 384 : memcpy(VARDATA(retval), dest_str, len);
744 nathan@postgresql.or 602 : 384 : pfree(dest_str);
603 : :
604 : : /* free memory if allocated by the toaster */
8890 ishii@postgresql.org 605 [ - + ]: 384 : PG_FREE_IF_COPY(string, 0);
606 : :
6665 andrew@dunslane.net 607 : 384 : PG_RETURN_BYTEA_P(retval);
608 : : }
609 : :
610 : : /*
611 : : * get the length of the string considered as text in the specified
612 : : * encoding. Raises an error if the data is not valid in that
613 : : * encoding.
614 : : *
615 : : * INT4 length (BYTEA string, NAME src_encoding_name)
616 : : */
617 : : Datum
6665 andrew@dunslane.net 618 :UBC 0 : length_in_encoding(PG_FUNCTION_ARGS)
619 : : {
4315 tgl@sss.pgh.pa.us 620 : 0 : bytea *string = PG_GETARG_BYTEA_PP(0);
6665 andrew@dunslane.net 621 : 0 : char *src_encoding_name = NameStr(*PG_GETARG_NAME(1));
622 : 0 : int src_encoding = pg_char_to_encoding(src_encoding_name);
623 : : const char *src_str;
624 : : int len;
625 : : int retval;
626 : :
6640 tgl@sss.pgh.pa.us 627 [ # # ]: 0 : if (src_encoding < 0)
628 [ # # ]: 0 : ereport(ERROR,
629 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
630 : : errmsg("invalid encoding name \"%s\"",
631 : : src_encoding_name)));
632 : :
4315 633 [ # # # # : 0 : len = VARSIZE_ANY_EXHDR(string);
# # # # #
# ]
634 [ # # ]: 0 : src_str = VARDATA_ANY(string);
635 : :
636 : 0 : retval = pg_verify_mbstr_len(src_encoding, src_str, len, false);
637 : :
638 : 0 : PG_RETURN_INT32(retval);
639 : : }
640 : :
641 : : /*
642 : : * Get maximum multibyte character length in the specified encoding.
643 : : *
644 : : * Note encoding is specified numerically, not by name as above.
645 : : */
646 : : Datum
6007 peter_e@gmx.net 647 : 0 : pg_encoding_max_length_sql(PG_FUNCTION_ARGS)
648 : : {
5773 bruce@momjian.us 649 : 0 : int encoding = PG_GETARG_INT32(0);
650 : :
6007 peter_e@gmx.net 651 [ # # # # ]: 0 : if (PG_VALID_ENCODING(encoding))
tgl@sss.pgh.pa.us 652 : 0 : PG_RETURN_INT32(pg_wchar_table[encoding].maxmblen);
653 : : else
peter_e@gmx.net 654 : 0 : PG_RETURN_NULL();
655 : : }
656 : :
657 : : /*
658 : : * Convert client encoding to server encoding.
659 : : *
660 : : * See the notes about string conversion functions at the top of this file.
661 : : */
662 : : char *
7389 tgl@sss.pgh.pa.us 663 :CBC 409995 : pg_client_to_server(const char *s, int len)
664 : : {
5413 itagaki.takahiro@gma 665 : 409995 : return pg_any_to_server(s, len, ClientEncoding->encoding);
666 : : }
667 : :
668 : : /*
669 : : * Convert any encoding to server encoding.
670 : : *
671 : : * See the notes about string conversion functions at the top of this file.
672 : : *
673 : : * Unlike the other string conversion functions, this will apply validation
674 : : * even if encoding == DatabaseEncoding->encoding. This is because this is
675 : : * used to process data coming in from outside the database, and we never
676 : : * want to just assume validity.
677 : : */
678 : : char *
679 : 453601 : pg_any_to_server(const char *s, int len, int encoding)
680 : : {
7150 tgl@sss.pgh.pa.us 681 [ + + ]: 453601 : if (len <= 0)
2401 682 : 35254 : return unconstify(char *, s); /* empty string is always valid */
683 : :
5413 itagaki.takahiro@gma 684 [ + + + + ]: 418347 : if (encoding == DatabaseEncoding->encoding ||
685 : : encoding == PG_SQL_ASCII)
686 : : {
687 : : /*
688 : : * No conversion is needed, but we must still validate the data.
689 : : */
7150 tgl@sss.pgh.pa.us 690 : 418163 : (void) pg_verify_mbstr(DatabaseEncoding->encoding, s, len, false);
2514 peter@eisentraut.org 691 : 418162 : return unconstify(char *, s);
692 : : }
693 : :
7150 tgl@sss.pgh.pa.us 694 [ + + ]: 184 : if (DatabaseEncoding->encoding == PG_SQL_ASCII)
695 : : {
696 : : /*
697 : : * No conversion is possible, but we must still validate the data,
698 : : * because the client-side code might have done string escaping using
699 : : * the selected client_encoding. If the client encoding is ASCII-safe
700 : : * then we just do a straight validation under that encoding. For an
701 : : * ASCII-unsafe encoding we have a problem: we dare not pass such data
702 : : * to the parser but we have no way to convert it. We compromise by
703 : : * rejecting the data if it contains any non-ASCII characters.
704 : : */
5413 itagaki.takahiro@gma 705 [ + - + + ]: 154 : if (PG_VALID_BE_ENCODING(encoding))
706 : 124 : (void) pg_verify_mbstr(encoding, s, len, false);
707 : : else
708 : : {
709 : : int i;
710 : :
7150 tgl@sss.pgh.pa.us 711 [ + + ]: 954 : for (i = 0; i < len; i++)
712 : : {
713 [ + - - + ]: 924 : if (s[i] == '\0' || IS_HIGHBIT_SET(s[i]))
7150 tgl@sss.pgh.pa.us 714 [ # # ]:UBC 0 : ereport(ERROR,
715 : : (errcode(ERRCODE_CHARACTER_NOT_IN_REPERTOIRE),
716 : : errmsg("invalid byte value for encoding \"%s\": 0x%02x",
717 : : pg_enc2name_tbl[PG_SQL_ASCII].name,
718 : : (unsigned char) s[i])));
719 : : }
720 : : }
2514 peter@eisentraut.org 721 :CBC 154 : return unconstify(char *, s);
722 : : }
723 : :
724 : : /* Fast path if we can use cached conversion function */
4315 tgl@sss.pgh.pa.us 725 [ + - ]: 30 : if (encoding == ClientEncoding->encoding)
5413 itagaki.takahiro@gma 726 : 30 : return perform_default_encoding_conversion(s, len, true);
727 : :
728 : : /* General case ... will not work outside transactions */
2514 peter@eisentraut.org 729 :UBC 0 : return (char *) pg_do_encoding_conversion((unsigned char *) unconstify(char *, s),
730 : : len,
731 : : encoding,
4315 tgl@sss.pgh.pa.us 732 : 0 : DatabaseEncoding->encoding);
733 : : }
734 : :
735 : : /*
736 : : * Convert server encoding to client encoding.
737 : : *
738 : : * See the notes about string conversion functions at the top of this file.
739 : : */
740 : : char *
7389 tgl@sss.pgh.pa.us 741 :CBC 19051135 : pg_server_to_client(const char *s, int len)
742 : : {
5413 itagaki.takahiro@gma 743 : 19051135 : return pg_server_to_any(s, len, ClientEncoding->encoding);
744 : : }
745 : :
746 : : /*
747 : : * Convert server encoding to any encoding.
748 : : *
749 : : * See the notes about string conversion functions at the top of this file.
750 : : */
751 : : char *
752 : 19070862 : pg_server_to_any(const char *s, int len, int encoding)
753 : : {
7150 tgl@sss.pgh.pa.us 754 [ + + ]: 19070862 : if (len <= 0)
2401 755 : 132078 : return unconstify(char *, s); /* empty string is always valid */
756 : :
5413 itagaki.takahiro@gma 757 [ + + + + ]: 18938784 : if (encoding == DatabaseEncoding->encoding ||
758 : : encoding == PG_SQL_ASCII)
2401 tgl@sss.pgh.pa.us 759 : 18938506 : return unconstify(char *, s); /* assume data is valid */
760 : :
4315 761 [ + + ]: 278 : if (DatabaseEncoding->encoding == PG_SQL_ASCII)
762 : : {
763 : : /* No conversion is possible, but we must validate the result */
764 : 84 : (void) pg_verify_mbstr(encoding, s, len, false);
2514 peter@eisentraut.org 765 : 84 : return unconstify(char *, s);
766 : : }
767 : :
768 : : /* Fast path if we can use cached conversion function */
4315 tgl@sss.pgh.pa.us 769 [ + - ]: 194 : if (encoding == ClientEncoding->encoding)
5413 itagaki.takahiro@gma 770 : 194 : return perform_default_encoding_conversion(s, len, false);
771 : :
772 : : /* General case ... will not work outside transactions */
2514 peter@eisentraut.org 773 :UBC 0 : return (char *) pg_do_encoding_conversion((unsigned char *) unconstify(char *, s),
774 : : len,
4315 tgl@sss.pgh.pa.us 775 : 0 : DatabaseEncoding->encoding,
776 : : encoding);
777 : : }
778 : :
779 : : /*
780 : : * Perform default encoding conversion using cached FmgrInfo. Since
781 : : * this function does not access database at all, it is safe to call
782 : : * outside transactions. If the conversion has not been set up by
783 : : * SetClientEncoding(), no conversion is performed.
784 : : */
785 : : static char *
4315 tgl@sss.pgh.pa.us 786 :CBC 224 : perform_default_encoding_conversion(const char *src, int len,
787 : : bool is_client_to_server)
788 : : {
789 : : char *result;
790 : : int src_encoding,
791 : : dest_encoding;
792 : : FmgrInfo *flinfo;
793 : :
8532 ishii@postgresql.org 794 [ + + ]: 224 : if (is_client_to_server)
795 : : {
796 : 30 : src_encoding = ClientEncoding->encoding;
797 : 30 : dest_encoding = DatabaseEncoding->encoding;
8446 tgl@sss.pgh.pa.us 798 : 30 : flinfo = ToServerConvProc;
799 : : }
800 : : else
801 : : {
8532 ishii@postgresql.org 802 : 194 : src_encoding = DatabaseEncoding->encoding;
803 : 194 : dest_encoding = ClientEncoding->encoding;
8446 tgl@sss.pgh.pa.us 804 : 194 : flinfo = ToClientConvProc;
805 : : }
806 : :
8532 ishii@postgresql.org 807 [ - + ]: 224 : if (flinfo == NULL)
2514 peter@eisentraut.org 808 :UBC 0 : return unconstify(char *, src);
809 : :
810 : : /*
811 : : * Allocate space for conversion result, being wary of integer overflow.
812 : : * See comments in pg_do_encoding_conversion.
813 : : */
2267 tgl@sss.pgh.pa.us 814 [ - + ]:CBC 224 : if ((Size) len >= (MaxAllocHugeSize / (Size) MAX_CONVERSION_GROWTH))
6778 tgl@sss.pgh.pa.us 815 [ # # ]:UBC 0 : ereport(ERROR,
816 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
817 : : errmsg("out of memory"),
818 : : errdetail("String of %d bytes is too long for encoding conversion.",
819 : : len)));
820 : :
821 : : result = (char *)
2267 tgl@sss.pgh.pa.us 822 :CBC 224 : MemoryContextAllocHuge(CurrentMemoryContext,
823 : 224 : (Size) len * MAX_CONVERSION_GROWTH + 1);
824 : :
1721 heikki.linnakangas@i 825 : 224 : FunctionCall6(flinfo,
826 : : Int32GetDatum(src_encoding),
827 : : Int32GetDatum(dest_encoding),
828 : : CStringGetDatum(src),
829 : : CStringGetDatum(result),
830 : : Int32GetDatum(len),
831 : : BoolGetDatum(false));
832 : :
833 : : /*
834 : : * Release extra space if there might be a lot --- see comments in
835 : : * pg_do_encoding_conversion.
836 : : */
2267 tgl@sss.pgh.pa.us 837 [ - + ]: 224 : if (len > 1000000)
838 : : {
2267 tgl@sss.pgh.pa.us 839 :UBC 0 : Size resultlen = strlen(result);
840 : :
841 [ # # ]: 0 : if (resultlen >= MaxAllocSize)
842 [ # # ]: 0 : ereport(ERROR,
843 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
844 : : errmsg("out of memory"),
845 : : errdetail("String of %d bytes is too long for encoding conversion.",
846 : : len)));
847 : :
848 : 0 : result = (char *) repalloc(result, resultlen + 1);
849 : : }
850 : :
8532 ishii@postgresql.org 851 :CBC 224 : return result;
852 : : }
853 : :
854 : : /*
855 : : * Convert a single Unicode code point into a string in the server encoding.
856 : : *
857 : : * The code point given by "c" is converted and stored at *s, which must
858 : : * have at least MAX_UNICODE_EQUIVALENT_STRING+1 bytes available.
859 : : * The output will have a trailing '\0'. Throws error if the conversion
860 : : * cannot be performed.
861 : : *
862 : : * Note that this relies on having previously looked up any required
863 : : * conversion function. That's partly for speed but mostly because the parser
864 : : * may call this outside any transaction, or in an aborted transaction.
865 : : */
866 : : void
49 jdavis@postgresql.or 867 :GNC 501 : pg_unicode_to_server(char32_t c, unsigned char *s)
868 : : {
869 : : unsigned char c_as_utf8[MAX_MULTIBYTE_CHAR_LEN + 1];
870 : : int c_as_utf8_len;
871 : : int server_encoding;
872 : :
873 : : /*
874 : : * Complain if invalid Unicode code point. The choice of errcode here is
875 : : * debatable, but really our caller should have checked this anyway.
876 : : */
2112 tgl@sss.pgh.pa.us 877 [ - + ]:CBC 501 : if (!is_valid_unicode_codepoint(c))
2112 tgl@sss.pgh.pa.us 878 [ # # ]:UBC 0 : ereport(ERROR,
879 : : (errcode(ERRCODE_SYNTAX_ERROR),
880 : : errmsg("invalid Unicode code point")));
881 : :
882 : : /* Otherwise, if it's in ASCII range, conversion is trivial */
2112 tgl@sss.pgh.pa.us 883 [ + + ]:CBC 501 : if (c <= 0x7F)
884 : : {
885 : 172 : s[0] = (unsigned char) c;
886 : 172 : s[1] = '\0';
887 : 501 : return;
888 : : }
889 : :
890 : : /* If the server encoding is UTF-8, we just need to reformat the code */
891 : 329 : server_encoding = GetDatabaseEncoding();
892 [ + - ]: 329 : if (server_encoding == PG_UTF8)
893 : : {
894 : 329 : unicode_to_utf8(c, s);
895 : 329 : s[pg_utf_mblen(s)] = '\0';
896 : 329 : return;
897 : : }
898 : :
899 : : /* For all other cases, we must have a conversion function available */
2112 tgl@sss.pgh.pa.us 900 [ # # ]:UBC 0 : if (Utf8ToServerConvProc == NULL)
901 [ # # ]: 0 : ereport(ERROR,
902 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
903 : : errmsg("conversion between %s and %s is not supported",
904 : : pg_enc2name_tbl[PG_UTF8].name,
905 : : GetDatabaseEncodingName())));
906 : :
907 : : /* Construct UTF-8 source string */
908 : 0 : unicode_to_utf8(c, c_as_utf8);
909 : 0 : c_as_utf8_len = pg_utf_mblen(c_as_utf8);
910 : 0 : c_as_utf8[c_as_utf8_len] = '\0';
911 : :
912 : : /* Convert, or throw error if we can't */
1721 heikki.linnakangas@i 913 : 0 : FunctionCall6(Utf8ToServerConvProc,
914 : : Int32GetDatum(PG_UTF8),
915 : : Int32GetDatum(server_encoding),
916 : : CStringGetDatum((char *) c_as_utf8),
917 : : CStringGetDatum((char *) s),
918 : : Int32GetDatum(c_as_utf8_len),
919 : : BoolGetDatum(false));
920 : : }
921 : :
922 : : /*
923 : : * Convert a single Unicode code point into a string in the server encoding.
924 : : *
925 : : * Same as pg_unicode_to_server(), except that we don't throw errors,
926 : : * but simply return false on conversion failure.
927 : : */
928 : : bool
49 jdavis@postgresql.or 929 :GNC 42 : pg_unicode_to_server_noerror(char32_t c, unsigned char *s)
930 : : {
931 : : unsigned char c_as_utf8[MAX_MULTIBYTE_CHAR_LEN + 1];
932 : : int c_as_utf8_len;
933 : : int converted_len;
934 : : int server_encoding;
935 : :
936 : : /* Fail if invalid Unicode code point */
1102 tgl@sss.pgh.pa.us 937 [ - + ]:CBC 42 : if (!is_valid_unicode_codepoint(c))
1102 tgl@sss.pgh.pa.us 938 :UBC 0 : return false;
939 : :
940 : : /* Otherwise, if it's in ASCII range, conversion is trivial */
1102 tgl@sss.pgh.pa.us 941 [ + + ]:CBC 42 : if (c <= 0x7F)
942 : : {
943 : 12 : s[0] = (unsigned char) c;
944 : 12 : s[1] = '\0';
945 : 12 : return true;
946 : : }
947 : :
948 : : /* If the server encoding is UTF-8, we just need to reformat the code */
949 : 30 : server_encoding = GetDatabaseEncoding();
950 [ + - ]: 30 : if (server_encoding == PG_UTF8)
951 : : {
952 : 30 : unicode_to_utf8(c, s);
953 : 30 : s[pg_utf_mblen(s)] = '\0';
954 : 30 : return true;
955 : : }
956 : :
957 : : /* For all other cases, we must have a conversion function available */
1102 tgl@sss.pgh.pa.us 958 [ # # ]:UBC 0 : if (Utf8ToServerConvProc == NULL)
959 : 0 : return false;
960 : :
961 : : /* Construct UTF-8 source string */
962 : 0 : unicode_to_utf8(c, c_as_utf8);
963 : 0 : c_as_utf8_len = pg_utf_mblen(c_as_utf8);
964 : 0 : c_as_utf8[c_as_utf8_len] = '\0';
965 : :
966 : : /* Convert, but without throwing error if we can't */
967 : 0 : converted_len = DatumGetInt32(FunctionCall6(Utf8ToServerConvProc,
968 : : Int32GetDatum(PG_UTF8),
969 : : Int32GetDatum(server_encoding),
970 : : CStringGetDatum((char *) c_as_utf8),
971 : : CStringGetDatum((char *) s),
972 : : Int32GetDatum(c_as_utf8_len),
973 : : BoolGetDatum(true)));
974 : :
975 : : /* Conversion was successful iff it consumed the whole input */
976 : 0 : return (converted_len == c_as_utf8_len);
977 : : }
978 : :
979 : :
980 : : /* convert a multibyte string to a wchar */
981 : : int
7389 982 : 0 : pg_mb2wchar(const char *from, pg_wchar *to)
983 : : {
3023 peter_e@gmx.net 984 : 0 : return pg_wchar_table[DatabaseEncoding->encoding].mb2wchar_with_len((const unsigned char *) from, to, strlen(from));
985 : : }
986 : :
987 : : /* convert a multibyte string to a wchar with a limited length */
988 : : int
7389 tgl@sss.pgh.pa.us 989 :CBC 5115837 : pg_mb2wchar_with_len(const char *from, pg_wchar *to, int len)
990 : : {
3023 peter_e@gmx.net 991 : 5115837 : return pg_wchar_table[DatabaseEncoding->encoding].mb2wchar_with_len((const unsigned char *) from, to, len);
992 : : }
993 : :
994 : : /* same, with any encoding */
995 : : int
6933 tgl@sss.pgh.pa.us 996 : 9140 : pg_encoding_mb2wchar_with_len(int encoding,
997 : : const char *from, pg_wchar *to, int len)
998 : : {
3023 peter_e@gmx.net 999 : 9140 : return pg_wchar_table[encoding].mb2wchar_with_len((const unsigned char *) from, to, len);
1000 : : }
1001 : :
1002 : : /* convert a wchar string to a multibyte */
1003 : : int
4914 rhaas@postgresql.org 1004 :UBC 0 : pg_wchar2mb(const pg_wchar *from, char *to)
1005 : : {
3023 peter_e@gmx.net 1006 : 0 : return pg_wchar_table[DatabaseEncoding->encoding].wchar2mb_with_len(from, (unsigned char *) to, pg_wchar_strlen(from));
1007 : : }
1008 : :
1009 : : /* convert a wchar string to a multibyte with a limited length */
1010 : : int
4914 rhaas@postgresql.org 1011 :CBC 558326 : pg_wchar2mb_with_len(const pg_wchar *from, char *to, int len)
1012 : : {
3023 peter_e@gmx.net 1013 : 558326 : return pg_wchar_table[DatabaseEncoding->encoding].wchar2mb_with_len(from, (unsigned char *) to, len);
1014 : : }
1015 : :
1016 : : /* same, with any encoding */
1017 : : int
4914 rhaas@postgresql.org 1018 :UBC 0 : pg_encoding_wchar2mb_with_len(int encoding,
1019 : : const pg_wchar *from, char *to, int len)
1020 : : {
3023 peter_e@gmx.net 1021 : 0 : return pg_wchar_table[encoding].wchar2mb_with_len(from, (unsigned char *) to, len);
1022 : : }
1023 : :
1024 : : /* returns the byte length of a multibyte character */
1025 : : int
7389 tgl@sss.pgh.pa.us 1026 :CBC 126978442 : pg_mblen(const char *mbstr)
1027 : : {
3023 peter_e@gmx.net 1028 : 126978442 : return pg_wchar_table[DatabaseEncoding->encoding].mblen((const unsigned char *) mbstr);
1029 : : }
1030 : :
1031 : : /* returns the display length of a multibyte character */
1032 : : int
7389 tgl@sss.pgh.pa.us 1033 : 4362 : pg_dsplen(const char *mbstr)
1034 : : {
3023 peter_e@gmx.net 1035 : 4362 : return pg_wchar_table[DatabaseEncoding->encoding].dsplen((const unsigned char *) mbstr);
1036 : : }
1037 : :
1038 : : /* returns the length (counted in wchars) of a multibyte string */
1039 : : int
7389 tgl@sss.pgh.pa.us 1040 : 351 : pg_mbstrlen(const char *mbstr)
1041 : : {
9969 bruce@momjian.us 1042 : 351 : int len = 0;
1043 : :
1044 : : /* optimization for single byte encoding */
8511 ishii@postgresql.org 1045 [ - + ]: 351 : if (pg_database_encoding_max_length() == 1)
7389 tgl@sss.pgh.pa.us 1046 :UBC 0 : return strlen(mbstr);
1047 : :
9969 bruce@momjian.us 1048 [ + + ]:CBC 813 : while (*mbstr)
1049 : : {
1050 : 462 : mbstr += pg_mblen(mbstr);
1051 : 462 : len++;
1052 : : }
7280 neilc@samurai.com 1053 : 351 : return len;
1054 : : }
1055 : :
1056 : : /* returns the length (counted in wchars) of a multibyte string
1057 : : * (not necessarily NULL terminated)
1058 : : */
1059 : : int
7389 tgl@sss.pgh.pa.us 1060 : 804748 : pg_mbstrlen_with_len(const char *mbstr, int limit)
1061 : : {
9969 bruce@momjian.us 1062 : 804748 : int len = 0;
1063 : :
1064 : : /* optimization for single byte encoding */
7465 tgl@sss.pgh.pa.us 1065 [ + + ]: 804748 : if (pg_database_encoding_max_length() == 1)
1066 : 200007 : return limit;
1067 : :
9050 1068 [ + + + - ]: 110792821 : while (limit > 0 && *mbstr)
1069 : : {
7368 bruce@momjian.us 1070 : 110188080 : int l = pg_mblen(mbstr);
1071 : :
9969 1072 : 110188080 : limit -= l;
1073 : 110188080 : mbstr += l;
1074 : 110188080 : len++;
1075 : : }
7280 neilc@samurai.com 1076 : 604741 : return len;
1077 : : }
1078 : :
1079 : : /*
1080 : : * returns the byte length of a multibyte string
1081 : : * (not necessarily NULL terminated)
1082 : : * that is no longer than limit.
1083 : : * this function does not break multibyte character boundary.
1084 : : */
1085 : : int
7389 tgl@sss.pgh.pa.us 1086 : 153148 : pg_mbcliplen(const char *mbstr, int len, int limit)
1087 : : {
6191 1088 : 153148 : return pg_encoding_mbcliplen(DatabaseEncoding->encoding, mbstr,
1089 : : len, limit);
1090 : : }
1091 : :
1092 : : /*
1093 : : * pg_mbcliplen with specified encoding; string must be valid in encoding
1094 : : */
1095 : : int
1096 : 153148 : pg_encoding_mbcliplen(int encoding, const char *mbstr,
1097 : : int len, int limit)
1098 : : {
1099 : : mblen_converter mblen_fn;
9945 bruce@momjian.us 1100 : 153148 : int clen = 0;
1101 : : int l;
1102 : :
1103 : : /* optimization for single byte encoding */
6191 tgl@sss.pgh.pa.us 1104 [ + + ]: 153148 : if (pg_encoding_max_length(encoding) == 1)
8511 ishii@postgresql.org 1105 : 18672 : return cliplen(mbstr, len, limit);
1106 : :
6191 tgl@sss.pgh.pa.us 1107 : 134476 : mblen_fn = pg_wchar_table[encoding].mblen;
1108 : :
9050 1109 [ + + + - ]: 1460232 : while (len > 0 && *mbstr)
1110 : : {
6191 1111 : 1393844 : l = (*mblen_fn) ((const unsigned char *) mbstr);
9703 bruce@momjian.us 1112 [ + + ]: 1393844 : if ((clen + l) > limit)
9945 1113 : 47 : break;
1114 : 1393797 : clen += l;
9703 1115 [ + + ]: 1393797 : if (clen == limit)
9945 1116 : 68041 : break;
1117 : 1325756 : len -= l;
1118 : 1325756 : mbstr += l;
1119 : : }
7280 neilc@samurai.com 1120 : 134476 : return clen;
1121 : : }
1122 : :
1123 : : /*
1124 : : * Similar to pg_mbcliplen except the limit parameter specifies the
1125 : : * character length, not the byte length.
1126 : : */
1127 : : int
7389 tgl@sss.pgh.pa.us 1128 : 264 : pg_mbcharcliplen(const char *mbstr, int len, int limit)
1129 : : {
8921 ishii@postgresql.org 1130 : 264 : int clen = 0;
1131 : 264 : int nch = 0;
1132 : : int l;
1133 : :
1134 : : /* optimization for single byte encoding */
8511 1135 [ - + ]: 264 : if (pg_database_encoding_max_length() == 1)
8511 ishii@postgresql.org 1136 :UBC 0 : return cliplen(mbstr, len, limit);
1137 : :
8921 ishii@postgresql.org 1138 [ + + + - ]:CBC 1164 : while (len > 0 && *mbstr)
1139 : : {
1140 : 1155 : l = pg_mblen(mbstr);
1141 : 1155 : nch++;
1142 [ + + ]: 1155 : if (nch > limit)
1143 : 255 : break;
1144 : 900 : clen += l;
1145 : 900 : len -= l;
1146 : 900 : mbstr += l;
1147 : : }
7280 neilc@samurai.com 1148 : 264 : return clen;
1149 : : }
1150 : :
1151 : : /* mbcliplen for any single-byte encoding */
1152 : : static int
6191 tgl@sss.pgh.pa.us 1153 : 18672 : cliplen(const char *str, int len, int limit)
1154 : : {
1155 : 18672 : int l = 0;
1156 : :
1157 : 18672 : len = Min(len, limit);
1158 [ + + + - ]: 145810 : while (l < len && str[l])
1159 : 127138 : l++;
1160 : 18672 : return l;
1161 : : }
1162 : :
1163 : : void
10008 scrappy@hub.org 1164 : 14038 : SetDatabaseEncoding(int encoding)
1165 : : {
8868 ishii@postgresql.org 1166 [ + - - + ]: 14038 : if (!PG_VALID_BE_ENCODING(encoding))
6936 peter_e@gmx.net 1167 [ # # ]:UBC 0 : elog(ERROR, "invalid database encoding: %d", encoding);
1168 : :
8819 bruce@momjian.us 1169 :CBC 14038 : DatabaseEncoding = &pg_enc2name_tbl[encoding];
8868 ishii@postgresql.org 1170 [ - + ]: 14038 : Assert(DatabaseEncoding->encoding == encoding);
6128 alvherre@alvh.no-ip. 1171 : 14038 : }
1172 : :
1173 : : void
4557 noah@leadboat.com 1174 : 15919 : SetMessageEncoding(int encoding)
1175 : : {
1176 : : /* Some calls happen before we can elog()! */
1177 [ + - - + ]: 15919 : Assert(PG_VALID_ENCODING(encoding));
1178 : :
1179 : 15919 : MessageEncoding = &pg_enc2name_tbl[encoding];
1180 [ - + ]: 15919 : Assert(MessageEncoding->encoding == encoding);
1181 : 15919 : }
1182 : :
1183 : : #ifdef ENABLE_NLS
1184 : : /*
1185 : : * Make one bind_textdomain_codeset() call, translating a pg_enc to a gettext
1186 : : * codeset. Fails for MULE_INTERNAL, an encoding unknown to gettext; can also
1187 : : * fail for gettext-internal causes like out-of-memory.
1188 : : */
1189 : : static bool
1190 : 1615 : raw_pg_bind_textdomain_codeset(const char *domainname, int encoding)
1191 : : {
1192 : 1615 : bool elog_ok = (CurrentMemoryContext != NULL);
1193 : :
656 michael@paquier.xyz 1194 [ + - + - : 1615 : if (!PG_VALID_ENCODING(encoding) || pg_enc2gettext_tbl[encoding] == NULL)
- + ]
656 michael@paquier.xyz 1195 :UBC 0 : return false;
1196 : :
656 michael@paquier.xyz 1197 [ + - ]:CBC 1615 : if (bind_textdomain_codeset(domainname,
1198 : : pg_enc2gettext_tbl[encoding]) != NULL)
1199 : 1615 : return true;
1200 : :
656 michael@paquier.xyz 1201 [ # # ]:UBC 0 : if (elog_ok)
1202 [ # # ]: 0 : elog(LOG, "bind_textdomain_codeset failed");
1203 : : else
1204 : 0 : write_stderr("bind_textdomain_codeset failed");
1205 : :
4557 noah@leadboat.com 1206 : 0 : return false;
1207 : : }
1208 : :
1209 : : /*
1210 : : * Bind a gettext message domain to the codeset corresponding to the database
1211 : : * encoding. For SQL_ASCII, instead bind to the codeset implied by LC_CTYPE.
1212 : : * Return the MessageEncoding implied by the new settings.
1213 : : *
1214 : : * On most platforms, gettext defaults to the codeset implied by LC_CTYPE.
1215 : : * When that matches the database encoding, we don't need to do anything. In
1216 : : * CREATE DATABASE, we enforce or trust that the locale's codeset matches the
1217 : : * database encoding, except for the C locale. (On Windows, we also permit a
1218 : : * discrepancy under the UTF8 encoding.) For the C locale, explicitly bind
1219 : : * gettext to the right codeset.
1220 : : *
1221 : : * On Windows, gettext defaults to the Windows ANSI code page. This is a
1222 : : * convenient departure for software that passes the strings to Windows ANSI
1223 : : * APIs, but we don't do that. Compel gettext to use database encoding or,
1224 : : * failing that, the LC_CTYPE encoding as it would on other platforms.
1225 : : *
1226 : : * This function is called before elog() and palloc() are usable.
1227 : : */
1228 : : int
4557 noah@leadboat.com 1229 :CBC 17854 : pg_bind_textdomain_codeset(const char *domainname)
1230 : : {
1231 : 17854 : bool elog_ok = (CurrentMemoryContext != NULL);
1232 : 17854 : int encoding = GetDatabaseEncoding();
1233 : : int new_msgenc;
1234 : :
1235 : : #ifndef WIN32
1236 : 17854 : const char *ctype = setlocale(LC_CTYPE, NULL);
1237 : :
1238 [ + + - + ]: 17854 : if (pg_strcasecmp(ctype, "C") == 0 || pg_strcasecmp(ctype, "POSIX") == 0)
1239 : : #endif
1240 [ + + + - ]: 3625 : if (encoding != PG_SQL_ASCII &&
1241 : 1615 : raw_pg_bind_textdomain_codeset(domainname, encoding))
1242 : 1615 : return encoding;
1243 : :
1244 : 16239 : new_msgenc = pg_get_encoding_from_locale(NULL, elog_ok);
1245 [ - + ]: 16239 : if (new_msgenc < 0)
4557 noah@leadboat.com 1246 :UBC 0 : new_msgenc = PG_SQL_ASCII;
1247 : :
1248 : : #ifdef WIN32
1249 : : if (!raw_pg_bind_textdomain_codeset(domainname, new_msgenc))
1250 : : /* On failure, the old message encoding remains valid. */
1251 : : return GetMessageEncoding();
1252 : : #endif
1253 : :
4557 noah@leadboat.com 1254 :CBC 16239 : return new_msgenc;
1255 : : }
1256 : : #endif
1257 : :
1258 : : /*
1259 : : * The database encoding, also called the server encoding, represents the
1260 : : * encoding of data stored in text-like data types. Affected types include
1261 : : * cstring, text, varchar, name, xml, and json.
1262 : : */
1263 : : int
8853 tgl@sss.pgh.pa.us 1264 : 4700027 : GetDatabaseEncoding(void)
1265 : : {
7280 neilc@samurai.com 1266 : 4700027 : return DatabaseEncoding->encoding;
1267 : : }
1268 : :
1269 : : const char *
8853 tgl@sss.pgh.pa.us 1270 : 29595 : GetDatabaseEncodingName(void)
1271 : : {
7280 neilc@samurai.com 1272 : 29595 : return DatabaseEncoding->name;
1273 : : }
1274 : :
1275 : : Datum
9318 tgl@sss.pgh.pa.us 1276 : 44 : getdatabaseencoding(PG_FUNCTION_ARGS)
1277 : : {
8868 ishii@postgresql.org 1278 : 44 : return DirectFunctionCall1(namein, CStringGetDatum(DatabaseEncoding->name));
1279 : : }
1280 : :
1281 : : Datum
8832 ishii@postgresql.org 1282 :UBC 0 : pg_client_encoding(PG_FUNCTION_ARGS)
1283 : : {
1284 : 0 : return DirectFunctionCall1(namein, CStringGetDatum(ClientEncoding->name));
1285 : : }
1286 : :
1287 : : Datum
2162 tgl@sss.pgh.pa.us 1288 :CBC 18 : PG_char_to_encoding(PG_FUNCTION_ARGS)
1289 : : {
1290 : 18 : Name s = PG_GETARG_NAME(0);
1291 : :
1292 : 18 : PG_RETURN_INT32(pg_char_to_encoding(NameStr(*s)));
1293 : : }
1294 : :
1295 : : Datum
1296 : 2457 : PG_encoding_to_char(PG_FUNCTION_ARGS)
1297 : : {
1298 : 2457 : int32 encoding = PG_GETARG_INT32(0);
1299 : 2457 : const char *encoding_name = pg_encoding_to_char(encoding);
1300 : :
1301 : 2457 : return DirectFunctionCall1(namein, CStringGetDatum(encoding_name));
1302 : : }
1303 : :
1304 : : /*
1305 : : * gettext() returns messages in this encoding. This often matches the
1306 : : * database encoding, but it differs for SQL_ASCII databases, for processes
1307 : : * not attached to a database, and under a database encoding lacking iconv
1308 : : * support (MULE_INTERNAL).
1309 : : */
1310 : : int
4557 noah@leadboat.com 1311 :UBC 0 : GetMessageEncoding(void)
1312 : : {
1313 : 0 : return MessageEncoding->encoding;
1314 : : }
1315 : :
1316 : :
1317 : : /*
1318 : : * Generic character incrementer function.
1319 : : *
1320 : : * Not knowing anything about the properties of the encoding in use, we just
1321 : : * keep incrementing the last byte until we get a validly-encoded result,
1322 : : * or we run out of values to try. We don't bother to try incrementing
1323 : : * higher-order bytes, so there's no growth in runtime for wider characters.
1324 : : * (If we did try to do that, we'd need to consider the likelihood that 255
1325 : : * is not a valid final byte in the encoding.)
1326 : : */
1327 : : static bool
2162 tgl@sss.pgh.pa.us 1328 :CBC 52 : pg_generic_charinc(unsigned char *charptr, int len)
1329 : : {
1330 : 52 : unsigned char *lastbyte = charptr + len - 1;
1331 : : mbchar_verifier mbverify;
1332 : :
1333 : : /* We can just invoke the character verifier directly. */
1784 heikki.linnakangas@i 1334 : 52 : mbverify = pg_wchar_table[GetDatabaseEncoding()].mbverifychar;
1335 : :
2162 tgl@sss.pgh.pa.us 1336 [ + - ]: 52 : while (*lastbyte < (unsigned char) 255)
1337 : : {
1338 : 52 : (*lastbyte)++;
1339 [ + - ]: 52 : if ((*mbverify) (charptr, len) == len)
1340 : 52 : return true;
1341 : : }
1342 : :
2162 tgl@sss.pgh.pa.us 1343 :UBC 0 : return false;
1344 : : }
1345 : :
1346 : : /*
1347 : : * UTF-8 character incrementer function.
1348 : : *
1349 : : * For a one-byte character less than 0x7F, we just increment the byte.
1350 : : *
1351 : : * For a multibyte character, every byte but the first must fall between 0x80
1352 : : * and 0xBF; and the first byte must be between 0xC0 and 0xF4. We increment
1353 : : * the last byte that's not already at its maximum value. If we can't find a
1354 : : * byte that's less than the maximum allowable value, we simply fail. We also
1355 : : * need some special-case logic to skip regions used for surrogate pair
1356 : : * handling, as those should not occur in valid UTF-8.
1357 : : *
1358 : : * Note that we don't reset lower-order bytes back to their minimums, since
1359 : : * we can't afford to make an exhaustive search (see make_greater_string).
1360 : : */
1361 : : static bool
2162 tgl@sss.pgh.pa.us 1362 :CBC 1763 : pg_utf8_increment(unsigned char *charptr, int length)
1363 : : {
1364 : : unsigned char a;
1365 : : unsigned char limit;
1366 : :
1367 [ - - - - : 1763 : switch (length)
+ ]
1368 : : {
2162 tgl@sss.pgh.pa.us 1369 :UBC 0 : default:
1370 : : /* reject lengths 5 and 6 for now */
1371 : 0 : return false;
1372 : 0 : case 4:
1373 : 0 : a = charptr[3];
1374 [ # # ]: 0 : if (a < 0xBF)
1375 : : {
1376 : 0 : charptr[3]++;
1377 : 0 : break;
1378 : : }
1379 : : /* FALL THRU */
1380 : : case 3:
1381 : 0 : a = charptr[2];
1382 [ # # ]: 0 : if (a < 0xBF)
1383 : : {
1384 : 0 : charptr[2]++;
1385 : 0 : break;
1386 : : }
1387 : : /* FALL THRU */
1388 : : case 2:
1389 : 0 : a = charptr[1];
1390 [ # # # ]: 0 : switch (*charptr)
1391 : : {
1392 : 0 : case 0xED:
1393 : 0 : limit = 0x9F;
1394 : 0 : break;
1395 : 0 : case 0xF4:
1396 : 0 : limit = 0x8F;
1397 : 0 : break;
1398 : 0 : default:
1399 : 0 : limit = 0xBF;
1400 : 0 : break;
1401 : : }
1402 [ # # ]: 0 : if (a < limit)
1403 : : {
1404 : 0 : charptr[1]++;
1405 : 0 : break;
1406 : : }
1407 : : /* FALL THRU */
1408 : : case 1:
2162 tgl@sss.pgh.pa.us 1409 :CBC 1763 : a = *charptr;
1410 [ + - + - : 1763 : if (a == 0x7F || a == 0xDF || a == 0xEF || a == 0xF4)
+ - - + ]
2162 tgl@sss.pgh.pa.us 1411 :UBC 0 : return false;
2162 tgl@sss.pgh.pa.us 1412 :CBC 1763 : charptr[0]++;
1413 : 1763 : break;
1414 : : }
1415 : :
1416 : 1763 : return true;
1417 : : }
1418 : :
1419 : : /*
1420 : : * EUC-JP character incrementer function.
1421 : : *
1422 : : * If the sequence starts with SS2 (0x8e), it must be a two-byte sequence
1423 : : * representing JIS X 0201 characters with the second byte ranging between
1424 : : * 0xa1 and 0xdf. We just increment the last byte if it's less than 0xdf,
1425 : : * and otherwise rewrite the whole sequence to 0xa1 0xa1.
1426 : : *
1427 : : * If the sequence starts with SS3 (0x8f), it must be a three-byte sequence
1428 : : * in which the last two bytes range between 0xa1 and 0xfe. The last byte
1429 : : * is incremented if possible, otherwise the second-to-last byte.
1430 : : *
1431 : : * If the sequence starts with a value other than the above and its MSB
1432 : : * is set, it must be a two-byte sequence representing JIS X 0208 characters
1433 : : * with both bytes ranging between 0xa1 and 0xfe. The last byte is
1434 : : * incremented if possible, otherwise the second-to-last byte.
1435 : : *
1436 : : * Otherwise, the sequence is a single-byte ASCII character. It is
1437 : : * incremented up to 0x7f.
1438 : : */
1439 : : static bool
2162 tgl@sss.pgh.pa.us 1440 :UBC 0 : pg_eucjp_increment(unsigned char *charptr, int length)
1441 : : {
1442 : : unsigned char c1,
1443 : : c2;
1444 : : int i;
1445 : :
1446 : 0 : c1 = *charptr;
1447 : :
1448 [ # # # ]: 0 : switch (c1)
1449 : : {
1450 : 0 : case SS2: /* JIS X 0201 */
1451 [ # # ]: 0 : if (length != 2)
1452 : 0 : return false;
1453 : :
1454 : 0 : c2 = charptr[1];
1455 : :
1456 [ # # ]: 0 : if (c2 >= 0xdf)
1457 : 0 : charptr[0] = charptr[1] = 0xa1;
1458 [ # # ]: 0 : else if (c2 < 0xa1)
1459 : 0 : charptr[1] = 0xa1;
1460 : : else
1461 : 0 : charptr[1]++;
1462 : 0 : break;
1463 : :
1464 : 0 : case SS3: /* JIS X 0212 */
1465 [ # # ]: 0 : if (length != 3)
1466 : 0 : return false;
1467 : :
1468 [ # # ]: 0 : for (i = 2; i > 0; i--)
1469 : : {
1470 : 0 : c2 = charptr[i];
1471 [ # # ]: 0 : if (c2 < 0xa1)
1472 : : {
1473 : 0 : charptr[i] = 0xa1;
1474 : 0 : return true;
1475 : : }
1476 [ # # ]: 0 : else if (c2 < 0xfe)
1477 : : {
1478 : 0 : charptr[i]++;
1479 : 0 : return true;
1480 : : }
1481 : : }
1482 : :
1483 : : /* Out of 3-byte code region */
1484 : 0 : return false;
1485 : :
1486 : 0 : default:
1487 [ # # ]: 0 : if (IS_HIGHBIT_SET(c1)) /* JIS X 0208? */
1488 : : {
1489 [ # # ]: 0 : if (length != 2)
1490 : 0 : return false;
1491 : :
1492 [ # # ]: 0 : for (i = 1; i >= 0; i--)
1493 : : {
1494 : 0 : c2 = charptr[i];
1495 [ # # ]: 0 : if (c2 < 0xa1)
1496 : : {
1497 : 0 : charptr[i] = 0xa1;
1498 : 0 : return true;
1499 : : }
1500 [ # # ]: 0 : else if (c2 < 0xfe)
1501 : : {
1502 : 0 : charptr[i]++;
1503 : 0 : return true;
1504 : : }
1505 : : }
1506 : :
1507 : : /* Out of 2 byte code region */
1508 : 0 : return false;
1509 : : }
1510 : : else
1511 : : { /* ASCII, single byte */
1512 [ # # ]: 0 : if (c1 > 0x7e)
1513 : 0 : return false;
1514 : 0 : (*charptr)++;
1515 : : }
1516 : 0 : break;
1517 : : }
1518 : :
1519 : 0 : return true;
1520 : : }
1521 : :
1522 : : /*
1523 : : * get the character incrementer for the encoding for the current database
1524 : : */
1525 : : mbcharacter_incrementer
2162 tgl@sss.pgh.pa.us 1526 :CBC 1815 : pg_database_encoding_character_incrementer(void)
1527 : : {
1528 : : /*
1529 : : * Eventually it might be best to add a field to pg_wchar_table[], but for
1530 : : * now we just use a switch.
1531 : : */
1532 [ + - + ]: 1815 : switch (GetDatabaseEncoding())
1533 : : {
1534 : 1763 : case PG_UTF8:
1535 : 1763 : return pg_utf8_increment;
1536 : :
2162 tgl@sss.pgh.pa.us 1537 :UBC 0 : case PG_EUC_JP:
1538 : 0 : return pg_eucjp_increment;
1539 : :
2162 tgl@sss.pgh.pa.us 1540 :CBC 52 : default:
1541 : 52 : return pg_generic_charinc;
1542 : : }
1543 : : }
1544 : :
1545 : : /*
1546 : : * fetch maximum length of the encoding for the current database
1547 : : */
1548 : : int
1549 : 3211370 : pg_database_encoding_max_length(void)
1550 : : {
1551 : 3211370 : return pg_wchar_table[GetDatabaseEncoding()].maxmblen;
1552 : : }
1553 : :
1554 : : /*
1555 : : * Verify mbstr to make sure that it is validly encoded in the current
1556 : : * database encoding. Otherwise same as pg_verify_mbstr().
1557 : : */
1558 : : bool
1559 : 103594 : pg_verifymbstr(const char *mbstr, int len, bool noError)
1560 : : {
1784 heikki.linnakangas@i 1561 : 103594 : return pg_verify_mbstr(GetDatabaseEncoding(), mbstr, len, noError);
1562 : : }
1563 : :
1564 : : /*
1565 : : * Verify mbstr to make sure that it is validly encoded in the specified
1566 : : * encoding.
1567 : : */
1568 : : bool
2162 tgl@sss.pgh.pa.us 1569 : 834739 : pg_verify_mbstr(int encoding, const char *mbstr, int len, bool noError)
1570 : : {
1571 : : int oklen;
1572 : :
1784 heikki.linnakangas@i 1573 [ + - - + ]: 834739 : Assert(PG_VALID_ENCODING(encoding));
1574 : :
1575 : 834739 : oklen = pg_wchar_table[encoding].mbverifystr((const unsigned char *) mbstr, len);
1576 [ + + ]: 834739 : if (oklen != len)
1577 : : {
1578 [ - + ]: 4 : if (noError)
1784 heikki.linnakangas@i 1579 :UBC 0 : return false;
1784 heikki.linnakangas@i 1580 :CBC 4 : report_invalid_encoding(encoding, mbstr + oklen, len - oklen);
1581 : : }
1582 : 834735 : return true;
1583 : : }
1584 : :
1585 : : /*
1586 : : * Verify mbstr to make sure that it is validly encoded in the specified
1587 : : * encoding.
1588 : : *
1589 : : * mbstr is not necessarily zero terminated; length of mbstr is
1590 : : * specified by len.
1591 : : *
1592 : : * If OK, return length of string in the encoding.
1593 : : * If a problem is found, return -1 when noError is
1594 : : * true; when noError is false, ereport() a descriptive message.
1595 : : *
1596 : : * Note: We cannot use the faster encoding-specific mbverifystr() function
1597 : : * here, because we need to count the number of characters in the string.
1598 : : */
1599 : : int
2162 tgl@sss.pgh.pa.us 1600 :UBC 0 : pg_verify_mbstr_len(int encoding, const char *mbstr, int len, bool noError)
1601 : : {
1602 : : mbchar_verifier mbverifychar;
1603 : : int mb_len;
1604 : :
1605 [ # # # # ]: 0 : Assert(PG_VALID_ENCODING(encoding));
1606 : :
1607 : : /*
1608 : : * In single-byte encodings, we need only reject nulls (\0).
1609 : : */
1610 [ # # ]: 0 : if (pg_encoding_max_length(encoding) <= 1)
1611 : : {
1612 : 0 : const char *nullpos = memchr(mbstr, 0, len);
1613 : :
1614 [ # # ]: 0 : if (nullpos == NULL)
1615 : 0 : return len;
1616 [ # # ]: 0 : if (noError)
1617 : 0 : return -1;
1618 : 0 : report_invalid_encoding(encoding, nullpos, 1);
1619 : : }
1620 : :
1621 : : /* fetch function pointer just once */
1784 heikki.linnakangas@i 1622 : 0 : mbverifychar = pg_wchar_table[encoding].mbverifychar;
1623 : :
2162 tgl@sss.pgh.pa.us 1624 : 0 : mb_len = 0;
1625 : :
1626 [ # # ]: 0 : while (len > 0)
1627 : : {
1628 : : int l;
1629 : :
1630 : : /* fast path for ASCII-subset characters */
1631 [ # # ]: 0 : if (!IS_HIGHBIT_SET(*mbstr))
1632 : : {
1633 [ # # ]: 0 : if (*mbstr != '\0')
1634 : : {
1635 : 0 : mb_len++;
1636 : 0 : mbstr++;
1637 : 0 : len--;
1638 : 0 : continue;
1639 : : }
1640 [ # # ]: 0 : if (noError)
1641 : 0 : return -1;
1642 : 0 : report_invalid_encoding(encoding, mbstr, len);
1643 : : }
1644 : :
1784 heikki.linnakangas@i 1645 : 0 : l = (*mbverifychar) ((const unsigned char *) mbstr, len);
1646 : :
2162 tgl@sss.pgh.pa.us 1647 [ # # ]: 0 : if (l < 0)
1648 : : {
1649 [ # # ]: 0 : if (noError)
1650 : 0 : return -1;
1651 : 0 : report_invalid_encoding(encoding, mbstr, len);
1652 : : }
1653 : :
1654 : 0 : mbstr += l;
1655 : 0 : len -= l;
1656 : 0 : mb_len++;
1657 : : }
1658 : 0 : return mb_len;
1659 : : }
1660 : :
1661 : : /*
1662 : : * check_encoding_conversion_args: check arguments of a conversion function
1663 : : *
1664 : : * "expected" arguments can be either an encoding ID or -1 to indicate that
1665 : : * the caller will check whether it accepts the ID.
1666 : : *
1667 : : * Note: the errors here are not really user-facing, so elog instead of
1668 : : * ereport seems sufficient. Also, we trust that the "expected" encoding
1669 : : * arguments are valid encoding IDs, but we don't trust the actuals.
1670 : : */
1671 : : void
2162 tgl@sss.pgh.pa.us 1672 :CBC 3544 : check_encoding_conversion_args(int src_encoding,
1673 : : int dest_encoding,
1674 : : int len,
1675 : : int expected_src_encoding,
1676 : : int expected_dest_encoding)
1677 : : {
1678 [ + - - + ]: 3544 : if (!PG_VALID_ENCODING(src_encoding))
2162 tgl@sss.pgh.pa.us 1679 [ # # ]:UBC 0 : elog(ERROR, "invalid source encoding ID: %d", src_encoding);
2162 tgl@sss.pgh.pa.us 1680 [ + + - + ]:CBC 3544 : if (src_encoding != expected_src_encoding && expected_src_encoding >= 0)
2162 tgl@sss.pgh.pa.us 1681 [ # # ]:UBC 0 : elog(ERROR, "expected source encoding \"%s\", but got \"%s\"",
1682 : : pg_enc2name_tbl[expected_src_encoding].name,
1683 : : pg_enc2name_tbl[src_encoding].name);
2162 tgl@sss.pgh.pa.us 1684 [ + - - + ]:CBC 3544 : if (!PG_VALID_ENCODING(dest_encoding))
2162 tgl@sss.pgh.pa.us 1685 [ # # ]:UBC 0 : elog(ERROR, "invalid destination encoding ID: %d", dest_encoding);
2162 tgl@sss.pgh.pa.us 1686 [ + + - + ]:CBC 3544 : if (dest_encoding != expected_dest_encoding && expected_dest_encoding >= 0)
2162 tgl@sss.pgh.pa.us 1687 [ # # ]:UBC 0 : elog(ERROR, "expected destination encoding \"%s\", but got \"%s\"",
1688 : : pg_enc2name_tbl[expected_dest_encoding].name,
1689 : : pg_enc2name_tbl[dest_encoding].name);
2162 tgl@sss.pgh.pa.us 1690 [ - + ]:CBC 3544 : if (len < 0)
2162 tgl@sss.pgh.pa.us 1691 [ # # ]:UBC 0 : elog(ERROR, "encoding conversion length must not be negative");
2162 tgl@sss.pgh.pa.us 1692 :CBC 3544 : }
1693 : :
1694 : : /*
1695 : : * report_invalid_encoding: complain about invalid multibyte character
1696 : : *
1697 : : * note: len is remaining length of string, not length of character;
1698 : : * len must be greater than zero (or we'd neglect initializing "buf").
1699 : : */
1700 : : void
1701 : 1495 : report_invalid_encoding(int encoding, const char *mbstr, int len)
1702 : : {
226 noah@leadboat.com 1703 : 1495 : int l = pg_encoding_mblen_or_incomplete(encoding, mbstr, len);
1704 : : char buf[8 * 5 + 1];
2162 tgl@sss.pgh.pa.us 1705 : 1495 : char *p = buf;
1706 : : int j,
1707 : : jlimit;
1708 : :
1709 : 1495 : jlimit = Min(l, len);
1710 : 1495 : jlimit = Min(jlimit, 8); /* prevent buffer overrun */
1711 : :
1712 [ + + ]: 4613 : for (j = 0; j < jlimit; j++)
1713 : : {
1714 : 3118 : p += sprintf(p, "0x%02x", (unsigned char) mbstr[j]);
1715 [ + + ]: 3118 : if (j < jlimit - 1)
1716 : 1623 : p += sprintf(p, " ");
1717 : : }
1718 : :
1719 [ + - ]: 1495 : ereport(ERROR,
1720 : : (errcode(ERRCODE_CHARACTER_NOT_IN_REPERTOIRE),
1721 : : errmsg("invalid byte sequence for encoding \"%s\": %s",
1722 : : pg_enc2name_tbl[encoding].name,
1723 : : buf)));
1724 : : }
1725 : :
1726 : : /*
1727 : : * report_untranslatable_char: complain about untranslatable character
1728 : : *
1729 : : * note: len is remaining length of string, not length of character;
1730 : : * len must be greater than zero (or we'd neglect initializing "buf").
1731 : : */
1732 : : void
1733 : 468 : report_untranslatable_char(int src_encoding, int dest_encoding,
1734 : : const char *mbstr, int len)
1735 : : {
1736 : : int l;
1737 : : char buf[8 * 5 + 1];
1738 : 468 : char *p = buf;
1739 : : int j,
1740 : : jlimit;
1741 : :
1742 : : /*
1743 : : * We probably could use plain pg_encoding_mblen(), because
1744 : : * gb18030_to_utf8() verifies before it converts. All conversions should.
1745 : : * For src_encoding!=GB18030, len>0 meets pg_encoding_mblen() needs. Even
1746 : : * so, be defensive, since a buggy conversion might pass invalid data.
1747 : : * This is not a performance-critical path.
1748 : : */
226 noah@leadboat.com 1749 : 468 : l = pg_encoding_mblen_or_incomplete(src_encoding, mbstr, len);
2162 tgl@sss.pgh.pa.us 1750 : 468 : jlimit = Min(l, len);
1751 : 468 : jlimit = Min(jlimit, 8); /* prevent buffer overrun */
1752 : :
1753 [ + + ]: 1764 : for (j = 0; j < jlimit; j++)
1754 : : {
1755 : 1296 : p += sprintf(p, "0x%02x", (unsigned char) mbstr[j]);
1756 [ + + ]: 1296 : if (j < jlimit - 1)
1757 : 828 : p += sprintf(p, " ");
1758 : : }
1759 : :
1760 [ + - ]: 468 : ereport(ERROR,
1761 : : (errcode(ERRCODE_UNTRANSLATABLE_CHARACTER),
1762 : : errmsg("character with byte sequence %s in encoding \"%s\" has no equivalent in encoding \"%s\"",
1763 : : buf,
1764 : : pg_enc2name_tbl[src_encoding].name,
1765 : : pg_enc2name_tbl[dest_encoding].name)));
1766 : : }
1767 : :
1768 : :
1769 : : #ifdef WIN32
1770 : : /*
1771 : : * Convert from MessageEncoding to a palloc'ed, null-terminated utf16
1772 : : * string. The character length is also passed to utf16len if not
1773 : : * null. Returns NULL iff failed. Before MessageEncoding initialization, "str"
1774 : : * should be ASCII-only; this will function as though MessageEncoding is UTF8.
1775 : : */
1776 : : WCHAR *
1777 : : pgwin32_message_to_UTF16(const char *str, int len, int *utf16len)
1778 : : {
1779 : : int msgenc = GetMessageEncoding();
1780 : : WCHAR *utf16;
1781 : : int dstlen;
1782 : : UINT codepage;
1783 : :
1784 : : if (msgenc == PG_SQL_ASCII)
1785 : : /* No conversion is possible, and SQL_ASCII is never utf16. */
1786 : : return NULL;
1787 : :
1788 : : codepage = pg_enc2name_tbl[msgenc].codepage;
1789 : :
1790 : : /*
1791 : : * Use MultiByteToWideChar directly if there is a corresponding codepage,
1792 : : * or double conversion through UTF8 if not. Double conversion is needed,
1793 : : * for example, in an ENCODING=LATIN8, LC_CTYPE=C database.
1794 : : */
1795 : : if (codepage != 0)
1796 : : {
1797 : : utf16 = palloc_array(WCHAR, len + 1);
1798 : : dstlen = MultiByteToWideChar(codepage, 0, str, len, utf16, len);
1799 : : utf16[dstlen] = (WCHAR) 0;
1800 : : }
1801 : : else
1802 : : {
1803 : : char *utf8;
1804 : :
1805 : : /*
1806 : : * XXX pg_do_encoding_conversion() requires a transaction. In the
1807 : : * absence of one, hope for the input to be valid UTF8.
1808 : : */
1809 : : if (IsTransactionState())
1810 : : {
1811 : : utf8 = (char *) pg_do_encoding_conversion((unsigned char *) str,
1812 : : len,
1813 : : msgenc,
1814 : : PG_UTF8);
1815 : : if (utf8 != str)
1816 : : len = strlen(utf8);
1817 : : }
1818 : : else
1819 : : utf8 = (char *) str;
1820 : :
1821 : : utf16 = palloc_array(WCHAR, len + 1);
1822 : : dstlen = MultiByteToWideChar(CP_UTF8, 0, utf8, len, utf16, len);
1823 : : utf16[dstlen] = (WCHAR) 0;
1824 : :
1825 : : if (utf8 != str)
1826 : : pfree(utf8);
1827 : : }
1828 : :
1829 : : if (dstlen == 0 && len > 0)
1830 : : {
1831 : : pfree(utf16);
1832 : : return NULL; /* error */
1833 : : }
1834 : :
1835 : : if (utf16len)
1836 : : *utf16len = dstlen;
1837 : : return utf16;
1838 : : }
1839 : :
1840 : : #endif /* WIN32 */
|