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);