]> git.openstreetmap.org Git - rails.git/blob - db/migrate/008_remove_segments_helper.cc
More translations for export dialog, and change "velja svæði" to "velja annað svæði...
[rails.git] / db / migrate / 008_remove_segments_helper.cc
1 #include <mysql.h>
2 #include <stdint.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <vector>
7 #include <list>
8 #include <sstream>
9 #include <map>
10 #include <string>
11
12 #ifdef __amd64__
13
14 #define F_U64 "%lu"
15 #define F_U32 "%u"
16
17 #else
18
19 #define F_U64 "%Lu"
20 #define F_U32 "%u"
21
22 #endif
23
24 using namespace std;
25
26 template <typename T>
27 static T parse(const char *str) {
28   istringstream in(str);
29   T t;
30   in >> t;
31   return t;
32 }
33
34 static void exit_mysql_err(MYSQL *mysql) {
35   const char *err = mysql_error(mysql);
36   if (err) {
37     fprintf(stderr, "008_remove_segments_helper: MySQL error: %s\n", err);
38   } else {
39     fprintf(stderr, "008_remove_segments_helper: MySQL error\n");
40   }
41   abort();
42   exit(EXIT_FAILURE);
43 }
44
45 static void exit_stmt_err(MYSQL_STMT *stmt) {
46   const char *err = mysql_stmt_error(stmt);
47   if (err) {
48     fprintf(stderr, "008_remove_segments_helper: MySQL stmt error: %s\n", err);
49   } else {
50     fprintf(stderr, "008_remove_segments_helper: MySQL stmt error\n");
51   }
52   abort();
53   exit(EXIT_FAILURE);
54 }
55
56 struct segment {
57   uint32_t from, to;
58 };
59
60 struct data {
61   MYSQL *mysql, *mysql2;
62
63   uint64_t seg_maxid, way_maxid;
64   uint64_t new_way_id;
65   uint64_t new_relation_id;
66
67   size_t segs_len;
68   struct segment *segs;
69   unsigned char *rem_segs;
70   unsigned char *tgd_segs;
71
72   FILE *ways, *way_nodes, *way_tags,
73     *relations, *relation_members, *relation_tags;
74 };
75
76 static uint64_t select_u64(MYSQL *mysql, const char *q) {
77   MYSQL_RES *res;
78   MYSQL_ROW row;
79   uint64_t ret;
80
81   if (mysql_query(mysql, q))
82     exit_mysql_err(mysql);
83
84   res = mysql_store_result(mysql);
85   if (!res) exit_mysql_err(mysql);
86
87   row = mysql_fetch_row(res);
88   if (!row) exit_mysql_err(mysql);
89
90   if (row[0]) {
91     ret = parse<uint64_t>(row[0]);
92   } else {
93     ret = 0;
94   }
95
96   mysql_free_result(res);
97
98   return ret;
99 }
100
101 static void find_maxids(struct data *d) {
102   d->seg_maxid = select_u64(d->mysql, "SELECT max(id) FROM current_segments");
103   d->segs_len = d->seg_maxid + 1;
104   d->way_maxid = select_u64(d->mysql, "SELECT max(id) FROM current_ways");
105   d->new_way_id = d->way_maxid + 1;
106   d->new_relation_id = select_u64(d->mysql, "SELECT max(id) FROM current_relations") + 1;
107 }
108
109 static void populate_segs(struct data *d) {
110   MYSQL_RES *res;
111   MYSQL_ROW row;
112   size_t id;
113
114   d->segs = (segment *) malloc(sizeof(struct segment) * d->segs_len);
115   memset(d->segs, 0, sizeof(struct segment) * d->segs_len);
116
117   d->rem_segs = (unsigned char *) malloc(d->segs_len);
118   d->tgd_segs = (unsigned char *) malloc(d->segs_len);
119   memset(d->rem_segs, 0, d->segs_len);
120   memset(d->tgd_segs, 0, d->segs_len);
121
122   if (mysql_query(d->mysql, "SELECT id, node_a, node_b "
123       "FROM current_segments WHERE visible"))
124     exit_mysql_err(d->mysql);
125
126   res = mysql_use_result(d->mysql);
127   if (!res) exit_mysql_err(d->mysql);
128
129   while ((row = mysql_fetch_row(res))) {
130     id = parse<size_t>(row[0]);
131     if (id >= d->segs_len) continue;
132     d->segs[id].from = parse<uint32_t>(row[1]);
133     d->segs[id].to   = parse<uint32_t>(row[2]);
134     d->rem_segs[id] = 1;
135   }
136   if (mysql_errno(d->mysql)) exit_mysql_err(d->mysql);
137
138   mysql_free_result(res);
139 }
140
141 static void write_csv_col(FILE *f, const char *str, char end) {
142   char *out = (char *) malloc(2 * strlen(str) + 4);
143   char *o = out;
144   size_t len;
145
146   *(o++) = '\"';
147   for (; *str; str++) {
148     if (*str == '\0') {
149       break;
150     } else if (*str == '\"') {
151       *(o++) = '\"';
152       *(o++) = '\"';
153     } else {
154       *(o++) = *str;
155     }
156   }
157   *(o++) = '\"';
158   *(o++) = end;
159   *(o++) = '\0';
160
161   len = strlen(out);
162   if (fwrite(out, len, 1, f) != 1) {
163     perror("fwrite");
164     exit(EXIT_FAILURE);
165   }
166
167   free(out);
168 }
169
170 static void convert_ways(struct data *d) {
171   MYSQL_RES *res;
172   MYSQL_ROW row;
173   MYSQL_STMT *load_segs, *load_tags;
174   const char
175     load_segs_stmt[] = "SELECT segment_id FROM current_way_segments "
176       "WHERE id = ? ORDER BY sequence_id",
177     load_tags_stmt[] = "SELECT k, v FROM current_way_tags WHERE id = ?";
178   char *k, *v;
179   const size_t max_tag_len = 1 << 16;
180   long long mysql_id, mysql_seg_id;
181   unsigned long res_len;
182   my_bool res_error;
183   MYSQL_BIND bind[1], seg_bind[1], tag_bind[2];
184
185   /* F***ing libmysql only support fixed size buffers for string results of
186    * prepared statements.  So allocate 65k for the tag key and the tag value
187    * and hope it'll suffice. */
188   k = (char *) malloc(max_tag_len);
189   v = (char *) malloc(max_tag_len);
190
191   load_segs = mysql_stmt_init(d->mysql2);
192   if (!load_segs) exit_mysql_err(d->mysql2);
193   if (mysql_stmt_prepare(load_segs, load_segs_stmt, sizeof(load_segs_stmt)))
194     exit_stmt_err(load_segs);
195
196   memset(bind, 0, sizeof(bind));
197   bind[0].buffer_type = MYSQL_TYPE_LONGLONG;
198   bind[0].buffer = (char *) &mysql_id;
199   bind[0].is_null = 0;
200   bind[0].length = 0;
201   if (mysql_stmt_bind_param(load_segs, bind))
202     exit_stmt_err(load_segs);
203
204   memset(seg_bind, 0, sizeof(seg_bind));
205   seg_bind[0].buffer_type = MYSQL_TYPE_LONGLONG;
206   seg_bind[0].buffer = (char *) &mysql_seg_id;
207   seg_bind[0].is_null = 0;
208   seg_bind[0].length = 0;
209   seg_bind[0].error = &res_error;
210   if (mysql_stmt_bind_result(load_segs, seg_bind))
211     exit_stmt_err(load_segs);
212
213   load_tags = mysql_stmt_init(d->mysql2);
214   if (!load_tags) exit_mysql_err(d->mysql2);
215   if (mysql_stmt_prepare(load_tags, load_tags_stmt, sizeof(load_tags_stmt)))
216     exit_stmt_err(load_tags);
217
218   memset(bind, 0, sizeof(bind));
219   bind[0].buffer_type = MYSQL_TYPE_LONGLONG;
220   bind[0].buffer = (char *) &mysql_id;
221   bind[0].is_null = 0;
222   bind[0].length = 0;
223
224   if (mysql_stmt_bind_param(load_tags, bind))
225     exit_stmt_err(load_tags);
226
227   memset(tag_bind, 0, sizeof(tag_bind));
228   tag_bind[0].buffer_type = MYSQL_TYPE_STRING;
229   tag_bind[0].buffer = k;
230   tag_bind[0].is_null = 0;
231   tag_bind[0].length = &res_len;
232   tag_bind[0].error = &res_error;
233   tag_bind[0].buffer_length = max_tag_len;
234   tag_bind[1].buffer_type = MYSQL_TYPE_STRING;
235   tag_bind[1].buffer = v;
236   tag_bind[1].is_null = 0;
237   tag_bind[1].length = &res_len;
238   tag_bind[1].error = &res_error;
239   tag_bind[1].buffer_length = max_tag_len;
240   if (mysql_stmt_bind_result(load_tags, tag_bind))
241     exit_stmt_err(load_tags);
242
243   if (mysql_query(d->mysql, "SELECT id, user_id, timestamp "
244       "FROM current_ways WHERE visible"))
245     exit_mysql_err(d->mysql);
246
247   res = mysql_use_result(d->mysql);
248   if (!res) exit_mysql_err(d->mysql);
249
250   while ((row = mysql_fetch_row(res))) {
251     uint64_t id;
252     const char *user_id, *timestamp;
253
254     id = parse<uint64_t>(row[0]);
255     user_id = row[1];
256     timestamp = row[2];
257
258     mysql_id = (long long) id;
259
260     if (mysql_stmt_execute(load_segs))
261       exit_stmt_err(load_segs);
262
263     if (mysql_stmt_store_result(load_segs))
264       exit_stmt_err(load_segs);
265
266     list<segment> segs;
267     while (!mysql_stmt_fetch(load_segs)) {
268       if (((uint64_t) mysql_seg_id) >= d->segs_len) continue;
269       segs.push_back(d->segs[mysql_seg_id]);
270       d->rem_segs[mysql_seg_id] = 0;
271     }
272
273     list<list<uint32_t> > node_lists;
274     while (segs.size()) {
275       list<uint32_t> node_list;
276       node_list.push_back(segs.front().from);
277       node_list.push_back(segs.front().to);
278       segs.pop_front();
279       while (true) {
280         bool found = false;
281         for (list<segment>::iterator it = segs.begin();
282             it != segs.end(); ) {
283           if (it->from == node_list.back()) {
284             node_list.push_back(it->to);
285             segs.erase(it++);
286             found = true;
287           } else if (it->to == node_list.front()) {
288             node_list.insert(node_list.begin(), it->from);
289             segs.erase(it++);
290             found = true;
291           } else {
292             ++it;
293           }
294         }
295         if (!found) break;
296       }
297       node_lists.push_back(node_list);
298     }
299
300     vector<uint64_t> ids; ids.reserve(node_lists.size());
301     bool orig_id_used = false;
302     for (list<list<uint32_t> >::iterator it = node_lists.begin();
303         it != node_lists.end(); ++it) {
304       uint64_t way_id;
305       int sid;
306       if (orig_id_used) {
307         way_id = d->new_way_id++;
308       } else {
309         way_id = id;
310         orig_id_used = true;
311       }
312       ids.push_back(way_id);
313
314       fprintf(d->ways, "\"" F_U64 "\",", way_id);
315       write_csv_col(d->ways, user_id, ',');
316       write_csv_col(d->ways, timestamp, '\n');
317
318       sid = 1;
319       for (list<uint32_t>::iterator nit = it->begin();
320           nit != it->end(); ++nit) {
321         fprintf(d->way_nodes, "\"" F_U64 "\",\"" F_U32 "\",\"%i\"\n", way_id, *nit, sid++);
322       }
323     }
324
325     if (mysql_stmt_execute(load_tags))
326       exit_stmt_err(load_tags);
327
328     if (mysql_stmt_store_result(load_tags))
329       exit_stmt_err(load_tags);
330
331     bool multiple_parts = ids.size() > 1,
332       create_multipolygon = false;
333
334     while (!mysql_stmt_fetch(load_tags)) {
335       if (multiple_parts && !create_multipolygon) {
336         if (!strcmp(k, "natural")) {
337           if (strcmp(v, "coastline")) {
338             create_multipolygon = true;
339           }
340         } else if (!strcmp(k, "waterway")) {
341           if (!strcmp(v, "riverbank")) {
342             create_multipolygon = true;
343           }
344         } else if (!strcmp(k, "leisure") || !strcmp(k, "landuse")
345             || !strcmp(k, "sport") || !strcmp(k, "amenity")
346             || !strcmp(k, "tourism") || !strcmp(k, "building")) {
347           create_multipolygon = true;
348         }
349       }
350
351       for (vector<uint64_t>::iterator it = ids.begin();
352           it != ids.end(); ++it) {
353         fprintf(d->way_tags, "\"" F_U64 "\",", *it);
354         write_csv_col(d->way_tags, k, ',');
355         write_csv_col(d->way_tags, v, '\n');
356       }
357     }
358
359     if (multiple_parts && create_multipolygon) {
360       uint64_t ent_id = d->new_relation_id++;
361
362       fprintf(d->relations, "\"" F_U64 "\",", ent_id);
363       write_csv_col(d->relations, user_id, ',');
364       write_csv_col(d->relations, timestamp, '\n');
365
366       fprintf(d->relation_tags,
367         "\"" F_U64 "\",\"type\",\"multipolygon\"\n", ent_id);
368
369       for (vector<uint64_t>::iterator it = ids.begin();
370           it != ids.end(); ++it) {
371         fprintf(d->relation_members,
372           "\"" F_U64 "\",\"way\",\"" F_U64 "\",\"\"\n", ent_id, *it);
373       }
374     }
375   }
376   if (mysql_errno(d->mysql)) exit_stmt_err(load_tags);
377
378   mysql_stmt_close(load_segs);
379   mysql_stmt_close(load_tags);
380
381   mysql_free_result(res);
382   free(k);
383   free(v);
384 }
385
386 static int read_seg_tags(char **tags, char **k, char **v) {
387   if (!**tags) return 0;
388   char *i = strchr(*tags, ';');
389   if (!i) i = *tags + strlen(*tags);
390   char *j = strchr(*tags, '=');
391   *k = *tags;
392   if (j && j < i) {
393     *v = j + 1;
394   } else {
395     *v = i;
396   }
397   *tags = *i ? i + 1 : i;
398   *i = '\0';
399   if (j) *j = '\0';
400   return 1;
401 }
402
403 static void mark_tagged_segs(struct data *d) {
404   MYSQL_RES *res;
405   MYSQL_ROW row;
406   MYSQL_STMT *way_tags;
407   const char
408     way_tags_stmt[] = "SELECT k, v FROM current_way_segments INNER JOIN "
409       "current_way_tags ON current_way_segments.id = "
410       "current_way_tags.id WHERE segment_id = ?";
411   char *wk, *wv;
412   const size_t max_tag_len = 1 << 16;
413   long long mysql_seg_id;
414   unsigned long res_len;
415   my_bool res_error;
416   MYSQL_BIND in_bind[1], out_bind[1];
417
418   /* F***ing libmysql only support fixed size buffers for string results of
419    * prepared statements.  So allocate 65k for the tag key and the tag value
420    * and hope it'll suffice. */
421   wk = (char *) malloc(max_tag_len);
422   wv = (char *) malloc(max_tag_len);
423
424   way_tags = mysql_stmt_init(d->mysql2);
425   if (!way_tags) exit_mysql_err(d->mysql2);
426   if (mysql_stmt_prepare(way_tags, way_tags_stmt, sizeof(way_tags_stmt)))
427     exit_stmt_err(way_tags);
428
429   memset(in_bind, 0, sizeof(in_bind));
430   in_bind[0].buffer_type = MYSQL_TYPE_LONGLONG;
431   in_bind[0].buffer = (char *) &mysql_seg_id;
432   in_bind[0].is_null = 0;
433   in_bind[0].length = 0;
434
435   if (mysql_stmt_bind_param(way_tags, in_bind))
436     exit_stmt_err(way_tags);
437
438   memset(out_bind, 0, sizeof(out_bind));
439   out_bind[0].buffer_type = MYSQL_TYPE_STRING;
440   out_bind[0].buffer = wk;
441   out_bind[0].is_null = 0;
442   out_bind[0].length = &res_len;
443   out_bind[0].error = &res_error;
444   out_bind[0].buffer_length = max_tag_len;
445   out_bind[1].buffer_type = MYSQL_TYPE_STRING;
446   out_bind[1].buffer = wv;
447   out_bind[1].is_null = 0;
448   out_bind[1].length = &res_len;
449   out_bind[1].error = &res_error;
450   out_bind[1].buffer_length = max_tag_len;
451   if (mysql_stmt_bind_result(way_tags, out_bind))
452     exit_stmt_err(way_tags);
453
454   if (mysql_query(d->mysql, "SELECT id, tags FROM current_segments "
455       "WHERE visible && tags != '' && tags != 'created_by=JOSM'"))
456     exit_mysql_err(d->mysql);
457
458   res = mysql_use_result(d->mysql);
459   if (!res) exit_mysql_err(d->mysql);
460
461   while ((row = mysql_fetch_row(res))) {
462     size_t id = parse<size_t>(row[0]);
463     if (d->rem_segs[id]) continue;
464
465     map<string, string> interesting_tags;
466
467     char *tags_it = row[1], *k, *v;
468     while (read_seg_tags(&tags_it, &k, &v)) {
469       if (strcmp(k, "created_by") &&
470           strcmp(k, "tiger:county") &&
471           strcmp(k, "tiger:upload_uuid") &&
472           strcmp(k, "converted_by") &&
473           (strcmp(k, "width") || strcmp(v, "4")) &&
474           (strcmp(k, "natural") || strcmp(v, "coastline")) &&
475           (strcmp(k, "source") || strncmp(v, "PGS", 3))) {
476         interesting_tags.insert(make_pair(string(k), string(v)));
477       }
478     }
479
480     if (interesting_tags.size() == 0) continue;
481
482     mysql_seg_id = id;
483
484     if (mysql_stmt_execute(way_tags))
485       exit_stmt_err(way_tags);
486
487     if (mysql_stmt_store_result(way_tags))
488       exit_stmt_err(way_tags);
489
490     while (!mysql_stmt_fetch(way_tags)) {
491       for (map<string, string>::iterator it = interesting_tags.find(wk);
492           it != interesting_tags.end() && it->first == wk; ++it) {
493         if (it->second == wv) {
494           interesting_tags.erase(it);
495           break;
496         }
497       }
498     }
499
500     if (interesting_tags.size() > 0) {
501       d->rem_segs[id] = 1;
502       d->tgd_segs[id] = 1;
503     }
504   }
505
506   mysql_free_result(res);
507
508   mysql_stmt_close(way_tags);
509   free(wk);
510   free(wv);
511 }
512
513 static void convert_remaining_segs(struct data *d) {
514   MYSQL_STMT *load_seg;
515   MYSQL_BIND args[1], res[3];
516   const size_t max_tag_len = 1 << 16;
517   char *tags, timestamp[100];
518   char *k, *v;
519   char notetmp[1024];
520   int user_id;
521   long long mysql_id;
522   unsigned long res_len;
523   my_bool res_error;
524   const char load_seg_stmt[] =
525     "SELECT user_id, tags, CAST(timestamp AS CHAR) FROM current_segments "
526     "WHERE visible && id = ?";
527
528   tags = (char *) malloc(max_tag_len);
529
530   load_seg = mysql_stmt_init(d->mysql);
531   if (!load_seg) exit_mysql_err(d->mysql);
532   if (mysql_stmt_prepare(load_seg, load_seg_stmt, sizeof(load_seg_stmt)))
533     exit_stmt_err(load_seg);
534
535   memset(args, 0, sizeof(args));
536   args[0].buffer_type = MYSQL_TYPE_LONGLONG;
537   args[0].buffer = (char *) &mysql_id;
538   args[0].is_null = 0;
539   args[0].length = 0;
540   if (mysql_stmt_bind_param(load_seg, args))
541     exit_stmt_err(load_seg);
542
543   memset(res, 0, sizeof(res));
544   res[0].buffer_type = MYSQL_TYPE_LONG;
545   res[0].buffer = (char *) &user_id;
546   res[0].is_null = 0;
547   res[0].length = 0;
548   res[0].error = &res_error;
549   res[1].buffer_type = MYSQL_TYPE_STRING;
550   res[1].buffer = tags;
551   res[1].is_null = 0;
552   res[1].length = &res_len;
553   res[1].error = &res_error;
554   res[1].buffer_length = max_tag_len;
555   res[2].buffer_type = MYSQL_TYPE_STRING;
556   res[2].buffer = timestamp;
557   res[2].is_null = 0;
558   res[2].length = &res_len;
559   res[2].error = &res_error;
560   res[2].buffer_length = sizeof(timestamp);
561   if (mysql_stmt_bind_result(load_seg, res))
562     exit_stmt_err(load_seg);
563
564   for (size_t seg_id = 0; seg_id < d->segs_len; seg_id++) {
565     if (!d->rem_segs[seg_id]) continue;
566     const char *what = d->tgd_segs[seg_id] ? "tagged" : "unwayed";
567     segment seg = d->segs[seg_id];
568
569     mysql_id = seg_id;
570     if (mysql_stmt_execute(load_seg)) exit_stmt_err(load_seg);
571     if (mysql_stmt_store_result(load_seg)) exit_stmt_err(load_seg);
572
573     while (!mysql_stmt_fetch(load_seg)) {
574       uint64_t way_id = d->new_way_id++;
575
576       fprintf(d->ways, "\"" F_U64 "\",\"%i\",", way_id, user_id);
577       write_csv_col(d->ways, timestamp, '\n');
578
579       fprintf(d->way_nodes, "\"" F_U64 "\",\"" F_U32 "\",\"%i\"\n", way_id, seg.from, 1);
580       fprintf(d->way_nodes, "\"" F_U64 "\",\"" F_U32 "\",\"%i\"\n", way_id, seg.to, 2);
581
582       char *tags_it = tags;
583       bool note = false;
584       while (read_seg_tags(&tags_it, &k, &v)) {
585         fprintf(d->way_tags, "\"" F_U64 "\",", way_id);
586         write_csv_col(d->way_tags, k, ',');
587         if(!strcmp(k,"note")) {
588           snprintf(notetmp, sizeof(notetmp), "%s; FIXME previously %s segment", v, what);
589           note = true;
590           write_csv_col(d->way_tags, notetmp, '\n');
591         } else {
592           write_csv_col(d->way_tags, v, '\n');
593         }
594       }
595       if (!note) {
596         sprintf(notetmp, "FIXME previously %s segment", what);
597         fprintf(d->way_tags, "\"" F_U64 "\",", way_id);
598         write_csv_col(d->way_tags, "note", ',');
599         write_csv_col(d->way_tags, notetmp, '\n');
600       }
601     }
602   }
603
604   mysql_stmt_close(load_seg);
605
606   free(tags);
607 }
608
609 static MYSQL *connect_to_mysql(char **argv) {
610   MYSQL *mysql = mysql_init(NULL);
611   if (!mysql) exit_mysql_err(mysql);
612
613   if (!mysql_real_connect(mysql, argv[1], argv[2], argv[3], argv[4],
614       argv[5][0] ? atoi(argv[5]) : 0, argv[6][0] ? argv[6] : NULL, 0))
615     exit_mysql_err(mysql);
616
617   if (mysql_set_character_set(mysql, "utf8"))
618     exit_mysql_err(mysql);
619
620   return mysql;
621 }
622
623 static void open_file(FILE **f, char *fn) {
624   *f = fopen(fn, "w+");
625   if (!*f) {
626     perror("fopen");
627     exit(EXIT_FAILURE);
628   }
629 }
630
631 int main(int argc, char **argv) {
632   struct data data;
633   struct data *d = &data;
634   size_t prefix_len;
635   char *tempfn;
636
637   if (argc != 8) {
638     printf("Usage: 008_remove_segments_helper host user passwd database port socket prefix\n");
639     exit(EXIT_FAILURE);
640   }
641
642   d->mysql = connect_to_mysql(argv);
643   d->mysql2 = connect_to_mysql(argv);
644
645   prefix_len = strlen(argv[7]);
646   tempfn = (char *) malloc(prefix_len + 15);
647   strcpy(tempfn, argv[7]);
648
649   strcpy(tempfn + prefix_len, "ways");
650   open_file(&d->ways, tempfn);
651
652   strcpy(tempfn + prefix_len, "way_nodes");
653   open_file(&d->way_nodes, tempfn);
654
655   strcpy(tempfn + prefix_len, "way_tags");
656   open_file(&d->way_tags, tempfn);
657
658   strcpy(tempfn + prefix_len, "relations");
659   open_file(&d->relations, tempfn);
660
661   strcpy(tempfn + prefix_len, "relation_members");
662   open_file(&d->relation_members, tempfn);
663
664   strcpy(tempfn + prefix_len, "relation_tags");
665   open_file(&d->relation_tags, tempfn);
666
667   free(tempfn);
668
669   find_maxids(d);
670   populate_segs(d);
671   convert_ways(d);
672   mark_tagged_segs(d);
673   convert_remaining_segs(d);
674
675   mysql_close(d->mysql);
676   mysql_close(d->mysql2);
677
678   fclose(d->ways);
679   fclose(d->way_nodes);
680   fclose(d->way_tags);
681
682   fclose(d->relations);
683   fclose(d->relation_members);
684   fclose(d->relation_tags);
685
686   free(d->segs);
687   free(d->rem_segs);
688   free(d->tgd_segs);
689
690   exit(EXIT_SUCCESS);
691 }