/*------------------------------------------------------------------------- * * deparse_text_search.c * All routines to deparse text search statements. * This file contains all entry points specific for text search statement deparsing. * * Copyright (c) Citus Data, Inc. * *------------------------------------------------------------------------- */ #include "postgres.h" #include "catalog/namespace.h" #include "utils/builtins.h" #include "distributed/citus_ruleutils.h" #include "distributed/deparser.h" #include "distributed/listutils.h" static void AppendDefElemList(StringInfo buf, List *defelms); 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 * * 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) { DefineStmt *stmt = castNode(DefineStmt, node); StringInfoData buf = { 0 }; initStringInfo(&buf); const char *identifier = NameListToQuotedString(stmt->defnames); appendStringInfo(&buf, "CREATE TEXT SEARCH CONFIGURATION %s ", identifier); appendStringInfoString(&buf, "("); AppendDefElemList(&buf, stmt->definition); appendStringInfoString(&buf, ");"); return buf.data; } /* * AppendDefElemList specialization to append a comma separated list of definitions to a * define statement. * * Currently only supports String and TypeName entries. Will error on others. */ static void AppendDefElemList(StringInfo buf, List *defelems) { DefElem *defelem = NULL; bool first = true; foreach_ptr(defelem, defelems) { if (!first) { appendStringInfoString(buf, ", "); } first = false; /* extract identifier from defelem */ const char *identifier = NULL; switch (nodeTag(defelem->arg)) { case T_String: { identifier = quote_identifier(strVal(defelem->arg)); break; } case T_TypeName: { TypeName *typeName = castNode(TypeName, defelem->arg); identifier = NameListToQuotedString(typeName->names); break; } default: { ereport(ERROR, (errmsg("unexpected argument during deparsing of " "TEXT SEARCH CONFIGURATION definition"))); } } /* stringify */ appendStringInfo(buf, "%s = %s", defelem->defname, identifier); } } /* * DeparseDropTextSearchConfigurationStmt returns the sql representation for a DROP TEXT * SEARCH CONFIGURATION ... statment. Supports dropping multiple configurations at once. */ char * DeparseDropTextSearchConfigurationStmt(Node *node) { DropStmt *stmt = castNode(DropStmt, node); Assert(stmt->removeType == OBJECT_TSCONFIGURATION); StringInfoData buf = { 0 }; initStringInfo(&buf); appendStringInfoString(&buf, "DROP TEXT SEARCH CONFIGURATION "); 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. */ char * DeparseRenameTextSearchConfigurationStmt(Node *node) { RenameStmt *stmt = castNode(RenameStmt, node); Assert(stmt->renameType == OBJECT_TSCONFIGURATION); StringInfoData buf = { 0 }; initStringInfo(&buf); char *identifier = NameListToQuotedString(castNode(List, stmt->object)); appendStringInfo(&buf, "ALTER TEXT SEARCH CONFIGURATION %s RENAME TO %s;", identifier, quote_identifier(stmt->newname)); return buf.data; } /* * DeparseAlterTextSearchConfigurationStmt returns the ql 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 [, ...] * - ALTER TEXT SEARCH CONFIGURATIONS ... ALTER MAPPING REPLACE ... WITH ... * - ALTER TEXT SEARCH CONFIGURATIONS ... ALTER MAPPING FOR [, ...] REPLACE ... WITH ... * - ALTER TEXT SEARCH CONFIGURATIONS ... DROP MAPPING [ IF EXISTS ] FOR ... */ char * DeparseAlterTextSearchConfigurationStmt(Node *node) { AlterTSConfigurationStmt *stmt = castNode(AlterTSConfigurationStmt, node); StringInfoData buf = { 0 }; initStringInfo(&buf); char *identifier = NameListToQuotedString(castNode(List, stmt->cfgname)); appendStringInfo(&buf, "ALTER TEXT SEARCH CONFIGURATION %s", identifier); switch (stmt->kind) { case ALTER_TSCONFIG_ADD_MAPPING: { appendStringInfoString(&buf, " ADD MAPPING FOR "); AppendStringInfoTokentypeList(&buf, stmt->tokentype); appendStringInfoString(&buf, " WITH "); AppendStringInfoDictnames(&buf, stmt->dicts); break; } case ALTER_TSCONFIG_ALTER_MAPPING_FOR_TOKEN: { appendStringInfoString(&buf, " ALTER MAPPING FOR "); AppendStringInfoTokentypeList(&buf, stmt->tokentype); appendStringInfoString(&buf, " WITH "); AppendStringInfoDictnames(&buf, stmt->dicts); break; } case ALTER_TSCONFIG_REPLACE_DICT: case ALTER_TSCONFIG_REPLACE_DICT_FOR_TOKEN: { appendStringInfoString(&buf, " ALTER MAPPING"); if (list_length(stmt->tokentype) > 0) { appendStringInfoString(&buf, " FOR "); AppendStringInfoTokentypeList(&buf, stmt->tokentype); } if (list_length(stmt->dicts) != 2) { elog(ERROR, "unexpected number of dictionaries while deparsing ALTER " "TEXT SEARCH CONFIGURATION ... ALTER MAPPING [FOR ...] REPLACE " "statement."); } appendStringInfo(&buf, " REPLACE %s", NameListToQuotedString(linitial(stmt->dicts))); appendStringInfo(&buf, " WITH %s", NameListToQuotedString(lsecond(stmt->dicts))); break; } case ALTER_TSCONFIG_DROP_MAPPING: { appendStringInfoString(&buf, " DROP MAPPING"); if (stmt->missing_ok) { appendStringInfoString(&buf, " IF EXISTS"); } appendStringInfoString(&buf, " FOR "); AppendStringInfoTokentypeList(&buf, stmt->tokentype); break; } default: { elog(ERROR, "unable to deparse unsupported ALTER TEXT SEARCH STATEMENT"); } } appendStringInfoString(&buf, ";"); return buf.data; } /* * DeparseAlterTextSearchConfigurationSchemaStmt returns the sql statement representing * ALTER TEXT SEARCH CONFIGURATION ... SET SCHEMA ... statements. */ char * DeparseAlterTextSearchConfigurationSchemaStmt(Node *node) { AlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node); Assert(stmt->objectType == OBJECT_TSCONFIGURATION); StringInfoData buf = { 0 }; initStringInfo(&buf); appendStringInfo(&buf, "ALTER TEXT SEARCH CONFIGURATION %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 ... */ char * DeparseTextSearchConfigurationCommentStmt(Node *node) { CommentStmt *stmt = castNode(CommentStmt, node); Assert(stmt->objtype == OBJECT_TSCONFIGURATION); StringInfoData buf = { 0 }; initStringInfo(&buf); appendStringInfo(&buf, "COMMENT ON TEXT SEARCH CONFIGURATION %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 */ static void AppendStringInfoTokentypeList(StringInfo buf, List *tokentypes) { Value *tokentype = NULL; bool first = true; foreach_ptr(tokentype, tokentypes) { if (nodeTag(tokentype) != T_String) { elog(ERROR, "unexpected tokentype for deparsing in text search configuration"); } if (!first) { appendStringInfoString(buf, ", "); } first = false; appendStringInfoString(buf, strVal(tokentype)); } } /* * AppendStringInfoDictnames specializes in appending a comma separated list of * dictionaries to TEXT SEARCH CONFIGURATION commands. */ static void AppendStringInfoDictnames(StringInfo buf, List *dicts) { List *dictNames = NIL; bool first = true; foreach_ptr(dictNames, dicts) { if (!first) { appendStringInfoString(buf, ", "); } first = false; char *dictIdentifier = NameListToQuotedString(dictNames); appendStringInfoString(buf, dictIdentifier); } } /* * DeparseAlterTextSearchConfigurationOwnerStmt returns the sql statement representing * ALTER TEXT SEARCH CONFIGURATION ... ONWER TO ... commands. */ char * DeparseAlterTextSearchConfigurationOwnerStmt(Node *node) { AlterOwnerStmt *stmt = castNode(AlterOwnerStmt, node); Assert(stmt->objectType == OBJECT_TSCONFIGURATION); StringInfoData buf = { 0 }; initStringInfo(&buf); appendStringInfo(&buf, "ALTER TEXT SEARCH CONFIGURATION %s OWNER TO %s;", NameListToQuotedString(castNode(List, stmt->object)), RoleSpecString(stmt->newowner, true)); return buf.data; }