Merge api06 branch to trunk.
[rails.git] / db / migrate / 020_populate_node_tags_and_remove_helper.c
1 #include <mysql.h>
2 #include <stdint.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6
7 static void exit_mysql_err(MYSQL *mysql) {
8   const char *err = mysql_error(mysql);
9   if (err) {
10     fprintf(stderr, "019_populate_node_tags_and_remove_helper: MySQL error: %s\n", err);
11   } else {
12     fprintf(stderr, "019_populate_node_tags_and_remove_helper: MySQL error\n");
13   }
14   abort();
15   exit(EXIT_FAILURE);
16 }
17
18 static void write_csv_col(FILE *f, const char *str, char end) {
19   char *out = (char *) malloc(2 * strlen(str) + 4);
20   char *o = out;
21   size_t len;
22
23   *(o++) = '\"';
24   for (; *str; str++) {
25     if (*str == '\0') {
26       break;
27     } else if (*str == '\"') {
28       *(o++) = '\"';
29       *(o++) = '\"';
30     } else {
31       *(o++) = *str;
32     }
33   }
34   *(o++) = '\"';
35   *(o++) = end;
36   *(o++) = '\0';
37
38   len = strlen(out);
39   if (fwrite(out, len, 1, f) != 1) {
40     perror("fwrite");
41     exit(EXIT_FAILURE);
42   }
43
44   free(out);
45 }
46
47 static void unescape(char *str) {
48   char *i = str, *o = str, tmp;
49
50   while (*i) {
51     if (*i == '\\') {
52       i++;
53       switch (tmp = *i++) {
54         case 's': *o++ = ';'; break;
55         case 'e': *o++ = '='; break;
56         case '\\': *o++ = '\\'; break;
57         default: *o++ = tmp; break;
58       }
59     } else {
60       *o++ = *i++;
61     }
62   }
63 }
64
65 static int read_node_tags(char **tags, char **k, char **v) {
66   if (!**tags) return 0;
67   char *i = strchr(*tags, ';');
68   if (!i) i = *tags + strlen(*tags);
69   char *j = strchr(*tags, '=');
70   *k = *tags;
71   if (j && j < i) {
72     *v = j + 1;
73   } else {
74     *v = i;
75   }
76   *tags = *i ? i + 1 : i;
77   *i = '\0';
78   if (j) *j = '\0';
79
80   unescape(*k);
81   unescape(*v);
82
83   return 1;
84 }
85
86 struct data {
87   MYSQL *mysql;
88   size_t version_size;
89   uint16_t *version;
90 };
91
92 static void proc_nodes(struct data *d, const char *tbl, FILE *out, FILE *out_tags, int hist) {
93   MYSQL_RES *res;
94   MYSQL_ROW row;
95   char query[256];
96
97   snprintf(query, sizeof(query),  "SELECT id, latitude, longitude, "
98       "user_id, visible, tags, timestamp, tile FROM %s", tbl);
99   if (mysql_query(d->mysql, query))
100     exit_mysql_err(d->mysql);
101
102   res = mysql_use_result(d->mysql);
103   if (!res) exit_mysql_err(d->mysql);
104
105   while ((row = mysql_fetch_row(res))) {
106     unsigned long id = strtoul(row[0], NULL, 10);
107     uint32_t version;
108
109     if (id >= d->version_size) {
110       fprintf(stderr, "preallocated nodes size exceeded");
111       abort();
112     }
113
114     if (hist) {
115       version = ++(d->version[id]);
116
117       fprintf(out, "\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%u\"\n",
118         row[0], row[1], row[2], row[3], row[4], row[6], row[7], version);
119     } else {
120       /*fprintf(out, "\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\"\n",
121         row[0], row[1], row[2], row[3], row[4], row[6], row[7]);*/
122     }
123
124     char *tags_it = row[5], *k, *v;
125     while (read_node_tags(&tags_it, &k, &v)) {
126       if (hist) {
127         fprintf(out_tags, "\"%s\",\"%u\",", row[0], version);
128       } else {
129         fprintf(out_tags, "\"%s\",", row[0]);
130       }
131
132       write_csv_col(out_tags, k, ',');
133       write_csv_col(out_tags, v, '\n');
134     }
135   }
136   if (mysql_errno(d->mysql)) exit_mysql_err(d->mysql);
137
138   mysql_free_result(res);
139 }
140
141 static size_t select_size(MYSQL *mysql, const char *q) {
142   MYSQL_RES *res;
143   MYSQL_ROW row;
144   size_t ret;
145
146   if (mysql_query(mysql, q))
147     exit_mysql_err(mysql);
148
149   res = mysql_store_result(mysql);
150   if (!res) exit_mysql_err(mysql);
151
152   row = mysql_fetch_row(res);
153   if (!row) exit_mysql_err(mysql);
154
155   if (row[0]) {
156     ret = strtoul(row[0], NULL, 10);
157   } else {
158     ret = 0;
159   }
160
161   mysql_free_result(res);
162
163   return ret;
164 }
165
166 static MYSQL *connect_to_mysql(char **argv) {
167   MYSQL *mysql = mysql_init(NULL);
168   if (!mysql) exit_mysql_err(mysql);
169
170   if (!mysql_real_connect(mysql, argv[1], argv[2], argv[3], argv[4],
171       argv[5][0] ? atoi(argv[5]) : 0, argv[6][0] ? argv[6] : NULL, 0))
172     exit_mysql_err(mysql);
173
174   if (mysql_set_character_set(mysql, "utf8"))
175     exit_mysql_err(mysql);
176
177   return mysql;
178 }
179
180 static void open_file(FILE **f, char *fn) {
181   *f = fopen(fn, "w+");
182   if (!*f) {
183     perror("fopen");
184     exit(EXIT_FAILURE);
185   }
186 }
187
188 int main(int argc, char **argv) {
189   size_t prefix_len;
190   FILE *current_nodes, *current_node_tags, *nodes, *node_tags;
191   char *tempfn;
192   struct data data, *d = &data;
193
194   if (argc != 8) {
195     printf("Usage: 019_populate_node_tags_and_remove_helper host user passwd database port socket prefix\n");
196     exit(EXIT_FAILURE);
197   }
198
199   d->mysql = connect_to_mysql(argv);
200
201   d->version_size = 1 + select_size(d->mysql, "SELECT max(id) FROM current_nodes");
202   d->version = (uint16_t *) malloc(sizeof(uint16_t) * d->version_size);
203   if (!d->version) {
204     perror("malloc");
205     abort();
206     exit(EXIT_FAILURE);
207   }
208   memset(d->version, 0, sizeof(uint16_t) * d->version_size);
209
210   prefix_len = strlen(argv[7]);
211   tempfn = (char *) malloc(prefix_len + 32);
212   strcpy(tempfn, argv[7]);
213
214   strcpy(tempfn + prefix_len, "current_nodes");
215   open_file(&current_nodes, tempfn);
216
217   strcpy(tempfn + prefix_len, "current_node_tags");
218   open_file(&current_node_tags, tempfn);
219
220   strcpy(tempfn + prefix_len, "nodes");
221   open_file(&nodes, tempfn);
222
223   strcpy(tempfn + prefix_len, "node_tags");
224   open_file(&node_tags, tempfn);
225
226   free(tempfn);
227
228   proc_nodes(d, "nodes", nodes, node_tags, 1);
229   proc_nodes(d, "current_nodes", current_nodes, current_node_tags, 0);
230
231   free(d->version);
232
233   mysql_close(d->mysql);
234
235   fclose(current_nodes);
236   fclose(current_node_tags);
237   fclose(nodes);
238   fclose(node_tags);
239
240   exit(EXIT_SUCCESS);
241 }