/* * regrolein - converts "rolename" to role OID * * We also accept a numeric OID, for symmetry with the output routine. * * '-' signifies unknown (OID 0). In all other cases, the input must * match an existing pg_authid entry. * * This function is not needed in bootstrap mode, so we don't worry about * making it work then. */ Datum regrolein(PG_FUNCTION_ARGS) { char *role_name_or_oid = PG_GETARG_CSTRING(0); Oid result; /* '-' ? */ if (strcmp(role_name_or_oid, "-") == 0) PG_RETURN_OID(InvalidOid); /* Numeric OID? */ if (role_name_or_oid[0] >= '0' && role_name_or_oid[0] <= '9' && strspn(role_name_or_oid, "0123456789") == strlen(role_name_or_oid)) { result = DatumGetObjectId(DirectFunctionCall1(oidin, CStringGetDatum(role_name_or_oid))); PG_RETURN_OID(result); } /* Normal case: see if the name matches any pg_authid entry. */ result = get_role_oid(role_name_or_oid, false); PG_RETURN_OID(result); }
Datum acl_check_access_text_name(PG_FUNCTION_ARGS) { ArrayType *acl; text *mask; Name rolename; bool implicit_allow; Oid who; if (!check_access_text_mask_extract_args(fcinfo, &acl, &mask, NULL, &implicit_allow, false, true)) PG_RETURN_NULL(); if (PG_ARGISNULL(2)) PG_RETURN_NULL(); rolename = PG_GETARG_NAME(2); who = get_role_oid(NameStr(*rolename), false); PG_RETURN_TEXT_P(check_access_text_mask(acl, ACL_TYPE_LENGTH, ACL_TYPE_ALIGNMENT, extract_acl_entry_base, mask, (intptr_t) who, who_matches, implicit_allow)); }
/* * to_regrole - converts "rolename" to role OID * * If the name is not found, we return NULL. */ Datum to_regrole(PG_FUNCTION_ARGS) { char *role_name = PG_GETARG_CSTRING(0); Oid result; result = get_role_oid(role_name, true); if (OidIsValid(result)) PG_RETURN_OID(result); else PG_RETURN_NULL(); }
CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString) #endif { const char *schemaName = stmt->schemaname; const char *authId = stmt->authid; Oid namespaceId; OverrideSearchPath *overridePath; List *parsetree_list; ListCell *parsetree_item; Oid owner_uid; Oid saved_uid; int save_sec_context; AclResult aclresult; GetUserIdAndSecContext(&saved_uid, &save_sec_context); /* * Who is supposed to own the new schema? */ if (authId) owner_uid = get_role_oid(authId, false); else owner_uid = saved_uid; /* * To create a schema, must have schema-create privilege on the current * database and must be able to become the target role (this does not * imply that the target role itself must have create-schema privilege). * The latter provision guards against "giveaway" attacks. Note that a * superuser will always have both of these privileges a fortiori. */ aclresult = pg_database_aclcheck(MyDatabaseId, saved_uid, ACL_CREATE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_DATABASE, get_database_name(MyDatabaseId)); check_is_member_of_role(saved_uid, owner_uid); /* Additional check to protect reserved schema names */ if (!allowSystemTableMods && IsReservedName(schemaName)) ereport(ERROR, (errcode(ERRCODE_RESERVED_NAME), errmsg("unacceptable schema name \"%s\"", schemaName), errdetail("The prefix \"pg_\" is reserved for system schemas."))); /* * If the requested authorization is different from the current user, * temporarily set the current user so that the object(s) will be created * with the correct ownership. * * (The setting will be restored at the end of this routine, or in case of * error, transaction abort will clean things up.) */ if (saved_uid != owner_uid) SetUserIdAndSecContext(owner_uid, save_sec_context | SECURITY_LOCAL_USERID_CHANGE); /* Create the schema's namespace */ namespaceId = NamespaceCreate(schemaName, owner_uid, false); /* Advance cmd counter to make the namespace visible */ CommandCounterIncrement(); /* * Temporarily make the new namespace be the front of the search path, as * well as the default creation target namespace. This will be undone at * the end of this routine, or upon error. */ overridePath = GetOverrideSearchPath(CurrentMemoryContext); overridePath->schemas = lcons_oid(namespaceId, overridePath->schemas); /* XXX should we clear overridePath->useTemp? */ PushOverrideSearchPath(overridePath); /* * Examine the list of commands embedded in the CREATE SCHEMA command, and * reorganize them into a sequentially executable order with no forward * references. Note that the result is still a list of raw parsetrees --- * we cannot, in general, run parse analysis on one statement until we * have actually executed the prior ones. */ parsetree_list = transformCreateSchemaStmt(stmt); #ifdef PGXC /* * Add a RemoteQuery node for a query at top level on a remote Coordinator, * if not done already. */ if (!sentToRemote) parsetree_list = AddRemoteQueryNode(parsetree_list, queryString, EXEC_ON_ALL_NODES, false); #endif /* * Execute each command contained in the CREATE SCHEMA. Since the grammar * allows only utility commands in CREATE SCHEMA, there is no need to pass * them through parse_analyze() or the rewriter; we can just hand them * straight to ProcessUtility. */ foreach(parsetree_item, parsetree_list) { Node *stmt = (Node *) lfirst(parsetree_item); /* do this step */ ProcessUtility(stmt, queryString, NULL, false, /* not top level */ None_Receiver, #ifdef PGXC true, #endif /* PGXC */ NULL); /* make sure later steps can see the object created here */ CommandCounterIncrement(); }
/* * Find an struct objaddr for a type of object that is identified by an * unqualified name. */ static struct objaddr get_objaddr_unqualified(objtype_e objtype, struct list *qualname) { const char *name; struct objaddr address; /* * The types of names handled by this function are not permitted to be * schema-qualified or catalog-qualified. */ if (list_length(qualname) != 1) { const char *msg; switch (objtype) { case OBJECT_DATABASE: msg = gettext_noop("database name cannot be qualified"); break; case OBJECT_EXTENSION: msg = gettext_noop("extension name cannot be qualified"); break; case OBJECT_TABLESPACE: msg = gettext_noop("tablespace name cannot be qualified"); break; case OBJECT_ROLE: msg = gettext_noop("role name cannot be qualified"); break; case OBJECT_SCHEMA: msg = gettext_noop("schema name cannot be qualified"); break; case OBJECT_LANGUAGE: msg = gettext_noop("language name cannot be qualified"); break; case OBJECT_FDW: msg = gettext_noop("foreign-data wrapper name cannot be qualified"); break; case OBJECT_FOREIGN_SERVER: msg = gettext_noop("server name cannot be qualified"); break; default: elog(ERROR, "unrecognized objtype: %d", (int) objtype); msg = NULL; /* placate compiler */ } ereport(ERROR, ( errcode(E_SYNTAX_ERROR), errmsg("%s", _(msg)))); } /* Format is valid, extract the actual name. */ name = str_value(linitial(qualname)); /* Translate name to OID. */ switch (objtype) { case OBJECT_DATABASE: address.classId = DatabaseRelationId; address.objectId = get_db_oid(name, false); address.objectSubId = 0; break; case OBJECT_EXTENSION: address.classId = ExtensionRelationId; address.objectId = get_extension_oid(name, false); address.objectSubId = 0; break; case OBJECT_TABLESPACE: address.classId = TableSpaceRelationId; address.objectId = tbs_get_oid(name, false); address.objectSubId = 0; break; case OBJECT_ROLE: address.classId = AuthIdRelationId; address.objectId = get_role_oid(name, false); address.objectSubId = 0; break; case OBJECT_SCHEMA: address.classId = NAMESPACE_RELATION_ID; address.objectId = get_ns_oid(name, false); address.objectSubId = 0; break; case OBJECT_LANGUAGE: address.classId = LanguageRelationId; address.objectId = get_lang_oid(name, false); address.objectSubId = 0; break; case OBJECT_FDW: address.classId = ForeignDataWrapperRelationId; address.objectId = get_fdw_oid(name, false); address.objectSubId = 0; break; case OBJECT_FOREIGN_SERVER: address.classId = ForeignServerRelationId; address.objectId = get_foreign_server_oid(name, false); address.objectSubId = 0; break; default: elog(ERROR, "unrecognized objtype: %d", (int) objtype); /* placate compiler, which doesn't know elog won't return */ address.classId = INVALID_OID; address.objectId = INVALID_OID; address.objectSubId = 0; } return address; }
/* * Create a table space * * Only superusers can create a tablespace. This seems a reasonable restriction * since we're determining the system layout and, anyway, we probably have * root if we're doing this kind of activity */ void CreateTableSpace(CreateTableSpaceStmt *stmt) { #ifdef HAVE_SYMLINK Relation rel; Datum values[Natts_pg_tablespace]; bool nulls[Natts_pg_tablespace]; HeapTuple tuple; Oid tablespaceoid; char *location; Oid ownerId; /* Must be super user */ if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied to create tablespace \"%s\"", stmt->tablespacename), errhint("Must be superuser to create a tablespace."))); /* However, the eventual owner of the tablespace need not be */ if (stmt->owner) ownerId = get_role_oid(stmt->owner, false); else ownerId = GetUserId(); /* Unix-ify the offered path, and strip any trailing slashes */ location = pstrdup(stmt->location); canonicalize_path(location); /* disallow quotes, else CREATE DATABASE would be at risk */ if (strchr(location, '\'')) ereport(ERROR, (errcode(ERRCODE_INVALID_NAME), errmsg("tablespace location cannot contain single quotes"))); /* * Allowing relative paths seems risky * * this also helps us ensure that location is not empty or whitespace */ if (!is_absolute_path(location)) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("tablespace location must be an absolute path"))); /* * Check that location isn't too long. Remember that we're going to append * 'PG_XXX/<dboid>/<relid>.<nnn>'. FYI, we never actually reference the * whole path, but mkdir() uses the first two parts. */ if (strlen(location) + 1 + strlen(TABLESPACE_VERSION_DIRECTORY) + 1 + OIDCHARS + 1 + OIDCHARS + 1 + OIDCHARS > MAXPGPATH) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("tablespace location \"%s\" is too long", location))); /* * Disallow creation of tablespaces named "pg_xxx"; we reserve this * namespace for system purposes. */ if (!allowSystemTableMods && IsReservedName(stmt->tablespacename)) ereport(ERROR, (errcode(ERRCODE_RESERVED_NAME), errmsg("unacceptable tablespace name \"%s\"", stmt->tablespacename), errdetail("The prefix \"pg_\" is reserved for system tablespaces."))); /* * Check that there is no other tablespace by this name. (The unique * index would catch this anyway, but might as well give a friendlier * message.) */ if (OidIsValid(get_tablespace_oid(stmt->tablespacename, true))) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("tablespace \"%s\" already exists", stmt->tablespacename))); /* * Insert tuple into pg_tablespace. The purpose of doing this first is to * lock the proposed tablename against other would-be creators. The * insertion will roll back if we find problems below. */ rel = heap_open(TableSpaceRelationId, RowExclusiveLock); MemSet(nulls, false, sizeof(nulls)); values[Anum_pg_tablespace_spcname - 1] = DirectFunctionCall1(namein, CStringGetDatum(stmt->tablespacename)); values[Anum_pg_tablespace_spcowner - 1] = ObjectIdGetDatum(ownerId); values[Anum_pg_tablespace_spclocation - 1] = CStringGetTextDatum(location); nulls[Anum_pg_tablespace_spcacl - 1] = true; nulls[Anum_pg_tablespace_spcoptions - 1] = true; tuple = heap_form_tuple(rel->rd_att, values, nulls); tablespaceoid = simple_heap_insert(rel, tuple); CatalogUpdateIndexes(rel, tuple); heap_freetuple(tuple); /* Record dependency on owner */ recordDependencyOnOwner(TableSpaceRelationId, tablespaceoid, ownerId); /* Post creation hook for new tablespace */ InvokeObjectAccessHook(OAT_POST_CREATE, TableSpaceRelationId, tablespaceoid, 0); create_tablespace_directories(location, tablespaceoid); /* Record the filesystem change in XLOG */ { xl_tblspc_create_rec xlrec; XLogRecData rdata[2]; xlrec.ts_id = tablespaceoid; rdata[0].data = (char *) &xlrec; rdata[0].len = offsetof(xl_tblspc_create_rec, ts_path); rdata[0].buffer = InvalidBuffer; rdata[0].next = &(rdata[1]); rdata[1].data = (char *) location; rdata[1].len = strlen(location) + 1; rdata[1].buffer = InvalidBuffer; rdata[1].next = NULL; (void) XLogInsert(RM_TBLSPC_ID, XLOG_TBLSPC_CREATE, rdata); } /* * Force synchronous commit, to minimize the window between creating the * symlink on-disk and marking the transaction committed. It's not great * that there is any window at all, but definitely we don't want to make * it larger than necessary. */ ForceSyncCommit(); pfree(location); /* We keep the lock on pg_tablespace until commit */ heap_close(rel, NoLock); #else /* !HAVE_SYMLINK */ ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("tablespaces are not supported on this platform"))); #endif /* HAVE_SYMLINK */ }
/* * Find an ObjectAddress for a type of object that is identified by an * unqualified name. */ static ObjectAddress get_object_address_unqualified(ObjectType objtype, List *qualname) { const char *name; ObjectAddress address; /* * The types of names handled by this function are not permitted to be * schema-qualified or catalog-qualified. */ if (list_length(qualname) != 1) { const char *msg; switch (objtype) { case OBJECT_DATABASE: msg = gettext_noop("database name cannot be qualified"); break; case OBJECT_EXTENSION: msg = gettext_noop("extension name cannot be qualified"); break; case OBJECT_TABLESPACE: msg = gettext_noop("tablespace name cannot be qualified"); break; case OBJECT_ROLE: msg = gettext_noop("role name cannot be qualified"); break; case OBJECT_SCHEMA: msg = gettext_noop("schema name cannot be qualified"); break; case OBJECT_LANGUAGE: msg = gettext_noop("language name cannot be qualified"); break; default: elog(ERROR, "unrecognized objtype: %d", (int) objtype); msg = NULL; /* placate compiler */ } ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("%s", _(msg)))); } /* Format is valid, extract the actual name. */ name = strVal(linitial(qualname)); /* Translate name to OID. */ switch (objtype) { case OBJECT_DATABASE: address.classId = DatabaseRelationId; address.objectId = get_database_oid(name, false); address.objectSubId = 0; break; case OBJECT_EXTENSION: address.classId = ExtensionRelationId; address.objectId = get_extension_oid(name, false); address.objectSubId = 0; break; case OBJECT_TABLESPACE: address.classId = TableSpaceRelationId; address.objectId = get_tablespace_oid(name, false); address.objectSubId = 0; break; case OBJECT_ROLE: address.classId = AuthIdRelationId; address.objectId = get_role_oid(name, false); address.objectSubId = 0; break; case OBJECT_SCHEMA: address.classId = NamespaceRelationId; address.objectId = get_namespace_oid(name, false); address.objectSubId = 0; break; case OBJECT_LANGUAGE: address.classId = LanguageRelationId; address.objectId = get_language_oid(name, false); address.objectSubId = 0; break; default: elog(ERROR, "unrecognized objtype: %d", (int) objtype); /* placate compiler, which doesn't know elog won't return */ address.classId = InvalidOid; address.objectId = InvalidOid; address.objectSubId = 0; } return address; }
/* * FetchRegularTable fetches the given table's data using the copy out command. * The function then fetches the DDL commands necessary to create this table's * replica, and locally applies these DDL commands. Last, the function copies * the fetched table data into the created table; and on success, returns true. * On failure due to connectivity issues with remote node, the function returns * false. On other types of failures, the function errors out. */ static bool FetchRegularTable(const char *nodeName, uint32 nodePort, const char *tableName) { StringInfo localFilePath = NULL; StringInfo remoteCopyCommand = NULL; List *ddlCommandList = NIL; ListCell *ddlCommandCell = NULL; CopyStmt *localCopyCommand = NULL; RangeVar *localTable = NULL; uint64 shardId = 0; bool received = false; StringInfo queryString = NULL; const char *tableOwner = NULL; Oid tableOwnerId = InvalidOid; Oid savedUserId = InvalidOid; int savedSecurityContext = 0; List *tableNameList = NIL; /* copy remote table's data to this node in an idempotent manner */ shardId = ExtractShardId(tableName); localFilePath = makeStringInfo(); appendStringInfo(localFilePath, "base/%s/%s" UINT64_FORMAT, PG_JOB_CACHE_DIR, TABLE_FILE_PREFIX, shardId); remoteCopyCommand = makeStringInfo(); appendStringInfo(remoteCopyCommand, COPY_OUT_COMMAND, tableName); received = ReceiveRegularFile(nodeName, nodePort, remoteCopyCommand, localFilePath); if (!received) { return false; } /* fetch the ddl commands needed to create the table */ tableOwner = RemoteTableOwner(nodeName, nodePort, tableName); if (tableOwner == NULL) { return false; } tableOwnerId = get_role_oid(tableOwner, false); /* fetch the ddl commands needed to create the table */ ddlCommandList = TableDDLCommandList(nodeName, nodePort, tableName); if (ddlCommandList == NIL) { return false; } /* * Apply DDL commands against the database. Note that on failure from here * on, we immediately error out instead of returning false. Have to do * this as the table's owner to ensure the local table is created with * compatible permissions. */ GetUserIdAndSecContext(&savedUserId, &savedSecurityContext); SetUserIdAndSecContext(tableOwnerId, SECURITY_LOCAL_USERID_CHANGE); foreach(ddlCommandCell, ddlCommandList) { StringInfo ddlCommand = (StringInfo) lfirst(ddlCommandCell); Node *ddlCommandNode = ParseTreeNode(ddlCommand->data); ProcessUtility(ddlCommandNode, ddlCommand->data, PROCESS_UTILITY_TOPLEVEL, NULL, None_Receiver, NULL); CommandCounterIncrement(); }
/* * Executes an ALTER OBJECT / OWNER TO statement. Based on the object * type, the function appropriate to that type is executed. */ void ExecAlterOwnerStmt(AlterOwnerStmt *stmt) { Oid newowner = get_role_oid(stmt->newowner, false); switch (stmt->objectType) { case OBJECT_AGGREGATE: AlterAggregateOwner(stmt->object, stmt->objarg, newowner); break; case OBJECT_CONVERSION: AlterConversionOwner(stmt->object, newowner); break; case OBJECT_DATABASE: AlterDatabaseOwner(strVal(linitial(stmt->object)), newowner); break; case OBJECT_FUNCTION: AlterFunctionOwner(stmt->object, stmt->objarg, newowner); break; case OBJECT_LANGUAGE: AlterLanguageOwner(strVal(linitial(stmt->object)), newowner); break; case OBJECT_LARGEOBJECT: LargeObjectAlterOwner(oidparse(linitial(stmt->object)), newowner); break; case OBJECT_OPERATOR: Assert(list_length(stmt->objarg) == 2); AlterOperatorOwner(stmt->object, (TypeName *) linitial(stmt->objarg), (TypeName *) lsecond(stmt->objarg), newowner); break; case OBJECT_OPCLASS: AlterOpClassOwner(stmt->object, stmt->addname, newowner); break; case OBJECT_OPFAMILY: AlterOpFamilyOwner(stmt->object, stmt->addname, newowner); break; case OBJECT_SCHEMA: AlterSchemaOwner(strVal(linitial(stmt->object)), newowner); break; case OBJECT_TABLESPACE: AlterTableSpaceOwner(strVal(linitial(stmt->object)), newowner); break; case OBJECT_TYPE: case OBJECT_DOMAIN: /* same as TYPE */ AlterTypeOwner(stmt->object, newowner); break; case OBJECT_TSDICTIONARY: AlterTSDictionaryOwner(stmt->object, newowner); break; case OBJECT_TSCONFIGURATION: AlterTSConfigurationOwner(stmt->object, newowner); break; case OBJECT_FDW: AlterForeignDataWrapperOwner(strVal(linitial(stmt->object)), newowner); break; case OBJECT_FOREIGN_SERVER: AlterForeignServerOwner(strVal(linitial(stmt->object)), newowner); break; default: elog(ERROR, "unrecognized AlterOwnerStmt type: %d", (int) stmt->objectType); } }
/* * CREATE SCHEMA */ Oid CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString) { const char *schemaName = stmt->schemaname; const char *authId = stmt->authid; Oid namespaceId; OverrideSearchPath *overridePath; List *parsetree_list; ListCell *parsetree_item; Oid owner_uid; Oid saved_uid; int save_sec_context; AclResult aclresult; GetUserIdAndSecContext(&saved_uid, &save_sec_context); /* * Who is supposed to own the new schema? */ if (authId) owner_uid = get_role_oid(authId, false); else owner_uid = saved_uid; /* * To create a schema, must have schema-create privilege on the current * database and must be able to become the target role (this does not * imply that the target role itself must have create-schema privilege). * The latter provision guards against "giveaway" attacks. Note that a * superuser will always have both of these privileges a fortiori. */ aclresult = pg_database_aclcheck(MyDatabaseId, saved_uid, ACL_CREATE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_DATABASE, get_database_name(MyDatabaseId)); check_is_member_of_role(saved_uid, owner_uid); /* Additional check to protect reserved schema names */ if (!allowSystemTableMods && IsReservedName(schemaName)) ereport(ERROR, (errcode(ERRCODE_RESERVED_NAME), errmsg("unacceptable schema name \"%s\"", schemaName), errdetail("The prefix \"pg_\" is reserved for system schemas."))); /* * If if_not_exists was given and the schema already exists, bail out. * (Note: we needn't check this when not if_not_exists, because * NamespaceCreate will complain anyway.) We could do this before making * the permissions checks, but since CREATE TABLE IF NOT EXISTS makes its * creation-permission check first, we do likewise. */ if (stmt->if_not_exists && SearchSysCacheExists1(NAMESPACENAME, PointerGetDatum(schemaName))) { ereport(NOTICE, (errcode(ERRCODE_DUPLICATE_SCHEMA), errmsg("schema \"%s\" already exists, skipping", schemaName))); return InvalidOid; } /* * If the requested authorization is different from the current user, * temporarily set the current user so that the object(s) will be created * with the correct ownership. * * (The setting will be restored at the end of this routine, or in case of * error, transaction abort will clean things up.) */ if (saved_uid != owner_uid) SetUserIdAndSecContext(owner_uid, save_sec_context | SECURITY_LOCAL_USERID_CHANGE); /* Create the schema's namespace */ namespaceId = NamespaceCreate(schemaName, owner_uid, false); /* Advance cmd counter to make the namespace visible */ CommandCounterIncrement(); /* * Temporarily make the new namespace be the front of the search path, as * well as the default creation target namespace. This will be undone at * the end of this routine, or upon error. */ overridePath = GetOverrideSearchPath(CurrentMemoryContext); overridePath->schemas = lcons_oid(namespaceId, overridePath->schemas); /* XXX should we clear overridePath->useTemp? */ PushOverrideSearchPath(overridePath); /* * Examine the list of commands embedded in the CREATE SCHEMA command, and * reorganize them into a sequentially executable order with no forward * references. Note that the result is still a list of raw parsetrees --- * we cannot, in general, run parse analysis on one statement until we * have actually executed the prior ones. */ parsetree_list = transformCreateSchemaStmt(stmt); /* * Execute each command contained in the CREATE SCHEMA. Since the grammar * allows only utility commands in CREATE SCHEMA, there is no need to pass * them through parse_analyze() or the rewriter; we can just hand them * straight to ProcessUtility. */ foreach(parsetree_item, parsetree_list) { Node *stmt = (Node *) lfirst(parsetree_item); /* do this step */ ProcessUtility(stmt, queryString, PROCESS_UTILITY_SUBCOMMAND, NULL, None_Receiver, NULL); /* make sure later steps can see the object created here */ CommandCounterIncrement(); }
static const char * parse_who(const char *s, void *opaque) { char name[NAMEDATALEN]; int len = 0; Oid oid; AclEntryOid *acl_entry = (AclEntryOid *) opaque; if (*s == '#') { for (++s; *s != '\0' && isalnum((unsigned char) *s); ++s) { name[len++] = *s; if (len >= 9) ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("invalid ACE who"))); } name[len] = '\0'; oid = DatumGetObjectId(DirectFunctionCall1(oidin, CStringGetDatum(name))); acl_entry->base.flags |= ACE_INVALID; } else { bool in_quotes = false; for (; *s != '\0' && (isalnum((unsigned char) *s) || *s == '_' || *s == '"' || in_quotes); ++s) { if (*s == '"') { if (*(s + 1) != '"') { in_quotes = !in_quotes; continue; } ++s; } if (len >= NAMEDATALEN - 1) ereport(ERROR, (errcode(ERRCODE_NAME_TOO_LONG), errmsg("identifier too long"), errdetail("Identifier must be less than %d characters.", NAMEDATALEN))); name[len++] = *s; } if (len == 0) { oid = PUBLIC_OID; } else { name[len] = '\0'; oid = get_role_oid(name, true); if (!OidIsValid(oid)) acl_entry->base.flags |= ACE_INVALID; } } acl_entry->who = oid; return s; }