|
|
|
@ -141,7 +141,7 @@ impl TableRow for Index {
|
|
|
|
|
#[async_trait]
|
|
|
|
|
impl Pool for SqlitePool {
|
|
|
|
|
async fn get_databases(&self) -> anyhow::Result<Vec<Database>> {
|
|
|
|
|
let databases = sqlx::query("SHOW DATABASES")
|
|
|
|
|
let databases = sqlx::query("SELECT name FROM pragma_database_list")
|
|
|
|
|
.fetch_all(&self.pool)
|
|
|
|
|
.await?
|
|
|
|
|
.iter()
|
|
|
|
@ -157,25 +157,32 @@ impl Pool for SqlitePool {
|
|
|
|
|
Ok(list)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn get_tables(&self, database: String) -> anyhow::Result<Vec<Child>> {
|
|
|
|
|
let tables =
|
|
|
|
|
sqlx::query_as::<_, Table>(format!("SHOW TABLE STATUS FROM `{}`", database).as_str())
|
|
|
|
|
.fetch_all(&self.pool)
|
|
|
|
|
.await?;
|
|
|
|
|
async fn get_tables(&self, _database: String) -> anyhow::Result<Vec<Child>> {
|
|
|
|
|
let mut rows =
|
|
|
|
|
sqlx::query("SELECT name FROM sqlite_master WHERE type = 'table'").fetch(&self.pool);
|
|
|
|
|
let mut tables = Vec::new();
|
|
|
|
|
while let Some(row) = rows.try_next().await? {
|
|
|
|
|
tables.push(Table {
|
|
|
|
|
name: row.try_get("name")?,
|
|
|
|
|
create_time: None,
|
|
|
|
|
update_time: None,
|
|
|
|
|
engine: None,
|
|
|
|
|
schema: None,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
Ok(tables.into_iter().map(|table| table.into()).collect())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn get_records(
|
|
|
|
|
&self,
|
|
|
|
|
database: &Database,
|
|
|
|
|
_database: &Database,
|
|
|
|
|
table: &Table,
|
|
|
|
|
page: u16,
|
|
|
|
|
filter: Option<String>,
|
|
|
|
|
) -> anyhow::Result<(Vec<String>, Vec<Vec<String>>)> {
|
|
|
|
|
let query = if let Some(filter) = filter {
|
|
|
|
|
format!(
|
|
|
|
|
"SELECT * FROM `{database}`.`{table}` WHERE {filter} LIMIT {page}, {limit}",
|
|
|
|
|
database = database.name,
|
|
|
|
|
"SELECT * FROM `{table}` WHERE {filter} LIMIT {page}, {limit}",
|
|
|
|
|
table = table.name,
|
|
|
|
|
filter = filter,
|
|
|
|
|
page = page,
|
|
|
|
@ -183,8 +190,7 @@ impl Pool for SqlitePool {
|
|
|
|
|
)
|
|
|
|
|
} else {
|
|
|
|
|
format!(
|
|
|
|
|
"SELECT * FROM `{}`.`{}` limit {page}, {limit}",
|
|
|
|
|
database.name,
|
|
|
|
|
"SELECT * FROM `{}` LIMIT {page}, {limit}",
|
|
|
|
|
table.name,
|
|
|
|
|
page = page,
|
|
|
|
|
limit = RECORDS_LIMIT_PER_PAGE
|
|
|
|
@ -210,22 +216,24 @@ impl Pool for SqlitePool {
|
|
|
|
|
|
|
|
|
|
async fn get_columns(
|
|
|
|
|
&self,
|
|
|
|
|
database: &Database,
|
|
|
|
|
_database: &Database,
|
|
|
|
|
table: &Table,
|
|
|
|
|
) -> anyhow::Result<Vec<Box<dyn TableRow>>> {
|
|
|
|
|
let query = format!(
|
|
|
|
|
"SHOW FULL COLUMNS FROM `{}`.`{}`",
|
|
|
|
|
database.name, table.name
|
|
|
|
|
);
|
|
|
|
|
let query = format!("SELECT * FROM pragma_table_info('{}');", table.name);
|
|
|
|
|
let mut rows = sqlx::query(query.as_str()).fetch(&self.pool);
|
|
|
|
|
let mut columns: Vec<Box<dyn TableRow>> = vec![];
|
|
|
|
|
while let Some(row) = rows.try_next().await? {
|
|
|
|
|
let null: Option<i16> = row.try_get("notnull")?;
|
|
|
|
|
columns.push(Box::new(Column {
|
|
|
|
|
name: row.try_get("Field")?,
|
|
|
|
|
r#type: row.try_get("Type")?,
|
|
|
|
|
null: row.try_get("Null")?,
|
|
|
|
|
default: row.try_get("Default")?,
|
|
|
|
|
comment: row.try_get("Comment")?,
|
|
|
|
|
name: row.try_get("name")?,
|
|
|
|
|
r#type: row.try_get("type")?,
|
|
|
|
|
null: if matches!(null, Some(null) if null == 1) {
|
|
|
|
|
Some("✔︎".to_string())
|
|
|
|
|
} else {
|
|
|
|
|
Some("".to_string())
|
|
|
|
|
},
|
|
|
|
|
default: row.try_get("dflt_value")?,
|
|
|
|
|
comment: None,
|
|
|
|
|
}))
|
|
|
|
|
}
|
|
|
|
|
Ok(columns)
|
|
|
|
@ -233,31 +241,29 @@ impl Pool for SqlitePool {
|
|
|
|
|
|
|
|
|
|
async fn get_constraints(
|
|
|
|
|
&self,
|
|
|
|
|
database: &Database,
|
|
|
|
|
_database: &Database,
|
|
|
|
|
table: &Table,
|
|
|
|
|
) -> anyhow::Result<Vec<Box<dyn TableRow>>> {
|
|
|
|
|
let mut rows = sqlx::query(
|
|
|
|
|
"
|
|
|
|
|
SELECT
|
|
|
|
|
COLUMN_NAME,
|
|
|
|
|
CONSTRAINT_NAME
|
|
|
|
|
FROM
|
|
|
|
|
information_schema.KEY_COLUMN_USAGE
|
|
|
|
|
WHERE
|
|
|
|
|
REFERENCED_TABLE_SCHEMA IS NULL
|
|
|
|
|
AND REFERENCED_TABLE_NAME IS NULL
|
|
|
|
|
AND TABLE_SCHEMA = ?
|
|
|
|
|
AND TABLE_NAME = ?
|
|
|
|
|
",
|
|
|
|
|
SELECT
|
|
|
|
|
m.name AS index_name,
|
|
|
|
|
p.*
|
|
|
|
|
FROM
|
|
|
|
|
sqlite_master m,
|
|
|
|
|
pragma_index_info(m.name) p
|
|
|
|
|
WHERE
|
|
|
|
|
m.type = 'index'
|
|
|
|
|
AND tbl_name = ?
|
|
|
|
|
",
|
|
|
|
|
)
|
|
|
|
|
.bind(&database.name)
|
|
|
|
|
.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(Constraint {
|
|
|
|
|
name: row.try_get("CONSTRAINT_NAME")?,
|
|
|
|
|
column_name: row.try_get("COLUMN_NAME")?,
|
|
|
|
|
name: row.try_get("index_name")?,
|
|
|
|
|
column_name: row.try_get("name")?,
|
|
|
|
|
}))
|
|
|
|
|
}
|
|
|
|
|
Ok(constraints)
|
|
|
|
@ -265,37 +271,33 @@ impl Pool for SqlitePool {
|
|
|
|
|
|
|
|
|
|
async fn get_foreign_keys(
|
|
|
|
|
&self,
|
|
|
|
|
database: &Database,
|
|
|
|
|
_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 = ?
|
|
|
|
|
SELECT
|
|
|
|
|
m.name AS index_name,
|
|
|
|
|
f.`from`,
|
|
|
|
|
f.`to`,
|
|
|
|
|
f.`table`
|
|
|
|
|
FROM
|
|
|
|
|
sqlite_master m,
|
|
|
|
|
pragma_index_info(m.name) p
|
|
|
|
|
INNER JOIN pragma_foreign_key_list(m.tbl_name) f ON f.`from` = p.name
|
|
|
|
|
WHERE
|
|
|
|
|
tbl_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")?,
|
|
|
|
|
name: row.try_get("index_name")?,
|
|
|
|
|
column_name: row.try_get("from")?,
|
|
|
|
|
ref_table: row.try_get("table")?,
|
|
|
|
|
ref_column: row.try_get("to")?,
|
|
|
|
|
}))
|
|
|
|
|
}
|
|
|
|
|
Ok(foreign_keys)
|
|
|
|
@ -303,32 +305,30 @@ impl Pool for SqlitePool {
|
|
|
|
|
|
|
|
|
|
async fn get_indexes(
|
|
|
|
|
&self,
|
|
|
|
|
database: &Database,
|
|
|
|
|
_database: &Database,
|
|
|
|
|
table: &Table,
|
|
|
|
|
) -> anyhow::Result<Vec<Box<dyn TableRow>>> {
|
|
|
|
|
let mut rows = sqlx::query(
|
|
|
|
|
"
|
|
|
|
|
SELECT
|
|
|
|
|
DISTINCT TABLE_NAME,
|
|
|
|
|
INDEX_NAME,
|
|
|
|
|
INDEX_TYPE,
|
|
|
|
|
COLUMN_NAME
|
|
|
|
|
FROM
|
|
|
|
|
INFORMATION_SCHEMA.STATISTICS
|
|
|
|
|
WHERE
|
|
|
|
|
TABLE_SCHEMA = ?
|
|
|
|
|
AND TABLE_NAME = ?
|
|
|
|
|
",
|
|
|
|
|
SELECT
|
|
|
|
|
m.name AS index_name,
|
|
|
|
|
p.*
|
|
|
|
|
FROM
|
|
|
|
|
sqlite_master m,
|
|
|
|
|
pragma_index_info(m.name) p
|
|
|
|
|
WHERE
|
|
|
|
|
m.type = 'index'
|
|
|
|
|
AND tbl_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(Index {
|
|
|
|
|
name: row.try_get("INDEX_NAME")?,
|
|
|
|
|
column_name: row.try_get("COLUMN_NAME")?,
|
|
|
|
|
r#type: row.try_get("INDEX_TYPE")?,
|
|
|
|
|
name: row.try_get("index_name")?,
|
|
|
|
|
column_name: row.try_get("name")?,
|
|
|
|
|
r#type: Some(String::new()),
|
|
|
|
|
}))
|
|
|
|
|
}
|
|
|
|
|
Ok(foreign_keys)
|
|
|
|
|