# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Mix.Tasks.Pleroma.Database do
alias Pleroma.Object
alias Pleroma.Repo
alias Pleroma.User
+
require Logger
require Pleroma.Constants
+
import Ecto.Query
import Mix.Pleroma
+
use Mix.Task
@shortdoc "A collection of database related tasks"
@moduledoc File.read!("docs/administration/CLI_tasks/database.md")
+ # Rolls back a specific migration (leaving subsequent migrations applied)
+ # Based on https://stackoverflow.com/a/53825840
+ def run(["rollback", version]) do
+ start_pleroma()
+
+ version = String.to_integer(version)
+ re = ~r/^#{version}_.*\.exs/
+ path = Application.app_dir(:pleroma, Path.join(["priv", "repo", "migrations"]))
+
+ result =
+ with {:find, "" <> file} <- {:find, Enum.find(File.ls!(path), &String.match?(&1, re))},
+ {:compile, [{mod, _} | _]} <- {:compile, Code.compile_file(Path.join(path, file))},
+ {:rollback, :ok} <- {:rollback, Ecto.Migrator.down(Repo, version, mod)} do
+ {:ok, "Reversed migration: #{file}"}
+ else
+ {:find, _} -> {:error, "No migration found with version prefix: #{version}"}
+ {:compile, e} -> {:error, "Problem compiling migration module: #{inspect(e)}"}
+ {:rollback, e} -> {:error, "Problem reversing migration: #{inspect(e)}"}
+ e -> {:error, "Something unexpected happened: #{inspect(e)}"}
+ end
+
+ IO.inspect(result)
+ end
+
def run(["remove_embedded_objects" | args]) do
{options, [], []} =
OptionParser.parse(
def run(["update_users_following_followers_counts"]) do
start_pleroma()
- User
- |> Repo.all()
- |> Enum.each(&User.update_follower_count/1)
+ Repo.transaction(
+ fn ->
+ from(u in User, select: u)
+ |> Repo.stream()
+ |> Stream.each(&User.update_follower_count/1)
+ |> Stream.run()
+ end,
+ timeout: :infinity
+ )
end
def run(["prune_objects" | args]) do
where: fragment("(?)->>'likes' is not null", object.data),
select: %{id: object.id, likes: fragment("(?)->>'likes'", object.data)}
)
- |> Pleroma.RepoStreamer.chunk_stream(100)
+ |> Pleroma.Repo.chunk_stream(100, :batches)
|> Stream.each(fn objects ->
ids =
objects
|> where(local: true)
|> where([a], fragment("(? ->> 'type'::text) = 'Create'", a.data))
|> where([_a, o], fragment("?->>'type' = 'Note'", o.data))
- |> Pleroma.RepoStreamer.chunk_stream(100)
+ |> Pleroma.Repo.chunk_stream(100, :batches)
|> Stream.each(fn activities ->
Enum.each(activities, fn activity ->
expires_at =
Pleroma.Workers.PurgeExpiredActivity.enqueue(%{
activity_id: activity.id,
- expires_at: expires_at,
- validate: false
+ expires_at: expires_at
})
end)
end)