resync from rails_port 11795:12304
authorShaun McDonald <shaun@shaunmcdonald.me.uk>
Fri, 12 Dec 2008 19:29:27 +0000 (19:29 +0000)
committerShaun McDonald <shaun@shaunmcdonald.me.uk>
Fri, 12 Dec 2008 19:29:27 +0000 (19:29 +0000)
1  2 
app/controllers/trace_controller.rb
app/models/user.rb
db/migrate/018_add_timestamp_indexes.rb
db/migrate/019_populate_node_tags_and_remove.rb
db/migrate/019_populate_node_tags_and_remove_helper.c
db/migrate/020_move_to_innodb.rb
db/migrate/021_key_constraints.rb
db/migrate/022_add_changesets.rb
db/migrate/023_order_relation_members.rb
db/migrate/024_add_end_time_to_changesets.rb

@@@ -47,12 -48,12 +48,13 @@@ class TraceController < ApplicationCont
      
      if params[:tag]
        @tag = params[:tag]
-       conditions[0] += " AND EXISTS (SELECT * FROM gpx_file_tags AS gft WHERE gft.gpx_id = gpx_files.id AND gft.tag = ?)"
-       conditions << @tag
+       files = Tracetag.find_all_by_tag(params[:tag]).collect { |tt| tt.gpx_id }
+       conditions[0] += " AND gpx_files.id IN (#{files.join(',')})"
      end
      
 -    conditions[0] += " AND gpx_files.visible = 1"
 +    conditions[0] += " AND gpx_files.visible = ?"
 +    conditions << true
  
      @trace_pages, @traces = paginate(:traces,
                                       :include => [:user, :tags],
@@@ -106,8 -104,17 +106,21 @@@ class User < ActiveRecord::Bas
      return false
    end
  
 +  def trace_public_default
 +    return self.preferences.find(:first, :conditions => {:k => "gps.trace.public", :v => "default"})
 +  end
 +
+   def delete
+     self.active = false
+     self.display_name = "user_#{self.id}"
+     self.description = nil
+     self.home_lat = nil
+     self.home_lon = nil
+     self.image = nil
+     self.email_valid = false
+     self.new_email = nil
+     self.visible = false
+     self.save
+   end
  end
index 2a3f3c9,0000000..8603586
mode 100644,000000..100644
--- /dev/null
@@@ -1,60 -1,0 +1,60 @@@
-       prefix = File.join Dir.tmpdir, "017_populate_node_tags_and_remove.#{$$}."
 +class PopulateNodeTagsAndRemove < ActiveRecord::Migration
 +  def self.up
 +    have_nodes = select_value("SELECT count(*) FROM current_nodes").to_i != 0
 +
 +    if have_nodes
-       cmd = "db/migrate/018_populate_node_tags_and_remove_helper"
++      prefix = File.join Dir.tmpdir, "019_populate_node_tags_and_remove.#{$$}."
 +
++      cmd = "db/migrate/019_populate_node_tags_and_remove_helper"
 +      src = "#{cmd}.c"
 +      if not File.exists? cmd or File.mtime(cmd) < File.mtime(src) then 
 +        system 'cc -O3 -Wall `mysql_config --cflags --libs` ' +
 +          "#{src} -o #{cmd}" or fail
 +      end
 +
 +      conn_opts = ActiveRecord::Base.connection.instance_eval { @connection_options }
 +      args = conn_opts.map { |arg| arg.to_s } + [prefix]
 +      fail "#{cmd} failed" unless system cmd, *args
 +
 +      tempfiles = ['nodes', 'node_tags', 'current_nodes', 'current_node_tags'].
 +        map { |base| prefix + base }
 +      nodes, node_tags, current_nodes, current_node_tags = tempfiles
 +    end
 +
 +    execute "TRUNCATE nodes"
 +    remove_column :nodes, :tags
 +    remove_column :current_nodes, :tags
 +
 +    add_column :nodes, :version, :bigint, :limit => 20, :null => false
 +
 +    create_table :current_node_tags, innodb_table do |t|
 +      t.column :id,          :bigint, :limit => 64, :null => false
 +      t.column :k,         :string, :default => "", :null => false
 +      t.column :v,         :string, :default => "", :null => false
 +    end
 +
 +    create_table :node_tags, innodb_table do |t|
 +      t.column :id,          :bigint, :limit => 64, :null => false
 +      t.column :version,     :bigint, :limit => 20, :null => false
 +      t.column :k,         :string, :default => "", :null => false
 +      t.column :v,         :string, :default => "", :null => false
 +    end
 +
 +    # now get the data back
 +    csvopts = "FIELDS TERMINATED BY ',' ENCLOSED BY '\"' ESCAPED BY '\"' LINES TERMINATED BY '\\n'"
 +
 +    if have_nodes
 +      execute "LOAD DATA INFILE '#{nodes}' INTO TABLE nodes #{csvopts} (id, latitude, longitude, user_id, visible, timestamp, tile, version)";
 +      execute "LOAD DATA INFILE '#{node_tags}' INTO TABLE node_tags #{csvopts} (id, version, k, v)"
 +      execute "LOAD DATA INFILE '#{current_node_tags}' INTO TABLE current_node_tags #{csvopts} (id, k, v)"
 +    end
 +
 +    tempfiles.each { |fn| File.unlink fn } if have_nodes
 +  end
 +
 +  def self.down
 +    raise IrreversibleMigration.new
 +#    add_column :nodes, "tags", :text, :default => "", :null => false
 +#    add_column :current_nodes, "tags", :text, :default => "", :null => false
 +  end
 +end
index 83c1b17,0000000..c41ea33
mode 100644,000000..100644
--- /dev/null
@@@ -1,241 -1,0 +1,241 @@@
-     fprintf(stderr, "018_populate_node_tags_and_remove_helper: MySQL error: %s\n", err);
 +#include <mysql.h>
 +#include <stdint.h>
 +#include <stdio.h>
 +#include <stdlib.h>
 +#include <string.h>
 +
 +static void exit_mysql_err(MYSQL *mysql) {
 +  const char *err = mysql_error(mysql);
 +  if (err) {
-     fprintf(stderr, "018_populate_node_tags_and_remove_helper: MySQL error\n");
++    fprintf(stderr, "019_populate_node_tags_and_remove_helper: MySQL error: %s\n", err);
 +  } else {
-     printf("Usage: 018_populate_node_tags_and_remove_helper host user passwd database port socket prefix\n");
++    fprintf(stderr, "019_populate_node_tags_and_remove_helper: MySQL error\n");
 +  }
 +  abort();
 +  exit(EXIT_FAILURE);
 +}
 +
 +static void write_csv_col(FILE *f, const char *str, char end) {
 +  char *out = (char *) malloc(2 * strlen(str) + 4);
 +  char *o = out;
 +  size_t len;
 +
 +  *(o++) = '\"';
 +  for (; *str; str++) {
 +    if (*str == '\0') {
 +      break;
 +    } else if (*str == '\"') {
 +      *(o++) = '\"';
 +      *(o++) = '\"';
 +    } else {
 +      *(o++) = *str;
 +    }
 +  }
 +  *(o++) = '\"';
 +  *(o++) = end;
 +  *(o++) = '\0';
 +
 +  len = strlen(out);
 +  if (fwrite(out, len, 1, f) != 1) {
 +    perror("fwrite");
 +    exit(EXIT_FAILURE);
 +  }
 +
 +  free(out);
 +}
 +
 +static void unescape(char *str) {
 +  char *i = str, *o = str, tmp;
 +
 +  while (*i) {
 +    if (*i == '\\') {
 +      i++;
 +      switch (tmp = *i++) {
 +        case 's': *o++ = ';'; break;
 +        case 'e': *o++ = '='; break;
 +        case '\\': *o++ = '\\'; break;
 +        default: *o++ = tmp; break;
 +      }
 +    } else {
 +      *o++ = *i++;
 +    }
 +  }
 +}
 +
 +static int read_node_tags(char **tags, char **k, char **v) {
 +  if (!**tags) return 0;
 +  char *i = strchr(*tags, ';');
 +  if (!i) i = *tags + strlen(*tags);
 +  char *j = strchr(*tags, '=');
 +  *k = *tags;
 +  if (j && j < i) {
 +    *v = j + 1;
 +  } else {
 +    *v = i;
 +  }
 +  *tags = *i ? i + 1 : i;
 +  *i = '\0';
 +  if (j) *j = '\0';
 +
 +  unescape(*k);
 +  unescape(*v);
 +
 +  return 1;
 +}
 +
 +struct data {
 +  MYSQL *mysql;
 +  size_t version_size;
 +  uint16_t *version;
 +};
 +
 +static void proc_nodes(struct data *d, const char *tbl, FILE *out, FILE *out_tags, int hist) {
 +  MYSQL_RES *res;
 +  MYSQL_ROW row;
 +  char query[256];
 +
 +  snprintf(query, sizeof(query),  "SELECT id, latitude, longitude, "
 +      "user_id, visible, tags, timestamp, tile FROM %s", tbl);
 +  if (mysql_query(d->mysql, query))
 +    exit_mysql_err(d->mysql);
 +
 +  res = mysql_use_result(d->mysql);
 +  if (!res) exit_mysql_err(d->mysql);
 +
 +  while ((row = mysql_fetch_row(res))) {
 +    unsigned long id = strtoul(row[0], NULL, 10);
 +    uint32_t version;
 +
 +    if (id >= d->version_size) {
 +      fprintf(stderr, "preallocated nodes size exceeded");
 +      abort();
 +    }
 +
 +    if (hist) {
 +      version = ++(d->version[id]);
 +
 +      fprintf(out, "\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%u\"\n",
 +        row[0], row[1], row[2], row[3], row[4], row[6], row[7], version);
 +    } else {
 +      /*fprintf(out, "\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\"\n",
 +      row[0], row[1], row[2], row[3], row[4], row[6], row[7]);*/
 +    }
 +
 +    char *tags_it = row[5], *k, *v;
 +    while (read_node_tags(&tags_it, &k, &v)) {
 +      if (hist) {
 +        fprintf(out_tags, "\"%s\",\"%u\",", row[0], version);
 +      } else {
 +        fprintf(out_tags, "\"%s\",", row[0]);
 +      }
 +
 +      write_csv_col(out_tags, k, ',');
 +      write_csv_col(out_tags, v, '\n');
 +    }
 +  }
 +  if (mysql_errno(d->mysql)) exit_mysql_err(d->mysql);
 +
 +  mysql_free_result(res);
 +}
 +
 +static size_t select_size(MYSQL *mysql, const char *q) {
 +  MYSQL_RES *res;
 +  MYSQL_ROW row;
 +  size_t ret;
 +
 +  if (mysql_query(mysql, q))
 +    exit_mysql_err(mysql);
 +
 +  res = mysql_store_result(mysql);
 +  if (!res) exit_mysql_err(mysql);
 +
 +  row = mysql_fetch_row(res);
 +  if (!row) exit_mysql_err(mysql);
 +
 +  if (row[0]) {
 +    ret = strtoul(row[0], NULL, 10);
 +  } else {
 +    ret = 0;
 +  }
 +
 +  mysql_free_result(res);
 +
 +  return ret;
 +}
 +
 +static MYSQL *connect_to_mysql(char **argv) {
 +  MYSQL *mysql = mysql_init(NULL);
 +  if (!mysql) exit_mysql_err(mysql);
 +
 +  if (!mysql_real_connect(mysql, argv[1], argv[2], argv[3], argv[4],
 +      argv[5][0] ? atoi(argv[5]) : 0, argv[6][0] ? argv[6] : NULL, 0))
 +    exit_mysql_err(mysql);
 +
 +  if (mysql_set_character_set(mysql, "utf8"))
 +    exit_mysql_err(mysql);
 +
 +  return mysql;
 +}
 +
 +static void open_file(FILE **f, char *fn) {
 +  *f = fopen(fn, "w+");
 +  if (!*f) {
 +    perror("fopen");
 +    exit(EXIT_FAILURE);
 +  }
 +}
 +
 +int main(int argc, char **argv) {
 +  size_t prefix_len;
 +  FILE *current_nodes, *current_node_tags, *nodes, *node_tags;
 +  char *tempfn;
 +  struct data data, *d = &data;
 +
 +  if (argc != 8) {
++    printf("Usage: 019_populate_node_tags_and_remove_helper host user passwd database port socket prefix\n");
 +    exit(EXIT_FAILURE);
 +  }
 +
 +  d->mysql = connect_to_mysql(argv);
 +
 +  d->version_size = 1 + select_size(d->mysql, "SELECT max(id) FROM current_nodes");
 +  d->version = (uint16_t *) malloc(sizeof(uint16_t) * d->version_size);
 +  if (!d->version) {
 +    perror("malloc");
 +    abort();
 +    exit(EXIT_FAILURE);
 +  }
 +  memset(d->version, 0, sizeof(uint16_t) * d->version_size);
 +
 +  prefix_len = strlen(argv[7]);
 +  tempfn = (char *) malloc(prefix_len + 32);
 +  strcpy(tempfn, argv[7]);
 +
 +  strcpy(tempfn + prefix_len, "current_nodes");
 +  open_file(&current_nodes, tempfn);
 +
 +  strcpy(tempfn + prefix_len, "current_node_tags");
 +  open_file(&current_node_tags, tempfn);
 +
 +  strcpy(tempfn + prefix_len, "nodes");
 +  open_file(&nodes, tempfn);
 +
 +  strcpy(tempfn + prefix_len, "node_tags");
 +  open_file(&node_tags, tempfn);
 +
 +  free(tempfn);
 +
 +  proc_nodes(d, "nodes", nodes, node_tags, 1);
 +  proc_nodes(d, "current_nodes", current_nodes, current_node_tags, 0);
 +
 +  free(d->version);
 +
 +  mysql_close(d->mysql);
 +
 +  fclose(current_nodes);
 +  fclose(current_node_tags);
 +  fclose(nodes);
 +  fclose(node_tags);
 +
 +  exit(EXIT_SUCCESS);
 +}
index da0488c,0000000..da0488c
mode 100644,000000..100644
--- /dev/null
index 40f98be,0000000..40f98be
mode 100644,000000..100644
--- /dev/null
index e0cf390,0000000..e0cf390
mode 100644,000000..100644
--- /dev/null