/src/MapServer/src/mapjoin.c
Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | * $Id$ |
3 | | * |
4 | | * Project: MapServer |
5 | | * Purpose: Implements MapServer joins. |
6 | | * Author: Steve Lime and the MapServer team. |
7 | | * |
8 | | ****************************************************************************** |
9 | | * Copyright (c) 1996-2005 Regents of the University of Minnesota. |
10 | | * |
11 | | * Permission is hereby granted, free of charge, to any person obtaining a |
12 | | * copy of this software and associated documentation files (the "Software"), |
13 | | * to deal in the Software without restriction, including without limitation |
14 | | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
15 | | * and/or sell copies of the Software, and to permit persons to whom the |
16 | | * Software is furnished to do so, subject to the following conditions: |
17 | | * |
18 | | * The above copyright notice and this permission notice shall be included in |
19 | | * all copies of this Software or works derived from this Software. |
20 | | * |
21 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
22 | | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
23 | | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
24 | | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
25 | | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
26 | | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
27 | | * DEALINGS IN THE SOFTWARE. |
28 | | ****************************************************************************/ |
29 | | |
30 | | #include "mapserver.h" |
31 | | |
32 | | #define ROW_ALLOCATION_SIZE 10 |
33 | | |
34 | | /* DBF/XBase function prototypes */ |
35 | | int msDBFJoinConnect(layerObj *layer, joinObj *join); |
36 | | int msDBFJoinPrepare(joinObj *join, shapeObj *shape); |
37 | | int msDBFJoinNext(joinObj *join); |
38 | | int msDBFJoinClose(joinObj *join); |
39 | | int msDBFJoinTable(layerObj *layer, joinObj *join, shapeObj *shape); |
40 | | |
41 | | /* CSV (comma delimited text file) function prototypes */ |
42 | | int msCSVJoinConnect(layerObj *layer, joinObj *join); |
43 | | int msCSVJoinPrepare(joinObj *join, shapeObj *shape); |
44 | | int msCSVJoinNext(joinObj *join); |
45 | | int msCSVJoinClose(joinObj *join); |
46 | | int msCSVJoinTable(layerObj *layer, joinObj *join, shapeObj *shape); |
47 | | |
48 | | /* MySQL function prototypes */ |
49 | | int msMySQLJoinConnect(layerObj *layer, joinObj *join); |
50 | | int msMySQLJoinPrepare(joinObj *join, shapeObj *shape); |
51 | | int msMySQLJoinNext(joinObj *join); |
52 | | int msMySQLJoinClose(joinObj *join); |
53 | | int msMySQLJoinTable(layerObj *layer, joinObj *join, shapeObj *shape); |
54 | | |
55 | | /* PostgreSQL function prototypes */ |
56 | | int msPOSTGRESQLJoinConnect(layerObj *layer, joinObj *join); |
57 | | int msPOSTGRESQLJoinPrepare(joinObj *join, shapeObj *shape); |
58 | | int msPOSTGRESQLJoinNext(joinObj *join); |
59 | | int msPOSTGRESQLJoinClose(joinObj *join); |
60 | | |
61 | | /* wrapper function for DB specific join functions */ |
62 | 0 | int msJoinConnect(layerObj *layer, joinObj *join) { |
63 | 0 | switch (join->connectiontype) { |
64 | 0 | case (MS_DB_XBASE): |
65 | 0 | return msDBFJoinConnect(layer, join); |
66 | 0 | break; |
67 | 0 | case (MS_DB_CSV): |
68 | 0 | return msCSVJoinConnect(layer, join); |
69 | 0 | break; |
70 | 0 | case (MS_DB_MYSQL): |
71 | 0 | return msMySQLJoinConnect(layer, join); |
72 | 0 | break; |
73 | 0 | case (MS_DB_POSTGRES): |
74 | 0 | return msPOSTGRESQLJoinConnect(layer, join); |
75 | 0 | break; |
76 | 0 | default: |
77 | 0 | break; |
78 | 0 | } |
79 | | |
80 | 0 | msSetError(MS_JOINERR, "Unsupported join connection type.", |
81 | 0 | "msJoinConnect()"); |
82 | 0 | return MS_FAILURE; |
83 | 0 | } |
84 | | |
85 | 0 | int msJoinPrepare(joinObj *join, shapeObj *shape) { |
86 | 0 | switch (join->connectiontype) { |
87 | 0 | case (MS_DB_XBASE): |
88 | 0 | return msDBFJoinPrepare(join, shape); |
89 | 0 | break; |
90 | 0 | case (MS_DB_CSV): |
91 | 0 | return msCSVJoinPrepare(join, shape); |
92 | 0 | break; |
93 | 0 | case (MS_DB_MYSQL): |
94 | 0 | return msMySQLJoinPrepare(join, shape); |
95 | 0 | break; |
96 | 0 | case (MS_DB_POSTGRES): |
97 | 0 | return msPOSTGRESQLJoinPrepare(join, shape); |
98 | 0 | break; |
99 | 0 | default: |
100 | 0 | break; |
101 | 0 | } |
102 | | |
103 | 0 | msSetError(MS_JOINERR, "Unsupported join connection type.", |
104 | 0 | "msJoinPrepare()"); |
105 | 0 | return MS_FAILURE; |
106 | 0 | } |
107 | | |
108 | 0 | int msJoinNext(joinObj *join) { |
109 | 0 | switch (join->connectiontype) { |
110 | 0 | case (MS_DB_XBASE): |
111 | 0 | return msDBFJoinNext(join); |
112 | 0 | break; |
113 | 0 | case (MS_DB_CSV): |
114 | 0 | return msCSVJoinNext(join); |
115 | 0 | break; |
116 | 0 | case (MS_DB_MYSQL): |
117 | 0 | return msMySQLJoinNext(join); |
118 | 0 | break; |
119 | 0 | case (MS_DB_POSTGRES): |
120 | 0 | return msPOSTGRESQLJoinNext(join); |
121 | 0 | break; |
122 | 0 | default: |
123 | 0 | break; |
124 | 0 | } |
125 | | |
126 | 0 | msSetError(MS_JOINERR, "Unsupported join connection type.", "msJoinNext()"); |
127 | 0 | return MS_FAILURE; |
128 | 0 | } |
129 | | |
130 | 148 | int msJoinClose(joinObj *join) { |
131 | 148 | switch (join->connectiontype) { |
132 | 148 | case (MS_DB_XBASE): |
133 | 148 | return msDBFJoinClose(join); |
134 | 0 | break; |
135 | 0 | case (MS_DB_CSV): |
136 | 0 | return msCSVJoinClose(join); |
137 | 0 | break; |
138 | 0 | case (MS_DB_MYSQL): |
139 | 0 | return msMySQLJoinClose(join); |
140 | 0 | break; |
141 | 0 | case (MS_DB_POSTGRES): |
142 | 0 | return msPOSTGRESQLJoinClose(join); |
143 | 0 | break; |
144 | 0 | default: |
145 | 0 | break; |
146 | 148 | } |
147 | | |
148 | 0 | msSetError(MS_JOINERR, "Unsupported join connection type.", "msJoinClose()"); |
149 | 0 | return MS_FAILURE; |
150 | 148 | } |
151 | | |
152 | | /* */ |
153 | | /* XBASE join functions */ |
154 | | /* */ |
155 | | typedef struct { |
156 | | DBFHandle hDBF; |
157 | | int fromindex, toindex; |
158 | | char *target; |
159 | | int nextrecord; |
160 | | } msDBFJoinInfo; |
161 | | |
162 | 0 | int msDBFJoinConnect(layerObj *layer, joinObj *join) { |
163 | 0 | int i; |
164 | 0 | char szPath[MS_MAXPATHLEN]; |
165 | 0 | msDBFJoinInfo *joininfo; |
166 | |
|
167 | 0 | if (join->joininfo) |
168 | 0 | return (MS_SUCCESS); /* already open */ |
169 | | |
170 | 0 | if (msCheckParentPointer(layer->map, "map") == MS_FAILURE) |
171 | 0 | return MS_FAILURE; |
172 | | |
173 | | /* allocate a msDBFJoinInfo struct */ |
174 | 0 | joininfo = (msDBFJoinInfo *)malloc(sizeof(msDBFJoinInfo)); |
175 | 0 | if (!joininfo) { |
176 | 0 | msSetError(MS_MEMERR, "Error allocating XBase table info structure.", |
177 | 0 | "msDBFJoinConnect()"); |
178 | 0 | return (MS_FAILURE); |
179 | 0 | } |
180 | | |
181 | | /* initialize any members that won't get set later on in this function */ |
182 | 0 | joininfo->target = NULL; |
183 | 0 | joininfo->nextrecord = 0; |
184 | |
|
185 | 0 | join->joininfo = joininfo; |
186 | | |
187 | | /* open the XBase file */ |
188 | 0 | if ((joininfo->hDBF = |
189 | 0 | msDBFOpen(msBuildPath3(szPath, layer->map->mappath, |
190 | 0 | layer->map->shapepath, join->table), |
191 | 0 | "rb")) == NULL) { |
192 | 0 | if ((joininfo->hDBF = |
193 | 0 | msDBFOpen(msBuildPath(szPath, layer->map->mappath, join->table), |
194 | 0 | "rb")) == NULL) { |
195 | 0 | msSetError(MS_IOERR, "(%s)", "msDBFJoinConnect()", join->table); |
196 | 0 | return (MS_FAILURE); |
197 | 0 | } |
198 | 0 | } |
199 | | |
200 | | /* get "to" item index */ |
201 | 0 | if ((joininfo->toindex = msDBFGetItemIndex(joininfo->hDBF, join->to)) == -1) { |
202 | 0 | msSetError(MS_DBFERR, "Item %s not found in table %s.", |
203 | 0 | "msDBFJoinConnect()", join->to, join->table); |
204 | 0 | return (MS_FAILURE); |
205 | 0 | } |
206 | | |
207 | | /* get "from" item index */ |
208 | 0 | for (i = 0; i < layer->numitems; i++) { |
209 | 0 | if (strcasecmp(layer->items[i], join->from) == 0) { /* found it */ |
210 | 0 | joininfo->fromindex = i; |
211 | 0 | break; |
212 | 0 | } |
213 | 0 | } |
214 | |
|
215 | 0 | if (i == layer->numitems) { |
216 | 0 | msSetError(MS_JOINERR, "Item %s not found in layer %s.", |
217 | 0 | "msDBFJoinConnect()", join->from, layer->name); |
218 | 0 | return (MS_FAILURE); |
219 | 0 | } |
220 | | |
221 | | /* finally store away the item names in the XBase table */ |
222 | 0 | join->numitems = msDBFGetFieldCount(joininfo->hDBF); |
223 | 0 | join->items = msDBFGetItems(joininfo->hDBF); |
224 | 0 | if (!join->items) |
225 | 0 | return (MS_FAILURE); |
226 | | |
227 | 0 | return (MS_SUCCESS); |
228 | 0 | } |
229 | | |
230 | 0 | int msDBFJoinPrepare(joinObj *join, shapeObj *shape) { |
231 | 0 | msDBFJoinInfo *joininfo = join->joininfo; |
232 | |
|
233 | 0 | if (!joininfo) { |
234 | 0 | msSetError(MS_JOINERR, "Join connection has not be created.", |
235 | 0 | "msDBFJoinPrepare()"); |
236 | 0 | return (MS_FAILURE); |
237 | 0 | } |
238 | | |
239 | 0 | if (!shape) { |
240 | 0 | msSetError(MS_JOINERR, "Shape to be joined is empty.", |
241 | 0 | "msDBFJoinPrepare()"); |
242 | 0 | return (MS_FAILURE); |
243 | 0 | } |
244 | | |
245 | 0 | if (!shape->values) { |
246 | 0 | msSetError(MS_JOINERR, "Shape to be joined has no attributes.", |
247 | 0 | "msDBFJoinPrepare()"); |
248 | 0 | return (MS_FAILURE); |
249 | 0 | } |
250 | | |
251 | 0 | joininfo->nextrecord = 0; /* starting with the first record */ |
252 | |
|
253 | 0 | if (joininfo->target) |
254 | 0 | free(joininfo->target); /* clear last target */ |
255 | 0 | joininfo->target = msStrdup(shape->values[joininfo->fromindex]); |
256 | |
|
257 | 0 | return (MS_SUCCESS); |
258 | 0 | } |
259 | | |
260 | 0 | int msDBFJoinNext(joinObj *join) { |
261 | 0 | int i, n; |
262 | 0 | msDBFJoinInfo *joininfo = join->joininfo; |
263 | |
|
264 | 0 | if (!joininfo) { |
265 | 0 | msSetError(MS_JOINERR, "Join connection has not be created.", |
266 | 0 | "msDBFJoinNext()"); |
267 | 0 | return (MS_FAILURE); |
268 | 0 | } |
269 | | |
270 | 0 | if (!joininfo->target) { |
271 | 0 | msSetError(MS_JOINERR, "No target specified, run msDBFJoinPrepare() first.", |
272 | 0 | "msDBFJoinNext()"); |
273 | 0 | return (MS_FAILURE); |
274 | 0 | } |
275 | | |
276 | | /* clear any old data */ |
277 | 0 | if (join->values) { |
278 | 0 | msFreeCharArray(join->values, join->numitems); |
279 | 0 | join->values = NULL; |
280 | 0 | } |
281 | |
|
282 | 0 | n = msDBFGetRecordCount(joininfo->hDBF); |
283 | |
|
284 | 0 | for (i = joininfo->nextrecord; i < n; i++) { /* find a match */ |
285 | 0 | if (strcmp(joininfo->target, msDBFReadStringAttribute(joininfo->hDBF, i, |
286 | 0 | joininfo->toindex)) == |
287 | 0 | 0) |
288 | 0 | break; |
289 | 0 | } |
290 | |
|
291 | 0 | if (i == n) { /* unable to do the join */ |
292 | 0 | if ((join->values = (char **)malloc(sizeof(char *) * join->numitems)) == |
293 | 0 | NULL) { |
294 | 0 | msSetError(MS_MEMERR, NULL, "msDBFJoinNext()"); |
295 | 0 | return (MS_FAILURE); |
296 | 0 | } |
297 | 0 | for (i = 0; i < join->numitems; i++) |
298 | 0 | join->values[i] = msStrdup("\0"); /* initialize to zero length strings */ |
299 | |
|
300 | 0 | joininfo->nextrecord = n; |
301 | 0 | return (MS_DONE); |
302 | 0 | } |
303 | | |
304 | 0 | if ((join->values = msDBFGetValues(joininfo->hDBF, i)) == NULL) |
305 | 0 | return (MS_FAILURE); |
306 | | |
307 | 0 | joininfo->nextrecord = |
308 | 0 | i + 1; /* so we know where to start looking next time through */ |
309 | |
|
310 | 0 | return (MS_SUCCESS); |
311 | 0 | } |
312 | | |
313 | 148 | int msDBFJoinClose(joinObj *join) { |
314 | 148 | msDBFJoinInfo *joininfo = join->joininfo; |
315 | | |
316 | 148 | if (!joininfo) |
317 | 148 | return (MS_SUCCESS); /* already closed */ |
318 | | |
319 | 0 | if (joininfo->hDBF) |
320 | 0 | msDBFClose(joininfo->hDBF); |
321 | 0 | if (joininfo->target) |
322 | 0 | free(joininfo->target); |
323 | 0 | free(joininfo); |
324 | 0 | joininfo = NULL; |
325 | |
|
326 | 0 | return (MS_SUCCESS); |
327 | 148 | } |
328 | | |
329 | | /* */ |
330 | | /* CSV (comma separated value) join functions */ |
331 | | /* */ |
332 | | typedef struct { |
333 | | int fromindex, toindex; |
334 | | char *target; |
335 | | char ***rows; |
336 | | int numrows; |
337 | | int nextrow; |
338 | | } msCSVJoinInfo; |
339 | | |
340 | 0 | int msCSVJoinConnect(layerObj *layer, joinObj *join) { |
341 | 0 | int i; |
342 | 0 | FILE *stream; |
343 | 0 | char szPath[MS_MAXPATHLEN]; |
344 | 0 | msCSVJoinInfo *joininfo; |
345 | 0 | char buffer[MS_BUFFER_LENGTH]; |
346 | |
|
347 | 0 | if (join->joininfo) |
348 | 0 | return (MS_SUCCESS); /* already open */ |
349 | 0 | if (msCheckParentPointer(layer->map, "map") == MS_FAILURE) |
350 | 0 | return MS_FAILURE; |
351 | | |
352 | | /* allocate a msCSVJoinInfo struct */ |
353 | 0 | if ((joininfo = (msCSVJoinInfo *)malloc(sizeof(msCSVJoinInfo))) == NULL) { |
354 | 0 | msSetError(MS_MEMERR, "Error allocating CSV table info structure.", |
355 | 0 | "msCSVJoinConnect()"); |
356 | 0 | return (MS_FAILURE); |
357 | 0 | } |
358 | | |
359 | | /* initialize any members that won't get set later on in this function */ |
360 | 0 | joininfo->target = NULL; |
361 | 0 | joininfo->nextrow = 0; |
362 | |
|
363 | 0 | join->joininfo = joininfo; |
364 | | |
365 | | /* open the CSV file */ |
366 | 0 | if ((stream = fopen(msBuildPath3(szPath, layer->map->mappath, |
367 | 0 | layer->map->shapepath, join->table), |
368 | 0 | "r")) == NULL) { |
369 | 0 | if ((stream = fopen(msBuildPath(szPath, layer->map->mappath, join->table), |
370 | 0 | "r")) == NULL) { |
371 | 0 | msSetError(MS_IOERR, "(%s)", "msCSVJoinConnect()", join->table); |
372 | 0 | return (MS_FAILURE); |
373 | 0 | } |
374 | 0 | } |
375 | | |
376 | | /* once through to get the number of rows */ |
377 | 0 | joininfo->numrows = 0; |
378 | 0 | while (fgets(buffer, MS_BUFFER_LENGTH, stream) != NULL) |
379 | 0 | joininfo->numrows++; |
380 | 0 | rewind(stream); |
381 | |
|
382 | 0 | if ((joininfo->rows = |
383 | 0 | (char ***)malloc(joininfo->numrows * sizeof(char **))) == NULL) { |
384 | 0 | fclose(stream); |
385 | 0 | msSetError(MS_MEMERR, "Error allocating rows.", "msCSVJoinConnect()"); |
386 | 0 | return (MS_FAILURE); |
387 | 0 | } |
388 | | |
389 | | /* load the rows */ |
390 | 0 | i = 0; |
391 | 0 | while (fgets(buffer, MS_BUFFER_LENGTH, stream) != NULL) { |
392 | 0 | msStringTrimEOL(buffer); |
393 | 0 | joininfo->rows[i] = msStringSplitComplex(buffer, ",", &(join->numitems), |
394 | 0 | MS_ALLOWEMPTYTOKENS); |
395 | 0 | i++; |
396 | 0 | } |
397 | 0 | fclose(stream); |
398 | | |
399 | | /* get "from" item index */ |
400 | 0 | for (i = 0; i < layer->numitems; i++) { |
401 | 0 | if (strcasecmp(layer->items[i], join->from) == 0) { /* found it */ |
402 | 0 | joininfo->fromindex = i; |
403 | 0 | break; |
404 | 0 | } |
405 | 0 | } |
406 | |
|
407 | 0 | if (i == layer->numitems) { |
408 | 0 | msSetError(MS_JOINERR, "Item %s not found in layer %s.", |
409 | 0 | "msCSVJoinConnect()", join->from, layer->name); |
410 | 0 | return (MS_FAILURE); |
411 | 0 | } |
412 | | |
413 | | /* get "to" index (for now the user tells us which column, 1..n) */ |
414 | 0 | joininfo->toindex = atoi(join->to) - 1; |
415 | 0 | if (joininfo->toindex < 0 || joininfo->toindex > join->numitems) { |
416 | 0 | msSetError(MS_JOINERR, "Invalid column index %s.", "msCSVJoinConnect()", |
417 | 0 | join->to); |
418 | 0 | return (MS_FAILURE); |
419 | 0 | } |
420 | | |
421 | | /* store away the column names (1..n) */ |
422 | 0 | if ((join->items = (char **)malloc(sizeof(char *) * join->numitems)) == |
423 | 0 | NULL) { |
424 | 0 | msSetError(MS_MEMERR, "Error allocating space for join item names.", |
425 | 0 | "msCSVJoinConnect()"); |
426 | 0 | return (MS_FAILURE); |
427 | 0 | } |
428 | 0 | for (i = 0; i < join->numitems; i++) { |
429 | 0 | join->items[i] = (char *)malloc(12); /* plenty of space */ |
430 | 0 | sprintf(join->items[i], "%d", i + 1); |
431 | 0 | } |
432 | |
|
433 | 0 | return (MS_SUCCESS); |
434 | 0 | } |
435 | | |
436 | 0 | int msCSVJoinPrepare(joinObj *join, shapeObj *shape) { |
437 | 0 | msCSVJoinInfo *joininfo = join->joininfo; |
438 | |
|
439 | 0 | if (!joininfo) { |
440 | 0 | msSetError(MS_JOINERR, "Join connection has not be created.", |
441 | 0 | "msCSVJoinPrepare()"); |
442 | 0 | return (MS_FAILURE); |
443 | 0 | } |
444 | | |
445 | 0 | if (!shape) { |
446 | 0 | msSetError(MS_JOINERR, "Shape to be joined is empty.", |
447 | 0 | "msCSVJoinPrepare()"); |
448 | 0 | return (MS_FAILURE); |
449 | 0 | } |
450 | | |
451 | 0 | if (!shape->values) { |
452 | 0 | msSetError(MS_JOINERR, "Shape to be joined has no attributes.", |
453 | 0 | "msCSVJoinPrepare()"); |
454 | 0 | return (MS_FAILURE); |
455 | 0 | } |
456 | | |
457 | 0 | joininfo->nextrow = 0; /* starting with the first record */ |
458 | |
|
459 | 0 | if (joininfo->target) |
460 | 0 | free(joininfo->target); /* clear last target */ |
461 | 0 | joininfo->target = msStrdup(shape->values[joininfo->fromindex]); |
462 | |
|
463 | 0 | return (MS_SUCCESS); |
464 | 0 | } |
465 | | |
466 | 0 | int msCSVJoinNext(joinObj *join) { |
467 | 0 | int i, j; |
468 | 0 | msCSVJoinInfo *joininfo = join->joininfo; |
469 | |
|
470 | 0 | if (!joininfo) { |
471 | 0 | msSetError(MS_JOINERR, "Join connection has not be created.", |
472 | 0 | "msCSVJoinNext()"); |
473 | 0 | return (MS_FAILURE); |
474 | 0 | } |
475 | | |
476 | | /* clear any old data */ |
477 | 0 | if (join->values) { |
478 | 0 | msFreeCharArray(join->values, join->numitems); |
479 | 0 | join->values = NULL; |
480 | 0 | } |
481 | |
|
482 | 0 | for (i = joininfo->nextrow; i < joininfo->numrows; i++) { /* find a match */ |
483 | 0 | if (strcmp(joininfo->target, joininfo->rows[i][joininfo->toindex]) == 0) |
484 | 0 | break; |
485 | 0 | } |
486 | |
|
487 | 0 | if ((join->values = (char **)malloc(sizeof(char *) * join->numitems)) == |
488 | 0 | NULL) { |
489 | 0 | msSetError(MS_MEMERR, NULL, "msCSVJoinNext()"); |
490 | 0 | return (MS_FAILURE); |
491 | 0 | } |
492 | | |
493 | 0 | if (i == joininfo->numrows) { /* unable to do the join */ |
494 | 0 | for (j = 0; j < join->numitems; j++) |
495 | 0 | join->values[j] = msStrdup("\0"); /* initialize to zero length strings */ |
496 | |
|
497 | 0 | joininfo->nextrow = joininfo->numrows; |
498 | 0 | return (MS_DONE); |
499 | 0 | } |
500 | | |
501 | 0 | for (j = 0; j < join->numitems; j++) |
502 | 0 | join->values[j] = msStrdup(joininfo->rows[i][j]); |
503 | |
|
504 | 0 | joininfo->nextrow = |
505 | 0 | i + 1; /* so we know where to start looking next time through */ |
506 | |
|
507 | 0 | return (MS_SUCCESS); |
508 | 0 | } |
509 | | |
510 | 0 | int msCSVJoinClose(joinObj *join) { |
511 | 0 | int i; |
512 | 0 | msCSVJoinInfo *joininfo = join->joininfo; |
513 | |
|
514 | 0 | if (!joininfo) |
515 | 0 | return (MS_SUCCESS); /* already closed */ |
516 | | |
517 | 0 | for (i = 0; i < joininfo->numrows; i++) |
518 | 0 | msFreeCharArray(joininfo->rows[i], join->numitems); |
519 | 0 | free(joininfo->rows); |
520 | 0 | if (joininfo->target) |
521 | 0 | free(joininfo->target); |
522 | 0 | free(joininfo); |
523 | 0 | joininfo = NULL; |
524 | |
|
525 | 0 | return (MS_SUCCESS); |
526 | 0 | } |
527 | | |
528 | | #ifdef USE_MYSQL |
529 | | |
530 | | #ifndef _mysql_h |
531 | | #include <mysql/mysql.h> |
532 | | #endif |
533 | | |
534 | | char *DB_HOST = NULL; |
535 | | char *DB_USER = NULL; |
536 | | char *DB_PASSWD = NULL; |
537 | | char *DB_DATABASE = NULL; |
538 | | char *delim; |
539 | | |
540 | | #define MYDEBUG if (0) |
541 | | |
542 | | MYSQL_RES *msMySQLQuery(char *q, MYSQL *conn) { |
543 | | MYSQL_RES *qresult = NULL; |
544 | | if (mysql_query(conn, q) < 0) { |
545 | | mysql_close(conn); |
546 | | msSetError(MS_QUERYERR, "Bad mysql query (%s)", "msMySQLQuery()", q); |
547 | | return qresult; |
548 | | } |
549 | | if (!(qresult = mysql_store_result(conn))) { |
550 | | mysql_close(conn); |
551 | | msSetError(MS_QUERYERR, "mysql query failed (%s)", "msMySQLQuery()", q); |
552 | | return qresult; |
553 | | } |
554 | | return qresult; |
555 | | } |
556 | | |
557 | | /* */ |
558 | | /* mysql join functions */ |
559 | | /* */ |
560 | | typedef struct { |
561 | | MYSQL mysql, *conn; |
562 | | MYSQL_RES *qresult; |
563 | | MYSQL_ROW row; |
564 | | int rows; |
565 | | int fromindex; |
566 | | char *tocolumn; |
567 | | char *target; |
568 | | int nextrecord; |
569 | | } msMySQLJoinInfo; |
570 | | #endif |
571 | | |
572 | 0 | int msMySQLJoinConnect(layerObj *layer, joinObj *join) { |
573 | 0 | (void)layer; |
574 | 0 | (void)join; |
575 | 0 | #ifndef USE_MYSQL |
576 | 0 | msSetError(MS_QUERYERR, |
577 | 0 | "MySQL support not available (compile with --with-mysql)", |
578 | 0 | "msMySQLJoinConnect()"); |
579 | 0 | return (MS_FAILURE); |
580 | | #else |
581 | | int i; |
582 | | char qbuf[4000]; |
583 | | char *conn_decrypted; |
584 | | msMySQLJoinInfo *joininfo; |
585 | | |
586 | | MYDEBUG if (setvbuf(stdout, NULL, _IONBF, 0)) { printf("Whoops..."); }; |
587 | | if (join->joininfo) |
588 | | return (MS_SUCCESS); /* already open */ |
589 | | |
590 | | /* allocate a msMySQLJoinInfo struct */ |
591 | | joininfo = (msMySQLJoinInfo *)malloc(sizeof(msMySQLJoinInfo)); |
592 | | if (!joininfo) { |
593 | | msSetError(MS_MEMERR, "Error allocating mysql table info structure.", |
594 | | "msMySQLJoinConnect()"); |
595 | | return (MS_FAILURE); |
596 | | } |
597 | | |
598 | | /* initialize any members that won't get set later on in this function */ |
599 | | joininfo->qresult = NULL; |
600 | | joininfo->target = NULL; |
601 | | joininfo->nextrecord = 0; |
602 | | |
603 | | join->joininfo = joininfo; |
604 | | |
605 | | /* open the mysql connection */ |
606 | | |
607 | | if (join->connection == NULL) { |
608 | | msSetError( |
609 | | MS_QUERYERR, |
610 | | "Error parsing MYSQL JOIN: nothing specified in CONNECTION statement.", |
611 | | "msMySQLJoinConnect()"); |
612 | | |
613 | | return (MS_FAILURE); |
614 | | } |
615 | | |
616 | | conn_decrypted = msDecryptStringTokens(layer->map, join->connection); |
617 | | if (conn_decrypted == NULL) { |
618 | | msSetError( |
619 | | MS_QUERYERR, |
620 | | "Error parsing MYSQL JOIN: unable to decrypt CONNECTION statement.", |
621 | | "msMySQLJoinConnect()"); |
622 | | return (MS_FAILURE); |
623 | | } |
624 | | |
625 | | delim = msStrdup(":"); |
626 | | DB_HOST = msStrdup(strtok(conn_decrypted, delim)); |
627 | | DB_USER = msStrdup(strtok(NULL, delim)); |
628 | | DB_PASSWD = msStrdup(strtok(NULL, delim)); |
629 | | DB_DATABASE = msStrdup(strtok(NULL, delim)); |
630 | | free(conn_decrypted); |
631 | | |
632 | | if (DB_HOST == NULL || DB_USER == NULL || DB_PASSWD == NULL || |
633 | | DB_DATABASE == NULL) { |
634 | | msSetError(MS_QUERYERR, |
635 | | "DB param error: at least one of HOST, USER, PASSWD or DATABASE " |
636 | | "is null!", |
637 | | "msMySQLJoinConnect()"); |
638 | | return MS_FAILURE; |
639 | | } |
640 | | if (strcmp(DB_PASSWD, "none") == 0) |
641 | | strcpy(DB_PASSWD, ""); |
642 | | |
643 | | #if MYSQL_VERSION_ID >= 40000 |
644 | | mysql_init(&(joininfo->mysql)); |
645 | | if (!(joininfo->conn = mysql_real_connect( |
646 | | &(joininfo->mysql), DB_HOST, DB_USER, DB_PASSWD, NULL, 0, NULL, 0))) |
647 | | #else |
648 | | if (!(joininfo->conn = |
649 | | mysql_connect(&(joininfo->mysql), DB_HOST, DB_USER, DB_PASSWD))) |
650 | | #endif |
651 | | { |
652 | | char tmp[4000]; |
653 | | snprintf(tmp, sizeof(tmp), |
654 | | "Failed to connect to SQL server: Error: %s\nHost: " |
655 | | "%s\nUsername:%s\nPassword:%s\n", |
656 | | mysql_error(joininfo->conn), DB_HOST, DB_USER, DB_PASSWD); |
657 | | msSetError(MS_QUERYERR, "%s", "msMYSQLLayerOpen()", tmp); |
658 | | free(joininfo); |
659 | | return MS_FAILURE; |
660 | | } |
661 | | |
662 | | MYDEBUG printf("msMYSQLLayerOpen2 called<br>\n"); |
663 | | if (mysql_select_db(joininfo->conn, DB_DATABASE) < 0) { |
664 | | mysql_close(joininfo->conn); |
665 | | } |
666 | | MYDEBUG printf("msMYSQLLayerOpen3 called<br>\n"); |
667 | | if (joininfo->qresult != NULL) { /* query leftover */ |
668 | | MYDEBUG printf("msMYSQLLayerOpen4 called<br>\n"); |
669 | | mysql_free_result(joininfo->qresult); |
670 | | } |
671 | | MYDEBUG printf("msMYSQLLayerOpen5 called<br>\n"); |
672 | | snprintf(qbuf, sizeof(qbuf), "SELECT count(%s) FROM %s", join->to, |
673 | | join->table); |
674 | | MYDEBUG printf("%s<br>\n", qbuf); |
675 | | if ((joininfo->qresult = |
676 | | msMySQLQuery(qbuf, joininfo->conn))) { /* There were some rows found, |
677 | | write 'em out for debug */ |
678 | | int numrows = mysql_affected_rows(joininfo->conn); |
679 | | MYDEBUG printf("%d rows<br>\n", numrows); |
680 | | for (i = 0; i < numrows; i++) { |
681 | | MYSQL_ROW row = mysql_fetch_row(joininfo->qresult); |
682 | | MYDEBUG printf("(%s)<BR>\n", row[0]); |
683 | | joininfo->rows = atoi(row[0]); |
684 | | } |
685 | | } else { |
686 | | msSetError(MS_DBFERR, "Item %s not found in table %s.", |
687 | | "msMySQLJoinConnect()", join->to, join->table); |
688 | | return (MS_FAILURE); |
689 | | } |
690 | | snprintf(qbuf, sizeof(qbuf), "EXPLAIN %s", join->table); |
691 | | if ((joininfo->qresult = |
692 | | msMySQLQuery(qbuf, joininfo->conn))) { /* There were some rows found, |
693 | | write 'em out for debug */ |
694 | | join->numitems = mysql_affected_rows(joininfo->conn); |
695 | | if ((join->items = (char **)malloc(sizeof(char *) * join->numitems)) == |
696 | | NULL) { |
697 | | msSetError(MS_MEMERR, NULL, "msMySQLJoinConnect()"); |
698 | | return (MS_FAILURE); |
699 | | } |
700 | | MYDEBUG printf("%d rows<br>\n", join->numitems); |
701 | | for (i = 0; i < join->numitems; i++) { |
702 | | MYSQL_ROW row = mysql_fetch_row(joininfo->qresult); |
703 | | MYDEBUG printf("(%s)<BR>\n", row[0]); |
704 | | join->items[i] = msStrdup(row[0]); |
705 | | } |
706 | | } else { |
707 | | msSetError(MS_DBFERR, "Item %s not found in table %s.", |
708 | | "msMySQLJoinConnect()", join->to, join->table); |
709 | | return (MS_FAILURE); |
710 | | } |
711 | | joininfo->tocolumn = msStrdup(join->to); |
712 | | |
713 | | /* get "from" item index */ |
714 | | for (i = 0; i < layer->numitems; i++) { |
715 | | if (strcasecmp(layer->items[i], join->from) == 0) { /* found it */ |
716 | | joininfo->fromindex = i; |
717 | | break; |
718 | | } |
719 | | } |
720 | | |
721 | | if (i == layer->numitems) { |
722 | | msSetError(MS_JOINERR, "Item %s not found in layer %s.", |
723 | | "msMySQLJoinConnect()", join->from, layer->name); |
724 | | return (MS_FAILURE); |
725 | | } |
726 | | |
727 | | /* finally store away the item names in the XBase table */ |
728 | | if (!join->items) |
729 | | return (MS_FAILURE); |
730 | | |
731 | | return (MS_SUCCESS); |
732 | | #endif |
733 | 0 | } |
734 | | |
735 | 0 | int msMySQLJoinPrepare(joinObj *join, shapeObj *shape) { |
736 | 0 | (void)join; |
737 | 0 | (void)shape; |
738 | 0 | #ifndef USE_MYSQL |
739 | 0 | msSetError(MS_QUERYERR, |
740 | 0 | "MySQL support not available (compile with --with-mysql)", |
741 | 0 | "msMySQLJoinPrepare()"); |
742 | 0 | return (MS_FAILURE); |
743 | | #else |
744 | | msMySQLJoinInfo *joininfo = join->joininfo; |
745 | | |
746 | | if (!joininfo) { |
747 | | msSetError(MS_JOINERR, "Join connection has not be created.", |
748 | | "msMySQLJoinPrepare()"); |
749 | | return (MS_FAILURE); |
750 | | } |
751 | | |
752 | | if (!shape) { |
753 | | msSetError(MS_JOINERR, "Shape to be joined is empty.", |
754 | | "msMySQLJoinPrepare()"); |
755 | | return (MS_FAILURE); |
756 | | } |
757 | | |
758 | | if (!shape->values) { |
759 | | msSetError(MS_JOINERR, "Shape to be joined has no attributes.", |
760 | | "msMySQLJoinPrepare()"); |
761 | | return (MS_FAILURE); |
762 | | } |
763 | | |
764 | | joininfo->nextrecord = 0; /* starting with the first record */ |
765 | | |
766 | | if (joininfo->target) |
767 | | free(joininfo->target); /* clear last target */ |
768 | | joininfo->target = msStrdup(shape->values[joininfo->fromindex]); |
769 | | |
770 | | return (MS_SUCCESS); |
771 | | #endif |
772 | 0 | } |
773 | | |
774 | 0 | int msMySQLJoinNext(joinObj *join) { |
775 | 0 | (void)join; |
776 | 0 | #ifndef USE_MYSQL |
777 | 0 | msSetError(MS_QUERYERR, |
778 | 0 | "MySQL support not available (compile with --with-mysql)", |
779 | 0 | "msMySQLJoinNext()"); |
780 | 0 | return (MS_FAILURE); |
781 | | #else |
782 | | int i; |
783 | | char qbuf[4000]; |
784 | | msMySQLJoinInfo *joininfo = join->joininfo; |
785 | | |
786 | | if (!joininfo) { |
787 | | msSetError(MS_JOINERR, "Join connection has not be created.", |
788 | | "msMySQLJoinNext()"); |
789 | | return (MS_FAILURE); |
790 | | } |
791 | | |
792 | | if (!joininfo->target) { |
793 | | msSetError(MS_JOINERR, |
794 | | "No target specified, run msMySQLJoinPrepare() first.", |
795 | | "msMySQLJoinNext()"); |
796 | | return (MS_FAILURE); |
797 | | } |
798 | | |
799 | | /* clear any old data */ |
800 | | if (join->values) { |
801 | | msFreeCharArray(join->values, join->numitems); |
802 | | join->values = NULL; |
803 | | } |
804 | | |
805 | | /* int n = joininfo->rows; */ |
806 | | |
807 | | /* for(i=joininfo->nextrecord; i<n; i++) { // find a match */ |
808 | | /* if(strcmp(joininfo->target, msMySQLReadStringAttribute(joininfo->conn, i, |
809 | | * joininfo->toindex)) == 0) break; */ |
810 | | /* } */ |
811 | | snprintf(qbuf, sizeof(qbuf), "SELECT * FROM %s WHERE %s = %s", join->table, |
812 | | joininfo->tocolumn, joininfo->target); |
813 | | MYDEBUG printf("%s<BR>\n", qbuf); |
814 | | if ((joininfo->qresult = |
815 | | msMySQLQuery(qbuf, joininfo->conn))) { /* There were some rows found, |
816 | | write 'em out for debug */ |
817 | | int numrows = mysql_affected_rows(joininfo->conn); |
818 | | int numfields = mysql_field_count(joininfo->conn); |
819 | | MYDEBUG printf("%d rows<br>\n", numrows); |
820 | | if (numrows > 0) { |
821 | | MYSQL_ROW row = mysql_fetch_row(joininfo->qresult); |
822 | | for (i = 0; i < numfields; i++) { |
823 | | MYDEBUG printf("%s,", row[i]); |
824 | | } |
825 | | MYDEBUG printf("<BR>\n"); |
826 | | free(join->values); |
827 | | if ((join->values = (char **)malloc(sizeof(char *) * join->numitems)) == |
828 | | NULL) { |
829 | | msSetError(MS_MEMERR, NULL, "msMySQLJoinNext()"); |
830 | | return (MS_FAILURE); |
831 | | } |
832 | | for (i = 0; i < join->numitems; i++) { |
833 | | /* join->values[i] = msStrdup("\0"); */ /* initialize to zero length |
834 | | strings */ |
835 | | join->values[i] = |
836 | | msStrdup(row[i]); /* initialize to zero length strings */ |
837 | | /* rows = atoi(row[0]); */ |
838 | | } |
839 | | } else { |
840 | | if ((join->values = (char **)malloc(sizeof(char *) * join->numitems)) == |
841 | | NULL) { |
842 | | msSetError(MS_MEMERR, NULL, "msMySQLJoinNext()"); |
843 | | return (MS_FAILURE); |
844 | | } |
845 | | for (i = 0; i < join->numitems; i++) |
846 | | join->values[i] = |
847 | | msStrdup("\0"); /* initialize to zero length strings */ |
848 | | |
849 | | return (MS_DONE); |
850 | | } |
851 | | } else { |
852 | | msSetError(MS_QUERYERR, "Query error (%s)", "msMySQLJoinNext()", qbuf); |
853 | | return (MS_FAILURE); |
854 | | } |
855 | | |
856 | | #ifdef __NOTDEF__ |
857 | | if (i == n) { /* unable to do the join */ |
858 | | if ((join->values = (char **)malloc(sizeof(char *) * join->numitems)) == |
859 | | NULL) { |
860 | | msSetError(MS_MEMERR, NULL, "msMySQLJoinNext()"); |
861 | | return (MS_FAILURE); |
862 | | } |
863 | | for (i = 0; i < join->numitems; i++) |
864 | | join->values[i] = msStrdup("\0"); /* initialize to zero length strings */ |
865 | | |
866 | | joininfo->nextrecord = n; |
867 | | return (MS_DONE); |
868 | | } |
869 | | |
870 | | if ((join->values = msMySQLGetValues(joininfo->conn, i)) == NULL) |
871 | | return (MS_FAILURE); |
872 | | |
873 | | joininfo->nextrecord = |
874 | | i + 1; /* so we know where to start looking next time through */ |
875 | | #endif /* __NOTDEF__ */ |
876 | | |
877 | | return (MS_SUCCESS); |
878 | | #endif |
879 | 0 | } |
880 | | |
881 | 0 | int msMySQLJoinClose(joinObj *join) { |
882 | 0 | (void)join; |
883 | 0 | #ifndef USE_MYSQL |
884 | 0 | msSetError(MS_QUERYERR, |
885 | 0 | "MySQL support not available (compile with --with-mysql)", |
886 | 0 | "msMySQLJoinClose()"); |
887 | 0 | return (MS_FAILURE); |
888 | | #else |
889 | | msMySQLJoinInfo *joininfo = join->joininfo; |
890 | | |
891 | | if (!joininfo) |
892 | | return (MS_SUCCESS); /* already closed */ |
893 | | |
894 | | mysql_close(joininfo->conn); |
895 | | if (joininfo->target) |
896 | | free(joininfo->target); |
897 | | free(joininfo); |
898 | | joininfo = NULL; |
899 | | |
900 | | return (MS_SUCCESS); |
901 | | #endif |
902 | 0 | } |