/
githubmirror
/
postgres
Обзор
Документация
Войти
/
githubmirror
/
postgres
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
contrib/pg_plan_advice/pgpa_parser.y
303 строки
7 KB
Robert Haas
pg_plan_advice: DO_NOT_SCAN is a simple tag, not a generic one.
29 май 2026, 20:51
29 май 2026, 20:51
b1901e2
Код
Авторство
О чём код?
%{ /* * Parser for plan advice * * Copyright (c) 2000-2026, PostgreSQL Global Development Group * * contrib/pg_plan_advice/pgpa_parser.y */ #include "postgres.h" #include <float.h> #include <math.h> #include "fmgr.h" #include "nodes/miscnodes.h" #include "utils/builtins.h" #include "utils/float.h" #include "pgpa_ast.h" #include "pgpa_parser.h" /* * Bison doesn't allocate anything that needs to live across parser calls, * so we can easily have it use palloc instead of malloc. This prevents * memory leaks if we error out during parsing. */ #define YYMALLOC palloc #define YYFREE pfree %} /* BISON Declarations */ %parse-param {List **result} %parse-param {char **parse_error_msg_p} %parse-param {yyscan_t yyscanner} %lex-param {List **result} %lex-param {char **parse_error_msg_p} %lex-param {yyscan_t yyscanner} %pure-parser %expect 0 %name-prefix="pgpa_yy" %union { char *str; int integer; List *list; pgpa_advice_item *item; pgpa_advice_target *target; pgpa_index_target *itarget; } %token <str> TOK_IDENT TOK_TAG_JOIN_ORDER TOK_TAG_INDEX %token <str> TOK_TAG_SIMPLE TOK_TAG_GENERIC %token <integer> TOK_INTEGER %type <integer> opt_ri_occurrence %type <item> advice_item %type <list> advice_item_list generic_target_list %type <list> index_target_list join_order_target_list %type <list> opt_partition simple_target_list %type <str> identifier opt_plan_name %type <target> generic_sublist join_order_sublist %type <target> relation_identifier %type <itarget> index_name %start parse_toplevel /* Grammar follows */ %% parse_toplevel: advice_item_list { (void) yynerrs; /* suppress compiler warning */ *result = $1; } ; advice_item_list: advice_item_list advice_item { $$ = lappend($1, $2); } | { $$ = NIL; } ; advice_item: TOK_TAG_JOIN_ORDER '(' join_order_target_list ')' { $$ = palloc0_object(pgpa_advice_item); $$->tag = PGPA_TAG_JOIN_ORDER; $$->targets = $3; if ($3 == NIL) pgpa_yyerror(result, parse_error_msg_p, yyscanner, "JOIN_ORDER must have at least one target"); } | TOK_TAG_INDEX '(' index_target_list ')' { $$ = palloc0_object(pgpa_advice_item); if (strcmp($1, "index_only_scan") == 0) $$->tag = PGPA_TAG_INDEX_ONLY_SCAN; else if (strcmp($1, "index_scan") == 0) $$->tag = PGPA_TAG_INDEX_SCAN; else elog(ERROR, "tag parsing failed: %s", $1); $$->targets = $3; } | TOK_TAG_SIMPLE '(' simple_target_list ')' { $$ = palloc0_object(pgpa_advice_item); if (strcmp($1, "bitmap_heap_scan") == 0) $$->tag = PGPA_TAG_BITMAP_HEAP_SCAN; else if (strcmp($1, "do_not_scan") == 0) $$->tag = PGPA_TAG_DO_NOT_SCAN; else if (strcmp($1, "no_gather") == 0) $$->tag = PGPA_TAG_NO_GATHER; else if (strcmp($1, "seq_scan") == 0) $$->tag = PGPA_TAG_SEQ_SCAN; else if (strcmp($1, "tid_scan") == 0) $$->tag = PGPA_TAG_TID_SCAN; else elog(ERROR, "tag parsing failed: %s", $1); $$->targets = $3; } | TOK_TAG_GENERIC '(' generic_target_list ')' { bool fail; $$ = palloc0_object(pgpa_advice_item); $$->tag = pgpa_parse_advice_tag($1, &fail); if (fail) { pgpa_yyerror(result, parse_error_msg_p, yyscanner, "unrecognized advice tag"); } if ($$->tag == PGPA_TAG_FOREIGN_JOIN) { foreach_ptr(pgpa_advice_target, target, $3) { if (target->ttype == PGPA_TARGET_IDENTIFIER || list_length(target->children) == 1) pgpa_yyerror(result, parse_error_msg_p, yyscanner, "FOREIGN_JOIN targets must contain more than one relation identifier"); } } $$->targets = $3; } ; relation_identifier: identifier opt_ri_occurrence opt_partition opt_plan_name { $$ = palloc0_object(pgpa_advice_target); $$->ttype = PGPA_TARGET_IDENTIFIER; $$->rid.alias_name = $1; $$->rid.occurrence = $2; if (list_length($3) == 2) { $$->rid.partnsp = linitial($3); $$->rid.partrel = lsecond($3); } else if ($3 != NIL) $$->rid.partrel = linitial($3); $$->rid.plan_name = $4; } ; index_name: identifier { $$ = palloc0_object(pgpa_index_target); $$->indname = $1; } | identifier '.' identifier { $$ = palloc0_object(pgpa_index_target); $$->indnamespace = $1; $$->indname = $3; } ; opt_ri_occurrence: '#' TOK_INTEGER { if ($2 <= 0) pgpa_yyerror(result, parse_error_msg_p, yyscanner, "only positive occurrence numbers are permitted"); $$ = $2; } | { /* The default occurrence number is 1. */ $$ = 1; } ; identifier: TOK_IDENT | TOK_TAG_JOIN_ORDER | TOK_TAG_INDEX | TOK_TAG_SIMPLE | TOK_TAG_GENERIC ; /* * When generating advice, we always schema-qualify the partition name, but * when parsing advice, we accept a specification that lacks one. */ opt_partition: '/' identifier '.' identifier { $$ = list_make2($2, $4); } | '/' identifier { $$ = list_make1($2); } | { $$ = NIL; } ; opt_plan_name: '@' identifier { $$ = $2; } | { $$ = NULL; } ; generic_target_list: generic_target_list relation_identifier { $$ = lappend($1, $2); } | generic_target_list generic_sublist { $$ = lappend($1, $2); } | { $$ = NIL; } ; generic_sublist: '(' simple_target_list ')' { $$ = palloc0_object(pgpa_advice_target); $$->ttype = PGPA_TARGET_ORDERED_LIST; $$->children = $2; } ; index_target_list: index_target_list relation_identifier index_name { $2->itarget = $3; $$ = lappend($1, $2); } | { $$ = NIL; } ; join_order_target_list: join_order_target_list relation_identifier { $$ = lappend($1, $2); } | join_order_target_list join_order_sublist { $$ = lappend($1, $2); } | { $$ = NIL; } ; join_order_sublist: '(' join_order_target_list ')' { $$ = palloc0_object(pgpa_advice_target); $$->ttype = PGPA_TARGET_ORDERED_LIST; $$->children = $2; } | '{' simple_target_list '}' { $$ = palloc0_object(pgpa_advice_target); $$->ttype = PGPA_TARGET_UNORDERED_LIST; $$->children = $2; } ; simple_target_list: simple_target_list relation_identifier { $$ = lappend($1, $2); } | { $$ = NIL; } ; %% /* * Parse an advice_string and return the resulting list of pgpa_advice_item * objects. If a parse error occurs, instead return NULL. * * If the return value is NULL, *error_p will be set to the error message; * otherwise, *error_p will be set to NULL. */ List * pgpa_parse(const char *advice_string, char **error_p) { yyscan_t scanner; List *result; char *error = NULL; pgpa_scanner_init(advice_string, &scanner); pgpa_yyparse(&result, &error, scanner); pgpa_scanner_finish(scanner); if (error != NULL) { *error_p = error; return NULL; } *error_p = NULL; return result; }