mirror of
https://github.com/TaKO8Ki/gobang
synced 2024-10-31 03:20:33 +00:00
get foreign keys (#44)
This commit is contained in:
parent
2a0abf65cb
commit
1d7d6b48dc
38
src/app.rs
38
src/app.rs
@ -26,6 +26,7 @@ pub struct App {
|
||||
record_table: RecordTableComponent,
|
||||
column_table: TableComponent,
|
||||
constraint_table: TableComponent,
|
||||
foreign_key_table: TableComponent,
|
||||
focus: Focus,
|
||||
tab: TabComponent,
|
||||
help: HelpComponent,
|
||||
@ -45,6 +46,7 @@ impl App {
|
||||
record_table: RecordTableComponent::new(config.key_config.clone()),
|
||||
column_table: TableComponent::new(config.key_config.clone()),
|
||||
constraint_table: TableComponent::new(config.key_config.clone()),
|
||||
foreign_key_table: TableComponent::new(config.key_config.clone()),
|
||||
tab: TabComponent::new(config.key_config.clone()),
|
||||
help: HelpComponent::new(config.key_config.clone()),
|
||||
databases: DatabasesComponent::new(config.key_config.clone()),
|
||||
@ -104,6 +106,11 @@ impl App {
|
||||
right_chunks[1],
|
||||
matches!(self.focus, Focus::Table),
|
||||
)?,
|
||||
Tab::ForeignKeys => self.foreign_key_table.draw(
|
||||
f,
|
||||
right_chunks[1],
|
||||
matches!(self.focus, Focus::Table),
|
||||
)?,
|
||||
}
|
||||
self.error.draw(f, Rect::default(), false)?;
|
||||
self.help.draw(f, Rect::default(), false)?;
|
||||
@ -215,6 +222,24 @@ impl App {
|
||||
table.clone(),
|
||||
);
|
||||
}
|
||||
self.foreign_key_table.reset();
|
||||
let foreign_keys = self
|
||||
.pool
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.get_foreign_keys(&database, &table)
|
||||
.await?;
|
||||
if !constraints.is_empty() {
|
||||
self.foreign_key_table.update(
|
||||
foreign_keys
|
||||
.iter()
|
||||
.map(|c| c.columns())
|
||||
.collect::<Vec<Vec<String>>>(),
|
||||
constraints.get(0).unwrap().fields(),
|
||||
database.clone(),
|
||||
table.clone(),
|
||||
);
|
||||
}
|
||||
self.table_status
|
||||
.update(self.record_table.len() as u64, table);
|
||||
}
|
||||
@ -356,7 +381,18 @@ impl App {
|
||||
};
|
||||
|
||||
if key == self.config.key_config.copy {
|
||||
if let Some(text) = self.column_table.selected_cells() {
|
||||
if let Some(text) = self.constraint_table.selected_cells() {
|
||||
copy_to_clipboard(text.as_str())?
|
||||
}
|
||||
};
|
||||
}
|
||||
Tab::ForeignKeys => {
|
||||
if self.foreign_key_table.event(key)?.is_consumed() {
|
||||
return Ok(EventState::Consumed);
|
||||
};
|
||||
|
||||
if key == self.config.key_config.copy {
|
||||
if let Some(text) = self.foreign_key_table.selected_cells() {
|
||||
copy_to_clipboard(text.as_str())?
|
||||
}
|
||||
};
|
||||
|
@ -110,11 +110,21 @@ pub fn tab_constraints(key: &KeyConfig) -> CommandText {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn tab_foreign_keys(key: &KeyConfig) -> CommandText {
|
||||
CommandText::new(
|
||||
format!("Foreign keys [{}]", key.tab_foreign_keys),
|
||||
CMD_GROUP_TABLE,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn toggle_tabs(key_config: &KeyConfig) -> CommandText {
|
||||
CommandText::new(
|
||||
format!(
|
||||
"Tab [{},{},{}]",
|
||||
key_config.tab_records, key_config.tab_columns, key_config.tab_constraints
|
||||
"Tab [{},{},{},{}]",
|
||||
key_config.tab_records,
|
||||
key_config.tab_columns,
|
||||
key_config.tab_constraints,
|
||||
key_config.tab_foreign_keys
|
||||
),
|
||||
CMD_GROUP_GENERAL,
|
||||
)
|
||||
|
@ -18,6 +18,7 @@ pub enum Tab {
|
||||
Records,
|
||||
Columns,
|
||||
Constraints,
|
||||
ForeignKeys,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Tab {
|
||||
@ -48,6 +49,7 @@ impl TabComponent {
|
||||
command::tab_records(&self.key_config).name,
|
||||
command::tab_columns(&self.key_config).name,
|
||||
command::tab_constraints(&self.key_config).name,
|
||||
command::tab_foreign_keys(&self.key_config).name,
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -82,6 +84,9 @@ impl Component for TabComponent {
|
||||
} else if key == self.key_config.tab_constraints {
|
||||
self.selected_tab = Tab::Constraints;
|
||||
return Ok(EventState::Consumed);
|
||||
} else if key == self.key_config.tab_foreign_keys {
|
||||
self.selected_tab = Tab::ForeignKeys;
|
||||
return Ok(EventState::Consumed);
|
||||
}
|
||||
Ok(EventState::NotConsumed)
|
||||
}
|
||||
|
@ -81,6 +81,7 @@ pub struct KeyConfig {
|
||||
pub tab_records: Key,
|
||||
pub tab_columns: Key,
|
||||
pub tab_constraints: Key,
|
||||
pub tab_foreign_keys: Key,
|
||||
}
|
||||
|
||||
impl Default for KeyConfig {
|
||||
@ -111,6 +112,7 @@ impl Default for KeyConfig {
|
||||
tab_records: Key::Char('1'),
|
||||
tab_columns: Key::Char('2'),
|
||||
tab_constraints: Key::Char('3'),
|
||||
tab_foreign_keys: Key::Char('4'),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,11 @@ pub trait Pool {
|
||||
database: &Database,
|
||||
table: &Table,
|
||||
) -> anyhow::Result<Vec<Box<dyn TableRow>>>;
|
||||
async fn get_foreign_keys(
|
||||
&self,
|
||||
database: &Database,
|
||||
table: &Table,
|
||||
) -> anyhow::Result<Vec<Box<dyn TableRow>>>;
|
||||
async fn close(&self);
|
||||
}
|
||||
|
||||
|
@ -73,6 +73,41 @@ impl TableRow for Column {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ForeignKey {
|
||||
name: Option<String>,
|
||||
column_name: Option<String>,
|
||||
ref_table: Option<String>,
|
||||
ref_column: Option<String>,
|
||||
}
|
||||
|
||||
impl TableRow for ForeignKey {
|
||||
fn fields(&self) -> Vec<String> {
|
||||
vec![
|
||||
"name".to_string(),
|
||||
"column_name".to_string(),
|
||||
"ref_table".to_string(),
|
||||
"ref_column".to_string(),
|
||||
]
|
||||
}
|
||||
|
||||
fn columns(&self) -> Vec<String> {
|
||||
vec![
|
||||
self.name
|
||||
.as_ref()
|
||||
.map_or(String::new(), |name| name.to_string()),
|
||||
self.column_name
|
||||
.as_ref()
|
||||
.map_or(String::new(), |r#type| r#type.to_string()),
|
||||
self.ref_table
|
||||
.as_ref()
|
||||
.map_or(String::new(), |r#type| r#type.to_string()),
|
||||
self.ref_column
|
||||
.as_ref()
|
||||
.map_or(String::new(), |r#type| r#type.to_string()),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Pool for MySqlPool {
|
||||
async fn get_databases(&self) -> anyhow::Result<Vec<Database>> {
|
||||
@ -179,6 +214,8 @@ impl Pool for MySqlPool {
|
||||
FROM
|
||||
information_schema.KEY_COLUMN_USAGE
|
||||
WHERE
|
||||
REFERENCED_TABLE_SCHEMA IS NULL
|
||||
AND REFERENCED_TABLE_NAME IS NULL
|
||||
TABLE_SCHEMA = ?
|
||||
AND TABLE_NAME = ?
|
||||
",
|
||||
@ -196,6 +233,44 @@ impl Pool for MySqlPool {
|
||||
Ok(constraints)
|
||||
}
|
||||
|
||||
async fn get_foreign_keys(
|
||||
&self,
|
||||
database: &Database,
|
||||
table: &Table,
|
||||
) -> anyhow::Result<Vec<Box<dyn TableRow>>> {
|
||||
let mut rows = sqlx::query(
|
||||
"
|
||||
SELECT
|
||||
TABLE_NAME,
|
||||
COLUMN_NAME,
|
||||
CONSTRAINT_NAME,
|
||||
REFERENCED_TABLE_SCHEMA,
|
||||
REFERENCED_TABLE_NAME,
|
||||
REFERENCED_COLUMN_NAME
|
||||
FROM
|
||||
INFORMATION_SCHEMA.KEY_COLUMN_USAGE
|
||||
WHERE
|
||||
REFERENCED_TABLE_SCHEMA IS NOT NULL
|
||||
AND REFERENCED_TABLE_NAME IS NOT NULL
|
||||
AND TABLE_SCHEMA = ?
|
||||
AND TABLE_NAME = ?
|
||||
",
|
||||
)
|
||||
.bind(&database.name)
|
||||
.bind(&table.name)
|
||||
.fetch(&self.pool);
|
||||
let mut foreign_keys: Vec<Box<dyn TableRow>> = vec![];
|
||||
while let Some(row) = rows.try_next().await? {
|
||||
foreign_keys.push(Box::new(ForeignKey {
|
||||
name: row.try_get("CONSTRAINT_NAME")?,
|
||||
column_name: row.try_get("COLUMN_NAME")?,
|
||||
ref_table: row.try_get("REFERENCED_TABLE_NAME")?,
|
||||
ref_column: row.try_get("REFERENCED_COLUMN_NAME")?,
|
||||
}))
|
||||
}
|
||||
Ok(foreign_keys)
|
||||
}
|
||||
|
||||
async fn close(&self) {
|
||||
self.pool.close().await;
|
||||
}
|
||||
|
@ -74,6 +74,41 @@ impl TableRow for Column {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ForeignKey {
|
||||
name: Option<String>,
|
||||
column_name: Option<String>,
|
||||
ref_table: Option<String>,
|
||||
ref_column: Option<String>,
|
||||
}
|
||||
|
||||
impl TableRow for ForeignKey {
|
||||
fn fields(&self) -> Vec<String> {
|
||||
vec![
|
||||
"name".to_string(),
|
||||
"column_name".to_string(),
|
||||
"ref_table".to_string(),
|
||||
"ref_column".to_string(),
|
||||
]
|
||||
}
|
||||
|
||||
fn columns(&self) -> Vec<String> {
|
||||
vec![
|
||||
self.name
|
||||
.as_ref()
|
||||
.map_or(String::new(), |name| name.to_string()),
|
||||
self.column_name
|
||||
.as_ref()
|
||||
.map_or(String::new(), |r#type| r#type.to_string()),
|
||||
self.ref_table
|
||||
.as_ref()
|
||||
.map_or(String::new(), |r#type| r#type.to_string()),
|
||||
self.ref_column
|
||||
.as_ref()
|
||||
.map_or(String::new(), |r#type| r#type.to_string()),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Pool for PostgresPool {
|
||||
async fn get_databases(&self) -> anyhow::Result<Vec<Database>> {
|
||||
@ -233,16 +268,22 @@ impl Pool for PostgresPool {
|
||||
let mut rows = sqlx::query(
|
||||
"
|
||||
SELECT
|
||||
tc.constraint_name, tc.table_name, kcu.column_name,
|
||||
tc.table_schema,
|
||||
tc.constraint_name,
|
||||
tc.table_name,
|
||||
kcu.column_name,
|
||||
ccu.table_schema AS foreign_table_schema,
|
||||
ccu.table_name AS foreign_table_name,
|
||||
ccu.column_name AS foreign_column_name
|
||||
FROM
|
||||
information_schema.table_constraints AS tc
|
||||
JOIN information_schema.key_column_usage AS kcu
|
||||
ON tc.constraint_name = kcu.constraint_name
|
||||
JOIN information_schema.constraint_column_usage AS ccu
|
||||
ON ccu.constraint_name = tc.constraint_name
|
||||
WHERE ccu.table_name = $1
|
||||
JOIN information_schema.key_column_usage AS kcu ON tc.constraint_name = kcu.constraint_name
|
||||
AND tc.table_schema = kcu.table_schema
|
||||
JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name
|
||||
AND ccu.table_schema = tc.table_schema
|
||||
WHERE
|
||||
NOT tc.constraint_type = 'FOREIGN KEY'
|
||||
AND tc.table_name = $1
|
||||
",
|
||||
)
|
||||
.bind(&table.name)
|
||||
@ -257,6 +298,46 @@ impl Pool for PostgresPool {
|
||||
Ok(constraints)
|
||||
}
|
||||
|
||||
async fn get_foreign_keys(
|
||||
&self,
|
||||
_database: &Database,
|
||||
table: &Table,
|
||||
) -> anyhow::Result<Vec<Box<dyn TableRow>>> {
|
||||
let mut rows = sqlx::query(
|
||||
"
|
||||
SELECT
|
||||
tc.table_schema,
|
||||
tc.constraint_name,
|
||||
tc.table_name,
|
||||
kcu.column_name,
|
||||
ccu.table_schema AS foreign_table_schema,
|
||||
ccu.table_name AS foreign_table_name,
|
||||
ccu.column_name AS foreign_column_name
|
||||
FROM
|
||||
information_schema.table_constraints AS tc
|
||||
JOIN information_schema.key_column_usage AS kcu ON tc.constraint_name = kcu.constraint_name
|
||||
AND tc.table_schema = kcu.table_schema
|
||||
JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name
|
||||
AND ccu.table_schema = tc.table_schema
|
||||
WHERE
|
||||
tc.constraint_type = 'FOREIGN KEY'
|
||||
AND tc.table_name = $1
|
||||
",
|
||||
)
|
||||
.bind(&table.name)
|
||||
.fetch(&self.pool);
|
||||
let mut constraints: Vec<Box<dyn TableRow>> = vec![];
|
||||
while let Some(row) = rows.try_next().await? {
|
||||
constraints.push(Box::new(ForeignKey {
|
||||
name: row.try_get("constraint_name")?,
|
||||
column_name: row.try_get("column_name")?,
|
||||
ref_table: row.try_get("foreign_table_name")?,
|
||||
ref_column: row.try_get("foreign_column_name")?,
|
||||
}))
|
||||
}
|
||||
Ok(constraints)
|
||||
}
|
||||
|
||||
async fn close(&self) {
|
||||
self.pool.close().await;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user