mirror of
https://github.com/postgres/postgres.git
synced 2026-02-26 11:21:03 -05:00
This patch adds nestable conditional blocks to psql. The control structure feature per se is complete, but the boolean expressions understood by \if and \elif are pretty primitive; basically, after variable substitution and backtick expansion, the result has to be "true" or "false" or one of the other standard spellings of a boolean value. But that's enough for many purposes, since you can always do the heavy lifting on the server side; and we can extend it later. Along the way, pay down some of the technical debt that had built up around psql/command.c: * Refactor exec_command() into a function per command, instead of being a 1500-line monstrosity. This makes the file noticeably longer because of repetitive function header/trailer overhead, but it seems much more readable. * Teach psql_get_variable() and psqlscanslash.l to suppress variable substitution and backtick expansion on the basis of the conditional stack state, thereby allowing removal of the OT_NO_EVAL kluge. * Fix the no-doubt-once-expedient hack of sometimes silently substituting mainloop.c's previous_buf for query_buf when calling HandleSlashCmds. (It's a bit remarkable that commands like \r worked at all with that.) Recall of a previous query is now done explicitly in the slash commands where that should happen. Corey Huinker, reviewed by Fabien Coelho, further hacking by me Discussion: https://postgr.es/m/CADkLM=c94OSRTnat=LX0ivNq4pxDNeoomFfYvBKM5N_xfmLtAA@mail.gmail.com
153 lines
3 KiB
C
153 lines
3 KiB
C
/*
|
|
* psql - the PostgreSQL interactive terminal
|
|
*
|
|
* Copyright (c) 2000-2017, PostgreSQL Global Development Group
|
|
*
|
|
* src/bin/psql/conditional.c
|
|
*/
|
|
#include "postgres_fe.h"
|
|
|
|
#include "conditional.h"
|
|
|
|
/*
|
|
* create stack
|
|
*/
|
|
ConditionalStack
|
|
conditional_stack_create(void)
|
|
{
|
|
ConditionalStack cstack = pg_malloc(sizeof(ConditionalStackData));
|
|
|
|
cstack->head = NULL;
|
|
return cstack;
|
|
}
|
|
|
|
/*
|
|
* destroy stack
|
|
*/
|
|
void
|
|
conditional_stack_destroy(ConditionalStack cstack)
|
|
{
|
|
while (conditional_stack_pop(cstack))
|
|
continue;
|
|
free(cstack);
|
|
}
|
|
|
|
/*
|
|
* Create a new conditional branch.
|
|
*/
|
|
void
|
|
conditional_stack_push(ConditionalStack cstack, ifState new_state)
|
|
{
|
|
IfStackElem *p = (IfStackElem *) pg_malloc(sizeof(IfStackElem));
|
|
|
|
p->if_state = new_state;
|
|
p->query_len = -1;
|
|
p->paren_depth = -1;
|
|
p->next = cstack->head;
|
|
cstack->head = p;
|
|
}
|
|
|
|
/*
|
|
* Destroy the topmost conditional branch.
|
|
* Returns false if there was no branch to end.
|
|
*/
|
|
bool
|
|
conditional_stack_pop(ConditionalStack cstack)
|
|
{
|
|
IfStackElem *p = cstack->head;
|
|
|
|
if (!p)
|
|
return false;
|
|
cstack->head = cstack->head->next;
|
|
free(p);
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* Fetch the current state of the top of the stack.
|
|
*/
|
|
ifState
|
|
conditional_stack_peek(ConditionalStack cstack)
|
|
{
|
|
if (conditional_stack_empty(cstack))
|
|
return IFSTATE_NONE;
|
|
return cstack->head->if_state;
|
|
}
|
|
|
|
/*
|
|
* Change the state of the topmost branch.
|
|
* Returns false if there was no branch state to set.
|
|
*/
|
|
bool
|
|
conditional_stack_poke(ConditionalStack cstack, ifState new_state)
|
|
{
|
|
if (conditional_stack_empty(cstack))
|
|
return false;
|
|
cstack->head->if_state = new_state;
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* True if there are no active \if-blocks.
|
|
*/
|
|
bool
|
|
conditional_stack_empty(ConditionalStack cstack)
|
|
{
|
|
return cstack->head == NULL;
|
|
}
|
|
|
|
/*
|
|
* True if we should execute commands normally; that is, the current
|
|
* conditional branch is active, or there is no open \if block.
|
|
*/
|
|
bool
|
|
conditional_active(ConditionalStack cstack)
|
|
{
|
|
ifState s = conditional_stack_peek(cstack);
|
|
|
|
return s == IFSTATE_NONE || s == IFSTATE_TRUE || s == IFSTATE_ELSE_TRUE;
|
|
}
|
|
|
|
/*
|
|
* Save current query buffer length in topmost stack entry.
|
|
*/
|
|
void
|
|
conditional_stack_set_query_len(ConditionalStack cstack, int len)
|
|
{
|
|
Assert(!conditional_stack_empty(cstack));
|
|
cstack->head->query_len = len;
|
|
}
|
|
|
|
/*
|
|
* Fetch last-recorded query buffer length from topmost stack entry.
|
|
* Will return -1 if no stack or it was never saved.
|
|
*/
|
|
int
|
|
conditional_stack_get_query_len(ConditionalStack cstack)
|
|
{
|
|
if (conditional_stack_empty(cstack))
|
|
return -1;
|
|
return cstack->head->query_len;
|
|
}
|
|
|
|
/*
|
|
* Save current parenthesis nesting depth in topmost stack entry.
|
|
*/
|
|
void
|
|
conditional_stack_set_paren_depth(ConditionalStack cstack, int depth)
|
|
{
|
|
Assert(!conditional_stack_empty(cstack));
|
|
cstack->head->paren_depth = depth;
|
|
}
|
|
|
|
/*
|
|
* Fetch last-recorded parenthesis nesting depth from topmost stack entry.
|
|
* Will return -1 if no stack or it was never saved.
|
|
*/
|
|
int
|
|
conditional_stack_get_paren_depth(ConditionalStack cstack)
|
|
{
|
|
if (conditional_stack_empty(cstack))
|
|
return -1;
|
|
return cstack->head->paren_depth;
|
|
}
|