Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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