Example #1
0
static void
json_aestart(void *state, bool isnull)
{
	pgspParserContext *ctx = (pgspParserContext *)state;
	if (ctx->remove)
		return;

	if (IS_INDENTED_ARRAY(ctx->current_list) &&
		ctx->wlist_level == 1)
	{
		if (!bms_is_member(ctx->level, ctx->first))
			appendStringInfoChar(ctx->dest, ',');

		if (ctx->mode == PGSP_JSON_INFLATE)
		{
			appendStringInfoChar(ctx->dest, '\n');
			appendStringInfoSpaces(ctx->dest, (ctx->level) * INDENT_STEP);
		}
	}
	else
	{
		if (!bms_is_member(ctx->level, ctx->first))
		{
			appendStringInfoChar(ctx->dest, ',');

			if (ctx->mode == PGSP_JSON_INFLATE &&
				!ctx->last_elem_is_object)
				appendStringInfoChar(ctx->dest, ' ');
		}
	}

	ctx->first = bms_del_member(ctx->first, ctx->level);
}
static void
xml_aestart(void *state, bool isnull)
{
	pgspParserContext *ctx = (pgspParserContext *)state;
	char *tag;

	/*
	 * The "Trigger" in "Triggers", "Plan" in "Plans" and "Item" nodes are
	 * implicitly represented in JSON format.  Restore them for XML format.
	 */

	ctx->level++;
	if (bms_is_member(ctx->level, ctx->not_item))
	{
		if (ctx->processing == P_Plan)
			tag = "<Plan>";
		else
			tag = "<Trigger>";
	}
	else
		tag = "<Item>";

	appendStringInfoChar(ctx->dest, '\n');
	appendStringInfoSpaces(ctx->dest, (ctx->level + 1) * INDENT_STEP);
	appendStringInfoString(ctx->dest, tag);
}
static void
yaml_ofstart(void *state, char *fname, bool isnull)
{
	word_table *p;
	pgspParserContext *ctx = (pgspParserContext *)state;
	char *s;

	p = search_word_table(propfields, fname, ctx->mode);
	if (!p)
	{
		ereport(DEBUG1,
				(errmsg("Short JSON parser encoutered unknown field name: \"%s\".", fname),
				 errdetail_log("INPUT: \"%s\"", ctx->org_string)));
	}		
	s = (p ? p->longname : fname);

	if (!bms_is_member(ctx->level, ctx->first))
	{
		appendStringInfoString(ctx->dest, "\n");
		appendStringInfoSpaces(ctx->dest, ctx->level * INDENT_STEP);
	}
	else
		ctx->first = bms_del_member(ctx->first, ctx->level);

	ctx->valconverter = NULL;
	ctx->fname = s;
	ctx->valconverter = (p ? p->converter : NULL);
}
Example #4
0
/*
 * Write relation attributes to the stream.
 */
static void
logicalrep_write_attrs(StringInfo out, Relation rel)
{
	TupleDesc	desc;
	int			i;
	uint16		nliveatts = 0;
	Bitmapset  *idattrs = NULL;
	bool		replidentfull;

	desc = RelationGetDescr(rel);

	/* send number of live attributes */
	for (i = 0; i < desc->natts; i++)
	{
		if (TupleDescAttr(desc, i)->attisdropped)
			continue;
		nliveatts++;
	}
	pq_sendint(out, nliveatts, 2);

	/* fetch bitmap of REPLICATION IDENTITY attributes */
	replidentfull = (rel->rd_rel->relreplident == REPLICA_IDENTITY_FULL);
	if (!replidentfull)
		idattrs = RelationGetIndexAttrBitmap(rel,
											 INDEX_ATTR_BITMAP_IDENTITY_KEY);

	/* send the attributes */
	for (i = 0; i < desc->natts; i++)
	{
		Form_pg_attribute att = TupleDescAttr(desc, i);
		uint8		flags = 0;

		if (att->attisdropped)
			continue;

		/* REPLICA IDENTITY FULL means all columns are sent as part of key. */
		if (replidentfull ||
			bms_is_member(att->attnum - FirstLowInvalidHeapAttributeNumber,
						  idattrs))
			flags |= LOGICALREP_IS_REPLICA_IDENTITY;

		pq_sendbyte(out, flags);

		/* attribute name */
		pq_sendstring(out, NameStr(att->attname));

		/* attribute type id */
		pq_sendint(out, (int) att->atttypid, sizeof(att->atttypid));

		/* attribute mode */
		pq_sendint(out, att->atttypmod, sizeof(att->atttypmod));
	}

	bms_free(idattrs);
}
Example #5
0
/*
 * Checks if any of the 'attnums' is a partition key attribute for rel
 *
 * Sets *used_in_expr if any of the 'attnums' is found to be referenced in some
 * partition key expression.  It's possible for a column to be both used
 * directly and as part of an expression; if that happens, *used_in_expr may
 * end up as either true or false.  That's OK for current uses of this
 * function, because *used_in_expr is only used to tailor the error message
 * text.
 */
bool
has_partition_attrs(Relation rel, Bitmapset *attnums, bool *used_in_expr)
{
	PartitionKey key;
	int			partnatts;
	List	   *partexprs;
	ListCell   *partexprs_item;
	int			i;

	if (attnums == NULL || rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
		return false;

	key = RelationGetPartitionKey(rel);
	partnatts = get_partition_natts(key);
	partexprs = get_partition_exprs(key);

	partexprs_item = list_head(partexprs);
	for (i = 0; i < partnatts; i++)
	{
		AttrNumber	partattno = get_partition_col_attnum(key, i);

		if (partattno != 0)
		{
			if (bms_is_member(partattno - FirstLowInvalidHeapAttributeNumber,
							  attnums))
			{
				if (used_in_expr)
					*used_in_expr = false;
				return true;
			}
		}
		else
		{
			/* Arbitrary expression */
			Node	   *expr = (Node *) lfirst(partexprs_item);
			Bitmapset  *expr_attrs = NULL;

			/* Find all attributes referenced */
			pull_varattnos(expr, 1, &expr_attrs);
			partexprs_item = lnext(partexprs_item);

			if (bms_overlap(attnums, expr_attrs))
			{
				if (used_in_expr)
					*used_in_expr = true;
				return true;
			}
		}
	}

	return false;
}
static void
json_ofstart(void *state, char *fname, bool isnull)
{
	word_table *p;
	pgspParserContext *ctx = (pgspParserContext *)state;
	char *fn;

	ctx->remove = false;
	p = search_word_table(propfields, fname, ctx->mode);
	if (!p)
	{
		ereport(DEBUG1,
				(errmsg("JSON parser encoutered unknown field name: \"%s\".", fname),
				 errdetail_log("INPUT: \"%s\"", ctx->org_string)));
	}		

	ctx->remove = (ctx->mode == PGSP_JSON_NORMALIZE &&
				   (!p || !p->normalize_use));

	if (ctx->remove)
		return;

	if (!bms_is_member(ctx->level, ctx->first))
	{
		appendStringInfoChar(ctx->dest, ',');
		if (ctx->mode == PGSP_JSON_INFLATE)
			appendStringInfoChar(ctx->dest, '\n');
	}
	else
		ctx->first = bms_del_member(ctx->first, ctx->level);

	if (ctx->mode == PGSP_JSON_INFLATE)
		appendStringInfoSpaces(ctx->dest, ctx->level * INDENT_STEP);

	if (!p || !p->longname)
		fn = fname;
	else if (ctx->mode == PGSP_JSON_INFLATE)
		fn = p->longname;
	else
		fn = p->shortname;

	escape_json(ctx->dest, fn);
	ctx->fname = fn;
	ctx->valconverter = (p ? p->converter : NULL);

	appendStringInfoChar(ctx->dest, ':');

	if (ctx->mode == PGSP_JSON_INFLATE)
		appendStringInfoChar(ctx->dest, ' ');
}
static void
json_aestart(void *state, bool isnull)
{
	pgspParserContext *ctx = (pgspParserContext *)state;
	if (ctx->remove)
		return;

	if (!bms_is_member(ctx->level, ctx->first))
	{
		appendStringInfoChar(ctx->dest, ',');
		if (ctx->mode == PGSP_JSON_INFLATE &&
			!ctx->last_elem_is_object)
			appendStringInfoChar(ctx->dest, ' ');
	}
	else
		ctx->first = bms_del_member(ctx->first, ctx->level);
}
Example #8
0
/*
 * GetAnyDataNode
 * Pick any data node from given set, but try a preferred node
 */
int
GetAnyDataNode(Bitmapset *nodes)
{
	Bitmapset  *preferred = NULL;
	int			i, nodeid;
	int			nmembers = 0;
	int			members[NumDataNodes];

	for (i = 0; i < num_preferred_data_nodes; i++)
	{
		char ntype = PGXC_NODE_DATANODE;
		nodeid = PGXCNodeGetNodeId(preferred_data_node[i], &ntype);

		/* OK, found one */
		if (bms_is_member(nodeid, nodes))
			preferred = bms_add_member(preferred, nodeid);
	}

	/*
	 * If no preferred data nodes or they are not in the desired set, pick up
	 * from the original set.
	 */
	if (bms_is_empty(preferred))
		preferred = bms_copy(nodes);

	/*
	 * Load balance.
	 * We can not get item from the set, convert it to array
	 */
	while ((nodeid = bms_first_member(preferred)) >= 0)
		members[nmembers++] = nodeid;
	bms_free(preferred);

	/* If there is a single member nothing to balance */
	if (nmembers == 1)
		return members[0];

	/*
	 * In general, the set may contain any number of nodes, and if we save
	 * previous returned index for load balancing the distribution won't be
	 * flat, because small set will probably reset saved value, and lower
	 * indexes will be picked up more often.
	 * So we just get a random value from 0..nmembers-1.
	 */
	return members[((unsigned int) random()) % nmembers];
}
static void
json_objend(void *state)
{
	pgspParserContext *ctx = (pgspParserContext *)state;
	if (ctx->mode == PGSP_JSON_INFLATE)
	{
		if (!bms_is_member(ctx->level, ctx->first))
			appendStringInfoChar(ctx->dest, '\n');
		appendStringInfoSpaces(ctx->dest, (ctx->level - 1) * INDENT_STEP);
	}

	appendStringInfoChar(ctx->dest, '}');

	ctx->level--;
	ctx->last_elem_is_object = true;
	ctx->first = bms_del_member(ctx->first, ctx->level);
	ctx->fname = NULL;
}
Example #10
0
/*
 * fixup_whole_row_references
 *
 * When user reference a whole of row, it is equivalent to reference to
 * all the user columns (not system columns). So, we need to fix up the
 * given bitmapset, if it contains a whole of the row reference.
 */
static Bitmapset *
fixup_whole_row_references(Oid relOid, Bitmapset *columns)
{
	Bitmapset  *result;
	HeapTuple	tuple;
	AttrNumber	natts;
	AttrNumber	attno;
	int			index;

	/* if no whole of row references, do not anything */
	index = InvalidAttrNumber - FirstLowInvalidHeapAttributeNumber;
	if (!bms_is_member(index, columns))
		return columns;

	/* obtain number of attributes */
	tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
	if (!HeapTupleIsValid(tuple))
		elog(ERROR, "cache lookup failed for relation %u", relOid);
	natts = ((Form_pg_class) GETSTRUCT(tuple))->relnatts;
	ReleaseSysCache(tuple);

	/* fix up the given columns */
	result = bms_copy(columns);
	result = bms_del_member(result, index);

	for (attno = 1; attno <= natts; attno++)
	{
		tuple = SearchSysCache2(ATTNUM,
								ObjectIdGetDatum(relOid),
								Int16GetDatum(attno));
		if (!HeapTupleIsValid(tuple))
			continue;

		if (((Form_pg_attribute) GETSTRUCT(tuple))->attisdropped)
			continue;

		index = attno - FirstLowInvalidHeapAttributeNumber;

		result = bms_add_member(result, index);

		ReleaseSysCache(tuple);
	}
	return result;
}
Example #11
0
/*
 * dependency_is_fully_matched
 *		checks that a functional dependency is fully matched given clauses on
 *		attributes (assuming the clauses are suitable equality clauses)
 */
static bool
dependency_is_fully_matched(MVDependency *dependency, Bitmapset *attnums)
{
	int			j;

	/*
	 * Check that the dependency actually is fully covered by clauses. We have
	 * to translate all attribute numbers, as those are referenced
	 */
	for (j = 0; j < dependency->nattributes; j++)
	{
		int			attnum = dependency->attributes[j];

		if (!bms_is_member(attnum, attnums))
			return false;
	}

	return true;
}
static void
xml_aeend(void *state, bool isnull)
{
	pgspParserContext *ctx = (pgspParserContext *)state;
	char *tag;

	/*
	 * The "Plan" in "Plans" or "Item" nodes are implicitly represented in
	 * JSON format.  Restore it for XML format.
	 */

	if (bms_is_member(ctx->level, ctx->not_item))
	{
		if (ctx->processing == P_Plan)
			tag = "</Plan>";
		else
			tag = "</Trigger>";
	}
	else
		tag = "</Item>";
	appendStringInfoString(ctx->dest, tag);
	ctx->level--;
}
Example #13
0
/*
 * check_relation_privileges
 *
 * It actually checks required permissions on a certain relation
 * and its columns.
 */
static bool
check_relation_privileges(Oid relOid,
						  Bitmapset *selected,
						  Bitmapset *inserted,
						  Bitmapset *updated,
						  uint32 required,
						  bool abort_on_violation)
{
	ObjectAddress object;
	char	   *audit_name;
	Bitmapset  *columns;
	int			index;
	char		relkind = get_rel_relkind(relOid);
	bool		result = true;

	/*
	 * Hardwired Policies: SE-PostgreSQL enforces - clients cannot modify
	 * system catalogs using DMLs - clients cannot reference/modify toast
	 * relations using DMLs
	 */
	if (sepgsql_getenforce() > 0)
	{
		Oid			relnamespace = get_rel_namespace(relOid);

		if (IsSystemNamespace(relnamespace) &&
			(required & (SEPG_DB_TABLE__UPDATE |
						 SEPG_DB_TABLE__INSERT |
						 SEPG_DB_TABLE__DELETE)) != 0)
			ereport(ERROR,
					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
					 errmsg("SELinux: hardwired security policy violation")));

		if (relkind == RELKIND_TOASTVALUE)
			ereport(ERROR,
					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
					 errmsg("SELinux: hardwired security policy violation")));
	}

	/*
	 * Check permissions on the relation
	 */
	object.classId = RelationRelationId;
	object.objectId = relOid;
	object.objectSubId = 0;
	audit_name = getObjectIdentity(&object);
	switch (relkind)
	{
		case RELKIND_RELATION:
		case RELKIND_PARTITIONED_TABLE:
			result = sepgsql_avc_check_perms(&object,
											 SEPG_CLASS_DB_TABLE,
											 required,
											 audit_name,
											 abort_on_violation);
			break;

		case RELKIND_SEQUENCE:
			Assert((required & ~SEPG_DB_TABLE__SELECT) == 0);

			if (required & SEPG_DB_TABLE__SELECT)
				result = sepgsql_avc_check_perms(&object,
												 SEPG_CLASS_DB_SEQUENCE,
												 SEPG_DB_SEQUENCE__GET_VALUE,
												 audit_name,
												 abort_on_violation);
			break;

		case RELKIND_VIEW:
			result = sepgsql_avc_check_perms(&object,
											 SEPG_CLASS_DB_VIEW,
											 SEPG_DB_VIEW__EXPAND,
											 audit_name,
											 abort_on_violation);
			break;

		default:
			/* nothing to be checked */
			break;
	}
	pfree(audit_name);

	/*
	 * Only columns owned by relations shall be checked
	 */
	if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE)
		return true;

	/*
	 * Check permissions on the columns
	 */
	selected = fixup_whole_row_references(relOid, selected);
	inserted = fixup_whole_row_references(relOid, inserted);
	updated = fixup_whole_row_references(relOid, updated);
	columns = bms_union(selected, bms_union(inserted, updated));

	while ((index = bms_first_member(columns)) >= 0)
	{
		AttrNumber	attnum;
		uint32		column_perms = 0;

		if (bms_is_member(index, selected))
			column_perms |= SEPG_DB_COLUMN__SELECT;
		if (bms_is_member(index, inserted))
		{
			if (required & SEPG_DB_TABLE__INSERT)
				column_perms |= SEPG_DB_COLUMN__INSERT;
		}
		if (bms_is_member(index, updated))
		{
			if (required & SEPG_DB_TABLE__UPDATE)
				column_perms |= SEPG_DB_COLUMN__UPDATE;
		}
		if (column_perms == 0)
			continue;

		/* obtain column's permission */
		attnum = index + FirstLowInvalidHeapAttributeNumber;

		object.classId = RelationRelationId;
		object.objectId = relOid;
		object.objectSubId = attnum;
		audit_name = getObjectDescription(&object);

		result = sepgsql_avc_check_perms(&object,
										 SEPG_CLASS_DB_COLUMN,
										 column_perms,
										 audit_name,
										 abort_on_violation);
		pfree(audit_name);

		if (!result)
			return result;
	}
	return true;
}
Example #14
0
/*
 * Open the local relation associated with the remote one.
 *
 * Optionally rebuilds the Relcache mapping if it was invalidated
 * by local DDL.
 */
LogicalRepRelMapEntry *
logicalrep_rel_open(LogicalRepRelId remoteid, LOCKMODE lockmode)
{
	LogicalRepRelMapEntry  *entry;
	bool		found;

	if (LogicalRepRelMap == NULL)
		logicalrep_relmap_init();

	/* Search for existing entry. */
	entry = hash_search(LogicalRepRelMap, (void *) &remoteid,
						HASH_FIND, &found);

	if (!found)
		elog(ERROR, "no relation map entry for remote relation ID %u",
			 remoteid);

	/* Need to update the local cache? */
	if (!OidIsValid(entry->localreloid))
	{
		Oid			relid;
		int			i;
		int			found;
		Bitmapset  *idkey;
		TupleDesc	desc;
		LogicalRepRelation *remoterel;
		MemoryContext		oldctx;
		remoterel = &entry->remoterel;

		/* Try to find and lock the relation by name. */
		relid = RangeVarGetRelid(makeRangeVar(remoterel->nspname,
											  remoterel->relname, -1),
								 lockmode, true);
		if (!OidIsValid(relid))
			ereport(ERROR,
					(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
					 errmsg("logical replication target relation \"%s.%s\" does not exist",
							remoterel->nspname, remoterel->relname)));
		entry->localrel = heap_open(relid, NoLock);

		/*
		 * We currently only support writing to regular and partitioned
		 * tables.
		 */
		if (entry->localrel->rd_rel->relkind != RELKIND_RELATION)
			ereport(ERROR,
					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
					 errmsg("logical replication target relation \"%s.%s\" is not a table",
							remoterel->nspname, remoterel->relname)));

		/*
		 * Build the mapping of local attribute numbers to remote attribute
		 * numbers and validate that we don't miss any replicated columns
		 * as that would result in potentially unwanted data loss.
		 */
		desc = RelationGetDescr(entry->localrel);
		oldctx = MemoryContextSwitchTo(LogicalRepRelMapContext);
		entry->attrmap = palloc(desc->natts * sizeof(int));
		MemoryContextSwitchTo(oldctx);

		found = 0;
		for (i = 0; i < desc->natts; i++)
		{
			int	attnum = logicalrep_rel_att_by_name(remoterel,
											NameStr(desc->attrs[i]->attname));
			entry->attrmap[i] = attnum;
			if (attnum >= 0)
				found++;
		}

		/* TODO, detail message with names of missing columns */
		if (found < remoterel->natts)
			ereport(ERROR,
					(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
					 errmsg("logical replication target relation \"%s.%s\" is missing "
							"some replicated columns",
							remoterel->nspname, remoterel->relname)));

		/*
		 * Check that replica identity matches. We allow for stricter replica
		 * identity (fewer columns) on subscriber as that will not stop us
		 * from finding unique tuple. IE, if publisher has identity
		 * (id,timestamp) and subscriber just (id) this will not be a problem,
		 * but in the opposite scenario it will.
		 *
		 * Don't throw any error here just mark the relation entry as not
		 * updatable, as replica identity is only for updates and deletes
		 * but inserts can be replicated even without it.
		 */
		entry->updatable = true;
		idkey = RelationGetIndexAttrBitmap(entry->localrel,
										   INDEX_ATTR_BITMAP_IDENTITY_KEY);
		/* fallback to PK if no replica identity */
		if (idkey == NULL)
		{
			idkey = RelationGetIndexAttrBitmap(entry->localrel,
											   INDEX_ATTR_BITMAP_PRIMARY_KEY);
			/*
			 * If no replica identity index and no PK, the published table
			 * must have replica identity FULL.
			 */
			if (idkey == NULL && remoterel->replident != REPLICA_IDENTITY_FULL)
				entry->updatable = false;
		}

		i = -1;
		while ((i = bms_next_member(idkey, i)) >= 0)
		{
			int attnum = i + FirstLowInvalidHeapAttributeNumber;

			if (!AttrNumberIsForUserDefinedAttr(attnum))
				ereport(ERROR,
						(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
						 errmsg("logical replication target relation \"%s.%s\" uses "
								"system columns in REPLICA IDENTITY index",
								remoterel->nspname, remoterel->relname)));

			attnum = AttrNumberGetAttrOffset(attnum);

			if (!bms_is_member(entry->attrmap[attnum], remoterel->attkeys))
			{
				entry->updatable = false;
				break;
			}
		}

		entry->localreloid = relid;
	}
	else
		entry->localrel = heap_open(entry->localreloid, lockmode);

	return entry;
}
Example #15
0
static void
json_ofstart(void *state, char *fname, bool isnull)
{
	word_table *p;
	pgspParserContext *ctx = (pgspParserContext *)state;
	char *fn;

	ctx->remove = false;
	p = search_word_table(propfields, fname, ctx->mode);
	if (!p)
	{
		ereport(DEBUG1,
				(errmsg("JSON parser encoutered unknown field name: \"%s\".", fname),
				 errdetail_log("INPUT: \"%s\"", ctx->org_string)));
	}		

	ctx->remove = (ctx->mode == PGSP_JSON_NORMALIZE &&
				   (!p || !p->normalize_use));

	if (ctx->remove)
		return;

	if (!bms_is_member(ctx->level, ctx->first))
	{
		appendStringInfoChar(ctx->dest, ',');
		if (ctx->mode == PGSP_JSON_INFLATE)
			appendStringInfoChar(ctx->dest, '\n');
	}
	else
		ctx->first = bms_del_member(ctx->first, ctx->level);

	if (ctx->mode == PGSP_JSON_INFLATE)
		appendStringInfoSpaces(ctx->dest, ctx->level * INDENT_STEP);

	/*
	 * We intentionally let some property names not have a short name. Use long
	 * name for the cases.
	 */
	if (!p || !p->longname)
		fn = fname;
	else if (ctx->mode == PGSP_JSON_INFLATE ||
			 !(p->shortname && p->shortname[0]))
		fn = p->longname;
	else
		fn = p->shortname;

	escape_json(ctx->dest, fn);
	ctx->fname = fn;
	ctx->valconverter = (p ? p->converter : NULL);

	appendStringInfoChar(ctx->dest, ':');

	if (ctx->mode == PGSP_JSON_INFLATE)
		appendStringInfoChar(ctx->dest, ' ');

	if (p && IS_INDENTED_ARRAY(p->tag))
	{
		ctx->current_list = p->tag;
		ctx->list_fname = fname;
		ctx->wlist_level = 0;
	}
}