13 static T parse(const char *str) {
14 istringstream in(str);
20 static void exit_mysql_err(MYSQL *mysql) {
21 const char *err = mysql_error(mysql);
23 fprintf(stderr, "005_remove_segments_helper: MySQL error: %s\n", err);
25 fprintf(stderr, "005_remove_segments_helper: MySQL error\n");
31 static void exit_stmt_err(MYSQL_STMT *stmt) {
32 const char *err = mysql_stmt_error(stmt);
34 fprintf(stderr, "005_remove_segments_helper: MySQL stmt error: %s\n", err);
36 fprintf(stderr, "005_remove_segments_helper: MySQL stmt error\n");
47 MYSQL *mysql, *mysql2;
49 uint64_t seg_maxid, way_maxid;
51 uint64_t new_relation_id;
55 unsigned char *rem_segs;
57 FILE *ways, *way_nodes, *way_tags,
58 *relations, *relation_members, *relation_tags;
61 static uint64_t select_u64(MYSQL *mysql, const char *q) {
66 if (mysql_query(mysql, q))
67 exit_mysql_err(mysql);
69 res = mysql_store_result(mysql);
70 if (!res) exit_mysql_err(mysql);
72 row = mysql_fetch_row(res);
73 if (!row) exit_mysql_err(mysql);
76 ret = parse<uint64_t>(row[0]);
81 mysql_free_result(res);
86 static void find_maxids(struct data *d) {
87 d->seg_maxid = select_u64(d->mysql, "SELECT max(id) FROM current_segments");
88 d->segs_len = d->seg_maxid + 1;
89 d->way_maxid = select_u64(d->mysql, "SELECT max(id) FROM current_ways");
90 d->new_way_id = d->way_maxid + 1;
91 d->new_relation_id = select_u64(d->mysql, "SELECT max(id) FROM current_relations") + 1;
94 static void populate_segs(struct data *d) {
99 d->segs = (segment *) malloc(sizeof(struct segment) * d->segs_len);
100 memset(d->segs, 0, sizeof(struct segment) * d->segs_len);
102 d->rem_segs = (unsigned char *) malloc(d->segs_len);
103 memset(d->rem_segs, 0, d->segs_len);
105 if (mysql_query(d->mysql, "SELECT id, node_a, node_b "
106 "FROM current_segments WHERE visible"))
107 exit_mysql_err(d->mysql);
109 res = mysql_use_result(d->mysql);
110 if (!res) exit_mysql_err(d->mysql);
112 while (row = mysql_fetch_row(res)) {
113 id = parse<size_t>(row[0]);
114 if (id >= d->segs_len) continue;
115 d->segs[id].from = parse<uint32_t>(row[1]);
116 d->segs[id].to = parse<uint32_t>(row[2]);
119 if (mysql_errno(d->mysql)) exit_mysql_err(d->mysql);
121 mysql_free_result(res);
124 static void write_csv_col(FILE *f, const char *str, char end) {
125 char *out = (char *) malloc(2 * strlen(str) + 4);
130 for (; *str; str++) {
133 } else if (*str == '\"') {
145 if (fwrite(out, len, 1, f) != 1) {
153 static void convert_ways(struct data *d) {
156 MYSQL_STMT *load_segs, *load_tags;
158 load_segs_stmt[] = "SELECT segment_id FROM current_way_segments "
159 "WHERE id = ? ORDER BY sequence_id",
160 load_tags_stmt[] = "SELECT k, v FROM current_way_tags WHERE id = ?";
162 const size_t max_tag_len = 1 << 16;
163 long long mysql_id, mysql_seg_id;
164 unsigned long res_len;
166 MYSQL_BIND bind[1], seg_bind[1], tag_bind[2];
168 /* F***ing libmysql only support fixed size buffers for string results of
169 * prepared statements. So allocate 65k for the tag key and the tag value
170 * and hope it'll suffice. */
171 k = (char *) malloc(max_tag_len);
172 v = (char *) malloc(max_tag_len);
174 load_segs = mysql_stmt_init(d->mysql2);
175 if (!load_segs) exit_mysql_err(d->mysql2);
176 if (mysql_stmt_prepare(load_segs, load_segs_stmt, sizeof(load_segs_stmt)))
177 exit_stmt_err(load_segs);
179 memset(bind, 0, sizeof(bind));
180 bind[0].buffer_type = MYSQL_TYPE_LONGLONG;
181 bind[0].buffer = (char *) &mysql_id;
184 if (mysql_stmt_bind_param(load_segs, bind))
185 exit_stmt_err(load_segs);
187 memset(bind, 0, sizeof(seg_bind));
188 seg_bind[0].buffer_type = MYSQL_TYPE_LONGLONG;
189 seg_bind[0].buffer = (char *) &mysql_seg_id;
190 seg_bind[0].is_null = 0;
191 seg_bind[0].length = 0;
192 seg_bind[0].error = &res_error;
193 if (mysql_stmt_bind_result(load_segs, seg_bind))
194 exit_stmt_err(load_segs);
196 load_tags = mysql_stmt_init(d->mysql2);
197 if (!load_tags) exit_mysql_err(d->mysql2);
198 if (mysql_stmt_prepare(load_tags, load_tags_stmt, sizeof(load_tags_stmt)))
199 exit_stmt_err(load_tags);
201 memset(bind, 0, sizeof(bind));
202 bind[0].buffer_type = MYSQL_TYPE_LONGLONG;
203 bind[0].buffer = (char *) &mysql_id;
207 if (mysql_stmt_bind_param(load_tags, bind))
208 exit_stmt_err(load_tags);
210 memset(bind, 0, sizeof(tag_bind));
211 tag_bind[0].buffer_type = MYSQL_TYPE_STRING;
212 tag_bind[0].buffer = k;
213 tag_bind[0].is_null = 0;
214 tag_bind[0].length = &res_len;
215 tag_bind[0].error = &res_error;
216 tag_bind[0].buffer_length = max_tag_len;
217 tag_bind[1].buffer_type = MYSQL_TYPE_STRING;
218 tag_bind[1].buffer = v;
219 tag_bind[1].is_null = 0;
220 tag_bind[1].length = &res_len;
221 tag_bind[1].error = &res_error;
222 tag_bind[1].buffer_length = max_tag_len;
223 if (mysql_stmt_bind_result(load_tags, tag_bind))
224 exit_stmt_err(load_tags);
226 if (mysql_query(d->mysql, "SELECT id, user_id, timestamp "
227 "FROM current_ways WHERE visible"))
228 exit_mysql_err(d->mysql);
230 res = mysql_use_result(d->mysql);
231 if (!res) exit_mysql_err(d->mysql);
233 while (row = mysql_fetch_row(res)) {
235 const char *user_id, *timestamp;
237 id = parse<uint64_t>(row[0]);
241 mysql_id = (long long) id;
243 if (mysql_stmt_execute(load_segs))
244 exit_stmt_err(load_segs);
246 if (mysql_stmt_store_result(load_segs))
247 exit_stmt_err(load_segs);
250 while (!mysql_stmt_fetch(load_segs)) {
251 if (mysql_seg_id >= d->segs_len) continue;
252 segs.push_back(d->segs[mysql_seg_id]);
253 d->rem_segs[mysql_seg_id] = 0;
256 list<list<uint32_t> > node_lists;
257 while (segs.size()) {
258 list<uint32_t> node_list;
259 node_list.push_back(segs.front().from);
260 node_list.push_back(segs.front().to);
264 for (list<segment>::iterator it = segs.begin();
265 it != segs.end(); ) {
266 if (it->from == node_list.back()) {
267 node_list.push_back(it->to);
270 } else if (it->to == node_list.front()) {
271 node_list.insert(node_list.begin(), it->from);
280 node_lists.push_back(node_list);
283 vector<uint64_t> ids; ids.reserve(node_lists.size());
284 bool orig_id_used = false;
285 for (list<list<uint32_t> >::iterator it = node_lists.begin();
286 it != node_lists.end(); ++it) {
290 way_id = d->new_way_id++;
295 ids.push_back(way_id);
297 fprintf(d->ways, "\"%Lu\",", way_id);
298 write_csv_col(d->ways, user_id, ',');
299 write_csv_col(d->ways, timestamp, '\n');
302 for (list<uint32_t>::iterator nit = it->begin();
303 nit != it->end(); ++nit) {
304 fprintf(d->way_nodes, "\"%Lu\",\"%lu\",\"%i\"\n", way_id, *nit, sid++);
308 if (mysql_stmt_execute(load_tags))
309 exit_stmt_err(load_tags);
311 if (mysql_stmt_store_result(load_tags))
312 exit_stmt_err(load_tags);
314 bool multiple_parts = ids.size() > 1,
315 create_multipolygon = false;
317 while (!mysql_stmt_fetch(load_tags)) {
318 if (multiple_parts && !create_multipolygon) {
319 if (!strcmp(k, "natural")) {
320 if (strcmp(v, "coastline")) {
321 create_multipolygon = true;
323 } else if (!strcmp(k, "waterway")) {
324 if (!strcmp(v, "riverbank")) {
325 create_multipolygon = true;
327 } else if (!strcmp(k, "leisure") || !strcmp(k, "landuse")
328 || !strcmp(k, "sport") || !strcmp(k, "amenity")
329 || !strcmp(k, "tourism") || !strcmp(k, "building")) {
330 create_multipolygon = true;
334 for (vector<uint64_t>::iterator it = ids.begin();
335 it != ids.end(); ++it) {
336 fprintf(d->way_tags, "\"%Lu\",", *it);
337 write_csv_col(d->way_tags, k, ',');
338 write_csv_col(d->way_tags, v, '\n');
342 if (multiple_parts && create_multipolygon) {
343 uint64_t ent_id = d->new_relation_id++;
345 fprintf(d->relations, "\"%Lu\",", ent_id);
346 write_csv_col(d->relations, user_id, ',');
347 write_csv_col(d->relations, timestamp, '\n');
349 fprintf(d->relation_tags,
350 "\"%Lu\",\"type\",\"multipolygon\"\n", ent_id);
352 for (vector<uint64_t>::iterator it = ids.begin();
353 it != ids.end(); ++it) {
354 fprintf(d->relation_members,
355 "\"%Lu\",\"way\",\"%Lu\",\"\"\n", ent_id, *it);
359 if (mysql_errno(d->mysql)) exit_stmt_err(load_tags);
361 mysql_stmt_close(load_segs);
362 mysql_stmt_close(load_tags);
364 mysql_free_result(res);
369 static int read_seg_tags(char **tags, char **k, char **v) {
370 if (!**tags) return 0;
371 char *i = strchr(*tags, ';');
372 if (!i) i = *tags + strlen(*tags);
373 char *j = strchr(*tags, '=');
380 *tags = *i ? i + 1 : i;
386 static void mark_tagged_segs(struct data *d) {
390 if (mysql_query(d->mysql, "SELECT id, tags FROM current_segments "
391 "WHERE visible && tags != '' && tags != 'created_by=JOSM'"))
392 exit_mysql_err(d->mysql);
394 res = mysql_use_result(d->mysql);
395 if (!res) exit_mysql_err(d->mysql);
397 while (row = mysql_fetch_row(res)) {
398 size_t id = parse<size_t>(row[0]);
399 if (d->rem_segs[id]) continue;
400 char *tags_it = row[1], *k, *v;
401 while (read_seg_tags(&tags_it, &k, &v)) {
402 if (!strcmp(k, "created_by")) {
409 mysql_free_result(res);
412 static void convert_remaining_segs(struct data *d) {
413 MYSQL_STMT *load_seg;
414 MYSQL_BIND args[1], res[3];
415 const size_t max_tag_len = 1 << 16;
416 char *tags, timestamp[100];
420 unsigned long res_len;
422 const char load_seg_stmt[] =
423 "SELECT user_id, tags, CAST(timestamp AS CHAR) FROM current_segments "
424 "WHERE visible && id = ?";
426 tags = (char *) malloc(max_tag_len);
428 load_seg = mysql_stmt_init(d->mysql);
429 if (!load_seg) exit_mysql_err(d->mysql);
430 if (mysql_stmt_prepare(load_seg, load_seg_stmt, sizeof(load_seg_stmt)))
431 exit_stmt_err(load_seg);
433 memset(args, 0, sizeof(args));
434 args[0].buffer_type = MYSQL_TYPE_LONGLONG;
435 args[0].buffer = (char *) &mysql_id;
438 if (mysql_stmt_bind_param(load_seg, args))
439 exit_stmt_err(load_seg);
441 memset(res, 0, sizeof(res));
442 res[0].buffer_type = MYSQL_TYPE_LONG;
443 res[0].buffer = (char *) &user_id;
446 res[0].error = &res_error;
447 res[1].buffer_type = MYSQL_TYPE_STRING;
448 res[1].buffer = tags;
450 res[1].length = &res_len;
451 res[1].error = &res_error;
452 res[1].buffer_length = max_tag_len;
453 res[2].buffer_type = MYSQL_TYPE_STRING;
454 res[2].buffer = timestamp;
456 res[2].length = &res_len;
457 res[2].error = &res_error;
458 res[2].buffer_length = sizeof(timestamp);
459 if (mysql_stmt_bind_result(load_seg, res))
460 exit_stmt_err(load_seg);
462 for (size_t seg_id = 0; seg_id < d->segs_len; seg_id++) {
463 if (!d->rem_segs[seg_id]) continue;
464 segment seg = d->segs[seg_id];
467 if (mysql_stmt_execute(load_seg)) exit_stmt_err(load_seg);
468 if (mysql_stmt_store_result(load_seg)) exit_stmt_err(load_seg);
470 while (!mysql_stmt_fetch(load_seg)) {
471 uint64_t way_id = d->new_way_id++;
473 fprintf(d->ways, "\"%Lu\",\"%i\",", way_id, user_id);
474 write_csv_col(d->ways, timestamp, '\n');
476 fprintf(d->way_nodes, "\"%Lu\",\"%lu\",\"%i\"\n", way_id, seg.from, 1);
477 fprintf(d->way_nodes, "\"%Lu\",\"%lu\",\"%i\"\n", way_id, seg.to, 2);
479 char *tags_it = tags;
480 while (read_seg_tags(&tags_it, &k, &v)) {
481 fprintf(d->way_tags, "\"%Lu\",", way_id);
482 write_csv_col(d->way_tags, k, ',');
483 write_csv_col(d->way_tags, v, '\n');
488 mysql_stmt_close(load_seg);
493 static MYSQL *connect_to_mysql(char **argv) {
494 MYSQL *mysql = mysql_init(NULL);
495 if (!mysql) exit_mysql_err(mysql);
497 if (!mysql_real_connect(mysql, argv[1], argv[2], argv[3], argv[4],
498 argv[5][0] ? atoi(argv[5]) : 0, argv[6][0] ? argv[6] : NULL, 0))
499 exit_mysql_err(mysql);
501 if (mysql_set_character_set(mysql, "utf8"))
502 exit_mysql_err(mysql);
507 static void open_file(FILE **f, char *fn) {
508 *f = fopen(fn, "w+");
515 int main(int argc, char **argv) {
517 struct data *d = &data;
522 printf("Usage: 006_remove_segments_helper host user passwd database port socket prefix\n");
526 d->mysql = connect_to_mysql(argv);
527 d->mysql2 = connect_to_mysql(argv);
529 prefix_len = strlen(argv[7]);
530 tempfn = (char *) malloc(prefix_len + 15);
531 strcpy(tempfn, argv[7]);
533 strcpy(tempfn + prefix_len, "ways");
534 open_file(&d->ways, tempfn);
536 strcpy(tempfn + prefix_len, "way_nodes");
537 open_file(&d->way_nodes, tempfn);
539 strcpy(tempfn + prefix_len, "way_tags");
540 open_file(&d->way_tags, tempfn);
542 strcpy(tempfn + prefix_len, "relations");
543 open_file(&d->relations, tempfn);
545 strcpy(tempfn + prefix_len, "relation_members");
546 open_file(&d->relation_members, tempfn);
548 strcpy(tempfn + prefix_len, "relation_tags");
549 open_file(&d->relation_tags, tempfn);
557 convert_remaining_segs(d);
559 mysql_close(d->mysql);
560 mysql_close(d->mysql2);
563 fclose(d->way_nodes);
566 fclose(d->relations);
567 fclose(d->relation_members);
568 fclose(d->relation_tags);