citus/src/backend/distributed/commands/text_search.c

1594 lines
49 KiB
C

/*-------------------------------------------------------------------------
*
* text_search.c
* Commands for creating and altering TEXT SEARCH objects
*
* Copyright (c) Citus Data, Inc.
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/genam.h"
#include "access/xact.h"
#include "catalog/namespace.h"
#include "catalog/objectaddress.h"
#include "catalog/pg_ts_config.h"
#include "catalog/pg_ts_config_map.h"
#include "catalog/pg_ts_dict.h"
#include "catalog/pg_ts_parser.h"
#include "catalog/pg_ts_template.h"
#include "commands/comment.h"
#include "commands/defrem.h"
#include "commands/extension.h"
#include "fmgr.h"
#include "nodes/makefuncs.h"
#include "tsearch/ts_cache.h"
#include "tsearch/ts_public.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
#include "distributed/commands.h"
#include "distributed/commands/utility_hook.h"
#include "distributed/deparser.h"
#include "distributed/listutils.h"
#include "distributed/metadata/distobject.h"
#include "distributed/metadata_sync.h"
#include "distributed/multi_executor.h"
#include "distributed/relation_access_tracking.h"
#include "distributed/worker_create_or_replace.h"
static List * GetDistributedTextSearchConfigurationNames(DropStmt *stmt);
static List * GetDistributedTextSearchDictionaryNames(DropStmt *stmt);
static DefineStmt * GetTextSearchConfigDefineStmt(Oid tsconfigOid);
static DefineStmt * GetTextSearchDictionaryDefineStmt(Oid tsdictOid);
static List * GetTextSearchDictionaryInitOptions(HeapTuple tup, Form_pg_ts_dict dict);
static List * GetTextSearchConfigCommentStmt(Oid tsconfigOid);
static List * GetTextSearchDictionaryCommentStmt(Oid tsconfigOid);
static List * get_ts_parser_namelist(Oid tsparserOid);
static List * GetTextSearchConfigMappingStmt(Oid tsconfigOid);
static List * GetTextSearchConfigOwnerStmts(Oid tsconfigOid);
static List * GetTextSearchDictionaryOwnerStmts(Oid tsdictOid);
static List * get_ts_dict_namelist(Oid tsdictOid);
static List * get_ts_template_namelist(Oid tstemplateOid);
static Oid get_ts_config_parser_oid(Oid tsconfigOid);
static char * get_ts_parser_tokentype_name(Oid parserOid, int32 tokentype);
/*
* PostprocessCreateTextSearchConfigurationStmt is called after the TEXT SEARCH
* CONFIGURATION has been created locally.
*
* Contrary to many other objects a text search configuration is often created as a copy
* of an existing configuration. After the copy there is no relation to the configuration
* that has been copied. This prevents our normal approach of ensuring dependencies to
* exist before forwarding a close ressemblance of the statement the user executed.
*
* Instead we recreate the object based on what we find in our own catalog, hence the
* amount of work we perform in the postprocess function, contrary to other objects.
*/
List *
PostprocessCreateTextSearchConfigurationStmt(Node *node, const char *queryString)
{
DefineStmt *stmt = castNode(DefineStmt, node);
Assert(stmt->kind == OBJECT_TSCONFIGURATION);
if (!ShouldPropagate())
{
return NIL;
}
/* check creation against multi-statement transaction policy */
if (!ShouldPropagateCreateInCoordinatedTransction())
{
return NIL;
}
EnsureCoordinator();
EnsureSequentialMode(OBJECT_TSCONFIGURATION);
ObjectAddress address = GetObjectAddressFromParseTree((Node *) stmt, false);
EnsureDependenciesExistOnAllNodes(&address);
/*
* TEXT SEARCH CONFIGURATION objects are more complex with their mappings and the
* possibility of copying from existing templates that we will require the idempotent
* recreation commands to be run for successful propagation
*/
List *commands = CreateTextSearchConfigDDLCommandsIdempotent(&address);
commands = lcons(DISABLE_DDL_PROPAGATION, commands);
commands = lappend(commands, ENABLE_DDL_PROPAGATION);
return NodeDDLTaskList(NON_COORDINATOR_NODES, commands);
}
/*
* PostprocessCreateTextSearchDictionaryStmt is called after the TEXT SEARCH DICTIONARY has been
* created locally.
*/
List *
PostprocessCreateTextSearchDictionaryStmt(Node *node, const char *queryString)
{
DefineStmt *stmt = castNode(DefineStmt, node);
Assert(stmt->kind == OBJECT_TSDICTIONARY);
if (!ShouldPropagate())
{
return NIL;
}
/* check creation against multi-statement transaction policy */
if (!ShouldPropagateCreateInCoordinatedTransction())
{
return NIL;
}
EnsureCoordinator();
EnsureSequentialMode(OBJECT_TSDICTIONARY);
ObjectAddress address = GetObjectAddressFromParseTree((Node *) stmt, false);
EnsureDependenciesExistOnAllNodes(&address);
QualifyTreeNode(node);
const char *createTSDictionaryStmtSql = DeparseTreeNode(node);
/*
* To prevent recursive propagation in mx architecture, we disable ddl
* propagation before sending the command to workers.
*/
List *commands = list_make3(DISABLE_DDL_PROPAGATION,
(void *) createTSDictionaryStmtSql,
ENABLE_DDL_PROPAGATION);
return NodeDDLTaskList(NON_COORDINATOR_NODES, commands);
}
List *
GetCreateTextSearchConfigStatements(const ObjectAddress *address)
{
Assert(address->classId == TSConfigRelationId);
List *stmts = NIL;
/* CREATE TEXT SEARCH CONFIGURATION ...*/
stmts = lappend(stmts, GetTextSearchConfigDefineStmt(address->objectId));
/* ALTER TEXT SEARCH CONFIGURATION ... OWNER TO ...*/
stmts = list_concat(stmts, GetTextSearchConfigOwnerStmts(address->objectId));
/* COMMENT ON TEXT SEARCH CONFIGURATION ... */
stmts = list_concat(stmts, GetTextSearchConfigCommentStmt(address->objectId));
/* ALTER TEXT SEARCH CONFIGURATION ... ADD MAPPING FOR ... WITH ... */
stmts = list_concat(stmts, GetTextSearchConfigMappingStmt(address->objectId));
return stmts;
}
List *
GetCreateTextSearchDictionaryStatements(const ObjectAddress *address)
{
Assert(address->classId == TSDictionaryRelationId);
List *stmts = NIL;
/* CREATE TEXT SEARCH DICTIONARY ...*/
stmts = lappend(stmts, GetTextSearchDictionaryDefineStmt(address->objectId));
/* ALTER TEXT SEARCH DICTIONARY ... OWNER TO ...*/
stmts = list_concat(stmts, GetTextSearchDictionaryOwnerStmts(address->objectId));
/* COMMENT ON TEXT SEARCH DICTIONARY ... */
stmts = list_concat(stmts, GetTextSearchDictionaryCommentStmt(address->objectId));
return stmts;
}
/*
* CreateTextSearchConfigDDLCommandsIdempotent creates a list of ddl commands to recreate
* a TEXT SERACH CONFIGURATION object in an idempotent manner on workers.
*/
List *
CreateTextSearchConfigDDLCommandsIdempotent(const ObjectAddress *address)
{
List *stmts = GetCreateTextSearchConfigStatements(address);
List *sqls = DeparseTreeNodes(stmts);
return list_make1(WrapCreateOrReplaceList(sqls));
}
/*
* CreateTextSearchDictDDLCommandsIdempotent creates a list of ddl commands to recreate
* a TEXT SEARCH CONFIGURATION object in an idempotent manner on workers.
*/
List *
CreateTextSearchDictDDLCommandsIdempotent(const ObjectAddress *address)
{
List *stmts = GetCreateTextSearchDictionaryStatements(address);
List *sqls = DeparseTreeNodes(stmts);
return list_make1(WrapCreateOrReplaceList(sqls));
}
/*
* PreprocessDropTextSearchConfigurationStmt prepares the statements we need to send to
* the workers. After we have dropped the configurations locally they also got removed from
* pg_dist_object so it is important to do all distribution checks before the change is
* made locally.
*/
List *
PreprocessDropTextSearchConfigurationStmt(Node *node, const char *queryString,
ProcessUtilityContext processUtilityContext)
{
DropStmt *stmt = castNode(DropStmt, node);
Assert(stmt->removeType == OBJECT_TSCONFIGURATION);
if (!ShouldPropagate())
{
return NIL;
}
List *distributedObjects = GetDistributedTextSearchConfigurationNames(stmt);
if (list_length(distributedObjects) == 0)
{
/* no distributed objects to remove */
return NIL;
}
EnsureCoordinator();
EnsureSequentialMode(OBJECT_TSCONFIGURATION);
/*
* Temporarily replace the list of objects being dropped with only the list
* containing the distributed objects. After we have created the sql statement we
* restore the original list of objects to execute on locally.
*
* Because searchpaths on coordinator and workers might not be in sync we fully
* qualify the list before deparsing. This is safe because qualification doesn't
* change the original names in place, but insteads creates new ones.
*/
List *originalObjects = stmt->objects;
stmt->objects = distributedObjects;
QualifyTreeNode((Node *) stmt);
const char *dropStmtSql = DeparseTreeNode((Node *) stmt);
stmt->objects = originalObjects;
List *commands = list_make3(DISABLE_DDL_PROPAGATION,
(void *) dropStmtSql,
ENABLE_DDL_PROPAGATION);
return NodeDDLTaskList(NON_COORDINATOR_METADATA_NODES, commands);
}
/*
* PreprocessDropTextSearchDictionaryStmt prepares the statements we need to send to
* the workers. After we have dropped the dictionaries locally they also got removed from
* pg_dist_object so it is important to do all distribution checks before the change is
* made locally.
*/
List *
PreprocessDropTextSearchDictionaryStmt(Node *node, const char *queryString,
ProcessUtilityContext processUtilityContext)
{
DropStmt *stmt = castNode(DropStmt, node);
Assert(stmt->removeType == OBJECT_TSDICTIONARY);
if (!ShouldPropagate())
{
return NIL;
}
List *distributedObjects = GetDistributedTextSearchDictionaryNames(stmt);
if (list_length(distributedObjects) == 0)
{
/* no distributed objects to remove */
return NIL;
}
EnsureCoordinator();
EnsureSequentialMode(OBJECT_TSDICTIONARY);
/*
* Temporarily replace the list of objects being dropped with only the list
* containing the distributed objects. After we have created the sql statement we
* restore the original list of objects to execute on locally.
*
* Because searchpaths on coordinator and workers might not be in sync we fully
* qualify the list before deparsing. This is safe because qualification doesn't
* change the original names in place, but insteads creates new ones.
*/
List *originalObjects = stmt->objects;
stmt->objects = distributedObjects;
QualifyTreeNode((Node *) stmt);
const char *dropStmtSql = DeparseTreeNode((Node *) stmt);
stmt->objects = originalObjects;
List *commands = list_make3(DISABLE_DDL_PROPAGATION,
(void *) dropStmtSql,
ENABLE_DDL_PROPAGATION);
return NodeDDLTaskList(NON_COORDINATOR_METADATA_NODES, commands);
}
/*
* GetDistributedTextSearchConfigurationNames iterates over all text search configurations
* dropped, and create a list containing all configurations that are distributed.
*/
static List *
GetDistributedTextSearchConfigurationNames(DropStmt *stmt)
{
List *objName = NULL;
List *distributedObjects = NIL;
foreach_ptr(objName, stmt->objects)
{
Oid tsconfigOid = get_ts_config_oid(objName, stmt->missing_ok);
if (!OidIsValid(tsconfigOid))
{
/* skip missing configuration names, they can't be distributed */
continue;
}
ObjectAddress address = { 0 };
ObjectAddressSet(address, TSConfigRelationId, tsconfigOid);
if (!IsObjectDistributed(&address))
{
continue;
}
distributedObjects = lappend(distributedObjects, objName);
}
return distributedObjects;
}
/*
* GetDistributedTextSearchDictionaryNames iterates over all text search dictionaries
* dropped, and create a list containing all dictionaries that are distributed.
*/
static List *
GetDistributedTextSearchDictionaryNames(DropStmt *stmt)
{
List *objName = NULL;
List *distributedObjects = NIL;
foreach_ptr(objName, stmt->objects)
{
Oid tsdictOid = get_ts_dict_oid(objName, stmt->missing_ok);
if (!OidIsValid(tsdictOid))
{
/* skip missing dictionary names, they can't be distributed */
continue;
}
ObjectAddress address = { 0 };
ObjectAddressSet(address, TSDictionaryRelationId, tsdictOid);
if (!IsObjectDistributed(&address))
{
continue;
}
distributedObjects = lappend(distributedObjects, objName);
}
return distributedObjects;
}
/*
* PreprocessAlterTextSearchConfigurationStmt verifies if the configuration being altered
* is distributed in the cluster. If that is the case it will prepare the list of commands
* to send to the worker to apply the same changes remote.
*/
List *
PreprocessAlterTextSearchConfigurationStmt(Node *node, const char *queryString,
ProcessUtilityContext processUtilityContext)
{
AlterTSConfigurationStmt *stmt = castNode(AlterTSConfigurationStmt, node);
ObjectAddress address = GetObjectAddressFromParseTree((Node *) stmt, false);
if (!ShouldPropagateObject(&address))
{
return NIL;
}
EnsureCoordinator();
EnsureSequentialMode(OBJECT_TSCONFIGURATION);
QualifyTreeNode((Node *) stmt);
const char *alterStmtSql = DeparseTreeNode((Node *) stmt);
List *commands = list_make3(DISABLE_DDL_PROPAGATION,
(void *) alterStmtSql,
ENABLE_DDL_PROPAGATION);
return NodeDDLTaskList(NON_COORDINATOR_METADATA_NODES, commands);
}
/*
* PreprocessAlterTextSearchDictionaryStmt verifies if the dictionary being altered is
* distributed in the cluster. If that is the case it will prepare the list of commands to
* send to the worker to apply the same changes remote.
*/
List *
PreprocessAlterTextSearchDictionaryStmt(Node *node, const char *queryString,
ProcessUtilityContext processUtilityContext)
{
AlterTSDictionaryStmt *stmt = castNode(AlterTSDictionaryStmt, node);
ObjectAddress address = GetObjectAddressFromParseTree((Node *) stmt, false);
if (!ShouldPropagateObject(&address))
{
return NIL;
}
EnsureCoordinator();
EnsureSequentialMode(OBJECT_TSDICTIONARY);
QualifyTreeNode((Node *) stmt);
const char *alterStmtSql = DeparseTreeNode((Node *) stmt);
List *commands = list_make3(DISABLE_DDL_PROPAGATION,
(void *) alterStmtSql,
ENABLE_DDL_PROPAGATION);
return NodeDDLTaskList(NON_COORDINATOR_METADATA_NODES, commands);
}
/*
* PreprocessRenameTextSearchConfigurationStmt verifies if the configuration being altered
* is distributed in the cluster. If that is the case it will prepare the list of commands
* to send to the worker to apply the same changes remote.
*/
List *
PreprocessRenameTextSearchConfigurationStmt(Node *node, const char *queryString,
ProcessUtilityContext processUtilityContext)
{
RenameStmt *stmt = castNode(RenameStmt, node);
Assert(stmt->renameType == OBJECT_TSCONFIGURATION);
ObjectAddress address = GetObjectAddressFromParseTree((Node *) stmt, false);
if (!ShouldPropagateObject(&address))
{
return NIL;
}
EnsureCoordinator();
EnsureSequentialMode(OBJECT_TSCONFIGURATION);
QualifyTreeNode((Node *) stmt);
char *ddlCommand = DeparseTreeNode((Node *) stmt);
List *commands = list_make3(DISABLE_DDL_PROPAGATION,
(void *) ddlCommand,
ENABLE_DDL_PROPAGATION);
return NodeDDLTaskList(NON_COORDINATOR_METADATA_NODES, commands);
}
/*
* PreprocessRenameTextSearchDictionaryStmt verifies if the dictionary being altered
* is distributed in the cluster. If that is the case it will prepare the list of commands
* to send to the worker to apply the same changes remote.
*/
List *
PreprocessRenameTextSearchDictionaryStmt(Node *node, const char *queryString,
ProcessUtilityContext processUtilityContext)
{
RenameStmt *stmt = castNode(RenameStmt, node);
Assert(stmt->renameType == OBJECT_TSDICTIONARY);
ObjectAddress address = GetObjectAddressFromParseTree((Node *) stmt, false);
if (!ShouldPropagateObject(&address))
{
return NIL;
}
EnsureCoordinator();
EnsureSequentialMode(OBJECT_TSDICTIONARY);
QualifyTreeNode((Node *) stmt);
char *ddlCommand = DeparseTreeNode((Node *) stmt);
List *commands = list_make3(DISABLE_DDL_PROPAGATION,
(void *) ddlCommand,
ENABLE_DDL_PROPAGATION);
return NodeDDLTaskList(NON_COORDINATOR_METADATA_NODES, commands);
}
/*
* PreprocessAlterTextSearchConfigurationSchemaStmt verifies if the configuration being
* altered is distributed in the cluster. If that is the case it will prepare the list of
* commands to send to the worker to apply the same changes remote.
*/
List *
PreprocessAlterTextSearchConfigurationSchemaStmt(Node *node, const char *queryString,
ProcessUtilityContext
processUtilityContext)
{
AlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);
Assert(stmt->objectType == OBJECT_TSCONFIGURATION);
ObjectAddress address = GetObjectAddressFromParseTree((Node *) stmt,
stmt->missing_ok);
if (!ShouldPropagateObject(&address))
{
return NIL;
}
EnsureCoordinator();
EnsureSequentialMode(OBJECT_TSCONFIGURATION);
QualifyTreeNode((Node *) stmt);
const char *sql = DeparseTreeNode((Node *) stmt);
List *commands = list_make3(DISABLE_DDL_PROPAGATION,
(void *) sql,
ENABLE_DDL_PROPAGATION);
return NodeDDLTaskList(NON_COORDINATOR_METADATA_NODES, commands);
}
/*
* PreprocessAlterTextSearchDictionarySchemaStmt verifies if the dictionary being
* altered is distributed in the cluster. If that is the case it will prepare the list of
* commands to send to the worker to apply the same changes remote.
*/
List *
PreprocessAlterTextSearchDictionarySchemaStmt(Node *node, const char *queryString,
ProcessUtilityContext
processUtilityContext)
{
AlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);
Assert(stmt->objectType == OBJECT_TSDICTIONARY);
ObjectAddress address = GetObjectAddressFromParseTree((Node *) stmt,
stmt->missing_ok);
if (!ShouldPropagateObject(&address))
{
return NIL;
}
EnsureCoordinator();
EnsureSequentialMode(OBJECT_TSDICTIONARY);
QualifyTreeNode((Node *) stmt);
const char *sql = DeparseTreeNode((Node *) stmt);
List *commands = list_make3(DISABLE_DDL_PROPAGATION,
(void *) sql,
ENABLE_DDL_PROPAGATION);
return NodeDDLTaskList(NON_COORDINATOR_METADATA_NODES, commands);
}
/*
* PostprocessAlterTextSearchConfigurationSchemaStmt is invoked after the schema has been
* changed locally. Since changing the schema could result in new dependencies being found
* for this object we re-ensure all the dependencies for the configuration do exist. This
* is solely to propagate the new schema (and all its dependencies) if it was not already
* distributed in the cluster.
*/
List *
PostprocessAlterTextSearchConfigurationSchemaStmt(Node *node, const char *queryString)
{
AlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);
Assert(stmt->objectType == OBJECT_TSCONFIGURATION);
ObjectAddress address = GetObjectAddressFromParseTree((Node *) stmt,
stmt->missing_ok);
if (!ShouldPropagateObject(&address))
{
return NIL;
}
/* dependencies have changed (schema) let's ensure they exist */
EnsureDependenciesExistOnAllNodes(&address);
return NIL;
}
/*
* PostprocessAlterTextSearchDictionarySchemaStmt is invoked after the schema has been
* changed locally. Since changing the schema could result in new dependencies being found
* for this object we re-ensure all the dependencies for the dictionary do exist. This
* is solely to propagate the new schema (and all its dependencies) if it was not already
* distributed in the cluster.
*/
List *
PostprocessAlterTextSearchDictionarySchemaStmt(Node *node, const char *queryString)
{
AlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);
Assert(stmt->objectType == OBJECT_TSDICTIONARY);
ObjectAddress address = GetObjectAddressFromParseTree((Node *) stmt,
stmt->missing_ok);
if (!ShouldPropagateObject(&address))
{
return NIL;
}
/* dependencies have changed (schema) let's ensure they exist */
EnsureDependenciesExistOnAllNodes(&address);
return NIL;
}
/*
* PreprocessTextSearchConfigurationCommentStmt propagates any comment on a distributed
* configuration to the workers. Since comments for configurations are promenently shown
* when listing all text search configurations this is purely a cosmetic thing when
* running in MX.
*/
List *
PreprocessTextSearchConfigurationCommentStmt(Node *node, const char *queryString,
ProcessUtilityContext processUtilityContext)
{
CommentStmt *stmt = castNode(CommentStmt, node);
Assert(stmt->objtype == OBJECT_TSCONFIGURATION);
ObjectAddress address = GetObjectAddressFromParseTree((Node *) stmt, false);
if (!ShouldPropagateObject(&address))
{
return NIL;
}
EnsureCoordinator();
EnsureSequentialMode(OBJECT_TSCONFIGURATION);
QualifyTreeNode((Node *) stmt);
const char *sql = DeparseTreeNode((Node *) stmt);
List *commands = list_make3(DISABLE_DDL_PROPAGATION,
(void *) sql,
ENABLE_DDL_PROPAGATION);
return NodeDDLTaskList(NON_COORDINATOR_METADATA_NODES, commands);
}
/*
* PreprocessTextSearchDictionaryCommentStmt propagates any comment on a distributed
* dictionary to the workers. Since comments for dictionaries are promenently shown
* when listing all text search dictionaries this is purely a cosmetic thing when
* running in MX.
*/
List *
PreprocessTextSearchDictionaryCommentStmt(Node *node, const char *queryString,
ProcessUtilityContext processUtilityContext)
{
CommentStmt *stmt = castNode(CommentStmt, node);
Assert(stmt->objtype == OBJECT_TSDICTIONARY);
ObjectAddress address = GetObjectAddressFromParseTree((Node *) stmt, false);
if (!ShouldPropagateObject(&address))
{
return NIL;
}
EnsureCoordinator();
EnsureSequentialMode(OBJECT_TSDICTIONARY);
QualifyTreeNode((Node *) stmt);
const char *sql = DeparseTreeNode((Node *) stmt);
List *commands = list_make3(DISABLE_DDL_PROPAGATION,
(void *) sql,
ENABLE_DDL_PROPAGATION);
return NodeDDLTaskList(NON_COORDINATOR_METADATA_NODES, commands);
}
/*
* PreprocessAlterTextSearchConfigurationOwnerStmt verifies if the configuration being
* altered is distributed in the cluster. If that is the case it will prepare the list of
* commands to send to the worker to apply the same changes remote.
*/
List *
PreprocessAlterTextSearchConfigurationOwnerStmt(Node *node, const char *queryString,
ProcessUtilityContext
processUtilityContext)
{
AlterOwnerStmt *stmt = castNode(AlterOwnerStmt, node);
Assert(stmt->objectType == OBJECT_TSCONFIGURATION);
ObjectAddress address = GetObjectAddressFromParseTree((Node *) stmt, false);
if (!ShouldPropagateObject(&address))
{
return NIL;
}
EnsureCoordinator();
EnsureSequentialMode(OBJECT_TSCONFIGURATION);
QualifyTreeNode((Node *) stmt);
char *sql = DeparseTreeNode((Node *) stmt);
List *commands = list_make3(DISABLE_DDL_PROPAGATION,
(void *) sql,
ENABLE_DDL_PROPAGATION);
return NodeDDLTaskList(NON_COORDINATOR_NODES, commands);
}
/*
* PreprocessAlterTextSearchDictionaryOwnerStmt verifies if the dictionary being
* altered is distributed in the cluster. If that is the case it will prepare the list of
* commands to send to the worker to apply the same changes remote.
*/
List *
PreprocessAlterTextSearchDictionaryOwnerStmt(Node *node, const char *queryString,
ProcessUtilityContext
processUtilityContext)
{
AlterOwnerStmt *stmt = castNode(AlterOwnerStmt, node);
Assert(stmt->objectType == OBJECT_TSDICTIONARY);
ObjectAddress address = GetObjectAddressFromParseTree((Node *) stmt, false);
if (!ShouldPropagateObject(&address))
{
return NIL;
}
EnsureCoordinator();
EnsureSequentialMode(OBJECT_TSDICTIONARY);
QualifyTreeNode((Node *) stmt);
char *sql = DeparseTreeNode((Node *) stmt);
List *commands = list_make3(DISABLE_DDL_PROPAGATION,
(void *) sql,
ENABLE_DDL_PROPAGATION);
return NodeDDLTaskList(NON_COORDINATOR_NODES, commands);
}
/*
* PostprocessAlterTextSearchConfigurationOwnerStmt is invoked after the owner has been
* changed locally. Since changing the owner could result in new dependencies being found
* for this object we re-ensure all the dependencies for the configuration do exist. This
* is solely to propagate the new owner (and all its dependencies) if it was not already
* distributed in the cluster.
*/
List *
PostprocessAlterTextSearchConfigurationOwnerStmt(Node *node, const char *queryString)
{
AlterOwnerStmt *stmt = castNode(AlterOwnerStmt, node);
Assert(stmt->objectType == OBJECT_TSCONFIGURATION);
ObjectAddress address = GetObjectAddressFromParseTree((Node *) stmt, false);
if (!ShouldPropagateObject(&address))
{
return NIL;
}
/* dependencies have changed (owner) let's ensure they exist */
EnsureDependenciesExistOnAllNodes(&address);
return NIL;
}
/*
* PostprocessAlterTextSearchDictionaryOwnerStmt is invoked after the owner has been
* changed locally. Since changing the owner could result in new dependencies being found
* for this object we re-ensure all the dependencies for the dictionary do exist. This
* is solely to propagate the new owner (and all its dependencies) if it was not already
* distributed in the cluster.
*/
List *
PostprocessAlterTextSearchDictionaryOwnerStmt(Node *node, const char *queryString)
{
AlterOwnerStmt *stmt = castNode(AlterOwnerStmt, node);
Assert(stmt->objectType == OBJECT_TSDICTIONARY);
ObjectAddress address = GetObjectAddressFromParseTree((Node *) stmt, false);
if (!ShouldPropagateObject(&address))
{
return NIL;
}
/* dependencies have changed (owner) let's ensure they exist */
EnsureDependenciesExistOnAllNodes(&address);
return NIL;
}
/*
* GetTextSearchConfigDefineStmt returns the DefineStmt for a TEXT SEARCH CONFIGURATION
* based on the configuration as defined in the catalog identified by tsconfigOid.
*
* This statement will only contain the parser, as all other properties for text search
* configurations are stored as mappings in a different catalog.
*/
static DefineStmt *
GetTextSearchConfigDefineStmt(Oid tsconfigOid)
{
HeapTuple tup = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(tsconfigOid));
if (!HeapTupleIsValid(tup)) /* should not happen */
{
elog(ERROR, "cache lookup failed for text search configuration %u",
tsconfigOid);
}
Form_pg_ts_config config = (Form_pg_ts_config) GETSTRUCT(tup);
DefineStmt *stmt = makeNode(DefineStmt);
stmt->kind = OBJECT_TSCONFIGURATION;
stmt->defnames = get_ts_config_namelist(tsconfigOid);
List *parserNameList = get_ts_parser_namelist(config->cfgparser);
TypeName *parserTypeName = makeTypeNameFromNameList(parserNameList);
stmt->definition = list_make1(makeDefElem("parser", (Node *) parserTypeName, -1));
ReleaseSysCache(tup);
return stmt;
}
/*
* GetTextSearchDictionaryDefineStmt returns the DefineStmt for a TEXT SEARCH DICTIONARY
* based on the dictionary as defined in the catalog identified by tsdictOid.
*
* This statement will contain the template along with all initilaization options.
*/
static DefineStmt *
GetTextSearchDictionaryDefineStmt(Oid tsdictOid)
{
HeapTuple tup = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(tsdictOid));
if (!HeapTupleIsValid(tup)) /* should not happen */
{
elog(ERROR, "cache lookup failed for text search dictionary %u",
tsdictOid);
}
Form_pg_ts_dict dict = (Form_pg_ts_dict) GETSTRUCT(tup);
DefineStmt *stmt = makeNode(DefineStmt);
stmt->kind = OBJECT_TSDICTIONARY;
stmt->defnames = get_ts_dict_namelist(tsdictOid);
stmt->definition = GetTextSearchDictionaryInitOptions(tup, dict);
ReleaseSysCache(tup);
return stmt;
}
/*
* GetTextSearchDictionaryInitOptions returns the list of DefElem for the initialization
* options for a TEXT SEARCH DICTIONARY.
*
* The initialization options contain both the template name, and template specific key,
* value pairs that are supplied when the dictionary was first created.
*/
static List *
GetTextSearchDictionaryInitOptions(HeapTuple tup, Form_pg_ts_dict dict)
{
List *templateNameList = get_ts_template_namelist(dict->dicttemplate);
TypeName *templateTypeName = makeTypeNameFromNameList(templateNameList);
DefElem *templateDefElem = makeDefElem("template", (Node *) templateTypeName, -1);
Relation TSDictionaryRelation = table_open(TSDictionaryRelationId, AccessShareLock);
TupleDesc TSDictDescription = RelationGetDescr(TSDictionaryRelation);
bool isnull = false;
Datum dictinitoption = heap_getattr(tup, Anum_pg_ts_dict_dictinitoption,
TSDictDescription, &isnull);
List *initOptionDefElemList = NIL;
if (!isnull)
{
initOptionDefElemList = deserialize_deflist(dictinitoption);
}
table_close(TSDictionaryRelation, AccessShareLock);
return lcons(templateDefElem, initOptionDefElemList);
}
/*
* GetTextSearchConfigCommentStmt returns a list containing all entries to recreate a
* comment on the configuration identified by tsconfigOid. The list could be empty if
* there is no comment on a configuration.
*
* The reason for a list is for easy use when building a list of all statements to invoke
* to recreate the text search configuration. An empty list can easily be concatinated
* without inspection, contrary to a NULL ptr if we would return the CommentStmt struct.
*/
static List *
GetTextSearchConfigCommentStmt(Oid tsconfigOid)
{
char *comment = GetComment(tsconfigOid, TSConfigRelationId, 0);
if (!comment)
{
return NIL;
}
CommentStmt *stmt = makeNode(CommentStmt);
stmt->objtype = OBJECT_TSCONFIGURATION;
stmt->object = (Node *) get_ts_config_namelist(tsconfigOid);
stmt->comment = comment;
return list_make1(stmt);
}
/*
* GetTextSearchDictionaryCommentStmt returns a list containing all entries to recreate a
* comment on the dictionary identified by tsconfigOid. The list could be empty if
* there is no comment on a dictionary.
*
* The reason for a list is for easy use when building a list of all statements to invoke
* to recreate the text search dictionary. An empty list can easily be concatinated
* without inspection, contrary to a NULL ptr if we would return the CommentStmt struct.
*/
static List *
GetTextSearchDictionaryCommentStmt(Oid tsdictOid)
{
char *comment = GetComment(tsdictOid, TSDictionaryRelationId, 0);
if (!comment)
{
return NIL;
}
CommentStmt *stmt = makeNode(CommentStmt);
stmt->objtype = OBJECT_TSDICTIONARY;
stmt->object = (Node *) get_ts_dict_namelist(tsdictOid);
stmt->comment = comment;
return list_make1(stmt);
}
/*
* GetTextSearchConfigMappingStmt returns a list of all mappings from token_types to
* dictionaries configured on a text search configuration identified by tsconfigOid.
*
* Many mappings can exist on a configuration which all require their own statement to
* recreate.
*/
static List *
GetTextSearchConfigMappingStmt(Oid tsconfigOid)
{
ScanKeyData mapskey = { 0 };
/* mapcfg = tsconfigOid */
ScanKeyInit(&mapskey,
Anum_pg_ts_config_map_mapcfg,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(tsconfigOid));
Relation maprel = table_open(TSConfigMapRelationId, AccessShareLock);
Relation mapidx = index_open(TSConfigMapIndexId, AccessShareLock);
SysScanDesc mapscan = systable_beginscan_ordered(maprel, mapidx, NULL, 1, &mapskey);
List *stmts = NIL;
AlterTSConfigurationStmt *stmt = NULL;
/*
* We iterate the config mappings on the index order filtered by mapcfg. Meaning we
* get equal maptokentype's in 1 run. By comparing the current tokentype to the last
* we know when we can create a new stmt and append the previous constructed one to
* the list.
*/
int lastTokType = -1;
/*
* We read all mappings filtered by config id, hence we only need to load the name
* once and can reuse for every statement.
*/
List *configName = get_ts_config_namelist(tsconfigOid);
Oid parserOid = get_ts_config_parser_oid(tsconfigOid);
HeapTuple maptup = NULL;
while ((maptup = systable_getnext_ordered(mapscan, ForwardScanDirection)) != NULL)
{
Form_pg_ts_config_map cfgmap = (Form_pg_ts_config_map) GETSTRUCT(maptup);
if (lastTokType != cfgmap->maptokentype)
{
/* creating a new statement, appending the previous one (if existing) */
if (stmt != NULL)
{
stmts = lappend(stmts, stmt);
}
stmt = makeNode(AlterTSConfigurationStmt);
stmt->cfgname = configName;
stmt->kind = ALTER_TSCONFIG_ADD_MAPPING;
stmt->tokentype = list_make1(makeString(
get_ts_parser_tokentype_name(parserOid,
cfgmap->
maptokentype)));
lastTokType = cfgmap->maptokentype;
}
stmt->dicts = lappend(stmt->dicts, get_ts_dict_namelist(cfgmap->mapdict));
}
/*
* If we have ran atleast 1 iteration above we have the last stmt not added to the
* stmts list.
*/
if (stmt != NULL)
{
stmts = lappend(stmts, stmt);
stmt = NULL;
}
systable_endscan_ordered(mapscan);
index_close(mapidx, NoLock);
table_close(maprel, NoLock);
return stmts;
}
/*
* GetTextSearchConfigOwnerStmts returns a potentially empty list of statements to change
* the ownership of a TEXT SEARCH CONFIGURATION object.
*
* The list is for convenience when building a full list of statements to recreate the
* configuration.
*/
static List *
GetTextSearchConfigOwnerStmts(Oid tsconfigOid)
{
HeapTuple tup = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(tsconfigOid));
if (!HeapTupleIsValid(tup)) /* should not happen */
{
elog(ERROR, "cache lookup failed for text search configuration %u",
tsconfigOid);
}
Form_pg_ts_config config = (Form_pg_ts_config) GETSTRUCT(tup);
AlterOwnerStmt *stmt = makeNode(AlterOwnerStmt);
stmt->objectType = OBJECT_TSCONFIGURATION;
stmt->object = (Node *) get_ts_config_namelist(tsconfigOid);
stmt->newowner = GetRoleSpecObjectForUser(config->cfgowner);
ReleaseSysCache(tup);
return list_make1(stmt);
}
/*
* GetTextSearchDictionaryOwnerStmts returns a potentially empty list of statements to change
* the ownership of a TEXT SEARCH DICTIONARY object.
*
* The list is for convenience when building a full list of statements to recreate the
* dictionary.
*/
static List *
GetTextSearchDictionaryOwnerStmts(Oid tsdictOid)
{
HeapTuple tup = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(tsdictOid));
if (!HeapTupleIsValid(tup)) /* should not happen */
{
elog(ERROR, "cache lookup failed for text search dictionary %u",
tsdictOid);
}
Form_pg_ts_dict dict = (Form_pg_ts_dict) GETSTRUCT(tup);
AlterOwnerStmt *stmt = makeNode(AlterOwnerStmt);
stmt->objectType = OBJECT_TSDICTIONARY;
stmt->object = (Node *) get_ts_dict_namelist(tsdictOid);
stmt->newowner = GetRoleSpecObjectForUser(dict->dictowner);
ReleaseSysCache(tup);
return list_make1(stmt);
}
/*
* get_ts_config_namelist based on the tsconfigOid this function creates the namelist that
* identifies the configuration in a fully qualified manner, irregardless of the schema
* existing on the search_path.
*/
List *
get_ts_config_namelist(Oid tsconfigOid)
{
HeapTuple tup = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(tsconfigOid));
if (!HeapTupleIsValid(tup)) /* should not happen */
{
elog(ERROR, "cache lookup failed for text search configuration %u",
tsconfigOid);
}
Form_pg_ts_config config = (Form_pg_ts_config) GETSTRUCT(tup);
char *schema = get_namespace_name(config->cfgnamespace);
char *configName = pstrdup(NameStr(config->cfgname));
List *names = list_make2(makeString(schema), makeString(configName));
ReleaseSysCache(tup);
return names;
}
/*
* get_ts_dict_namelist based on the tsdictOid this function creates the namelist that
* identifies the dictionary in a fully qualified manner, irregardless of the schema
* existing on the search_path.
*/
static List *
get_ts_dict_namelist(Oid tsdictOid)
{
HeapTuple tup = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(tsdictOid));
if (!HeapTupleIsValid(tup)) /* should not happen */
{
elog(ERROR, "cache lookup failed for text search dictionary %u", tsdictOid);
}
Form_pg_ts_dict dict = (Form_pg_ts_dict) GETSTRUCT(tup);
char *schema = get_namespace_name(dict->dictnamespace);
char *dictName = pstrdup(NameStr(dict->dictname));
List *names = list_make2(makeString(schema), makeString(dictName));
ReleaseSysCache(tup);
return names;
}
/*
* get_ts_template_namelist based on the tstemplateOid this function creates the namelist
* that identifies the template in a fully qualified manner, irregardless of the schema
* existing on the search_path.
*/
static List *
get_ts_template_namelist(Oid tstemplateOid)
{
HeapTuple tup = SearchSysCache1(TSTEMPLATEOID, ObjectIdGetDatum(tstemplateOid));
if (!HeapTupleIsValid(tup)) /* should not happen */
{
elog(ERROR, "cache lookup failed for text search template %u", tstemplateOid);
}
Form_pg_ts_template template = (Form_pg_ts_template) GETSTRUCT(tup);
char *schema = get_namespace_name(template->tmplnamespace);
char *templateName = pstrdup(NameStr(template->tmplname));
List *names = list_make2(makeString(schema), makeString(templateName));
ReleaseSysCache(tup);
return names;
}
/*
* get_ts_config_parser_oid based on the tsconfigOid this function returns the Oid of the
* parser used in the configuration.
*/
static Oid
get_ts_config_parser_oid(Oid tsconfigOid)
{
HeapTuple tup = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(tsconfigOid));
if (!HeapTupleIsValid(tup)) /* should not happen */
{
elog(ERROR, "cache lookup failed for text search configuration %u", tsconfigOid);
}
Form_pg_ts_config config = (Form_pg_ts_config) GETSTRUCT(tup);
Oid parserOid = config->cfgparser;
ReleaseSysCache(tup);
return parserOid;
}
/*
* get_ts_parser_tokentype_name returns the name of the token as known to the parser by
* its tokentype identifier. The parser used to resolve the token name is identified by
* parserOid and should be the same that emitted the tokentype to begin with.
*/
static char *
get_ts_parser_tokentype_name(Oid parserOid, int32 tokentype)
{
TSParserCacheEntry *parserCache = lookup_ts_parser_cache(parserOid);
if (!OidIsValid(parserCache->lextypeOid))
{
elog(ERROR, "method lextype isn't defined for text search parser %u", parserOid);
}
/* take lextypes from parser */
LexDescr *tokenlist = (LexDescr *) DatumGetPointer(
OidFunctionCall1(parserCache->lextypeOid, Int32GetDatum(0)));
/* and find the one with lexid = tokentype */
int tokenIndex = 0;
while (tokenlist && tokenlist[tokenIndex].lexid)
{
if (tokenlist[tokenIndex].lexid == tokentype)
{
return pstrdup(tokenlist[tokenIndex].alias);
}
tokenIndex++;
}
/* we haven't found the token */
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("token type \"%d\" does not exist in parser", tokentype)));
}
/*
* get_ts_parser_namelist based on the tsparserOid this function creates the namelist that
* identifies the parser in a fully qualified manner, irregardless of the schema existing
* on the search_path.
*/
static List *
get_ts_parser_namelist(Oid tsparserOid)
{
HeapTuple tup = SearchSysCache1(TSPARSEROID, ObjectIdGetDatum(tsparserOid));
if (!HeapTupleIsValid(tup)) /* should not happen */
{
elog(ERROR, "cache lookup failed for text search parser %u",
tsparserOid);
}
Form_pg_ts_parser parser = (Form_pg_ts_parser) GETSTRUCT(tup);
char *schema = get_namespace_name(parser->prsnamespace);
char *parserName = pstrdup(NameStr(parser->prsname));
List *names = list_make2(makeString(schema), makeString(parserName));
ReleaseSysCache(tup);
return names;
}
/*
* CreateTextSearchConfigurationObjectAddress resolves the ObjectAddress for the object
* being created. If missing_pk is false the function will error, explaining to the user
* the text search configuration described in the statement doesn't exist.
*/
ObjectAddress
CreateTextSearchConfigurationObjectAddress(Node *node, bool missing_ok)
{
DefineStmt *stmt = castNode(DefineStmt, node);
Assert(stmt->kind == OBJECT_TSCONFIGURATION);
Oid objid = get_ts_config_oid(stmt->defnames, missing_ok);
ObjectAddress address = { 0 };
ObjectAddressSet(address, TSConfigRelationId, objid);
return address;
}
/*
* CreateTextSearchDictObjectAddress resolves the ObjectAddress for the object
* being created. If missing_pk is false the function will error, explaining to the user
* the text search dictionary described in the statement doesn't exist.
*/
ObjectAddress
CreateTextSearchDictObjectAddress(Node *node, bool missing_ok)
{
DefineStmt *stmt = castNode(DefineStmt, node);
Assert(stmt->kind == OBJECT_TSDICTIONARY);
Oid objid = get_ts_dict_oid(stmt->defnames, missing_ok);
ObjectAddress address = { 0 };
ObjectAddressSet(address, TSDictionaryRelationId, objid);
return address;
}
/*
* RenameTextSearchConfigurationStmtObjectAddress resolves the ObjectAddress for the TEXT
* SEARCH CONFIGURATION being renamed. Optionally errors if the configuration does not
* exist based on the missing_ok flag passed in by the caller.
*/
ObjectAddress
RenameTextSearchConfigurationStmtObjectAddress(Node *node, bool missing_ok)
{
RenameStmt *stmt = castNode(RenameStmt, node);
Assert(stmt->renameType == OBJECT_TSCONFIGURATION);
Oid objid = get_ts_config_oid(castNode(List, stmt->object), missing_ok);
ObjectAddress address = { 0 };
ObjectAddressSet(address, TSConfigRelationId, objid);
return address;
}
/*
* RenameTextSearchDictionaryStmtObjectAddress resolves the ObjectAddress for the TEXT
* SEARCH DICTIONARY being renamed. Optionally errors if the dictionary does not
* exist based on the missing_ok flag passed in by the caller.
*/
ObjectAddress
RenameTextSearchDictionaryStmtObjectAddress(Node *node, bool missing_ok)
{
RenameStmt *stmt = castNode(RenameStmt, node);
Assert(stmt->renameType == OBJECT_TSDICTIONARY);
Oid objid = get_ts_dict_oid(castNode(List, stmt->object), missing_ok);
ObjectAddress address = { 0 };
ObjectAddressSet(address, TSDictionaryRelationId, objid);
return address;
}
/*
* AlterTextSearchConfigurationStmtObjectAddress resolves the ObjectAddress for the TEXT
* SEARCH CONFIGURATION being altered. Optionally errors if the configuration does not
* exist based on the missing_ok flag passed in by the caller.
*/
ObjectAddress
AlterTextSearchConfigurationStmtObjectAddress(Node *node, bool missing_ok)
{
AlterTSConfigurationStmt *stmt = castNode(AlterTSConfigurationStmt, node);
Oid objid = get_ts_config_oid(stmt->cfgname, missing_ok);
ObjectAddress address = { 0 };
ObjectAddressSet(address, TSConfigRelationId, objid);
return address;
}
/*
* AlterTextSearchDictionaryStmtObjectAddress resolves the ObjectAddress for the TEXT
* SEARCH CONFIGURATION being altered. Optionally errors if the configuration does not
* exist based on the missing_ok flag passed in by the caller.
*/
ObjectAddress
AlterTextSearchDictionaryStmtObjectAddress(Node *node, bool missing_ok)
{
AlterTSDictionaryStmt *stmt = castNode(AlterTSDictionaryStmt, node);
Oid objid = get_ts_dict_oid(stmt->dictname, missing_ok);
ObjectAddress address = { 0 };
ObjectAddressSet(address, TSDictionaryRelationId, objid);
return address;
}
/*
* AlterTextSearchConfigurationSchemaStmtObjectAddress resolves the ObjectAddress for the
* TEXT SEARCH CONFIGURATION being moved to a different schema. Optionally errors if the
* configuration does not exist based on the missing_ok flag passed in by the caller.
*
* This can be called, either before or after the move of schema has been executed, hence
* the triple checking before the error might be thrown. Errors for non-existing schema's
* in edgecases will be raised by postgres while executing the move.
*/
ObjectAddress
AlterTextSearchConfigurationSchemaStmtObjectAddress(Node *node, bool missing_ok)
{
AlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);
Assert(stmt->objectType == OBJECT_TSCONFIGURATION);
Oid objid = get_ts_config_oid(castNode(List, stmt->object), true);
if (!OidIsValid(objid))
{
/*
* couldn't find the text search configuration, might have already been moved to
* the new schema, we construct a new sequence name that uses the new schema to
* search in.
*/
char *schemaname = NULL;
char *config_name = NULL;
DeconstructQualifiedName(castNode(List, stmt->object), &schemaname, &config_name);
char *newSchemaName = stmt->newschema;
List *names = list_make2(makeString(newSchemaName), makeString(config_name));
objid = get_ts_config_oid(names, true);
if (!missing_ok && !OidIsValid(objid))
{
/*
* if the text search config id is still invalid we couldn't find it, error
* with the same message postgres would error with if missing_ok is false
* (not ok to miss)
*/
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("text search configuration \"%s\" does not exist",
NameListToString(castNode(List, stmt->object)))));
}
}
ObjectAddress sequenceAddress = { 0 };
ObjectAddressSet(sequenceAddress, TSConfigRelationId, objid);
return sequenceAddress;
}
/*
* AlterTextSearchDictionarySchemaStmtObjectAddress resolves the ObjectAddress for the
* TEXT SEARCH DICTIONARY being moved to a different schema. Optionally errors if the
* dictionary does not exist based on the missing_ok flag passed in by the caller.
*
* This can be called, either before or after the move of schema has been executed, hence
* the triple checking before the error might be thrown. Errors for non-existing schema's
* in edgecases will be raised by postgres while executing the move.
*/
ObjectAddress
AlterTextSearchDictionarySchemaStmtObjectAddress(Node *node, bool missing_ok)
{
AlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);
Assert(stmt->objectType == OBJECT_TSDICTIONARY);
Oid objid = get_ts_dict_oid(castNode(List, stmt->object), true);
if (!OidIsValid(objid))
{
/*
* couldn't find the text search dictionary, might have already been moved to
* the new schema, we construct a new sequence name that uses the new schema to
* search in.
*/
char *schemaname = NULL;
char *dict_name = NULL;
DeconstructQualifiedName(castNode(List, stmt->object), &schemaname, &dict_name);
char *newSchemaName = stmt->newschema;
List *names = list_make2(makeString(newSchemaName), makeString(dict_name));
objid = get_ts_dict_oid(names, true);
if (!missing_ok && !OidIsValid(objid))
{
/*
* if the text search dict id is still invalid we couldn't find it, error
* with the same message postgres would error with if missing_ok is false
* (not ok to miss)
*/
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("text search dictionary \"%s\" does not exist",
NameListToString(castNode(List, stmt->object)))));
}
}
ObjectAddress sequenceAddress = { 0 };
ObjectAddressSet(sequenceAddress, TSDictionaryRelationId, objid);
return sequenceAddress;
}
/*
* TextSearchConfigurationCommentObjectAddress resolves the ObjectAddress for the TEXT
* SEARCH CONFIGURATION on which the comment is placed. Optionally errors if the
* configuration does not exist based on the missing_ok flag passed in by the caller.
*/
ObjectAddress
TextSearchConfigurationCommentObjectAddress(Node *node, bool missing_ok)
{
CommentStmt *stmt = castNode(CommentStmt, node);
Assert(stmt->objtype == OBJECT_TSCONFIGURATION);
Oid objid = get_ts_config_oid(castNode(List, stmt->object), missing_ok);
ObjectAddress address = { 0 };
ObjectAddressSet(address, TSConfigRelationId, objid);
return address;
}
/*
* TextSearchDictCommentObjectAddress resolves the ObjectAddress for the TEXT SEARCH
* DICTIONARY on which the comment is placed. Optionally errors if the dictionary does not
* exist based on the missing_ok flag passed in by the caller.
*/
ObjectAddress
TextSearchDictCommentObjectAddress(Node *node, bool missing_ok)
{
CommentStmt *stmt = castNode(CommentStmt, node);
Assert(stmt->objtype == OBJECT_TSDICTIONARY);
Oid objid = get_ts_dict_oid(castNode(List, stmt->object), missing_ok);
ObjectAddress address = { 0 };
ObjectAddressSet(address, TSDictionaryRelationId, objid);
return address;
}
/*
* AlterTextSearchConfigurationOwnerObjectAddress resolves the ObjectAddress for the TEXT
* SEARCH CONFIGURATION for which the owner is changed. Optionally errors if the
* configuration does not exist based on the missing_ok flag passed in by the caller.
*/
ObjectAddress
AlterTextSearchConfigurationOwnerObjectAddress(Node *node, bool missing_ok)
{
AlterOwnerStmt *stmt = castNode(AlterOwnerStmt, node);
Relation relation = NULL;
Assert(stmt->objectType == OBJECT_TSCONFIGURATION);
return get_object_address(stmt->objectType, stmt->object, &relation, AccessShareLock,
missing_ok);
}
/*
* AlterTextSearchDictOwnerObjectAddress resolves the ObjectAddress for the TEXT
* SEARCH DICTIONARY for which the owner is changed. Optionally errors if the
* configuration does not exist based on the missing_ok flag passed in by the caller.
*/
ObjectAddress
AlterTextSearchDictOwnerObjectAddress(Node *node, bool missing_ok)
{
AlterOwnerStmt *stmt = castNode(AlterOwnerStmt, node);
Relation relation = NULL;
Assert(stmt->objectType == OBJECT_TSDICTIONARY);
return get_object_address(stmt->objectType, stmt->object, &relation, AccessShareLock,
missing_ok);
}
/*
* GenerateBackupNameForTextSearchConfiguration generates a safe name that is not in use
* already that can be used to rename an existing TEXT SEARCH CONFIGURATION to allow the
* configuration with a specific name to be created, even if this would not have been
* possible due to name collisions.
*/
char *
GenerateBackupNameForTextSearchConfiguration(const ObjectAddress *address)
{
Assert(address->classId == TSConfigRelationId);
List *names = get_ts_config_namelist(address->objectId);
RangeVar *rel = makeRangeVarFromNameList(names);
char *newName = palloc0(NAMEDATALEN);
char suffix[NAMEDATALEN] = { 0 };
char *baseName = rel->relname;
int baseLength = strlen(baseName);
int count = 0;
while (true)
{
int suffixLength = SafeSnprintf(suffix, NAMEDATALEN - 1, "(citus_backup_%d)",
count);
/* trim the base name at the end to leave space for the suffix and trailing \0 */
baseLength = Min(baseLength, NAMEDATALEN - suffixLength - 1);
/* clear newName before copying the potentially trimmed baseName and suffix */
memset(newName, 0, NAMEDATALEN);
strncpy_s(newName, NAMEDATALEN, baseName, baseLength);
strncpy_s(newName + baseLength, NAMEDATALEN - baseLength, suffix,
suffixLength);
rel->relname = newName;
List *newNameList = MakeNameListFromRangeVar(rel);
Oid tsconfigOid = get_ts_config_oid(newNameList, true);
if (!OidIsValid(tsconfigOid))
{
return newName;
}
count++;
}
}