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
get_value(VarCache * cache,const struct var_lookup * lk)43 static inline struct PStr *get_value(VarCache *cache, const struct var_lookup *lk)
44 {
45 return cache->var_list[lk->idx];
46 }
47
varcache_set(VarCache * cache,const char * key,const char * value)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 /* NULL value? */
71 if (!value)
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
apply_var(PktBuf * pkt,const char * key,const struct PStr * cval,const struct PStr * sval)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
varcache_apply(PgSocket * server,PgSocket * client,bool * changes_p)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 query 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_immediate(pkt, server);
144 }
145
varcache_fill_unset(VarCache * src,PgSocket * dst)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
varcache_clean(VarCache * cache)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
varcache_add_params(PktBuf * pkt,VarCache * vars)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
varcache_deinit(void)180 void varcache_deinit(void)
181 {
182 strpool_free(vpool);
183 vpool = NULL;
184 }
185