Add support for TEXT SEARCH DICTIONARY objects

TEXT SEARCH DICTIONARY objects depend on TEXT SEARCH TEMPLATE objects.
Since we do not yet support distributed TS TEMPLATE objects, we skip
dependency checks for text search templates, similar to what we do for
roles.

The user is expected to manually create the TEXT SEARCH TEMPLATE objects
before a) adding new nodes, b) creating TEXT SEARCH DICTIONARY objects.
pull/5772/head
Hanefi Onaldi 2022-03-08 01:07:53 +03:00
parent 49467e27e6
commit b0eb685101
No known key found for this signature in database
GPG Key ID: F18CDB10BA0DFDC7
16 changed files with 1490 additions and 80 deletions

View File

@ -398,6 +398,11 @@ GetDependencyCreateDDLCommands(const ObjectAddress *dependency)
return CreateTextSearchConfigDDLCommandsIdempotent(dependency);
}
case OCLASS_TSDICT:
{
return CreateTextSearchDictDDLCommandsIdempotent(dependency);
}
case OCLASS_TYPE:
{
return CreateTypeDDLCommandsIdempotent(dependency);

View File

@ -538,7 +538,7 @@ static DistributeObjectOps TextSearchConfig_Comment = {
.markDistributed = false,
};
static DistributeObjectOps TextSearchConfig_Define = {
.deparse = DeparseCreateTextSearchStmt,
.deparse = DeparseCreateTextSearchConfigurationStmt,
.qualify = NULL,
.preprocess = NULL,
.postprocess = PostprocessCreateTextSearchConfigurationStmt,
@ -561,6 +561,62 @@ static DistributeObjectOps TextSearchConfig_Rename = {
.address = RenameTextSearchConfigurationStmtObjectAddress,
.markDistributed = false,
};
static DistributeObjectOps TextSearchDict_Alter = {
.deparse = DeparseAlterTextSearchDictionaryStmt,
.qualify = QualifyAlterTextSearchDictionaryStmt,
.preprocess = PreprocessAlterTextSearchDictionaryStmt,
.postprocess = NULL,
.address = AlterTextSearchDictionaryStmtObjectAddress,
.markDistributed = false,
};
static DistributeObjectOps TextSearchDict_AlterObjectSchema = {
.deparse = DeparseAlterTextSearchDictionarySchemaStmt,
.qualify = QualifyAlterTextSearchDictionarySchemaStmt,
.preprocess = PreprocessAlterTextSearchDictionarySchemaStmt,
.postprocess = PostprocessAlterTextSearchDictionarySchemaStmt,
.address = AlterTextSearchDictionarySchemaStmtObjectAddress,
.markDistributed = false,
};
static DistributeObjectOps TextSearchDict_AlterOwner = {
.deparse = DeparseAlterTextSearchDictionaryOwnerStmt,
.qualify = QualifyAlterTextSearchDictionaryOwnerStmt,
.preprocess = PreprocessAlterTextSearchDictionaryOwnerStmt,
.postprocess = PostprocessAlterTextSearchDictionaryOwnerStmt,
.address = AlterTextSearchDictOwnerObjectAddress,
.markDistributed = false,
};
static DistributeObjectOps TextSearchDict_Comment = {
.deparse = DeparseTextSearchDictionaryCommentStmt,
.qualify = QualifyTextSearchDictionaryCommentStmt,
.preprocess = PreprocessTextSearchDictionaryCommentStmt,
.postprocess = NULL,
.address = TextSearchDictCommentObjectAddress,
.markDistributed = false,
};
static DistributeObjectOps TextSearchDict_Define = {
.deparse = DeparseCreateTextSearchDictionaryStmt,
.qualify = NULL,
.preprocess = NULL,
.postprocess = PostprocessCreateTextSearchDictionaryStmt,
.address = CreateTextSearchDictObjectAddress,
.markDistributed = true,
};
static DistributeObjectOps TextSearchDict_Drop = {
.deparse = DeparseDropTextSearchDictionaryStmt,
.qualify = QualifyDropTextSearchDictionaryStmt,
.preprocess = PreprocessDropTextSearchDictionaryStmt,
.postprocess = NULL,
.address = NULL,
.markDistributed = false,
};
static DistributeObjectOps TextSearchDict_Rename = {
.deparse = DeparseRenameTextSearchDictionaryStmt,
.qualify = QualifyRenameTextSearchDictionaryStmt,
.preprocess = PreprocessRenameTextSearchDictionaryStmt,
.postprocess = NULL,
.address = RenameTextSearchDictionaryStmtObjectAddress,
.markDistributed = false,
};
static DistributeObjectOps Trigger_AlterObjectDepends = {
.deparse = NULL,
.qualify = NULL,
@ -872,6 +928,11 @@ GetDistributeObjectOps(Node *node)
return &TextSearchConfig_AlterObjectSchema;
}
case OBJECT_TSDICTIONARY:
{
return &TextSearchDict_AlterObjectSchema;
}
case OBJECT_TYPE:
{
return &Type_AlterObjectSchema;
@ -934,6 +995,11 @@ GetDistributeObjectOps(Node *node)
return &TextSearchConfig_AlterOwner;
}
case OBJECT_TSDICTIONARY:
{
return &TextSearchDict_AlterOwner;
}
case OBJECT_TYPE:
{
return &Type_AlterOwner;
@ -1020,6 +1086,11 @@ GetDistributeObjectOps(Node *node)
return &TextSearchConfig_Alter;
}
case T_AlterTSDictionaryStmt:
{
return &TextSearchDict_Alter;
}
case T_ClusterStmt:
{
return &Any_Cluster;
@ -1035,6 +1106,11 @@ GetDistributeObjectOps(Node *node)
return &TextSearchConfig_Comment;
}
case OBJECT_TSDICTIONARY:
{
return &TextSearchDict_Comment;
}
default:
{
return &NoDistributeOps;
@ -1107,6 +1183,11 @@ GetDistributeObjectOps(Node *node)
return &TextSearchConfig_Define;
}
case OBJECT_TSDICTIONARY:
{
return &TextSearchDict_Define;
}
default:
{
return &NoDistributeOps;
@ -1189,6 +1270,11 @@ GetDistributeObjectOps(Node *node)
return &TextSearchConfig_Drop;
}
case OBJECT_TSDICTIONARY:
{
return &TextSearchDict_Drop;
}
case OBJECT_TYPE:
{
return &Type_Drop;
@ -1293,6 +1379,11 @@ GetDistributeObjectOps(Node *node)
return &TextSearchConfig_Rename;
}
case OBJECT_TSDICTIONARY:
{
return &TextSearchDict_Rename;
}
case OBJECT_TYPE:
{
return &Type_Rename;

View File

@ -18,7 +18,9 @@
#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"
@ -40,13 +42,19 @@
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);
@ -99,6 +107,48 @@ PostprocessCreateTextSearchConfigurationStmt(Node *node, const char *queryString
}
/*
* 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)
{
@ -122,6 +172,25 @@ GetCreateTextSearchConfigStatements(const ObjectAddress *address)
}
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.
@ -135,9 +204,22 @@ CreateTextSearchConfigDDLCommandsIdempotent(const ObjectAddress *address)
}
/*
* 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 schema's locally they also got removed from
* 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.
*/
@ -186,9 +268,60 @@ PreprocessDropTextSearchConfigurationStmt(Node *node, const char *queryString,
}
/*
* 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 containign all configurations that are distributed.
* dropped, and create a list containing all configurations that are distributed.
*/
static List *
GetDistributedTextSearchConfigurationNames(DropStmt *stmt)
@ -200,7 +333,7 @@ GetDistributedTextSearchConfigurationNames(DropStmt *stmt)
Oid tsconfigOid = get_ts_config_oid(objName, stmt->missing_ok);
if (!OidIsValid(tsconfigOid))
{
/* skip missing configuration names, they can't be dirstibuted */
/* skip missing configuration names, they can't be distributed */
continue;
}
@ -216,6 +349,36 @@ GetDistributedTextSearchConfigurationNames(DropStmt *stmt)
}
/*
* 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
@ -247,6 +410,37 @@ PreprocessAlterTextSearchConfigurationStmt(Node *node, const char *queryString,
}
/*
* 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
@ -280,6 +474,39 @@ PreprocessRenameTextSearchConfigurationStmt(Node *node, const char *queryString,
}
/*
* 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
@ -314,6 +541,40 @@ PreprocessAlterTextSearchConfigurationSchemaStmt(Node *node, const char *querySt
}
/*
* 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
@ -341,6 +602,33 @@ PostprocessAlterTextSearchConfigurationSchemaStmt(Node *node, const char *queryS
}
/*
* 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
@ -374,6 +662,39 @@ PreprocessTextSearchConfigurationCommentStmt(Node *node, const char *queryString
}
/*
* 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
@ -407,6 +728,39 @@ PreprocessAlterTextSearchConfigurationOwnerStmt(Node *node, const char *queryStr
}
/*
* 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
@ -433,6 +787,32 @@ PostprocessAlterTextSearchConfigurationOwnerStmt(Node *node, const char *querySt
}
/*
* 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.
@ -465,6 +845,65 @@ GetTextSearchConfigDefineStmt(Oid tsconfigOid)
}
/*
* 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
@ -492,6 +931,33 @@ GetTextSearchConfigCommentStmt(Oid tsconfigOid)
}
/*
* 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.
@ -581,7 +1047,7 @@ GetTextSearchConfigMappingStmt(Oid tsconfigOid)
* GetTextSearchConfigOwnerStmts returns a potentially empty list of statements to change
* the ownership of a TEXT SEARCH CONFIGURATION object.
*
* The list is for convenienve when building a full list of statements to recreate the
* The list is for convenience when building a full list of statements to recreate the
* configuration.
*/
static List *
@ -605,6 +1071,34 @@ GetTextSearchConfigOwnerStmts(Oid tsconfigOid)
}
/*
* 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
@ -654,6 +1148,30 @@ get_ts_dict_namelist(Oid tsdictOid)
}
/*
* 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.
@ -753,6 +1271,25 @@ CreateTextSearchConfigurationObjectAddress(Node *node, bool missing_ok)
}
/*
* 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
@ -772,6 +1309,25 @@ RenameTextSearchConfigurationStmtObjectAddress(Node *node, bool missing_ok)
}
/*
* 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
@ -790,6 +1346,24 @@ AlterTextSearchConfigurationStmtObjectAddress(Node *node, bool missing_ok)
}
/*
* 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
@ -843,6 +1417,59 @@ AlterTextSearchConfigurationSchemaStmtObjectAddress(Node *node, bool missing_ok)
}
/*
* 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
@ -862,6 +1489,25 @@ TextSearchConfigurationCommentObjectAddress(Node *node, bool missing_ok)
}
/*
* 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
@ -880,6 +1526,24 @@ AlterTextSearchConfigurationOwnerObjectAddress(Node *node, bool 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

View File

@ -18,21 +18,21 @@
#include "distributed/deparser.h"
#include "distributed/listutils.h"
static void AppendDefElemList(StringInfo buf, List *defelms);
static void AppendDefElemList(StringInfo buf, List *defelems, char *objectName);
static void AppendStringInfoTokentypeList(StringInfo buf, List *tokentypes);
static void AppendStringInfoDictnames(StringInfo buf, List *dicts);
/*
* DeparseCreateTextSearchStmt returns the sql for a DefineStmt defining a TEXT SEARCH
* CONFIGURATION
* DeparseCreateTextSearchConfigurationStmt returns the sql for a DefineStmt defining a
* TEXT SEARCH CONFIGURATION
*
* Although the syntax is mutually exclusive on the two arguments that can be passed in
* the deparser will syntactically correct multiple definitions if provided. *
*/
char *
DeparseCreateTextSearchStmt(Node *node)
DeparseCreateTextSearchConfigurationStmt(Node *node)
{
DefineStmt *stmt = castNode(DefineStmt, node);
@ -42,7 +42,7 @@ DeparseCreateTextSearchStmt(Node *node)
const char *identifier = NameListToQuotedString(stmt->defnames);
appendStringInfo(&buf, "CREATE TEXT SEARCH CONFIGURATION %s ", identifier);
appendStringInfoString(&buf, "(");
AppendDefElemList(&buf, stmt->definition);
AppendDefElemList(&buf, stmt->definition, "CONFIGURATION");
appendStringInfoString(&buf, ");");
return buf.data;
@ -50,13 +50,40 @@ DeparseCreateTextSearchStmt(Node *node)
/*
* AppendDefElemList specialization to append a comma separated list of definitions to a
* DeparseCreateTextSearchDictionaryStmt returns the sql for a DefineStmt defining a
* TEXT SEARCH DICTIONARY
*
* Although the syntax is mutually exclusive on the two arguments that can be passed in
* the deparser will syntactically correct multiple definitions if provided. *
*/
char *
DeparseCreateTextSearchDictionaryStmt(Node *node)
{
DefineStmt *stmt = castNode(DefineStmt, node);
StringInfoData buf = { 0 };
initStringInfo(&buf);
const char *identifier = NameListToQuotedString(stmt->defnames);
appendStringInfo(&buf, "CREATE TEXT SEARCH DICTIONARY %s ", identifier);
appendStringInfoString(&buf, "(");
AppendDefElemList(&buf, stmt->definition, "DICTIONARY");
appendStringInfoString(&buf, ");");
return buf.data;
}
/*
* AppendDefElemList is a helper to append a comma separated list of definitions to a
* define statement.
*
* Currently only supports String and TypeName entries. Will error on others.
*
* The extra objectName parameter is used to create meaningful error messages.
*/
static void
AppendDefElemList(StringInfo buf, List *defelems)
AppendDefElemList(StringInfo buf, List *defelems, char *objectName)
{
DefElem *defelem = NULL;
bool first = true;
@ -68,6 +95,20 @@ AppendDefElemList(StringInfo buf, List *defelems)
}
first = false;
/*
* There are some operations that can omit the argument. In that case, we only use
* the defname.
*
* For example, omitting [ = value ] in the next query results in resetting the
* option to defaults:
* ALTER TEXT SEARCH DICTIONARY name ( option [ = value ] );
*/
if (defelem->arg == NULL)
{
appendStringInfo(buf, "%s", defelem->defname);
continue;
}
/* extract identifier from defelem */
const char *identifier = NULL;
switch (nodeTag(defelem->arg))
@ -88,7 +129,7 @@ AppendDefElemList(StringInfo buf, List *defelems)
default:
{
ereport(ERROR, (errmsg("unexpected argument during deparsing of "
"TEXT SEARCH CONFIGURATION definition")));
"TEXT SEARCH %s definition", objectName)));
}
}
@ -136,6 +177,44 @@ DeparseDropTextSearchConfigurationStmt(Node *node)
}
/*
* DeparseDropTextSearchDictionaryStmt returns the sql representation for a DROP TEXT SEARCH
* DICTIONARY ... statment. Supports dropping multiple dictionaries at once.
*/
char *
DeparseDropTextSearchDictionaryStmt(Node *node)
{
DropStmt *stmt = castNode(DropStmt, node);
Assert(stmt->removeType == OBJECT_TSDICTIONARY);
StringInfoData buf = { 0 };
initStringInfo(&buf);
appendStringInfoString(&buf, "DROP TEXT SEARCH DICTIONARY ");
List *nameList = NIL;
bool first = true;
foreach_ptr(nameList, stmt->objects)
{
if (!first)
{
appendStringInfoString(&buf, ", ");
}
first = false;
appendStringInfoString(&buf, NameListToQuotedString(nameList));
}
if (stmt->behavior == DROP_CASCADE)
{
appendStringInfoString(&buf, " CASCADE");
}
appendStringInfoString(&buf, ";");
return buf.data;
}
/*
* DeparseRenameTextSearchConfigurationStmt returns the sql representation of a ALTER TEXT
* SEARCH CONFIGURATION ... RENAME TO ... statement.
@ -158,7 +237,28 @@ DeparseRenameTextSearchConfigurationStmt(Node *node)
/*
* DeparseAlterTextSearchConfigurationStmt returns the ql representation of any generic
* DeparseRenameTextSearchDictionaryStmt returns the sql representation of a ALTER TEXT SEARCH
* DICTIONARY ... RENAME TO ... statement.
*/
char *
DeparseRenameTextSearchDictionaryStmt(Node *node)
{
RenameStmt *stmt = castNode(RenameStmt, node);
Assert(stmt->renameType == OBJECT_TSDICTIONARY);
StringInfoData buf = { 0 };
initStringInfo(&buf);
char *identifier = NameListToQuotedString(castNode(List, stmt->object));
appendStringInfo(&buf, "ALTER TEXT SEARCH DICTIONARY %s RENAME TO %s;",
identifier, quote_identifier(stmt->newname));
return buf.data;
}
/*
* DeparseAlterTextSearchConfigurationStmt returns the sql representation of any generic
* ALTER TEXT SEARCH CONFIGURATION .... statement. The statements supported include:
* - ALTER TEXT SEARCH CONFIGURATIONS ... ADD MAPPING FOR [, ...] WITH [, ...]
* - ALTER TEXT SEARCH CONFIGURATIONS ... ALTER MAPPING FOR [, ...] WITH [, ...]
@ -253,6 +353,28 @@ DeparseAlterTextSearchConfigurationStmt(Node *node)
}
/*
* DeparseAlterTextSearchConfigurationStmt returns the sql representation of any generic
* ALTER TEXT SEARCH DICTIONARY .... statement. The statements supported include
* - ALTER TEXT SEARCH DICTIONARY name ( option [ = value ] [, ... ] )
*/
char *
DeparseAlterTextSearchDictionaryStmt(Node *node)
{
AlterTSDictionaryStmt *stmt = castNode(AlterTSDictionaryStmt, node);
StringInfoData buf = { 0 };
initStringInfo(&buf);
char *identifier = NameListToQuotedString(castNode(List, stmt->dictname));
appendStringInfo(&buf, "ALTER TEXT SEARCH DICTIONARY %s ( ", identifier);
AppendDefElemList(&buf, stmt->options, "DICTIONARY");
appendStringInfoString(&buf, " );");
return buf.data;
}
/*
* DeparseAlterTextSearchConfigurationSchemaStmt returns the sql statement representing
* ALTER TEXT SEARCH CONFIGURATION ... SET SCHEMA ... statements.
@ -274,6 +396,27 @@ DeparseAlterTextSearchConfigurationSchemaStmt(Node *node)
}
/*
* DeparseAlterTextSearchDictionarySchemaStmt returns the sql statement representing ALTER TEXT
* SEARCH DICTIONARY ... SET SCHEMA ... statements.
*/
char *
DeparseAlterTextSearchDictionarySchemaStmt(Node *node)
{
AlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);
Assert(stmt->objectType == OBJECT_TSDICTIONARY);
StringInfoData buf = { 0 };
initStringInfo(&buf);
appendStringInfo(&buf, "ALTER TEXT SEARCH DICTIONARY %s SET SCHEMA %s;",
NameListToQuotedString(castNode(List, stmt->object)),
quote_identifier(stmt->newschema));
return buf.data;
}
/*
* DeparseTextSearchConfigurationCommentStmt returns the sql statement representing
* COMMENT ON TEXT SEARCH CONFIGURATION ... IS ...
@ -305,6 +448,37 @@ DeparseTextSearchConfigurationCommentStmt(Node *node)
}
/*
* DeparseTextSearchDictionaryCommentStmt returns the sql statement representing
* COMMENT ON TEXT SEARCH DICTIONARY ... IS ...
*/
char *
DeparseTextSearchDictionaryCommentStmt(Node *node)
{
CommentStmt *stmt = castNode(CommentStmt, node);
Assert(stmt->objtype == OBJECT_TSDICTIONARY);
StringInfoData buf = { 0 };
initStringInfo(&buf);
appendStringInfo(&buf, "COMMENT ON TEXT SEARCH DICTIONARY %s IS ",
NameListToQuotedString(castNode(List, stmt->object)));
if (stmt->comment == NULL)
{
appendStringInfoString(&buf, "NULL");
}
else
{
appendStringInfoString(&buf, quote_literal_cstr(stmt->comment));
}
appendStringInfoString(&buf, ";");
return buf.data;
}
/*
* AppendStringInfoTokentypeList specializes in adding a comma separated list of
* token_tyoe's to TEXT SEARCH CONFIGURATION commands
@ -375,3 +549,24 @@ DeparseAlterTextSearchConfigurationOwnerStmt(Node *node)
return buf.data;
}
/*
* DeparseAlterTextSearchDictionaryOwnerStmt returns the sql statement representing ALTER TEXT
* SEARCH DICTIONARY ... ONWER TO ... commands.
*/
char *
DeparseAlterTextSearchDictionaryOwnerStmt(Node *node)
{
AlterOwnerStmt *stmt = castNode(AlterOwnerStmt, node);
Assert(stmt->objectType == OBJECT_TSDICTIONARY);
StringInfoData buf = { 0 };
initStringInfo(&buf);
appendStringInfo(&buf, "ALTER TEXT SEARCH DICTIONARY %s OWNER TO %s;",
NameListToQuotedString(castNode(List, stmt->object)),
RoleSpecString(stmt->newowner, true));
return buf.data;
}

View File

@ -69,6 +69,44 @@ QualifyDropTextSearchConfigurationStmt(Node *node)
}
/*
* QualifyDropTextSearchDictionaryStmt adds any missing schema names to text search
* dictionaries being dropped. All dictionaries are expected to exists before fully
* qualifying the statement. Errors will be raised for objects not existing. Non-existing
* objects are expected to not be distributed.
*/
void
QualifyDropTextSearchDictionaryStmt(Node *node)
{
DropStmt *stmt = castNode(DropStmt, node);
Assert(stmt->removeType == OBJECT_TSDICTIONARY);
List *qualifiedObjects = NIL;
List *objName = NIL;
foreach_ptr(objName, stmt->objects)
{
char *schemaName = NULL;
char *tsdictName = NULL;
DeconstructQualifiedName(objName, &schemaName, &tsdictName);
if (!schemaName)
{
Oid tsdictOid = get_ts_dict_oid(objName, false);
Oid namespaceOid = get_ts_dict_namespace(tsdictOid);
schemaName = get_namespace_name(namespaceOid);
objName = list_make2(makeString(schemaName),
makeString(tsdictName));
}
qualifiedObjects = lappend(qualifiedObjects, objName);
}
stmt->objects = qualifiedObjects;
}
/*
* QualifyAlterTextSearchConfigurationStmt adds the schema name (if missing) to the name
* of the text search configurations, as well as the dictionaries referenced.
@ -128,6 +166,32 @@ QualifyAlterTextSearchConfigurationStmt(Node *node)
}
/*
* QualifyAlterTextSearchDictionaryStmt adds the schema name (if missing) to the name
* of the text search dictionary.
*/
void
QualifyAlterTextSearchDictionaryStmt(Node *node)
{
AlterTSDictionaryStmt *stmt = castNode(AlterTSDictionaryStmt, node);
char *schemaName = NULL;
char *objName = NULL;
DeconstructQualifiedName(stmt->dictname, &schemaName, &objName);
/* fully qualify the dictname being altered */
if (!schemaName)
{
Oid tsdictOid = get_ts_dict_oid(stmt->dictname, false);
Oid namespaceOid = get_ts_dict_namespace(tsdictOid);
schemaName = get_namespace_name(namespaceOid);
stmt->dictname = list_make2(makeString(schemaName),
makeString(objName));
}
}
/*
* QualifyRenameTextSearchConfigurationStmt adds the schema name (if missing) to the
* configuration being renamed. The new name will kept be without schema name since this
@ -156,9 +220,37 @@ QualifyRenameTextSearchConfigurationStmt(Node *node)
}
/*
* QualifyRenameTextSearchDictionaryStmt adds the schema name (if missing) to the
* dictionary being renamed. The new name will kept be without schema name since this
* command cannot be used to change the schema of a dictionary.
*/
void
QualifyRenameTextSearchDictionaryStmt(Node *node)
{
RenameStmt *stmt = castNode(RenameStmt, node);
Assert(stmt->renameType == OBJECT_TSDICTIONARY);
char *schemaName = NULL;
char *objName = NULL;
DeconstructQualifiedName(castNode(List, stmt->object), &schemaName, &objName);
/* fully qualify the dictname being altered */
if (!schemaName)
{
Oid tsdictOid = get_ts_dict_oid(castNode(List, stmt->object), false);
Oid namespaceOid = get_ts_dict_namespace(tsdictOid);
schemaName = get_namespace_name(namespaceOid);
stmt->object = (Node *) list_make2(makeString(schemaName),
makeString(objName));
}
}
/*
* QualifyAlterTextSearchConfigurationSchemaStmt adds the schema name (if missing) for the
* text search being moved to a new schema.
* text search config being moved to a new schema.
*/
void
QualifyAlterTextSearchConfigurationSchemaStmt(Node *node)
@ -182,6 +274,32 @@ QualifyAlterTextSearchConfigurationSchemaStmt(Node *node)
}
/*
* QualifyAlterTextSearchDictionarySchemaStmt adds the schema name (if missing) for the
* text search dictionary being moved to a new schema.
*/
void
QualifyAlterTextSearchDictionarySchemaStmt(Node *node)
{
AlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);
Assert(stmt->objectType == OBJECT_TSDICTIONARY);
char *schemaName = NULL;
char *objName = NULL;
DeconstructQualifiedName(castNode(List, stmt->object), &schemaName, &objName);
if (!schemaName)
{
Oid tsdictOid = get_ts_dict_oid(castNode(List, stmt->object), false);
Oid namespaceOid = get_ts_dict_namespace(tsdictOid);
schemaName = get_namespace_name(namespaceOid);
stmt->object = (Node *) list_make2(makeString(schemaName),
makeString(objName));
}
}
/*
* QualifyTextSearchConfigurationCommentStmt adds the schema name (if missing) to the
* configuration name on which the comment is created.
@ -208,6 +326,32 @@ QualifyTextSearchConfigurationCommentStmt(Node *node)
}
/*
* QualifyTextSearchDictionaryCommentStmt adds the schema name (if missing) to the
* dictionary name on which the comment is created.
*/
void
QualifyTextSearchDictionaryCommentStmt(Node *node)
{
CommentStmt *stmt = castNode(CommentStmt, node);
Assert(stmt->objtype == OBJECT_TSDICTIONARY);
char *schemaName = NULL;
char *objName = NULL;
DeconstructQualifiedName(castNode(List, stmt->object), &schemaName, &objName);
if (!schemaName)
{
Oid tsdictOid = get_ts_dict_oid(castNode(List, stmt->object), false);
Oid namespaceOid = get_ts_dict_namespace(tsdictOid);
schemaName = get_namespace_name(namespaceOid);
stmt->object = (Node *) list_make2(makeString(schemaName),
makeString(objName));
}
}
/*
* QualifyAlterTextSearchConfigurationOwnerStmt adds the schema name (if missing) to the
* configuration for which the owner is changing.
@ -234,6 +378,32 @@ QualifyAlterTextSearchConfigurationOwnerStmt(Node *node)
}
/*
* QualifyAlterTextSearchDictionaryOwnerStmt adds the schema name (if missing) to the
* dictionary for which the owner is changing.
*/
void
QualifyAlterTextSearchDictionaryOwnerStmt(Node *node)
{
AlterOwnerStmt *stmt = castNode(AlterOwnerStmt, node);
Assert(stmt->objectType == OBJECT_TSDICTIONARY);
char *schemaName = NULL;
char *objName = NULL;
DeconstructQualifiedName(castNode(List, stmt->object), &schemaName, &objName);
if (!schemaName)
{
Oid tsdictOid = get_ts_dict_oid(castNode(List, stmt->object), false);
Oid namespaceOid = get_ts_dict_namespace(tsdictOid);
schemaName = get_namespace_name(namespaceOid);
stmt->object = (Node *) list_make2(makeString(schemaName),
makeString(objName));
}
}
/*
* get_ts_config_namespace returns the oid of the namespace which is housing the text
* search configuration identified by tsconfigOid.

View File

@ -788,6 +788,11 @@ GetObjectTypeString(ObjectType objType)
return "text search configuration";
}
case OBJECT_TSDICTIONARY:
{
return "text search dictionary";
}
case OBJECT_TYPE:
{
return "type";

View File

@ -676,6 +676,11 @@ SupportedDependencyByCitus(const ObjectAddress *address)
return true;
}
case OCLASS_TSDICT:
{
return true;
}
case OCLASS_TYPE:
{
switch (get_typtype(address->objectId))
@ -857,9 +862,13 @@ GetUndistributableDependency(const ObjectAddress *objectAddress)
if (!SupportedDependencyByCitus(dependency))
{
/*
* Since roles should be handled manually with Citus community, skip them.
* Skip roles and text search templates.
*
* Roles should be handled manually with Citus community whereas text search
* templates should be handled manually in both community and enterprise
*/
if (getObjectClass(dependency) != OCLASS_ROLE)
if (getObjectClass(dependency) != OCLASS_ROLE &&
getObjectClass(dependency) != OCLASS_TSTEMPLATE)
{
return dependency;
}

View File

@ -411,6 +411,7 @@ ErrorIfCurrentUserCanNotDistributeObject(ObjectType type, ObjectAddress *addr,
case OBJECT_PROCEDURE:
case OBJECT_AGGREGATE:
case OBJECT_TSCONFIGURATION:
case OBJECT_TSDICTIONARY:
case OBJECT_TYPE:
case OBJECT_FOREIGN_SERVER:
case OBJECT_SEQUENCE:

View File

@ -286,18 +286,16 @@ CreateStmtListByObjectAddress(const ObjectAddress *address)
case OCLASS_TSCONFIG:
{
/*
* We do support TEXT SEARCH CONFIGURATION, however, we can't recreate the
* object in 1 command. Since the returned text is compared to the create
* statement sql we always want the sql to be different compared to the
* canonical creation sql we return here, hence we return an empty string, as
* that should never match the sql we have passed in for the creation.
*/
List *stmts = GetCreateTextSearchConfigStatements(address);
return DeparseTreeNodes(stmts);
}
case OCLASS_TSDICT:
{
List *stmts = GetCreateTextSearchDictionaryStatements(address);
return DeparseTreeNodes(stmts);
}
case OCLASS_TYPE:
{
return list_make1(DeparseTreeNode(CreateTypeStmtByObjectAddress(address)));

View File

@ -479,49 +479,94 @@ extern bool ConstrTypeUsesIndex(ConstrType constrType);
/* text_search.c - forward declarations */
extern List * PostprocessCreateTextSearchConfigurationStmt(Node *node,
const char *queryString);
extern List * PostprocessCreateTextSearchDictionaryStmt(Node *node,
const char *queryString);
extern List * GetCreateTextSearchConfigStatements(const ObjectAddress *address);
extern List * GetCreateTextSearchDictionaryStatements(const ObjectAddress *address);
extern List * CreateTextSearchConfigDDLCommandsIdempotent(const ObjectAddress *address);
extern List * CreateTextSearchDictDDLCommandsIdempotent(const ObjectAddress *address);
extern List * PreprocessDropTextSearchConfigurationStmt(Node *node,
const char *queryString,
ProcessUtilityContext
processUtilityContext);
extern List * PreprocessDropTextSearchDictionaryStmt(Node *node,
const char *queryString,
ProcessUtilityContext
processUtilityContext);
extern List * PreprocessAlterTextSearchConfigurationStmt(Node *node,
const char *queryString,
ProcessUtilityContext
processUtilityContext);
extern List * PreprocessAlterTextSearchDictionaryStmt(Node *node,
const char *queryString,
ProcessUtilityContext
processUtilityContext);
extern List * PreprocessRenameTextSearchConfigurationStmt(Node *node,
const char *queryString,
ProcessUtilityContext
processUtilityContext);
extern List * PreprocessRenameTextSearchDictionaryStmt(Node *node,
const char *queryString,
ProcessUtilityContext
processUtilityContext);
extern List * PreprocessAlterTextSearchConfigurationSchemaStmt(Node *node,
const char *queryString,
ProcessUtilityContext
processUtilityContext);
extern List * PreprocessAlterTextSearchDictionarySchemaStmt(Node *node,
const char *queryString,
ProcessUtilityContext
processUtilityContext);
extern List * PostprocessAlterTextSearchConfigurationSchemaStmt(Node *node,
const char *queryString);
extern List * PostprocessAlterTextSearchDictionarySchemaStmt(Node *node,
const char *queryString);
extern List * PreprocessTextSearchConfigurationCommentStmt(Node *node,
const char *queryString,
ProcessUtilityContext
processUtilityContext);
extern List * PreprocessTextSearchDictionaryCommentStmt(Node *node,
const char *queryString,
ProcessUtilityContext
processUtilityContext);
extern List * PreprocessAlterTextSearchConfigurationOwnerStmt(Node *node,
const char *queryString,
ProcessUtilityContext
processUtilityContext);
extern List * PreprocessAlterTextSearchDictionaryOwnerStmt(Node *node,
const char *queryString,
ProcessUtilityContext
processUtilityContext);
extern List * PostprocessAlterTextSearchConfigurationOwnerStmt(Node *node,
const char *queryString);
extern List * PostprocessAlterTextSearchDictionaryOwnerStmt(Node *node,
const char *queryString);
extern ObjectAddress CreateTextSearchConfigurationObjectAddress(Node *node,
bool missing_ok);
extern ObjectAddress CreateTextSearchDictObjectAddress(Node *node,
bool missing_ok);
extern ObjectAddress RenameTextSearchConfigurationStmtObjectAddress(Node *node,
bool missing_ok);
extern ObjectAddress RenameTextSearchDictionaryStmtObjectAddress(Node *node,
bool missing_ok);
extern ObjectAddress AlterTextSearchConfigurationStmtObjectAddress(Node *node,
bool missing_ok);
extern ObjectAddress AlterTextSearchDictionaryStmtObjectAddress(Node *node,
bool missing_ok);
extern ObjectAddress AlterTextSearchConfigurationSchemaStmtObjectAddress(Node *node,
bool missing_ok);
extern ObjectAddress AlterTextSearchDictionarySchemaStmtObjectAddress(Node *node,
bool missing_ok);
extern ObjectAddress TextSearchConfigurationCommentObjectAddress(Node *node,
bool missing_ok);
extern ObjectAddress TextSearchDictCommentObjectAddress(Node *node,
bool missing_ok);
extern ObjectAddress AlterTextSearchConfigurationOwnerObjectAddress(Node *node,
bool missing_ok);
extern ObjectAddress AlterTextSearchDictOwnerObjectAddress(Node *node,
bool missing_ok);
extern char * GenerateBackupNameForTextSearchConfiguration(const ObjectAddress *address);
extern char * GenerateBackupNameForTextSearchDict(const ObjectAddress *address);
extern List * get_ts_config_namelist(Oid tsconfigOid);
/* truncate.c - forward declarations */

View File

@ -63,14 +63,21 @@ extern char * DeparseAlterTableStmt(Node *node);
extern void QualifyAlterTableSchemaStmt(Node *stmt);
/* foward declarations fro deparse_text_search.c */
extern char * DeparseCreateTextSearchStmt(Node *node);
extern char * DeparseDropTextSearchConfigurationStmt(Node *node);
extern char * DeparseRenameTextSearchConfigurationStmt(Node *node);
extern char * DeparseAlterTextSearchConfigurationStmt(Node *node);
extern char * DeparseAlterTextSearchConfigurationSchemaStmt(Node *node);
extern char * DeparseTextSearchConfigurationCommentStmt(Node *node);
/* forward declarations for deparse_text_search.c */
extern char * DeparseAlterTextSearchConfigurationOwnerStmt(Node *node);
extern char * DeparseAlterTextSearchConfigurationSchemaStmt(Node *node);
extern char * DeparseAlterTextSearchConfigurationStmt(Node *node);
extern char * DeparseAlterTextSearchDictionaryOwnerStmt(Node *node);
extern char * DeparseAlterTextSearchDictionarySchemaStmt(Node *node);
extern char * DeparseAlterTextSearchDictionaryStmt(Node *node);
extern char * DeparseCreateTextSearchConfigurationStmt(Node *node);
extern char * DeparseCreateTextSearchDictionaryStmt(Node *node);
extern char * DeparseDropTextSearchConfigurationStmt(Node *node);
extern char * DeparseDropTextSearchDictionaryStmt(Node *node);
extern char * DeparseRenameTextSearchConfigurationStmt(Node *node);
extern char * DeparseRenameTextSearchDictionaryStmt(Node *node);
extern char * DeparseTextSearchConfigurationCommentStmt(Node *node);
extern char * DeparseTextSearchDictionaryCommentStmt(Node *node);
/* forward declarations for deparse_schema_stmts.c */
extern char * DeparseCreateSchemaStmt(Node *node);
@ -153,13 +160,19 @@ extern char * DeparseAlterExtensionStmt(Node *stmt);
/* forward declarations for deparse_database_stmts.c */
extern char * DeparseAlterDatabaseOwnerStmt(Node *node);
/* forward declatations for depatse_text_search_stmts.c */
extern void QualifyDropTextSearchConfigurationStmt(Node *node);
extern void QualifyAlterTextSearchConfigurationStmt(Node *node);
extern void QualifyRenameTextSearchConfigurationStmt(Node *node);
extern void QualifyAlterTextSearchConfigurationSchemaStmt(Node *node);
extern void QualifyTextSearchConfigurationCommentStmt(Node *node);
/* forward declatations for deparse_text_search_stmts.c */
extern void QualifyAlterTextSearchConfigurationOwnerStmt(Node *node);
extern void QualifyAlterTextSearchConfigurationSchemaStmt(Node *node);
extern void QualifyAlterTextSearchConfigurationStmt(Node *node);
extern void QualifyAlterTextSearchDictionaryOwnerStmt(Node *node);
extern void QualifyAlterTextSearchDictionarySchemaStmt(Node *node);
extern void QualifyAlterTextSearchDictionaryStmt(Node *node);
extern void QualifyDropTextSearchConfigurationStmt(Node *node);
extern void QualifyDropTextSearchDictionaryStmt(Node *node);
extern void QualifyRenameTextSearchConfigurationStmt(Node *node);
extern void QualifyRenameTextSearchDictionaryStmt(Node *node);
extern void QualifyTextSearchConfigurationCommentStmt(Node *node);
extern void QualifyTextSearchDictionaryCommentStmt(Node *node);
/* forward declarations for deparse_sequence_stmts.c */
extern char * DeparseDropSequenceStmt(Node *node);

View File

@ -1,3 +1,11 @@
-- print whether we're using version > 12 to make version-specific tests clear
SHOW server_version \gset
SELECT substring(:'server_version', '\d+')::int > 12 AS version_above_twelve;
version_above_twelve
---------------------------------------------------------------------
t
(1 row)
CREATE SCHEMA "extension'test";
-- use a schema name with escape character
SET search_path TO "extension'test";
@ -184,8 +192,6 @@ SELECT create_reference_table('ref_table_2');
CREATE FUNCTION dintdict_init(internal) RETURNS internal AS 'dict_int.so' LANGUAGE C STRICT;
CREATE FUNCTION dintdict_lexize(internal, internal, internal, internal) RETURNS internal AS 'dict_int.so' LANGUAGE C STRICT;
CREATE TEXT SEARCH TEMPLATE intdict_template (LEXIZE = dintdict_lexize, INIT = dintdict_init );
CREATE TEXT SEARCH DICTIONARY intdict (TEMPLATE = intdict_template);
COMMENT ON TEXT SEARCH DICTIONARY intdict IS 'dictionary for integers';
SELECT run_command_on_workers($$
CREATE TEXT SEARCH TEMPLATE intdict_template (LEXIZE = dintdict_lexize, INIT = dintdict_init );
$$);
@ -194,22 +200,8 @@ $$);
(localhost,57637,t,"CREATE TEXT SEARCH TEMPLATE")
(1 row)
SELECT run_command_on_workers($$
CREATE TEXT SEARCH DICTIONARY intdict (TEMPLATE = intdict_template);
$$);
run_command_on_workers
---------------------------------------------------------------------
(localhost,57637,t,"CREATE TEXT SEARCH DICTIONARY")
(1 row)
SELECT run_command_on_workers($$
COMMENT ON TEXT SEARCH DICTIONARY intdict IS 'dictionary for integers';
$$);
run_command_on_workers
---------------------------------------------------------------------
(localhost,57637,t,COMMENT)
(1 row)
CREATE EXTENSION dict_int FROM unpackaged;
ERROR: CREATE EXTENSION ... FROM is no longer supported
SELECT run_command_on_workers($$SELECT count(extnamespace) FROM pg_extension WHERE extname = 'dict_int'$$);
@ -224,7 +216,20 @@ SELECT run_command_on_workers($$SELECT extversion FROM pg_extension WHERE extnam
(localhost,57637,t,"")
(1 row)
-- and add the other node
-- adding the second node will fail as the text search template needs to be created manually
SELECT 1 from master_add_node('localhost', :worker_2_port);
ERROR: text search template "public.intdict_template" does not exist
CONTEXT: while executing command on localhost:xxxxx
-- create the text search template manually on the worker
\c - - - :worker_2_port
SET citus.enable_metadata_sync TO false;
CREATE FUNCTION dintdict_init(internal) RETURNS internal AS 'dict_int.so' LANGUAGE C STRICT;
CREATE FUNCTION dintdict_lexize(internal, internal, internal, internal) RETURNS internal AS 'dict_int.so' LANGUAGE C STRICT;
CREATE TEXT SEARCH TEMPLATE intdict_template (LEXIZE = dintdict_lexize, INIT = dintdict_init );
RESET citus.enable_metadata_sync;
\c - - - :master_port
SET client_min_messages TO WARNING;
-- add the second node now
SELECT 1 from master_add_node('localhost', :worker_2_port);
?column?
---------------------------------------------------------------------

View File

@ -1,3 +1,11 @@
-- print whether we're using version > 12 to make version-specific tests clear
SHOW server_version \gset
SELECT substring(:'server_version', '\d+')::int > 12 AS version_above_twelve;
version_above_twelve
---------------------------------------------------------------------
f
(1 row)
CREATE SCHEMA "extension'test";
-- use a schema name with escape character
SET search_path TO "extension'test";
@ -184,8 +192,6 @@ SELECT create_reference_table('ref_table_2');
CREATE FUNCTION dintdict_init(internal) RETURNS internal AS 'dict_int.so' LANGUAGE C STRICT;
CREATE FUNCTION dintdict_lexize(internal, internal, internal, internal) RETURNS internal AS 'dict_int.so' LANGUAGE C STRICT;
CREATE TEXT SEARCH TEMPLATE intdict_template (LEXIZE = dintdict_lexize, INIT = dintdict_init );
CREATE TEXT SEARCH DICTIONARY intdict (TEMPLATE = intdict_template);
COMMENT ON TEXT SEARCH DICTIONARY intdict IS 'dictionary for integers';
SELECT run_command_on_workers($$
CREATE TEXT SEARCH TEMPLATE intdict_template (LEXIZE = dintdict_lexize, INIT = dintdict_init );
$$);
@ -194,22 +200,8 @@ $$);
(localhost,57637,t,"CREATE TEXT SEARCH TEMPLATE")
(1 row)
SELECT run_command_on_workers($$
CREATE TEXT SEARCH DICTIONARY intdict (TEMPLATE = intdict_template);
$$);
run_command_on_workers
---------------------------------------------------------------------
(localhost,57637,t,"CREATE TEXT SEARCH DICTIONARY")
(1 row)
SELECT run_command_on_workers($$
COMMENT ON TEXT SEARCH DICTIONARY intdict IS 'dictionary for integers';
$$);
run_command_on_workers
---------------------------------------------------------------------
(localhost,57637,t,COMMENT)
(1 row)
CREATE EXTENSION dict_int FROM unpackaged;
SELECT run_command_on_workers($$SELECT count(extnamespace) FROM pg_extension WHERE extname = 'dict_int'$$);
run_command_on_workers
@ -223,7 +215,27 @@ SELECT run_command_on_workers($$SELECT extversion FROM pg_extension WHERE extnam
(localhost,57637,t,1.0)
(1 row)
-- and add the other node
-- adding the second node will fail as the text search template needs to be created manually
SELECT 1 from master_add_node('localhost', :worker_2_port);
?column?
---------------------------------------------------------------------
1
(1 row)
-- create the text search template manually on the worker
\c - - - :worker_2_port
SET citus.enable_metadata_sync TO false;
CREATE FUNCTION dintdict_init(internal) RETURNS internal AS 'dict_int.so' LANGUAGE C STRICT;
ERROR: function "dintdict_init" already exists with same argument types
CREATE FUNCTION dintdict_lexize(internal, internal, internal, internal) RETURNS internal AS 'dict_int.so' LANGUAGE C STRICT;
ERROR: function "dintdict_lexize" already exists with same argument types
CREATE TEXT SEARCH TEMPLATE intdict_template (LEXIZE = dintdict_lexize, INIT = dintdict_init );
ERROR: duplicate key value violates unique constraint "pg_ts_template_tmplname_index"
DETAIL: Key (tmplname, tmplnamespace)=(intdict_template, 2200) already exists.
RESET citus.enable_metadata_sync;
\c - - - :master_port
SET client_min_messages TO WARNING;
-- add the second node now
SELECT 1 from master_add_node('localhost', :worker_2_port);
?column?
---------------------------------------------------------------------

View File

@ -503,6 +503,122 @@ SELECT create_distributed_table('sensors', 'measureid');
(1 row)
-- create a new dictionary from scratch
CREATE TEXT SEARCH DICTIONARY my_english_dict (
template = snowball,
language = english,
stopwords = english
);
-- verify that the dictionary definition is the same in all nodes
SELECT result FROM run_command_on_all_nodes($$
SELECT ROW(dictname, dictnamespace::regnamespace, dictowner::regrole, tmplname, dictinitoption)
FROM pg_ts_dict d JOIN pg_ts_template t ON ( d.dicttemplate = t.oid )
WHERE dictname = 'my_english_dict';
$$);
result
---------------------------------------------------------------------
(my_english_dict,text_search,postgres,snowball,"language = 'english', stopwords = 'english'")
(my_english_dict,text_search,postgres,snowball,"language = 'english', stopwords = 'english'")
(my_english_dict,text_search,postgres,snowball,"language = 'english', stopwords = 'english'")
(3 rows)
-- use the new dictionary in a configuration mapping
CREATE TEXT SEARCH CONFIGURATION my_english_config ( COPY = english );
ALTER TEXT SEARCH CONFIGURATION my_english_config ALTER MAPPING FOR asciiword WITH my_english_dict;
-- verify that the dictionary is available on the worker nodes
SELECT result FROM run_command_on_all_nodes($$
SELECT ROW(alias,dictionary) FROM ts_debug('text_search.my_english_config', 'The Brightest supernovaes') WHERE alias = 'asciiword' LIMIT 1;
$$);
result
---------------------------------------------------------------------
(asciiword,text_search.my_english_dict)
(asciiword,text_search.my_english_dict)
(asciiword,text_search.my_english_dict)
(3 rows)
-- comment on a text search dictionary
COMMENT ON TEXT SEARCH DICTIONARY my_english_dict IS 'a text search dictionary that is butchered to test all edge cases';
SELECT result FROM run_command_on_all_nodes($$
SELECT obj_description('text_search.my_english_dict'::regdictionary);
$$);
result
---------------------------------------------------------------------
a text search dictionary that is butchered to test all edge cases
a text search dictionary that is butchered to test all edge cases
a text search dictionary that is butchered to test all edge cases
(3 rows)
-- remove a comment
COMMENT ON TEXT SEARCH DICTIONARY my_english_dict IS NULL;
SELECT result FROM run_command_on_all_nodes($$
SELECT obj_description('text_search.my_english_dict'::regdictionary);
$$);
result
---------------------------------------------------------------------
(3 rows)
-- test various ALTER TEXT SEARCH DICTIONARY commands
ALTER TEXT SEARCH DICTIONARY my_english_dict RENAME TO my_turkish_dict;
ALTER TEXT SEARCH DICTIONARY my_turkish_dict (language = turkish, stopwords);
ALTER TEXT SEARCH DICTIONARY my_turkish_dict OWNER TO text_search_owner;
ALTER TEXT SEARCH DICTIONARY my_turkish_dict SET SCHEMA "Text Search Requiring Quote's";
-- verify that the dictionary definition is the same in all nodes
SELECT result FROM run_command_on_all_nodes($$
SELECT ROW(dictname, dictnamespace::regnamespace, dictowner::regrole, tmplname, dictinitoption)
FROM pg_ts_dict d JOIN pg_ts_template t ON ( d.dicttemplate = t.oid )
WHERE dictname = 'my_turkish_dict';
$$);
result
---------------------------------------------------------------------
(my_turkish_dict,"""Text Search Requiring Quote's""",text_search_owner,snowball,"language = 'turkish'")
(my_turkish_dict,"""Text Search Requiring Quote's""",text_search_owner,snowball,"language = 'turkish'")
(my_turkish_dict,"""Text Search Requiring Quote's""",text_search_owner,snowball,"language = 'turkish'")
(3 rows)
-- verify that the configuration dictionary is changed in all nodes
SELECT result FROM run_command_on_all_nodes($$
SELECT ROW(alias,dictionary) FROM ts_debug('text_search.my_english_config', 'The Brightest supernovaes') WHERE alias = 'asciiword' LIMIT 1;
$$);
result
---------------------------------------------------------------------
(asciiword,"""Text Search Requiring Quote's"".my_turkish_dict")
(asciiword,"""Text Search Requiring Quote's"".my_turkish_dict")
(asciiword,"""Text Search Requiring Quote's"".my_turkish_dict")
(3 rows)
-- before testing drops, check that the dictionary exists on all nodes
SELECT result FROM run_command_on_all_nodes($$
SELECT '"Text Search Requiring Quote''s".my_turkish_dict'::regdictionary;
$$);
result
---------------------------------------------------------------------
"Text Search Requiring Quote's".my_turkish_dict
"Text Search Requiring Quote's".my_turkish_dict
"Text Search Requiring Quote's".my_turkish_dict
(3 rows)
ALTER TEXT SEARCH DICTIONARY "Text Search Requiring Quote's".my_turkish_dict SET SCHEMA text_search;
-- verify that we can drop the dictionary only with cascade option
DROP TEXT SEARCH DICTIONARY my_turkish_dict;
ERROR: cannot drop text search dictionary my_turkish_dict because other objects depend on it
DETAIL: text search configuration my_english_config depends on text search dictionary my_turkish_dict
HINT: Use DROP ... CASCADE to drop the dependent objects too.
DROP TEXT SEARCH DICTIONARY my_turkish_dict CASCADE;
NOTICE: drop cascades to text search configuration my_english_config
-- verify that it is dropped now
SELECT result FROM run_command_on_all_nodes($$
SELECT 'my_turkish_dict'::regdictionary;
$$);
result
---------------------------------------------------------------------
ERROR: text search dictionary "my_turkish_dict" does not exist
ERROR: text search dictionary "my_turkish_dict" does not exist
ERROR: text search dictionary "my_turkish_dict" does not exist
(3 rows)
SET client_min_messages TO 'warning';
DROP SCHEMA text_search, text_search2, "Text Search Requiring Quote's" CASCADE;
DROP ROLE text_search_owner;

View File

@ -1,3 +1,7 @@
-- print whether we're using version > 12 to make version-specific tests clear
SHOW server_version \gset
SELECT substring(:'server_version', '\d+')::int > 12 AS version_above_twelve;
CREATE SCHEMA "extension'test";
-- use a schema name with escape character
@ -114,26 +118,33 @@ SELECT create_reference_table('ref_table_2');
CREATE FUNCTION dintdict_init(internal) RETURNS internal AS 'dict_int.so' LANGUAGE C STRICT;
CREATE FUNCTION dintdict_lexize(internal, internal, internal, internal) RETURNS internal AS 'dict_int.so' LANGUAGE C STRICT;
CREATE TEXT SEARCH TEMPLATE intdict_template (LEXIZE = dintdict_lexize, INIT = dintdict_init );
CREATE TEXT SEARCH DICTIONARY intdict (TEMPLATE = intdict_template);
COMMENT ON TEXT SEARCH DICTIONARY intdict IS 'dictionary for integers';
SELECT run_command_on_workers($$
CREATE TEXT SEARCH TEMPLATE intdict_template (LEXIZE = dintdict_lexize, INIT = dintdict_init );
$$);
SELECT run_command_on_workers($$
CREATE TEXT SEARCH DICTIONARY intdict (TEMPLATE = intdict_template);
$$);
SELECT run_command_on_workers($$
COMMENT ON TEXT SEARCH DICTIONARY intdict IS 'dictionary for integers';
$$);
CREATE EXTENSION dict_int FROM unpackaged;
SELECT run_command_on_workers($$SELECT count(extnamespace) FROM pg_extension WHERE extname = 'dict_int'$$);
SELECT run_command_on_workers($$SELECT extversion FROM pg_extension WHERE extname = 'dict_int'$$);
-- and add the other node
-- adding the second node will fail as the text search template needs to be created manually
SELECT 1 from master_add_node('localhost', :worker_2_port);
-- create the text search template manually on the worker
\c - - - :worker_2_port
SET citus.enable_metadata_sync TO false;
CREATE FUNCTION dintdict_init(internal) RETURNS internal AS 'dict_int.so' LANGUAGE C STRICT;
CREATE FUNCTION dintdict_lexize(internal, internal, internal, internal) RETURNS internal AS 'dict_int.so' LANGUAGE C STRICT;
CREATE TEXT SEARCH TEMPLATE intdict_template (LEXIZE = dintdict_lexize, INIT = dintdict_init );
RESET citus.enable_metadata_sync;
\c - - - :master_port
SET client_min_messages TO WARNING;
-- add the second node now
SELECT 1 from master_add_node('localhost', :worker_2_port);
-- show that the extension is created on both existing and new node

View File

@ -276,6 +276,76 @@ CREATE TABLE sensors_a_partition PARTITION OF sensors FOR VALUES FROM ('2000-01-
CREATE INDEX sensors_search_name ON sensors USING gin (to_tsvector('partial_index_test_config'::regconfig, (COALESCE(name, ''::character varying))::text));
SELECT create_distributed_table('sensors', 'measureid');
-- create a new dictionary from scratch
CREATE TEXT SEARCH DICTIONARY my_english_dict (
template = snowball,
language = english,
stopwords = english
);
-- verify that the dictionary definition is the same in all nodes
SELECT result FROM run_command_on_all_nodes($$
SELECT ROW(dictname, dictnamespace::regnamespace, dictowner::regrole, tmplname, dictinitoption)
FROM pg_ts_dict d JOIN pg_ts_template t ON ( d.dicttemplate = t.oid )
WHERE dictname = 'my_english_dict';
$$);
-- use the new dictionary in a configuration mapping
CREATE TEXT SEARCH CONFIGURATION my_english_config ( COPY = english );
ALTER TEXT SEARCH CONFIGURATION my_english_config ALTER MAPPING FOR asciiword WITH my_english_dict;
-- verify that the dictionary is available on the worker nodes
SELECT result FROM run_command_on_all_nodes($$
SELECT ROW(alias,dictionary) FROM ts_debug('text_search.my_english_config', 'The Brightest supernovaes') WHERE alias = 'asciiword' LIMIT 1;
$$);
-- comment on a text search dictionary
COMMENT ON TEXT SEARCH DICTIONARY my_english_dict IS 'a text search dictionary that is butchered to test all edge cases';
SELECT result FROM run_command_on_all_nodes($$
SELECT obj_description('text_search.my_english_dict'::regdictionary);
$$);
-- remove a comment
COMMENT ON TEXT SEARCH DICTIONARY my_english_dict IS NULL;
SELECT result FROM run_command_on_all_nodes($$
SELECT obj_description('text_search.my_english_dict'::regdictionary);
$$);
-- test various ALTER TEXT SEARCH DICTIONARY commands
ALTER TEXT SEARCH DICTIONARY my_english_dict RENAME TO my_turkish_dict;
ALTER TEXT SEARCH DICTIONARY my_turkish_dict (language = turkish, stopwords);
ALTER TEXT SEARCH DICTIONARY my_turkish_dict OWNER TO text_search_owner;
ALTER TEXT SEARCH DICTIONARY my_turkish_dict SET SCHEMA "Text Search Requiring Quote's";
-- verify that the dictionary definition is the same in all nodes
SELECT result FROM run_command_on_all_nodes($$
SELECT ROW(dictname, dictnamespace::regnamespace, dictowner::regrole, tmplname, dictinitoption)
FROM pg_ts_dict d JOIN pg_ts_template t ON ( d.dicttemplate = t.oid )
WHERE dictname = 'my_turkish_dict';
$$);
-- verify that the configuration dictionary is changed in all nodes
SELECT result FROM run_command_on_all_nodes($$
SELECT ROW(alias,dictionary) FROM ts_debug('text_search.my_english_config', 'The Brightest supernovaes') WHERE alias = 'asciiword' LIMIT 1;
$$);
-- before testing drops, check that the dictionary exists on all nodes
SELECT result FROM run_command_on_all_nodes($$
SELECT '"Text Search Requiring Quote''s".my_turkish_dict'::regdictionary;
$$);
ALTER TEXT SEARCH DICTIONARY "Text Search Requiring Quote's".my_turkish_dict SET SCHEMA text_search;
-- verify that we can drop the dictionary only with cascade option
DROP TEXT SEARCH DICTIONARY my_turkish_dict;
DROP TEXT SEARCH DICTIONARY my_turkish_dict CASCADE;
-- verify that it is dropped now
SELECT result FROM run_command_on_all_nodes($$
SELECT 'my_turkish_dict'::regdictionary;
$$);
SET client_min_messages TO 'warning';
DROP SCHEMA text_search, text_search2, "Text Search Requiring Quote's" CASCADE;
DROP ROLE text_search_owner;