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;
 
  71   FILE *ways, *way_nodes, *way_tags,
 
  72     *relations, *relation_members, *relation_tags;
 
  75 static uint64_t select_u64(MYSQL *mysql, const char *q) {
 
  80   if (mysql_query(mysql, q))
 
  81     exit_mysql_err(mysql);
 
  83   res = mysql_store_result(mysql);
 
  84   if (!res) exit_mysql_err(mysql);
 
  86   row = mysql_fetch_row(res);
 
  87   if (!row) exit_mysql_err(mysql);
 
  90     ret = parse<uint64_t>(row[0]);
 
  95   mysql_free_result(res);
 
 100 static void find_maxids(struct data *d) {
 
 101   d->seg_maxid = select_u64(d->mysql, "SELECT max(id) FROM current_segments");
 
 102   d->segs_len = d->seg_maxid + 1;
 
 103   d->way_maxid = select_u64(d->mysql, "SELECT max(id) FROM current_ways");
 
 104   d->new_way_id = d->way_maxid + 1;
 
 105   d->new_relation_id = select_u64(d->mysql, "SELECT max(id) FROM current_relations") + 1;
 
 108 static void populate_segs(struct data *d) {
 
 113   d->segs = (segment *) malloc(sizeof(struct segment) * d->segs_len);
 
 114   memset(d->segs, 0, sizeof(struct segment) * d->segs_len);
 
 116   d->rem_segs = (unsigned char *) malloc(d->segs_len);
 
 117   memset(d->rem_segs, 0, d->segs_len);
 
 119   if (mysql_query(d->mysql, "SELECT id, node_a, node_b "
 
 120       "FROM current_segments WHERE visible"))
 
 121     exit_mysql_err(d->mysql);
 
 123   res = mysql_use_result(d->mysql);
 
 124   if (!res) exit_mysql_err(d->mysql);
 
 126   while ((row = mysql_fetch_row(res))) {
 
 127     id = parse<size_t>(row[0]);
 
 128     if (id >= d->segs_len) continue;
 
 129     d->segs[id].from = parse<uint32_t>(row[1]);
 
 130     d->segs[id].to   = parse<uint32_t>(row[2]);
 
 133   if (mysql_errno(d->mysql)) exit_mysql_err(d->mysql);
 
 135   mysql_free_result(res);
 
 138 static void write_csv_col(FILE *f, const char *str, char end) {
 
 139   char *out = (char *) malloc(2 * strlen(str) + 4);
 
 144   for (; *str; str++) {
 
 147     } else if (*str == '\"') {
 
 159   if (fwrite(out, len, 1, f) != 1) {
 
 167 static void convert_ways(struct data *d) {
 
 170   MYSQL_STMT *load_segs, *load_tags;
 
 172     load_segs_stmt[] = "SELECT segment_id FROM current_way_segments "
 
 173       "WHERE id = ? ORDER BY sequence_id",
 
 174     load_tags_stmt[] = "SELECT k, v FROM current_way_tags WHERE id = ?";
 
 176   const size_t max_tag_len = 1 << 16;
 
 177   long long mysql_id, mysql_seg_id;
 
 178   unsigned long res_len;
 
 180   MYSQL_BIND bind[1], seg_bind[1], tag_bind[2];
 
 182   /* F***ing libmysql only support fixed size buffers for string results of
 
 183    * prepared statements.  So allocate 65k for the tag key and the tag value
 
 184    * and hope it'll suffice. */
 
 185   k = (char *) malloc(max_tag_len);
 
 186   v = (char *) malloc(max_tag_len);
 
 188   load_segs = mysql_stmt_init(d->mysql2);
 
 189   if (!load_segs) exit_mysql_err(d->mysql2);
 
 190   if (mysql_stmt_prepare(load_segs, load_segs_stmt, sizeof(load_segs_stmt)))
 
 191     exit_stmt_err(load_segs);
 
 193   memset(bind, 0, sizeof(bind));
 
 194   bind[0].buffer_type = MYSQL_TYPE_LONGLONG;
 
 195   bind[0].buffer = (char *) &mysql_id;
 
 198   if (mysql_stmt_bind_param(load_segs, bind))
 
 199     exit_stmt_err(load_segs);
 
 201   memset(seg_bind, 0, sizeof(seg_bind));
 
 202   seg_bind[0].buffer_type = MYSQL_TYPE_LONGLONG;
 
 203   seg_bind[0].buffer = (char *) &mysql_seg_id;
 
 204   seg_bind[0].is_null = 0;
 
 205   seg_bind[0].length = 0;
 
 206   seg_bind[0].error = &res_error;
 
 207   if (mysql_stmt_bind_result(load_segs, seg_bind))
 
 208     exit_stmt_err(load_segs);
 
 210   load_tags = mysql_stmt_init(d->mysql2);
 
 211   if (!load_tags) exit_mysql_err(d->mysql2);
 
 212   if (mysql_stmt_prepare(load_tags, load_tags_stmt, sizeof(load_tags_stmt)))
 
 213     exit_stmt_err(load_tags);
 
 215   memset(bind, 0, sizeof(bind));
 
 216   bind[0].buffer_type = MYSQL_TYPE_LONGLONG;
 
 217   bind[0].buffer = (char *) &mysql_id;
 
 221   if (mysql_stmt_bind_param(load_tags, bind))
 
 222     exit_stmt_err(load_tags);
 
 224   memset(tag_bind, 0, sizeof(tag_bind));
 
 225   tag_bind[0].buffer_type = MYSQL_TYPE_STRING;
 
 226   tag_bind[0].buffer = k;
 
 227   tag_bind[0].is_null = 0;
 
 228   tag_bind[0].length = &res_len;
 
 229   tag_bind[0].error = &res_error;
 
 230   tag_bind[0].buffer_length = max_tag_len;
 
 231   tag_bind[1].buffer_type = MYSQL_TYPE_STRING;
 
 232   tag_bind[1].buffer = v;
 
 233   tag_bind[1].is_null = 0;
 
 234   tag_bind[1].length = &res_len;
 
 235   tag_bind[1].error = &res_error;
 
 236   tag_bind[1].buffer_length = max_tag_len;
 
 237   if (mysql_stmt_bind_result(load_tags, tag_bind))
 
 238     exit_stmt_err(load_tags);
 
 240   if (mysql_query(d->mysql, "SELECT id, user_id, timestamp "
 
 241       "FROM current_ways WHERE visible"))
 
 242     exit_mysql_err(d->mysql);
 
 244   res = mysql_use_result(d->mysql);
 
 245   if (!res) exit_mysql_err(d->mysql);
 
 247   while ((row = mysql_fetch_row(res))) {
 
 249     const char *user_id, *timestamp;
 
 251     id = parse<uint64_t>(row[0]);
 
 255     mysql_id = (long long) id;
 
 257     if (mysql_stmt_execute(load_segs))
 
 258       exit_stmt_err(load_segs);
 
 260     if (mysql_stmt_store_result(load_segs))
 
 261       exit_stmt_err(load_segs);
 
 264     while (!mysql_stmt_fetch(load_segs)) {
 
 265       if (((uint64_t) mysql_seg_id) >= d->segs_len) continue;
 
 266       segs.push_back(d->segs[mysql_seg_id]);
 
 267       d->rem_segs[mysql_seg_id] = 0;
 
 270     list<list<uint32_t> > node_lists;
 
 271     while (segs.size()) {
 
 272       list<uint32_t> node_list;
 
 273       node_list.push_back(segs.front().from);
 
 274       node_list.push_back(segs.front().to);
 
 278         for (list<segment>::iterator it = segs.begin();
 
 279             it != segs.end(); ) {
 
 280           if (it->from == node_list.back()) {
 
 281             node_list.push_back(it->to);
 
 284           } else if (it->to == node_list.front()) {
 
 285             node_list.insert(node_list.begin(), it->from);
 
 294       node_lists.push_back(node_list);
 
 297     vector<uint64_t> ids; ids.reserve(node_lists.size());
 
 298     bool orig_id_used = false;
 
 299     for (list<list<uint32_t> >::iterator it = node_lists.begin();
 
 300         it != node_lists.end(); ++it) {
 
 304         way_id = d->new_way_id++;
 
 309       ids.push_back(way_id);
 
 311       fprintf(d->ways, "\"" F_U64 "\",", way_id);
 
 312       write_csv_col(d->ways, user_id, ',');
 
 313       write_csv_col(d->ways, timestamp, '\n');
 
 316       for (list<uint32_t>::iterator nit = it->begin();
 
 317           nit != it->end(); ++nit) {
 
 318         fprintf(d->way_nodes, "\"" F_U64 "\",\"" F_U32 "\",\"%i\"\n", way_id, *nit, sid++);
 
 322     if (mysql_stmt_execute(load_tags))
 
 323       exit_stmt_err(load_tags);
 
 325     if (mysql_stmt_store_result(load_tags))
 
 326       exit_stmt_err(load_tags);
 
 328     bool multiple_parts = ids.size() > 1,
 
 329       create_multipolygon = false;
 
 331     while (!mysql_stmt_fetch(load_tags)) {
 
 332       if (multiple_parts && !create_multipolygon) {
 
 333         if (!strcmp(k, "natural")) {
 
 334           if (strcmp(v, "coastline")) {
 
 335             create_multipolygon = true;
 
 337         } else if (!strcmp(k, "waterway")) {
 
 338           if (!strcmp(v, "riverbank")) {
 
 339             create_multipolygon = true;
 
 341         } else if (!strcmp(k, "leisure") || !strcmp(k, "landuse")
 
 342             || !strcmp(k, "sport") || !strcmp(k, "amenity")
 
 343             || !strcmp(k, "tourism") || !strcmp(k, "building")) {
 
 344           create_multipolygon = true;
 
 348       for (vector<uint64_t>::iterator it = ids.begin();
 
 349           it != ids.end(); ++it) {
 
 350         fprintf(d->way_tags, "\"" F_U64 "\",", *it);
 
 351         write_csv_col(d->way_tags, k, ',');
 
 352         write_csv_col(d->way_tags, v, '\n');
 
 356     if (multiple_parts && create_multipolygon) {
 
 357       uint64_t ent_id = d->new_relation_id++;
 
 359       fprintf(d->relations, "\"" F_U64 "\",", ent_id);
 
 360       write_csv_col(d->relations, user_id, ',');
 
 361       write_csv_col(d->relations, timestamp, '\n');
 
 363       fprintf(d->relation_tags,
 
 364         "\"" F_U64 "\",\"type\",\"multipolygon\"\n", ent_id);
 
 366       for (vector<uint64_t>::iterator it = ids.begin();
 
 367           it != ids.end(); ++it) {
 
 368         fprintf(d->relation_members,
 
 369           "\"" F_U64 "\",\"way\",\"" F_U64 "\",\"\"\n", ent_id, *it);
 
 373   if (mysql_errno(d->mysql)) exit_stmt_err(load_tags);
 
 375   mysql_stmt_close(load_segs);
 
 376   mysql_stmt_close(load_tags);
 
 378   mysql_free_result(res);
 
 383 static int read_seg_tags(char **tags, char **k, char **v) {
 
 384   if (!**tags) return 0;
 
 385   char *i = strchr(*tags, ';');
 
 386   if (!i) i = *tags + strlen(*tags);
 
 387   char *j = strchr(*tags, '=');
 
 394   *tags = *i ? i + 1 : i;
 
 400 static void mark_tagged_segs(struct data *d) {
 
 403   MYSQL_STMT *way_tags;
 
 405     way_tags_stmt[] = "SELECT k, v FROM current_way_segments INNER JOIN "
 
 406       "current_way_tags ON current_way_segments.id = "
 
 407       "current_way_tags.id WHERE segment_id = ?";
 
 409   const size_t max_tag_len = 1 << 16;
 
 410   long long mysql_seg_id;
 
 411   unsigned long res_len;
 
 413   MYSQL_BIND in_bind[1], out_bind[1];
 
 415   /* F***ing libmysql only support fixed size buffers for string results of
 
 416    * prepared statements.  So allocate 65k for the tag key and the tag value
 
 417    * and hope it'll suffice. */
 
 418   wk = (char *) malloc(max_tag_len);
 
 419   wv = (char *) malloc(max_tag_len);
 
 421   way_tags = mysql_stmt_init(d->mysql2);
 
 422   if (!way_tags) exit_mysql_err(d->mysql2);
 
 423   if (mysql_stmt_prepare(way_tags, way_tags_stmt, sizeof(way_tags_stmt)))
 
 424     exit_stmt_err(way_tags);
 
 426   memset(in_bind, 0, sizeof(in_bind));
 
 427   in_bind[0].buffer_type = MYSQL_TYPE_LONGLONG;
 
 428   in_bind[0].buffer = (char *) &mysql_seg_id;
 
 429   in_bind[0].is_null = 0;
 
 430   in_bind[0].length = 0;
 
 432   if (mysql_stmt_bind_param(way_tags, in_bind))
 
 433     exit_stmt_err(way_tags);
 
 435   memset(out_bind, 0, sizeof(out_bind));
 
 436   out_bind[0].buffer_type = MYSQL_TYPE_STRING;
 
 437   out_bind[0].buffer = wk;
 
 438   out_bind[0].is_null = 0;
 
 439   out_bind[0].length = &res_len;
 
 440   out_bind[0].error = &res_error;
 
 441   out_bind[0].buffer_length = max_tag_len;
 
 442   out_bind[1].buffer_type = MYSQL_TYPE_STRING;
 
 443   out_bind[1].buffer = wv;
 
 444   out_bind[1].is_null = 0;
 
 445   out_bind[1].length = &res_len;
 
 446   out_bind[1].error = &res_error;
 
 447   out_bind[1].buffer_length = max_tag_len;
 
 448   if (mysql_stmt_bind_result(way_tags, out_bind))
 
 449     exit_stmt_err(way_tags);
 
 451   if (mysql_query(d->mysql, "SELECT id, tags FROM current_segments "
 
 452       "WHERE visible && tags != '' && tags != 'created_by=JOSM'"))
 
 453     exit_mysql_err(d->mysql);
 
 455   res = mysql_use_result(d->mysql);
 
 456   if (!res) exit_mysql_err(d->mysql);
 
 458   while ((row = mysql_fetch_row(res))) {
 
 459     size_t id = parse<size_t>(row[0]);
 
 460     if (d->rem_segs[id]) continue;
 
 462     map<string, string> interesting_tags;
 
 464     char *tags_it = row[1], *k, *v;
 
 465     while (read_seg_tags(&tags_it, &k, &v)) {
 
 466       if (strcmp(k, "created_by") &&
 
 467           strcmp(k, "tiger:county") &&
 
 468           strcmp(k, "tiger:upload_uuid") &&
 
 469           strcmp(k, "converted_by") &&
 
 470           (strcmp(k, "width") || strcmp(v, "4")) &&
 
 471           (strcmp(k, "natural") || strcmp(v, "coastline")) &&
 
 472           (strcmp(k, "source") || strncmp(v, "PGS", 3))) {
 
 473         interesting_tags.insert(make_pair(string(k), string(v)));
 
 477     if (interesting_tags.size() == 0) continue;
 
 481     if (mysql_stmt_execute(way_tags))
 
 482       exit_stmt_err(way_tags);
 
 484     if (mysql_stmt_store_result(way_tags))
 
 485       exit_stmt_err(way_tags);
 
 487     while (!mysql_stmt_fetch(way_tags)) {
 
 488       for (map<string, string>::iterator it = interesting_tags.find(wk);
 
 489           it != interesting_tags.end() && it->first == wk; ++it) {
 
 490         if (it->second == wv) {
 
 491           interesting_tags.erase(it);
 
 497     if (interesting_tags.size() > 0) {
 
 502   mysql_free_result(res);
 
 504   mysql_stmt_close(way_tags);
 
 509 static void convert_remaining_segs(struct data *d) {
 
 510   MYSQL_STMT *load_seg;
 
 511   MYSQL_BIND args[1], res[3];
 
 512   const size_t max_tag_len = 1 << 16;
 
 513   char *tags, timestamp[100];
 
 517   unsigned long res_len;
 
 519   const char load_seg_stmt[] =
 
 520     "SELECT user_id, tags, CAST(timestamp AS CHAR) FROM current_segments "
 
 521     "WHERE visible && id = ?";
 
 523   tags = (char *) malloc(max_tag_len);
 
 525   load_seg = mysql_stmt_init(d->mysql);
 
 526   if (!load_seg) exit_mysql_err(d->mysql);
 
 527   if (mysql_stmt_prepare(load_seg, load_seg_stmt, sizeof(load_seg_stmt)))
 
 528     exit_stmt_err(load_seg);
 
 530   memset(args, 0, sizeof(args));
 
 531   args[0].buffer_type = MYSQL_TYPE_LONGLONG;
 
 532   args[0].buffer = (char *) &mysql_id;
 
 535   if (mysql_stmt_bind_param(load_seg, args))
 
 536     exit_stmt_err(load_seg);
 
 538   memset(res, 0, sizeof(res));
 
 539   res[0].buffer_type = MYSQL_TYPE_LONG;
 
 540   res[0].buffer = (char *) &user_id;
 
 543   res[0].error = &res_error;
 
 544   res[1].buffer_type = MYSQL_TYPE_STRING;
 
 545   res[1].buffer = tags;
 
 547   res[1].length = &res_len;
 
 548   res[1].error = &res_error;
 
 549   res[1].buffer_length = max_tag_len;
 
 550   res[2].buffer_type = MYSQL_TYPE_STRING;
 
 551   res[2].buffer = timestamp;
 
 553   res[2].length = &res_len;
 
 554   res[2].error = &res_error;
 
 555   res[2].buffer_length = sizeof(timestamp);
 
 556   if (mysql_stmt_bind_result(load_seg, res))
 
 557     exit_stmt_err(load_seg);
 
 559   for (size_t seg_id = 0; seg_id < d->segs_len; seg_id++) {
 
 560     if (!d->rem_segs[seg_id]) continue;
 
 561     segment seg = d->segs[seg_id];
 
 564     if (mysql_stmt_execute(load_seg)) exit_stmt_err(load_seg);
 
 565     if (mysql_stmt_store_result(load_seg)) exit_stmt_err(load_seg);
 
 567     while (!mysql_stmt_fetch(load_seg)) {
 
 568       uint64_t way_id = d->new_way_id++;
 
 570       fprintf(d->ways, "\"" F_U64 "\",\"%i\",", way_id, user_id);
 
 571       write_csv_col(d->ways, timestamp, '\n');
 
 573       fprintf(d->way_nodes, "\"" F_U64 "\",\"" F_U32 "\",\"%i\"\n", way_id, seg.from, 1);
 
 574       fprintf(d->way_nodes, "\"" F_U64 "\",\"" F_U32 "\",\"%i\"\n", way_id, seg.to, 2);
 
 576       char *tags_it = tags;
 
 577       while (read_seg_tags(&tags_it, &k, &v)) {
 
 578         fprintf(d->way_tags, "\"" F_U64 "\",", way_id);
 
 579         write_csv_col(d->way_tags, k, ',');
 
 580         write_csv_col(d->way_tags, v, '\n');
 
 585   mysql_stmt_close(load_seg);
 
 590 static MYSQL *connect_to_mysql(char **argv) {
 
 591   MYSQL *mysql = mysql_init(NULL);
 
 592   if (!mysql) exit_mysql_err(mysql);
 
 594   if (!mysql_real_connect(mysql, argv[1], argv[2], argv[3], argv[4],
 
 595       argv[5][0] ? atoi(argv[5]) : 0, argv[6][0] ? argv[6] : NULL, 0))
 
 596     exit_mysql_err(mysql);
 
 598   if (mysql_set_character_set(mysql, "utf8"))
 
 599     exit_mysql_err(mysql);
 
 604 static void open_file(FILE **f, char *fn) {
 
 605   *f = fopen(fn, "w+");
 
 612 int main(int argc, char **argv) {
 
 614   struct data *d = &data;
 
 619     printf("Usage: 008_remove_segments_helper host user passwd database port socket prefix\n");
 
 623   d->mysql = connect_to_mysql(argv);
 
 624   d->mysql2 = connect_to_mysql(argv);
 
 626   prefix_len = strlen(argv[7]);
 
 627   tempfn = (char *) malloc(prefix_len + 15);
 
 628   strcpy(tempfn, argv[7]);
 
 630   strcpy(tempfn + prefix_len, "ways");
 
 631   open_file(&d->ways, tempfn);
 
 633   strcpy(tempfn + prefix_len, "way_nodes");
 
 634   open_file(&d->way_nodes, tempfn);
 
 636   strcpy(tempfn + prefix_len, "way_tags");
 
 637   open_file(&d->way_tags, tempfn);
 
 639   strcpy(tempfn + prefix_len, "relations");
 
 640   open_file(&d->relations, tempfn);
 
 642   strcpy(tempfn + prefix_len, "relation_members");
 
 643   open_file(&d->relation_members, tempfn);
 
 645   strcpy(tempfn + prefix_len, "relation_tags");
 
 646   open_file(&d->relation_tags, tempfn);
 
 654   convert_remaining_segs(d);
 
 656   mysql_close(d->mysql);
 
 657   mysql_close(d->mysql2);
 
 660   fclose(d->way_nodes);
 
 663   fclose(d->relations);
 
 664   fclose(d->relation_members);
 
 665   fclose(d->relation_tags);