Example #1
0
gboolean
ephy_sqlite_connection_table_exists (EphySQLiteConnection *self, const char *table_name)
{
  GError *error = NULL;
  gboolean table_exists = FALSE;

  EphySQLiteStatement *statement = ephy_sqlite_connection_create_statement (self,
                                                                            "SELECT COUNT(type) FROM sqlite_master WHERE type='table' and name=?", &error);
  if (error) {
    g_warning ("Could not detect table existence: %s", error->message);
    g_error_free (error);
    return FALSE;
  }

  ephy_sqlite_statement_bind_string (statement, 0, table_name, &error);
  if (error) {
    g_object_unref (statement);
    g_warning ("Could not detect table existence: %s", error->message);
    g_error_free (error);
    return FALSE;
  }

  ephy_sqlite_statement_step (statement, &error);
  if (error) {
    g_object_unref (statement);
    g_warning ("Could not detect table existence: %s", error->message);
    g_error_free (error);
    return FALSE;
  }

  table_exists = ephy_sqlite_statement_get_column_as_int (statement, 0);
  g_object_unref (statement);
  return table_exists;
}
void
ephy_history_service_add_url_row (EphyHistoryService *self, EphyHistoryURL *url)
{
  EphySQLiteStatement *statement = NULL;
  GError *error = NULL;

  g_assert (self->history_thread == g_thread_self ());
  g_assert (self->history_database != NULL);

  statement = ephy_sqlite_connection_create_statement (self->history_database,
                                                       "INSERT INTO urls (url, title, visit_count, typed_count, last_visit_time, host, sync_id) "
                                                       " VALUES (?, ?, ?, ?, ?, ?, ?)", &error);
  if (error) {
    g_warning ("Could not build urls table addition statement: %s", error->message);
    g_error_free (error);
    return;
  }

  if (ephy_sqlite_statement_bind_string (statement, 0, url->url, &error) == FALSE ||
      ephy_sqlite_statement_bind_string (statement, 1, url->title, &error) == FALSE ||
      ephy_sqlite_statement_bind_int (statement, 2, url->visit_count, &error) == FALSE ||
      ephy_sqlite_statement_bind_int (statement, 3, url->typed_count, &error) == FALSE ||
      ephy_sqlite_statement_bind_int64 (statement, 4, url->last_visit_time, &error) == FALSE ||
      ephy_sqlite_statement_bind_int (statement, 5, url->host->id, &error) == FALSE ||
      ephy_sqlite_statement_bind_string (statement, 6, url->sync_id, &error) == FALSE) {
    g_warning ("Could not insert URL into urls table: %s", error->message);
    g_error_free (error);
    g_object_unref (statement);
    return;
  }

  ephy_sqlite_statement_step (statement, &error);
  if (error) {
    g_warning ("Could not insert URL into urls table: %s", error->message);
    g_error_free (error);
  } else {
    url->id = ephy_sqlite_connection_get_last_insert_id (self->history_database);
  }

  g_object_unref (statement);
}
void
ephy_history_service_add_host_row (EphyHistoryService *self, EphyHistoryHost *host)
{
  EphyHistoryServicePrivate *priv = EPHY_HISTORY_SERVICE (self)->priv;
  EphySQLiteStatement *statement = NULL;
  GError *error = NULL;

  g_assert (priv->history_thread == g_thread_self ());
  g_assert (priv->history_database != NULL);

  statement = ephy_sqlite_connection_create_statement (priv->history_database,
    "INSERT INTO hosts (url, title, visit_count, zoom_level) "
    "VALUES (?, ?, ?, ?)", &error);

  if (error) {
    g_error ("Could not build hosts table addition statement: %s", error->message);
    g_error_free (error);
    return;
  }

  if (ephy_sqlite_statement_bind_string (statement, 0, host->url, &error) == FALSE ||
      ephy_sqlite_statement_bind_string (statement, 1, host->title, &error) == FALSE ||
      ephy_sqlite_statement_bind_int (statement, 2, host->visit_count, &error) == FALSE ||
      ephy_sqlite_statement_bind_double (statement, 3, host->zoom_level, &error) == FALSE) {
    g_error ("Could not insert host into hosts table: %s", error->message);
    g_error_free (error);
    return;
  }

  ephy_sqlite_statement_step (statement, &error);
  if (error) {
    g_error ("Could not insert host into hosts table: %s", error->message);
    g_error_free (error);
    return;
  } else {
    host->id = ephy_sqlite_connection_get_last_insert_id (priv->history_database);
  }

  g_object_unref (statement);
}
void
ephy_history_service_update_host_row (EphyHistoryService *self, EphyHistoryHost *host)
{
  EphySQLiteStatement *statement;
  GError *error = NULL;
  gdouble zoom_level;

  g_assert (self->history_thread == g_thread_self ());
  g_assert (self->history_database != NULL);

  statement = ephy_sqlite_connection_create_statement (self->history_database,
                                                       "UPDATE hosts SET url=?, title=?, visit_count=?, zoom_level=?"
                                                       "WHERE id=?", &error);
  if (error) {
    g_warning ("Could not build hosts table modification statement: %s", error->message);
    g_error_free (error);
    return;
  }

  zoom_level = host->zoom_level;

  if (ephy_sqlite_statement_bind_string (statement, 0, host->url, &error) == FALSE ||
      ephy_sqlite_statement_bind_string (statement, 1, host->title, &error) == FALSE ||
      ephy_sqlite_statement_bind_int (statement, 2, host->visit_count, &error) == FALSE ||
      ephy_sqlite_statement_bind_double (statement, 3, zoom_level, &error) == FALSE ||
      ephy_sqlite_statement_bind_int (statement, 4, host->id, &error) == FALSE) {
    g_warning ("Could not modify host in hosts table: %s", error->message);
    g_error_free (error);
    g_object_unref (statement);
    return;
  }

  ephy_sqlite_statement_step (statement, &error);
  if (error) {
    g_warning ("Could not modify URL in urls table: %s", error->message);
    g_error_free (error);
  }
  g_object_unref (statement);
}
void
ephy_history_service_update_url_row (EphyHistoryService *self, EphyHistoryURL *url)
{
  EphySQLiteStatement *statement;
  GError *error = NULL;

  g_assert (self->history_thread == g_thread_self ());
  g_assert (self->history_database != NULL);

  statement = ephy_sqlite_connection_create_statement (self->history_database,
                                                       "UPDATE urls SET title=?, visit_count=?, typed_count=?, last_visit_time=?, hidden_from_overview=?, sync_id=? "
                                                       "WHERE id=?", &error);
  if (error) {
    g_warning ("Could not build urls table modification statement: %s", error->message);
    g_error_free (error);
    return;
  }

  if (ephy_sqlite_statement_bind_string (statement, 0, url->title, &error) == FALSE ||
      ephy_sqlite_statement_bind_int (statement, 1, url->visit_count, &error) == FALSE ||
      ephy_sqlite_statement_bind_int (statement, 2, url->typed_count, &error) == FALSE ||
      ephy_sqlite_statement_bind_int64 (statement, 3, url->last_visit_time, &error) == FALSE ||
      ephy_sqlite_statement_bind_int (statement, 4, url->hidden, &error) == FALSE ||
      ephy_sqlite_statement_bind_string (statement, 5, url->sync_id, &error) == FALSE ||
      ephy_sqlite_statement_bind_int (statement, 6, url->id, &error) == FALSE) {
    g_warning ("Could not modify URL in urls table: %s", error->message);
    g_error_free (error);
    g_object_unref (statement);
    return;
  }

  ephy_sqlite_statement_step (statement, &error);
  if (error) {
    g_warning ("Could not modify URL in urls table: %s", error->message);
    g_error_free (error);
  }
  g_object_unref (statement);
}
void
ephy_history_service_delete_host_row (EphyHistoryService *self,
                                      EphyHistoryHost *host)
{
  EphyHistoryServicePrivate *priv = EPHY_HISTORY_SERVICE (self)->priv;
  EphySQLiteStatement *statement = NULL;
  gchar *sql_statement;
  GError *error = NULL;

  g_assert (priv->history_thread == g_thread_self ());
  g_assert (priv->history_database != NULL);

  g_assert (host->id != -1 || host->url);

  if (host->id != -1)
    sql_statement = g_strdup ("DELETE FROM hosts WHERE id=?");
  else
    sql_statement = g_strdup ("DELETE FROM hosts WHERE url=?");

  statement = ephy_sqlite_connection_create_statement (priv->history_database,
                                                       sql_statement, &error);
  g_free (sql_statement);

  if (error) {
    g_error ("Could not build urls table query statement: %s", error->message);
    g_error_free (error);
    g_object_unref (statement);
    return;
  }

  if (host->id != -1)
    ephy_sqlite_statement_bind_int (statement, 0, host->id, &error);
  else
    ephy_sqlite_statement_bind_string (statement, 0, host->url, &error);

  if (error) {
    g_error ("Could not build hosts table query statement: %s", error->message);
    g_error_free (error);
    g_object_unref (statement);
    return;
  }

  ephy_sqlite_statement_step (statement, &error);
  if (error) {
    g_error ("Could not modify host in hosts table: %s", error->message);
    g_error_free (error);
  }
  g_object_unref (statement);
}
void
ephy_history_service_delete_url (EphyHistoryService *self, EphyHistoryURL *url)
{
  EphySQLiteStatement *statement = NULL;
  const char *sql_statement;
  GError *error = NULL;

  g_assert (self->history_thread == g_thread_self ());
  g_assert (self->history_database != NULL);

  g_assert (url->id != -1 || url->url);

  if (url->id != -1)
    sql_statement = "DELETE FROM urls WHERE id=?";
  else
    sql_statement = "DELETE FROM urls WHERE url=?";

  statement = ephy_sqlite_connection_create_statement (self->history_database,
                                                       sql_statement, &error);

  if (error) {
    g_warning ("Could not build urls table query statement: %s", error->message);
    g_error_free (error);
    return;
  }

  if (url->id != -1)
    ephy_sqlite_statement_bind_int (statement, 0, url->id, &error);
  else
    ephy_sqlite_statement_bind_string (statement, 0, url->url, &error);

  if (error) {
    g_warning ("Could not build urls table query statement: %s", error->message);
    g_error_free (error);
    g_object_unref (statement);
    return;
  }

  ephy_sqlite_statement_step (statement, &error);
  if (error) {
    g_warning ("Could not modify URL in urls table: %s", error->message);
    g_error_free (error);
  }
  g_object_unref (statement);
}
GList *
ephy_history_service_find_host_rows (EphyHistoryService *self, EphyHistoryQuery *query)
{
  EphySQLiteStatement *statement = NULL;
  GList *substring;
  GString *statement_str;
  GList *hosts = NULL;
  GError *error = NULL;
  const char *base_statement = ""
                               "SELECT "
                               "DISTINCT hosts.id, "
                               "hosts.url, "
                               "hosts.title, "
                               "hosts.visit_count, "
                               "hosts.zoom_level "
                               "FROM "
                               "hosts ";

  int i = 0;

  g_assert (self->history_thread == g_thread_self ());
  g_assert (self->history_database != NULL);

  statement_str = g_string_new (base_statement);

  /* In either of these cases we need to at least join with the urls table. */
  if (query->substring_list || query->from > 0 || query->to > 0)
    statement_str = g_string_append (statement_str, "JOIN urls on hosts.id = urls.host ");

  /* In these cases, we additionally need to join with the visits table. */
  if (query->from > 0 || query->to > 0) {
    statement_str = g_string_append (statement_str, "JOIN visits on urls.id = visits.url WHERE ");
    if (query->from > 0)
      statement_str = g_string_append (statement_str, "visits.visit_time >= ? AND ");
    if (query->to > 0)
      statement_str = g_string_append (statement_str, "visits.visit_time <= ? AND ");
  } else {
    statement_str = g_string_append (statement_str, "WHERE ");
  }

  for (substring = query->substring_list; substring != NULL; substring = substring->next)
    statement_str = g_string_append (statement_str, "(hosts.url LIKE ? OR hosts.title LIKE ? OR "
                                     "urls.url LIKE ? OR urls.title LIKE ?) AND ");

  statement_str = g_string_append (statement_str, "1 ");

  statement = ephy_sqlite_connection_create_statement (self->history_database,
                                                       statement_str->str, &error);
  g_string_free (statement_str, TRUE);

  if (error) {
    g_warning ("Could not build hosts table query statement: %s", error->message);
    g_error_free (error);
    return NULL;
  }
  if (query->from > 0) {
    if (ephy_sqlite_statement_bind_int64 (statement, i++, query->from, &error) == FALSE) {
      g_warning ("Could not build hosts table query statement: %s", error->message);
      g_error_free (error);
      g_object_unref (statement);
      return NULL;
    }
  }
  if (query->to > 0) {
    if (ephy_sqlite_statement_bind_int64 (statement, i++, query->to, &error) == FALSE) {
      g_warning ("Could not build hosts table query statement: %s", error->message);
      g_error_free (error);
      g_object_unref (statement);
      return NULL;
    }
  }
  for (substring = query->substring_list; substring != NULL; substring = substring->next) {
    int j = 4;
    char *string = ephy_sqlite_create_match_pattern (substring->data);
    while (j--)
      /* The bitwise operation ensures we only skip two characters for titles. */
      if (ephy_sqlite_statement_bind_string (statement, i++, string + 2 * ((j + 1) & 1), &error) == FALSE) {
        g_warning ("Could not build hosts table query statement: %s", error->message);
        g_error_free (error);
        g_object_unref (statement);
        g_free (string);
        return NULL;
      }
    g_free (string);
  }

  while (ephy_sqlite_statement_step (statement, &error))
    hosts = g_list_prepend (hosts, create_host_from_statement (statement));

  hosts = g_list_reverse (hosts);

  if (error) {
    g_warning ("Could not execute hosts table query statement: %s", error->message);
    g_error_free (error);
  }
  g_object_unref (statement);

  return hosts;
}
EphyHistoryHost *
ephy_history_service_get_host_row (EphyHistoryService *self, const gchar *host_string, EphyHistoryHost *host)
{
  EphySQLiteStatement *statement = NULL;
  GError *error = NULL;

  g_assert (self->history_thread == g_thread_self ());
  g_assert (self->history_database != NULL);

  if (host_string == NULL && host != NULL)
    host_string = host->url;

  g_assert (host_string || (host != NULL && host->id != -1));

  if (host != NULL && host->id != -1) {
    statement = ephy_sqlite_connection_create_statement (self->history_database,
                                                         "SELECT id, url, title, visit_count, zoom_level FROM hosts "
                                                         "WHERE id=?", &error);
  } else {
    statement = ephy_sqlite_connection_create_statement (self->history_database,
                                                         "SELECT id, url, title, visit_count, zoom_level FROM hosts "
                                                         "WHERE url=?", &error);
  }

  if (error) {
    g_warning ("Could not build hosts query statement: %s", error->message);
    g_error_free (error);
    return NULL;
  }

  if (host != NULL && host->id != -1)
    ephy_sqlite_statement_bind_int (statement, 0, host->id, &error);
  else
    ephy_sqlite_statement_bind_string (statement, 0, host_string, &error);

  if (error) {
    g_warning ("Could not build hosts table query statement: %s", error->message);
    g_error_free (error);
    g_object_unref (statement);
    return NULL;
  }

  if (ephy_sqlite_statement_step (statement, &error) == FALSE) {
    if (error)
      g_error_free (error);
    g_object_unref (statement);
    return NULL;
  }

  if (host == NULL) {
    host = ephy_history_host_new (NULL, NULL, 0, 0.0);
  } else {
    if (host->url)
      g_free (host->url);
    if (host->title)
      g_free (host->title);
  }

  host->id = ephy_sqlite_statement_get_column_as_int (statement, 0);
  host->url = g_strdup (ephy_sqlite_statement_get_column_as_string (statement, 1));
  host->title = g_strdup (ephy_sqlite_statement_get_column_as_string (statement, 2));
  host->visit_count = ephy_sqlite_statement_get_column_as_int (statement, 3);
  host->zoom_level = ephy_sqlite_statement_get_column_as_double (statement, 4);

  g_object_unref (statement);
  return host;
}
EphyHistoryURL *
ephy_history_service_get_url_row (EphyHistoryService *self, const char *url_string, EphyHistoryURL *url)
{
  EphySQLiteStatement *statement = NULL;
  GError *error = NULL;

  g_assert (self->history_thread == g_thread_self ());
  g_assert (self->history_database != NULL);

  if (url_string == NULL && url != NULL)
    url_string = url->url;

  g_assert (url_string || (url != NULL && url->id != -1));

  if (url != NULL && url->id != -1) {
    statement = ephy_sqlite_connection_create_statement (self->history_database,
                                                         "SELECT id, url, title, visit_count, typed_count, last_visit_time, hidden_from_overview, sync_id FROM urls "
                                                         "WHERE id=?", &error);
  } else {
    statement = ephy_sqlite_connection_create_statement (self->history_database,
                                                         "SELECT id, url, title, visit_count, typed_count, last_visit_time, hidden_from_overview, sync_id FROM urls "
                                                         "WHERE url=?", &error);
  }

  if (error) {
    g_warning ("Could not build urls table query statement: %s", error->message);
    g_error_free (error);
    return NULL;
  }

  if (url != NULL && url->id != -1) {
    ephy_sqlite_statement_bind_int (statement, 0, url->id, &error);
  } else {
    ephy_sqlite_statement_bind_string (statement, 0, url_string, &error);
  }
  if (error) {
    g_warning ("Could not build urls table query statement: %s", error->message);
    g_error_free (error);
    g_object_unref (statement);
    return NULL;
  }

  if (ephy_sqlite_statement_step (statement, &error) == FALSE) {
    g_object_unref (statement);
    return NULL;
  }

  if (url == NULL) {
    url = ephy_history_url_new (NULL, NULL, 0, 0, 0);
  }

  url->id = ephy_sqlite_statement_get_column_as_int (statement, 0);

  /* Only get the URL and page title if we don't know it yet, as the version in the
     history could be outdated. */
  if (url->url == NULL)
    url->url = g_strdup (ephy_sqlite_statement_get_column_as_string (statement, 1));
  if (url->title == NULL)
    url->title = g_strdup (ephy_sqlite_statement_get_column_as_string (statement, 2));

  url->visit_count = ephy_sqlite_statement_get_column_as_int (statement, 3),
  url->typed_count = ephy_sqlite_statement_get_column_as_int (statement, 4),
  url->last_visit_time = ephy_sqlite_statement_get_column_as_int64 (statement, 5);
  url->hidden = ephy_sqlite_statement_get_column_as_int (statement, 6);
  url->sync_id = g_strdup (ephy_sqlite_statement_get_column_as_string (statement, 7));

  g_object_unref (statement);
  return url;
}
GList *
ephy_history_service_find_url_rows (EphyHistoryService *self, EphyHistoryQuery *query)
{
  EphySQLiteStatement *statement = NULL;
  GList *substring;
  GString *statement_str;
  GList *urls = NULL;
  GError *error = NULL;
  const char *base_statement = ""
                               "SELECT "
                               "DISTINCT urls.id, "
                               "urls.url, "
                               "urls.title, "
                               "urls.visit_count, "
                               "urls.typed_count, "
                               "urls.last_visit_time, "
                               "urls.hidden_from_overview, "
                               "urls.host, "
                               "urls.sync_id "
                               "FROM "
                               "urls ";

  int i = 0;

  g_assert (self->history_thread == g_thread_self ());
  g_assert (self->history_database != NULL);

  statement_str = g_string_new (base_statement);

  if (query->from > 0 || query->to > 0) {
    statement_str = g_string_append (statement_str, "JOIN visits ON visits.url = urls.id WHERE ");
    if (query->from > 0)
      statement_str = g_string_append (statement_str, "visits.visit_time >= ? AND ");
    if (query->to > 0)
      statement_str = g_string_append (statement_str, "visits.visit_time <= ? AND ");
  } else {
    statement_str = g_string_append (statement_str, "WHERE ");
  }

  if (query->ignore_hidden)
    statement_str = g_string_append (statement_str, "urls.hidden_from_overview = 0 AND ");

  if (query->ignore_local)
    statement_str = g_string_append (statement_str, "urls.url LIKE 'http%' AND ");

  if (query->host > 0)
    statement_str = g_string_append (statement_str, "urls.host = ? AND ");

  for (substring = query->substring_list; substring != NULL; substring = substring->next)
    statement_str = g_string_append (statement_str, "(urls.url LIKE ? OR urls.title LIKE ?) AND ");

  statement_str = g_string_append (statement_str, "1 ");

  switch (query->sort_type) {
    case EPHY_HISTORY_SORT_MOST_VISITED:
      statement_str = g_string_append (statement_str, "ORDER BY urls.visit_count DESC ");
      break;
    case EPHY_HISTORY_SORT_LEAST_VISITED:
      statement_str = g_string_append (statement_str, "ORDER BY urls.visit_count ");
      break;
    case EPHY_HISTORY_SORT_MOST_RECENTLY_VISITED:
      statement_str = g_string_append (statement_str, "ORDER BY urls.last_visit_time DESC ");
      break;
    case EPHY_HISTORY_SORT_LEAST_RECENTLY_VISITED:
      statement_str = g_string_append (statement_str, "ORDER BY urls.last_visit_time ");
      break;
    case EPHY_HISTORY_SORT_TITLE_ASCENDING:
      statement_str = g_string_append (statement_str, "ORDER BY LOWER(urls.title) ");
      break;
    case EPHY_HISTORY_SORT_TITLE_DESCENDING:
      statement_str = g_string_append (statement_str, "ORDER BY LOWER(urls.title) DESC ");
      break;
    case EPHY_HISTORY_SORT_URL_ASCENDING:
      statement_str = g_string_append (statement_str, "ORDER BY LOWER(urls.url) ");
      break;
    case EPHY_HISTORY_SORT_URL_DESCENDING:
      statement_str = g_string_append (statement_str, "ORDER BY LOWER(urls.url) DESC ");
      break;
    case EPHY_HISTORY_SORT_NONE:
    default:
      g_warning ("We don't support this sorting method yet.");
  }

  if (query->limit) {
    statement_str = g_string_append (statement_str, "LIMIT ? ");
  }

  statement = ephy_sqlite_connection_create_statement (self->history_database,
                                                       statement_str->str, &error);
  g_string_free (statement_str, TRUE);

  if (error) {
    g_warning ("Could not build urls table query statement: %s", error->message);
    g_error_free (error);
    return NULL;
  }

  if (query->from > 0) {
    if (ephy_sqlite_statement_bind_int64 (statement, i++, query->from, &error) == FALSE) {
      g_warning ("Could not build urls table query statement: %s", error->message);
      g_error_free (error);
      g_object_unref (statement);
      return NULL;
    }
  }
  if (query->to > 0) {
    if (ephy_sqlite_statement_bind_int64 (statement, i++, query->to, &error) == FALSE) {
      g_warning ("Could not build urls table query statement: %s", error->message);
      g_error_free (error);
      g_object_unref (statement);
      return NULL;
    }
  }
  if (query->host > 0) {
    if (ephy_sqlite_statement_bind_int (statement, i++, (int)query->host, &error) == FALSE) {
      g_warning ("Could not build urls table query statement: %s", error->message);
      g_error_free (error);
      g_object_unref (statement);
      return NULL;
    }
  }
  for (substring = query->substring_list; substring != NULL; substring = substring->next) {
    char *string = ephy_sqlite_create_match_pattern (substring->data);
    if (ephy_sqlite_statement_bind_string (statement, i++, string, &error) == FALSE) {
      g_warning ("Could not build urls table query statement: %s", error->message);
      g_error_free (error);
      g_object_unref (statement);
      g_free (string);
      return NULL;
    }
    if (ephy_sqlite_statement_bind_string (statement, i++, string + 2, &error) == FALSE) {
      g_warning ("Could not build urls table query statement: %s", error->message);
      g_error_free (error);
      g_object_unref (statement);
      g_free (string);
      return NULL;
    }
    g_free (string);
  }

  if (query->limit)
    if (ephy_sqlite_statement_bind_int (statement, i++, query->limit, &error) == FALSE) {
      g_warning ("Could not build urls table query statement: %s", error->message);
      g_error_free (error);
      g_object_unref (statement);
      return NULL;
    }

  while (ephy_sqlite_statement_step (statement, &error))
    urls = g_list_prepend (urls, create_url_from_statement (statement));

  urls = g_list_reverse (urls);

  if (error) {
    g_warning ("Could not execute urls table query statement: %s", error->message);
    g_error_free (error);
    g_object_unref (statement);
    g_list_free_full (urls, (GDestroyNotify)ephy_history_url_free);
    return NULL;
  }

  g_object_unref (statement);
  return urls;
}
GList *
ephy_history_service_find_visit_rows (EphyHistoryService *self, EphyHistoryQuery *query)
{
  EphySQLiteStatement *statement = NULL;
  GList *substring;
  GString *statement_str;
  GList *visits = NULL;
  GError *error = NULL;
  const char *base_statement = ""
                               "SELECT "
                               "visits.url, "
                               "visits.visit_time, "
                               "visits.visit_type ";
  const char *from_join_statement = ""
                                    "FROM "
                                    "visits JOIN urls ON visits.url = urls.id ";
  const char *from_visits_statement = ""
                                      "FROM "
                                      "visits ";

  int i = 0;

  g_assert (self->history_thread == g_thread_self ());
  g_assert (self->history_database != NULL);

  statement_str = g_string_new (base_statement);

  if (query->substring_list)
    statement_str = g_string_append (statement_str, from_join_statement);
  else
    statement_str = g_string_append (statement_str, from_visits_statement);

  statement_str = g_string_append (statement_str, "WHERE ");

  if (query->from >= 0)
    statement_str = g_string_append (statement_str, "visits.visit_time >= ? AND ");
  if (query->to >= 0)
    statement_str = g_string_append (statement_str, "visits.visit_time <= ? AND ");

  if (query->host > 0)
    statement_str = g_string_append (statement_str, "urls.host = ? AND ");

  for (substring = query->substring_list; substring != NULL; substring = substring->next) {
    statement_str = g_string_append (statement_str, "(urls.url LIKE ? OR urls.title LIKE ?) AND ");
  }

  statement_str = g_string_append (statement_str, "1");

  statement = ephy_sqlite_connection_create_statement (self->history_database,
                                                       statement_str->str, &error);
  g_string_free (statement_str, TRUE);

  if (error) {
    g_warning ("Could not build visits table query statement: %s", error->message);
    g_error_free (error);
    return NULL;
  }

  if (query->from >= 0) {
    if (ephy_sqlite_statement_bind_int64 (statement, i++, query->from, &error) == FALSE) {
      g_warning ("Could not build urls table query statement: %s", error->message);
      g_error_free (error);
      g_object_unref (statement);
      return NULL;
    }
  }
  if (query->to >= 0) {
    if (ephy_sqlite_statement_bind_int64 (statement, i++, query->to, &error) == FALSE) {
      g_warning ("Could not build urls table query statement: %s", error->message);
      g_error_free (error);
      g_object_unref (statement);
      return NULL;
    }
  }
  if (query->host > 0) {
    if (ephy_sqlite_statement_bind_int (statement, i++, (int)query->host, &error) == FALSE) {
      g_warning ("Could not build urls table query statement: %s", error->message);
      g_error_free (error);
      g_object_unref (statement);
      return NULL;
    }
  }
  for (substring = query->substring_list; substring != NULL; substring = substring->next) {
    char *string = ephy_sqlite_create_match_pattern (substring->data);
    if (ephy_sqlite_statement_bind_string (statement, i++, string, &error) == FALSE) {
      g_warning ("Could not build urls table query statement: %s", error->message);
      g_error_free (error);
      g_object_unref (statement);
      g_free (string);
      return NULL;
    }
    if (ephy_sqlite_statement_bind_string (statement, i++, string + 2, &error) == FALSE) {
      g_warning ("Could not build urls table query statement: %s", error->message);
      g_error_free (error);
      g_object_unref (statement);
      g_free (string);
      return NULL;
    }
    g_free (string);
  }

  while (ephy_sqlite_statement_step (statement, &error))
    visits = g_list_prepend (visits, create_page_visit_from_statement (statement));

  visits = g_list_reverse (visits);

  if (error) {
    g_warning ("Could not execute visits table query statement: %s", error->message);
    g_error_free (error);
    g_object_unref (statement);
    ephy_history_page_visit_list_free (visits);
    return NULL;
  }

  g_object_unref (statement);
  return visits;
}