forked from EnterpriseDB/mysql_fdw
-
Notifications
You must be signed in to change notification settings - Fork 0
/
connection.c
228 lines (199 loc) · 5.43 KB
/
connection.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
/*-------------------------------------------------------------------------
*
* connection.c
* Foreign-data wrapper for remote MySQL servers
*
* Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group
*
* Portions Copyright (c) 2004-2014, EnterpriseDB Corporation.
*
* IDENTIFICATION
* connection.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "mysql_fdw.h"
#include "access/xact.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "utils/hsearch.h"
#include "utils/memutils.h"
#include "utils/resowner.h"
/* Length of host */
#define HOST_LEN 256
/*
* Connection cache hash table entry
*
* The lookup key in this hash table is the foreign server OID plus the user
* mapping OID. (We use just one connection per user per foreign server,
* so that we can ensure all scans use the same snapshot during a query.)
*/
typedef struct ConnCacheKey
{
Oid serverid; /* OID of foreign server */
Oid userid; /* OID of local user whose mapping we use */
} ConnCacheKey;
typedef struct ConnCacheEntry
{
ConnCacheKey key; /* hash key (must be first) */
MYSQL *conn; /* connection to foreign server, or NULL */
} ConnCacheEntry;
/*
* Connection cache (initialized on first use)
*/
static HTAB *ConnectionHash = NULL;
/*
* mysql_get_connection:
* Get a connection which can be used to execute queries on
* the remote MySQL server with the user's authorization. A new connection
* is established if we don't already have a suitable one.
*/
MYSQL*
mysql_get_connection(ForeignServer *server, UserMapping *user, mysql_opt *opt)
{
bool found;
ConnCacheEntry *entry;
ConnCacheKey key;
/* First time through, initialize connection cache hashtable */
if (ConnectionHash == NULL)
{
HASHCTL ctl;
MemSet(&ctl, 0, sizeof(ctl));
ctl.keysize = sizeof(ConnCacheKey);
ctl.entrysize = sizeof(ConnCacheEntry);
ctl.hash = tag_hash;
/* allocate ConnectionHash in the cache context */
ctl.hcxt = CacheMemoryContext;
ConnectionHash = hash_create("mysql_fdw connections", 8,
&ctl,
HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
}
/* Create hash key for the entry. Assume no pad bytes in key struct */
key.serverid = server->serverid;
key.userid = user->userid;
/*
* Find or create cached entry for requested connection.
*/
entry = hash_search(ConnectionHash, &key, HASH_ENTER, &found);
if (!found)
{
/* initialize new hashtable entry (key is already filled in) */
entry->conn = NULL;
}
if (entry->conn == NULL)
{
entry->conn = mysql_connect(
opt->svr_address,
opt->svr_username,
opt->svr_password,
opt->svr_database,
opt->svr_port,
opt->svr_sa,
opt->svr_init_command,
opt->ssl_key,
opt->ssl_cert,
opt->ssl_ca,
opt->ssl_capath,
opt->ssl_cipher
);
elog(DEBUG3, "new mysql_fdw connection %p for server \"%s\"",
entry->conn, server->servername);
}
return entry->conn;
}
/*
* cleanup_connection:
* Delete all the cache entries on backend exists.
*/
void
mysql_cleanup_connection(void)
{
HASH_SEQ_STATUS scan;
ConnCacheEntry *entry;
if (ConnectionHash == NULL)
return;
hash_seq_init(&scan, ConnectionHash);
while ((entry = (ConnCacheEntry *) hash_seq_search(&scan)))
{
if (entry->conn == NULL)
continue;
elog(DEBUG3, "disconnecting mysql_fdw connection %p", entry->conn);
_mysql_close(entry->conn);
entry->conn = NULL;
}
}
/*
* Release connection created by calling GetConnection.
*/
void
mysql_rel_connection(MYSQL *conn)
{
HASH_SEQ_STATUS scan;
ConnCacheEntry *entry;
if (ConnectionHash == NULL)
return;
hash_seq_init(&scan, ConnectionHash);
while ((entry = (ConnCacheEntry *) hash_seq_search(&scan)))
{
if (entry->conn == NULL)
continue;
if (entry->conn == conn)
{
elog(DEBUG3, "disconnecting mysql_fdw connection %p", entry->conn);
_mysql_close(entry->conn);
entry->conn = NULL;
hash_seq_term(&scan);
break;
}
}
}
MYSQL*
mysql_connect(
char *svr_address,
char *svr_username,
char *svr_password,
char *svr_database,
int svr_port,
bool svr_sa,
char *svr_init_command,
char *ssl_key,
char *ssl_cert,
char *ssl_ca,
char *ssl_capath,
char *ssl_cipher)
{
MYSQL *conn = NULL;
my_bool secure_auth = svr_sa;
/* Connect to the server */
conn = _mysql_init(NULL);
if (!conn)
ereport(ERROR,
(errcode(ERRCODE_FDW_OUT_OF_MEMORY),
errmsg("failed to initialise the MySQL connection object")
));
_mysql_options(conn, MYSQL_SET_CHARSET_NAME, GetDatabaseEncodingName());
_mysql_options(conn, MYSQL_SECURE_AUTH, &secure_auth);
if (!svr_sa)
elog(WARNING, "MySQL secure authentication is off");
if (svr_init_command != NULL)
_mysql_options(conn, MYSQL_INIT_COMMAND, svr_init_command);
_mysql_ssl_set(conn, ssl_key, ssl_cert, ssl_ca, ssl_capath, ssl_cipher);
if (!_mysql_real_connect(conn, svr_address, svr_username, svr_password, svr_database, svr_port, NULL, 0))
ereport(ERROR,
(errcode(ERRCODE_FDW_UNABLE_TO_ESTABLISH_CONNECTION),
errmsg("failed to connect to MySQL: %s", _mysql_error(conn))
));
// useful for verifying that the connection's secured
elog(INFO,
"Successfully connected to MySQL database %s "
"at server %s with cipher %s "
"(server version: %s, protocol version: %d) ",
(svr_database != NULL) ? svr_database : "<none>",
_mysql_get_host_info (conn),
(ssl_cipher != NULL) ? ssl_cipher : "<none>",
_mysql_get_server_info (conn),
_mysql_get_proto_info (conn)
);
return conn;
}