Beispiel #1
0
/*
 * regconfigin		- converts "tsconfigname" to tsconfig 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_ts_config entry.
 *
 * This function is not needed in bootstrap mode, so we don't worry about
 * making it work then.
 */
Datum
regconfigin(PG_FUNCTION_ARGS)
{
	char	   *cfg_name_or_oid = PG_GETARG_CSTRING(0);
	Oid			result;
	List	   *names;

	/* '-' ? */
	if (strcmp(cfg_name_or_oid, "-") == 0)
		PG_RETURN_OID(InvalidOid);

	/* Numeric OID? */
	if (cfg_name_or_oid[0] >= '0' &&
		cfg_name_or_oid[0] <= '9' &&
		strspn(cfg_name_or_oid, "0123456789") == strlen(cfg_name_or_oid))
	{
		result = DatumGetObjectId(DirectFunctionCall1(oidin,
										  CStringGetDatum(cfg_name_or_oid)));
		PG_RETURN_OID(result);
	}

	/*
	 * Normal case: parse the name into components and see if it matches any
	 * pg_ts_config entries in the current search path.
	 */
	names = stringToQualifiedNameList(cfg_name_or_oid);

	result = get_ts_config_oid(names, false);

	PG_RETURN_OID(result);
}
Beispiel #2
0
Oid
getTSCurrentConfig(bool emitError)
{
	/* if we have a cached value, return it */
	if (OidIsValid(TSCurrentConfigCache))
		return TSCurrentConfigCache;

	/* fail if GUC hasn't been set up yet */
	if (TSCurrentConfig == NULL || *TSCurrentConfig == '\0')
	{
		if (emitError)
			elog(ERROR, "text search configuration isn't set");
		else
			return InvalidOid;
	}

	if (TSConfigCacheHash == NULL)
	{
		/* First time through: initialize the tsconfig inval callback */
		init_ts_config_cache();
	}

	/* Look up the config */
	TSCurrentConfigCache =
		get_ts_config_oid(stringToQualifiedNameList(TSCurrentConfig),
						  !emitError);

	return TSCurrentConfigCache;
}
Beispiel #3
0
/* GUC check_hook for default_text_search_config */
bool
check_TSCurrentConfig(char **newval, void **extra, GucSource source)
{
	/*
	 * If we aren't inside a transaction, we cannot do database access so
	 * cannot verify the config name.  Must accept it on faith.
	 */
	if (IsTransactionState())
	{
		Oid			cfgId;
		HeapTuple	tuple;
		Form_pg_ts_config cfg;
		char	   *buf;

		cfgId = get_ts_config_oid(stringToQualifiedNameList(*newval), true);

		/*
		 * When source == PGC_S_TEST, don't throw a hard error for a
		 * nonexistent configuration, only a NOTICE.  See comments in guc.h.
		 */
		if (!OidIsValid(cfgId))
		{
			if (source == PGC_S_TEST)
			{
				ereport(NOTICE,
						(errcode(ERRCODE_UNDEFINED_OBJECT),
						 errmsg("text search configuration \"%s\" does not exist", *newval)));
				return true;
			}
			else
				return false;
		}

		/*
		 * Modify the actually stored value to be fully qualified, to ensure
		 * later changes of search_path don't affect it.
		 */
		tuple = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(cfgId));
		if (!HeapTupleIsValid(tuple))
			elog(ERROR, "cache lookup failed for text search configuration %u",
				 cfgId);
		cfg = (Form_pg_ts_config) GETSTRUCT(tuple);

		buf = quote_qualified_identifier(get_namespace_name(cfg->cfgnamespace),
										 NameStr(cfg->cfgname));

		ReleaseSysCache(tuple);

		/* GUC wants it malloc'd not palloc'd */
		free(*newval);
		*newval = strdup(buf);
		pfree(buf);
		if (!*newval)
			return false;
	}

	return true;
}
Beispiel #4
0
static Datum
tsvector_update_trigger(PG_FUNCTION_ARGS, bool config_column)
{
	TriggerData *trigdata;
	Trigger    *trigger;
	Relation	rel;
	HeapTuple	rettuple = NULL;
	int			tsvector_attr_num,
				i;
	ParsedText	prs;
	Datum		datum;
	bool		isnull;
	text	   *txt;
	Oid			cfgId;

	/* Check call context */
	if (!CALLED_AS_TRIGGER(fcinfo))		/* internal error */
		elog(ERROR, "tsvector_update_trigger: not fired by trigger manager");

	trigdata = (TriggerData *) fcinfo->context;
	if (!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
		elog(ERROR, "tsvector_update_trigger: must be fired for row");
	if (!TRIGGER_FIRED_BEFORE(trigdata->tg_event))
		elog(ERROR, "tsvector_update_trigger: must be fired BEFORE event");

	if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
		rettuple = trigdata->tg_trigtuple;
	else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
		rettuple = trigdata->tg_newtuple;
	else
		elog(ERROR, "tsvector_update_trigger: must be fired for INSERT or UPDATE");

	trigger = trigdata->tg_trigger;
	rel = trigdata->tg_relation;

	if (trigger->tgnargs < 3)
		elog(ERROR, "tsvector_update_trigger: arguments must be tsvector_field, ts_config, text_field1, ...)");

	/* Find the target tsvector column */
	tsvector_attr_num = SPI_fnumber(rel->rd_att, trigger->tgargs[0]);
	if (tsvector_attr_num == SPI_ERROR_NOATTRIBUTE)
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_COLUMN),
				 errmsg("tsvector column \"%s\" does not exist",
						trigger->tgargs[0])));
	if (!IsBinaryCoercible(SPI_gettypeid(rel->rd_att, tsvector_attr_num),
						  TSVECTOROID))
		ereport(ERROR,
				(errcode(ERRCODE_DATATYPE_MISMATCH),
				 errmsg("column \"%s\" is not of tsvector type",
						trigger->tgargs[0])));

	/* Find the configuration to use */
	if (config_column)
	{
		int			config_attr_num;

		config_attr_num = SPI_fnumber(rel->rd_att, trigger->tgargs[1]);
		if (config_attr_num == SPI_ERROR_NOATTRIBUTE)
			ereport(ERROR,
					(errcode(ERRCODE_UNDEFINED_COLUMN),
					 errmsg("configuration column \"%s\" does not exist",
							trigger->tgargs[1])));
		if (!IsBinaryCoercible(SPI_gettypeid(rel->rd_att, config_attr_num),
							  REGCONFIGOID))
			ereport(ERROR,
					(errcode(ERRCODE_DATATYPE_MISMATCH),
					 errmsg("column \"%s\" is not of regconfig type",
							trigger->tgargs[1])));

		datum = SPI_getbinval(rettuple, rel->rd_att, config_attr_num, &isnull);
		if (isnull)
			ereport(ERROR,
					(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
					 errmsg("configuration column \"%s\" must not be null",
							trigger->tgargs[1])));
		cfgId = DatumGetObjectId(datum);
	}
	else
	{
		List	   *names;

		names = stringToQualifiedNameList(trigger->tgargs[1]);
		/* require a schema so that results are not search path dependent */
		if (list_length(names) < 2)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("text search configuration name \"%s\" must be schema-qualified",
							trigger->tgargs[1])));
		cfgId = get_ts_config_oid(names, false);
	}

	/* initialize parse state */
	prs.lenwords = 32;
	prs.curwords = 0;
	prs.pos = 0;
	prs.words = (ParsedWord *) palloc(sizeof(ParsedWord) * prs.lenwords);

	/* find all words in indexable column(s) */
	for (i = 2; i < trigger->tgnargs; i++)
	{
		int			numattr;

		numattr = SPI_fnumber(rel->rd_att, trigger->tgargs[i]);
		if (numattr == SPI_ERROR_NOATTRIBUTE)
			ereport(ERROR,
					(errcode(ERRCODE_UNDEFINED_COLUMN),
					 errmsg("column \"%s\" does not exist",
							trigger->tgargs[i])));
		if (!IsBinaryCoercible(SPI_gettypeid(rel->rd_att, numattr), TEXTOID))
			ereport(ERROR,
					(errcode(ERRCODE_DATATYPE_MISMATCH),
					 errmsg("column \"%s\" is not of a character type",
							trigger->tgargs[i])));

		datum = SPI_getbinval(rettuple, rel->rd_att, numattr, &isnull);
		if (isnull)
			continue;

		txt = DatumGetTextP(datum);

		parsetext(cfgId, &prs, VARDATA(txt), VARSIZE(txt) - VARHDRSZ);

		if (txt != (text *) DatumGetPointer(datum))
			pfree(txt);
	}

	/* make tsvector value */
	if (prs.curwords)
	{
		datum = PointerGetDatum(make_tsvector(&prs));
		rettuple = SPI_modifytuple(rel, rettuple, 1, &tsvector_attr_num,
								   &datum, NULL);
		pfree(DatumGetPointer(datum));
	}
	else
	{
		TSVector	out = palloc(CALCDATASIZE(0, 0));

		SET_VARSIZE(out, CALCDATASIZE(0, 0));
		out->size = 0;
		datum = PointerGetDatum(out);
		rettuple = SPI_modifytuple(rel, rettuple, 1, &tsvector_attr_num,
								   &datum, NULL);
		pfree(prs.words);
	}

	if (rettuple == NULL)		/* internal error */
		elog(ERROR, "tsvector_update_trigger: %d returned by SPI_modifytuple",
			 SPI_result);

	return PointerGetDatum(rettuple);
}
Beispiel #5
0
/*
 * Translate an object name and arguments (as passed by the parser) to an
 * ObjectAddress.
 *
 * The returned object will be locked using the specified lockmode.  If a
 * sub-object is looked up, the parent object will be locked instead.
 *
 * If the object is a relation or a child object of a relation (e.g. an
 * attribute or contraint), the relation is also opened and *relp receives
 * the open relcache entry pointer; otherwise, *relp is set to NULL.  This
 * is a bit grotty but it makes life simpler, since the caller will
 * typically need the relcache entry too.  Caller must close the relcache
 * entry when done with it.  The relation is locked with the specified lockmode
 * if the target object is the relation itself or an attribute, but for other
 * child objects, only ACCESS_SHR_LOCK is acquired on the relation.
 *
 * We don't currently provide a function to release the locks acquired here;
 * typically, the lock must be held until commit to guard against a concurrent
 * drop operation.
 */
struct objaddr
get_object_address(
	objtype_e objtype,
	struct list* objname,
	struct list* objargs,
	struct relation** relp,
	lockmode_t lockmode)
{
	struct objaddr address;
	struct relation* relation = NULL;

	/* Some kind of lock must be taken. */
	ASSERT(lockmode != NO_LOCK);

	switch (objtype) {
	case OBJECT_INDEX:
	case OBJECT_SEQUENCE:
	case OBJECT_TABLE:
	case OBJECT_VIEW:
	case OBJECT_FOREIGN_TABLE:
		relation = get_rel_by_qualified_name(objtype, objname, lockmode);
		address.classId = RelationRelationId;
		address.objectId = REL_ID(relation);
		address.objectSubId = 0;
		break;

	case OBJECT_COLUMN:
		address = get_objaddr_attr(objtype, objname, &relation, lockmode);
		break;

	case OBJECT_RULE:
	case OBJECT_TRIGGER:
	case OBJECT_CONSTRAINT:
		address = get_objaddr_relobj(objtype, objname, &relation);
		break;

	case OBJECT_DATABASE:
	case OBJECT_EXTENSION:
	case OBJECT_TABLESPACE:
	case OBJECT_ROLE:
	case OBJECT_SCHEMA:
	case OBJECT_LANGUAGE:
	case OBJECT_FDW:
	case OBJECT_FOREIGN_SERVER:
		address = get_objaddr_unqualified(objtype, objname);
		break;

	case OBJECT_TYPE:
	case OBJECT_DOMAIN:
		address.classId = TypeRelationId;
		address.objectId = typename_to_oid(NULL, makeTypeNameFromNameList(objname));
		address.objectSubId = 0;
		break;

	case OBJECT_AGGREGATE:
		address.classId = ProcedureRelationId;
		address.objectId = LookupAggNameTypeNames(objname, objargs, false);
		address.objectSubId = 0;
		break;

	case OBJECT_FUNCTION:
		address.classId = ProcedureRelationId;
		address.objectId = LookupFuncNameTypeNames(objname, objargs, false);
		address.objectSubId = 0;
		break;

	case OBJECT_OPERATOR:
		ASSERT(list_length(objargs) == 2);
		address.classId = OperatorRelationId;
		address.objectId = lookup_opr_name_type_names(
			NULL,
			objname,
			(type_name_n*) linitial(objargs),
			(type_name_n*) lsecond(objargs),
			false,
			-1);
		address.objectSubId = 0;
		break;

	case OBJECT_COLLATION:
		address.classId = CollationRelationId;
		address.objectId = get_collation_oid(objname, false);
		address.objectSubId = 0;
		break;

	case OBJECT_CONVERSION:
		address.classId = ConversionRelationId;
		address.objectId = get_conversion_oid(objname, false);
		address.objectSubId = 0;
		break;

	case OBJECT_OPCLASS:
	case OBJECT_OPFAMILY:
		address = get_objaddr_opcf(objtype, objname, objargs);
		break;

	case OBJECT_LARGEOBJECT:
		ASSERT(list_length(objname) == 1);
		address.classId = LargeObjectRelationId;
		address.objectId = oidparse(linitial(objname));
		address.objectSubId = 0;
		if (!large_obj_exists(address.objectId)) {
			ereport(ERROR, (
			errcode(E_UNDEFINED_OBJECT),
			errmsg("large object %u does not exist",
				address.objectId)));
		}
		break;

	case OBJECT_CAST: {
		type_name_n* sourcetype;
		type_name_n* targettype;
		oid_t sourcetypeid;
		oid_t targettypeid;

		sourcetype = (type_name_n*) linitial(objname);
		targettype = (type_name_n*) linitial(objargs);
		sourcetypeid = typename_to_oid(NULL, sourcetype);
		targettypeid = typename_to_oid(NULL, targettype);
		address.classId = CastRelationId;
		address.objectId = get_cast_oid(sourcetypeid, targettypeid, false);
		address.objectSubId = 0;
		}
		break;

	case OBJECT_TSPARSER:
		address.classId = TSParserRelationId;
		address.objectId = get_ts_parser_oid(objname, false);
		address.objectSubId = 0;
		break;

	case OBJECT_TSDICTIONARY:
		address.classId = TSDictionaryRelationId;
		address.objectId = get_ts_dict_oid(objname, false);
		address.objectSubId = 0;
		break;

	case OBJECT_TSTEMPLATE:
		address.classId = TSTemplateRelationId;
		address.objectId = get_ts_template_oid(objname, false);
		address.objectSubId = 0;
		break;

	case OBJECT_TSCONFIGURATION:
		address.classId = TSConfigRelationId;
		address.objectId = get_ts_config_oid(objname, false);
		address.objectSubId = 0;
		break;

	default:
		elog(ERROR, "unrecognized objtype: %d", (int) objtype);
		/* placate compiler, in case it thinks elog might return */
		address.classId = INVALID_OID;
		address.objectId = INVALID_OID;
		address.objectSubId = 0;
	}

	/*
	 * If we're dealing with a relation or attribute, then the relation is
	 * already locked.	If we're dealing with any other type of object, we
	 * need to lock it and then verify that it still exists.
	 */
	if (address.classId != RelationRelationId) {
		if (is_shared_rel(address.classId))
			lock_sobj(address.classId, address.objectId, 0, lockmode);
		else
			lock_db_obj(address.classId, address.objectId, 0, lockmode);

		/* Did it go away while we were waiting for the lock? */
		if (!object_exists(address)) {
			elog(ERROR,
				"cache lookup failed for class %u object %u subobj %d",
				address.classId,
				address.objectId,
				address.objectSubId);
		}
	}

	/* Return the object address and the relation. */
	*relp = relation;
	return address;
}
Beispiel #6
0
/*
 * Translate an object name and arguments (as passed by the parser) to an
 * ObjectAddress.
 *
 * The returned object will be locked using the specified lockmode.  If a
 * sub-object is looked up, the parent object will be locked instead.
 *
 * If the object is a relation or a child object of a relation (e.g. an
 * attribute or contraint), the relation is also opened and *relp receives
 * the open relcache entry pointer; otherwise, *relp is set to NULL.  This
 * is a bit grotty but it makes life simpler, since the caller will
 * typically need the relcache entry too.  Caller must close the relcache
 * entry when done with it.  The relation is locked with the specified lockmode
 * if the target object is the relation itself or an attribute, but for other
 * child objects, only AccessShareLock is acquired on the relation.
 *
 * We don't currently provide a function to release the locks acquired here;
 * typically, the lock must be held until commit to guard against a concurrent
 * drop operation.
 */
ObjectAddress
get_object_address(ObjectType objtype, List *objname, List *objargs,
				   Relation *relp, LOCKMODE lockmode)
{
	ObjectAddress	address;
	Relation		relation = NULL;

	/* Some kind of lock must be taken. */
	Assert(lockmode != NoLock);

	switch (objtype)
	{
		case OBJECT_INDEX:
		case OBJECT_SEQUENCE:
		case OBJECT_TABLE:
		case OBJECT_VIEW:
		case OBJECT_FOREIGN_TABLE:
			relation =
				get_relation_by_qualified_name(objtype, objname, lockmode);
			address.classId = RelationRelationId;
			address.objectId = RelationGetRelid(relation);
			address.objectSubId = 0;
			break;
		case OBJECT_COLUMN:
			address =
				get_object_address_attribute(objtype, objname, &relation,
					lockmode);
			break;
		case OBJECT_RULE:
		case OBJECT_TRIGGER:
		case OBJECT_CONSTRAINT:
			address = get_object_address_relobject(objtype, objname, &relation);
			break;
		case OBJECT_DATABASE:
		case OBJECT_EXTENSION:
		case OBJECT_TABLESPACE:
		case OBJECT_ROLE:
		case OBJECT_SCHEMA:
		case OBJECT_LANGUAGE:
		case OBJECT_FDW:
		case OBJECT_FOREIGN_SERVER:
			address = get_object_address_unqualified(objtype, objname);
			break;
		case OBJECT_TYPE:
		case OBJECT_DOMAIN:
			address.classId = TypeRelationId;
			address.objectId =
				typenameTypeId(NULL, makeTypeNameFromNameList(objname));
			address.objectSubId = 0;
			break;
		case OBJECT_AGGREGATE:
			address.classId = ProcedureRelationId;
			address.objectId = LookupAggNameTypeNames(objname, objargs, false);
			address.objectSubId = 0;
			break;
		case OBJECT_FUNCTION:
			address.classId = ProcedureRelationId;
			address.objectId = LookupFuncNameTypeNames(objname, objargs, false);
			address.objectSubId = 0;
			break;
		case OBJECT_OPERATOR:
			Assert(list_length(objargs) == 2);
			address.classId = OperatorRelationId;
			address.objectId =
				LookupOperNameTypeNames(NULL, objname,
										(TypeName *) linitial(objargs),
										(TypeName *) lsecond(objargs),
										false, -1);
			address.objectSubId = 0;
			break;
		case OBJECT_COLLATION:
			address.classId = CollationRelationId;
			address.objectId = get_collation_oid(objname, false);
			address.objectSubId = 0;
			break;
		case OBJECT_CONVERSION:
			address.classId = ConversionRelationId;
			address.objectId = get_conversion_oid(objname, false);
			address.objectSubId = 0;
			break;
		case OBJECT_OPCLASS:
		case OBJECT_OPFAMILY:
			address = get_object_address_opcf(objtype, objname, objargs);
			break;
		case OBJECT_LARGEOBJECT:
			Assert(list_length(objname) == 1);
			address.classId = LargeObjectRelationId;
			address.objectId = oidparse(linitial(objname));
			address.objectSubId = 0;
			if (!LargeObjectExists(address.objectId))
				ereport(ERROR,
						(errcode(ERRCODE_UNDEFINED_OBJECT),
						 errmsg("large object %u does not exist",
								address.objectId)));
			break;
		case OBJECT_CAST:
			{
				TypeName *sourcetype = (TypeName *) linitial(objname);
				TypeName *targettype = (TypeName *) linitial(objargs);
				Oid sourcetypeid = typenameTypeId(NULL, sourcetype);
				Oid targettypeid = typenameTypeId(NULL, targettype);

				address.classId = CastRelationId;
				address.objectId =
					get_cast_oid(sourcetypeid, targettypeid, false);
				address.objectSubId = 0;
			}
			break;
		case OBJECT_TSPARSER:
			address.classId = TSParserRelationId;
			address.objectId = get_ts_parser_oid(objname, false);
			address.objectSubId = 0;
			break;
		case OBJECT_TSDICTIONARY:
			address.classId = TSDictionaryRelationId;
			address.objectId = get_ts_dict_oid(objname, false);
			address.objectSubId = 0;
			break;
		case OBJECT_TSTEMPLATE:
			address.classId = TSTemplateRelationId;
			address.objectId = get_ts_template_oid(objname, false);
			address.objectSubId = 0;
			break;
		case OBJECT_TSCONFIGURATION:
			address.classId = TSConfigRelationId;
			address.objectId = get_ts_config_oid(objname, false);
			address.objectSubId = 0;
			break;
		default:
			elog(ERROR, "unrecognized objtype: %d", (int) objtype);
			/* placate compiler, in case it thinks elog might return */
			address.classId = InvalidOid;
			address.objectId = InvalidOid;
			address.objectSubId = 0;
	}

	/*
	 * If we're dealing with a relation or attribute, then the relation is
	 * already locked.  If we're dealing with any other type of object, we need
	 * to lock it and then verify that it still exists.
	 */
	if (address.classId != RelationRelationId)
	{
		if (IsSharedRelation(address.classId))
			LockSharedObject(address.classId, address.objectId, 0, lockmode);
		else
			LockDatabaseObject(address.classId, address.objectId, 0, lockmode);
		/* Did it go away while we were waiting for the lock? */
		if (!object_exists(address))
			elog(ERROR, "cache lookup failed for class %u object %u subobj %d",
				 address.classId, address.objectId, address.objectSubId);
	}

	/* Return the object address and the relation. */
	*relp = relation;
	return address;
}