]> granicus.if.org Git - pgbouncer/blob - src/varcache.c
DateStyle and TimeZone server params need to use exact case.
[pgbouncer] / src / varcache.c
1 /*
2  * PgBouncer - Lightweight connection pooler for PostgreSQL.
3  * 
4  * Copyright (c) 2007-2009  Marko Kreen, Skype Technologies OÜ
5  * 
6  * Permission to use, copy, modify, and/or 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 #include <usual/pgutil.h>
26
27 struct var_lookup {
28         const char *name;
29         enum VarCacheIdx idx;
30 };
31
32 static const struct var_lookup lookup [] = {
33  {"client_encoding",             VClientEncoding },
34  {"DateStyle",                   VDateStyle },
35  {"TimeZone",                    VTimeZone },
36  {"standard_conforming_strings", VStdStr },
37  {"application_name",            VAppName },
38  {NULL},
39 };
40
41 static struct StrPool *vpool;
42
43 static inline struct PStr *get_value(VarCache *cache, const struct var_lookup *lk)
44 {
45         return cache->var_list[lk->idx];
46 }
47
48 bool varcache_set(VarCache *cache, const char *key, const char *value)
49 {
50         const struct var_lookup *lk;
51         struct PStr *pstr = NULL;
52
53         if (!vpool) {
54                 vpool = strpool_create(USUAL_ALLOC);
55                 if (!vpool)
56                         return false;
57         }
58
59         for (lk = lookup; lk->name; lk++) {
60                 if (strcasecmp(lk->name, key) == 0)
61                         goto set_value;
62         }
63         return false;
64
65 set_value:
66         /* drop old value */
67         strpool_decref(cache->var_list[lk->idx]);
68         cache->var_list[lk->idx] = NULL;
69
70         /* ignore empty value */
71         if (!value || !value[0])
72                 return false;
73
74         /* set new value */
75         pstr = strpool_get(vpool, value, strlen(value));
76         if (!pstr)
77                 return false;
78         cache->var_list[lk->idx] = pstr;
79         return true;
80 }
81
82 static int apply_var(PktBuf *pkt, const char *key,
83                      const struct PStr *cval,
84                      const struct PStr *sval)
85 {
86         char buf[128];
87         char qbuf[128];
88         unsigned len;
89
90         /* if unset, skip */
91         if (!cval || !sval || !*cval->str)
92                 return 0;
93
94         /* if equal, skip */
95         if (cval == sval)
96                 return 0;
97
98         /* ignore case difference */
99         if (strcasecmp(cval->str, sval->str) == 0)
100                 return 0;
101
102         /* the string may have been taken from startup pkt */
103         if (!pg_quote_literal(qbuf, cval->str, sizeof(qbuf)))
104                 return 0;
105
106         /* add SET statement to packet */
107         len = snprintf(buf, sizeof(buf), "SET %s=%s;", key, qbuf);
108         if (len < sizeof(buf)) {
109                 pktbuf_put_bytes(pkt, buf, len);
110                 return 1;
111         } else {
112                 log_warning("got too long value, skipping");
113                 return 0;
114         }
115 }
116
117 bool varcache_apply(PgSocket *server, PgSocket *client, bool *changes_p)
118 {
119         int changes = 0;
120         struct PStr *cval, *sval;
121         const struct var_lookup *lk;
122         int sql_ofs;
123         struct PktBuf *pkt = pktbuf_temp();
124
125         pktbuf_start_packet(pkt, 'Q');
126
127         /* grab quory position inside pkt */
128         sql_ofs = pktbuf_written(pkt);
129
130         for (lk = lookup; lk->name; lk++) {
131                 sval = get_value(&server->vars, lk);
132                 cval = get_value(&client->vars, lk);
133                 changes += apply_var(pkt, lk->name, cval, sval);
134         }
135         *changes_p = changes > 0;
136         if (!changes)
137                 return true;
138
139         pktbuf_put_char(pkt, 0);
140         pktbuf_finish_packet(pkt);
141
142         slog_debug(server, "varcache_apply: %s", pkt->buf + sql_ofs);
143         return pktbuf_send_immidiate(pkt, server);
144 }
145
146 void varcache_fill_unset(VarCache *src, PgSocket *dst)
147 {
148         struct PStr *srcval, *dstval;
149         const struct var_lookup *lk;
150         for (lk = lookup; lk->name; lk++) {
151                 srcval = src->var_list[lk->idx];
152                 dstval = dst->vars.var_list[lk->idx];
153                 if (!dstval) {
154                         strpool_incref(srcval);
155                         dst->vars.var_list[lk->idx] = srcval;
156                 }
157         }
158 }
159
160 void varcache_clean(VarCache *cache)
161 {
162         int i;
163         for (i = 0; i < NumVars; i++) {
164                 strpool_decref(cache->var_list[i]);
165                 cache->var_list[i] = NULL;
166         }
167 }
168
169 void varcache_add_params(PktBuf *pkt, VarCache *vars)
170 {
171         struct PStr *val;
172         const struct var_lookup *lk;
173         for (lk = lookup; lk->name; lk++) {
174                 val = vars->var_list[lk->idx];
175                 if (val)
176                         pktbuf_write_ParameterStatus(pkt, lk->name, val->str);
177         }
178 }
179