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 | */ |
17 | ConditionalStack |
18 | conditional_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 | */ |
29 | void |
30 | conditional_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 | */ |
40 | void |
41 | conditional_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 | */ |
56 | bool |
57 | conditional_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 | */ |
71 | int |
72 | conditional_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 | */ |
93 | ifState |
94 | conditional_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 | */ |
105 | bool |
106 | conditional_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 | */ |
117 | bool |
118 | conditional_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 | */ |
127 | bool |
128 | conditional_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 | */ |
138 | void |
139 | conditional_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 | */ |
149 | int |
150 | conditional_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 | */ |
160 | void |
161 | conditional_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 | */ |
171 | int |
172 | conditional_stack_get_paren_depth(ConditionalStack cstack) |
173 | { |
174 | if (conditional_stack_empty(cstack)) |
175 | return -1; |
176 | return cstack->head->paren_depth; |
177 | } |
178 |