]> granicus.if.org Git - pgbouncer/blob - src/varcache.c
sanity check before trying to set parameters
[pgbouncer] / src / varcache.c
1 /*
2  * PgBouncer - Lightweight connection pooler for PostgreSQL.
3  * 
4  * Copyright (c) 2007 Marko Kreen, Skype Technologies OÜ
5  * 
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  * 
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18
19 /*
20  * Operations with server config parameters.
21  */
22
23 #include "bouncer.h"
24
25 struct var_lookup {
26         const char *name;
27         int offset;
28         int len;
29 };
30
31 static const struct var_lookup lookup [] = {
32  {"client_encoding",             offsetof(VarCache, client_encoding), VAR_ENCODING_LEN },
33  {"datestyle",                   offsetof(VarCache, datestyle),       VAR_DATESTYLE_LEN },
34  {"timezone",                    offsetof(VarCache, timezone),        VAR_TIMEZONE_LEN },
35  {"standard_conforming_strings", offsetof(VarCache, std_strings),     VAR_STDSTR_LEN },
36  {NULL},
37 };
38
39 static inline char *get_value(VarCache *cache, const struct var_lookup *lk)
40 {
41         return (char *)(cache) + lk->offset;
42 }
43
44 bool varcache_set(VarCache *cache, const char *key, const char *value)
45 {
46         int vlen;
47         char *pos;
48         const struct var_lookup *lk;
49
50         for (lk = lookup; lk->name; lk++) {
51                 if (strcasecmp(lk->name, key) != 0)
52                         continue;
53
54                 vlen = strlen(value);
55                 if (vlen >= lk->len) {
56                         log_warning("varcache_set overflow: %s", key);
57                         return false;
58                 }
59
60                 pos = get_value(cache, lk);
61                 memcpy(pos, value, vlen + 1);
62                 return true;
63         }
64         return false;
65 }
66
67 static bool is_std_quote(VarCache *vars)
68 {
69         const char *val = vars->std_strings;
70         return strcasecmp(val, "on") == 0;
71 }
72
73 static bool quote_literal(char *buf, int buflen, const char *src, bool std_quote)
74 {
75         char *dst = buf;
76         char *end = buf + buflen - 2;
77
78         if (buflen < 3)
79                 return false;
80
81         *dst++ = '\'';
82         while (*src && dst < end) {
83                 if (*src == '\'')
84                         *dst++ = '\'';
85                 else if (*src == '\\' && !std_quote)
86                         *dst++ = '\\';
87                 *dst++ = *src++;
88         }
89         if (*src || dst > end)
90                 return false;
91
92         *dst++ = '\'';
93         *dst = 0;
94
95         return true;
96 }
97
98 static int apply_var(PktBuf *pkt, const char *key,
99                      const char *cval, const char *sval,
100                      bool std_quote)
101 {
102         char buf[128];
103         char qbuf[128];
104         int len;
105
106         if (strcasecmp(cval, sval) == 0)
107                 return 0;
108
109         /* sanity check */
110         if (!*cval || !*sval) {
111                 /* parameters that can change should be always set */
112                 log_warning("Parameter unset: key='%s' client='%s' server='%s'",
113                             key, cval, sval);
114                 return 0;
115         }
116
117         /* the string may have been taken from startup pkt */
118         if (!quote_literal(qbuf, sizeof(qbuf), cval, std_quote))
119                 return 0;
120
121         /* add SET statement to packet */
122         len = snprintf(buf, sizeof(buf), "SET %s=%s;", key, qbuf);
123         if (len < sizeof(buf)) {
124                 pktbuf_put_bytes(pkt, buf, len);
125                 return 1;
126         } else {
127                 log_warning("got too long value, skipping");
128                 return 0;
129         }
130 }
131
132 bool varcache_apply(PgSocket *server, PgSocket *client, bool *changes_p)
133 {
134         PktBuf pkt;
135         uint8 buf[1024];
136         int changes = 0;
137         const char *cval, *sval;
138         const struct var_lookup *lk;
139         uint8 *debug_sql;
140         bool std_quote = is_std_quote(&server->vars);
141
142         pktbuf_static(&pkt, buf, sizeof(buf));
143         pktbuf_start_packet(&pkt, 'Q');
144
145         /* grab quory position inside pkt */
146         debug_sql = pkt.buf + pkt.write_pos;
147
148         for (lk = lookup; lk->name; lk++) {
149                 sval = get_value(&server->vars, lk);
150                 cval = get_value(&client->vars, lk);
151                 changes += apply_var(&pkt, lk->name, cval, sval, std_quote);
152         }
153         *changes_p = changes > 0;
154         if (!changes)
155                 return true;
156
157         pktbuf_put_char(&pkt, 0);
158         pktbuf_finish_packet(&pkt);
159
160         slog_debug(server, "varcache_apply: %s", debug_sql);
161         return pktbuf_send_immidiate(&pkt, server);
162 }
163
164 void varcache_fill_unset(VarCache *src, PgSocket *dst)
165 {
166         char *srcval, *dstval;
167         const struct var_lookup *lk;
168         for (lk = lookup; lk->name; lk++) {
169                 srcval = get_value(src, lk);
170                 dstval = get_value(&dst->vars, lk);
171                 if (!*dstval)
172                         strlcpy(dstval, srcval, lk->len);
173         }
174 }
175
176 void varcache_clean(VarCache *cache)
177 {
178         cache->client_encoding[0] = 0;
179         cache->datestyle[0] = 0;
180         cache->timezone[0] = 0;
181         cache->std_strings[0] = 0;
182 }
183
184 void varcache_add_params(PktBuf *pkt, VarCache *vars)
185 {
186         char *val;
187         const struct var_lookup *lk;
188         for (lk = lookup; lk->name; lk++) {
189                 val = get_value(vars, lk);
190                 if (*val)
191                         pktbuf_write_ParameterStatus(pkt, lk->name, val);
192         }
193 }
194