| /*------------------------------------------------------------------------- |
| * |
| * name.c |
| * Functions for the built-in type "name". |
| * |
| * name replaces char16 and is carefully implemented so that it |
| * is a string of physical length NAMEDATALEN. |
| * DO NOT use hard-coded constants anywhere |
| * always use NAMEDATALEN as the symbolic constant! - jolly 8/21/95 |
| * |
| * |
| * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group |
| * Portions Copyright (c) 1994, Regents of the University of California |
| * |
| * |
| * IDENTIFICATION |
| * src/backend/utils/adt/name.c |
| * |
| *------------------------------------------------------------------------- |
| */ |
| #include "postgres.h" |
| |
| #include "catalog/namespace.h" |
| #include "catalog/pg_collation.h" |
| #include "catalog/pg_type.h" |
| #include "libpq/pqformat.h" |
| #include "mb/pg_wchar.h" |
| #include "miscadmin.h" |
| #include "utils/array.h" |
| #include "utils/builtins.h" |
| #include "utils/lsyscache.h" |
| #include "utils/varlena.h" |
| |
| |
| /***************************************************************************** |
| * USER I/O ROUTINES (none) * |
| *****************************************************************************/ |
| |
| |
| /* |
| * namein - converts "..." to internal representation |
| * |
| * Note: |
| * [Old] Currently if strlen(s) < NAMEDATALEN, the extra chars are nulls |
| * Now, always NULL terminated |
| */ |
| Datum |
| namein(PG_FUNCTION_ARGS) |
| { |
| char *s = PG_GETARG_CSTRING(0); |
| Name result; |
| int len; |
| |
| len = strlen(s); |
| |
| /* Truncate oversize input */ |
| if (len >= NAMEDATALEN) |
| len = pg_mbcliplen(s, len, NAMEDATALEN - 1); |
| |
| /* We use palloc0 here to ensure result is zero-padded */ |
| result = (Name) palloc0(NAMEDATALEN); |
| memcpy(NameStr(*result), s, len); |
| |
| PG_RETURN_NAME(result); |
| } |
| |
| /* |
| * nameout - converts internal representation to "..." |
| */ |
| Datum |
| nameout(PG_FUNCTION_ARGS) |
| { |
| Name s = PG_GETARG_NAME(0); |
| |
| PG_RETURN_CSTRING(pstrdup(NameStr(*s))); |
| } |
| |
| /* |
| * namerecv - converts external binary format to name |
| */ |
| Datum |
| namerecv(PG_FUNCTION_ARGS) |
| { |
| StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); |
| Name result; |
| char *str; |
| int nbytes; |
| |
| str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes); |
| if (nbytes >= NAMEDATALEN) |
| ereport(ERROR, |
| (errcode(ERRCODE_NAME_TOO_LONG), |
| errmsg("identifier too long"), |
| errdetail("Identifier must be less than %d characters.", |
| NAMEDATALEN))); |
| result = (NameData *) palloc0(NAMEDATALEN); |
| memcpy(result, str, nbytes); |
| pfree(str); |
| PG_RETURN_NAME(result); |
| } |
| |
| /* |
| * namesend - converts name to binary format |
| */ |
| Datum |
| namesend(PG_FUNCTION_ARGS) |
| { |
| Name s = PG_GETARG_NAME(0); |
| StringInfoData buf; |
| |
| pq_begintypsend(&buf); |
| pq_sendtext(&buf, NameStr(*s), strlen(NameStr(*s))); |
| PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); |
| } |
| |
| |
| /***************************************************************************** |
| * COMPARISON/SORTING ROUTINES * |
| *****************************************************************************/ |
| |
| /* |
| * nameeq - returns 1 iff arguments are equal |
| * namene - returns 1 iff arguments are not equal |
| * namelt - returns 1 iff a < b |
| * namele - returns 1 iff a <= b |
| * namegt - returns 1 iff a > b |
| * namege - returns 1 iff a >= b |
| * |
| * Note that the use of strncmp with NAMEDATALEN limit is mostly historical; |
| * strcmp would do as well, because we do not allow NAME values that don't |
| * have a '\0' terminator. Whatever might be past the terminator is not |
| * considered relevant to comparisons. |
| */ |
| static int |
| namecmp(Name arg1, Name arg2, Oid collid) |
| { |
| /* Fast path for common case used in system catalogs */ |
| if (collid == C_COLLATION_OID) |
| return strncmp(NameStr(*arg1), NameStr(*arg2), NAMEDATALEN); |
| |
| /* Else rely on the varstr infrastructure */ |
| return varstr_cmp(NameStr(*arg1), strlen(NameStr(*arg1)), |
| NameStr(*arg2), strlen(NameStr(*arg2)), |
| collid); |
| } |
| |
| Datum |
| nameeq(PG_FUNCTION_ARGS) |
| { |
| Name arg1 = PG_GETARG_NAME(0); |
| Name arg2 = PG_GETARG_NAME(1); |
| |
| PG_RETURN_BOOL(namecmp(arg1, arg2, PG_GET_COLLATION()) == 0); |
| } |
| |
| Datum |
| namene(PG_FUNCTION_ARGS) |
| { |
| Name arg1 = PG_GETARG_NAME(0); |
| Name arg2 = PG_GETARG_NAME(1); |
| |
| PG_RETURN_BOOL(namecmp(arg1, arg2, PG_GET_COLLATION()) != 0); |
| } |
| |
| Datum |
| namelt(PG_FUNCTION_ARGS) |
| { |
| Name arg1 = PG_GETARG_NAME(0); |
| Name arg2 = PG_GETARG_NAME(1); |
| |
| PG_RETURN_BOOL(namecmp(arg1, arg2, PG_GET_COLLATION()) < 0); |
| } |
| |
| Datum |
| namele(PG_FUNCTION_ARGS) |
| { |
| Name arg1 = PG_GETARG_NAME(0); |
| Name arg2 = PG_GETARG_NAME(1); |
| |
| PG_RETURN_BOOL(namecmp(arg1, arg2, PG_GET_COLLATION()) <= 0); |
| } |
| |
| Datum |
| namegt(PG_FUNCTION_ARGS) |
| { |
| Name arg1 = PG_GETARG_NAME(0); |
| Name arg2 = PG_GETARG_NAME(1); |
| |
| PG_RETURN_BOOL(namecmp(arg1, arg2, PG_GET_COLLATION()) > 0); |
| } |
| |
| Datum |
| namege(PG_FUNCTION_ARGS) |
| { |
| Name arg1 = PG_GETARG_NAME(0); |
| Name arg2 = PG_GETARG_NAME(1); |
| |
| PG_RETURN_BOOL(namecmp(arg1, arg2, PG_GET_COLLATION()) >= 0); |
| } |
| |
| Datum |
| btnamecmp(PG_FUNCTION_ARGS) |
| { |
| Name arg1 = PG_GETARG_NAME(0); |
| Name arg2 = PG_GETARG_NAME(1); |
| |
| PG_RETURN_INT32(namecmp(arg1, arg2, PG_GET_COLLATION())); |
| } |
| |
| Datum |
| btnamesortsupport(PG_FUNCTION_ARGS) |
| { |
| SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); |
| Oid collid = ssup->ssup_collation; |
| MemoryContext oldcontext; |
| |
| oldcontext = MemoryContextSwitchTo(ssup->ssup_cxt); |
| |
| /* Use generic string SortSupport */ |
| varstr_sortsupport(ssup, NAMEOID, collid); |
| |
| MemoryContextSwitchTo(oldcontext); |
| |
| PG_RETURN_VOID(); |
| } |
| |
| |
| /***************************************************************************** |
| * MISCELLANEOUS PUBLIC ROUTINES * |
| *****************************************************************************/ |
| |
| void |
| namestrcpy(Name name, const char *str) |
| { |
| /* NB: We need to zero-pad the destination. */ |
| strncpy(NameStr(*name), str, NAMEDATALEN); |
| NameStr(*name)[NAMEDATALEN - 1] = '\0'; |
| } |
| |
| /* |
| * Compare a NAME to a C string |
| * |
| * Assumes C collation always; be careful when using this for |
| * anything but equality checks! |
| */ |
| int |
| namestrcmp(Name name, const char *str) |
| { |
| if (!name && !str) |
| return 0; |
| if (!name) |
| return -1; /* NULL < anything */ |
| if (!str) |
| return 1; /* NULL < anything */ |
| return strncmp(NameStr(*name), str, NAMEDATALEN); |
| } |
| |
| |
| /* |
| * SQL-functions CURRENT_USER, SESSION_USER |
| */ |
| Datum |
| current_user(PG_FUNCTION_ARGS) |
| { |
| PG_RETURN_DATUM(DirectFunctionCall1(namein, CStringGetDatum(GetUserNameFromId(GetUserId(), false)))); |
| } |
| |
| Datum |
| session_user(PG_FUNCTION_ARGS) |
| { |
| PG_RETURN_DATUM(DirectFunctionCall1(namein, CStringGetDatum(GetUserNameFromId(GetSessionUserId(), false)))); |
| } |
| |
| |
| /* |
| * SQL-functions CURRENT_SCHEMA, CURRENT_SCHEMAS |
| */ |
| Datum |
| current_schema(PG_FUNCTION_ARGS) |
| { |
| List *search_path = fetch_search_path(false); |
| char *nspname; |
| |
| if (search_path == NIL) |
| PG_RETURN_NULL(); |
| nspname = get_namespace_name(linitial_oid(search_path)); |
| list_free(search_path); |
| if (!nspname) |
| PG_RETURN_NULL(); /* recently-deleted namespace? */ |
| PG_RETURN_DATUM(DirectFunctionCall1(namein, CStringGetDatum(nspname))); |
| } |
| |
| Datum |
| current_schemas(PG_FUNCTION_ARGS) |
| { |
| List *search_path = fetch_search_path(PG_GETARG_BOOL(0)); |
| ListCell *l; |
| Datum *names; |
| int i; |
| ArrayType *array; |
| |
| names = (Datum *) palloc(list_length(search_path) * sizeof(Datum)); |
| i = 0; |
| foreach(l, search_path) |
| { |
| char *nspname; |
| |
| nspname = get_namespace_name(lfirst_oid(l)); |
| if (nspname) /* watch out for deleted namespace */ |
| { |
| names[i] = DirectFunctionCall1(namein, CStringGetDatum(nspname)); |
| i++; |
| } |
| } |
| list_free(search_path); |
| |
| array = construct_array(names, i, |
| NAMEOID, |
| NAMEDATALEN, /* sizeof(Name) */ |
| false, /* Name is not by-val */ |
| TYPALIGN_CHAR); /* alignment of Name */ |
| |
| PG_RETURN_POINTER(array); |
| } |
| |
| /* |
| * SQL-function nameconcatoid(name, oid) returns name |
| * |
| * This is used in the information_schema to produce specific_name columns, |
| * which are supposed to be unique per schema. We achieve that (in an ugly |
| * way) by appending the object's OID. The result is the same as |
| * ($1::text || '_' || $2::text)::name |
| * except that, if it would not fit in NAMEDATALEN, we make it do so by |
| * truncating the name input (not the oid). |
| */ |
| Datum |
| nameconcatoid(PG_FUNCTION_ARGS) |
| { |
| Name nam = PG_GETARG_NAME(0); |
| Oid oid = PG_GETARG_OID(1); |
| Name result; |
| char suffix[20]; |
| int suflen; |
| int namlen; |
| |
| suflen = snprintf(suffix, sizeof(suffix), "_%u", oid); |
| namlen = strlen(NameStr(*nam)); |
| |
| /* Truncate oversize input by truncating name part, not suffix */ |
| if (namlen + suflen >= NAMEDATALEN) |
| namlen = pg_mbcliplen(NameStr(*nam), namlen, NAMEDATALEN - 1 - suflen); |
| |
| /* We use palloc0 here to ensure result is zero-padded */ |
| result = (Name) palloc0(NAMEDATALEN); |
| memcpy(NameStr(*result), NameStr(*nam), namlen); |
| memcpy(NameStr(*result) + namlen, suffix, suflen); |
| |
| PG_RETURN_NAME(result); |
| } |