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