summaryrefslogtreecommitdiff
path: root/src/backend/catalog
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2007-08-21 01:11:32 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2007-08-21 01:11:32 +0000
commit140d4ebcb46e17cdb1be43892ed797e5e060c8ef (patch)
treef99d209dbe5e40dcb434c3841e0c8b4ff383f453 /src/backend/catalog
parent4e94d1f952c3ce5670ceae3c12b55e344503a701 (diff)
downloadpostgresql-140d4ebcb46e17cdb1be43892ed797e5e060c8ef.tar.gz
Tsearch2 functionality migrates to core. The bulk of this work is by
Oleg Bartunov and Teodor Sigaev, but I did a lot of editorializing, so anything that's broken is probably my fault. Documentation is nonexistent as yet, but let's land the patch so we can get some portability testing done.
Diffstat (limited to 'src/backend/catalog')
-rw-r--r--src/backend/catalog/Makefile4
-rw-r--r--src/backend/catalog/aclchk.c75
-rw-r--r--src/backend/catalog/dependency.c141
-rw-r--r--src/backend/catalog/namespace.c557
-rw-r--r--src/backend/catalog/system_views.sql73
5 files changed, 841 insertions, 9 deletions
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index 3503385c2a..6e87b2f601 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -2,7 +2,7 @@
#
# Makefile for backend/catalog
#
-# $PostgreSQL: pgsql/src/backend/catalog/Makefile,v 1.64 2007/04/02 03:49:37 tgl Exp $
+# $PostgreSQL: pgsql/src/backend/catalog/Makefile,v 1.65 2007/08/21 01:11:13 tgl Exp $
#
#-------------------------------------------------------------------------
@@ -35,6 +35,8 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
pg_enum.h pg_namespace.h pg_conversion.h pg_depend.h \
pg_database.h pg_tablespace.h pg_pltemplate.h \
pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \
+ pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \
+ pg_ts_parser.h pg_ts_template.h \
toasting.h indexing.h \
)
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index 292a737099..96d238c724 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/aclchk.c,v 1.139 2007/04/20 02:37:37 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/aclchk.c,v 1.140 2007/08/21 01:11:13 tgl Exp $
*
* NOTES
* See acl.h.
@@ -34,6 +34,8 @@
#include "catalog/pg_proc.h"
#include "catalog/pg_tablespace.h"
#include "catalog/pg_type.h"
+#include "catalog/pg_ts_config.h"
+#include "catalog/pg_ts_dict.h"
#include "commands/dbcommands.h"
#include "miscadmin.h"
#include "parser/parse_func.h"
@@ -1416,7 +1418,11 @@ static const char *const no_priv_msg[MAX_ACL_KIND] =
/* ACL_KIND_CONVERSION */
gettext_noop("permission denied for conversion %s"),
/* ACL_KIND_TABLESPACE */
- gettext_noop("permission denied for tablespace %s")
+ gettext_noop("permission denied for tablespace %s"),
+ /* ACL_KIND_TSDICTIONARY */
+ gettext_noop("permission denied for text search dictionary %s"),
+ /* ACL_KIND_TSCONFIGURATION */
+ gettext_noop("permission denied for text search configuration %s")
};
static const char *const not_owner_msg[MAX_ACL_KIND] =
@@ -1444,7 +1450,11 @@ static const char *const not_owner_msg[MAX_ACL_KIND] =
/* ACL_KIND_CONVERSION */
gettext_noop("must be owner of conversion %s"),
/* ACL_KIND_TABLESPACE */
- gettext_noop("must be owner of tablespace %s")
+ gettext_noop("must be owner of tablespace %s"),
+ /* ACL_KIND_TSDICTIONARY */
+ gettext_noop("must be owner of text search dictionary %s"),
+ /* ACL_KIND_TSCONFIGURATION */
+ gettext_noop("must be owner of text search configuration %s")
};
@@ -2298,6 +2308,65 @@ pg_opfamily_ownercheck(Oid opf_oid, Oid roleid)
}
/*
+ * Ownership check for a text search dictionary (specified by OID).
+ */
+bool
+pg_ts_dict_ownercheck(Oid dict_oid, Oid roleid)
+{
+ HeapTuple tuple;
+ Oid ownerId;
+
+ /* Superusers bypass all permission checking. */
+ if (superuser_arg(roleid))
+ return true;
+
+ tuple = SearchSysCache(TSDICTOID,
+ ObjectIdGetDatum(dict_oid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("text search dictionary with OID %u does not exist",
+ dict_oid)));
+
+ ownerId = ((Form_pg_ts_dict) GETSTRUCT(tuple))->dictowner;
+
+ ReleaseSysCache(tuple);
+
+ return has_privs_of_role(roleid, ownerId);
+}
+
+/*
+ * Ownership check for a text search configuration (specified by OID).
+ */
+bool
+pg_ts_config_ownercheck(Oid cfg_oid, Oid roleid)
+{
+ HeapTuple tuple;
+ Oid ownerId;
+
+ /* Superusers bypass all permission checking. */
+ if (superuser_arg(roleid))
+ return true;
+
+ tuple = SearchSysCache(TSCONFIGOID,
+ ObjectIdGetDatum(cfg_oid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("text search configuration with OID %u does not exist",
+ cfg_oid)));
+
+ ownerId = ((Form_pg_ts_config) GETSTRUCT(tuple))->cfgowner;
+
+ ReleaseSysCache(tuple);
+
+ return has_privs_of_role(roleid, ownerId);
+}
+
+
+/*
* Ownership check for a database (specified by OID).
*/
bool
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 6b58af6522..51bb4ba17f 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.66 2007/06/05 21:31:04 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.67 2007/08/21 01:11:13 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -40,6 +40,10 @@
#include "catalog/pg_rewrite.h"
#include "catalog/pg_tablespace.h"
#include "catalog/pg_trigger.h"
+#include "catalog/pg_ts_config.h"
+#include "catalog/pg_ts_dict.h"
+#include "catalog/pg_ts_parser.h"
+#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
#include "commands/comment.h"
#include "commands/dbcommands.h"
@@ -97,6 +101,10 @@ static const Oid object_classes[MAX_OCLASS] = {
RewriteRelationId, /* OCLASS_REWRITE */
TriggerRelationId, /* OCLASS_TRIGGER */
NamespaceRelationId, /* OCLASS_SCHEMA */
+ TSParserRelationId, /* OCLASS_TSPARSER */
+ TSDictionaryRelationId, /* OCLASS_TSDICT */
+ TSTemplateRelationId, /* OCLASS_TSTEMPLATE */
+ TSConfigRelationId, /* OCLASS_TSCONFIG */
AuthIdRelationId, /* OCLASS_ROLE */
DatabaseRelationId, /* OCLASS_DATABASE */
TableSpaceRelationId /* OCLASS_TBLSPACE */
@@ -988,6 +996,22 @@ doDeletion(const ObjectAddress *object)
RemoveSchemaById(object->objectId);
break;
+ case OCLASS_TSPARSER:
+ RemoveTSParserById(object->objectId);
+ break;
+
+ case OCLASS_TSDICT:
+ RemoveTSDictionaryById(object->objectId);
+ break;
+
+ case OCLASS_TSTEMPLATE:
+ RemoveTSTemplateById(object->objectId);
+ break;
+
+ case OCLASS_TSCONFIG:
+ RemoveTSConfigurationById(object->objectId);
+ break;
+
/* OCLASS_ROLE, OCLASS_DATABASE, OCLASS_TBLSPACE not handled */
default:
@@ -1201,8 +1225,8 @@ find_expr_references_walker(Node *node,
/*
* If it's a regclass or similar literal referring to an existing
* object, add a reference to that object. (Currently, only the
- * regclass case has any likely use, but we may as well handle all the
- * OID-alias datatypes consistently.)
+ * regclass and regconfig cases have any likely use, but we may as
+ * well handle all the OID-alias datatypes consistently.)
*/
if (!con->constisnull)
{
@@ -1242,6 +1266,22 @@ find_expr_references_walker(Node *node,
add_object_address(OCLASS_TYPE, objoid, 0,
context->addrs);
break;
+ case REGCONFIGOID:
+ objoid = DatumGetObjectId(con->constvalue);
+ if (SearchSysCacheExists(TSCONFIGOID,
+ ObjectIdGetDatum(objoid),
+ 0, 0, 0))
+ add_object_address(OCLASS_TSCONFIG, objoid, 0,
+ context->addrs);
+ break;
+ case REGDICTIONARYOID:
+ objoid = DatumGetObjectId(con->constvalue);
+ if (SearchSysCacheExists(TSDICTOID,
+ ObjectIdGetDatum(objoid),
+ 0, 0, 0))
+ add_object_address(OCLASS_TSDICT, objoid, 0,
+ context->addrs);
+ break;
}
}
return false;
@@ -1606,6 +1646,21 @@ object_address_present(const ObjectAddress *object,
}
/*
+ * Record multiple dependencies from an ObjectAddresses array, after first
+ * removing any duplicates.
+ */
+void
+record_object_address_dependencies(const ObjectAddress *depender,
+ ObjectAddresses *referenced,
+ DependencyType behavior)
+{
+ eliminate_duplicate_dependencies(referenced);
+ recordMultipleDependencies(depender,
+ referenced->refs, referenced->numrefs,
+ behavior);
+}
+
+/*
* Clean up when done with an ObjectAddresses array.
*/
void
@@ -1690,6 +1745,22 @@ getObjectClass(const ObjectAddress *object)
Assert(object->objectSubId == 0);
return OCLASS_SCHEMA;
+ case TSParserRelationId:
+ Assert(object->objectSubId == 0);
+ return OCLASS_TSPARSER;
+
+ case TSDictionaryRelationId:
+ Assert(object->objectSubId == 0);
+ return OCLASS_TSDICT;
+
+ case TSTemplateRelationId:
+ Assert(object->objectSubId == 0);
+ return OCLASS_TSTEMPLATE;
+
+ case TSConfigRelationId:
+ Assert(object->objectSubId == 0);
+ return OCLASS_TSCONFIG;
+
case AuthIdRelationId:
Assert(object->objectSubId == 0);
return OCLASS_ROLE;
@@ -2080,6 +2151,70 @@ getObjectDescription(const ObjectAddress *object)
break;
}
+ case OCLASS_TSPARSER:
+ {
+ HeapTuple tup;
+
+ tup = SearchSysCache(TSPARSEROID,
+ ObjectIdGetDatum(object->objectId),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for text search parser %u",
+ object->objectId);
+ appendStringInfo(&buffer, _("text search parser %s"),
+ NameStr(((Form_pg_ts_parser) GETSTRUCT(tup))->prsname));
+ ReleaseSysCache(tup);
+ break;
+ }
+
+ case OCLASS_TSDICT:
+ {
+ HeapTuple tup;
+
+ tup = SearchSysCache(TSDICTOID,
+ ObjectIdGetDatum(object->objectId),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for text search dictionary %u",
+ object->objectId);
+ appendStringInfo(&buffer, _("text search dictionary %s"),
+ NameStr(((Form_pg_ts_dict) GETSTRUCT(tup))->dictname));
+ ReleaseSysCache(tup);
+ break;
+ }
+
+ case OCLASS_TSTEMPLATE:
+ {
+ HeapTuple tup;
+
+ tup = SearchSysCache(TSTEMPLATEOID,
+ ObjectIdGetDatum(object->objectId),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for text search template %u",
+ object->objectId);
+ appendStringInfo(&buffer, _("text search template %s"),
+ NameStr(((Form_pg_ts_template) GETSTRUCT(tup))->tmplname));
+ ReleaseSysCache(tup);
+ break;
+ }
+
+ case OCLASS_TSCONFIG:
+ {
+ HeapTuple tup;
+
+ tup = SearchSysCache(TSCONFIGOID,
+ ObjectIdGetDatum(object->objectId),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for text search configuration %u",
+ object->objectId);
+ appendStringInfo(&buffer, _("text search configuration %s"),
+ NameStr(((Form_pg_ts_config) GETSTRUCT(tup))->cfgname));
+ ReleaseSysCache(tup);
+ break;
+ }
+
case OCLASS_ROLE:
{
appendStringInfo(&buffer, _("role %s"),
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index 2f8753bd2e..84220bd4ce 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -13,7 +13,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/namespace.c,v 1.97 2007/07/25 22:16:18 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/namespace.c,v 1.98 2007/08/21 01:11:13 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -29,6 +29,10 @@
#include "catalog/pg_operator.h"
#include "catalog/pg_opfamily.h"
#include "catalog/pg_proc.h"
+#include "catalog/pg_ts_config.h"
+#include "catalog/pg_ts_dict.h"
+#include "catalog/pg_ts_parser.h"
+#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
#include "commands/dbcommands.h"
#include "miscadmin.h"
@@ -189,6 +193,10 @@ Datum pg_function_is_visible(PG_FUNCTION_ARGS);
Datum pg_operator_is_visible(PG_FUNCTION_ARGS);
Datum pg_opclass_is_visible(PG_FUNCTION_ARGS);
Datum pg_conversion_is_visible(PG_FUNCTION_ARGS);
+Datum pg_ts_parser_is_visible(PG_FUNCTION_ARGS);
+Datum pg_ts_dict_is_visible(PG_FUNCTION_ARGS);
+Datum pg_ts_template_is_visible(PG_FUNCTION_ARGS);
+Datum pg_ts_config_is_visible(PG_FUNCTION_ARGS);
Datum pg_my_temp_schema(PG_FUNCTION_ARGS);
Datum pg_is_other_temp_schema(PG_FUNCTION_ARGS);
@@ -1315,6 +1323,521 @@ ConversionIsVisible(Oid conid)
}
/*
+ * TSParserGetPrsid - find a TS parser by possibly qualified name
+ *
+ * If not found, returns InvalidOid if failOK, else throws error
+ */
+Oid
+TSParserGetPrsid(List *names, bool failOK)
+{
+ char *schemaname;
+ char *parser_name;
+ Oid namespaceId;
+ Oid prsoid = InvalidOid;
+ ListCell *l;
+
+ /* deconstruct the name list */
+ DeconstructQualifiedName(names, &schemaname, &parser_name);
+
+ if (schemaname)
+ {
+ /* use exact schema given */
+ namespaceId = LookupExplicitNamespace(schemaname);
+ prsoid = GetSysCacheOid(TSPARSERNAMENSP,
+ PointerGetDatum(parser_name),
+ ObjectIdGetDatum(namespaceId),
+ 0, 0);
+ }
+ else
+ {
+ /* search for it in search path */
+ recomputeNamespacePath();
+
+ foreach(l, activeSearchPath)
+ {
+ namespaceId = lfirst_oid(l);
+
+ if (namespaceId == myTempNamespace)
+ continue; /* do not look in temp namespace */
+
+ prsoid = GetSysCacheOid(TSPARSERNAMENSP,
+ PointerGetDatum(parser_name),
+ ObjectIdGetDatum(namespaceId),
+ 0, 0);
+ if (OidIsValid(prsoid))
+ break;
+ }
+ }
+
+ if (!OidIsValid(prsoid) && !failOK)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("text search parser \"%s\" does not exist",
+ NameListToString(names))));
+
+ return prsoid;
+}
+
+/*
+ * TSParserIsVisible
+ * Determine whether a parser (identified by OID) is visible in the
+ * current search path. Visible means "would be found by searching
+ * for the unqualified parser name".
+ */
+bool
+TSParserIsVisible(Oid prsId)
+{
+ HeapTuple tup;
+ Form_pg_ts_parser form;
+ Oid namespace;
+ bool visible;
+
+ tup = SearchSysCache(TSPARSEROID,
+ ObjectIdGetDatum(prsId),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for text search parser %u", prsId);
+ form = (Form_pg_ts_parser) GETSTRUCT(tup);
+
+ recomputeNamespacePath();
+
+ /*
+ * Quick check: if it ain't in the path at all, it ain't visible. Items in
+ * the system namespace are surely in the path and so we needn't even do
+ * list_member_oid() for them.
+ */
+ namespace = form->prsnamespace;
+ if (namespace != PG_CATALOG_NAMESPACE &&
+ !list_member_oid(activeSearchPath, namespace))
+ visible = false;
+ else
+ {
+ /*
+ * If it is in the path, it might still not be visible; it could be
+ * hidden by another parser of the same name earlier in the path. So we
+ * must do a slow check for conflicting parsers.
+ */
+ char *name = NameStr(form->prsname);
+ ListCell *l;
+
+ visible = false;
+ foreach(l, activeSearchPath)
+ {
+ Oid namespaceId = lfirst_oid(l);
+
+ if (namespaceId == myTempNamespace)
+ continue; /* do not look in temp namespace */
+
+ if (namespaceId == namespace)
+ {
+ /* Found it first in path */
+ visible = true;
+ break;
+ }
+ if (SearchSysCacheExists(TSPARSERNAMENSP,
+ PointerGetDatum(name),
+ ObjectIdGetDatum(namespaceId),
+ 0, 0))
+ {
+ /* Found something else first in path */
+ break;
+ }
+ }
+ }
+
+ ReleaseSysCache(tup);
+
+ return visible;
+}
+
+/*
+ * TSDictionaryGetDictid - find a TS dictionary by possibly qualified name
+ *
+ * If not found, returns InvalidOid if failOK, else throws error
+ */
+Oid
+TSDictionaryGetDictid(List *names, bool failOK)
+{
+ char *schemaname;
+ char *dict_name;
+ Oid namespaceId;
+ Oid dictoid = InvalidOid;
+ ListCell *l;
+
+ /* deconstruct the name list */
+ DeconstructQualifiedName(names, &schemaname, &dict_name);
+
+ if (schemaname)
+ {
+ /* use exact schema given */
+ namespaceId = LookupExplicitNamespace(schemaname);
+ dictoid = GetSysCacheOid(TSDICTNAMENSP,
+ PointerGetDatum(dict_name),
+ ObjectIdGetDatum(namespaceId),
+ 0, 0);
+ }
+ else
+ {
+ /* search for it in search path */
+ recomputeNamespacePath();
+
+ foreach(l, activeSearchPath)
+ {
+ namespaceId = lfirst_oid(l);
+
+ if (namespaceId == myTempNamespace)
+ continue; /* do not look in temp namespace */
+
+ dictoid = GetSysCacheOid(TSDICTNAMENSP,
+ PointerGetDatum(dict_name),
+ ObjectIdGetDatum(namespaceId),
+ 0, 0);
+ if (OidIsValid(dictoid))
+ break;
+ }
+ }
+
+ if (!OidIsValid(dictoid) && !failOK)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("text search dictionary \"%s\" does not exist",
+ NameListToString(names))));
+
+ return dictoid;
+}
+
+/*
+ * TSDictionaryIsVisible
+ * Determine whether a dictionary (identified by OID) is visible in the
+ * current search path. Visible means "would be found by searching
+ * for the unqualified dictionary name".
+ */
+bool
+TSDictionaryIsVisible(Oid dictId)
+{
+ HeapTuple tup;
+ Form_pg_ts_dict form;
+ Oid namespace;
+ bool visible;
+
+ tup = SearchSysCache(TSDICTOID,
+ ObjectIdGetDatum(dictId),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for text search dictionary %u",
+ dictId);
+ form = (Form_pg_ts_dict) GETSTRUCT(tup);
+
+ recomputeNamespacePath();
+
+ /*
+ * Quick check: if it ain't in the path at all, it ain't visible. Items in
+ * the system namespace are surely in the path and so we needn't even do
+ * list_member_oid() for them.
+ */
+ namespace = form->dictnamespace;
+ if (namespace != PG_CATALOG_NAMESPACE &&
+ !list_member_oid(activeSearchPath, namespace))
+ visible = false;
+ else
+ {
+ /*
+ * If it is in the path, it might still not be visible; it could be
+ * hidden by another dictionary of the same name earlier in the
+ * path. So we must do a slow check for conflicting dictionaries.
+ */
+ char *name = NameStr(form->dictname);
+ ListCell *l;
+
+ visible = false;
+ foreach(l, activeSearchPath)
+ {
+ Oid namespaceId = lfirst_oid(l);
+
+ if (namespaceId == myTempNamespace)
+ continue; /* do not look in temp namespace */
+
+ if (namespaceId == namespace)
+ {
+ /* Found it first in path */
+ visible = true;
+ break;
+ }
+ if (SearchSysCacheExists(TSDICTNAMENSP,
+ PointerGetDatum(name),
+ ObjectIdGetDatum(namespaceId),
+ 0, 0))
+ {
+ /* Found something else first in path */
+ break;
+ }
+ }
+ }
+
+ ReleaseSysCache(tup);
+
+ return visible;
+}
+
+/*
+ * TSTemplateGetTmplid - find a TS template by possibly qualified name
+ *
+ * If not found, returns InvalidOid if failOK, else throws error
+ */
+Oid
+TSTemplateGetTmplid(List *names, bool failOK)
+{
+ char *schemaname;
+ char *template_name;
+ Oid namespaceId;
+ Oid tmploid = InvalidOid;
+ ListCell *l;
+
+ /* deconstruct the name list */
+ DeconstructQualifiedName(names, &schemaname, &template_name);
+
+ if (schemaname)
+ {
+ /* use exact schema given */
+ namespaceId = LookupExplicitNamespace(schemaname);
+ tmploid = GetSysCacheOid(TSTEMPLATENAMENSP,
+ PointerGetDatum(template_name),
+ ObjectIdGetDatum(namespaceId),
+ 0, 0);
+ }
+ else
+ {
+ /* search for it in search path */
+ recomputeNamespacePath();
+
+ foreach(l, activeSearchPath)
+ {
+ namespaceId = lfirst_oid(l);
+
+ if (namespaceId == myTempNamespace)
+ continue; /* do not look in temp namespace */
+
+ tmploid = GetSysCacheOid(TSTEMPLATENAMENSP,
+ PointerGetDatum(template_name),
+ ObjectIdGetDatum(namespaceId),
+ 0, 0);
+ if (OidIsValid(tmploid))
+ break;
+ }
+ }
+
+ if (!OidIsValid(tmploid) && !failOK)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("text search template \"%s\" does not exist",
+ NameListToString(names))));
+
+ return tmploid;
+}
+
+/*
+ * TSTemplateIsVisible
+ * Determine whether a template (identified by OID) is visible in the
+ * current search path. Visible means "would be found by searching
+ * for the unqualified template name".
+ */
+bool
+TSTemplateIsVisible(Oid tmplId)
+{
+ HeapTuple tup;
+ Form_pg_ts_template form;
+ Oid namespace;
+ bool visible;
+
+ tup = SearchSysCache(TSTEMPLATEOID,
+ ObjectIdGetDatum(tmplId),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for text search template %u", tmplId);
+ form = (Form_pg_ts_template) GETSTRUCT(tup);
+
+ recomputeNamespacePath();
+
+ /*
+ * Quick check: if it ain't in the path at all, it ain't visible. Items in
+ * the system namespace are surely in the path and so we needn't even do
+ * list_member_oid() for them.
+ */
+ namespace = form->tmplnamespace;
+ if (namespace != PG_CATALOG_NAMESPACE &&
+ !list_member_oid(activeSearchPath, namespace))
+ visible = false;
+ else
+ {
+ /*
+ * If it is in the path, it might still not be visible; it could be
+ * hidden by another template of the same name earlier in the path.
+ * So we must do a slow check for conflicting templates.
+ */
+ char *name = NameStr(form->tmplname);
+ ListCell *l;
+
+ visible = false;
+ foreach(l, activeSearchPath)
+ {
+ Oid namespaceId = lfirst_oid(l);
+
+ if (namespaceId == myTempNamespace)
+ continue; /* do not look in temp namespace */
+
+ if (namespaceId == namespace)
+ {
+ /* Found it first in path */
+ visible = true;
+ break;
+ }
+ if (SearchSysCacheExists(TSTEMPLATENAMENSP,
+ PointerGetDatum(name),
+ ObjectIdGetDatum(namespaceId),
+ 0, 0))
+ {
+ /* Found something else first in path */
+ break;
+ }
+ }
+ }
+
+ ReleaseSysCache(tup);
+
+ return visible;
+}
+
+/*
+ * TSConfigGetCfgid - find a TS config by possibly qualified name
+ *
+ * If not found, returns InvalidOid if failOK, else throws error
+ */
+Oid
+TSConfigGetCfgid(List *names, bool failOK)
+{
+ char *schemaname;
+ char *config_name;
+ Oid namespaceId;
+ Oid cfgoid = InvalidOid;
+ ListCell *l;
+
+ /* deconstruct the name list */
+ DeconstructQualifiedName(names, &schemaname, &config_name);
+
+ if (schemaname)
+ {
+ /* use exact schema given */
+ namespaceId = LookupExplicitNamespace(schemaname);
+ cfgoid = GetSysCacheOid(TSCONFIGNAMENSP,
+ PointerGetDatum(config_name),
+ ObjectIdGetDatum(namespaceId),
+ 0, 0);
+ }
+ else
+ {
+ /* search for it in search path */
+ recomputeNamespacePath();
+
+ foreach(l, activeSearchPath)
+ {
+ namespaceId = lfirst_oid(l);
+
+ if (namespaceId == myTempNamespace)
+ continue; /* do not look in temp namespace */
+
+ cfgoid = GetSysCacheOid(TSCONFIGNAMENSP,
+ PointerGetDatum(config_name),
+ ObjectIdGetDatum(namespaceId),
+ 0, 0);
+ if (OidIsValid(cfgoid))
+ break;
+ }
+ }
+
+ if (!OidIsValid(cfgoid) && !failOK)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("text search configuration \"%s\" does not exist",
+ NameListToString(names))));
+
+ return cfgoid;
+}
+
+/*
+ * TSConfigIsVisible
+ * Determine whether a text search configuration (identified by OID)
+ * is visible in the current search path. Visible means "would be found
+ * by searching for the unqualified text search configuration name".
+ */
+bool
+TSConfigIsVisible(Oid cfgid)
+{
+ HeapTuple tup;
+ Form_pg_ts_config form;
+ Oid namespace;
+ bool visible;
+
+ tup = SearchSysCache(TSCONFIGOID,
+ ObjectIdGetDatum(cfgid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for text search configuration %u",
+ cfgid);
+ form = (Form_pg_ts_config) GETSTRUCT(tup);
+
+ recomputeNamespacePath();
+
+ /*
+ * Quick check: if it ain't in the path at all, it ain't visible. Items in
+ * the system namespace are surely in the path and so we needn't even do
+ * list_member_oid() for them.
+ */
+ namespace = form->cfgnamespace;
+ if (namespace != PG_CATALOG_NAMESPACE &&
+ !list_member_oid(activeSearchPath, namespace))
+ visible = false;
+ else
+ {
+ /*
+ * If it is in the path, it might still not be visible; it could be
+ * hidden by another configuration of the same name earlier in the
+ * path. So we must do a slow check for conflicting configurations.
+ */
+ char *name = NameStr(form->cfgname);
+ ListCell *l;
+
+ visible = false;
+ foreach(l, activeSearchPath)
+ {
+ Oid namespaceId = lfirst_oid(l);
+
+ if (namespaceId == myTempNamespace)
+ continue; /* do not look in temp namespace */
+
+ if (namespaceId == namespace)
+ {
+ /* Found it first in path */
+ visible = true;
+ break;
+ }
+ if (SearchSysCacheExists(TSCONFIGNAMENSP,
+ PointerGetDatum(name),
+ ObjectIdGetDatum(namespaceId),
+ 0, 0))
+ {
+ /* Found something else first in path */
+ break;
+ }
+ }
+ }
+
+ ReleaseSysCache(tup);
+
+ return visible;
+}
+
+
+/*
* DeconstructQualifiedName
* Given a possibly-qualified name expressed as a list of String nodes,
* extract the schema name and object name.
@@ -2516,6 +3039,38 @@ pg_conversion_is_visible(PG_FUNCTION_ARGS)
}
Datum
+pg_ts_parser_is_visible(PG_FUNCTION_ARGS)
+{
+ Oid oid = PG_GETARG_OID(0);
+
+ PG_RETURN_BOOL(TSParserIsVisible(oid));
+}
+
+Datum
+pg_ts_dict_is_visible(PG_FUNCTION_ARGS)
+{
+ Oid oid = PG_GETARG_OID(0);
+
+ PG_RETURN_BOOL(TSDictionaryIsVisible(oid));
+}
+
+Datum
+pg_ts_template_is_visible(PG_FUNCTION_ARGS)
+{
+ Oid oid = PG_GETARG_OID(0);
+
+ PG_RETURN_BOOL(TSTemplateIsVisible(oid));
+}
+
+Datum
+pg_ts_config_is_visible(PG_FUNCTION_ARGS)
+{
+ Oid oid = PG_GETARG_OID(0);
+
+ PG_RETURN_BOOL(TSConfigIsVisible(oid));
+}
+
+Datum
pg_my_temp_schema(PG_FUNCTION_ARGS)
{
PG_RETURN_OID(myTempNamespace);
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index c091c85570..756809b8ae 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -3,7 +3,7 @@
*
* Copyright (c) 1996-2007, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/backend/catalog/system_views.sql,v 1.39 2007/07/25 22:16:18 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/system_views.sql,v 1.40 2007/08/21 01:11:13 tgl Exp $
*/
CREATE VIEW pg_roles AS
@@ -382,3 +382,74 @@ CREATE VIEW pg_stat_bgwriter AS
pg_stat_get_bgwriter_buf_written_checkpoints() AS buffers_checkpoint,
pg_stat_get_bgwriter_buf_written_clean() AS buffers_clean,
pg_stat_get_bgwriter_maxwritten_clean() AS maxwritten_clean;
+
+-- Tsearch debug function. Defined here because it'd be pretty unwieldy
+-- to put it into pg_proc.h
+
+CREATE TYPE ts_debug AS (
+ "Alias" text,
+ "Description" text,
+ "Token" text,
+ "Dictionaries" regdictionary[],
+ "Lexized token" text
+);
+
+COMMENT ON TYPE ts_debug IS 'returned type from ts_debug() function';
+
+CREATE FUNCTION ts_debug(regconfig, text)
+RETURNS SETOF ts_debug AS
+$$
+SELECT
+ (
+ SELECT
+ tt.alias
+ FROM
+ pg_catalog.ts_token_type(
+ (SELECT cfgparser FROM pg_catalog.pg_ts_config WHERE oid = $1 )
+ ) AS tt
+ WHERE
+ tt.tokid = parse.tokid
+ ) AS "Alias",
+ (
+ SELECT
+ tt.description
+ FROM
+ pg_catalog.ts_token_type(
+ (SELECT cfgparser FROM pg_catalog.pg_ts_config WHERE oid = $1 )
+ ) AS tt
+ WHERE
+ tt.tokid = parse.tokid
+ ) AS "Description",
+ parse.token AS "Token",
+ ARRAY ( SELECT m.mapdict::pg_catalog.regdictionary
+ FROM pg_catalog.pg_ts_config_map AS m
+ WHERE m.mapcfg = $1 AND m.maptokentype = parse.tokid
+ ORDER BY m.mapcfg, m.maptokentype, m.mapseqno )
+ AS "Dictionaries",
+ (
+ SELECT
+ dl.mapdict::pg_catalog.regdictionary || ': ' || dl.lex::pg_catalog.text
+ FROM
+ ( SELECT mapdict, pg_catalog.ts_lexize(mapdict, parse.token) AS lex
+ FROM pg_catalog.pg_ts_config_map AS m
+ WHERE m.mapcfg = $1 AND m.maptokentype = parse.tokid
+ ORDER BY m.mapcfg, m.maptokentype, m.mapseqno ) dl
+ WHERE dl.lex IS NOT NULL
+ LIMIT 1
+ ) AS "Lexized token"
+FROM pg_catalog.ts_parse(
+ (SELECT cfgparser FROM pg_catalog.pg_ts_config WHERE oid = $1 ), $2
+ ) AS parse;
+$$
+LANGUAGE SQL RETURNS NULL ON NULL INPUT;
+
+COMMENT ON FUNCTION ts_debug(regconfig,text) IS 'debug function for text search configuration';
+
+CREATE FUNCTION ts_debug(text)
+RETURNS SETOF ts_debug AS
+$$
+ SELECT * FROM pg_catalog.ts_debug( pg_catalog.get_current_ts_config(), $1 );
+$$
+LANGUAGE SQL RETURNS NULL ON NULL INPUT;
+
+COMMENT ON FUNCTION ts_debug(text) IS 'debug function for current text search configuration';