Auto Upgrade

This commit is contained in:
jipengfei-jpf
2023-07-23 15:51:37 +08:00
parent 339d3eca4d
commit 2f42d74c60
15 changed files with 450 additions and 306 deletions

View File

@ -1,19 +1,52 @@
package ai.chat2db.plugin.postgresql;
import java.sql.Connection;
import java.sql.SQLException;
import ai.chat2db.spi.DBManage;
import ai.chat2db.spi.jdbc.DefaultDBManage;
import ai.chat2db.spi.sql.SQLExecutor;
import ai.chat2db.spi.sql.ConnectInfo;
public class PostgreSQLDBManage extends DefaultDBManage implements DBManage {
@Override
public void connectDatabase(Connection connection, String database) {
try {
SQLExecutor.getInstance().execute(connection,"SELECT pg_database_size('"+database+"');");
} catch (SQLException e) {
throw new RuntimeException(e);
}
//try {
// SQLExecutor.getInstance().execute(connection,"SELECT pg_database_size('"+database+"');");
//} catch (SQLException e) {
// throw new RuntimeException(e);
//}
}
@Override
public Connection getConnection(ConnectInfo connectInfo) {
String url = connectInfo.getUrl();
String database = connectInfo.getDatabaseName();
if (database != null && !database.isEmpty()) {
url = replaceDatabaseInJdbcUrl(url, database);
}
connectInfo.setUrl(url);
return super.getConnection(connectInfo);
}
public String replaceDatabaseInJdbcUrl(String url, String newDatabase) {
// 先在"?"字符处分割字符串,处理查询参数
String[] urlAndParams = url.split("\\?");
String urlWithoutParams = urlAndParams[0];
// 在URL中的"/"字符处分割字符串
String[] parts = urlWithoutParams.split("/");
// 取最后一部分,即数据库名,并替换为新的数据库名
parts[parts.length - 1] = newDatabase;
// 将修改后的部分重新组合成URL
String newUrlWithoutParams = String.join("/", parts);
// 如果存在查询参数,重新添加
String newUrl = urlAndParams.length > 1 ? newUrlWithoutParams + "?" + urlAndParams[1] : newUrlWithoutParams;
return newUrl;
}
}

View File

@ -2,172 +2,25 @@ package ai.chat2db.plugin.postgresql;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import ai.chat2db.spi.MetaData;
import ai.chat2db.spi.jdbc.DefaultMetaService;
import ai.chat2db.spi.model.Database;
import ai.chat2db.spi.model.Schema;
import ai.chat2db.spi.sql.SQLExecutor;
import static ai.chat2db.plugin.postgresql.consts.SQLConst.FUNCTION_SQL;
public class PostgreSQLMetaData extends DefaultMetaService implements MetaData {
private String functionSQL =
" CREATE OR REPLACE FUNCTION showcreatetable(namespace character varying, tablename character "
+ "varying)\n"
+ " RETURNS character varying AS\n"
+ "\n"
+ " $BODY$\n"
+ " declare\n"
+ " tableScript character varying default '';\n"
+ "\n"
+ " begin\n"
+ " -- columns\n"
+ " tableScript:=tableScript || ' CREATE TABLE '|| tablename|| ' ( '|| chr(13)||chr(10) || "
+ "array_to_string"
+ "(\n"
+ " array(\n"
+ " select ' ' || concat_ws(' ',fieldName, fieldType, fieldLen, indexType, isNullStr, fieldComment"
+ " ) as "
+ "column_line\n"
+ " from (\n"
+ " select a.attname as fieldName,format_type(a.atttypid,a.atttypmod) as fieldType,(case when "
+ "atttypmod-4>0 then\n"
+ " atttypmod-4 else 0 end) as fieldLen,\n"
+ " (case when (select count(*) from pg_constraint where conrelid = a.attrelid and conkey[1]=attnum "
+ "and\n"
+ " contype='p')>0 then 'PRI'\n"
+ " when (select count(*) from pg_constraint where conrelid = a.attrelid and conkey[1]=attnum and "
+ "contype='u')>0\n"
+ " then 'UNI'\n"
+ " when (select count(*) from pg_constraint where conrelid = a.attrelid and conkey[1]=attnum and "
+ "contype='f')>0\n"
+ " then 'FRI'\n"
+ " else '' end) as indexType,\n"
+ " (case when a.attnotnull=true then 'not null' else 'null' end) as isNullStr,\n"
+ " ' comment ' || col_description(a.attrelid,a.attnum) as fieldComment\n"
+ " from pg_attribute a where attstattarget=-1 and attrelid = (select c.oid from pg_class c,"
+ "pg_namespace n"
+ " where\n"
+ " c.relnamespace=n.oid and n.nspname =namespace and relname =tablename)\n"
+ "\n"
+ " ) as string_columns\n"
+ " ),','||chr(13)||chr(10)) || ',';\n"
+ "\n"
+ "\n"
+ " -- 约束\n"
+ " tableScript:= tableScript || chr(13)||chr(10) || array_to_string(\n"
+ " array(\n"
+ " select concat(' CONSTRAINT ',conname ,c ,u,p,f) from (\n"
+ " select conname,\n"
+ " case when contype='c' then ' CHECK('|| ( select findattname(namespace,tablename,'c') ) ||')' "
+ "end "
+ "as c "
+ ",\n"
+ " case when contype='u' then ' UNIQUE('|| ( select findattname(namespace,tablename,'u') ) ||')' "
+ "end "
+ "as u"
+ " ,\n"
+ " case when contype='p' then ' PRIMARY KEY ('|| ( select findattname(namespace,tablename,'p') ) "
+ "||')' "
+ "end as p ,\n"
+ " case when contype='f' then ' FOREIGN KEY('|| ( select findattname(namespace,tablename,'u') ) "
+ "||') "
+ "REFERENCES '||\n"
+ " (select p.relname from pg_class p where p.oid=c.confrelid ) || '('|| ( select\n"
+ " findattname(namespace,tablename,'u') ) ||')' end as f\n"
+ " from pg_constraint c\n"
+ " where contype in('u','c','f','p') and conrelid=(\n"
+ " select oid from pg_class where relname=tablename and relnamespace =(\n"
+ " select oid from pg_namespace where nspname = namespace\n"
+ " )\n"
+ " )\n"
+ " ) as t\n"
+ " ) ,',' || chr(13)||chr(10) ) || chr(13)||chr(10) ||' ); ';\n"
+ "\n"
+ " -- indexs\n"
+ " -- CREATE UNIQUE INDEX pg_language_oid_index ON pg_language USING btree (oid); -- table "
+ "pg_language\n"
+ "\n"
+ "\n"
+ " --\n"
+ " /** **/\n"
+ " --- 获取非约束索引 column\n"
+ " -- CREATE UNIQUE INDEX pg_language_oid_index ON pg_language USING btree (oid); -- table "
+ "pg_language\n"
+ " tableScript:= tableScript || chr(13)||chr(10) || chr(13)||chr(10) || array_to_string(\n"
+ " array(\n"
+ " select 'CREATE INDEX ' || indexrelname || ' ON ' || tablename || ' USING btree '|| '(' || "
+ "attname "
+ "|| "
+ "');' from (\n"
+ " SELECT\n"
+ " i.relname AS indexrelname , x.indkey,\n"
+ "\n"
+ " ( select array_to_string (\n"
+ " array(\n"
+ " select a.attname from pg_attribute a where attrelid=c.oid and a.attnum in ( select unnest(x"
+ ".indkey) )\n"
+ "\n"
+ " )\n"
+ " ,',' ) )as attname\n"
+ "\n"
+ " FROM pg_class c\n"
+ " JOIN pg_index x ON c.oid = x.indrelid\n"
+ " JOIN pg_class i ON i.oid = x.indexrelid\n"
+ " LEFT JOIN pg_namespace n ON n.oid = c.relnamespace\n"
+ " WHERE c.relname=tablename and i.relname not in\n"
+ " ( select constraint_name from information_schema.key_column_usage where table_name=tablename )\n"
+ " )as t\n"
+ " ) ,','|| chr(13)||chr(10));\n"
+ "\n"
+ "\n"
+ " -- COMMENT COMMENT ON COLUMN sys_activity.id IS '主键';\n"
+ " tableScript:= tableScript || chr(13)||chr(10) || chr(13)||chr(10) || array_to_string(\n"
+ " array(\n"
+ " SELECT 'COMMENT ON COLUMN' || tablename || '.' || a.attname ||' IS '|| ''''|| d.description "
+ "||''''\n"
+ " FROM pg_class c\n"
+ " JOIN pg_description d ON c.oid=d.objoid\n"
+ " JOIN pg_attribute a ON c.oid = a.attrelid\n"
+ " WHERE c.relname=tablename\n"
+ " AND a.attnum = d.objsubid),','|| chr(13)||chr(10)) ;\n"
+ "\n"
+ " return tableScript;\n"
+ "\n"
+ " end\n"
+ " $BODY$ LANGUAGE plpgsql;\n"
+ "\n"
+ " CREATE OR REPLACE FUNCTION findattname(namespace character varying, tablename character "
+ "varying, "
+ "ctype"
+ " character\n"
+ " varying)\n"
+ " RETURNS character varying as $BODY$\n"
+ "\n"
+ " declare\n"
+ " tt oid ;\n"
+ " aname character varying default '';\n"
+ "\n"
+ " begin\n"
+ " tt := oid from pg_class where relname= tablename and relnamespace =(select oid from "
+ "pg_namespace "
+ "where\n"
+ " nspname=namespace) ;\n"
+ " aname:= array_to_string(\n"
+ " array(\n"
+ " select a.attname from pg_attribute a\n"
+ " where a.attrelid=tt and a.attnum in (\n"
+ " select unnest(conkey) from pg_constraint c where contype=ctype\n"
+ " and conrelid=tt and array_to_string(conkey,',') is not null\n"
+ " )\n"
+ " ),',');\n"
+ "\n"
+ " return aname;\n"
+ " end\n"
+ " $BODY$ LANGUAGE plpgsql";
@Override
public String tableDDL(Connection connection, String databaseName, String schemaName, String tableName) {
SQLExecutor.getInstance().executeSql(connection,functionSQL.replaceFirst("tableSchema", schemaName), resultSet -> null);
SQLExecutor.getInstance().executeSql(connection, FUNCTION_SQL.replaceFirst("tableSchema", schemaName),
resultSet -> null);
String ddlSql = "select showcreatetable('" + schemaName + "','" + tableName + "') as sql";
return SQLExecutor.getInstance().executeSql(connection,ddlSql, resultSet -> {
return SQLExecutor.getInstance().executeSql(connection, ddlSql, resultSet -> {
try {
if (resultSet.next()) {
return resultSet.getString("sql");
@ -179,4 +32,46 @@ public class PostgreSQLMetaData extends DefaultMetaService implements MetaData {
});
}
@Override
public List<Database> databases(Connection connection) {
return SQLExecutor.getInstance().executeSql(connection, "SELECT datname FROM pg_database;", resultSet -> {
List<Database> databases = new ArrayList<>();
try {
while (resultSet.next()) {
String dbName = resultSet.getString("datname");
if ("template0".equals(dbName) || "template1".equals(dbName)) {
continue;
}
Database database = new Database();
database.setName(dbName);
databases.add(database);
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
return databases;
});
}
@Override
public List<Schema> schemas(Connection connection, String databaseName) {
return SQLExecutor.getInstance().executeSql(connection,
"SELECT catalog_name, schema_name FROM information_schema.schemata;", resultSet -> {
List<Schema> databases = new ArrayList<>();
try {
while (resultSet.next()) {
Schema schema = new Schema();
String name = resultSet.getString("schema_name");
String catalogName = resultSet.getString("catalog_name");
schema.setName(name);
schema.setDatabaseName(catalogName);
databases.add(schema);
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
return databases;
});
}
}

View File

@ -0,0 +1,158 @@
package ai.chat2db.plugin.postgresql.consts;
public class SQLConst {
public static String FUNCTION_SQL =
" CREATE OR REPLACE FUNCTION showcreatetable(namespace character varying, tablename character "
+ "varying)\n"
+ " RETURNS character varying AS\n"
+ "\n"
+ " $BODY$\n"
+ " declare\n"
+ " tableScript character varying default '';\n"
+ "\n"
+ " begin\n"
+ " -- columns\n"
+ " tableScript:=tableScript || ' CREATE TABLE '|| tablename|| ' ( '|| chr(13)||chr(10) || "
+ "array_to_string"
+ "(\n"
+ " array(\n"
+ " select ' ' || concat_ws(' ',fieldName, fieldType, fieldLen, indexType, isNullStr, fieldComment"
+ " ) as "
+ "column_line\n"
+ " from (\n"
+ " select a.attname as fieldName,format_type(a.atttypid,a.atttypmod) as fieldType,(case when "
+ "atttypmod-4>0 then\n"
+ " atttypmod-4 else 0 end) as fieldLen,\n"
+ " (case when (select count(*) from pg_constraint where conrelid = a.attrelid and conkey[1]=attnum "
+ "and\n"
+ " contype='p')>0 then 'PRI'\n"
+ " when (select count(*) from pg_constraint where conrelid = a.attrelid and conkey[1]=attnum and "
+ "contype='u')>0\n"
+ " then 'UNI'\n"
+ " when (select count(*) from pg_constraint where conrelid = a.attrelid and conkey[1]=attnum and "
+ "contype='f')>0\n"
+ " then 'FRI'\n"
+ " else '' end) as indexType,\n"
+ " (case when a.attnotnull=true then 'not null' else 'null' end) as isNullStr,\n"
+ " ' comment ' || col_description(a.attrelid,a.attnum) as fieldComment\n"
+ " from pg_attribute a where attstattarget=-1 and attrelid = (select c.oid from pg_class c,"
+ "pg_namespace n"
+ " where\n"
+ " c.relnamespace=n.oid and n.nspname =namespace and relname =tablename)\n"
+ "\n"
+ " ) as string_columns\n"
+ " ),','||chr(13)||chr(10)) || ',';\n"
+ "\n"
+ "\n"
+ " -- 约束\n"
+ " tableScript:= tableScript || chr(13)||chr(10) || array_to_string(\n"
+ " array(\n"
+ " select concat(' CONSTRAINT ',conname ,c ,u,p,f) from (\n"
+ " select conname,\n"
+ " case when contype='c' then ' CHECK('|| ( select findattname(namespace,tablename,'c') ) ||')' "
+ "end "
+ "as c "
+ ",\n"
+ " case when contype='u' then ' UNIQUE('|| ( select findattname(namespace,tablename,'u') ) ||')' "
+ "end "
+ "as u"
+ " ,\n"
+ " case when contype='p' then ' PRIMARY KEY ('|| ( select findattname(namespace,tablename,'p') ) "
+ "||')' "
+ "end as p ,\n"
+ " case when contype='f' then ' FOREIGN KEY('|| ( select findattname(namespace,tablename,'u') ) "
+ "||') "
+ "REFERENCES '||\n"
+ " (select p.relname from pg_class p where p.oid=c.confrelid ) || '('|| ( select\n"
+ " findattname(namespace,tablename,'u') ) ||')' end as f\n"
+ " from pg_constraint c\n"
+ " where contype in('u','c','f','p') and conrelid=(\n"
+ " select oid from pg_class where relname=tablename and relnamespace =(\n"
+ " select oid from pg_namespace where nspname = namespace\n"
+ " )\n"
+ " )\n"
+ " ) as t\n"
+ " ) ,',' || chr(13)||chr(10) ) || chr(13)||chr(10) ||' ); ';\n"
+ "\n"
+ " -- indexs\n"
+ " -- CREATE UNIQUE INDEX pg_language_oid_index ON pg_language USING btree (oid); -- table "
+ "pg_language\n"
+ "\n"
+ "\n"
+ " --\n"
+ " /** **/\n"
+ " --- 获取非约束索引 column\n"
+ " -- CREATE UNIQUE INDEX pg_language_oid_index ON pg_language USING btree (oid); -- table "
+ "pg_language\n"
+ " tableScript:= tableScript || chr(13)||chr(10) || chr(13)||chr(10) || array_to_string(\n"
+ " array(\n"
+ " select 'CREATE INDEX ' || indexrelname || ' ON ' || tablename || ' USING btree '|| '(' || "
+ "attname "
+ "|| "
+ "');' from (\n"
+ " SELECT\n"
+ " i.relname AS indexrelname , x.indkey,\n"
+ "\n"
+ " ( select array_to_string (\n"
+ " array(\n"
+ " select a.attname from pg_attribute a where attrelid=c.oid and a.attnum in ( select unnest(x"
+ ".indkey) )\n"
+ "\n"
+ " )\n"
+ " ,',' ) )as attname\n"
+ "\n"
+ " FROM pg_class c\n"
+ " JOIN pg_index x ON c.oid = x.indrelid\n"
+ " JOIN pg_class i ON i.oid = x.indexrelid\n"
+ " LEFT JOIN pg_namespace n ON n.oid = c.relnamespace\n"
+ " WHERE c.relname=tablename and i.relname not in\n"
+ " ( select constraint_name from information_schema.key_column_usage where table_name=tablename )\n"
+ " )as t\n"
+ " ) ,','|| chr(13)||chr(10));\n"
+ "\n"
+ "\n"
+ " -- COMMENT COMMENT ON COLUMN sys_activity.id IS '主键';\n"
+ " tableScript:= tableScript || chr(13)||chr(10) || chr(13)||chr(10) || array_to_string(\n"
+ " array(\n"
+ " SELECT 'COMMENT ON COLUMN' || tablename || '.' || a.attname ||' IS '|| ''''|| d.description "
+ "||''''\n"
+ " FROM pg_class c\n"
+ " JOIN pg_description d ON c.oid=d.objoid\n"
+ " JOIN pg_attribute a ON c.oid = a.attrelid\n"
+ " WHERE c.relname=tablename\n"
+ " AND a.attnum = d.objsubid),','|| chr(13)||chr(10)) ;\n"
+ "\n"
+ " return tableScript;\n"
+ "\n"
+ " end\n"
+ " $BODY$ LANGUAGE plpgsql;\n"
+ "\n"
+ " CREATE OR REPLACE FUNCTION findattname(namespace character varying, tablename character "
+ "varying, "
+ "ctype"
+ " character\n"
+ " varying)\n"
+ " RETURNS character varying as $BODY$\n"
+ "\n"
+ " declare\n"
+ " tt oid ;\n"
+ " aname character varying default '';\n"
+ "\n"
+ " begin\n"
+ " tt := oid from pg_class where relname= tablename and relnamespace =(select oid from "
+ "pg_namespace "
+ "where\n"
+ " nspname=namespace) ;\n"
+ " aname:= array_to_string(\n"
+ " array(\n"
+ " select a.attname from pg_attribute a\n"
+ " where a.attrelid=tt and a.attnum in (\n"
+ " select unnest(conkey) from pg_constraint c where contype=ctype\n"
+ " and conrelid=tt and array_to_string(conkey,',') is not null\n"
+ " )\n"
+ " ),',');\n"
+ "\n"
+ " return aname;\n"
+ " end\n"
+ " $BODY$ LANGUAGE plpgsql";
}