Skip to content

Commit

Permalink
Postgres/MyXQL adapters: Do not fail storage_up if the user has acces…
Browse files Browse the repository at this point in the history
…s to an already-created database (#297)

Closes #296
  • Loading branch information
rinpatch committed Jan 11, 2021
1 parent fc6c7a5 commit 051baf6
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 28 deletions.
19 changes: 18 additions & 1 deletion integration_test/myxql/storage_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,11 @@ defmodule Ecto.Integration.StorageTest do
run_mysql("DROP DATABASE #{params()[:database]};")
end

def create_database do
def create_database(grant_privileges_to \\ nil) do
run_mysql("CREATE DATABASE #{params()[:database]};")
if grant_privileges_to do
run_mysql("GRANT ALL PRIVILEGES ON #{params()[:database]}.* to #{grant_privileges_to}")
end
end

def create_posts do
Expand Down Expand Up @@ -73,6 +76,20 @@ defmodule Ecto.Integration.StorageTest do
drop_database()
end

test "storage up with unprivileged user with access to the database" do
unprivileged_params = Keyword.merge(params(),
username: "unprivileged",
password: "pass"
)
run_mysql("CREATE USER unprivileged IDENTIFIED BY 'pass'")
refute Ecto.Adapters.MyXQL.storage_up(unprivileged_params) == :ok
create_database("unprivileged")
assert Ecto.Adapters.MyXQL.storage_up(unprivileged_params) == {:error, :already_up}
after
run_mysql("DROP USER unprivileged")
drop_database()
end

test "structure dump and load" do
create_database()
create_posts()
Expand Down
24 changes: 22 additions & 2 deletions integration_test/pg/storage_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,14 @@ defmodule Ecto.Integration.StorageTest do
run_psql("DROP DATABASE #{params()[:database]};")
end

def create_database do
run_psql("CREATE DATABASE #{params()[:database]};")
def create_database(owner \\ nil) do
query = "CREATE DATABASE #{params()[:database]}"
query = if owner do
query <> " OWNER #{owner};"
else
query <> ";"
end
run_psql(query)
end

def create_posts do
Expand Down Expand Up @@ -74,6 +80,20 @@ defmodule Ecto.Integration.StorageTest do
drop_database()
end

test "storage up with unprivileged user with access to the database" do
unprivileged_params = Keyword.merge(params(),
username: "unprivileged",
password: "pass"
)
run_psql("CREATE USER unprivileged WITH NOCREATEDB PASSWORD 'pass'")
refute Postgres.storage_up(unprivileged_params) == :ok
create_database("unprivileged")
assert Postgres.storage_up(unprivileged_params) == {:error, :already_up}
after
run_psql("DROP USER unprivileged")
drop_database()
end

test "structure dump and load" do
create_database()
create_posts()
Expand Down
30 changes: 18 additions & 12 deletions lib/ecto/adapters/myxql.ex
Original file line number Diff line number Diff line change
Expand Up @@ -159,19 +159,25 @@ defmodule Ecto.Adapters.MyXQL do
opts = Keyword.delete(opts, :database)
charset = opts[:charset] || "utf8mb4"

command =
~s(CREATE DATABASE `#{database}` DEFAULT CHARACTER SET = #{charset})
|> concat_if(opts[:collation], &"DEFAULT COLLATE = #{&1}")

case run_query(command, opts) do
{:ok, _} ->
:ok
{:error, %{mysql: %{name: :ER_DB_CREATE_EXISTS}}} ->
check_existence_command = "SELECT TRUE FROM information_schema.schemata WHERE schema_name = '#{database}'"
case run_query(check_existence_command, opts) do
{:ok, %{num_rows: 1}} ->
{:error, :already_up}
{:error, error} ->
{:error, Exception.message(error)}
{:exit, exit} ->
{:error, exit_to_exception(exit)}
_ ->
create_command =
~s(CREATE DATABASE `#{database}` DEFAULT CHARACTER SET = #{charset})
|> concat_if(opts[:collation], &"DEFAULT COLLATE = #{&1}")

case run_query(create_command, opts) do
{:ok, _} ->
:ok
{:error, %{mysql: %{name: :ER_DB_CREATE_EXISTS}}} ->
{:error, :already_up}
{:error, error} ->
{:error, Exception.message(error)}
{:exit, exit} ->
{:error, exit_to_exception(exit)}
end
end
end

Expand Down
38 changes: 25 additions & 13 deletions lib/ecto/adapters/postgres.ex
Original file line number Diff line number Diff line change
Expand Up @@ -127,25 +127,37 @@ defmodule Ecto.Adapters.Postgres do

@impl true
def storage_up(opts) do
database = Keyword.fetch!(opts, :database) || raise ":database is nil in repository configuration"
database =
Keyword.fetch!(opts, :database) || raise ":database is nil in repository configuration"

encoding = if opts[:encoding] == :unspecified, do: nil, else: opts[:encoding] || "UTF8"
maintenance_database = Keyword.get(opts, :maintenance_database, @default_maintenance_database)
opts = Keyword.put(opts, :database, maintenance_database)

command =
~s(CREATE DATABASE "#{database}")
|> concat_if(encoding, &"ENCODING '#{&1}'")
|> concat_if(opts[:template], &"TEMPLATE=#{&1}")
|> concat_if(opts[:lc_ctype], &"LC_CTYPE='#{&1}'")
|> concat_if(opts[:lc_collate], &"LC_COLLATE='#{&1}'")
check_existence_command = "SELECT FROM pg_database WHERE datname = '#{database}'"

case run_query(command, opts) do
{:ok, _} ->
:ok
{:error, %{postgres: %{code: :duplicate_database}}} ->
case run_query(check_existence_command, opts) do
{:ok, %{num_rows: 1}} ->
{:error, :already_up}
{:error, error} ->
{:error, Exception.message(error)}

_ ->
create_command =
~s(CREATE DATABASE "#{database}")
|> concat_if(encoding, &"ENCODING '#{&1}'")
|> concat_if(opts[:template], &"TEMPLATE=#{&1}")
|> concat_if(opts[:lc_ctype], &"LC_CTYPE='#{&1}'")
|> concat_if(opts[:lc_collate], &"LC_COLLATE='#{&1}'")

case run_query(create_command, opts) do
{:ok, _} ->
:ok

{:error, %{postgres: %{code: :duplicate_database}}} ->
{:error, :already_up}

{:error, error} ->
{:error, Exception.message(error)}
end
end
end

Expand Down

0 comments on commit 051baf6

Please sign in to comment.