summaryrefslogtreecommitdiff
path: root/src/bin
diff options
context:
space:
mode:
authorTom Lane2007-08-21 01:11:32 +0000
committerTom Lane2007-08-21 01:11:32 +0000
commit140d4ebcb46e17cdb1be43892ed797e5e060c8ef (patch)
treef99d209dbe5e40dcb434c3841e0c8b4ff383f453 /src/bin
parent4e94d1f952c3ce5670ceae3c12b55e344503a701 (diff)
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/bin')
-rw-r--r--src/bin/initdb/initdb.c188
-rw-r--r--src/bin/pg_dump/common.c26
-rw-r--r--src/bin/pg_dump/pg_backup_archiver.c10
-rw-r--r--src/bin/pg_dump/pg_dump.c722
-rw-r--r--src/bin/pg_dump/pg_dump.h41
-rw-r--r--src/bin/pg_dump/pg_dump_sort.c30
-rw-r--r--src/bin/psql/command.c23
-rw-r--r--src/bin/psql/describe.c520
-rw-r--r--src/bin/psql/describe.h14
-rw-r--r--src/bin/psql/help.c6
10 files changed, 1565 insertions, 15 deletions
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 4560e77a636..15ac4b7cb2d 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -42,7 +42,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
* Portions taken from FreeBSD.
*
- * $PostgreSQL: pgsql/src/bin/initdb/initdb.c,v 1.139 2007/08/04 21:01:09 neilc Exp $
+ * $PostgreSQL: pgsql/src/bin/initdb/initdb.c,v 1.140 2007/08/21 01:11:20 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -88,6 +88,7 @@ static char *lc_monetary = "";
static char *lc_numeric = "";
static char *lc_time = "";
static char *lc_messages = "";
+static const char *default_text_search_config = "";
static char *username = "";
static bool pwprompt = false;
static char *pwfilename = NULL;
@@ -108,6 +109,7 @@ static char *hba_file;
static char *ident_file;
static char *conf_file;
static char *conversion_file;
+static char *dictionary_file;
static char *info_schema_file;
static char *features_file;
static char *system_views_file;
@@ -181,6 +183,7 @@ static void setup_depend(void);
static void setup_sysviews(void);
static void setup_description(void);
static void setup_conversion(void);
+static void setup_dictionary(void);
static void setup_privileges(void);
static void set_info_version(void);
static void setup_schema(void);
@@ -729,7 +732,7 @@ struct encoding_match
const char *system_enc_name;
};
-struct encoding_match encoding_match_list[] = {
+static const struct encoding_match encoding_match_list[] = {
{PG_EUC_JP, "EUC-JP"},
{PG_EUC_JP, "eucJP"},
{PG_EUC_JP, "IBM-eucJP"},
@@ -907,6 +910,94 @@ find_matching_encoding(const char *ctype)
}
#endif /* HAVE_LANGINFO_H && CODESET */
+
+/*
+ * Support for determining the best default text search configuration.
+ * We key this off LC_CTYPE, after stripping its encoding indicator if any.
+ */
+struct tsearch_config_match
+{
+ const char *tsconfname;
+ const char *langname;
+};
+
+static const struct tsearch_config_match tsearch_config_languages[] =
+{
+ {"danish", "da_DK"},
+ {"danish", "Danish_Danmark"},
+ {"dutch", "nl_NL"},
+ {"dutch", "Dutch_Netherlands"},
+ {"english", "C"},
+ {"english", "POSIX"},
+ {"english", "en_US"},
+ {"english", "English_America"},
+ {"english", "en_UK"},
+ {"english", "English_Britain"},
+ {"finnish", "fi_FI"},
+ {"finnish", "Finnish_Finland"},
+ {"french", "fr_FR"},
+ {"french", "French_France"},
+ {"german", "de_DE"},
+ {"german", "German_Germany"},
+ {"hungarian", "hu_HU"},
+ {"hungarian", "Hungarian_Hungary"},
+ {"italian", "it_IT"},
+ {"italian", "Italian_Italy"},
+ {"norwegian", "no_NO"},
+ {"norwegian", "Norwegian_Norway"},
+ {"portuguese", "pt_PT"},
+ {"portuguese", "Portuguese_Portugal"},
+ {"romanian", "ro_RO"},
+ {"russian", "ru_RU"},
+ {"russian", "Russian_Russia"},
+ {"spanish", "es_ES"},
+ {"spanish", "Spanish_Spain"},
+ {"swedish", "sv_SE"},
+ {"swedish", "Swedish_Sweden"},
+ {"turkish", "tr_TR"},
+ {"turkish", "Turkish_Turkey"},
+ {NULL, NULL} /* end marker */
+};
+
+/*
+ * Look for a text search configuration matching lc_ctype, and return its
+ * name; return NULL if no match.
+ */
+static const char *
+find_matching_ts_config(const char *lc_type)
+{
+ int i;
+ char *langname,
+ *ptr;
+
+ /*
+ * Convert lc_ctype to a language name by stripping ".utf8" or
+ * what-have-you
+ */
+ if (lc_type == NULL)
+ langname = xstrdup("");
+ else
+ {
+ ptr = langname = xstrdup(lc_type);
+ while (*ptr && *ptr != '.')
+ ptr++;
+ *ptr = '\0';
+ }
+
+ for (i = 0; tsearch_config_languages[i].tsconfname; i++)
+ {
+ if (pg_strcasecmp(tsearch_config_languages[i].langname, langname) == 0)
+ {
+ free(langname);
+ return tsearch_config_languages[i].tsconfname;
+ }
+ }
+
+ free(langname);
+ return NULL;
+}
+
+
/*
* get short version of VERSION
*/
@@ -1305,6 +1396,13 @@ setup_config(void)
}
conflines = replace_token(conflines, "#datestyle = 'iso, mdy'", repltok);
+ snprintf(repltok, sizeof(repltok),
+ "default_text_search_config = 'pg_catalog.%s'",
+ escape_quotes(default_text_search_config));
+ conflines = replace_token(conflines,
+ "#default_text_search_config = 'pg_catalog.simple'",
+ repltok);
+
snprintf(path, sizeof(path), "%s/postgresql.conf", pg_data);
writefile(path, conflines);
@@ -1679,6 +1777,14 @@ setup_depend(void)
" FROM pg_namespace "
" WHERE nspname LIKE 'pg%';\n",
+ "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
+ " FROM pg_ts_parser;\n",
+ "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
+ " FROM pg_ts_dict;\n",
+ "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
+ " FROM pg_ts_template;\n",
+ "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
+ " FROM pg_ts_config;\n",
"INSERT INTO pg_shdepend SELECT 0, 0, 0, tableoid, oid, 'p' "
" FROM pg_authid;\n",
NULL
@@ -1826,6 +1932,43 @@ setup_conversion(void)
}
/*
+ * load extra dictionaries (Snowball stemmers)
+ */
+static void
+setup_dictionary(void)
+{
+ PG_CMD_DECL;
+ char **line;
+ char **conv_lines;
+
+ fputs(_("creating dictionaries ... "), stdout);
+ fflush(stdout);
+
+ /*
+ * We use -j here to avoid backslashing stuff
+ */
+ snprintf(cmd, sizeof(cmd),
+ "\"%s\" %s -j template1 >%s",
+ backend_exec, backend_options,
+ DEVNULL);
+
+ PG_CMD_OPEN;
+
+ conv_lines = readfile(dictionary_file);
+ for (line = conv_lines; *line != NULL; line++)
+ {
+ PG_CMD_PUTS(*line);
+ free(*line);
+ }
+
+ free(conv_lines);
+
+ PG_CMD_CLOSE;
+
+ check_ok();
+}
+
+/*
* Set up privileges
*
* We mark most system catalogs as world-readable. We don't currently have
@@ -2403,6 +2546,8 @@ usage(const char *progname)
" in the respective category (default taken from\n"
" environment)\n"));
printf(_(" --no-locale equivalent to --locale=C\n"));
+ printf(_(" -T, --text-search-config=CFG\n"));
+ printf(_(" set default text search configuration\n"));
printf(_(" -X, --xlogdir=XLOGDIR location for the transaction log directory\n"));
printf(_(" -A, --auth=METHOD default authentication method for local connections\n"));
printf(_(" -U, --username=NAME database superuser name\n"));
@@ -2438,6 +2583,7 @@ main(int argc, char *argv[])
{"lc-time", required_argument, NULL, 6},
{"lc-messages", required_argument, NULL, 7},
{"no-locale", no_argument, NULL, 8},
+ {"text-search-config", required_argument, NULL, 'T'},
{"auth", required_argument, NULL, 'A'},
{"pwprompt", no_argument, NULL, 'W'},
{"pwfile", required_argument, NULL, 9},
@@ -2498,7 +2644,7 @@ main(int argc, char *argv[])
/* process command-line options */
- while ((c = getopt_long(argc, argv, "dD:E:L:nU:WA:sX:", long_options, &option_index)) != -1)
+ while ((c = getopt_long(argc, argv, "dD:E:L:nU:WA:sT:X:", long_options, &option_index)) != -1)
{
switch (c)
{
@@ -2558,6 +2704,9 @@ main(int argc, char *argv[])
case 's':
show_setting = true;
break;
+ case 'T':
+ default_text_search_config = xstrdup(optarg);
+ break;
case 'X':
xlog_dir = xstrdup(optarg);
break;
@@ -2771,6 +2920,7 @@ main(int argc, char *argv[])
set_input(&ident_file, "pg_ident.conf.sample");
set_input(&conf_file, "postgresql.conf.sample");
set_input(&conversion_file, "conversion_create.sql");
+ set_input(&dictionary_file, "snowball_create.sql");
set_input(&info_schema_file, "information_schema.sql");
set_input(&features_file, "sql_features.txt");
set_input(&system_views_file, "system_views.sql");
@@ -2803,6 +2953,7 @@ main(int argc, char *argv[])
check_input(ident_file);
check_input(conf_file);
check_input(conversion_file);
+ check_input(dictionary_file);
check_input(info_schema_file);
check_input(features_file);
check_input(system_views_file);
@@ -2864,6 +3015,35 @@ main(int argc, char *argv[])
}
#endif /* HAVE_LANGINFO_H && CODESET */
+ if (strlen(default_text_search_config) == 0)
+ {
+ default_text_search_config = find_matching_ts_config(lc_ctype);
+ if (default_text_search_config == NULL)
+ {
+ printf(_("%s: could not find suitable text search configuration for locale \"%s\"\n"),
+ progname, lc_ctype);
+ default_text_search_config = "simple";
+ }
+ }
+ else
+ {
+ const char *checkmatch = find_matching_ts_config(lc_ctype);
+
+ if (checkmatch == NULL)
+ {
+ printf(_("%s: warning: suitable text search configuration for locale \"%s\" is unknown\n"),
+ progname, lc_ctype);
+ }
+ else if (strcmp(checkmatch, default_text_search_config) != 0)
+ {
+ printf(_("%s: warning: specified text search configuration \"%s\" may not match locale \"%s\"\n"),
+ progname, default_text_search_config, lc_ctype);
+ }
+ }
+
+ printf(_("The default text search configuration will be set to \"%s\".\n"),
+ default_text_search_config);
+
printf("\n");
umask(077);
@@ -3062,6 +3242,8 @@ main(int argc, char *argv[])
setup_conversion();
+ setup_dictionary();
+
setup_privileges();
setup_schema();
diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
index 212c952d2c6..53d15ac970d 100644
--- a/src/bin/pg_dump/common.c
+++ b/src/bin/pg_dump/common.c
@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/bin/pg_dump/common.c,v 1.96 2007/01/23 17:54:50 tgl Exp $
+ * $PostgreSQL: pgsql/src/bin/pg_dump/common.c,v 1.97 2007/08/21 01:11:20 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -79,6 +79,10 @@ getSchemaData(int *numTablesPtr)
OpclassInfo *opcinfo;
OpfamilyInfo *opfinfo;
ConvInfo *convinfo;
+ TSParserInfo *prsinfo;
+ TSTemplateInfo *tmplinfo;
+ TSDictInfo *dictinfo;
+ TSConfigInfo *cfginfo;
int numNamespaces;
int numAggregates;
int numInherits;
@@ -88,6 +92,10 @@ getSchemaData(int *numTablesPtr)
int numOpclasses;
int numOpfamilies;
int numConversions;
+ int numTSParsers;
+ int numTSTemplates;
+ int numTSDicts;
+ int numTSConfigs;
if (g_verbose)
write_msg(NULL, "reading schemas\n");
@@ -120,6 +128,22 @@ getSchemaData(int *numTablesPtr)
opcinfo = getOpclasses(&numOpclasses);
if (g_verbose)
+ write_msg(NULL, "reading user-defined text search parsers\n");
+ prsinfo = getTSParsers(&numTSParsers);
+
+ if (g_verbose)
+ write_msg(NULL, "reading user-defined text search templates\n");
+ tmplinfo = getTSTemplates(&numTSTemplates);
+
+ if (g_verbose)
+ write_msg(NULL, "reading user-defined text search dictionaries\n");
+ dictinfo = getTSDictionaries(&numTSDicts);
+
+ if (g_verbose)
+ write_msg(NULL, "reading user-defined text search configurations\n");
+ cfginfo = getTSConfigurations(&numTSConfigs);
+
+ if (g_verbose)
write_msg(NULL, "reading user-defined operator families\n");
opfinfo = getOpfamilies(&numOpfamilies);
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index c0e192e859b..01439a18073 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -15,7 +15,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_archiver.c,v 1.145 2007/08/06 01:38:14 tgl Exp $
+ * $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_archiver.c,v 1.146 2007/08/21 01:11:20 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -2419,7 +2419,9 @@ _getObjectDescription(PQExpBuffer buf, TocEntry *te, ArchiveHandle *AH)
if (strcmp(type, "CONVERSION") == 0 ||
strcmp(type, "DOMAIN") == 0 ||
strcmp(type, "TABLE") == 0 ||
- strcmp(type, "TYPE") == 0)
+ strcmp(type, "TYPE") == 0 ||
+ strcmp(type, "TEXT SEARCH DICTIONARY") == 0 ||
+ strcmp(type, "TEXT SEARCH CONFIGURATION") == 0)
{
appendPQExpBuffer(buf, "%s ", type);
if (te->namespace && te->namespace[0]) /* is null pre-7.3 */
@@ -2591,7 +2593,9 @@ _printTocEntry(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt, bool isDat
strcmp(te->desc, "TABLE") == 0 ||
strcmp(te->desc, "TYPE") == 0 ||
strcmp(te->desc, "VIEW") == 0 ||
- strcmp(te->desc, "SEQUENCE") == 0)
+ strcmp(te->desc, "SEQUENCE") == 0 ||
+ strcmp(te->desc, "TEXT SEARCH DICTIONARY") == 0 ||
+ strcmp(te->desc, "TEXT SEARCH CONFIGURATION") == 0)
{
PQExpBuffer temp = createPQExpBuffer();
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index ee7b6bca790..4d4d7f7986e 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -12,7 +12,7 @@
* by PostgreSQL
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.469 2007/07/12 23:25:26 neilc Exp $
+ * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.470 2007/08/21 01:11:21 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -158,6 +158,10 @@ static void dumpSequence(Archive *fout, TableInfo *tbinfo);
static void dumpIndex(Archive *fout, IndxInfo *indxinfo);
static void dumpConstraint(Archive *fout, ConstraintInfo *coninfo);
static void dumpTableConstraintComment(Archive *fout, ConstraintInfo *coninfo);
+static void dumpTSParser(Archive *fout, TSParserInfo *prsinfo);
+static void dumpTSDictionary(Archive *fout, TSDictInfo *dictinfo);
+static void dumpTSTemplate(Archive *fout, TSTemplateInfo *tmplinfo);
+static void dumpTSConfig(Archive *fout, TSConfigInfo *cfginfo);
static void dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId,
const char *type, const char *name,
@@ -174,6 +178,7 @@ static char *format_function_arguments(FuncInfo *finfo, int nallargs,
static char *format_function_signature(FuncInfo *finfo, bool honor_quotes);
static const char *convertRegProcReference(const char *proc);
static const char *convertOperatorReference(const char *opr);
+static const char *convertTSFunction(Oid funcOid);
static Oid findLastBuiltinOid_V71(const char *);
static Oid findLastBuiltinOid_V70(void);
static void selectSourceSchema(const char *schemaName);
@@ -4678,6 +4683,327 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
/*
+ * getTSParsers:
+ * read all text search parsers in the system catalogs and return them
+ * in the TSParserInfo* structure
+ *
+ * numTSParsers is set to the number of parsers read in
+ */
+TSParserInfo *
+getTSParsers(int *numTSParsers)
+{
+ PGresult *res;
+ int ntups;
+ int i;
+ PQExpBuffer query = createPQExpBuffer();
+ TSParserInfo *prsinfo;
+ int i_tableoid;
+ int i_oid;
+ int i_prsname;
+ int i_prsnamespace;
+ int i_prsstart;
+ int i_prstoken;
+ int i_prsend;
+ int i_prsheadline;
+ int i_prslextype;
+
+ /* Before 8.3, there is no built-in text search support */
+ if (g_fout->remoteVersion < 80300)
+ {
+ *numTSParsers = 0;
+ return NULL;
+ }
+
+ /*
+ * find all text search objects, including builtin ones; we filter out
+ * system-defined objects at dump-out time.
+ */
+
+ /* Make sure we are in proper schema */
+ selectSourceSchema("pg_catalog");
+
+ appendPQExpBuffer(query, "SELECT tableoid, oid, prsname, prsnamespace, "
+ "prsstart::oid, prstoken::oid, "
+ "prsend::oid, prsheadline::oid, prslextype::oid "
+ "FROM pg_ts_parser");
+
+ res = PQexec(g_conn, query->data);
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
+
+ ntups = PQntuples(res);
+ *numTSParsers = ntups;
+
+ prsinfo = (TSParserInfo *) malloc(ntups * sizeof(TSParserInfo));
+
+ i_tableoid = PQfnumber(res, "tableoid");
+ i_oid = PQfnumber(res, "oid");
+ i_prsname = PQfnumber(res, "prsname");
+ i_prsnamespace = PQfnumber(res, "prsnamespace");
+ i_prsstart = PQfnumber(res, "prsstart");
+ i_prstoken = PQfnumber(res, "prstoken");
+ i_prsend = PQfnumber(res, "prsend");
+ i_prsheadline = PQfnumber(res, "prsheadline");
+ i_prslextype = PQfnumber(res, "prslextype");
+
+ for (i = 0; i < ntups; i++)
+ {
+ prsinfo[i].dobj.objType = DO_TSPARSER;
+ prsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
+ prsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ AssignDumpId(&prsinfo[i].dobj);
+ prsinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_prsname));
+ prsinfo[i].dobj.namespace = findNamespace(atooid(PQgetvalue(res, i, i_prsnamespace)),
+ prsinfo[i].dobj.catId.oid);
+ prsinfo[i].prsstart = atooid(PQgetvalue(res, i, i_prsstart));
+ prsinfo[i].prstoken = atooid(PQgetvalue(res, i, i_prstoken));
+ prsinfo[i].prsend = atooid(PQgetvalue(res, i, i_prsend));
+ prsinfo[i].prsheadline = atooid(PQgetvalue(res, i, i_prsheadline));
+ prsinfo[i].prslextype = atooid(PQgetvalue(res, i, i_prslextype));
+
+ /* Decide whether we want to dump it */
+ selectDumpableObject(&(prsinfo[i].dobj));
+ }
+
+ PQclear(res);
+
+ destroyPQExpBuffer(query);
+
+ return prsinfo;
+}
+
+/*
+ * getTSDictionaries:
+ * read all text search dictionaries in the system catalogs and return them
+ * in the TSDictInfo* structure
+ *
+ * numTSDicts is set to the number of dictionaries read in
+ */
+TSDictInfo *
+getTSDictionaries(int *numTSDicts)
+{
+ PGresult *res;
+ int ntups;
+ int i;
+ PQExpBuffer query = createPQExpBuffer();
+ TSDictInfo *dictinfo;
+ int i_tableoid;
+ int i_oid;
+ int i_dictname;
+ int i_dictnamespace;
+ int i_rolname;
+ int i_dicttemplate;
+ int i_dictinitoption;
+
+ /* Before 8.3, there is no built-in text search support */
+ if (g_fout->remoteVersion < 80300)
+ {
+ *numTSDicts = 0;
+ return NULL;
+ }
+
+ /* Make sure we are in proper schema */
+ selectSourceSchema("pg_catalog");
+
+ appendPQExpBuffer(query, "SELECT tableoid, oid, dictname, "
+ "dictnamespace, (%s dictowner) as rolname, "
+ "dicttemplate, dictinitoption "
+ "FROM pg_ts_dict",
+ username_subquery);
+
+ res = PQexec(g_conn, query->data);
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
+
+ ntups = PQntuples(res);
+ *numTSDicts = ntups;
+
+ dictinfo = (TSDictInfo *) malloc(ntups * sizeof(TSDictInfo));
+
+ i_tableoid = PQfnumber(res, "tableoid");
+ i_oid = PQfnumber(res, "oid");
+ i_dictname = PQfnumber(res, "dictname");
+ i_dictnamespace = PQfnumber(res, "dictnamespace");
+ i_rolname = PQfnumber(res, "rolname");
+ i_dictinitoption = PQfnumber(res, "dictinitoption");
+ i_dicttemplate = PQfnumber(res, "dicttemplate");
+
+ for (i = 0; i < ntups; i++)
+ {
+ dictinfo[i].dobj.objType = DO_TSDICT;
+ dictinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
+ dictinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ AssignDumpId(&dictinfo[i].dobj);
+ dictinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_dictname));
+ dictinfo[i].dobj.namespace = findNamespace(atooid(PQgetvalue(res, i, i_dictnamespace)),
+ dictinfo[i].dobj.catId.oid);
+ dictinfo[i].rolname = strdup(PQgetvalue(res, i, i_rolname));
+ dictinfo[i].dicttemplate = atooid(PQgetvalue(res, i, i_dicttemplate));
+ if (PQgetisnull(res, i, i_dictinitoption))
+ dictinfo[i].dictinitoption = NULL;
+ else
+ dictinfo[i].dictinitoption = strdup(PQgetvalue(res, i, i_dictinitoption));
+
+ /* Decide whether we want to dump it */
+ selectDumpableObject(&(dictinfo[i].dobj));
+ }
+
+ PQclear(res);
+
+ destroyPQExpBuffer(query);
+
+ return dictinfo;
+}
+
+/*
+ * getTSTemplates:
+ * read all text search templates in the system catalogs and return them
+ * in the TSTemplateInfo* structure
+ *
+ * numTSTemplates is set to the number of templates read in
+ */
+TSTemplateInfo *
+getTSTemplates(int *numTSTemplates)
+{
+ PGresult *res;
+ int ntups;
+ int i;
+ PQExpBuffer query = createPQExpBuffer();
+ TSTemplateInfo *tmplinfo;
+ int i_tableoid;
+ int i_oid;
+ int i_tmplname;
+ int i_tmplnamespace;
+ int i_tmplinit;
+ int i_tmpllexize;
+
+ /* Before 8.3, there is no built-in text search support */
+ if (g_fout->remoteVersion < 80300)
+ {
+ *numTSTemplates = 0;
+ return NULL;
+ }
+
+ /* Make sure we are in proper schema */
+ selectSourceSchema("pg_catalog");
+
+ appendPQExpBuffer(query, "SELECT tableoid, oid, tmplname, "
+ "tmplnamespace, tmplinit::oid, tmpllexize::oid "
+ "FROM pg_ts_template");
+
+ res = PQexec(g_conn, query->data);
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
+
+ ntups = PQntuples(res);
+ *numTSTemplates = ntups;
+
+ tmplinfo = (TSTemplateInfo *) malloc(ntups * sizeof(TSTemplateInfo));
+
+ i_tableoid = PQfnumber(res, "tableoid");
+ i_oid = PQfnumber(res, "oid");
+ i_tmplname = PQfnumber(res, "tmplname");
+ i_tmplnamespace = PQfnumber(res, "tmplnamespace");
+ i_tmplinit = PQfnumber(res, "tmplinit");
+ i_tmpllexize = PQfnumber(res, "tmpllexize");
+
+ for (i = 0; i < ntups; i++)
+ {
+ tmplinfo[i].dobj.objType = DO_TSTEMPLATE;
+ tmplinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
+ tmplinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ AssignDumpId(&tmplinfo[i].dobj);
+ tmplinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_tmplname));
+ tmplinfo[i].dobj.namespace = findNamespace(atooid(PQgetvalue(res, i, i_tmplnamespace)),
+ tmplinfo[i].dobj.catId.oid);
+ tmplinfo[i].tmplinit = atooid(PQgetvalue(res, i, i_tmplinit));
+ tmplinfo[i].tmpllexize = atooid(PQgetvalue(res, i, i_tmpllexize));
+
+ /* Decide whether we want to dump it */
+ selectDumpableObject(&(tmplinfo[i].dobj));
+ }
+
+ PQclear(res);
+
+ destroyPQExpBuffer(query);
+
+ return tmplinfo;
+}
+
+/*
+ * getTSConfigurations:
+ * read all text search configurations in the system catalogs and return
+ * them in the TSConfigInfo* structure
+ *
+ * numTSConfigs is set to the number of configurations read in
+ */
+TSConfigInfo *
+getTSConfigurations(int *numTSConfigs)
+{
+ PGresult *res;
+ int ntups;
+ int i;
+ PQExpBuffer query = createPQExpBuffer();
+ TSConfigInfo *cfginfo;
+ int i_tableoid;
+ int i_oid;
+ int i_cfgname;
+ int i_cfgnamespace;
+ int i_rolname;
+ int i_cfgparser;
+
+ /* Before 8.3, there is no built-in text search support */
+ if (g_fout->remoteVersion < 80300)
+ {
+ *numTSConfigs = 0;
+ return NULL;
+ }
+
+ /* Make sure we are in proper schema */
+ selectSourceSchema("pg_catalog");
+
+ appendPQExpBuffer(query, "SELECT tableoid, oid, cfgname, "
+ "cfgnamespace, (%s cfgowner) as rolname, cfgparser "
+ "FROM pg_ts_config",
+ username_subquery);
+
+ res = PQexec(g_conn, query->data);
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
+
+ ntups = PQntuples(res);
+ *numTSConfigs = ntups;
+
+ cfginfo = (TSConfigInfo *) malloc(ntups * sizeof(TSConfigInfo));
+
+ i_tableoid = PQfnumber(res, "tableoid");
+ i_oid = PQfnumber(res, "oid");
+ i_cfgname = PQfnumber(res, "cfgname");
+ i_cfgnamespace = PQfnumber(res, "cfgnamespace");
+ i_rolname = PQfnumber(res, "rolname");
+ i_cfgparser = PQfnumber(res, "cfgparser");
+
+ for (i = 0; i < ntups; i++)
+ {
+ cfginfo[i].dobj.objType = DO_TSCONFIG;
+ cfginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
+ cfginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ AssignDumpId(&cfginfo[i].dobj);
+ cfginfo[i].dobj.name = strdup(PQgetvalue(res, i, i_cfgname));
+ cfginfo[i].dobj.namespace = findNamespace(atooid(PQgetvalue(res, i, i_cfgnamespace)),
+ cfginfo[i].dobj.catId.oid);
+ cfginfo[i].rolname = strdup(PQgetvalue(res, i, i_rolname));
+ cfginfo[i].cfgparser = atooid(PQgetvalue(res, i, i_cfgparser));
+
+ /* Decide whether we want to dump it */
+ selectDumpableObject(&(cfginfo[i].dobj));
+ }
+
+ PQclear(res);
+
+ destroyPQExpBuffer(query);
+
+ return cfginfo;
+}
+
+
+/*
* dumpComment --
*
* This routine is used to dump any comments associated with the
@@ -5066,6 +5392,18 @@ dumpDumpableObject(Archive *fout, DumpableObject *dobj)
case DO_TABLE_TYPE:
/* table rowtypes are never dumped separately */
break;
+ case DO_TSPARSER:
+ dumpTSParser(fout, (TSParserInfo *) dobj);
+ break;
+ case DO_TSDICT:
+ dumpTSDictionary(fout, (TSDictInfo *) dobj);
+ break;
+ case DO_TSTEMPLATE:
+ dumpTSTemplate(fout, (TSTemplateInfo *) dobj);
+ break;
+ case DO_TSCONFIG:
+ dumpTSConfig(fout, (TSConfigInfo *) dobj);
+ break;
case DO_BLOBS:
ArchiveEntry(fout, dobj->catId, dobj->dumpId,
dobj->name, NULL, NULL, "",
@@ -6875,6 +7213,43 @@ convertOperatorReference(const char *opr)
}
/*
+ * Convert a function OID obtained from pg_ts_parser or pg_ts_template
+ *
+ * It is sufficient to use REGPROC rather than REGPROCEDURE, since the
+ * argument lists of these functions are predetermined. Note that the
+ * caller should ensure we are in the proper schema, because the results
+ * are search path dependent!
+ */
+static const char *
+convertTSFunction(Oid funcOid)
+{
+ char *result;
+ char query[128];
+ PGresult *res;
+ int ntups;
+
+ snprintf(query, sizeof(query),
+ "SELECT '%u'::pg_catalog.regproc", funcOid);
+ res = PQexec(g_conn, query);
+ check_sql_result(res, g_conn, query, PGRES_TUPLES_OK);
+
+ ntups = PQntuples(res);
+ if (ntups != 1)
+ {
+ write_msg(NULL, "Got %d rows instead of one from \"%s\"\n",
+ ntups, query);
+ exit_nicely();
+ }
+
+ result = strdup(PQgetvalue(res, 0, 0));
+
+ PQclear(res);
+
+ return result;
+}
+
+
+/*
* dumpOpclass
* write out a single operator class definition
*/
@@ -7795,6 +8170,351 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
destroyPQExpBuffer(details);
}
+/*
+ * dumpTSParser
+ * write out a single text search parser
+ */
+static void
+dumpTSParser(Archive *fout, TSParserInfo * prsinfo)
+{
+ PQExpBuffer q;
+ PQExpBuffer delq;
+
+ /* Skip if not to be dumped */
+ if (!prsinfo->dobj.dump || dataOnly)
+ return;
+
+ q = createPQExpBuffer();
+ delq = createPQExpBuffer();
+
+ /* Make sure we are in proper schema */
+ selectSourceSchema(prsinfo->dobj.namespace->dobj.name);
+
+ appendPQExpBuffer(q, "CREATE TEXT SEARCH PARSER %s (\n",
+ fmtId(prsinfo->dobj.name));
+
+ appendPQExpBuffer(q, " START = %s,\n",
+ convertTSFunction(prsinfo->prsstart));
+ appendPQExpBuffer(q, " GETTOKEN = %s,\n",
+ convertTSFunction(prsinfo->prstoken));
+ appendPQExpBuffer(q, " END = %s,\n",
+ convertTSFunction(prsinfo->prsend));
+ if (prsinfo->prsheadline != InvalidOid)
+ appendPQExpBuffer(q, " HEADLINE = %s,\n",
+ convertTSFunction(prsinfo->prsheadline));
+ appendPQExpBuffer(q, " LEXTYPES = %s );\n",
+ convertTSFunction(prsinfo->prslextype));
+
+ /*
+ * DROP must be fully qualified in case same name appears in pg_catalog
+ */
+ appendPQExpBuffer(delq, "DROP TEXT SEARCH PARSER %s",
+ fmtId(prsinfo->dobj.namespace->dobj.name));
+ appendPQExpBuffer(delq, ".%s;\n",
+ fmtId(prsinfo->dobj.name));
+
+ ArchiveEntry(fout, prsinfo->dobj.catId, prsinfo->dobj.dumpId,
+ prsinfo->dobj.name,
+ prsinfo->dobj.namespace->dobj.name,
+ NULL,
+ "",
+ false, "TEXT SEARCH PARSER", q->data, delq->data, NULL,
+ prsinfo->dobj.dependencies, prsinfo->dobj.nDeps,
+ NULL, NULL);
+
+ /* Dump Parser Comments */
+ resetPQExpBuffer(q);
+ appendPQExpBuffer(q, "TEXT SEARCH PARSER %s",
+ fmtId(prsinfo->dobj.name));
+ dumpComment(fout, q->data,
+ NULL, "",
+ prsinfo->dobj.catId, 0, prsinfo->dobj.dumpId);
+
+ destroyPQExpBuffer(q);
+ destroyPQExpBuffer(delq);
+}
+
+/*
+ * dumpTSDictionary
+ * write out a single text search dictionary
+ */
+static void
+dumpTSDictionary(Archive *fout, TSDictInfo * dictinfo)
+{
+ PQExpBuffer q;
+ PQExpBuffer delq;
+ PQExpBuffer query;
+ PGresult *res;
+ int ntups;
+ char *nspname;
+ char *tmplname;
+
+ /* Skip if not to be dumped */
+ if (!dictinfo->dobj.dump || dataOnly)
+ return;
+
+ q = createPQExpBuffer();
+ delq = createPQExpBuffer();
+ query = createPQExpBuffer();
+
+ /* Fetch name and namespace of the dictionary's template */
+ selectSourceSchema("pg_catalog");
+ appendPQExpBuffer(query, "SELECT nspname, tmplname "
+ "FROM pg_ts_template p, pg_namespace n "
+ "WHERE p.oid = '%u' AND n.oid = tmplnamespace",
+ dictinfo->dicttemplate);
+ res = PQexec(g_conn, query->data);
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
+ ntups = PQntuples(res);
+ if (ntups != 1)
+ {
+ write_msg(NULL, "Got %d rows instead of one from \"%s\"\n",
+ ntups, query->data);
+ exit_nicely();
+ }
+ nspname = PQgetvalue(res, 0, 0);
+ tmplname = PQgetvalue(res, 0, 1);
+
+ /* Make sure we are in proper schema */
+ selectSourceSchema(dictinfo->dobj.namespace->dobj.name);
+
+ appendPQExpBuffer(q, "CREATE TEXT SEARCH DICTIONARY %s (\n",
+ fmtId(dictinfo->dobj.name));
+
+ appendPQExpBuffer(q, " TEMPLATE = ");
+ if (strcmp(nspname, dictinfo->dobj.namespace->dobj.name) != 0)
+ appendPQExpBuffer(q, "%s.", fmtId(nspname));
+ appendPQExpBuffer(q, "%s", fmtId(tmplname));
+
+ PQclear(res);
+
+ if (dictinfo->dictinitoption)
+ {
+ appendPQExpBuffer(q, ",\n OPTION = ");
+ appendStringLiteralConn(q, dictinfo->dictinitoption, g_conn);
+ }
+
+ appendPQExpBuffer(q, " );\n");
+
+ /*
+ * DROP must be fully qualified in case same name appears in pg_catalog
+ */
+ appendPQExpBuffer(delq, "DROP TEXT SEARCH DICTIONARY %s",
+ fmtId(dictinfo->dobj.namespace->dobj.name));
+ appendPQExpBuffer(delq, ".%s;\n",
+ fmtId(dictinfo->dobj.name));
+
+ ArchiveEntry(fout, dictinfo->dobj.catId, dictinfo->dobj.dumpId,
+ dictinfo->dobj.name,
+ dictinfo->dobj.namespace->dobj.name,
+ NULL,
+ dictinfo->rolname,
+ false, "TEXT SEARCH DICTIONARY", q->data, delq->data, NULL,
+ dictinfo->dobj.dependencies, dictinfo->dobj.nDeps,
+ NULL, NULL);
+
+ /* Dump Dictionary Comments */
+ resetPQExpBuffer(q);
+ appendPQExpBuffer(q, "TEXT SEARCH DICTIONARY %s",
+ fmtId(dictinfo->dobj.name));
+ dumpComment(fout, q->data,
+ NULL, dictinfo->rolname,
+ dictinfo->dobj.catId, 0, dictinfo->dobj.dumpId);
+
+ destroyPQExpBuffer(q);
+ destroyPQExpBuffer(delq);
+ destroyPQExpBuffer(query);
+}
+
+/*
+ * dumpTSTemplate
+ * write out a single text search template
+ */
+static void
+dumpTSTemplate(Archive *fout, TSTemplateInfo * tmplinfo)
+{
+ PQExpBuffer q;
+ PQExpBuffer delq;
+
+ /* Skip if not to be dumped */
+ if (!tmplinfo->dobj.dump || dataOnly)
+ return;
+
+ q = createPQExpBuffer();
+ delq = createPQExpBuffer();
+
+ /* Make sure we are in proper schema */
+ selectSourceSchema(tmplinfo->dobj.namespace->dobj.name);
+
+ appendPQExpBuffer(q, "CREATE TEXT SEARCH TEMPLATE %s (\n",
+ fmtId(tmplinfo->dobj.name));
+
+ if (tmplinfo->tmplinit != InvalidOid)
+ appendPQExpBuffer(q, " INIT = %s,\n",
+ convertTSFunction(tmplinfo->tmplinit));
+ appendPQExpBuffer(q, " LEXIZE = %s );\n",
+ convertTSFunction(tmplinfo->tmpllexize));
+
+ /*
+ * DROP must be fully qualified in case same name appears in pg_catalog
+ */
+ appendPQExpBuffer(delq, "DROP TEXT SEARCH TEMPLATE %s",
+ fmtId(tmplinfo->dobj.namespace->dobj.name));
+ appendPQExpBuffer(delq, ".%s;\n",
+ fmtId(tmplinfo->dobj.name));
+
+ ArchiveEntry(fout, tmplinfo->dobj.catId, tmplinfo->dobj.dumpId,
+ tmplinfo->dobj.name,
+ tmplinfo->dobj.namespace->dobj.name,
+ NULL,
+ "",
+ false, "TEXT SEARCH TEMPLATE", q->data, delq->data, NULL,
+ tmplinfo->dobj.dependencies, tmplinfo->dobj.nDeps,
+ NULL, NULL);
+
+ /* Dump Template Comments */
+ resetPQExpBuffer(q);
+ appendPQExpBuffer(q, "TEXT SEARCH TEMPLATE %s",
+ fmtId(tmplinfo->dobj.name));
+ dumpComment(fout, q->data,
+ NULL, "",
+ tmplinfo->dobj.catId, 0, tmplinfo->dobj.dumpId);
+
+ destroyPQExpBuffer(q);
+ destroyPQExpBuffer(delq);
+}
+
+/*
+ * dumpTSConfig
+ * write out a single text search configuration
+ */
+static void
+dumpTSConfig(Archive *fout, TSConfigInfo * cfginfo)
+{
+ PQExpBuffer q;
+ PQExpBuffer delq;
+ PQExpBuffer query;
+ PGresult *res;
+ char *nspname;
+ char *prsname;
+ int ntups,
+ i;
+ int i_tokenname;
+ int i_dictname;
+
+ /* Skip if not to be dumped */
+ if (!cfginfo->dobj.dump || dataOnly)
+ return;
+
+ q = createPQExpBuffer();
+ delq = createPQExpBuffer();
+ query = createPQExpBuffer();
+
+ /* Fetch name and namespace of the config's parser */
+ selectSourceSchema("pg_catalog");
+ appendPQExpBuffer(query, "SELECT nspname, prsname "
+ "FROM pg_ts_parser p, pg_namespace n "
+ "WHERE p.oid = '%u' AND n.oid = prsnamespace",
+ cfginfo->cfgparser);
+ res = PQexec(g_conn, query->data);
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
+ ntups = PQntuples(res);
+ if (ntups != 1)
+ {
+ write_msg(NULL, "Got %d rows instead of one from \"%s\"\n",
+ ntups, query->data);
+ exit_nicely();
+ }
+ nspname = PQgetvalue(res, 0, 0);
+ prsname = PQgetvalue(res, 0, 1);
+
+ /* Make sure we are in proper schema */
+ selectSourceSchema(cfginfo->dobj.namespace->dobj.name);
+
+ appendPQExpBuffer(q, "CREATE TEXT SEARCH CONFIGURATION %s (\n",
+ fmtId(cfginfo->dobj.name));
+
+ appendPQExpBuffer(q, " PARSER = ");
+ if (strcmp(nspname, cfginfo->dobj.namespace->dobj.name) != 0)
+ appendPQExpBuffer(q, "%s.", fmtId(nspname));
+ appendPQExpBuffer(q, "%s );\n", fmtId(prsname));
+
+ PQclear(res);
+
+ resetPQExpBuffer(query);
+ appendPQExpBuffer(query,
+ "SELECT \n"
+ " ( SELECT alias FROM pg_catalog.ts_token_type('%u'::pg_catalog.oid) AS t \n"
+ " WHERE t.tokid = m.maptokentype ) AS tokenname, \n"
+ " m.mapdict::pg_catalog.regdictionary AS dictname \n"
+ "FROM pg_catalog.pg_ts_config_map AS m \n"
+ "WHERE m.mapcfg = '%u' \n"
+ "ORDER BY m.mapcfg, m.maptokentype, m.mapseqno",
+ cfginfo->cfgparser, cfginfo->dobj.catId.oid);
+
+ res = PQexec(g_conn, query->data);
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
+ ntups = PQntuples(res);
+
+ i_tokenname = PQfnumber(res, "tokenname");
+ i_dictname = PQfnumber(res, "dictname");
+
+ for (i = 0; i < ntups; i++)
+ {
+ char *tokenname = PQgetvalue(res, i, i_tokenname);
+ char *dictname = PQgetvalue(res, i, i_dictname);
+
+ if (i == 0 ||
+ strcmp(tokenname, PQgetvalue(res, i-1, i_tokenname)) != 0)
+ {
+ /* starting a new token type, so start a new command */
+ if (i > 0)
+ appendPQExpBuffer(q, ";\n");
+ appendPQExpBuffer(q, "\nALTER TEXT SEARCH CONFIGURATION %s\n",
+ fmtId(cfginfo->dobj.name));
+ /* tokenname needs quoting, dictname does NOT */
+ appendPQExpBuffer(q, " ADD MAPPING FOR %s WITH %s",
+ fmtId(tokenname), dictname);
+ }
+ else
+ appendPQExpBuffer(q, ", %s", dictname);
+ }
+
+ if (ntups > 0)
+ appendPQExpBuffer(q, ";\n");
+
+ PQclear(res);
+
+ /*
+ * DROP must be fully qualified in case same name appears in pg_catalog
+ */
+ appendPQExpBuffer(delq, "DROP TEXT SEARCH CONFIGURATION %s",
+ fmtId(cfginfo->dobj.namespace->dobj.name));
+ appendPQExpBuffer(delq, ".%s;\n",
+ fmtId(cfginfo->dobj.name));
+
+ ArchiveEntry(fout, cfginfo->dobj.catId, cfginfo->dobj.dumpId,
+ cfginfo->dobj.name,
+ cfginfo->dobj.namespace->dobj.name,
+ NULL,
+ cfginfo->rolname,
+ false, "TEXT SEARCH CONFIGURATION", q->data, delq->data, NULL,
+ cfginfo->dobj.dependencies, cfginfo->dobj.nDeps,
+ NULL, NULL);
+
+ /* Dump Configuration Comments */
+ resetPQExpBuffer(q);
+ appendPQExpBuffer(q, "TEXT SEARCH CONFIGURATION %s",
+ fmtId(cfginfo->dobj.name));
+ dumpComment(fout, q->data,
+ NULL, cfginfo->rolname,
+ cfginfo->dobj.catId, 0, cfginfo->dobj.dumpId);
+
+ destroyPQExpBuffer(q);
+ destroyPQExpBuffer(delq);
+ destroyPQExpBuffer(query);
+}
+
/*----------
* Write out grant/revoke information
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 9575cd5b19d..0383b0d296d 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.h,v 1.135 2007/05/11 17:57:13 tgl Exp $
+ * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.h,v 1.136 2007/08/21 01:11:21 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -127,6 +127,10 @@ typedef enum
DO_CAST,
DO_TABLE_DATA,
DO_TABLE_TYPE,
+ DO_TSPARSER,
+ DO_TSDICT,
+ DO_TSTEMPLATE,
+ DO_TSCONFIG,
DO_BLOBS,
DO_BLOB_COMMENTS
} DumpableObjectType;
@@ -379,6 +383,37 @@ typedef struct _inhInfo
Oid inhparent; /* OID of its parent */
} InhInfo;
+typedef struct _prsInfo
+{
+ DumpableObject dobj;
+ Oid prsstart;
+ Oid prstoken;
+ Oid prsend;
+ Oid prsheadline;
+ Oid prslextype;
+} TSParserInfo;
+
+typedef struct _dictInfo
+{
+ DumpableObject dobj;
+ char *rolname;
+ Oid dicttemplate;
+ char *dictinitoption;
+} TSDictInfo;
+
+typedef struct _tmplInfo
+{
+ DumpableObject dobj;
+ Oid tmplinit;
+ Oid tmpllexize;
+} TSTemplateInfo;
+
+typedef struct _cfgInfo
+{
+ DumpableObject dobj;
+ char *rolname;
+ Oid cfgparser;
+} TSConfigInfo;
/* global decls */
extern bool force_quotes; /* double-quotes for identifiers flag */
@@ -458,5 +493,9 @@ extern void getTriggers(TableInfo tblinfo[], int numTables);
extern ProcLangInfo *getProcLangs(int *numProcLangs);
extern CastInfo *getCasts(int *numCasts);
extern void getTableAttrs(TableInfo *tbinfo, int numTables);
+extern TSParserInfo *getTSParsers(int *numTSParsers);
+extern TSDictInfo *getTSDictionaries(int *numTSDicts);
+extern TSTemplateInfo *getTSTemplates(int *numTSTemplates);
+extern TSConfigInfo *getTSConfigurations(int *numTSConfigs);
#endif /* PG_DUMP_H */
diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c
index a74857fb68e..d74a5facf8e 100644
--- a/src/bin/pg_dump/pg_dump_sort.c
+++ b/src/bin/pg_dump/pg_dump_sort.c
@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump_sort.c,v 1.18 2007/03/18 16:50:44 neilc Exp $
+ * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump_sort.c,v 1.19 2007/08/21 01:11:21 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -46,6 +46,10 @@ static const int oldObjectTypePriority[] =
2, /* DO_CAST */
9, /* DO_TABLE_DATA */
7, /* DO_TABLE_TYPE */
+ 3, /* DO_TSPARSER */
+ 4, /* DO_TSDICT */
+ 3, /* DO_TSTEMPLATE */
+ 5, /* DO_TSCONFIG */
10, /* DO_BLOBS */
11 /* DO_BLOB_COMMENTS */
};
@@ -76,6 +80,10 @@ static const int newObjectTypePriority[] =
8, /* DO_CAST */
13, /* DO_TABLE_DATA */
11, /* DO_TABLE_TYPE */
+ 5, /* DO_TSPARSER */
+ 6, /* DO_TSDICT */
+ 5, /* DO_TSTEMPLATE */
+ 7, /* DO_TSCONFIG */
14, /* DO_BLOBS */
15 /* DO_BLOB_COMMENTS */
};
@@ -1067,6 +1075,26 @@ describeDumpableObject(DumpableObject *obj, char *buf, int bufsize)
"TABLE TYPE %s (ID %d OID %u)",
obj->name, obj->dumpId, obj->catId.oid);
return;
+ case DO_TSPARSER:
+ snprintf(buf, bufsize,
+ "TEXT SEARCH PARSER %s (ID %d OID %u)",
+ obj->name, obj->dumpId, obj->catId.oid);
+ return;
+ case DO_TSDICT:
+ snprintf(buf, bufsize,
+ "TEXT SEARCH DICTIONARY %s (ID %d OID %u)",
+ obj->name, obj->dumpId, obj->catId.oid);
+ return;
+ case DO_TSTEMPLATE:
+ snprintf(buf, bufsize,
+ "TEXT SEARCH TEMPLATE %s (ID %d OID %u)",
+ obj->name, obj->dumpId, obj->catId.oid);
+ return;
+ case DO_TSCONFIG:
+ snprintf(buf, bufsize,
+ "TEXT SEARCH CONFIGURATION %s (ID %d OID %u)",
+ obj->name, obj->dumpId, obj->catId.oid);
+ return;
case DO_BLOBS:
snprintf(buf, bufsize,
"BLOBS (ID %d)",
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 936c56b2031..04e5cce6361 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -3,7 +3,7 @@
*
* Copyright (c) 2000-2007, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/bin/psql/command.c,v 1.180 2007/07/08 19:07:38 tgl Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/command.c,v 1.181 2007/08/21 01:11:22 tgl Exp $
*/
#include "postgres_fe.h"
#include "command.h"
@@ -405,6 +405,27 @@ exec_command(const char *cmd,
case 'u':
success = describeRoles(pattern, show_verbose);
break;
+ case 'F': /* text search subsystem */
+ switch (cmd[2])
+ {
+ case '\0':
+ case '+':
+ success = listTSConfigs(pattern, show_verbose);
+ break;
+ case 'p':
+ success = listTSParsers(pattern, show_verbose);
+ break;
+ case 'd':
+ success = listTSDictionaries(pattern, show_verbose);
+ break;
+ case 't':
+ success = listTSTemplates(pattern, show_verbose);
+ break;
+ default:
+ status = PSQL_CMD_UNKNOWN;
+ break;
+ }
+ break;
default:
status = PSQL_CMD_UNKNOWN;
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 6dd156af4af..f258e16ce1a 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -3,7 +3,7 @@
*
* Copyright (c) 2000-2007, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.157 2007/07/25 22:16:18 tgl Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.158 2007/08/21 01:11:22 tgl Exp $
*/
#include "postgres_fe.h"
#include "describe.h"
@@ -33,6 +33,14 @@ static bool describeOneTableDetails(const char *schemaname,
bool verbose);
static bool add_tablespace_footer(char relkind, Oid tablespace, char **footers,
int *count, PQExpBufferData buf, bool newline);
+static bool listTSParsersVerbose(const char *pattern);
+static bool describeOneTSParser(const char *oid, const char *nspname,
+ const char *prsname);
+static bool listTSConfigsVerbose(const char *pattern);
+static bool describeOneTSConfig(const char *oid, const char *nspname,
+ const char *cfgname,
+ const char *pnspname, const char *prsname);
+
/*----------------
* Handlers for various slash commands displaying some sort of list
@@ -325,7 +333,6 @@ describeTypes(const char *pattern, bool verbose)
}
-
/* \do
*/
bool
@@ -1930,3 +1937,512 @@ listSchemas(const char *pattern, bool verbose)
PQclear(res);
return true;
}
+
+
+/*
+ * \dFp
+ * list text search parsers
+ */
+bool
+listTSParsers(const char *pattern, bool verbose)
+{
+ PQExpBufferData buf;
+ PGresult *res;
+ printQueryOpt myopt = pset.popt;
+
+ if (verbose)
+ return listTSParsersVerbose(pattern);
+
+ initPQExpBuffer(&buf);
+
+ printfPQExpBuffer(&buf,
+ "SELECT \n"
+ " n.nspname as \"%s\",\n"
+ " p.prsname as \"%s\",\n"
+ " pg_catalog.obj_description(p.oid, 'pg_ts_parser') as \"%s\"\n"
+ "FROM pg_catalog.pg_ts_parser p \n"
+ "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.prsnamespace\n",
+ _("Schema"),
+ _("Name"),
+ _("Description")
+ );
+
+ processSQLNamePattern(pset.db, &buf, pattern, false, false,
+ "n.nspname", "p.prsname", NULL,
+ "pg_catalog.pg_ts_parser_is_visible(p.oid)");
+
+ appendPQExpBuffer(&buf, "ORDER BY 1, 2;");
+
+ res = PSQLexec(buf.data, false);
+ termPQExpBuffer(&buf);
+ if (!res)
+ return false;
+
+ myopt.nullPrint = NULL;
+ myopt.title = _("List of text search parsers");
+
+ printQuery(res, &myopt, pset.queryFout, pset.logfile);
+
+ PQclear(res);
+ return true;
+}
+
+/*
+ * full description of parsers
+ */
+static bool
+listTSParsersVerbose(const char *pattern)
+{
+ PQExpBufferData buf;
+ PGresult *res;
+ int i;
+
+ initPQExpBuffer(&buf);
+
+ printfPQExpBuffer(&buf,
+ "SELECT p.oid, \n"
+ " n.nspname, \n"
+ " p.prsname \n"
+ "FROM pg_catalog.pg_ts_parser p\n"
+ "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.prsnamespace\n"
+ );
+
+ processSQLNamePattern(pset.db, &buf, pattern, false, false,
+ "n.nspname", "p.prsname", NULL,
+ "pg_catalog.pg_ts_parser_is_visible(p.oid)");
+
+ appendPQExpBuffer(&buf, "ORDER BY 1, 2;");
+
+ res = PSQLexec(buf.data, false);
+ termPQExpBuffer(&buf);
+ if (!res)
+ return false;
+
+ if (PQntuples(res) == 0)
+ {
+ if (!pset.quiet)
+ fprintf(stderr, _("Did not find any text search parser named \"%s\".\n"),
+ pattern);
+ PQclear(res);
+ return false;
+ }
+
+ for (i = 0; i < PQntuples(res); i++)
+ {
+ const char *oid;
+ const char *nspname = NULL;
+ const char *prsname;
+
+ oid = PQgetvalue(res, i, 0);
+ if (!PQgetisnull(res, i, 1))
+ nspname = PQgetvalue(res, i, 1);
+ prsname = PQgetvalue(res, i, 2);
+
+ if (!describeOneTSParser(oid, nspname, prsname))
+ {
+ PQclear(res);
+ return false;
+ }
+
+ if (cancel_pressed)
+ {
+ PQclear(res);
+ return false;
+ }
+ }
+
+ PQclear(res);
+ return true;
+}
+
+static bool
+describeOneTSParser(const char *oid, const char *nspname, const char *prsname)
+{
+ PQExpBufferData buf;
+ PGresult *res;
+ char title[1024];
+ printQueryOpt myopt = pset.popt;
+
+ initPQExpBuffer(&buf);
+
+ printfPQExpBuffer(&buf,
+ "SELECT '%s' AS \"%s\", \n"
+ " p.prsstart::pg_catalog.regproc AS \"%s\", \n"
+ " pg_catalog.obj_description(p.prsstart, 'pg_proc') as \"%s\" \n"
+ " FROM pg_catalog.pg_ts_parser p \n"
+ " WHERE p.oid = '%s' \n"
+ "UNION ALL \n"
+ "SELECT '%s', \n"
+ " p.prstoken::pg_catalog.regproc, \n"
+ " pg_catalog.obj_description(p.prstoken, 'pg_proc') \n"
+ " FROM pg_catalog.pg_ts_parser p \n"
+ " WHERE p.oid = '%s' \n"
+ "UNION ALL \n"
+ "SELECT '%s', \n"
+ " p.prsend::pg_catalog.regproc, \n"
+ " pg_catalog.obj_description(p.prsend, 'pg_proc') \n"
+ " FROM pg_catalog.pg_ts_parser p \n"
+ " WHERE p.oid = '%s' \n"
+ "UNION ALL \n"
+ "SELECT '%s', \n"
+ " p.prsheadline::pg_catalog.regproc, \n"
+ " pg_catalog.obj_description(p.prsheadline, 'pg_proc') \n"
+ " FROM pg_catalog.pg_ts_parser p \n"
+ " WHERE p.oid = '%s' \n"
+ "UNION ALL \n"
+ "SELECT '%s', \n"
+ " p.prslextype::pg_catalog.regproc, \n"
+ " pg_catalog.obj_description(p.prslextype, 'pg_proc') \n"
+ " FROM pg_catalog.pg_ts_parser p \n"
+ " WHERE p.oid = '%s' \n",
+ _("Start parse"),
+ _("Method"), _("Function"), _("Description"),
+ oid,
+ _("Get next token"), oid,
+ _("End parse"), oid,
+ _("Get headline"), oid,
+ _("Get lexeme types"), oid
+ );
+
+ res = PSQLexec(buf.data, false);
+ termPQExpBuffer(&buf);
+ if (!res)
+ return false;
+
+ myopt.nullPrint = NULL;
+ if (nspname)
+ sprintf(title, _("Text search parser \"%s.%s\""), nspname, prsname);
+ else
+ sprintf(title, _("Text search parser \"%s\""), prsname);
+ myopt.title = title;
+ myopt.footers = NULL;
+ myopt.default_footer = false;
+
+ printQuery(res, &myopt, pset.queryFout, pset.logfile);
+
+ PQclear(res);
+
+ initPQExpBuffer(&buf);
+
+ printfPQExpBuffer(&buf,
+ "SELECT t.alias as \"%s\", \n"
+ " t.description as \"%s\" \n"
+ "FROM pg_catalog.ts_token_type( '%s'::pg_catalog.oid ) as t \n"
+ "ORDER BY 1;",
+ _("Token name"),
+ _("Description"),
+ oid);
+
+ res = PSQLexec(buf.data, false);
+ termPQExpBuffer(&buf);
+ if (!res)
+ return false;
+
+ myopt.nullPrint = NULL;
+ if (nspname)
+ sprintf(title, _("Token types for parser \"%s.%s\""), nspname, prsname);
+ else
+ sprintf(title, _("Token types for parser \"%s\""), prsname);
+ myopt.title = title;
+ myopt.footers = NULL;
+ myopt.default_footer = true;
+
+ printQuery(res, &myopt, pset.queryFout, pset.logfile);
+
+ PQclear(res);
+ return true;
+}
+
+
+/*
+ * \dFd
+ * list text search dictionaries
+ */
+bool
+listTSDictionaries(const char *pattern, bool verbose)
+{
+ PQExpBufferData buf;
+ PGresult *res;
+ printQueryOpt myopt = pset.popt;
+
+ initPQExpBuffer(&buf);
+
+ printfPQExpBuffer(&buf,
+ "SELECT \n"
+ " n.nspname as \"%s\",\n"
+ " d.dictname as \"%s\",\n",
+ _("Schema"),
+ _("Name"));
+
+ if (verbose)
+ {
+ appendPQExpBuffer(&buf,
+ " ( SELECT COALESCE(nt.nspname, '(null)')::pg_catalog.text || '.' || t.tmplname FROM \n"
+ " pg_catalog.pg_ts_template t \n"
+ " LEFT JOIN pg_catalog.pg_namespace nt ON nt.oid = t.tmplnamespace \n"
+ " WHERE d.dicttemplate = t.oid ) AS \"%s\", \n"
+ " d.dictinitoption as \"%s\", \n",
+ _("Template"),
+ _("Init options"));
+ }
+
+ appendPQExpBuffer(&buf,
+ " pg_catalog.obj_description(d.oid, 'pg_ts_dict') as \"%s\"\n",
+ _("Description"));
+
+ appendPQExpBuffer(&buf, "FROM pg_catalog.pg_ts_dict d\n"
+ "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = d.dictnamespace\n");
+
+ processSQLNamePattern(pset.db, &buf, pattern, false, false,
+ "n.nspname", "d.dictname", NULL,
+ "pg_catalog.pg_ts_dict_is_visible(d.oid)");
+
+ appendPQExpBuffer(&buf, "ORDER BY 1, 2;");
+
+ res = PSQLexec(buf.data, false);
+ termPQExpBuffer(&buf);
+ if (!res)
+ return false;
+
+ myopt.nullPrint = NULL;
+ myopt.title = _("List of text search dictionaries");
+
+ printQuery(res, &myopt, pset.queryFout, pset.logfile);
+
+ PQclear(res);
+ return true;
+}
+
+
+/*
+ * \dFt
+ * list text search templates
+ */
+bool
+listTSTemplates(const char *pattern, bool verbose)
+{
+ PQExpBufferData buf;
+ PGresult *res;
+ printQueryOpt myopt = pset.popt;
+
+ initPQExpBuffer(&buf);
+
+ printfPQExpBuffer(&buf,
+ "SELECT \n"
+ " n.nspname AS \"%s\",\n"
+ " t.tmplname AS \"%s\",\n"
+ " t.tmplinit::pg_catalog.regproc AS \"%s\",\n"
+ " t.tmpllexize::pg_catalog.regproc AS \"%s\",\n"
+ " pg_catalog.obj_description(t.oid, 'pg_ts_template') AS \"%s\"\n",
+ _("Schema"),
+ _("Name"),
+ _("Init"),
+ _("Lexize"),
+ _("Description"));
+
+ appendPQExpBuffer(&buf, "FROM pg_catalog.pg_ts_template t\n"
+ "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.tmplnamespace\n");
+
+ processSQLNamePattern(pset.db, &buf, pattern, false, false,
+ "n.nspname", "t.tmplname", NULL,
+ "pg_catalog.pg_ts_template_is_visible(t.oid)");
+
+ appendPQExpBuffer(&buf, "ORDER BY 1, 2;");
+
+ res = PSQLexec(buf.data, false);
+ termPQExpBuffer(&buf);
+ if (!res)
+ return false;
+
+ myopt.nullPrint = NULL;
+ myopt.title = _("List of text search templates");
+
+ printQuery(res, &myopt, pset.queryFout, pset.logfile);
+
+ PQclear(res);
+ return true;
+}
+
+
+/*
+ * \dF
+ * list text search configurations
+ */
+bool
+listTSConfigs(const char *pattern, bool verbose)
+{
+ PQExpBufferData buf;
+ PGresult *res;
+ printQueryOpt myopt = pset.popt;
+
+ if (verbose)
+ return listTSConfigsVerbose(pattern);
+
+ initPQExpBuffer(&buf);
+
+ printfPQExpBuffer(&buf,
+ "SELECT \n"
+ " n.nspname as \"%s\",\n"
+ " c.cfgname as \"%s\",\n"
+ " pg_catalog.obj_description(c.oid, 'pg_ts_config') as \"%s\"\n"
+ "FROM pg_catalog.pg_ts_config c\n"
+ "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.cfgnamespace \n",
+ _("Schema"),
+ _("Name"),
+ _("Description")
+ );
+
+ processSQLNamePattern(pset.db, &buf, pattern, false, false,
+ "n.nspname", "c.cfgname", NULL,
+ "pg_catalog.pg_ts_config_is_visible(c.oid)");
+
+ appendPQExpBuffer(&buf, "ORDER BY 1, 2;");
+
+ res = PSQLexec(buf.data, false);
+ termPQExpBuffer(&buf);
+ if (!res)
+ return false;
+
+ myopt.nullPrint = NULL;
+ myopt.title = _("List of text search configurations");
+
+ printQuery(res, &myopt, pset.queryFout, pset.logfile);
+
+ PQclear(res);
+ return true;
+}
+
+static bool
+listTSConfigsVerbose(const char *pattern)
+{
+ PQExpBufferData buf;
+ PGresult *res;
+ int i;
+
+ initPQExpBuffer(&buf);
+
+ printfPQExpBuffer(&buf,
+ "SELECT c.oid, c.cfgname,\n"
+ " n.nspname, \n"
+ " p.prsname, \n"
+ " np.nspname as pnspname \n"
+ "FROM pg_catalog.pg_ts_config c \n"
+ " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.cfgnamespace, \n"
+ " pg_catalog.pg_ts_parser p \n"
+ " LEFT JOIN pg_catalog.pg_namespace np ON np.oid = p.prsnamespace \n"
+ "WHERE p.oid = c.cfgparser\n"
+ );
+
+ processSQLNamePattern(pset.db, &buf, pattern, true, false,
+ "n.nspname", "c.cfgname", NULL,
+ "pg_catalog.pg_ts_config_is_visible(c.oid)");
+
+ appendPQExpBuffer(&buf, "ORDER BY 3, 2;");
+
+ res = PSQLexec(buf.data, false);
+ termPQExpBuffer(&buf);
+ if (!res)
+ return false;
+
+ if (PQntuples(res) == 0)
+ {
+ if (!pset.quiet)
+ fprintf(stderr, _("Did not find any text search configuration named \"%s\".\n"),
+ pattern);
+ PQclear(res);
+ return false;
+ }
+
+ for (i = 0; i < PQntuples(res); i++)
+ {
+ const char *oid;
+ const char *cfgname;
+ const char *nspname = NULL;
+ const char *prsname;
+ const char *pnspname = NULL;
+
+ oid = PQgetvalue(res, i, 0);
+ cfgname = PQgetvalue(res, i, 1);
+ if (!PQgetisnull(res, i, 2))
+ nspname = PQgetvalue(res, i, 2);
+ prsname = PQgetvalue(res, i, 3);
+ if (!PQgetisnull(res, i, 4))
+ pnspname = PQgetvalue(res, i, 4);
+
+ if (!describeOneTSConfig(oid, nspname, cfgname, pnspname, prsname))
+ {
+ PQclear(res);
+ return false;
+ }
+
+ if (cancel_pressed)
+ {
+ PQclear(res);
+ return false;
+ }
+ }
+
+ PQclear(res);
+ return true;
+}
+
+static bool
+describeOneTSConfig(const char *oid, const char *nspname, const char *cfgname,
+ const char *pnspname, const char *prsname)
+{
+ PQExpBufferData buf,
+ title;
+ PGresult *res;
+ printQueryOpt myopt = pset.popt;
+
+ initPQExpBuffer(&buf);
+
+ printfPQExpBuffer(&buf,
+ "SELECT \n"
+ " ( SELECT t.alias FROM \n"
+ " pg_catalog.ts_token_type(c.cfgparser) AS t \n"
+ " WHERE t.tokid = m.maptokentype ) AS \"%s\", \n"
+ " pg_catalog.btrim( \n"
+ " ARRAY( SELECT mm.mapdict::pg_catalog.regdictionary \n"
+ " FROM pg_catalog.pg_ts_config_map AS mm \n"
+ " WHERE mm.mapcfg = m.mapcfg AND mm.maptokentype = m.maptokentype \n"
+ " ORDER BY mapcfg, maptokentype, mapseqno \n"
+ " ) :: pg_catalog.text , \n"
+ " '{}') AS \"%s\" \n"
+ "FROM pg_catalog.pg_ts_config AS c, pg_catalog.pg_ts_config_map AS m \n"
+ "WHERE c.oid = '%s' AND m.mapcfg = c.oid \n"
+ "GROUP BY m.mapcfg, m.maptokentype, c.cfgparser \n"
+ "ORDER BY 1",
+ _("Token"),
+ _("Dictionaries"),
+ oid);
+
+ res = PSQLexec(buf.data, false);
+ termPQExpBuffer(&buf);
+ if (!res)
+ return false;
+
+ initPQExpBuffer(&title);
+
+ if (nspname)
+ appendPQExpBuffer(&title, _("Text search configuration \"%s.%s\""), nspname, cfgname);
+ else
+ appendPQExpBuffer(&title, _("Text search configuration \"%s\""), cfgname);
+
+ if (pnspname)
+ appendPQExpBuffer(&title, _("\nParser: \"%s.%s\""), pnspname, prsname);
+ else
+ appendPQExpBuffer(&title, _("\nParser: \"%s\""), prsname);
+
+ myopt.nullPrint = NULL;
+ myopt.title = title.data;
+ myopt.footers = NULL;
+ myopt.default_footer = false;
+
+ printQuery(res, &myopt, pset.queryFout, pset.logfile);
+
+ termPQExpBuffer(&title);
+
+ PQclear(res);
+ return true;
+}
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index 88d355f57b2..1ba3b54fc14 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -3,7 +3,7 @@
*
* Copyright (c) 2000-2007, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/bin/psql/describe.h,v 1.33 2007/01/05 22:19:49 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/describe.h,v 1.34 2007/08/21 01:11:22 tgl Exp $
*/
#ifndef DESCRIBE_H
#define DESCRIBE_H
@@ -36,6 +36,18 @@ extern bool objectDescription(const char *pattern);
/* \d foo */
extern bool describeTableDetails(const char *pattern, bool verbose);
+/* \dF */
+extern bool listTSConfigs(const char *pattern, bool verbose);
+
+/* \dFp */
+extern bool listTSParsers(const char *pattern, bool verbose);
+
+/* \dFd */
+extern bool listTSDictionaries(const char *pattern, bool verbose);
+
+/* \dFt */
+extern bool listTSTemplates(const char *pattern, bool verbose);
+
/* \l */
extern bool listAllDbs(bool verbose);
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 027b5cb0207..b220339a498 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -3,7 +3,7 @@
*
* Copyright (c) 2000-2007, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/bin/psql/help.c,v 1.117 2007/02/23 18:20:59 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/help.c,v 1.118 2007/08/21 01:11:22 tgl Exp $
*/
#include "postgres_fe.h"
@@ -219,6 +219,10 @@ slashUsage(unsigned short int pager)
fprintf(output, _(" \\dd [PATTERN] show comment for object\n"));
fprintf(output, _(" \\dD [PATTERN] list domains\n"));
fprintf(output, _(" \\df [PATTERN] list functions (add \"+\" for more detail)\n"));
+ fprintf(output, _(" \\dF [PATTERN] list text search configurations (add \"+\" for more detail)\n"));
+ fprintf(output, _(" \\dFd [PATTERN] list text search dictionaries (add \"+\" for more detail)\n"));
+ fprintf(output, _(" \\dFt [PATTERN] list text search templates\n"));
+ fprintf(output, _(" \\dFp [PATTERN] list text search parsers (add \"+\" for more detail)\n"));
fprintf(output, _(" \\dg [PATTERN] list groups\n"));
fprintf(output, _(" \\dn [PATTERN] list schemas (add \"+\" for more detail)\n"));
fprintf(output, _(" \\do [NAME] list operators\n"));