From cc1e2634754c43e41cb1ca5dd7f7422f6040edcf Mon Sep 17 00:00:00 2001
From: HammamSamara <h.samara.74@gmail.com>
Date: Wed, 5 May 2021 02:07:41 +0300
Subject: [PATCH 01/10] depend on information schema for mysql

---
 README.md                                     | 13 ---
 config/test.exs                               |  1 +
 lib/mix/tasks/triplex.migrate.ex              |  2 +-
 lib/mix/tasks/triplex.mysql.install.ex        | 67 ----------------
 lib/mix/tasks/triplex.rollback.ex             |  2 +-
 lib/triplex.ex                                | 79 ++++++++-----------
 lib/triplex/config.ex                         | 11 ++-
 test/mix/tasks/triplex.mysql.install_test.exs | 68 ----------------
 8 files changed, 42 insertions(+), 201 deletions(-)
 delete mode 100644 lib/mix/tasks/triplex.mysql.install.ex
 delete mode 100644 test/mix/tasks/triplex.mysql.install_test.exs

diff --git a/README.md b/README.md
index 43330b2..6d738e3 100644
--- a/README.md
+++ b/README.md
@@ -40,19 +40,6 @@ Configure the Repo you will use to execute the database commands with:
 
     config :triplex, repo: ExampleApp.Repo
 
-### Additional configuration for MySQL
-
-In MySQL, each tenant will have its own MySQL database.
-Triplex uses a table called `tenants` in the main Repo to keep track of the different tenants.
-Generate the migration that will create the table by running:
-
-	mix triplex.mysql.install
-
-And then create the table:
-
-	mix ecto.migrate
-
-
 ## Usage
 
 Here is a quick overview of what you can do with triplex!
diff --git a/config/test.exs b/config/test.exs
index 0c6daa7..e8aa247 100644
--- a/config/test.exs
+++ b/config/test.exs
@@ -9,6 +9,7 @@ config :triplex,
     "security",
     "app",
     "staging",
+    "triplex_test",
     ~r/^db\d+$/
   ]
 
diff --git a/lib/mix/tasks/triplex.migrate.ex b/lib/mix/tasks/triplex.migrate.ex
index a9c7347..8a3ffa2 100644
--- a/lib/mix/tasks/triplex.migrate.ex
+++ b/lib/mix/tasks/triplex.migrate.ex
@@ -61,7 +61,7 @@ defmodule Mix.Tasks.Triplex.Migrate do
     * `--log-sql` - log the raw sql migrations are running
     * `--strict-version-order` - abort when applying a migration with old timestamp
     * `--no-compile` - does not compile applications before migrating
-    * `--no-deps-check` - does not check depedendencies before migrating
+    * `--no-deps-check` - does not check dependencies before migrating
 
   ## PS
 
diff --git a/lib/mix/tasks/triplex.mysql.install.ex b/lib/mix/tasks/triplex.mysql.install.ex
deleted file mode 100644
index 1aa0a02..0000000
--- a/lib/mix/tasks/triplex.mysql.install.ex
+++ /dev/null
@@ -1,67 +0,0 @@
-defmodule Mix.Tasks.Triplex.Mysql.Install do
-  @moduledoc """
-  Generates a migration to create the tenant table
-  in the default database (MySQL only).
-  """
-
-  use Mix.Task
-
-  require Mix.Generator
-
-  alias Ecto.Adapters.MyXQL
-  alias Ecto.Migrator
-  alias Mix.Ecto
-  alias Mix.Generator
-  alias Mix.Project
-
-  @migration_name "create_tenant"
-
-  @shortdoc "Generates a migration for the tenant table in the default database"
-
-  @doc false
-  def run(args) do
-    Ecto.no_umbrella!("ecto.gen.migration")
-    repos = Ecto.parse_repo(args)
-
-    Enum.each(repos, fn repo ->
-      Ecto.ensure_repo(repo, args)
-
-      if repo.__adapter__ != MyXQL do
-        Mix.raise("the tenant table only makes sense for MySQL repositories")
-      end
-
-      path = Path.relative_to(Migrator.migrations_path(repo), Project.app_path())
-      file = Path.join(path, "#{timestamp()}_#{@migration_name}.exs")
-      Generator.create_directory(path)
-
-      Generator.create_file(
-        file,
-        migration_template(
-          repo: repo,
-          migration_name: @migration_name,
-          tenant_table: Triplex.config().tenant_table
-        )
-      )
-    end)
-  end
-
-  defp timestamp do
-    {{y, m, d}, {hh, mm, ss}} = :calendar.universal_time()
-    "#{y}#{pad(m)}#{pad(d)}#{pad(hh)}#{pad(mm)}#{pad(ss)}"
-  end
-
-  defp pad(i), do: i |> to_string() |> String.pad_leading(2, "0")
-
-  Generator.embed_template(:migration, """
-  defmodule <%= Module.concat([@repo, Migrations, Macro.camelize(@migration_name)]) %> do
-    use Ecto.Migration
-
-    def change do
-      create table(:<%= @tenant_table %>) do
-        add :name, :string
-      end
-      create unique_index(:<%= @tenant_table %>, [:name])
-    end
-  end
-  """)
-end
diff --git a/lib/mix/tasks/triplex.rollback.ex b/lib/mix/tasks/triplex.rollback.ex
index 7fc4cff..a4442e7 100644
--- a/lib/mix/tasks/triplex.rollback.ex
+++ b/lib/mix/tasks/triplex.rollback.ex
@@ -57,7 +57,7 @@ defmodule Mix.Tasks.Triplex.Rollback do
     * `--pool-size` - the pool size if the repository is started only for the task (defaults to 1)
     * `--log-sql` - log the raw sql migrations are running
     * `--no-compile` - does not compile applications before rolling back
-    * `--no-deps-check` - does not check depedendencies before rolling back
+    * `--no-deps-check` - does not check dependencies before rolling back
 
   ## PS
 
diff --git a/lib/triplex.ex b/lib/triplex.ex
index bc132b2..de2ae5e 100644
--- a/lib/triplex.ex
+++ b/lib/triplex.ex
@@ -2,7 +2,7 @@ defmodule Triplex do
   @moduledoc """
   This is the main module of Triplex.
 
-  The main objetive of it is to make a little bit easier to manage tenants
+  The main objective of it is to make a little bit easier to manage tenants
   through postgres db schemas or equivalents, executing queries and commands
   inside and outside the tenant without much boilerplate code.
 
@@ -15,7 +15,7 @@ defmodule Triplex do
 
       Repo.all(User, prefix: Triplex.to_prefix("my_tenant"))
 
-  It's a good idea to call `Triplex.to_prefix` on your tenant name, altough is
+  It's a good idea to call `Triplex.to_prefix` on your tenant name, although is
   not required. Because, if you configured a `tenant_prefix`, this function will
   return the prefixed one.
   """
@@ -30,7 +30,7 @@ defmodule Triplex do
   def config, do: struct(Triplex.Config, Application.get_all_env(:triplex))
 
   @doc """
-  Returns the list of reserverd tenants.
+  Returns the list of reserved tenants.
 
   By default, there are some limitations for the name of a tenant depending on
   the database, like "public" or anything that start with "pg_".
@@ -47,6 +47,9 @@ defmodule Triplex do
       nil,
       "public",
       "information_schema",
+      "performance_schema",
+      "sys",
+      "mysql",
       ~r/^pg_/
       | config().reserved_tenants
     ]
@@ -137,7 +140,7 @@ defmodule Triplex do
 
   After creating it successfully, the given `func` callback is called with
   the `tenant` and the `repo` as arguments. The `func` must return
-  `{:ok, any}` if successfull or `{:error, reason}` otherwise. In the case
+  `{:ok, any}` if successful or `{:error, reason}` otherwise. In the case
   the `func` fails, this func will rollback the created schema and
   fail with the same `reason`.
 
@@ -147,18 +150,24 @@ defmodule Triplex do
     if reserved_tenant?(tenant) do
       {:error, reserved_message(tenant)}
     else
+      charset = config().opts[:charset]
+      collate = config().opts[:collate]
+
       sql =
         case repo.__adapter__ do
-          Ecto.Adapters.MyXQL -> "CREATE DATABASE #{to_prefix(tenant)}"
-          Ecto.Adapters.Postgres -> "CREATE SCHEMA \"#{to_prefix(tenant)}\""
+          Ecto.Adapters.MyXQL ->
+            "CREATE DATABASE #{to_prefix(tenant)} DEFAULT CHARSET #{charset} COLLATE #{collate}"
+
+          Ecto.Adapters.Postgres ->
+            "CREATE SCHEMA \"#{to_prefix(tenant)}\""
         end
 
       case SQL.query(repo, sql, []) do
         {:ok, _} ->
-          with {:ok, _} <- add_to_tenants_table(tenant, repo),
-               {:ok, _} <- exec_func(func, tenant, repo) do
-            {:ok, tenant}
-          else
+          case exec_func(func, tenant, repo) do
+            {:ok, _} ->
+              {:ok, tenant}
+
             {:error, reason} ->
               drop(tenant, repo)
               {:error, error_message(reason)}
@@ -178,27 +187,6 @@ defmodule Triplex do
     end
   end
 
-  defp add_to_tenants_table(tenant, repo) do
-    case repo.__adapter__ do
-      Ecto.Adapters.MyXQL ->
-        sql = "INSERT INTO #{Triplex.config().tenant_table} (name) VALUES (?)"
-        SQL.query(repo, sql, [tenant])
-
-      Ecto.Adapters.Postgres ->
-        {:ok, :skipped}
-    end
-  end
-
-  defp remove_from_tenants_table(tenant, repo) do
-    case repo.__adapter__ do
-      Ecto.Adapters.MyXQL ->
-        SQL.query(repo, "DELETE FROM #{Triplex.config().tenant_table} WHERE NAME = ?", [tenant])
-
-      Ecto.Adapters.Postgres ->
-        {:ok, :skipped}
-    end
-  end
-
   defp exec_func(nil, tenant, _) do
     {:ok, tenant}
   end
@@ -227,10 +215,10 @@ defmodule Triplex do
           Ecto.Adapters.Postgres -> "DROP SCHEMA \"#{to_prefix(tenant)}\" CASCADE"
         end
 
-      with {:ok, _} <- SQL.query(repo, sql, []),
-           {:ok, _} <- remove_from_tenants_table(tenant, repo) do
-        {:ok, tenant}
-      else
+      case SQL.query(repo, sql, []) do
+        {:ok, _} ->
+          {:ok, tenant}
+
         {:error, exception} ->
           {:error, error_message(exception)}
       end
@@ -274,17 +262,10 @@ defmodule Triplex do
   Returns all the tenants on the given `repo`.
   """
   def all(repo \\ config().repo) do
-    sql =
-      case repo.__adapter__ do
-        Ecto.Adapters.MyXQL ->
-          "SELECT name FROM #{config().tenant_table}"
-
-        Ecto.Adapters.Postgres ->
-          """
-          SELECT schema_name
-          FROM information_schema.schemata
-          """
-      end
+    sql = """
+    SELECT schema_name
+    FROM information_schema.schemata
+    """
 
     %{rows: result} = SQL.query!(repo, sql, [])
 
@@ -305,7 +286,11 @@ defmodule Triplex do
       sql =
         case repo.__adapter__ do
           Ecto.Adapters.MyXQL ->
-            "SELECT COUNT(*) FROM #{config().tenant_table} WHERE name = ?"
+            """
+            SELECT COUNT(*)
+            FROM information_schema.schemata
+            WHERE schema_name = ?
+            """
 
           Ecto.Adapters.Postgres ->
             """
diff --git a/lib/triplex/config.ex b/lib/triplex/config.ex
index 7b781ac..75b8c33 100644
--- a/lib/triplex/config.ex
+++ b/lib/triplex/config.ex
@@ -5,9 +5,9 @@ defmodule Triplex.Config do
   - `repo`: the ecto repo that will be used to execute the schema operations.
   - `tenant_prefix`: a prefix for all tenants.
   - `reserved_tenants`: a list of reserved tenants, which cannot be created
-  thourhg triplex APIs. The items here can be strings or regexes.
-  - `tenant_field`: an atom with the name of the field to get the tenant name
-  if the given tenant is a struct. By default it's `:id`.
+  through triplex APIs. The items here can be strings or regexes.
+  - `opts`: extra options to supply for the create database query for MySQL driver
+  supported options are `charset` and `collate`.
   """
 
   defstruct [
@@ -16,6 +16,9 @@ defmodule Triplex.Config do
     migrations_path: "tenant_migrations",
     reserved_tenants: [],
     tenant_field: :id,
-    tenant_table: :tenants
+    opts: [
+      charset: "utf8mb4",
+      collate: "utf8mb4_bin"
+    ]
   ]
 end
diff --git a/test/mix/tasks/triplex.mysql.install_test.exs b/test/mix/tasks/triplex.mysql.install_test.exs
deleted file mode 100644
index 98e9db6..0000000
--- a/test/mix/tasks/triplex.mysql.install_test.exs
+++ /dev/null
@@ -1,68 +0,0 @@
-defmodule Mix.Tasks.Triplex.Mysql.InstallTest do
-  use ExUnit.Case, async: true
-
-  import Support.FileHelpers
-
-  alias Mix.Tasks.Triplex.Mysql.Install
-
-  tmp_path = Path.join(tmp_path(), inspect(Install))
-  @migrations_path Path.join(tmp_path, "migrations")
-
-  defmodule MySQLRepo do
-    def __adapter__ do
-      Ecto.Adapters.MyXQL
-    end
-
-    def config do
-      [priv: "tmp/#{inspect(Install)}", otp_app: :triplex]
-    end
-  end
-
-  defmodule PGRepo do
-    def __adapter__ do
-      Ecto.Adapters.Postgres
-    end
-
-    def config do
-      [priv: "tmp/#{inspect(Triplex.MySQL.Install)}", otp_app: :triplex]
-    end
-  end
-
-  setup do
-    File.rm_rf!(unquote(tmp_path))
-
-    Mix.shell(Mix.Shell.Process)
-
-    on_exit(fn ->
-      Mix.shell(Mix.Shell.IO)
-    end)
-
-    :ok
-  end
-
-  test "generates a migration to install mysql" do
-    Install.run(["-r", to_string(MySQLRepo)])
-    assert [name] = File.ls!(@migrations_path)
-    assert String.match?(name, ~r/^\d{14}_create_tenant\.exs$/)
-
-    assert_file(Path.join(@migrations_path, name), fn file ->
-      assert file =~ """
-             defmodule Elixir.Mix.Tasks.Triplex.Mysql.InstallTest.MySQLRepo.Migrations.CreateTenant do
-             """
-
-      assert file =~ "use Ecto.Migration"
-      assert file =~ "def change do"
-      assert file =~ "create table(:tenants) do"
-      assert file =~ "add :name, :string"
-      assert file =~ "create unique_index(:tenants, [:name])"
-    end)
-  end
-
-  test "raises an exception for non mysql repos" do
-    msg = "the tenant table only makes sense for MySQL repositories"
-
-    assert_raise Mix.Error, msg, fn ->
-      Install.run(["-r", to_string(PGRepo)])
-    end
-  end
-end

From 8c6fef865e466f06c77d693961d9227663230139 Mon Sep 17 00:00:00 2001
From: HammamSamara <h.samara.74@gmail.com>
Date: Wed, 5 May 2021 11:56:39 +0300
Subject: [PATCH 02/10] better test

---
 lib/mix/tasks/triplex.gen.migration.ex | 2 +-
 lib/triplex/plugs/session_plug.ex      | 2 +-
 lib/triplex/plugs/subdomain_plug.ex    | 2 +-
 test/mix/triplex_test.exs              | 2 +-
 test/triplex/triplex_test.exs          | 3 ++-
 5 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/lib/mix/tasks/triplex.gen.migration.ex b/lib/mix/tasks/triplex.gen.migration.ex
index db64670..0710d2e 100644
--- a/lib/mix/tasks/triplex.gen.migration.ex
+++ b/lib/mix/tasks/triplex.gen.migration.ex
@@ -3,8 +3,8 @@ defmodule Mix.Tasks.Triplex.Gen.Migration do
 
   require Mix.Generator
 
-  alias Mix.Project
   alias Mix.Generator
+  alias Mix.Project
 
   @shortdoc "Generates a new tenant migration for the repo"
 
diff --git a/lib/triplex/plugs/session_plug.ex b/lib/triplex/plugs/session_plug.ex
index 1e7efd1..7baccb5 100644
--- a/lib/triplex/plugs/session_plug.ex
+++ b/lib/triplex/plugs/session_plug.ex
@@ -15,8 +15,8 @@ if Code.ensure_loaded?(Plug) do
 
     alias Plug.Conn
 
-    alias Triplex.SessionPlugConfig
     alias Triplex.Plug
+    alias Triplex.SessionPlugConfig
 
     @doc false
     def init(opts), do: struct(SessionPlugConfig, opts)
diff --git a/lib/triplex/plugs/subdomain_plug.ex b/lib/triplex/plugs/subdomain_plug.ex
index 249b83b..7edc225 100644
--- a/lib/triplex/plugs/subdomain_plug.ex
+++ b/lib/triplex/plugs/subdomain_plug.ex
@@ -15,8 +15,8 @@ if Code.ensure_loaded?(Plug) do
 
     alias Plug.Conn
 
-    alias Triplex.SubdomainPlugConfig
     alias Triplex.Plug
+    alias Triplex.SubdomainPlugConfig
 
     @doc false
     def init(opts), do: struct(SubdomainPlugConfig, opts)
diff --git a/test/mix/triplex_test.exs b/test/mix/triplex_test.exs
index 448c87c..764f988 100644
--- a/test/mix/triplex_test.exs
+++ b/test/mix/triplex_test.exs
@@ -1,5 +1,5 @@
 defmodule Mix.TriplexTest do
-  use ExUnit.Case, async: true
+  use ExUnit.Case, async: false
 
   @repos [Triplex.PGTestRepo, Triplex.MSTestRepo]
 
diff --git a/test/triplex/triplex_test.exs b/test/triplex/triplex_test.exs
index d96f824..0c79e25 100644
--- a/test/triplex/triplex_test.exs
+++ b/test/triplex/triplex_test.exs
@@ -91,7 +91,8 @@ defmodule TriplexTest do
       Triplex.create("lala", repo)
       Triplex.create("lili", repo)
       Triplex.create("lolo", repo)
-      assert MapSet.new(Triplex.all(repo)) == MapSet.new(["lala", "lili", "lolo"])
+
+      assert MapSet.subset?(MapSet.new(["lala", "lili", "lolo"]), MapSet.new(Triplex.all(repo)))
     end
   end
 

From 27503e39f6fe723235eb38cb170315edf8f7233d Mon Sep 17 00:00:00 2001
From: HammamSamara <h.samara.74@gmail.com>
Date: Wed, 5 May 2021 12:14:59 +0300
Subject: [PATCH 03/10] add travis to reserved tenants

---
 config/test.exs               | 1 +
 test/mix/triplex_test.exs     | 2 +-
 test/triplex/triplex_test.exs | 2 +-
 3 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/config/test.exs b/config/test.exs
index e8aa247..60053d9 100644
--- a/config/test.exs
+++ b/config/test.exs
@@ -10,6 +10,7 @@ config :triplex,
     "app",
     "staging",
     "triplex_test",
+    "travis",
     ~r/^db\d+$/
   ]
 
diff --git a/test/mix/triplex_test.exs b/test/mix/triplex_test.exs
index 764f988..448c87c 100644
--- a/test/mix/triplex_test.exs
+++ b/test/mix/triplex_test.exs
@@ -1,5 +1,5 @@
 defmodule Mix.TriplexTest do
-  use ExUnit.Case, async: false
+  use ExUnit.Case, async: true
 
   @repos [Triplex.PGTestRepo, Triplex.MSTestRepo]
 
diff --git a/test/triplex/triplex_test.exs b/test/triplex/triplex_test.exs
index 0c79e25..07fbb5f 100644
--- a/test/triplex/triplex_test.exs
+++ b/test/triplex/triplex_test.exs
@@ -92,7 +92,7 @@ defmodule TriplexTest do
       Triplex.create("lili", repo)
       Triplex.create("lolo", repo)
 
-      assert MapSet.subset?(MapSet.new(["lala", "lili", "lolo"]), MapSet.new(Triplex.all(repo)))
+      assert MapSet.new(Triplex.all(repo)) == MapSet.new(["lala", "lili", "lolo"])
     end
   end
 

From df9b642ffc4cf9f0910cf5b1de488ed2dffb052f Mon Sep 17 00:00:00 2001
From: HammamSamara <h.samara.74@gmail.com>
Date: Mon, 10 May 2021 14:20:28 +0300
Subject: [PATCH 04/10] secure database name for mysql

---
 lib/triplex.ex | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/triplex.ex b/lib/triplex.ex
index de2ae5e..f8b012f 100644
--- a/lib/triplex.ex
+++ b/lib/triplex.ex
@@ -156,7 +156,7 @@ defmodule Triplex do
       sql =
         case repo.__adapter__ do
           Ecto.Adapters.MyXQL ->
-            "CREATE DATABASE #{to_prefix(tenant)} DEFAULT CHARSET #{charset} COLLATE #{collate}"
+            "CREATE DATABASE `#{to_prefix(tenant)}` DEFAULT CHARSET #{charset} COLLATE #{collate}"
 
           Ecto.Adapters.Postgres ->
             "CREATE SCHEMA \"#{to_prefix(tenant)}\""

From c3f4518098551365674a4263a1cc6d6d269753a8 Mon Sep 17 00:00:00 2001
From: HammamSamara <h.samara.74@gmail.com>
Date: Tue, 11 May 2021 11:33:56 +0300
Subject: [PATCH 05/10] secure database name for mysql on drop

---
 lib/triplex.ex | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/triplex.ex b/lib/triplex.ex
index f8b012f..d53a806 100644
--- a/lib/triplex.ex
+++ b/lib/triplex.ex
@@ -211,7 +211,7 @@ defmodule Triplex do
     else
       sql =
         case repo.__adapter__ do
-          Ecto.Adapters.MyXQL -> "DROP DATABASE #{to_prefix(tenant)}"
+          Ecto.Adapters.MyXQL -> "DROP DATABASE `#{to_prefix(tenant)}`"
           Ecto.Adapters.Postgres -> "DROP SCHEMA \"#{to_prefix(tenant)}\" CASCADE"
         end
 

From d9071dbbd845f117cf2e9c6a8c787ff9ff0fe641 Mon Sep 17 00:00:00 2001
From: HammamSamara <h.samara.74@gmail.com>
Date: Fri, 14 May 2021 19:11:47 +0300
Subject: [PATCH 06/10] rename opts to mysql opts

---
 lib/triplex.ex                             | 4 ++--
 lib/triplex/config.ex                      | 5 +++--
 lib/triplex/plugs/subdomain_plug_config.ex | 2 +-
 3 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/lib/triplex.ex b/lib/triplex.ex
index d53a806..5decf59 100644
--- a/lib/triplex.ex
+++ b/lib/triplex.ex
@@ -150,8 +150,8 @@ defmodule Triplex do
     if reserved_tenant?(tenant) do
       {:error, reserved_message(tenant)}
     else
-      charset = config().opts[:charset]
-      collate = config().opts[:collate]
+      charset = config().mysql[:charset]
+      collate = config().mysql[:collate]
 
       sql =
         case repo.__adapter__ do
diff --git a/lib/triplex/config.ex b/lib/triplex/config.ex
index 75b8c33..3356384 100644
--- a/lib/triplex/config.ex
+++ b/lib/triplex/config.ex
@@ -6,7 +6,8 @@ defmodule Triplex.Config do
   - `tenant_prefix`: a prefix for all tenants.
   - `reserved_tenants`: a list of reserved tenants, which cannot be created
   through triplex APIs. The items here can be strings or regexes.
-  - `opts`: extra options to supply for the create database query for MySQL driver
+  - `mysql`: extra options to supply for the create database query for MySQL driver.
+  The SQL standard allows a DEFAULT CHARACTER SET clause in CREATE SCHEMA than are presently accepted by PostgreSQL.
   supported options are `charset` and `collate`.
   """
 
@@ -16,7 +17,7 @@ defmodule Triplex.Config do
     migrations_path: "tenant_migrations",
     reserved_tenants: [],
     tenant_field: :id,
-    opts: [
+    mysql: [
       charset: "utf8mb4",
       collate: "utf8mb4_bin"
     ]
diff --git a/lib/triplex/plugs/subdomain_plug_config.ex b/lib/triplex/plugs/subdomain_plug_config.ex
index 08784c5..2c6a43e 100644
--- a/lib/triplex/plugs/subdomain_plug_config.ex
+++ b/lib/triplex/plugs/subdomain_plug_config.ex
@@ -7,7 +7,7 @@ defmodule Triplex.SubdomainPlugConfig do
   - `tenant_handler`: function to handle the tenant param. Its return will
   be used as the tenant.
   - `assign`: the name of the assign where we must save the tenant.
-  - `endpoint`: the Phoenix.Endpoint to get the host name to dicover the
+  - `endpoint`: the Phoenix.Endpoint to get the host name to discover the
   subdomain.
   """
 

From b28909f176028ddd9d2ef3d2899a76e89c4cf4ba Mon Sep 17 00:00:00 2001
From: HammamSamara <h.samara.74@gmail.com>
Date: Fri, 14 May 2021 20:33:00 +0300
Subject: [PATCH 07/10] backward compatability for tenants table

---
 README.md                                     | 20 +++++-
 config/test.exs                               |  2 +
 lib/mix/tasks/triplex.mysql.install.ex        | 66 +++++++++++++++++
 lib/triplex.ex                                | 71 ++++++++++++++-----
 lib/triplex/config.ex                         |  3 +
 test/mix/tasks/triplex.mysql.install_test.exs | 68 ++++++++++++++++++
 test/triplex/triplex_test.exs                 |  1 -
 7 files changed, 212 insertions(+), 19 deletions(-)
 create mode 100644 lib/mix/tasks/triplex.mysql.install.ex
 create mode 100644 test/mix/tasks/triplex.mysql.install_test.exs

diff --git a/README.md b/README.md
index 6d738e3..eab0c16 100644
--- a/README.md
+++ b/README.md
@@ -40,12 +40,30 @@ Configure the Repo you will use to execute the database commands with:
 
     config :triplex, repo: ExampleApp.Repo
 
+### Additional configuration for MySQL
+
+In MySQL, each tenant will have its own MySQL database.
+Triplex used to use a table called `tenants` in the main Repo to keep track of the different tenants.
+If you wish to keep this behavior, generate the migration that will create the table by running:
+
+    mix triplex.mysql.install
+
+And then create the table:
+
+	  mix ecto.migrate
+
+Finally, configure Triplex to use the `tenants` table:
+
+    config :triplex, tenant_table: :tenants
+
+Otherwise, Triplex will continue to use the `information_schema.schemata` table as the default behavior for storing tenants.
+
 ## Usage
 
 Here is a quick overview of what you can do with triplex!
 
 
-### Creating, renaming and droping tenants
+### Creating, renaming and dropping tenants
 
 
 #### To create a new tenant:
diff --git a/config/test.exs b/config/test.exs
index 60053d9..daf61e9 100644
--- a/config/test.exs
+++ b/config/test.exs
@@ -14,6 +14,8 @@ config :triplex,
     ~r/^db\d+$/
   ]
 
+config :triplex, tenant_table: :tenants
+
 # Configure your database
 config :triplex, ecto_repos: [Triplex.PGTestRepo, Triplex.MSTestRepo]
 
diff --git a/lib/mix/tasks/triplex.mysql.install.ex b/lib/mix/tasks/triplex.mysql.install.ex
new file mode 100644
index 0000000..345d3b4
--- /dev/null
+++ b/lib/mix/tasks/triplex.mysql.install.ex
@@ -0,0 +1,66 @@
+defmodule Mix.Tasks.Triplex.Mysql.Install do
+  @moduledoc """
+  Generates a migration to create the tenant table
+  in the default database (MySQL only).
+  """
+
+  use Mix.Task
+
+  require Mix.Generator
+
+  alias Ecto.Adapters.MyXQL
+  alias Ecto.Migrator
+  alias Mix.Ecto
+  alias Mix.Generator
+  alias Mix.Project
+
+  @migration_name "create_tenant"
+
+  @shortdoc "Generates a migration for the tenant table in the default database"
+
+  @doc false
+  def run(args) do
+    Ecto.no_umbrella!("ecto.gen.migration")
+    repos = Ecto.parse_repo(args)
+
+    Enum.each(repos, fn repo ->
+      Ecto.ensure_repo(repo, args)
+
+      if repo.__adapter__ != MyXQL do
+        Mix.raise("the tenant table only makes sense for MySQL repositories")
+      end
+
+      path = Path.relative_to(Migrator.migrations_path(repo), Project.app_path())
+      file = Path.join(path, "#{timestamp()}_#{@migration_name}.exs")
+      Generator.create_directory(path)
+
+      Generator.create_file(
+        file,
+        migration_template(
+          repo: repo,
+          migration_name: @migration_name,
+          tenant_table: Triplex.config().tenant_table
+        )
+      )
+    end)
+  end
+
+  defp timestamp do
+    {{y, m, d}, {hh, mm, ss}} = :calendar.universal_time()
+    "#{y}#{pad(m)}#{pad(d)}#{pad(hh)}#{pad(mm)}#{pad(ss)}"
+  end
+
+  defp pad(i), do: i |> to_string() |> String.pad_leading(2, "0")
+
+  Generator.embed_template(:migration, """
+  defmodule <%= Module.concat([@repo, Migrations, Macro.camelize(@migration_name)]) %> do
+    use Ecto.Migration
+    def change do
+      create table(:<%= @tenant_table %>) do
+        add :name, :string
+      end
+      create unique_index(:<%= @tenant_table %>, [:name])
+    end
+  end
+  """)
+end
diff --git a/lib/triplex.ex b/lib/triplex.ex
index 5decf59..97eaaae 100644
--- a/lib/triplex.ex
+++ b/lib/triplex.ex
@@ -164,10 +164,10 @@ defmodule Triplex do
 
       case SQL.query(repo, sql, []) do
         {:ok, _} ->
-          case exec_func(func, tenant, repo) do
-            {:ok, _} ->
-              {:ok, tenant}
-
+          with {:ok, _} <- add_to_tenants_table(tenant, repo),
+               {:ok, _} <- exec_func(func, tenant, repo) do
+            {:ok, tenant}
+          else
             {:error, reason} ->
               drop(tenant, repo)
               {:error, error_message(reason)}
@@ -187,6 +187,35 @@ defmodule Triplex do
     end
   end
 
+  defp add_to_tenants_table(tenant, repo) do
+    case repo.__adapter__ do
+      Ecto.Adapters.MyXQL ->
+        if Triplex.config().tenant_table == :"information_schema.schemata" do
+          {:ok, :skipped}
+        else
+          sql = "INSERT INTO #{Triplex.config().tenant_table} (name) VALUES (?)"
+          SQL.query(repo, sql, [tenant])
+        end
+
+      Ecto.Adapters.Postgres ->
+        {:ok, :skipped}
+    end
+  end
+
+  defp remove_from_tenants_table(tenant, repo) do
+    case repo.__adapter__ do
+      Ecto.Adapters.MyXQL ->
+        if Triplex.config().tenant_table == :"information_schema.schemata" do
+          {:ok, :skipped}
+        else
+          SQL.query(repo, "DELETE FROM #{Triplex.config().tenant_table} WHERE NAME = ?", [tenant])
+        end
+
+      Ecto.Adapters.Postgres ->
+        {:ok, :skipped}
+    end
+  end
+
   defp exec_func(nil, tenant, _) do
     {:ok, tenant}
   end
@@ -215,10 +244,10 @@ defmodule Triplex do
           Ecto.Adapters.Postgres -> "DROP SCHEMA \"#{to_prefix(tenant)}\" CASCADE"
         end
 
-      case SQL.query(repo, sql, []) do
-        {:ok, _} ->
-          {:ok, tenant}
-
+      with {:ok, _} <- SQL.query(repo, sql, []),
+           {:ok, _} <- remove_from_tenants_table(tenant, repo) do
+        {:ok, tenant}
+      else
         {:error, exception} ->
           {:error, error_message(exception)}
       end
@@ -262,10 +291,22 @@ defmodule Triplex do
   Returns all the tenants on the given `repo`.
   """
   def all(repo \\ config().repo) do
-    sql = """
-    SELECT schema_name
-    FROM information_schema.schemata
-    """
+    sql =
+      case repo.__adapter__ do
+        Ecto.Adapters.MyXQL ->
+          field_name =
+            if Triplex.config().tenant_table == :"information_schema.schemata",
+              do: "schema_name",
+              else: "name"
+
+          "SELECT #{field_name} FROM `#{config().tenant_table}`"
+
+        Ecto.Adapters.Postgres ->
+          """
+          SELECT schema_name
+          FROM information_schema.schemata
+          """
+      end
 
     %{rows: result} = SQL.query!(repo, sql, [])
 
@@ -286,11 +327,7 @@ defmodule Triplex do
       sql =
         case repo.__adapter__ do
           Ecto.Adapters.MyXQL ->
-            """
-            SELECT COUNT(*)
-            FROM information_schema.schemata
-            WHERE schema_name = ?
-            """
+            "SELECT COUNT(*) FROM `#{config().tenant_table}` WHERE name = ?"
 
           Ecto.Adapters.Postgres ->
             """
diff --git a/lib/triplex/config.ex b/lib/triplex/config.ex
index 3356384..b1a728c 100644
--- a/lib/triplex/config.ex
+++ b/lib/triplex/config.ex
@@ -6,6 +6,8 @@ defmodule Triplex.Config do
   - `tenant_prefix`: a prefix for all tenants.
   - `reserved_tenants`: a list of reserved tenants, which cannot be created
   through triplex APIs. The items here can be strings or regexes.
+  - `tenant_field`: an atom with the name of the field to get the tenant name
+  if the given tenant is a struct. By default it's `:id`.
   - `mysql`: extra options to supply for the create database query for MySQL driver.
   The SQL standard allows a DEFAULT CHARACTER SET clause in CREATE SCHEMA than are presently accepted by PostgreSQL.
   supported options are `charset` and `collate`.
@@ -17,6 +19,7 @@ defmodule Triplex.Config do
     migrations_path: "tenant_migrations",
     reserved_tenants: [],
     tenant_field: :id,
+    tenant_table: :"information_schema.schemata",
     mysql: [
       charset: "utf8mb4",
       collate: "utf8mb4_bin"
diff --git a/test/mix/tasks/triplex.mysql.install_test.exs b/test/mix/tasks/triplex.mysql.install_test.exs
new file mode 100644
index 0000000..98e9db6
--- /dev/null
+++ b/test/mix/tasks/triplex.mysql.install_test.exs
@@ -0,0 +1,68 @@
+defmodule Mix.Tasks.Triplex.Mysql.InstallTest do
+  use ExUnit.Case, async: true
+
+  import Support.FileHelpers
+
+  alias Mix.Tasks.Triplex.Mysql.Install
+
+  tmp_path = Path.join(tmp_path(), inspect(Install))
+  @migrations_path Path.join(tmp_path, "migrations")
+
+  defmodule MySQLRepo do
+    def __adapter__ do
+      Ecto.Adapters.MyXQL
+    end
+
+    def config do
+      [priv: "tmp/#{inspect(Install)}", otp_app: :triplex]
+    end
+  end
+
+  defmodule PGRepo do
+    def __adapter__ do
+      Ecto.Adapters.Postgres
+    end
+
+    def config do
+      [priv: "tmp/#{inspect(Triplex.MySQL.Install)}", otp_app: :triplex]
+    end
+  end
+
+  setup do
+    File.rm_rf!(unquote(tmp_path))
+
+    Mix.shell(Mix.Shell.Process)
+
+    on_exit(fn ->
+      Mix.shell(Mix.Shell.IO)
+    end)
+
+    :ok
+  end
+
+  test "generates a migration to install mysql" do
+    Install.run(["-r", to_string(MySQLRepo)])
+    assert [name] = File.ls!(@migrations_path)
+    assert String.match?(name, ~r/^\d{14}_create_tenant\.exs$/)
+
+    assert_file(Path.join(@migrations_path, name), fn file ->
+      assert file =~ """
+             defmodule Elixir.Mix.Tasks.Triplex.Mysql.InstallTest.MySQLRepo.Migrations.CreateTenant do
+             """
+
+      assert file =~ "use Ecto.Migration"
+      assert file =~ "def change do"
+      assert file =~ "create table(:tenants) do"
+      assert file =~ "add :name, :string"
+      assert file =~ "create unique_index(:tenants, [:name])"
+    end)
+  end
+
+  test "raises an exception for non mysql repos" do
+    msg = "the tenant table only makes sense for MySQL repositories"
+
+    assert_raise Mix.Error, msg, fn ->
+      Install.run(["-r", to_string(PGRepo)])
+    end
+  end
+end
diff --git a/test/triplex/triplex_test.exs b/test/triplex/triplex_test.exs
index 07fbb5f..d96f824 100644
--- a/test/triplex/triplex_test.exs
+++ b/test/triplex/triplex_test.exs
@@ -91,7 +91,6 @@ defmodule TriplexTest do
       Triplex.create("lala", repo)
       Triplex.create("lili", repo)
       Triplex.create("lolo", repo)
-
       assert MapSet.new(Triplex.all(repo)) == MapSet.new(["lala", "lili", "lolo"])
     end
   end

From c841cc10ea8e7b1c31cec43a3e381a9e6be09bb4 Mon Sep 17 00:00:00 2001
From: HammamSamara <h.samara.74@gmail.com>
Date: Fri, 14 May 2021 21:00:00 +0300
Subject: [PATCH 08/10] set default tenant_table as :tenants

---
 README.md             | 12 +++++-------
 config/test.exs       |  2 --
 lib/triplex/config.ex |  2 +-
 3 files changed, 6 insertions(+), 10 deletions(-)

diff --git a/README.md b/README.md
index eab0c16..e97d1ce 100644
--- a/README.md
+++ b/README.md
@@ -43,20 +43,18 @@ Configure the Repo you will use to execute the database commands with:
 ### Additional configuration for MySQL
 
 In MySQL, each tenant will have its own MySQL database.
-Triplex used to use a table called `tenants` in the main Repo to keep track of the different tenants.
-If you wish to keep this behavior, generate the migration that will create the table by running:
+Triplex uses a table called `tenants` in the main Repo to keep track of the different tenants.
+Generate the migration that will create the table by running:
 
     mix triplex.mysql.install
 
 And then create the table:
 
-	  mix ecto.migrate
+    mix ecto.migrate
 
-Finally, configure Triplex to use the `tenants` table:
+Otherwise, if you wish to skip this behavior, configure Triplex to use the default `information_schema.schemata` table:
 
-    config :triplex, tenant_table: :tenants
-
-Otherwise, Triplex will continue to use the `information_schema.schemata` table as the default behavior for storing tenants.
+    config :triplex, tenant_table: :"information_schema.schemata"
 
 ## Usage
 
diff --git a/config/test.exs b/config/test.exs
index daf61e9..60053d9 100644
--- a/config/test.exs
+++ b/config/test.exs
@@ -14,8 +14,6 @@ config :triplex,
     ~r/^db\d+$/
   ]
 
-config :triplex, tenant_table: :tenants
-
 # Configure your database
 config :triplex, ecto_repos: [Triplex.PGTestRepo, Triplex.MSTestRepo]
 
diff --git a/lib/triplex/config.ex b/lib/triplex/config.ex
index b1a728c..409d320 100644
--- a/lib/triplex/config.ex
+++ b/lib/triplex/config.ex
@@ -19,7 +19,7 @@ defmodule Triplex.Config do
     migrations_path: "tenant_migrations",
     reserved_tenants: [],
     tenant_field: :id,
-    tenant_table: :"information_schema.schemata",
+    tenant_table: :tenants,
     mysql: [
       charset: "utf8mb4",
       collate: "utf8mb4_bin"

From 41ff67a7234df31e64c65a42844d04d55dc69466 Mon Sep 17 00:00:00 2001
From: HammamSamara <h.samara.74@gmail.com>
Date: Fri, 21 May 2021 17:02:37 +0300
Subject: [PATCH 09/10] drop mysql default character set to be introduced in
 different pr

---
 lib/mix/tasks/triplex.mysql.install.ex |  1 +
 lib/triplex.ex                         | 14 ++++----------
 lib/triplex/config.ex                  |  9 +--------
 3 files changed, 6 insertions(+), 18 deletions(-)

diff --git a/lib/mix/tasks/triplex.mysql.install.ex b/lib/mix/tasks/triplex.mysql.install.ex
index 345d3b4..1aa0a02 100644
--- a/lib/mix/tasks/triplex.mysql.install.ex
+++ b/lib/mix/tasks/triplex.mysql.install.ex
@@ -55,6 +55,7 @@ defmodule Mix.Tasks.Triplex.Mysql.Install do
   Generator.embed_template(:migration, """
   defmodule <%= Module.concat([@repo, Migrations, Macro.camelize(@migration_name)]) %> do
     use Ecto.Migration
+
     def change do
       create table(:<%= @tenant_table %>) do
         add :name, :string
diff --git a/lib/triplex.ex b/lib/triplex.ex
index 97eaaae..9c9cd24 100644
--- a/lib/triplex.ex
+++ b/lib/triplex.ex
@@ -150,16 +150,10 @@ defmodule Triplex do
     if reserved_tenant?(tenant) do
       {:error, reserved_message(tenant)}
     else
-      charset = config().mysql[:charset]
-      collate = config().mysql[:collate]
-
       sql =
         case repo.__adapter__ do
-          Ecto.Adapters.MyXQL ->
-            "CREATE DATABASE `#{to_prefix(tenant)}` DEFAULT CHARSET #{charset} COLLATE #{collate}"
-
-          Ecto.Adapters.Postgres ->
-            "CREATE SCHEMA \"#{to_prefix(tenant)}\""
+          Ecto.Adapters.MyXQL -> "CREATE DATABASE `#{to_prefix(tenant)}`"
+          Ecto.Adapters.Postgres -> "CREATE SCHEMA \"#{to_prefix(tenant)}\""
         end
 
       case SQL.query(repo, sql, []) do
@@ -294,12 +288,12 @@ defmodule Triplex do
     sql =
       case repo.__adapter__ do
         Ecto.Adapters.MyXQL ->
-          field_name =
+          column_name =
             if Triplex.config().tenant_table == :"information_schema.schemata",
               do: "schema_name",
               else: "name"
 
-          "SELECT #{field_name} FROM `#{config().tenant_table}`"
+          "SELECT #{column_name} FROM `#{config().tenant_table}`"
 
         Ecto.Adapters.Postgres ->
           """
diff --git a/lib/triplex/config.ex b/lib/triplex/config.ex
index 409d320..12f21e1 100644
--- a/lib/triplex/config.ex
+++ b/lib/triplex/config.ex
@@ -8,9 +8,6 @@ defmodule Triplex.Config do
   through triplex APIs. The items here can be strings or regexes.
   - `tenant_field`: an atom with the name of the field to get the tenant name
   if the given tenant is a struct. By default it's `:id`.
-  - `mysql`: extra options to supply for the create database query for MySQL driver.
-  The SQL standard allows a DEFAULT CHARACTER SET clause in CREATE SCHEMA than are presently accepted by PostgreSQL.
-  supported options are `charset` and `collate`.
   """
 
   defstruct [
@@ -19,10 +16,6 @@ defmodule Triplex.Config do
     migrations_path: "tenant_migrations",
     reserved_tenants: [],
     tenant_field: :id,
-    tenant_table: :tenants,
-    mysql: [
-      charset: "utf8mb4",
-      collate: "utf8mb4_bin"
-    ]
+    tenant_table: :tenants
   ]
 end

From 9bda4dc759f62e454e52abb1ecec6f83534831c3 Mon Sep 17 00:00:00 2001
From: HammamSamara <h.samara.74@gmail.com>
Date: Sat, 29 May 2021 02:41:18 +0300
Subject: [PATCH 10/10] use information_schema as the default behaviour

---
 README.md             | 10 ++++++----
 config/test.exs       |  2 ++
 lib/triplex/config.ex |  2 +-
 3 files changed, 9 insertions(+), 5 deletions(-)

diff --git a/README.md b/README.md
index e97d1ce..d1ba591 100644
--- a/README.md
+++ b/README.md
@@ -43,8 +43,8 @@ Configure the Repo you will use to execute the database commands with:
 ### Additional configuration for MySQL
 
 In MySQL, each tenant will have its own MySQL database.
-Triplex uses a table called `tenants` in the main Repo to keep track of the different tenants.
-Generate the migration that will create the table by running:
+Triplex used to use a table called `tenants` in the main Repo to keep track of the different tenants.
+If you wish to keep this behavior, generate the migration that will create the table by running:
 
     mix triplex.mysql.install
 
@@ -52,9 +52,11 @@ And then create the table:
 
     mix ecto.migrate
 
-Otherwise, if you wish to skip this behavior, configure Triplex to use the default `information_schema.schemata` table:
+Finally, configure Triplex to use the `tenants` table:
 
-    config :triplex, tenant_table: :"information_schema.schemata"
+    config :triplex, tenant_table: :tenants
+
+Otherwise, Triplex will continue to use the `information_schema.schemata` table as the default behavior for storing tenants.
 
 ## Usage
 
diff --git a/config/test.exs b/config/test.exs
index 60053d9..daf61e9 100644
--- a/config/test.exs
+++ b/config/test.exs
@@ -14,6 +14,8 @@ config :triplex,
     ~r/^db\d+$/
   ]
 
+config :triplex, tenant_table: :tenants
+
 # Configure your database
 config :triplex, ecto_repos: [Triplex.PGTestRepo, Triplex.MSTestRepo]
 
diff --git a/lib/triplex/config.ex b/lib/triplex/config.ex
index 12f21e1..2c0bdc2 100644
--- a/lib/triplex/config.ex
+++ b/lib/triplex/config.ex
@@ -16,6 +16,6 @@ defmodule Triplex.Config do
     migrations_path: "tenant_migrations",
     reserved_tenants: [],
     tenant_field: :id,
-    tenant_table: :tenants
+    tenant_table: :"information_schema.schemata"
   ]
 end