diff --git a/contrib/sepgsql/expected/create.out b/contrib/sepgsql/expected/create.out index 3293a73..d450dc5 100644 --- a/contrib/sepgsql/expected/create.out +++ b/contrib/sepgsql/expected/create.out @@ -16,8 +16,36 @@ LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_ CREATE SCHEMA regtest_schema; LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema" SET search_path = regtest_schema, public; +CREATE TABLE regtest_table (x serial primary key, y text); +NOTICE: CREATE TABLE will create implicit sequence "regtest_table_x_seq" for serial column "regtest_table.x" +LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_seq_t:s0 tclass=db_sequence name="sequence regtest_table_x_seq" +LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="table regtest_table" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table column tableoid" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table column cmax" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table column xmax" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table column cmin" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table column xmin" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table column ctid" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table column x" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table column y" +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "regtest_table_pkey" for table "regtest_table" +CREATE VIEW regtest_view AS SELECT * FROM regtest_table WHERE x < 10; +LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_view_t:s0 tclass=db_view name="view regtest_view" +CREATE SEQUENCE regtest_seq; +LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_seq_t:s0 tclass=db_sequence name="sequence regtest_seq" +CREATE TYPE regtest_type AS (a int, b text); +LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema" -- -- clean-up -- DROP DATABASE IF EXISTS regtest_sepgsql_test_database; DROP SCHEMA IF EXISTS regtest_schema CASCADE; +NOTICE: drop cascades to 4 other objects +DETAIL: drop cascades to table regtest_table +drop cascades to view regtest_view +drop cascades to sequence regtest_seq +drop cascades to type regtest_type diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c index 2b62a75..aa56d9c 100644 --- a/contrib/sepgsql/hooks.c +++ b/contrib/sepgsql/hooks.c @@ -146,6 +146,15 @@ sepgsql_object_prep_create(Oid classId, Oid objectId, int subId, sepgsql_schema_prep_create(args->pg_namespace.nspname); break; + case RelationRelationId: + sepgsql_relation_prep_create(cinfo, + args->pg_class.relname, + args->pg_class.relkind, + args->pg_class.namespaceId, + args->pg_class.tablespaceId, + args->pg_class.tupdesc); + break; + default: /* Ignore unsupported object classes */ break; @@ -181,7 +190,7 @@ sepgsql_object_post_create(Oid classId, Oid objectId, int subId, case RelationRelationId: if (subId == 0) - sepgsql_relation_post_create(objectId); + sepgsql_relation_post_create(cinfo, objectId); else sepgsql_attribute_post_create(objectId, subId); break; diff --git a/contrib/sepgsql/relation.c b/contrib/sepgsql/relation.c index 0767382..09f80d7 100644 --- a/contrib/sepgsql/relation.c +++ b/contrib/sepgsql/relation.c @@ -13,6 +13,7 @@ #include "access/genam.h" #include "access/heapam.h" #include "access/sysattr.h" +#include "catalog/heap.h" #include "catalog/indexing.h" #include "catalog/dependency.h" #include "catalog/pg_attribute.h" @@ -113,102 +114,174 @@ sepgsql_attribute_relabel(Oid relOid, AttrNumber attnum, } /* - * sepgsql_relation_post_create + * sepgsql_relation_prep_create * - * The post creation hook of relation/attribute + * This routine computes default security label of relation and columns + * if relation is a table, then checks permissions to create them. */ void -sepgsql_relation_post_create(Oid relOid) +sepgsql_relation_prep_create(sepgsql_creation_info *info, + const char *relname, char relkind, + Oid namespaceId, Oid tablespaceId, + TupleDesc tupdesc) { - Relation rel; - ScanKeyData skey; - SysScanDesc sscan; - HeapTuple tuple; - Form_pg_class classForm; - ObjectAddress object; + char *scontext; + char *tcontext; + char *ncontext; + char *acontext; + char audit_name[2 * NAMEDATALEN + 16]; uint16 tclass; - char *scontext; /* subject */ - char *tcontext; /* schema */ - char *rcontext; /* relation */ - char *ccontext; /* column */ + ObjectAddress object; /* - * Fetch catalog record of the new relation. Because pg_class entry is not - * visible right now, we need to scan the catalog using SnapshotSelf. + * so check db_schema:{add_name} permission */ - rel = heap_open(RelationRelationId, AccessShareLock); - - ScanKeyInit(&skey, - ObjectIdAttributeNumber, - BTEqualStrategyNumber, F_OIDEQ, - ObjectIdGetDatum(relOid)); - - sscan = systable_beginscan(rel, ClassOidIndexId, true, - SnapshotSelf, 1, &skey); + object.classId = NamespaceRelationId; + object.objectId = namespaceId; + object.objectSubId = 0; + sepgsql_avc_check_perms(&object, + SEPG_CLASS_DB_SCHEMA, + SEPG_DB_SCHEMA__ADD_NAME, + getObjectDescription(&object), + true); + /* + * compute default security label of new relation + */ + switch (relkind) + { + case RELKIND_RELATION: + tclass = SEPG_CLASS_DB_TABLE; + snprintf(audit_name, sizeof(audit_name), "table %s", relname); + break; - tuple = systable_getnext(sscan); - if (!HeapTupleIsValid(tuple)) - elog(ERROR, "catalog lookup failed for relation %u", relOid); + case RELKIND_SEQUENCE: + tclass = SEPG_CLASS_DB_SEQUENCE; + snprintf(audit_name, sizeof(audit_name), "sequence %s", relname); + break; - classForm = (Form_pg_class) GETSTRUCT(tuple); + case RELKIND_VIEW: + tclass = SEPG_CLASS_DB_VIEW; + snprintf(audit_name, sizeof(audit_name), "view %s", relname); + break; - if (classForm->relkind == RELKIND_RELATION) - tclass = SEPG_CLASS_DB_TABLE; - else if (classForm->relkind == RELKIND_SEQUENCE) - tclass = SEPG_CLASS_DB_SEQUENCE; - else if (classForm->relkind == RELKIND_VIEW) - tclass = SEPG_CLASS_DB_VIEW; - else - goto out; /* No need to assign individual labels */ - - /* - * Compute a default security label when we create a new relation object - * under the specified namespace. - */ + default: + /* + * No need to check permission any more, if relation is not + * a table, sequence or view. + */ + info->ncontext = NULL; + info->nattrs = 0; + info->acontexts = NULL; + return; + } scontext = sepgsql_get_client_label(); - tcontext = sepgsql_get_label(NamespaceRelationId, - classForm->relnamespace, 0); - rcontext = sepgsql_compute_create(scontext, tcontext, tclass); + tcontext = sepgsql_get_label(NamespaceRelationId, namespaceId, 0); + ncontext = sepgsql_compute_create(scontext, tcontext, tclass); + info->ncontext = ncontext; /* - * Assign the default security label on the new relation + * check db_xxx:{create} permission */ - object.classId = RelationRelationId; - object.objectId = relOid; - object.objectSubId = 0; - SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, rcontext); - + sepgsql_avc_check_perms_label(ncontext, + tclass, + SEPG_DB_TABLE__CREATE, + audit_name, + true); /* - * We also assigns a default security label on columns of the new regular - * tables. + * compute default security label of columns, and check + * db_column:{create} permission, if relkind == RELKIND_RELATION */ - if (classForm->relkind == RELKIND_RELATION) + if (relkind == RELKIND_RELATION) { - AttrNumber index; + AttrNumber attnum; + int shift = - FirstLowInvalidHeapAttributeNumber - 1; - ccontext = sepgsql_compute_create(scontext, rcontext, + /* + * XXX - upcoming libselinux allows case handling when a new + * object that has a particular name, but it is not supported + * right now. So, we omit to compute default security label + * for each columns, because it shall be uniform when same + * client create columns under the same table. + */ + acontext = sepgsql_compute_create(scontext, ncontext, SEPG_CLASS_DB_COLUMN); - for (index = FirstLowInvalidHeapAttributeNumber + 1; - index <= classForm->relnatts; - index++) + + info->nattrs = tupdesc->natts; + info->acontexts = palloc0(sizeof(const char *) * + (tupdesc->natts + shift + 1)); + + for (attnum = FirstLowInvalidHeapAttributeNumber + 1; + attnum <= tupdesc->natts; + attnum++) { - if (index == InvalidAttrNumber) - continue; + Form_pg_attribute attr; - if (index == ObjectIdAttributeNumber && !classForm->relhasoids) + if (attnum == InvalidAttrNumber || + (attnum == ObjectIdAttributeNumber && !tupdesc->tdhasoid)) + { + info->acontexts[attnum + shift] = NULL; continue; + } + /* check db_column:{create} permission */ + if (attnum < 0) + attr = SystemAttributeDefinition(attnum, true); + else + attr = tupdesc->attrs[attnum - 1]; + snprintf(audit_name, sizeof(audit_name), "table %s column %s", + relname, NameStr(attr->attname)); - object.classId = RelationRelationId; - object.objectId = relOid; - object.objectSubId = index; - SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, ccontext); + sepgsql_avc_check_perms_label(acontext, + SEPG_CLASS_DB_COLUMN, + SEPG_DB_COLUMN__CREATE, + audit_name, + true); + info->acontexts[attnum + shift] = acontext; } - pfree(ccontext); } - pfree(rcontext); -out: - systable_endscan(sscan); - heap_close(rel, AccessShareLock); + else + { + info->nattrs = 0; + info->acontexts = NULL; + } +} + +/* + * sepgsql_relation_post_create + * + * The post creation hook of relation/attribute + */ +void +sepgsql_relation_post_create(sepgsql_creation_info *info, + Oid relationOid) +{ + ObjectAddress object; + AttrNumber attnum; + int shift = - FirstLowInvalidHeapAttributeNumber - 1; + + if (!info || !info->ncontext) + return; + + object.classId = RelationRelationId; + object.objectId = relationOid; + object.objectSubId = 0; + SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, info->ncontext); + + if (!info->acontexts) + return; + + for (attnum = FirstLowInvalidHeapAttributeNumber + 1; + attnum <= info->nattrs; + attnum++) + { + if (!info->acontexts[attnum + shift]) + continue; + + object.classId = RelationRelationId; + object.objectId = relationOid; + object.objectSubId = attnum; + SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, + info->acontexts[attnum + shift]); + } } /* diff --git a/contrib/sepgsql/sepgsql.h b/contrib/sepgsql/sepgsql.h index b4a3e4d..cd9c4c6 100644 --- a/contrib/sepgsql/sepgsql.h +++ b/contrib/sepgsql/sepgsql.h @@ -226,6 +226,10 @@ typedef struct { /* a default security label to be assigned on */ const char *ncontext; + + /* a default security label of columns (only for tables) */ + AttrNumber nattrs; + const char **acontexts; } sepgsql_creation_info; /* @@ -323,7 +327,12 @@ extern void sepgsql_schema_relabel(Oid namespaceId, const char *seclabel); extern void sepgsql_attribute_post_create(Oid relOid, AttrNumber attnum); extern void sepgsql_attribute_relabel(Oid relOid, AttrNumber attnum, const char *seclabel); -extern void sepgsql_relation_post_create(Oid relOid); +extern void sepgsql_relation_prep_create(sepgsql_creation_info *info, + const char *relname, char relkind, + Oid namespaceId, Oid tablespaceId, + TupleDesc tupdesc); +extern void sepgsql_relation_post_create(sepgsql_creation_info *info, + Oid relationOid); extern void sepgsql_relation_relabel(Oid relOid, const char *seclabel); /* diff --git a/contrib/sepgsql/sql/create.sql b/contrib/sepgsql/sql/create.sql index 9ce1db9..4e12411 100644 --- a/contrib/sepgsql/sql/create.sql +++ b/contrib/sepgsql/sql/create.sql @@ -13,6 +13,14 @@ CREATE SCHEMA regtest_schema; SET search_path = regtest_schema, public; +CREATE TABLE regtest_table (x serial primary key, y text); + +CREATE VIEW regtest_view AS SELECT * FROM regtest_table WHERE x < 10; + +CREATE SEQUENCE regtest_seq; + +CREATE TYPE regtest_type AS (a int, b text); + -- -- clean-up -- diff --git a/contrib/sepgsql/test_sepgsql b/contrib/sepgsql/test_sepgsql index 52237e6..4e43e97 100755 --- a/contrib/sepgsql/test_sepgsql +++ b/contrib/sepgsql/test_sepgsql @@ -163,6 +163,34 @@ if [ "${POLICY_STATUS}" != on ]; then exit 1 fi +# Verify that sepgsql_enable_users_ddl is active. +echo -n "checking whether DDL is allowed ... " +POLICY_STATUS=`getsebool sepgsql_enable_users_ddl | awk '{print $3}'` +echo ${POLICY_STATUS:-failed} +if [ "${POLICY_STATUS}" != on ]; then + echo "" + echo "The SELinux boolean 'sepgsql_enable_users_ddl' must be" + echo "turned on in order to enable the rules necessary to run the" + echo "regression tests." + echo "" + if [ "${POLICY_STATUS}" = "" ]; then + echo "We attempted to determine the state of this Boolean using" + echo "'getsebool', but that command did not produce the expected" + echo "output. Please verify that getsebool is available and in" + echo "your PATH." + else + echo "You can turn on this variable using the following commands:" + echo "" + echo " \$ sudo setsebool sepgsql_enable_users_ddl on" + echo "" + echo "For security reasons, it is suggested that you turn off this" + echo "variable unless you want unprivileged users to execute" + echo "DDL commands." + fi + echo "" + exit 1 +fi + # 'psql' command must be executable from test domain echo -n "checking whether we can run psql ... " CMD_PSQL="${PG_BINDIR}/psql" diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y index d0a0e92..4ca33ab 100644 --- a/src/backend/bootstrap/bootparse.y +++ b/src/backend/bootstrap/bootparse.y @@ -248,7 +248,8 @@ Boot_CreateStmt: ONCOMMIT_NOOP, (Datum) 0, false, - true); + true, + (Datum) 0); elog(DEBUG4, "relation created with OID %u", id); } do_end(); diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index e11d896..84613f7 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -993,7 +993,8 @@ heap_create_with_catalog(const char *relname, OnCommitAction oncommit, Datum reloptions, bool use_user_acl, - bool allow_system_table_mods) + bool allow_system_table_mods, + Datum hook_private) { Relation pg_class_desc; Relation new_rel_desc; @@ -1279,7 +1280,9 @@ heap_create_with_catalog(const char *relname, } /* Post creation hook for new relation */ - InvokeObjectAccessHook(OAT_POST_CREATE, RelationRelationId, relid, 0); + InvokeObjectAccessHookArg(OAT_POST_CREATE, + RelationRelationId, relid, 0, + hook_private); /* * Store any supplied constraints and defaults. diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c index 3a40e8b..a28c423 100644 --- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -228,7 +228,8 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio ONCOMMIT_NOOP, reloptions, false, - true); + true, + (Datum) 0); Assert(toast_relid != InvalidOid); /* make the toast relation visible, else heap_open will fail */ diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c index edec44d..08ad2bc 100644 --- a/src/backend/commands/cluster.c +++ b/src/backend/commands/cluster.c @@ -645,7 +645,8 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace) ONCOMMIT_NOOP, reloptions, false, - true); + true, + (Datum) 0); Assert(OIDNewHeap != InvalidOid); ReleaseSysCache(tuple); diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index c4622c0..a8b5a56 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -410,6 +410,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId) List *rawDefaults; List *cookedDefaults; Datum reloptions; + Datum hook_private = 0; ListCell *listptr; AttrNumber attnum; static char *validnsps[] = HEAP_RELOPT_NAMESPACES; @@ -575,6 +576,10 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId) } } + /* Prep-creation hook for new relation */ + InvokePrepCreateRelationHook(&hook_private, relname, relkind, + namespaceId, tablespaceId, descriptor); + /* * Create the relation. Inherited defaults and constraints are passed in * for immediate handling --- since they don't need parsing, they can be @@ -599,7 +604,8 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId) stmt->oncommit, reloptions, true, - allowSystemTableMods); + allowSystemTableMods, + hook_private); /* Store inheritance information for new rel. */ StoreCatalogInheritance(relationId, inheritOids); diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index fd7a9ed..8d97d1e 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -43,6 +43,7 @@ #include "access/xact.h" #include "catalog/heap.h" #include "catalog/namespace.h" +#include "catalog/objectaccess.h" #include "catalog/toasting.h" #include "commands/tablespace.h" #include "commands/trigger.h" @@ -2393,6 +2394,7 @@ OpenIntoRel(QueryDesc *queryDesc) Oid namespaceId; Oid tablespaceId; Datum reloptions; + Datum hook_private = 0; Oid intoRelationId; DR_intorel *myState; static char *validnsps[] = HEAP_RELOPT_NAMESPACES; @@ -2458,6 +2460,12 @@ OpenIntoRel(QueryDesc *queryDesc) get_tablespace_name(tablespaceId)); } + /* Prep-creation hook for new relation */ + InvokePrepCreateRelationHook(&hook_private, intoName, + RELKIND_RELATION, + namespaceId, tablespaceId, + queryDesc->tupDesc); + /* Parse and validate any reloptions */ reloptions = transformRelOptions((Datum) 0, into->options, @@ -2486,7 +2494,8 @@ OpenIntoRel(QueryDesc *queryDesc) into->onCommit, reloptions, true, - allowSystemTableMods); + allowSystemTableMods, + hook_private); Assert(intoRelationId != InvalidOid); /* diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h index aee2d88..ca4d6a1 100644 --- a/src/include/catalog/heap.h +++ b/src/include/catalog/heap.h @@ -65,7 +65,8 @@ extern Oid heap_create_with_catalog(const char *relname, OnCommitAction oncommit, Datum reloptions, bool use_user_acl, - bool allow_system_table_mods); + bool allow_system_table_mods, + Datum hook_private); extern void heap_drop_with_catalog(Oid relid); diff --git a/src/include/catalog/objectaccess.h b/src/include/catalog/objectaccess.h index cb5c396..d151ee5 100644 --- a/src/include/catalog/objectaccess.h +++ b/src/include/catalog/objectaccess.h @@ -10,6 +10,8 @@ #ifndef OBJECTACCESS_H #define OBJECTACCESS_H +#include "access/tupdesc.h" + /* * Object access hooks are intended to be called just before or just after * performing certain actions on a SQL object. This is intended as @@ -55,6 +57,14 @@ typedef union Datum *private; /* common */ const char *nspname; /* name of new schema */ } pg_namespace; + struct { + Datum *private; /* common */ + const char *relname; /* name of new relation */ + char relkind; + Oid namespaceId; + Oid tablespaceId; + TupleDesc tupdesc; + } pg_class; } ObjectAccessCreateObjectArgs; /* @@ -121,4 +131,24 @@ extern PGDLLIMPORT object_access_hook_type object_access_hook; } \ } while(0) +#define InvokePrepCreateRelationHook(_private,_relname,_relkind, \ + _namespace,_tablespace,_tupdesc) \ + do { \ + if (object_access_hook) \ + { \ + ObjectAccessCreateObjectArgs __args; \ + \ + __args.pg_class.private = (_private); \ + __args.pg_class.relname = (_relname); \ + __args.pg_class.relkind = (_relkind); \ + __args.pg_class.namespaceId = (_namespace); \ + __args.pg_class.tablespaceId = (_tablespace); \ + __args.pg_class.tupdesc = (_tupdesc); \ + \ + (*object_access_hook)(OAT_PREP_CREATE, \ + RelationRelationId, InvalidOid, 0, \ + PointerGetDatum(&__args)); \ + } \ + } while(0) + #endif /* OBJECTACCESS_H */