1/*-------------------------------------------------------------------------
2 * A stack of automaton states to handle nested conditionals.
3 *
4 * Copyright (c) 2000-2019, PostgreSQL Global Development Group
5 *
6 * src/fe_utils/conditional.c
7 *
8 *-------------------------------------------------------------------------
9 */
10#include "postgres_fe.h"
11
12#include "fe_utils/conditional.h"
13
14/*
15 * create stack
16 */
17ConditionalStack
18conditional_stack_create(void)
19{
20 ConditionalStack cstack = pg_malloc(sizeof(ConditionalStackData));
21
22 cstack->head = NULL;
23 return cstack;
24}
25
26/*
27 * destroy stack
28 */
29void
30conditional_stack_destroy(ConditionalStack cstack)
31{
32 while (conditional_stack_pop(cstack))
33 continue;
34 free(cstack);
35}
36
37/*
38 * Create a new conditional branch.
39 */
40void
41conditional_stack_push(ConditionalStack cstack, ifState new_state)
42{
43 IfStackElem *p = (IfStackElem *) pg_malloc(sizeof(IfStackElem));
44
45 p->if_state = new_state;
46 p->query_len = -1;
47 p->paren_depth = -1;
48 p->next = cstack->head;
49 cstack->head = p;
50}
51
52/*
53 * Destroy the topmost conditional branch.
54 * Returns false if there was no branch to end.
55 */
56bool
57conditional_stack_pop(ConditionalStack cstack)
58{
59 IfStackElem *p = cstack->head;
60
61 if (!p)
62 return false;
63 cstack->head = cstack->head->next;
64 free(p);
65 return true;
66}
67
68/*
69 * Returns current stack depth, for debugging purposes.
70 */
71int
72conditional_stack_depth(ConditionalStack cstack)
73{
74 if (cstack == NULL)
75 return -1;
76 else
77 {
78 IfStackElem *p = cstack->head;
79 int depth = 0;
80
81 while (p != NULL)
82 {
83 depth++;
84 p = p->next;
85 }
86 return depth;
87 }
88}
89
90/*
91 * Fetch the current state of the top of the stack.
92 */
93ifState
94conditional_stack_peek(ConditionalStack cstack)
95{
96 if (conditional_stack_empty(cstack))
97 return IFSTATE_NONE;
98 return cstack->head->if_state;
99}
100
101/*
102 * Change the state of the topmost branch.
103 * Returns false if there was no branch state to set.
104 */
105bool
106conditional_stack_poke(ConditionalStack cstack, ifState new_state)
107{
108 if (conditional_stack_empty(cstack))
109 return false;
110 cstack->head->if_state = new_state;
111 return true;
112}
113
114/*
115 * True if there are no active \if-blocks.
116 */
117bool
118conditional_stack_empty(ConditionalStack cstack)
119{
120 return cstack->head == NULL;
121}
122
123/*
124 * True if we should execute commands normally; that is, the current
125 * conditional branch is active, or there is no open \if block.
126 */
127bool
128conditional_active(ConditionalStack cstack)
129{
130 ifState s = conditional_stack_peek(cstack);
131
132 return s == IFSTATE_NONE || s == IFSTATE_TRUE || s == IFSTATE_ELSE_TRUE;
133}
134
135/*
136 * Save current query buffer length in topmost stack entry.
137 */
138void
139conditional_stack_set_query_len(ConditionalStack cstack, int len)
140{
141 Assert(!conditional_stack_empty(cstack));
142 cstack->head->query_len = len;
143}
144
145/*
146 * Fetch last-recorded query buffer length from topmost stack entry.
147 * Will return -1 if no stack or it was never saved.
148 */
149int
150conditional_stack_get_query_len(ConditionalStack cstack)
151{
152 if (conditional_stack_empty(cstack))
153 return -1;
154 return cstack->head->query_len;
155}
156
157/*
158 * Save current parenthesis nesting depth in topmost stack entry.
159 */
160void
161conditional_stack_set_paren_depth(ConditionalStack cstack, int depth)
162{
163 Assert(!conditional_stack_empty(cstack));
164 cstack->head->paren_depth = depth;
165}
166
167/*
168 * Fetch last-recorded parenthesis nesting depth from topmost stack entry.
169 * Will return -1 if no stack or it was never saved.
170 */
171int
172conditional_stack_get_paren_depth(ConditionalStack cstack)
173{
174 if (conditional_stack_empty(cstack))
175 return -1;
176 return cstack->head->paren_depth;
177}
178