1 /*
2 * Some utility functions for Postgres.
3 *
4 * - Literal & ident quoting.
5 * - Array parsing
6 */
7
8 #include <usual/pgutil.h>
9
10 #include <usual/ctype.h>
11
12 /* str -> E'str' */
pg_quote_literal(char * _dst,const char * _src,int dstlen)13 bool pg_quote_literal(char *_dst, const char *_src, int dstlen)
14 {
15 char *dst = _dst;
16 char *end = _dst + dstlen - 2;
17 const char *src = _src;
18 bool stdquote = true;
19
20 if (dstlen < 3)
21 return false;
22
23 if (_src == NULL) {
24 if (dstlen < 5)
25 return false;
26 memcpy(_dst, "NULL", 5);
27 return true;
28 }
29
30 retry:
31 *dst++ = '\'';
32 while (*src && dst < end) {
33 if (*src == '\'')
34 *dst++ = '\'';
35 else if (*src == '\\') {
36 if (stdquote)
37 goto retry_ext;
38 *dst++ = '\\';
39 }
40 *dst++ = *src++;
41 }
42 if (*src || dst > end)
43 return false;
44
45 *dst++ = '\'';
46 *dst = 0;
47
48 return true;
49 retry_ext:
50 /* string contains '\\', retry as E'' string */
51 dst = _dst;
52 src = _src;
53 *dst++ = 'E';
54 stdquote = false;
55 goto retry;
56 }
57
id_start(unsigned char c)58 static inline bool id_start(unsigned char c)
59 {
60 return (c >= 'a' && c <= 'z') || c == '_';
61 }
62
id_body(unsigned char c)63 static inline bool id_body(unsigned char c)
64 {
65 return id_start(c) || (c >= '0' && c <= '9');
66 }
67
68 /* ident -> "ident" */
pg_quote_ident(char * _dst,const char * _src,int dstlen)69 bool pg_quote_ident(char *_dst, const char *_src, int dstlen)
70 {
71 char *dst = _dst;
72 char *end = _dst + dstlen - 1;
73 const char *src = _src;
74
75 if (dstlen < 1)
76 return false;
77
78 if (!id_start(*src))
79 goto needs_quoting;
80
81 while (*src && dst < end) {
82 if (!id_body(*src))
83 goto needs_quoting;
84 *dst++ = *src++;
85 }
86 if (*src)
87 return false;
88 *dst = 0;
89
90 if (!pg_is_reserved_word(_dst))
91 return true;
92
93 needs_quoting:
94 dst = _dst;
95 src = _src;
96 end = _dst + dstlen - 2;
97 if (dstlen < 3)
98 return false;
99 *dst++ = '"';
100 while (*src && dst < end) {
101 if (*src == '"')
102 *dst++ = *src;
103 *dst++ = *src++;
104 }
105 if (*src)
106 return false;
107 *dst++ = '"';
108 *dst = 0;
109 return true;
110 }
111
112 /* schema.name -> "schema"."name" */
pg_quote_fqident(char * _dst,const char * _src,int dstlen)113 bool pg_quote_fqident(char *_dst, const char *_src, int dstlen)
114 {
115 const char *dot = strchr(_src, '.');
116 char scmbuf[128];
117 const char *scm;
118 int scmlen;
119 if (dot) {
120 scmlen = dot - _src;
121 if (scmlen >= (int)sizeof(scmbuf))
122 return false;
123 memcpy(scmbuf, _src, scmlen);
124 scmbuf[scmlen] = 0;
125 scm = scmbuf;
126 _src = dot + 1;
127 } else {
128 scm = "public";
129 }
130 if (!pg_quote_ident(_dst, scm, dstlen))
131 return false;
132
133 scmlen = strlen(_dst);
134 _dst[scmlen] = '.';
135 _dst += scmlen + 1;
136 dstlen -= scmlen + 1;
137 if (!pg_quote_ident(_dst, _src, dstlen))
138 return false;
139 return true;
140 }
141
142 /*
143 * pgarray parsing
144 */
145
parse_value(struct StrList * arr,const char * val,const char * vend,CxMem * cx)146 static bool parse_value(struct StrList *arr, const char *val, const char *vend,
147 CxMem *cx)
148 {
149 int len;
150 const char *s;
151 char *str, *p;
152 unsigned c;
153
154 while (val < vend && isspace(*val))
155 val++;
156 while (vend > val && isspace(vend[-1]))
157 vend--;
158 if (val == vend) return false;
159
160 s = val;
161 len = vend - val;
162 if (len == 4 && !strncasecmp(val, "null", len)) {
163 return strlist_append_ref(arr, NULL);
164 }
165 p = str = cx_alloc(cx, len + 1);
166 if (!str)
167 return false;
168
169 /* unquote & copy */
170 while (s < vend) {
171 c = *s++;
172 if (c == '"') {
173 while (1) {
174 c = *s++;
175 if (c == '"')
176 break;
177 else if (c == '\\')
178 *p++ = *s++;
179 else
180 *p++ = c;
181 }
182 } else if (c == '\\') {
183 *p++ = *s++;
184 } else
185 *p++ = c;
186 }
187 *p++ = 0;
188 if (!strlist_append_ref(arr, str)) {
189 cx_free(cx, str);
190 return false;
191 }
192 return true;
193 }
194
pg_parse_array(const char * pgarr,CxMem * cx)195 struct StrList *pg_parse_array(const char *pgarr, CxMem *cx)
196 {
197 const char *s = pgarr;
198 struct StrList *lst;
199 const char *val = NULL;
200 unsigned c;
201
202 /* skip dimension def "[x,y]={..}" */
203 if (*s == '[') {
204 s = strchr(s, ']');
205 if (!s || s[1] != '=')
206 return NULL;
207 s += 2;
208 }
209 if (*s++ != '{')
210 return NULL;
211
212 lst = strlist_new(cx);
213 if (!lst)
214 return NULL;
215
216 while (*s) {
217 /* array end */
218 if (s[0] == '}') {
219 if (s[1] != 0) {
220 goto failed;
221 }
222 if (val) {
223 if (!parse_value(lst, val, s, cx))
224 goto failed;
225 }
226 return lst;
227 }
228
229 /* cannot init earlier to support empty arrays */
230 if (!val)
231 val = s;
232
233 /* val done? */
234 if (*s == ',') {
235 if (!parse_value(lst, val, s, cx))
236 goto failed;
237 val = ++s;
238 continue;
239 }
240
241 /* scan value */
242 c = *s++;
243 if (c == '"') {
244 while (1) {
245 c = *s++;
246 if (c == '"')
247 break;
248 else if (c == '\\') {
249 if (!*s) goto failed;
250 s++;
251 } else if (!*s)
252 goto failed;
253 }
254 } else if (c == '\\') {
255 if (!*s) goto failed;
256 s++;
257 }
258 }
259 if (s[-1] != '}')
260 goto failed;
261 return lst;
262 failed:
263 strlist_free(lst);
264 return NULL;
265 }
266
267 /*
268 * Postgres keyword lookup.
269 */
270
271 /* gperf tries ot inline a non-static function. */
272 #undef inline
273 #undef __inline
274 #undef __attribute__
275 #define inline
276 #define __inline
277 #define __attribute__(x)
278 #define long uintptr_t
279
280 /* include gperf code */
281 const char *pg_keyword_lookup_real(const char *str, unsigned int len);
282 #include <usual/pgutil_kwlookup.h>
283
pg_is_reserved_word(const char * str)284 bool pg_is_reserved_word(const char *str)
285 {
286 const char *kw = pg_keyword_lookup_real(str, strlen(str));
287 return kw != NULL;
288 }
289