Line data Source code
1 : /* FreeTDS - Library of routines accessing Sybase and Microsoft databases
2 : * Copyright (C) 1998-2004 Brian Bruns
3 : *
4 : * This library is free software; you can redistribute it and/or
5 : * modify it under the terms of the GNU Library General Public
6 : * License as published by the Free Software Foundation; either
7 : * version 2 of the License, or (at your option) any later version.
8 : *
9 : * This library is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 : * Library General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU Library General Public
15 : * License along with this library; if not, write to the
16 : * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 : * Boston, MA 02111-1307, USA.
18 : */
19 : #include <config.h>
20 :
21 : #include <assert.h>
22 :
23 : #if HAVE_STDLIB_H
24 : #include <stdlib.h>
25 : #endif /* HAVE_STDLIB_H */
26 :
27 : #include <freetds/tds.h>
28 : #include <freetds/server.h>
29 :
30 : static char *query;
31 : static size_t query_buflen = 0;
32 : static TDSHEADERS head;
33 :
34 : /**
35 : * Read a TDS5 tokenized query.
36 : */
37 : char *
38 0 : tds_get_query(TDSSOCKET * tds)
39 : {
40 : int len;
41 :
42 0 : if (query_buflen == 0) {
43 0 : query_buflen = 1024;
44 0 : query = tds_new(char, query_buflen);
45 : }
46 0 : tds_get_byte(tds); /* 33 */
47 0 : len = tds_get_int(tds); /* query size +1 */
48 0 : tds_get_byte(tds); /* has args, ignored TODO */
49 0 : assert(len >= 1); /* TODO handle correctly */
50 0 : if (len > query_buflen) {
51 0 : query_buflen = len;
52 0 : query = (char *) realloc(query, query_buflen);
53 : }
54 0 : --len;
55 0 : tds_get_n(tds, query, len);
56 0 : query[len] = 0;
57 0 : return query;
58 : }
59 :
60 : static bool
61 : tds_lastpacket(TDSSOCKET * tds)
62 : {
63 208 : if (!tds || !tds->in_buf || tds->recv_packet->capacity < 2)
64 : return true;
65 :
66 208 : return tds->in_buf[1] != 0;
67 : }
68 :
69 : /**
70 : * get query packet of a given type
71 : * \tds
72 : * \param head extra information to put in a TDS7 header
73 : */
74 : static TDSRET
75 208 : tds_get_query_head(TDSSOCKET * tds, TDSHEADERS * head)
76 : {
77 208 : int qn_len = 0, more;
78 208 : char *qn_msgtext = NULL;
79 208 : char *qn_options = NULL;
80 208 : size_t qn_msgtext_len = 0;
81 208 : size_t qn_options_len = 0;
82 :
83 208 : if (!IS_TDS72_PLUS(tds->conn))
84 : return TDS_SUCCESS;
85 :
86 208 : free((void *) head->qn_options);
87 208 : head->qn_options = NULL;
88 208 : free((void *) head->qn_msgtext);
89 208 : head->qn_msgtext = NULL;
90 :
91 208 : qn_len = tds_get_int(tds) - 4 - 18; /* total length */
92 208 : tds_get_int(tds); /* length: transaction descriptor, ignored */
93 208 : tds_get_smallint(tds); /* type: transaction descriptor, ignored */
94 208 : tds_get_n(tds, tds->conn->tds72_transaction, 8); /* transaction */
95 208 : tds_get_int(tds); /* request count, ignored */
96 208 : if (qn_len != 0) {
97 0 : qn_len = tds_get_int(tds); /* length: query notification */
98 0 : tds_get_smallint(tds); /* type: query notification, ignored */
99 0 : qn_msgtext_len = tds_get_smallint(tds); /* notifyid */
100 0 : if (qn_msgtext_len > 0) {
101 0 : qn_msgtext = (char *) realloc(qn_msgtext, qn_msgtext_len);
102 0 : tds_get_n(tds, qn_msgtext, qn_msgtext_len);
103 : }
104 :
105 0 : qn_options_len = tds_get_smallint(tds); /* ssbdeployment */
106 0 : if (qn_options_len > 0) {
107 0 : qn_options = (char *) realloc(qn_options, qn_options_len);
108 0 : tds_get_n(tds, qn_options, qn_options_len);
109 : }
110 0 : more = tds->in_len - tds->in_pos;
111 0 : if (more)
112 0 : head->qn_timeout = tds_get_int(tds); /* timeout */
113 :
114 0 : head->qn_options = qn_options;
115 0 : head->qn_msgtext = qn_msgtext;
116 : }
117 : return TDS_SUCCESS;
118 : }
119 :
120 : /**
121 : * Read a query, and return it as an ASCII string with a \0 terminator. This
122 : * should work for TDS4, TDS5, and TDS7+ connections. Also, it converts RPC
123 : * calls into stored procedure queries, and it skips CANCEL packets. The query
124 : * string is returned in a static buffer which is overwritten each time this
125 : * function is called.
126 : * \param tds The socket to read from.
127 : * \return A query string if successful, or NULL if we either can't read from
128 : * the socket or we read something that we can't handle.
129 : */
130 : char *
131 224 : tds_get_generic_query(TDSSOCKET * tds)
132 : {
133 : int token, byte;
134 : int len, more, i, j;
135 :
136 : for (;;) {
137 : /*
138 : * Read a new packet. We must explicitly read it,
139 : * instead of letting functions such as tds_get_byte()
140 : * to read it for us, so that we can examine the packet
141 : * type via tds->in_flag.
142 : */
143 224 : if (tds_read_packet(tds) < 0)
144 : return NULL;
145 :
146 : /* Queries can arrive in a couple different formats. */
147 208 : switch (tds->in_flag) {
148 : case TDS_RPC:
149 : /* TODO */
150 : return NULL;
151 0 : case TDS_NORMAL: /* TDS5 query packet */
152 : /* get the token */
153 0 : token = tds_get_byte(tds);
154 0 : switch (token) {
155 0 : case TDS_LANGUAGE_TOKEN:
156 : /* SQL query */
157 0 : len = tds_get_int(tds); /* query size +1 */
158 0 : assert(len >= 1); /* TODO handle */
159 0 : tds_get_byte(tds); /* has args, ignored TODO */
160 0 : if (len > query_buflen) {
161 0 : query_buflen = len;
162 0 : query = (char *) realloc(query, query_buflen);
163 : }
164 0 : --len;
165 0 : tds_get_n(tds, query, len);
166 0 : query[len] = 0;
167 0 : return query;
168 :
169 0 : case TDS_DBRPC_TOKEN:
170 : /* RPC call -- make it look like a query */
171 :
172 : /* skip the overall length */
173 0 : (void)tds_get_smallint(tds);
174 :
175 : /* get the length of the stored procedure's name */
176 0 : len = tds_get_byte(tds) + 1;/* sproc name size +1 */
177 0 : if (len > query_buflen) {
178 0 : query_buflen = len;
179 0 : query = (char *) realloc(query, query_buflen);
180 : }
181 :
182 : /*
183 : * Read the chars of the name. Skip NUL
184 : * bytes, as a cheap way to convert
185 : * Unicode to ASCII. (For TDS7+, the
186 : * name is sent in Unicode.)
187 : */
188 0 : for (i = j = 0; i < len - 1; i++) {
189 0 : byte = tds_get_byte(tds);
190 0 : if (byte != '\0')
191 0 : query[j++] = byte;
192 : }
193 0 : query[j] = '\0';
194 :
195 : /* TODO: WE DON'T HANDLE PARAMETERS YET! */
196 :
197 : /* eat the rest of the packet */
198 0 : while (!tds_lastpacket(tds) && tds_read_packet(tds) > 0) {
199 : }
200 0 : return query;
201 :
202 : default:
203 : /* unexpected token */
204 :
205 : /* eat the rest of the packet */
206 0 : while (!tds_lastpacket(tds) && tds_read_packet(tds) > 0) {
207 : }
208 : return NULL;
209 : }
210 : break;
211 :
212 208 : case TDS_QUERY:
213 : /* TDS7+ adds a query head */
214 208 : if (IS_TDS72_PLUS(tds->conn) && tds_get_query_head(tds, &head) != TDS_SUCCESS)
215 : return NULL;
216 :
217 : /* TDS4 and TDS7+ fill the whole packet with a query */
218 : len = 0;
219 : for (;;) {
220 : const char *src;
221 :
222 : /* If buffer needs to grow, then grow */
223 208 : more = tds->in_len - tds->in_pos;
224 208 : src = (char *) (tds->in_buf + tds->in_pos);
225 208 : if ((size_t)(len + more + 1) > query_buflen) {
226 16 : query_buflen = len + more + 1024u;
227 16 : query_buflen -= query_buflen % 1024u;
228 16 : query = (char *) realloc(query, query_buflen);
229 : }
230 :
231 : /*
232 : * Pull new data into the query buffer.
233 : * Ignore NUL bytes -- this is a cheap way
234 : * to convert Unicode to Latin-1/ASCII.
235 : */
236 16848 : while (--more >= 0) {
237 16640 : query[len] = *src++;
238 16640 : if (query[len] != '\0')
239 8320 : len++;
240 : }
241 :
242 : /* if more then read it */
243 208 : if (tds_lastpacket(tds))
244 : break;
245 0 : if (tds_read_packet(tds) < 0)
246 : return NULL;
247 : }
248 :
249 : /* add a NUL to mark the end */
250 208 : query[len] = '\0';
251 208 : return query;
252 :
253 : case TDS_CANCEL:
254 : /*
255 : * ignore cancel requests -- if we're waiting
256 : * for the next query then it's obviously too
257 : * late to cancel the previous query.
258 : */
259 : /* TODO it's not too late -- freddy77 */
260 : return NULL;
261 :
262 : default:
263 : /* not a query packet */
264 : return NULL;
265 : }
266 : }
267 : }
268 :
269 : /**
270 : * Free query buffer returned by tds_get_generic_query.
271 : */
272 : void
273 16 : tds_free_query(void)
274 : {
275 16 : TDS_ZERO_FREE(query);
276 16 : query_buflen = 0;
277 :
278 16 : free((void *) head.qn_options);
279 16 : head.qn_options = NULL;
280 16 : free((void *) head.qn_msgtext);
281 16 : head.qn_msgtext = NULL;
282 16 : }
|