Coverage Report

Created: 2025-06-13 06:29

/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
}