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,
|
record_table: RecordTableComponent,
|
||||||
column_table: TableComponent,
|
column_table: TableComponent,
|
||||||
constraint_table: TableComponent,
|
constraint_table: TableComponent,
|
||||||
|
foreign_key_table: TableComponent,
|
||||||
focus: Focus,
|
focus: Focus,
|
||||||
tab: TabComponent,
|
tab: TabComponent,
|
||||||
help: HelpComponent,
|
help: HelpComponent,
|
||||||
@ -45,6 +46,7 @@ impl App {
|
|||||||
record_table: RecordTableComponent::new(config.key_config.clone()),
|
record_table: RecordTableComponent::new(config.key_config.clone()),
|
||||||
column_table: TableComponent::new(config.key_config.clone()),
|
column_table: TableComponent::new(config.key_config.clone()),
|
||||||
constraint_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()),
|
tab: TabComponent::new(config.key_config.clone()),
|
||||||
help: HelpComponent::new(config.key_config.clone()),
|
help: HelpComponent::new(config.key_config.clone()),
|
||||||
databases: DatabasesComponent::new(config.key_config.clone()),
|
databases: DatabasesComponent::new(config.key_config.clone()),
|
||||||
@ -104,6 +106,11 @@ impl App {
|
|||||||
right_chunks[1],
|
right_chunks[1],
|
||||||
matches!(self.focus, Focus::Table),
|
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.error.draw(f, Rect::default(), false)?;
|
||||||
self.help.draw(f, Rect::default(), false)?;
|
self.help.draw(f, Rect::default(), false)?;
|
||||||
@ -215,6 +222,24 @@ impl App {
|
|||||||
table.clone(),
|
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
|
self.table_status
|
||||||
.update(self.record_table.len() as u64, table);
|
.update(self.record_table.len() as u64, table);
|
||||||
}
|
}
|
||||||
@ -356,7 +381,18 @@ impl App {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if key == self.config.key_config.copy {
|
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())?
|
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 {
|
pub fn toggle_tabs(key_config: &KeyConfig) -> CommandText {
|
||||||
CommandText::new(
|
CommandText::new(
|
||||||
format!(
|
format!(
|
||||||
"Tab [{},{},{}]",
|
"Tab [{},{},{},{}]",
|
||||||
key_config.tab_records, key_config.tab_columns, key_config.tab_constraints
|
key_config.tab_records,
|
||||||
|
key_config.tab_columns,
|
||||||
|
key_config.tab_constraints,
|
||||||
|
key_config.tab_foreign_keys
|
||||||
),
|
),
|
||||||
CMD_GROUP_GENERAL,
|
CMD_GROUP_GENERAL,
|
||||||
)
|
)
|
||||||
|
@ -18,6 +18,7 @@ pub enum Tab {
|
|||||||
Records,
|
Records,
|
||||||
Columns,
|
Columns,
|
||||||
Constraints,
|
Constraints,
|
||||||
|
ForeignKeys,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for Tab {
|
impl std::fmt::Display for Tab {
|
||||||
@ -48,6 +49,7 @@ impl TabComponent {
|
|||||||
command::tab_records(&self.key_config).name,
|
command::tab_records(&self.key_config).name,
|
||||||
command::tab_columns(&self.key_config).name,
|
command::tab_columns(&self.key_config).name,
|
||||||
command::tab_constraints(&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 {
|
} else if key == self.key_config.tab_constraints {
|
||||||
self.selected_tab = Tab::Constraints;
|
self.selected_tab = Tab::Constraints;
|
||||||
return Ok(EventState::Consumed);
|
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)
|
Ok(EventState::NotConsumed)
|
||||||
}
|
}
|
||||||
|
@ -81,6 +81,7 @@ pub struct KeyConfig {
|
|||||||
pub tab_records: Key,
|
pub tab_records: Key,
|
||||||
pub tab_columns: Key,
|
pub tab_columns: Key,
|
||||||
pub tab_constraints: Key,
|
pub tab_constraints: Key,
|
||||||
|
pub tab_foreign_keys: Key,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for KeyConfig {
|
impl Default for KeyConfig {
|
||||||
@ -111,6 +112,7 @@ impl Default for KeyConfig {
|
|||||||
tab_records: Key::Char('1'),
|
tab_records: Key::Char('1'),
|
||||||
tab_columns: Key::Char('2'),
|
tab_columns: Key::Char('2'),
|
||||||
tab_constraints: Key::Char('3'),
|
tab_constraints: Key::Char('3'),
|
||||||
|
tab_foreign_keys: Key::Char('4'),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,11 @@ pub trait Pool {
|
|||||||
database: &Database,
|
database: &Database,
|
||||||
table: &Table,
|
table: &Table,
|
||||||
) -> anyhow::Result<Vec<Box<dyn TableRow>>>;
|
) -> 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);
|
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]
|
#[async_trait]
|
||||||
impl Pool for MySqlPool {
|
impl Pool for MySqlPool {
|
||||||
async fn get_databases(&self) -> anyhow::Result<Vec<Database>> {
|
async fn get_databases(&self) -> anyhow::Result<Vec<Database>> {
|
||||||
@ -179,6 +214,8 @@ impl Pool for MySqlPool {
|
|||||||
FROM
|
FROM
|
||||||
information_schema.KEY_COLUMN_USAGE
|
information_schema.KEY_COLUMN_USAGE
|
||||||
WHERE
|
WHERE
|
||||||
|
REFERENCED_TABLE_SCHEMA IS NULL
|
||||||
|
AND REFERENCED_TABLE_NAME IS NULL
|
||||||
TABLE_SCHEMA = ?
|
TABLE_SCHEMA = ?
|
||||||
AND TABLE_NAME = ?
|
AND TABLE_NAME = ?
|
||||||
",
|
",
|
||||||
@ -196,6 +233,44 @@ impl Pool for MySqlPool {
|
|||||||
Ok(constraints)
|
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) {
|
async fn close(&self) {
|
||||||
self.pool.close().await;
|
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]
|
#[async_trait]
|
||||||
impl Pool for PostgresPool {
|
impl Pool for PostgresPool {
|
||||||
async fn get_databases(&self) -> anyhow::Result<Vec<Database>> {
|
async fn get_databases(&self) -> anyhow::Result<Vec<Database>> {
|
||||||
@ -233,16 +268,22 @@ impl Pool for PostgresPool {
|
|||||||
let mut rows = sqlx::query(
|
let mut rows = sqlx::query(
|
||||||
"
|
"
|
||||||
SELECT
|
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.table_name AS foreign_table_name,
|
||||||
ccu.column_name AS foreign_column_name
|
ccu.column_name AS foreign_column_name
|
||||||
FROM
|
FROM
|
||||||
information_schema.table_constraints AS tc
|
information_schema.table_constraints AS tc
|
||||||
JOIN information_schema.key_column_usage AS kcu
|
JOIN information_schema.key_column_usage AS kcu ON tc.constraint_name = kcu.constraint_name
|
||||||
ON tc.constraint_name = kcu.constraint_name
|
AND tc.table_schema = kcu.table_schema
|
||||||
JOIN information_schema.constraint_column_usage AS ccu
|
JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name
|
||||||
ON ccu.constraint_name = tc.constraint_name
|
AND ccu.table_schema = tc.table_schema
|
||||||
WHERE ccu.table_name = $1
|
WHERE
|
||||||
|
NOT tc.constraint_type = 'FOREIGN KEY'
|
||||||
|
AND tc.table_name = $1
|
||||||
",
|
",
|
||||||
)
|
)
|
||||||
.bind(&table.name)
|
.bind(&table.name)
|
||||||
@ -257,6 +298,46 @@ impl Pool for PostgresPool {
|
|||||||
Ok(constraints)
|
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) {
|
async fn close(&self) {
|
||||||
self.pool.close().await;
|
self.pool.close().await;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user