27 static T parse(const char *str) {
28 istringstream in(str);
34 static void exit_mysql_err(MYSQL *mysql) {
35 const char *err = mysql_error(mysql);
37 fprintf(stderr, "008_remove_segments_helper: MySQL error: %s\n", err);
39 fprintf(stderr, "008_remove_segments_helper: MySQL error\n");
45 static void exit_stmt_err(MYSQL_STMT *stmt) {
46 const char *err = mysql_stmt_error(stmt);
48 fprintf(stderr, "008_remove_segments_helper: MySQL stmt error: %s\n", err);
50 fprintf(stderr, "008_remove_segments_helper: MySQL stmt error\n");
61 MYSQL *mysql, *mysql2;
63 uint64_t seg_maxid, way_maxid;
65 uint64_t new_relation_id;
69 unsigned char *rem_segs;
70 unsigned char *tgd_segs;
72 FILE *ways, *way_nodes, *way_tags,
73 *relations, *relation_members, *relation_tags;
76 static uint64_t select_u64(MYSQL *mysql, const char *q) {
81 if (mysql_query(mysql, q))
82 exit_mysql_err(mysql);
84 res = mysql_store_result(mysql);
85 if (!res) exit_mysql_err(mysql);
87 row = mysql_fetch_row(res);
88 if (!row) exit_mysql_err(mysql);
91 ret = parse<uint64_t>(row[0]);
96 mysql_free_result(res);
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;
109 static void populate_segs(struct data *d) {
114 d->segs = (segment *) malloc(sizeof(struct segment) * d->segs_len);
115 memset(d->segs, 0, sizeof(struct segment) * d->segs_len);
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);
122 if (mysql_query(d->mysql, "SELECT id, node_a, node_b "
123 "FROM current_segments WHERE visible"))
124 exit_mysql_err(d->mysql);
126 res = mysql_use_result(d->mysql);
127 if (!res) exit_mysql_err(d->mysql);
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]);
136 if (mysql_errno(d->mysql)) exit_mysql_err(d->mysql);
138 mysql_free_result(res);
141 static void write_csv_col(FILE *f, const char *str, char end) {
142 char *out = (char *) malloc(2 * strlen(str) + 4);
147 for (; *str; str++) {
150 } else if (*str == '\"') {
162 if (fwrite(out, len, 1, f) != 1) {
170 static void convert_ways(struct data *d) {
173 MYSQL_STMT *load_segs, *load_tags;
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 = ?";
179 const size_t max_tag_len = 1 << 16;
180 long long mysql_id, mysql_seg_id;
181 unsigned long res_len;
183 MYSQL_BIND bind[1], seg_bind[1], tag_bind[2];
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);
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);
196 memset(bind, 0, sizeof(bind));
197 bind[0].buffer_type = MYSQL_TYPE_LONGLONG;
198 bind[0].buffer = (char *) &mysql_id;
201 if (mysql_stmt_bind_param(load_segs, bind))
202 exit_stmt_err(load_segs);
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);
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);
218 memset(bind, 0, sizeof(bind));
219 bind[0].buffer_type = MYSQL_TYPE_LONGLONG;
220 bind[0].buffer = (char *) &mysql_id;
224 if (mysql_stmt_bind_param(load_tags, bind))
225 exit_stmt_err(load_tags);
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);
243 if (mysql_query(d->mysql, "SELECT id, user_id, timestamp "
244 "FROM current_ways WHERE visible"))
245 exit_mysql_err(d->mysql);
247 res = mysql_use_result(d->mysql);
248 if (!res) exit_mysql_err(d->mysql);
250 while ((row = mysql_fetch_row(res))) {
252 const char *user_id, *timestamp;
254 id = parse<uint64_t>(row[0]);
258 mysql_id = (long long) id;
260 if (mysql_stmt_execute(load_segs))
261 exit_stmt_err(load_segs);
263 if (mysql_stmt_store_result(load_segs))
264 exit_stmt_err(load_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;
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);
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);
287 } else if (it->to == node_list.front()) {
288 node_list.insert(node_list.begin(), it->from);
297 node_lists.push_back(node_list);
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) {
307 way_id = d->new_way_id++;
312 ids.push_back(way_id);
314 fprintf(d->ways, "\"" F_U64 "\",", way_id);
315 write_csv_col(d->ways, user_id, ',');
316 write_csv_col(d->ways, timestamp, '\n');
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++);
325 if (mysql_stmt_execute(load_tags))
326 exit_stmt_err(load_tags);
328 if (mysql_stmt_store_result(load_tags))
329 exit_stmt_err(load_tags);
331 bool multiple_parts = ids.size() > 1,
332 create_multipolygon = false;
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;
340 } else if (!strcmp(k, "waterway")) {
341 if (!strcmp(v, "riverbank")) {
342 create_multipolygon = true;
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;
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');
359 if (multiple_parts && create_multipolygon) {
360 uint64_t ent_id = d->new_relation_id++;
362 fprintf(d->relations, "\"" F_U64 "\",", ent_id);
363 write_csv_col(d->relations, user_id, ',');
364 write_csv_col(d->relations, timestamp, '\n');
366 fprintf(d->relation_tags,
367 "\"" F_U64 "\",\"type\",\"multipolygon\"\n", ent_id);
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);
376 if (mysql_errno(d->mysql)) exit_stmt_err(load_tags);
378 mysql_stmt_close(load_segs);
379 mysql_stmt_close(load_tags);
381 mysql_free_result(res);
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, '=');
397 *tags = *i ? i + 1 : i;
403 static void mark_tagged_segs(struct data *d) {
406 MYSQL_STMT *way_tags;
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 = ?";
412 const size_t max_tag_len = 1 << 16;
413 long long mysql_seg_id;
414 unsigned long res_len;
416 MYSQL_BIND in_bind[1], out_bind[1];
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);
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);
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;
435 if (mysql_stmt_bind_param(way_tags, in_bind))
436 exit_stmt_err(way_tags);
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);
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);
458 res = mysql_use_result(d->mysql);
459 if (!res) exit_mysql_err(d->mysql);
461 while ((row = mysql_fetch_row(res))) {
462 size_t id = parse<size_t>(row[0]);
463 if (d->rem_segs[id]) continue;
465 map<string, string> interesting_tags;
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)));
480 if (interesting_tags.size() == 0) continue;
484 if (mysql_stmt_execute(way_tags))
485 exit_stmt_err(way_tags);
487 if (mysql_stmt_store_result(way_tags))
488 exit_stmt_err(way_tags);
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);
500 if (interesting_tags.size() > 0) {
506 mysql_free_result(res);
508 mysql_stmt_close(way_tags);
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];
522 unsigned long res_len;
524 const char load_seg_stmt[] =
525 "SELECT user_id, tags, CAST(timestamp AS CHAR) FROM current_segments "
526 "WHERE visible && id = ?";
528 tags = (char *) malloc(max_tag_len);
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);
535 memset(args, 0, sizeof(args));
536 args[0].buffer_type = MYSQL_TYPE_LONGLONG;
537 args[0].buffer = (char *) &mysql_id;
540 if (mysql_stmt_bind_param(load_seg, args))
541 exit_stmt_err(load_seg);
543 memset(res, 0, sizeof(res));
544 res[0].buffer_type = MYSQL_TYPE_LONG;
545 res[0].buffer = (char *) &user_id;
548 res[0].error = &res_error;
549 res[1].buffer_type = MYSQL_TYPE_STRING;
550 res[1].buffer = tags;
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;
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);
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];
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);
573 while (!mysql_stmt_fetch(load_seg)) {
574 uint64_t way_id = d->new_way_id++;
576 fprintf(d->ways, "\"" F_U64 "\",\"%i\",", way_id, user_id);
577 write_csv_col(d->ways, timestamp, '\n');
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);
582 char *tags_it = tags;
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);
590 write_csv_col(d->way_tags, notetmp, '\n');
592 write_csv_col(d->way_tags, v, '\n');
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');
604 mysql_stmt_close(load_seg);
609 static MYSQL *connect_to_mysql(char **argv) {
610 MYSQL *mysql = mysql_init(NULL);
611 if (!mysql) exit_mysql_err(mysql);
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);
617 if (mysql_set_character_set(mysql, "utf8"))
618 exit_mysql_err(mysql);
623 static void open_file(FILE **f, char *fn) {
624 *f = fopen(fn, "w+");
631 int main(int argc, char **argv) {
633 struct data *d = &data;
638 printf("Usage: 008_remove_segments_helper host user passwd database port socket prefix\n");
642 d->mysql = connect_to_mysql(argv);
643 d->mysql2 = connect_to_mysql(argv);
645 prefix_len = strlen(argv[7]);
646 tempfn = (char *) malloc(prefix_len + 15);
647 strcpy(tempfn, argv[7]);
649 strcpy(tempfn + prefix_len, "ways");
650 open_file(&d->ways, tempfn);
652 strcpy(tempfn + prefix_len, "way_nodes");
653 open_file(&d->way_nodes, tempfn);
655 strcpy(tempfn + prefix_len, "way_tags");
656 open_file(&d->way_tags, tempfn);
658 strcpy(tempfn + prefix_len, "relations");
659 open_file(&d->relations, tempfn);
661 strcpy(tempfn + prefix_len, "relation_members");
662 open_file(&d->relation_members, tempfn);
664 strcpy(tempfn + prefix_len, "relation_tags");
665 open_file(&d->relation_tags, tempfn);
673 convert_remaining_segs(d);
675 mysql_close(d->mysql);
676 mysql_close(d->mysql2);
679 fclose(d->way_nodes);
682 fclose(d->relations);
683 fclose(d->relation_members);
684 fclose(d->relation_tags);