]> granicus.if.org Git - pgbouncer/blob - src/varcache.c
Use pg_quote_literal from libusual.
[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();
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 char *cval, const char *sval)
84 {
85         char buf[128];
86         char qbuf[128];
87         unsigned len;
88
89         if (strcasecmp(cval, sval) == 0)
90                 return 0;
91
92         /* if unset, ignore */
93         if (!*cval)
94                 return 0;
95
96         /* the string may have been taken from startup pkt */
97         if (!pg_quote_literal(qbuf, cval, sizeof(qbuf)))
98                 return 0;
99
100         /* add SET statement to packet */
101         len = snprintf(buf, sizeof(buf), "SET %s=%s;", key, qbuf);
102         if (len < sizeof(buf)) {
103                 pktbuf_put_bytes(pkt, buf, len);
104                 return 1;
105         } else {
106                 log_warning("got too long value, skipping");
107                 return 0;
108         }
109 }
110
111 bool varcache_apply(PgSocket *server, PgSocket *client, bool *changes_p)
112 {
113         int changes = 0;
114         struct PStr *cval, *sval;
115         const struct var_lookup *lk;
116         int sql_ofs;
117         struct PktBuf *pkt = pktbuf_temp();
118
119         pktbuf_start_packet(pkt, 'Q');
120
121         /* grab quory position inside pkt */
122         sql_ofs = pktbuf_written(pkt);
123
124         for (lk = lookup; lk->name; lk++) {
125                 sval = get_value(&server->vars, lk);
126                 cval = get_value(&client->vars, lk);
127                 if (cval)
128                         changes += apply_var(pkt, lk->name, cval->str, sval->str);
129         }
130         *changes_p = changes > 0;
131         if (!changes)
132                 return true;
133
134         pktbuf_put_char(pkt, 0);
135         pktbuf_finish_packet(pkt);
136
137         slog_debug(server, "varcache_apply: %s", pkt->buf + sql_ofs);
138         return pktbuf_send_immidiate(pkt, server);
139 }
140
141 void varcache_fill_unset(VarCache *src, PgSocket *dst)
142 {
143         struct PStr *srcval, *dstval;
144         const struct var_lookup *lk;
145         for (lk = lookup; lk->name; lk++) {
146                 srcval = src->var_list[lk->idx];
147                 dstval = dst->vars.var_list[lk->idx];
148                 if (!dstval) {
149                         strpool_incref(srcval);
150                         dst->vars.var_list[lk->idx] = srcval;
151                 }
152         }
153 }
154
155 void varcache_clean(VarCache *cache)
156 {
157         int i;
158         for (i = 0; i < NumVars; i++) {
159                 strpool_decref(cache->var_list[i]);
160                 cache->var_list[i] = NULL;
161         }
162 }
163
164 void varcache_add_params(PktBuf *pkt, VarCache *vars)
165 {
166         struct PStr *val;
167         const struct var_lookup *lk;
168         for (lk = lookup; lk->name; lk++) {
169                 val = vars->var_list[lk->idx];
170                 if (val)
171                         pktbuf_write_ParameterStatus(pkt, lk->name, val->str);
172         }
173 }
174