Repository: diesel-rs/diesel (language: rust)

Issue:
splitting into multiple schema.rs
Your project helps a lot.  
One feature I want is splitting into multiple schema.rs. 
For instance, making the following diesel.toml

```toml
[[print_schema]]
file = "db_company/src/schema.rs"
filter = { only_tables = ["^companies$"] }
[[print_schema]]
file = "db_user/src/schema.rs"
filter = { only_tables = ["^users$"] }
```

Then, the `companies` schema info is written in `db_company/src/schema.rs`, while the `users` schema info goes to `db_user/src/schema.rs`. 

## Current Behavior

print_schema field only allows only on schema info.  
So, what I do right now is select one print_schema and comment out others like the following files. 

```toml
[print_schema]
file = "db_company/src/schema.rs"
filter = { only_tables = ["^companies$"] }
# [print_schema]
# file = "db_user/src/schema.rs"
# filter = { only_tables = ["^users$"] }
```

## Motivation

- Readability
  - By splitting schema.rs, we can easily search definition of tables. 
- Build Performance
  - This feature will parallelize building schema.rs. which will result in better build performance. 

## How to do 

- make print_schma field in diesel.toml to either single or multiple print_schema 
- edit print_schema.rs to run multiple time.

_Originally posted by @forest1102 in https://github.com/diesel-rs/diesel/discussions/3715_


Discussion / hints:

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index fff8d3863205..eaa59a4827d1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -20,6 +20,7 @@ Increasing the minimal supported Rust version will always be coupled at least wi
 * Logging in diesel-cli
 * Support for libsqlite3-sys 0.28
 * Add `sqlite-integer-primary-key-is-bigint` configuration option, usable with SQLite 3.37 or above, allowing to use `BigInt` for `INTEGER PRIMARY KEY` columns in SQLite for tables without the `WITHOUT ROWID` attribute ([SQLite doc](https://www.sqlite.org/lang_createtable.html#rowid)).
+* Support for multiple `print_schema` entry in `diesel.toml` (e.g. `[print_schema.user1]`), which allows generating multiple schema.rs files
 
 ### Changed
 
diff --git a/diesel_cli/Cargo.toml b/diesel_cli/Cargo.toml
index 05354ffabc87..87e24064d5d1 100644
--- a/diesel_cli/Cargo.toml
+++ b/diesel_cli/Cargo.toml
@@ -23,12 +23,15 @@ path = "src/main.rs"
 doc = false
 
 [dependencies]
-chrono = { version = "0.4.20", default-features = false, features = ["clock", "std"] }
-clap = { version = "4.0.2", features = ["cargo", "string"] }
+chrono = { version = "0.4.20", default-features = false, features = [
+    "clock",
+    "std",
+] }
+clap = { version = "4.4.14", features = ["cargo", "string"] }
 clap_complete = "4"
 dotenvy = "0.15"
 heck = "0.4.0"
-serde = { version = "1.0.0", features = ["derive"] }
+serde = { version = "1.0.193", features = ["derive", "std"] }
 toml = "0.8"
 url = { version = "2.2.2" }
 libsqlite3-sys = { version = ">=0.17.2, <0.29.0", optional = true }
diff --git a/diesel_cli/src/cli.rs b/diesel_cli/src/cli.rs
index c35fd86568c6..a160a3e0bf9e 100644
--- a/diesel_cli/src/cli.rs
+++ b/diesel_cli/src/cli.rs
@@ -6,6 +6,13 @@ use clap_complete::Shell;
 
 use crate::print_schema;
 
+fn position_sensitive_flag(arg: Arg) -> Arg {
+    arg.num_args(0)
+        .value_parser(clap::value_parser!(bool))
+        .default_missing_value("true")
+        .default_value("false")
+}
+
 pub fn build_cli() -> Command {
     let database_arg = Arg::new("DATABASE_URL")
         .long("database-url")
@@ -168,20 +175,24 @@ pub fn build_cli() -> Command {
                         .help("Table names to filter."),
                 )
                 .arg(
-                    Arg::new("only-tables")
+                    position_sensitive_flag(Arg::new("only-tables"))
                         .short('o')
                         .long("only-tables")
-                        .action(ArgAction::SetTrue)
-                        .help("Only include tables from table-name that matches regexp.")
-                        .conflicts_with("except-tables"),
+                        .action(ArgAction::Append)
+                        .help("Only include tables from table-name that matches regexp."),
                 )
                 .arg(
-                    Arg::new("except-tables")
+                    position_sensitive_flag(Arg::new("except-tables"))
                         .short('e')
                         .long("except-tables")
-                        .action(ArgAction::SetTrue)
-                        .help("Exclude tables from table-name that matches regex.")
-                        .conflicts_with("only-tables"),
+                        .action(ArgAction::Append)
+                        .help("Exclude tables from table-name that matches regex."),
+                )
+                .arg(
+                    Arg::new("schema-key")
+                        .long("schema-key")
+                        .action(clap::ArgAction::Append)
+                        .help("select schema key from diesel.toml, use 'default' for print_schema without key."),
                 ),
         )
         .subcommand_required(true)
@@ -238,25 +249,23 @@ pub fn build_cli() -> Command {
                 .help("Table names to filter."),
         )
         .arg(
-            Arg::new("only-tables")
+            position_sensitive_flag(Arg::new("only-tables"))
                 .short('o')
                 .long("only-tables")
-                .action(ArgAction::SetTrue)
+                .action(ArgAction::Append)
                 .help("Only include tables from table-name that matches regexp.")
-                .conflicts_with("except-tables"),
         )
         .arg(
-            Arg::new("except-tables")
+            position_sensitive_flag(Arg::new("except-tables"))
                 .short('e')
                 .long("except-tables")
-                .action(ArgAction::SetTrue)
+                .action(ArgAction::Append)
                 .help("Exclude tables from table-name that matches regex.")
-                .conflicts_with("only-tables"),
         )
         .arg(
-            Arg::new("with-docs")
+            position_sensitive_flag(Arg::new("with-docs"))
                 .long("with-docs")
-                .action(ArgAction::SetTrue)
+                .action(ArgAction::Append)
                 .help("Render documentation comments for tables and columns."),
         )
         .arg(
@@ -264,6 +273,7 @@ pub fn build_cli() -> Command {
                 .long("with-docs-config")
                 .help("Render documentation comments for tables and columns.")
                 .num_args(1)
+                .action(ArgAction::Append)
                 .value_parser(PossibleValuesParser::new(print_schema::DocConfig::VARIANTS_STR)),
         )
         .arg(
@@ -271,12 +281,14 @@ pub fn build_cli() -> Command {
                 .long("column-sorting")
                 .help("Sort order for table columns.")
                 .num_args(1)
+                .action(ArgAction::Append)
                 .value_parser(PossibleValuesParser::new(["ordinal_position", "name"])),
         )
         .arg(
             Arg::new("patch-file")
                 .long("patch-file")
                 .num_args(1)
+                .action(ArgAction::Append)
                 .value_parser(clap::value_parser!(std::path::PathBuf))
                 .help("A unified diff file to be applied to the final schema."),
         )
@@ -284,14 +296,15 @@ pub fn build_cli() -> Command {
             Arg::new("import-types")
                 .long("import-types")
                 .num_args(1..)
+                .action(ArgAction::Append)
                 .action(clap::ArgAction::Append)
                 .number_of_values(1)
                 .help("A list of types to import for every table, separated by commas."),
         )
         .arg(
-            Arg::new("generate-custom-type-definitions")
+            position_sensitive_flag(Arg::new("generate-custom-type-definitions"))
                 .long("no-generate-missing-sql-type-definitions")
-                .action(ArgAction::SetTrue)
+                .action(ArgAction::Append)
                 .help("Generate SQL type definitions for types not provided by diesel"),
         )
         .arg(
@@ -303,9 +316,15 @@ pub fn build_cli() -> Command {
                 .help("A list of derives to implement for every automatically generated SqlType in the schema, separated by commas."),
         )
         .arg(
-            Arg::new("sqlite-integer-primary-key-is-bigint")
+            Arg::new("schema-key")
+                .long("schema-key")
+                .action(ArgAction::Append)
+                .default_values(["default"])
+                .help("select schema key from diesel.toml, use 'default' for print_schema without key."),
+        ).arg(
+            position_sensitive_flag(Arg::new("sqlite-integer-primary-key-is-bigint"))
                 .long("sqlite-integer-primary-key-is-bigint")
-                .action(ArgAction::SetTrue)
+                .action(ArgAction::Append)
                 .help(
                     "For SQLite 3.37 and above, detect `INTEGER PRIMARY KEY` columns as `BigInt`, \
                      when the table isn't declared with `WITHOUT ROWID`.\n\
diff --git a/diesel_cli/src/config.rs b/diesel_cli/src/config.rs
index 889b62317a6d..adee31571385 100644
--- a/diesel_cli/src/config.rs
+++ b/diesel_cli/src/config.rs
@@ -1,25 +1,48 @@
+use super::find_project_root;
+use crate::infer_schema_internals::TableName;
+use crate::print_schema::ColumnSorting;
+use crate::print_schema::{self, DocConfig};
 use clap::ArgMatches;
 use serde::de::{self, MapAccess, Visitor};
 use serde::{Deserialize, Deserializer};
 use serde_regex::Serde as RegexWrapper;
-use std::fs;
+use std::collections::btree_map::Entry;
+use std::collections::BTreeMap;
+use std::ops::Bound;
 use std::path::{Path, PathBuf};
 use std::{env, fmt};
-
-use super::find_project_root;
-use crate::infer_schema_internals::TableName;
-use crate::print_schema;
-use crate::print_schema::ColumnSorting;
+use std::{fs, iter};
 
 #[derive(Deserialize, Default, Debug)]
 #[serde(deny_unknown_fields)]
 pub struct Config {
     #[serde(default)]
-    pub print_schema: PrintSchema,
+    pub print_schema: RootPrintSchema,
     #[serde(default)]
     pub migrations_directory: Option<MigrationsDirectory>,
 }
 
+fn get_values_with_indices<T: Clone + Send + Sync + 'static>(
+    matches: &ArgMatches,
+    id: &str,
+) -> Result<Option<BTreeMap<usize, T>>, crate::errors::Error> {
+    match matches.indices_of(id) {
+        Some(indices) => match matches.try_get_many::<T>(id) {
+            Ok(Some(values)) => Ok(Some(
+                indices
+                    .zip(values)
+                    .map(|(index, value)| (index, value.clone()))
+                    .collect(),
+            )),
+            Ok(None) => {
+                unreachable!("`ids` only reports what is present")
+            }
+            Err(e) => Err(e.into()),
+        },
+        None => Ok(None),
+    }
+}
+
 impl Config {
     pub fn file_path(matches: &ArgMatches) -> PathBuf {
         matches
@@ -53,23 +76,326 @@ impl Config {
     }
 
     pub fn set_filter(mut self, matches: &ArgMatches) -> Result<Self, crate::errors::Error> {
-        let table_names = matches
-            .get_many::<String>("table-name")
-            .unwrap_or_default()
-            .map(|table_name_regex| regex::Regex::new(table_name_regex).map(Into::into))
-            .collect::<Result<Vec<Regex>, _>>()?;
+        if self.print_schema.has_multiple_schema {
+            let selected_schema_keys =
+                get_values_with_indices::<String>(matches, "schema-key")?.unwrap_or_default();
+            let table_names_with_indices =
+                get_values_with_indices::<String>(matches, "table-name")?;
+            let only_tables_with_indices = get_values_with_indices::<bool>(matches, "only-tables")?;
+            let except_tables_with_indices =
+                get_values_with_indices::<bool>(matches, "except-tables")?;
 
-        if matches.get_flag("only-tables") {
-            self.print_schema.filter = Filtering::OnlyTables(table_names)
-        } else if matches.get_flag("except-tables") {
-            self.print_schema.filter = Filtering::ExceptTables(table_names)
+            for (key, boundary) in selected_schema_keys.values().cloned().zip(
+                selected_schema_keys
+                    .keys()
+                    .cloned()
+                    .map(Bound::Included)
+                    .zip(
+                        selected_schema_keys
+                            .keys()
+                            .cloned()
+                            .skip(1)
+                            .map(Bound::Excluded)
+                            .chain(iter::once(Bound::Unbounded)),
+                    ),
+            ) {
+                let print_schema = self
+                    .print_schema
+                    .all_configs
+                    .get_mut(&key)
+                    .ok_or(crate::errors::Error::NoSchemaKeyFound(key))?;
+                if let Some(table_names_with_indices) = table_names_with_indices.clone() {
+                    let table_names = table_names_with_indices
+                        .range(boundary)
+                        .map(|(_, v)| v.clone())
+                        .map(|table_name_regex| {
+                            regex::Regex::new(&table_name_regex).map(Into::into)
+                        })
+                        .collect::<Result<Vec<Regex>, _>>()?;
+                    if table_names.is_empty() {
+                        continue;
+                    }
+                    if only_tables_with_indices
+                        .clone()
+                        .and_then(|only_tables_with_indices| {
+                            only_tables_with_indices
+                                .range(boundary)
+                                .nth(0)
+                                .map(|v| *v.1)
+                        })
+                        .unwrap_or(false)
+                    {
+                        print_schema.filter = Filtering::OnlyTables(table_names.clone());
+                    }
+                    if except_tables_with_indices
+                        .clone()
+                        .and_then(|except_tables_with_indices| {
+                            except_tables_with_indices
+                                .range(boundary)
+                                .nth(0)
+                                .map(|v| *v.1)
+                        })
+                        .unwrap_or(false)
+                    {
+                        print_schema.filter = Filtering::ExceptTables(table_names);
+                    }
+                }
+            }
+        } else {
+            let print_schema = self
+                .print_schema
+                .all_configs
+                .entry("default".to_string())
+                .or_insert(PrintSchema::default().set_filter(matches)?);
+            let print_schema = print_schema.clone().set_filter(matches)?;
+            self.print_schema
+                .all_configs
+                .entry("default".to_string())
+                .and_modify(|v| *v = print_schema);
         }
+        Ok(self)
+    }
 
+    pub fn update_config(mut self, matches: &ArgMatches) -> Result<Self, crate::errors::Error> {
+        if self.print_schema.has_multiple_schema {
+            if let Some(selected_schema_keys) =
+                get_values_with_indices::<String>(matches, "schema-key")?
+            {
+                let schema_with_indices = get_values_with_indices::<String>(matches, "schema")?;
+                let with_docs_with_indices = get_values_with_indices::<bool>(matches, "with-docs")?;
+                let with_docs_config_with_indices =
+                    get_values_with_indices::<String>(matches, "with-docs-config")?;
+                let patch_file_with_indices =
+                    get_values_with_indices::<PathBuf>(matches, "patch-file")?;
+                let column_sorting_with_indices =
+                    get_values_with_indices::<String>(matches, "column-sorting")?;
+                let import_types_with_indices =
+                    get_values_with_indices::<String>(matches, "import-types")?;
+                let generate_custom_type_definitions_with_indices =
+                    get_values_with_indices::<bool>(matches, "generate-custom-type-definitions")?;
+                let custom_type_derives_with_indices =
+                    get_values_with_indices::<String>(matches, "custom-type-derives")?;
+                let sqlite_integer_primary_key_is_bigint_with_indices =
+                    get_values_with_indices::<bool>(
+                        matches,
+                        "sqlite-integer-primary-key-is-bigint",
+                    )?;
+                for (key, boundary) in selected_schema_keys.values().cloned().zip(
+                    selected_schema_keys
+                        .keys()
+                        .cloned()
+                        .map(Bound::Included)
+                        .zip(
+                            selected_schema_keys
+                                .keys()
+                                .cloned()
+                                .skip(1)
+                                .map(Bound::Excluded)
+                                .chain(iter::once(Bound::Unbounded)),
+                        ),
+                ) {
+                    let print_schema = self
+                        .print_schema
+                        .all_configs
+                        .get_mut(&key)
+                        .ok_or(crate::errors::Error::NoSchemaKeyFound(key))?;
+                    if let Some(schema) = schema_with_indices
+                        .clone()
+                        .and_then(|v| v.range(boundary).nth(0).map(|v| v.1.clone()))
+                    {
+                        print_schema.schema = Some(schema)
+                    }
+                    if with_docs_with_indices
+                        .clone()
+                        .and_then(|with_docs_with_indices| {
+                            with_docs_with_indices.range(boundary).nth(0).map(|v| *v.1)
+                        })
+                        .unwrap_or(false)
+                    {
+                        print_schema.with_docs =
+                            DocConfig::DatabaseCommentsFallbackToAutoGeneratedDocComment;
+                    }
+
+                    if let Some(doc_config) = with_docs_config_with_indices
+                        .clone()
+                        .and_then(|v| v.range(boundary).nth(0).map(|v| v.1.clone()))
+                    {
+                        print_schema.with_docs = doc_config.parse().map_err(|_| {
+                            crate::errors::Error::UnsupportedFeature(format!(
+                                "Invalid documentation config mode: {doc_config}"
+                            ))
+                        })?;
+                    }
+
+                    if let Some(sorting) = column_sorting_with_indices
+                        .clone()
+                        .and_then(|v| v.range(boundary).nth(0).map(|v| v.1.clone()))
+                    {
+                        match sorting.as_str() {
+                            "ordinal_position" => {
+                                print_schema.column_sorting = ColumnSorting::OrdinalPosition
+                            }
+                            "name" => print_schema.column_sorting = ColumnSorting::Name,
+                            _ => {
+                                return Err(crate::errors::Error::UnsupportedFeature(format!(
+                                    "Invalid column sorting mode: {sorting}"
+                                )))
+                            }
+                        }
+                    }
+
+                    if let Some(patch_file) = patch_file_with_indices
+                        .clone()
+                        .and_then(|v| v.range(boundary).nth(0).map(|v| v.1.clone()))
+                    {
+                        print_schema.patch_file = Some(patch_file);
+                    }
+
+                    let import_types = import_types_with_indices
+                        .clone()
+                        .map(|v| v.range(boundary).map(|v| v.1.clone()).collect())
+                        .unwrap_or(vec![]);
+                    if !import_types.is_empty() {
+                        print_schema.import_types = Some(import_types);
+                    }
+
+                    if generate_custom_type_definitions_with_indices
+                        .clone()
+                        .and_then(|generate_custom_type_definitions_with_indices| {
+                            generate_custom_type_definitions_with_indices
+                                .range(boundary)
+                                .nth(0)
+                                .map(|v| *v.1)
+                        })
+                        .unwrap_or(false)
+                    {
+                        print_schema.generate_missing_sql_type_definitions = Some(false)
+                    }
+
+                    let custom_type_derives = custom_type_derives_with_indices
+                        .clone()
+                        .map(|v| v.range(boundary).map(|v| v.1.clone()).collect())
+                        .unwrap_or(vec![]);
+                    if !custom_type_derives.is_empty() {
+                        print_schema.custom_type_derives = Some(custom_type_derives);
+                    }
+                    if let Some(sqlite_integer_primary_key_is_bigint) =
+                        sqlite_integer_primary_key_is_bigint_with_indices
+                            .clone()
+                            .and_then(|with_docs_with_indices| {
+                                with_docs_with_indices.range(boundary).nth(0).map(|v| *v.1)
+                            })
+                    {
+                        print_schema.sqlite_integer_primary_key_is_bigint =
+                            Some(sqlite_integer_primary_key_is_bigint);
+                    }
+                }
+            }
+        } else {
+            let config = match self.print_schema.all_configs.entry("default".to_string()) {
+                Entry::Vacant(entry) => entry.insert(PrintSchema::default()),
+                Entry::Occupied(entry) => entry.into_mut(),
+            };
+            if let Some(schema_name) = matches.get_one::<String>("schema") {
+                config.schema = Some(schema_name.clone())
+            }
+            if matches.get_flag("with-docs") {
+                config.with_docs = DocConfig::DatabaseCommentsFallbackToAutoGeneratedDocComment;
+            }
+            if let Some(doc_config) = matches.get_one::<String>("with-docs-config") {
+                config.with_docs = doc_config.parse().map_err(|_| {
+                    crate::errors::Error::UnsupportedFeature(format!(
+                        "Invalid documentation config mode: {doc_config}"
+                    ))
+                })?;
+            }
+
+            if let Some(sorting) = matches.get_one::<String>("column-sorting") {
+                match sorting as &str {
+                    "ordinal_position" => config.column_sorting = ColumnSorting::OrdinalPosition,
+                    "name" => config.column_sorting = ColumnSorting::Name,
+                    _ => {
+                        return Err(crate::errors::Error::UnsupportedFeature(format!(
+                            "Invalid column sorting mode: {sorting}"
+                        )))
+                    }
+                }
+            }
+
+            if let Some(path) = matches.get_one::<PathBuf>("patch-file") {
+                config.patch_file = Some(path.clone());
+            }
+
+            if let Some(types) = matches.get_many("import-types") {
+                let types = types.cloned().collect();
+                config.import_types = Some(types);
+            }
+
+            if matches.get_flag("generate-custom-type-definitions") {
+                config.generate_missing_sql_type_definitions = Some(false);
+            }
+
+            if let Some(derives) = matches.get_many("custom-type-derives") {
+                let derives = derives.cloned().collect();
+                config.custom_type_derives = Some(derives);
+            }
+            if matches.get_flag("sqlite-integer-primary-key-is-bigint") {
+                config.sqlite_integer_primary_key_is_bigint = Some(true);
+            }
+        }
         Ok(self)
     }
 }
 
-#[derive(Default, Deserialize, Debug)]
+#[derive(Default, Clone, Debug)]
+pub struct RootPrintSchema {
+    has_multiple_schema: bool,
+    pub all_configs: BTreeMap<String, PrintSchema>,
+}
+
+impl<'de> Deserialize<'de> for RootPrintSchema {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        #[derive(Deserialize)]
+        struct Inner {
+            #[serde(flatten)]
+            default_config: PrintSchema,
+            #[serde(flatten)]
+            other_configs: BTreeMap<String, PrintSchema>,
+        }
+        let Inner {
+            other_configs,
+            default_config,
+        } = Inner::deserialize(deserializer)?;
+        if other_configs.is_empty() {
+            Ok(RootPrintSchema {
+                has_multiple_schema: false,
+                all_configs: BTreeMap::from([("default".into(), default_config)]),
+            })
+        } else {
+            let mut other_configs = other_configs;
+            other_configs
+                .entry("default".to_string())
+                .or_insert(default_config);
+            Ok(RootPrintSchema {
+                all_configs: other_configs,
+                has_multiple_schema: true,
+            })
+        }
+    }
+}
+
+impl RootPrintSchema {
+    fn set_relative_path_base(&mut self, base: &Path) {
+        for config in self.all_configs.values_mut() {
+            config.set_relative_path_base(base);
+        }
+    }
+}
+
+#[derive(Default, Deserialize, Clone, Debug)]
 #[serde(deny_unknown_fields)]
 pub struct PrintSchema {
     #[serde(default)]
@@ -137,6 +463,29 @@ impl PrintSchema {
             derives
         }
     }
+
+    pub fn set_filter(mut self, matches: &ArgMatches) -> Result<Self, crate::errors::Error> {
+        let table_names = matches
+            .get_many::<String>("table-name")
+            .unwrap_or_default()
+            .map(|table_name_regex| regex::Regex::new(table_name_regex).map(Into::into))
+            .collect::<Result<Vec<Regex>, _>>()?;
+
+        if matches
+            .try_get_one::<bool>("only-tables")?
+            .cloned()
+            .unwrap_or(false)
+        {
+            self.filter = Filtering::OnlyTables(table_names)
+        } else if matches
+            .try_get_one::<bool>("except-tables")?
+            .cloned()
+            .unwrap_or(false)
+        {
+            self.filter = Filtering::ExceptTables(table_names)
+        }
+        Ok(self)
+    }
 }
 
 #[derive(Default, Deserialize, Debug)]
diff --git a/diesel_cli/src/errors.rs b/diesel_cli/src/errors.rs
index 9bf7299c2226..4d6d48cd38e3 100644
--- a/diesel_cli/src/errors.rs
+++ b/diesel_cli/src/errors.rs
@@ -58,4 +58,8 @@ pub enum Error {
     ColumnLiteralParseError(syn::Error),
     #[error("Failed to parse database url: {0}")]
     UrlParsingError(#[from] url::ParseError),
+    #[error("Failed to parse CLI parameter: {0}")]
+    ClapMatchesError(#[from] clap::parser::MatchesError),
+    #[error("No `[print_schema.{0}]` entries in your diesel.toml")]
+    NoSchemaKeyFound(String),
 }
diff --git a/diesel_cli/src/main.rs b/diesel_cli/src/main.rs
index f8cc868c43bd..778fe92e3ca1 100644
--- a/diesel_cli/src/main.rs
+++ b/diesel_cli/src/main.rs
@@ -246,61 +246,15 @@ fn run_infer_schema(matches: &ArgMatches) -> Result<(), crate::errors::Error> {
     use crate::print_schema::*;
 
     tracing::info!("Infer schema");
-
     let mut conn = InferConnection::from_matches(matches)?;
-    let mut config = Config::read(matches)?.set_filter(matches)?.print_schema;
-
-    if let Some(schema_name) = matches.get_one::<String>("schema") {
-        config.schema = Some(schema_name.clone())
-    }
-
-    if matches.get_flag("with-docs") {
-        config.with_docs = DocConfig::DatabaseCommentsFallbackToAutoGeneratedDocComment;
-    }
-
-    if let Some(doc_config) = matches.get_one::<String>("with-docs-config") {
-        config.with_docs = doc_config.parse().map_err(|_| {
-            crate::errors::Error::UnsupportedFeature(format!(
-                "Invalid documentation config mode: {doc_config}"
-            ))
-        })?;
-    }
-
-    if let Some(sorting) = matches.get_one::<String>("column-sorting") {
-        match sorting as &str {
-            "ordinal_position" => config.column_sorting = ColumnSorting::OrdinalPosition,
-            "name" => config.column_sorting = ColumnSorting::Name,
-            _ => {
-                return Err(crate::errors::Error::UnsupportedFeature(format!(
-                    "Invalid column sorting mode: {sorting}"
-                )))
-            }
-        }
-    }
-
-    if let Some(path) = matches.get_one::<PathBuf>("patch-file") {
-        config.patch_file = Some(path.clone());
-    }
-
-    if let Some(types) = matches.get_many("import-types") {
-        let types = types.cloned().collect();
-        config.import_types = Some(types);
+    let root_config = Config::read(matches)?
+        .set_filter(matches)?
+        .update_config(matches)?
+        .print_schema;
+    for config in root_config.all_configs.values() {
+        run_print_schema(&mut conn, config, &mut stdout())?;
     }
 
-    if matches.get_flag("generate-custom-type-definitions") {
-        config.generate_missing_sql_type_definitions = Some(false);
-    }
-
-    if let Some(derives) = matches.get_many("custom-type-derives") {
-        let derives = derives.cloned().collect();
-        config.custom_type_derives = Some(derives);
-    }
-
-    if matches.get_flag("sqlite-integer-primary-key-is-bigint") {
-        config.sqlite_integer_primary_key_is_bigint = Some(true);
-    }
-
-    run_print_schema(&mut conn, &config, &mut stdout())?;
     Ok(())
 }
 
@@ -311,33 +265,36 @@ fn regenerate_schema_if_file_specified(matches: &ArgMatches) -> Result<(), crate
     tracing::debug!("Regenerate schema if required");
 
     let config = Config::read(matches)?.print_schema;
-    if let Some(ref path) = config.file {
-        let mut connection = InferConnection::from_matches(matches)?;
-        if let Some(parent) = path.parent() {
-            fs::create_dir_all(parent)?;
-        }
-
-        if matches.get_flag("LOCKED_SCHEMA") {
-            let mut buf = Vec::new();
-            print_schema::run_print_schema(&mut connection, &config, &mut buf)?;
-
-            let mut old_buf = Vec::new();
-            let mut file = fs::File::open(path)?;
-            file.read_to_end(&mut old_buf)?;
-
-            if buf != old_buf {
-                return Err(crate::errors::Error::SchemaWouldChange(
-                    path.display().to_string(),
-                ));
+    for config in config.all_configs.values() {
+        if let Some(ref path) = config.file {
+            let mut connection = InferConnection::from_matches(matches)?;
+            if let Some(parent) = path.parent() {
+                fs::create_dir_all(parent)?;
             }
-        } else {
-            use std::io::Write;
 
-            let mut file = fs::File::create(path)?;
-            let schema = print_schema::output_schema(&mut connection, &config)?;
-            file.write_all(schema.as_bytes())?;
+            if matches.get_flag("LOCKED_SCHEMA") {
+                let mut buf = Vec::new();
+                print_schema::run_print_schema(&mut connection, config, &mut buf)?;
+
+                let mut old_buf = Vec::new();
+                let mut file = fs::File::open(path)?;
+                file.read_to_end(&mut old_buf)?;
+
+                if buf != old_buf {
+                    return Err(crate::errors::Error::SchemaWouldChange(
+                        path.display().to_string(),
+                    ));
+                }
+            } else {
+                use std::io::Write;
+
+                let mut file = fs::File::create(path)?;
+                let schema = print_schema::output_schema(&mut connection, config)?;
+                file.write_all(schema.as_bytes())?;
+            }
         }
     }
+
     Ok(())
 }
 
diff --git a/diesel_cli/src/migrations/diff_schema.rs b/diesel_cli/src/migrations/diff_schema.rs
index d55d4e610979..08bce704fb07 100644
--- a/diesel_cli/src/migrations/diff_schema.rs
+++ b/diesel_cli/src/migrations/diff_schema.rs
@@ -9,7 +9,7 @@ use std::io::Read;
 use std::path::Path;
 use syn::visit::Visit;
 
-use crate::config::{Config, PrintSchema};
+use crate::config::PrintSchema;
 use crate::database::InferConnection;
 use crate::infer_schema_internals::{
     filter_table_names, load_table_names, ColumnDefinition, ColumnType, ForeignKeyConstraint,
@@ -28,7 +28,7 @@ fn compatible_type_list() -> HashMap<&'static str, Vec<&'static str>> {
 
 #[tracing::instrument]
 pub fn generate_sql_based_on_diff_schema(
-    config: Config,
+    config: PrintSchema,
     matches: &ArgMatches,
     schema_file_path: &Path,
 ) -> Result<(String, String), crate::errors::Error> {
@@ -46,10 +46,6 @@ pub fn generate_sql_based_on_diff_schema(
 
     tables_from_schema.visit_file(&syn_file);
     let mut conn = InferConnection::from_matches(matches)?;
-    let tables_from_database = filter_table_names(
-        load_table_names(&mut conn, None)?,
-        &config.print_schema.filter,
-    );
 
     let foreign_keys =
         crate::infer_schema_internals::load_foreign_key_constraints(&mut conn, None)?;
@@ -87,31 +83,28 @@ pub fn generate_sql_based_on_diff_schema(
         table_pk_key_list.insert(t.table_name.to_string(), keys);
         expected_schema_map.insert(t.table_name.to_string(), t);
     }
-
-    config.print_schema.with_docs = DocConfig::NoDocComments;
-    config.print_schema.column_sorting = ColumnSorting::OrdinalPosition;
+    config.with_docs = DocConfig::NoDocComments;
+    config.column_sorting = ColumnSorting::OrdinalPosition;
 
     // Parameter `sqlite_integer_primary_key_is_bigint` is only used for a SQLite connection
     match conn {
         #[cfg(feature = "postgres")]
-        InferConnection::Pg(_) => config.print_schema.sqlite_integer_primary_key_is_bigint = None,
+        InferConnection::Pg(_) => config.sqlite_integer_primary_key_is_bigint = None,
         #[cfg(feature = "sqlite")]
         InferConnection::Sqlite(_) => (),
         #[cfg(feature = "mysql")]
         InferConnection::Mysql(_) => {
-            config.print_schema.sqlite_integer_primary_key_is_bigint = None;
+            config.sqlite_integer_primary_key_is_bigint = None;
         }
     }
 
     let mut schema_diff = Vec::new();
-
+    let table_names = load_table_names(&mut conn, None)?;
+    let tables_from_database = filter_table_names(table_names.clone(), &config.filter);
     for table in tables_from_database {
         tracing::info!(?table, "Diff for existing table");
-        let columns = crate::infer_schema_internals::load_table_data(
-            &mut conn,
-            table.clone(),
-            &config.print_schema,
-        )?;
+        let columns =
+            crate::infer_schema_internals::load_table_data(&mut conn, table.clone(), &config)?;
         if let Some(t) = expected_schema_map.remove(&table.sql_name.to_lowercase()) {
             tracing::info!(table = ?t.sql_name, "Table exists in schema.rs");
             let mut primary_keys_in_db =
@@ -222,19 +215,19 @@ pub fn generate_sql_based_on_diff_schema(
             #[cfg(feature = "postgres")]
             InferConnection::Pg(_) => {
                 let mut qb = diesel::pg::PgQueryBuilder::default();
-                diff.generate_up_sql(&mut qb, &config.print_schema)?;
+                diff.generate_up_sql(&mut qb, &config)?;
                 qb.finish()
             }
             #[cfg(feature = "sqlite")]
             InferConnection::Sqlite(_) => {
                 let mut qb = diesel::sqlite::SqliteQueryBuilder::default();
-                diff.generate_up_sql(&mut qb, &config.print_schema)?;
+                diff.generate_up_sql(&mut qb, &config)?;
                 qb.finish()
             }
             #[cfg(feature = "mysql")]
             InferConnection::Mysql(_) => {
                 let mut qb = diesel::mysql::MysqlQueryBuilder::default();
-                diff.generate_up_sql(&mut qb, &config.print_schema)?;
+                diff.generate_up_sql(&mut qb, &config)?;
                 qb.finish()
             }
         };
@@ -243,19 +236,19 @@ pub fn generate_sql_based_on_diff_schema(
             #[cfg(feature = "postgres")]
             InferConnection::Pg(_) => {
                 let mut qb = diesel::pg::PgQueryBuilder::default();
-                diff.generate_down_sql(&mut qb, &config.print_schema)?;
+                diff.generate_down_sql(&mut qb, &config)?;
                 qb.finish()
             }
             #[cfg(feature = "sqlite")]
             InferConnection::Sqlite(_) => {
                 let mut qb = diesel::sqlite::SqliteQueryBuilder::default();
-                diff.generate_down_sql(&mut qb, &config.print_schema)?;
+                diff.generate_down_sql(&mut qb, &config)?;
                 qb.finish()
             }
             #[cfg(feature = "mysql")]
             InferConnection::Mysql(_) => {
                 let mut qb = diesel::mysql::MysqlQueryBuilder::default();
-                diff.generate_down_sql(&mut qb, &config.print_schema)?;
+                diff.generate_down_sql(&mut qb, &config)?;
                 qb.finish()
             }
         };
diff --git a/diesel_cli/src/migrations/mod.rs b/diesel_cli/src/migrations/mod.rs
index 0d529e354086..8ed0810ca923 100644
--- a/diesel_cli/src/migrations/mod.rs
+++ b/diesel_cli/src/migrations/mod.rs
@@ -91,18 +91,33 @@ pub(super) fn run_migration_command(matches: &ArgMatches) -> Result<(), crate::e
 
             let (up_sql, down_sql) = if let Some(diff_schema) = args.get_one::<String>("SCHEMA_RS")
             {
-                let mut config = Config::read(matches)?;
+                let config = Config::read(matches)?;
+                let mut print_schema =
+                    if let Some(schema_key) = args.get_one::<String>("schema-key") {
+                        config
+                            .print_schema
+                            .all_configs
+                            .get(schema_key)
+                            .ok_or(crate::errors::Error::NoSchemaKeyFound(schema_key.clone()))?
+                    } else {
+                        config
+                            .print_schema
+                            .all_configs
+                            .get("default")
+                            .ok_or(crate::errors::Error::NoSchemaKeyFound("default".into()))?
+                    }
+                    .clone();
                 let diff_schema = if diff_schema == "NOT_SET" {
-                    config.print_schema.file.clone()
+                    print_schema.file.clone()
                 } else {
                     Some(PathBuf::from(diff_schema))
                 };
                 if args.get_flag("sqlite-integer-primary-key-is-bigint") {
-                    config.print_schema.sqlite_integer_primary_key_is_bigint = Some(true);
+                    print_schema.sqlite_integer_primary_key_is_bigint = Some(true);
                 }
                 if let Some(diff_schema) = diff_schema {
                     self::diff_schema::generate_sql_based_on_diff_schema(
-                        config,
+                        print_schema,
                         args,
                         &diff_schema,
                     )?
diff --git a/diesel_cli/src/print_schema.rs b/diesel_cli/src/print_schema.rs
index 76e6573e5187..3f9deda7bf74 100644
--- a/diesel_cli/src/print_schema.rs
+++ b/diesel_cli/src/print_schema.rs
@@ -10,7 +10,7 @@ use std::io::Write as IoWrite;
 const SCHEMA_HEADER: &str = "// @generated automatically by Diesel CLI.\n";
 
 /// How to sort columns when querying the table schema.
-#[derive(Debug, Deserialize, Serialize)]
+#[derive(Debug, Deserialize, Serialize, Clone, Copy)]
 pub enum ColumnSorting {
     /// Order by ordinal position
     #[serde(rename = "ordinal_position")]


Test patch:
diff --git a/diesel_cli/tests/print_schema.rs b/diesel_cli/tests/print_schema.rs
index e867a2e09edf..1d660d8a28f5 100644
--- a/diesel_cli/tests/print_schema.rs
+++ b/diesel_cli/tests/print_schema.rs
@@ -355,6 +355,27 @@ fn print_schema_quoted_schema_and_table_name() {
     )
 }
 
+#[test]
+fn print_schema_with_multiple_schema() {
+    test_multiple_print_schema(
+        "print_schema_with_multiple_schema",
+        vec![
+            "--schema-key",
+            "default",
+            "--schema-key",
+            "user1",
+            "-o",
+            "users1",
+            "--with-docs",
+            "--schema-key",
+            "user2",
+            "-o",
+            "users2",
+            "--with-docs",
+        ],
+    )
+}
+
 #[cfg(feature = "sqlite")]
 const BACKEND: &str = "sqlite";
 #[cfg(feature = "postgres")]
@@ -371,6 +392,45 @@ fn backend_file_path(test_name: &str, file: &str) -> PathBuf {
         .join(file)
 }
 
+fn test_multiple_print_schema(test_name: &str, args: Vec<&str>) {
+    let test_path = Path::new(env!("CARGO_MANIFEST_DIR"))
+        .join("tests")
+        .join("print_schema")
+        .join(test_name);
+    let p = project(test_name)
+        .file(
+            "diesel.toml",
+            r#"
+            [print_schema.user1]
+            [print_schema.user2]
+            "#,
+        )
+        .build();
+    let db = database(&p.database_url());
+
+    p.command("setup").run();
+
+    let schema = read_file(&backend_file_path(test_name, "schema.sql"));
+    db.execute(&schema);
+
+    let result = p.command("print-schema").args(args).run();
+
+    assert!(result.is_success(), "Result was unsuccessful {:?}", result);
+
+    let result = result.stdout().replace("\r\n", "\n");
+
+    let mut setting = insta::Settings::new();
+    setting.set_snapshot_path(backend_file_path(test_name, ""));
+    setting.set_omit_expression(true);
+    setting.set_description(format!("Test: {test_name}"));
+    setting.set_prepend_module_to_snapshot(false);
+
+    setting.bind(|| {
+        insta::assert_snapshot!("expected", result);
+        test_multiple_print_schema_config(test_name, &test_path, schema);
+    });
+}
+
 fn test_print_schema(test_name: &str, args: Vec<&str>) {
     let test_path = Path::new(env!("CARGO_MANIFEST_DIR"))
         .join("tests")
@@ -431,6 +491,35 @@ fn test_print_schema_config(test_name: &str, test_path: &Path, schema: String) {
     insta::assert_snapshot!("expected", result);
 }
 
+fn test_multiple_print_schema_config(test_name: &str, test_path: &Path, schema: String) {
+    let config = read_file(&test_path.join("diesel.toml"));
+    let mut p = project(&format!("{}_config", test_name)).file("diesel.toml", &config);
+
+    let patch_file = backend_file_path(test_name, "schema.patch");
+    if patch_file.exists() {
+        let patch_contents = read_file(&patch_file);
+        p = p.file("schema.patch", &patch_contents);
+    }
+
+    let p = p.build();
+
+    p.command("setup").run();
+    p.create_migration("12345_create_schema", &schema, None, None);
+    let result = p.command("migration").arg("run").run();
+    assert!(result.is_success(), "Result was unsuccessful {:?}", result);
+
+    let schema = p.file_contents("src/schema1.rs").replace("\r\n", "\n");
+    insta::assert_snapshot!("expected_1", schema);
+    let schema = p.file_contents("src/schema2.rs").replace("\r\n", "\n");
+    insta::assert_snapshot!("expected_2", schema);
+
+    let result = p.command("print-schema").run();
+    assert!(result.is_success(), "Result was unsuccessful {:?}", result);
+
+    let result = result.stdout().replace("\r\n", "\n");
+    insta::assert_snapshot!("expected", result);
+}
+
 fn read_file(path: &Path) -> String {
     let mut file = File::open(path).expect(&format!("Could not open {}", path.display()));
     let mut string = String::new();
diff --git a/diesel_cli/tests/print_schema/print_schema_with_multiple_schema/diesel.toml b/diesel_cli/tests/print_schema/print_schema_with_multiple_schema/diesel.toml
new file mode 100644
index 000000000000..26ed7123ab14
--- /dev/null
+++ b/diesel_cli/tests/print_schema/print_schema_with_multiple_schema/diesel.toml
@@ -0,0 +1,10 @@
+[print_schema.user1]
+file = "src/schema1.rs"
+with_docs = true
+filter = { only_tables = ["users1"] }
+
+
+[print_schema.user2]
+file = "src/schema2.rs"
+with_docs = true
+filter = { only_tables = ["users2"] }
diff --git a/diesel_cli/tests/print_schema/print_schema_with_multiple_schema/mysql/expected.snap b/diesel_cli/tests/print_schema/print_schema_with_multiple_schema/mysql/expected.snap
new file mode 100644
index 000000000000..64982a12d7fc
--- /dev/null
+++ b/diesel_cli/tests/print_schema/print_schema_with_multiple_schema/mysql/expected.snap
@@ -0,0 +1,54 @@
+---
+source: diesel_cli/tests/print_schema.rs
+assertion_line: 414
+description: "Test: print_schema_with_multiple_schema"
+---
+// @generated automatically by Diesel CLI.
+
+diesel::table! {
+    users1 (id) {
+        id -> Integer,
+    }
+}
+
+diesel::table! {
+    users2 (id) {
+        id -> Integer,
+    }
+}
+
+diesel::allow_tables_to_appear_in_same_query!(
+    users1,
+    users2,
+);
+// @generated automatically by Diesel CLI.
+
+diesel::table! {
+    /// Representation of the `users1` table.
+    ///
+    /// (Automatically generated by Diesel.)
+    users1 (id) {
+        /// The `id` column of the `users1` table.
+        ///
+        /// Its SQL type is `Integer`.
+        ///
+        /// (Automatically generated by Diesel.)
+        id -> Integer,
+    }
+}
+// @generated automatically by Diesel CLI.
+
+diesel::table! {
+    /// Representation of the `users2` table.
+    ///
+    /// (Automatically generated by Diesel.)
+    users2 (id) {
+        /// The `id` column of the `users2` table.
+        ///
+        /// Its SQL type is `Integer`.
+        ///
+        /// (Automatically generated by Diesel.)
+        id -> Integer,
+    }
+}
+
diff --git a/diesel_cli/tests/print_schema/print_schema_with_multiple_schema/mysql/expected_1.snap b/diesel_cli/tests/print_schema/print_schema_with_multiple_schema/mysql/expected_1.snap
new file mode 100644
index 000000000000..2bfe57e806dd
--- /dev/null
+++ b/diesel_cli/tests/print_schema/print_schema_with_multiple_schema/mysql/expected_1.snap
@@ -0,0 +1,20 @@
+---
+source: diesel_cli/tests/print_schema.rs
+description: "Test: print_schema_with_multiple_schema"
+---
+// @generated automatically by Diesel CLI.
+
+diesel::table! {
+    /// Representation of the `users1` table.
+    ///
+    /// (Automatically generated by Diesel.)
+    users1 (id) {
+        /// The `id` column of the `users1` table.
+        ///
+        /// Its SQL type is `Integer`.
+        ///
+        /// (Automatically generated by Diesel.)
+        id -> Integer,
+    }
+}
+
diff --git a/diesel_cli/tests/print_schema/print_schema_with_multiple_schema/mysql/expected_2.snap b/diesel_cli/tests/print_schema/print_schema_with_multiple_schema/mysql/expected_2.snap
new file mode 100644
index 000000000000..54cef0d71d88
--- /dev/null
+++ b/diesel_cli/tests/print_schema/print_schema_with_multiple_schema/mysql/expected_2.snap
@@ -0,0 +1,20 @@
+---
+source: diesel_cli/tests/print_schema.rs
+description: "Test: print_schema_with_multiple_schema"
+---
+// @generated automatically by Diesel CLI.
+
+diesel::table! {
+    /// Representation of the `users2` table.
+    ///
+    /// (Automatically generated by Diesel.)
+    users2 (id) {
+        /// The `id` column of the `users2` table.
+        ///
+        /// Its SQL type is `Integer`.
+        ///
+        /// (Automatically generated by Diesel.)
+        id -> Integer,
+    }
+}
+
diff --git a/diesel_cli/tests/print_schema/print_schema_with_multiple_schema/mysql/schema.sql b/diesel_cli/tests/print_schema/print_schema_with_multiple_schema/mysql/schema.sql
new file mode 100644
index 000000000000..556e9f7b54e8
--- /dev/null
+++ b/diesel_cli/tests/print_schema/print_schema_with_multiple_schema/mysql/schema.sql
@@ -0,0 +1,2 @@
+CREATE TABLE users1 (id INTEGER PRIMARY KEY);
+CREATE TABLE users2 (id INTEGER PRIMARY KEY);
diff --git a/diesel_cli/tests/print_schema/print_schema_with_multiple_schema/postgres/expected.snap b/diesel_cli/tests/print_schema/print_schema_with_multiple_schema/postgres/expected.snap
new file mode 100644
index 000000000000..4cf2e0348a4d
--- /dev/null
+++ b/diesel_cli/tests/print_schema/print_schema_with_multiple_schema/postgres/expected.snap
@@ -0,0 +1,53 @@
+---
+source: diesel_cli/tests/print_schema.rs
+description: "Test: print_schema_with_multiple_schema"
+---
+// @generated automatically by Diesel CLI.
+
+diesel::table! {
+    users1 (id) {
+        id -> Int4,
+    }
+}
+
+diesel::table! {
+    users2 (id) {
+        id -> Int4,
+    }
+}
+
+diesel::allow_tables_to_appear_in_same_query!(
+    users1,
+    users2,
+);
+// @generated automatically by Diesel CLI.
+
+diesel::table! {
+    /// Representation of the `users1` table.
+    ///
+    /// (Automatically generated by Diesel.)
+    users1 (id) {
+        /// The `id` column of the `users1` table.
+        ///
+        /// Its SQL type is `Int4`.
+        ///
+        /// (Automatically generated by Diesel.)
+        id -> Int4,
+    }
+}
+// @generated automatically by Diesel CLI.
+
+diesel::table! {
+    /// Representation of the `users2` table.
+    ///
+    /// (Automatically generated by Diesel.)
+    users2 (id) {
+        /// The `id` column of the `users2` table.
+        ///
+        /// Its SQL type is `Int4`.
+        ///
+        /// (Automatically generated by Diesel.)
+        id -> Int4,
+    }
+}
+
diff --git a/diesel_cli/tests/print_schema/print_schema_with_multiple_schema/postgres/expected_1.snap b/diesel_cli/tests/print_schema/print_schema_with_multiple_schema/postgres/expected_1.snap
new file mode 100644
index 000000000000..60d6577fe201
--- /dev/null
+++ b/diesel_cli/tests/print_schema/print_schema_with_multiple_schema/postgres/expected_1.snap
@@ -0,0 +1,20 @@
+---
+source: diesel_cli/tests/print_schema.rs
+description: "Test: print_schema_with_multiple_schema"
+---
+// @generated automatically by Diesel CLI.
+
+diesel::table! {
+    /// Representation of the `users1` table.
+    ///
+    /// (Automatically generated by Diesel.)
+    users1 (id) {
+        /// The `id` column of the `users1` table.
+        ///
+        /// Its SQL type is `Int4`.
+        ///
+        /// (Automatically generated by Diesel.)
+        id -> Int4,
+    }
+}
+
diff --git a/diesel_cli/tests/print_schema/print_schema_with_multiple_schema/postgres/expected_2.snap b/diesel_cli/tests/print_schema/print_schema_with_multiple_schema/postgres/expected_2.snap
new file mode 100644
index 000000000000..2283da73a569
--- /dev/null
+++ b/diesel_cli/tests/print_schema/print_schema_with_multiple_schema/postgres/expected_2.snap
@@ -0,0 +1,20 @@
+---
+source: diesel_cli/tests/print_schema.rs
+description: "Test: print_schema_with_multiple_schema"
+---
+// @generated automatically by Diesel CLI.
+
+diesel::table! {
+    /// Representation of the `users2` table.
+    ///
+    /// (Automatically generated by Diesel.)
+    users2 (id) {
+        /// The `id` column of the `users2` table.
+        ///
+        /// Its SQL type is `Int4`.
+        ///
+        /// (Automatically generated by Diesel.)
+        id -> Int4,
+    }
+}
+
diff --git a/diesel_cli/tests/print_schema/print_schema_with_multiple_schema/postgres/schema.sql b/diesel_cli/tests/print_schema/print_schema_with_multiple_schema/postgres/schema.sql
new file mode 100644
index 000000000000..37d66071ac40
--- /dev/null
+++ b/diesel_cli/tests/print_schema/print_schema_with_multiple_schema/postgres/schema.sql
@@ -0,0 +1,2 @@
+CREATE TABLE users1 (id SERIAL PRIMARY KEY);
+CREATE TABLE users2 (id SERIAL PRIMARY KEY);
diff --git a/diesel_cli/tests/print_schema/print_schema_with_multiple_schema/sqlite/expected.snap b/diesel_cli/tests/print_schema/print_schema_with_multiple_schema/sqlite/expected.snap
new file mode 100644
index 000000000000..8dc866e3d2c0
--- /dev/null
+++ b/diesel_cli/tests/print_schema/print_schema_with_multiple_schema/sqlite/expected.snap
@@ -0,0 +1,53 @@
+---
+source: diesel_cli/tests/print_schema.rs
+description: "Test: print_schema_with_multiple_schema"
+---
+// @generated automatically by Diesel CLI.
+
+diesel::table! {
+    users1 (id) {
+        id -> Nullable<Integer>,
+    }
+}
+
+diesel::table! {
+    users2 (id) {
+        id -> Nullable<Integer>,
+    }
+}
+
+diesel::allow_tables_to_appear_in_same_query!(
+    users1,
+    users2,
+);
+// @generated automatically by Diesel CLI.
+
+diesel::table! {
+    /// Representation of the `users1` table.
+    ///
+    /// (Automatically generated by Diesel.)
+    users1 (id) {
+        /// The `id` column of the `users1` table.
+        ///
+        /// Its SQL type is `Nullable<Integer>`.
+        ///
+        /// (Automatically generated by Diesel.)
+        id -> Nullable<Integer>,
+    }
+}
+// @generated automatically by Diesel CLI.
+
+diesel::table! {
+    /// Representation of the `users2` table.
+    ///
+    /// (Automatically generated by Diesel.)
+    users2 (id) {
+        /// The `id` column of the `users2` table.
+        ///
+        /// Its SQL type is `Nullable<Integer>`.
+        ///
+        /// (Automatically generated by Diesel.)
+        id -> Nullable<Integer>,
+    }
+}
+
diff --git a/diesel_cli/tests/print_schema/print_schema_with_multiple_schema/sqlite/expected_1.snap b/diesel_cli/tests/print_schema/print_schema_with_multiple_schema/sqlite/expected_1.snap
new file mode 100644
index 000000000000..0c5b75e16470
--- /dev/null
+++ b/diesel_cli/tests/print_schema/print_schema_with_multiple_schema/sqlite/expected_1.snap
@@ -0,0 +1,20 @@
+---
+source: diesel_cli/tests/print_schema.rs
+description: "Test: print_schema_with_multiple_schema"
+---
+// @generated automatically by Diesel CLI.
+
+diesel::table! {
+    /// Representation of the `users1` table.
+    ///
+    /// (Automatically generated by Diesel.)
+    users1 (id) {
+        /// The `id` column of the `users1` table.
+        ///
+        /// Its SQL type is `Nullable<Integer>`.
+        ///
+        /// (Automatically generated by Diesel.)
+        id -> Nullable<Integer>,
+    }
+}
+
diff --git a/diesel_cli/tests/print_schema/print_schema_with_multiple_schema/sqlite/expected_2.snap b/diesel_cli/tests/print_schema/print_schema_with_multiple_schema/sqlite/expected_2.snap
new file mode 100644
index 000000000000..0b25a7b13fd7
--- /dev/null
+++ b/diesel_cli/tests/print_schema/print_schema_with_multiple_schema/sqlite/expected_2.snap
@@ -0,0 +1,20 @@
+---
+source: diesel_cli/tests/print_schema.rs
+description: "Test: print_schema_with_multiple_schema"
+---
+// @generated automatically by Diesel CLI.
+
+diesel::table! {
+    /// Representation of the `users2` table.
+    ///
+    /// (Automatically generated by Diesel.)
+    users2 (id) {
+        /// The `id` column of the `users2` table.
+        ///
+        /// Its SQL type is `Nullable<Integer>`.
+        ///
+        /// (Automatically generated by Diesel.)
+        id -> Nullable<Integer>,
+    }
+}
+
diff --git a/diesel_cli/tests/print_schema/print_schema_with_multiple_schema/sqlite/schema.sql b/diesel_cli/tests/print_schema/print_schema_with_multiple_schema/sqlite/schema.sql
new file mode 100644
index 000000000000..556e9f7b54e8
--- /dev/null
+++ b/diesel_cli/tests/print_schema/print_schema_with_multiple_schema/sqlite/schema.sql
@@ -0,0 +1,2 @@
+CREATE TABLE users1 (id INTEGER PRIMARY KEY);
+CREATE TABLE users2 (id INTEGER PRIMARY KEY);