Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/include/duckdb/parser/peg/inlined_grammar.gram
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ Statement <-
DropDatabaseStatement /
CreateTSDictionaryStatement /
DropTSDictionaryStatement /
CreateServerStatement /
DropServerStatement /
CreateRoleStatement /
DropRoleStatement /
CreatePolicyStatement /
Expand Down Expand Up @@ -995,6 +997,12 @@ SeqMinOrMax <- MinValue / MaxValue
MinValue <- 'MINVALUE'
MaxValue <- 'MAXVALUE'

CreateServerStatement <- 'CREATE' 'SERVER' IfNotExists? ColId 'FOREIGN' 'DATA' 'WRAPPER' Identifier ServerOptions?
DropServerStatement <- 'DROP' 'SERVER' IfExists? ColId DropBehavior?

ServerOptions <- 'OPTIONS' Parens(List(ServerOption))
ServerOption <- ColLabel StringLiteral

CreateSubscriptionStatement <- 'CREATE' 'SUBSCRIPTION' Identifier SubscriptionConnection?
SubscriptionConnection <- 'CONNECTION' StringLiteral SubscriptionPublication?
SubscriptionPublication <- 'PUBLICATION' List(Identifier)
Expand Down
9 changes: 8 additions & 1 deletion src/include/duckdb/parser/peg/inlined_grammar.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ const char INLINED_PEG_GRAMMAR[] = {
" DropDatabaseStatement /\n"
" CreateTSDictionaryStatement /\n"
" DropTSDictionaryStatement /\n"
" CreateServerStatement /\n"
" DropServerStatement /\n"
" CreateRoleStatement /\n"
" DropRoleStatement /\n"
" CreatePolicyStatement /\n"
Expand Down Expand Up @@ -932,6 +934,10 @@ const char INLINED_PEG_GRAMMAR[] = {
"SeqMinOrMax <- MinValue / MaxValue\n"
"MinValue <- 'MINVALUE'\n"
"MaxValue <- 'MAXVALUE'\n"
"CreateServerStatement <- 'CREATE' 'SERVER' IfNotExists? ColId 'FOREIGN' 'DATA' 'WRAPPER' Identifier ServerOptions?\n"
"DropServerStatement <- 'DROP' 'SERVER' IfExists? ColId DropBehavior?\n"
"ServerOptions <- 'OPTIONS' Parens(List(ServerOption))\n"
"ServerOption <- ColLabel StringLiteral\n"
"CreateSubscriptionStatement <- 'CREATE' 'SUBSCRIPTION' Identifier SubscriptionConnection?\n"
"SubscriptionConnection <- 'CONNECTION' StringLiteral SubscriptionPublication?\n"
"SubscriptionPublication <- 'PUBLICATION' List(Identifier)\n"
Expand Down Expand Up @@ -1673,7 +1679,8 @@ const char INLINED_PEG_GRAMMAR[] = {
"OptionBool <- 'TRUE' / 'FALSE'\n"
"RoleOrUser <- 'ROLE' / 'USER'\n"
"# Object class for GRANT/REVOKE ... ON <type> name. Absent defaults to TABLE.\n"
"GrantObjType <- 'TABLE' / 'SEQUENCE' / 'FUNCTION' / 'DATABASE' / 'SCHEMA' / 'TYPE' / 'DOMAIN'\n"
"GrantObjType <- ForeignServerObjType / 'TABLE' / 'SEQUENCE' / 'FUNCTION' / 'DATABASE' / 'SCHEMA' / 'TYPE' / 'DOMAIN'\n"
"ForeignServerObjType <- 'FOREIGN' 'SERVER'\n"
"# PUBLIC is the pseudo-role; otherwise a normal role identifier.\n"
"Grantee <- 'PUBLIC' / ColId\n"
"RoleOptionList <- RoleOption+\n"
Expand Down
7 changes: 7 additions & 0 deletions src/include/duckdb/parser/peg/transformer/peg_transformer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,7 @@ class PEGTransformerFactory {
void RegisterNotify();
void RegisterCreateTextSearchDictionary();
void RegisterRbac();
void RegisterForeignServer();
void RegisterPivot();
void RegisterCreateMacro();
void RegisterDrop();
Expand Down Expand Up @@ -523,6 +524,12 @@ class PEGTransformerFactory {
ParseResult &parse_result);
static unique_ptr<SQLStatement> TransformAlterTableRowSecurityStatement(PEGTransformer &transformer,
ParseResult &parse_result);
// create_server.gram — CREATE/DROP SERVER walk an OPTIONS Parens(List(...)) body that the
// generator cannot auto-extract; hand-write the entry points.
static unique_ptr<SQLStatement> TransformCreateServerStatement(PEGTransformer &transformer,
ParseResult &parse_result);
static unique_ptr<SQLStatement> TransformDropServerStatement(PEGTransformer &transformer,
ParseResult &parse_result);
// comment.gram
static Value TransformCommentValue(PEGTransformer &transformer, ParseResult &parse_result);

Expand Down
2 changes: 2 additions & 0 deletions src/parser/peg/grammar/statements/common.gram
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ Statement <-
DropDatabaseStatement /
CreateTSDictionaryStatement /
DropTSDictionaryStatement /
CreateServerStatement /
DropServerStatement /
CreateRoleStatement /
DropRoleStatement /
CreatePolicyStatement /
Expand Down
5 changes: 5 additions & 0 deletions src/parser/peg/grammar/statements/create_server.gram
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
CreateServerStatement <- 'CREATE' 'SERVER' IfNotExists? ColId 'FOREIGN' 'DATA' 'WRAPPER' Identifier ServerOptions?
DropServerStatement <- 'DROP' 'SERVER' IfExists? ColId DropBehavior?

ServerOptions <- 'OPTIONS' Parens(List(ServerOption))
ServerOption <- ColLabel StringLiteral
3 changes: 2 additions & 1 deletion src/parser/peg/grammar/statements/rbac.gram
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@ OptionBool <- 'TRUE' / 'FALSE'

RoleOrUser <- 'ROLE' / 'USER'
# Object class for GRANT/REVOKE ... ON <type> name. Absent defaults to TABLE.
GrantObjType <- 'TABLE' / 'SEQUENCE' / 'FUNCTION' / 'DATABASE' / 'SCHEMA' / 'TYPE' / 'DOMAIN'
GrantObjType <- ForeignServerObjType / 'TABLE' / 'SEQUENCE' / 'FUNCTION' / 'DATABASE' / 'SCHEMA' / 'TYPE' / 'DOMAIN'
ForeignServerObjType <- 'FOREIGN' 'SERVER'
# PUBLIC is the pseudo-role; otherwise a normal role identifier.
Grantee <- 'PUBLIC' / ColId

Expand Down
1 change: 1 addition & 0 deletions src/parser/peg/transformer/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ add_library_unity(
transform_create_publication.cpp
transform_create_subscription.cpp
transform_notify.cpp
transform_foreign_server.cpp
transform_create_text_search_dictionary.cpp
transform_rbac.cpp
transform_create_trigger.cpp
Expand Down
7 changes: 7 additions & 0 deletions src/parser/peg/transformer/peg_transformer_factory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,12 @@ void PEGTransformerFactory::RegisterCreateTextSearchDictionary() {
REGISTER_TRANSFORM(TransformDropTSDictionaryStatement);
}

void PEGTransformerFactory::RegisterForeignServer() {
// create_server.gram — the OPTIONS Parens(List(...)) body is not auto-extractable.
REGISTER_TRANSFORM(TransformCreateServerStatement);
REGISTER_TRANSFORM(TransformDropServerStatement);
}

void PEGTransformerFactory::RegisterPivot() {
// PivotStatement and UnpivotStatement measure parameter usage while transforming
// the source table, so their top-level wrappers remain manual.
Expand Down Expand Up @@ -275,6 +281,7 @@ PEGTransformerFactory::PEGTransformerFactory() {
RegisterNotify();
RegisterCreateTextSearchDictionary();
RegisterRbac();
RegisterForeignServer();
RegisterPivot();
RegisterCreateMacro();
RegisterDrop();
Expand Down
87 changes: 87 additions & 0 deletions src/parser/peg/transformer/transform_foreign_server.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#include "duckdb/parser/peg/transformer/peg_transformer.hpp"
#include "duckdb/parser/statement/pragma_statement.hpp"
#include "duckdb/parser/expression/constant_expression.hpp"
#include "duckdb/common/types/value.hpp"
#include "duckdb/common/exception.hpp"
#include "duckdb/common/string_util.hpp"

namespace duckdb {

// ServerOptions <- 'OPTIONS' Parens(List(ServerOption)) — the optional child at `child_idx`,
// shared by CREATE SERVER and CREATE USER MAPPING. Emplaces each option into `info.named_parameters`.
static void TransformServerOptionsInto(PEGTransformer &transformer, ListParseResult &list_pr, idx_t child_idx,
PragmaInfo &info) {
auto &options_opt = list_pr.Child<OptionalParseResult>(child_idx);
if (!options_opt.HasResult()) {
return;
}
auto &options_list = options_opt.GetResult().Cast<ListParseResult>();
auto &list_inside = PEGTransformerFactory::ExtractResultFromParens(options_list.Child<ListParseResult>(1));
auto elements = PEGTransformerFactory::ExtractParseResultsFromList(list_inside);
for (auto &elem_ref : elements) {
auto &elem_pr = elem_ref.get().Cast<ListParseResult>();
// ServerOption <- ColLabel StringLiteral. Pass raw children to
// Transform<string> so it dispatches on each node's actual type
// (ColLabel resolves a keyword/identifier; StringLiteral a literal).
auto opt_name = transformer.Transform<string>(elem_pr.GetChild(0));
auto opt_value = transformer.Transform<string>(elem_pr.GetChild(1));
auto value_expr = make_uniq<ConstantExpression>(Value(opt_value));
auto [_, inserted] = info.named_parameters.emplace(opt_name, std::move(value_expr));
if (!inserted) {
throw InvalidInputException("conflicting or redundant options: \"%s\" specified more than once", opt_name);
}
}
}

// CREATE SERVER [IF NOT EXISTS] name FOREIGN DATA WRAPPER fdw OPTIONS (k 'v', ...)
// -> PRAGMA create_foreign_server('name', 'fdw', if_not_exists, k := 'v', ...)
unique_ptr<SQLStatement> PEGTransformerFactory::TransformCreateServerStatement(PEGTransformer &transformer,
ParseResult &parse_result) {
auto &list_pr = parse_result.Cast<ListParseResult>();
// Children:
// 0: 'CREATE'
// 1: 'SERVER'
// 2: IfNotExists?
// 3: ColId (server name -- a bare identifier, PG-style)
// 4: 'FOREIGN'
// 5: 'DATA'?
// 6: 'WRAPPER'
// 7: Identifier (fdw name)
// 8: ServerOptions?
bool if_not_exists = list_pr.Child<OptionalParseResult>(2).HasResult();
auto server_name = transformer.Transform<string>(list_pr.GetChild(3));
// The fdw name is a plain Identifier leaf (not a choice/list rule).
auto fdw_name = list_pr.Child<IdentifierParseResult>(7).identifier;

auto result = make_uniq<PragmaStatement>();
result->info->name = "create_foreign_server";
result->info->parameters.push_back(make_uniq<ConstantExpression>(Value(server_name)));
result->info->parameters.push_back(make_uniq<ConstantExpression>(Value(fdw_name)));
result->info->parameters.push_back(make_uniq<ConstantExpression>(Value::BOOLEAN(if_not_exists)));
TransformServerOptionsInto(transformer, list_pr, 8, *result->info);
return std::move(result);
}

// DROP SERVER [IF EXISTS] name -> PRAGMA drop_foreign_server('name', missing_ok)
unique_ptr<SQLStatement> PEGTransformerFactory::TransformDropServerStatement(PEGTransformer &transformer,
ParseResult &parse_result) {
auto &list_pr = parse_result.Cast<ListParseResult>();
// Children:
// 0: 'DROP', 1: 'SERVER'
// 2: IfExists?
// 3: ColId (server name)
// 4: DropBehavior? (CASCADE / RESTRICT; RESTRICT/absent = false)
bool missing_ok = list_pr.Child<OptionalParseResult>(2).HasResult();
auto server_name = transformer.Transform<string>(list_pr.GetChild(3));
bool cascade = false;
transformer.TransformOptional<bool>(list_pr, 4, cascade);

auto result = make_uniq<PragmaStatement>();
result->info->name = "drop_foreign_server";
result->info->parameters.push_back(make_uniq<ConstantExpression>(Value(server_name)));
result->info->parameters.push_back(make_uniq<ConstantExpression>(Value::BOOLEAN(missing_ok)));
result->info->parameters.push_back(make_uniq<ConstantExpression>(Value::BOOLEAN(cascade)));
return std::move(result);
}

} // namespace duckdb
19 changes: 13 additions & 6 deletions src/parser/peg/transformer/transform_rbac.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -786,12 +786,19 @@ static GrantTargetInfo TransformGrantTarget(PEGTransformer &transformer, ParseRe
string objtype = "TABLE";
auto &objtype_opt = named.Child<OptionalParseResult>(0);
if (objtype_opt.HasResult()) {
auto &kw = objtype_opt.GetResult()
.Cast<ListParseResult>()
.Child<ChoiceParseResult>(0)
.GetResult()
.Cast<KeywordParseResult>();
objtype = StringUtil::Upper(kw.keyword);
auto &chosen =
objtype_opt.GetResult().Cast<ListParseResult>().Child<ChoiceParseResult>(0).GetResult();
if (chosen.type == ParseResultType::KEYWORD) {
objtype = StringUtil::Upper(chosen.Cast<KeywordParseResult>().keyword);
} else {
// A multi-keyword object type (e.g. FOREIGN SERVER) is a rule whose
// body is a keyword sequence; join them into one space-separated word.
vector<string> words;
for (auto &child : chosen.Cast<ListParseResult>().GetChildren()) {
words.push_back(StringUtil::Upper(child.get().Cast<KeywordParseResult>().keyword));
}
objtype = StringUtil::Join(words, " ");
}
}
return {objtype, QualifiedTableName(transformer, named.GetChild(1))};
}
Expand Down