DBCHM/MJTop.Data/DatabaseInfo/PostgreSqlDBInfo.cs

480 lines
18 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using MJTop.Data.SPI;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Data;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace MJTop.Data.DatabaseInfo
{
public class PostgreSqlDBInfo : IDBInfo
{
internal DB Db { get; set; }
/// <summary>
/// 数据库工具
/// </summary>
public Tool Tools
{
get;
private set;
}
public PostgreSqlDBInfo(DB db)
{
this.Db = db;
Refresh();
this.Tools = new Tool(db, this);
}
public string DBName
{
get { return (Db.ConnectionStringBuilder as Npgsql.NpgsqlConnectionStringBuilder).Database; }
}
public string Version
{
get;
private set;
}
// PostgreSQL 9.6.3, compiled by Visual C++ build 1800, 32-bit => 9.6
public double VersionNumber
{
get
{
var mat = Regex.Match(Version, @"\D*(\d{1,}\.\d{1,})\D*", RegexOptions.Compiled);
double.TryParse(mat?.Groups[1]?.Value, out var res);
return res;
}
}
public string User
{
get { return (Db.ConnectionStringBuilder as Npgsql.NpgsqlConnectionStringBuilder).Username; }
}
public NameValueCollection TableComments { get; private set; } = new NameValueCollection();
private NameValueCollection TableSchemas { get; set; } = new NameValueCollection();
public List<string> TableNames { get; private set; } = new List<string>();
public IgCaseDictionary<TableInfo> TableInfoDict { get; private set; }
public IgCaseDictionary<List<string>> TableColumnNameDict { get; private set; }
public IgCaseDictionary<List<ColumnInfo>> TableColumnInfoDict { get; private set; }
public IgCaseDictionary<NameValueCollection> TableColumnComments { get; private set; }
private IgCaseDictionary<ColumnInfo> DictColumnInfo { get; set; }
private IgCaseDictionary<string> Dict_Table_Identity_Column { get; set; } = new IgCaseDictionary<string>();
public NameValueCollection Views { get; private set; }
public NameValueCollection Procs { get; private set; }
public List<string> DBNames { get; set; } = new List<string>();
public ColumnInfo this[string tableName, string columnName]
{
get
{
ColumnInfo colInfo;
var strKey = (tableName + "@" + columnName);
DictColumnInfo.TryGetValue(strKey, out colInfo);
return colInfo;
}
}
public List<string> this[string tableName]
{
get
{
List<string> colNames;
TableColumnNameDict.TryGetValue(tableName, out colNames);
return colNames;
}
}
public bool Refresh()
{
this.DictColumnInfo = new IgCaseDictionary<ColumnInfo>();
this.TableNames = new List<string>();
this.TableInfoDict = new IgCaseDictionary<TableInfo>();
this.TableColumnNameDict = new IgCaseDictionary<List<string>>();
this.TableColumnInfoDict = new IgCaseDictionary<List<ColumnInfo>>();
this.TableColumnComments = new IgCaseDictionary<NameValueCollection>();
string dbSql = "select datname from pg_database where datistemplate=false order by oid desc";
string strSql = @"select a.*,cast(obj_description(relfilenode,'pg_class') as varchar) as value from (
select b.oid as schid, a.table_schema as scName,concat(a.table_schema,'.',a.table_name) as Name,a.table_name from information_schema.tables a
left join pg_namespace b on b.nspname = a.table_schema
where a.table_schema not in ('pg_catalog','information_schema') and a.table_schema=:schema and a.table_type='BASE TABLE'
) a inner join pg_class b on a.table_name = b.relname and a.schid = b.relnamespace
order by name asc";
string viewSql = $"SELECT viewname,definition FROM pg_views where schemaname='{User}' order by viewname asc";
string procSql = $"select proname,prosrc from pg_proc where pronamespace=(SELECT pg_namespace.oid FROM pg_namespace WHERE nspname = '{User}') order by proname asc";
try
{
this.DBNames = Db.ReadList<string>(dbSql);
this.Version = Db.Scalar("select version()", string.Empty);
var data = Db.GetDataTable(strSql, new { schema = User });
var dictGp = new Dictionary<string, List<string>>();
foreach (DataRow dr in data.Rows)
{
this.TableComments[dr["name"].ToString()] = dr["value"].ToString();
this.TableSchemas[dr["name"].ToString()] = dr["scname"].ToString();
dictGp.AddRange(dr["scname"].ToString(), dr["name"].ToString());
}
foreach (var item in dictGp)
{
this.TableNames.AddRange(item.Value.OrderBy(t => t));
}
this.Views = Db.ReadNameValues(viewSql);
this.Procs = Db.ReadNameValues(procSql);
if (this.TableComments != null && this.TableComments.Count > 0)
{
List<Task> lstTask = new List<Task>();
foreach (var tableName in this.TableNames)
{
Task task = Task.Run(() =>
{
TableInfo tabInfo = new TableInfo();
tabInfo.TableName = tableName;
tabInfo.TabComment = TableComments[tableName];
strSql = @"SELECT
col.ordinal_position AS Colorder,
col.table_schema AS SCHEMA_NAME,
col.TABLE_NAME,
col.COLUMN_NAME AS ColumnName,
col.udt_name AS TypeName,
col_description ( C.oid, col.ordinal_position ) AS DeText,
COALESCE(col.character_maximum_length, col.numeric_precision, -1) AS Length,
col.numeric_scale AS Scale,
(CASE col.is_identity WHEN 'YES' THEN 1 ELSE 0 END) AS IsIdentity,
(CASE col.is_nullable WHEN 'YES' THEN 1 ELSE 0 END) AS CanNull,
col.column_default AS DefaultVal
FROM
information_schema.COLUMNS AS col
LEFT JOIN pg_namespace ns ON ns.nspname = col.table_schema
LEFT JOIN pg_class C ON col.TABLE_NAME = C.relname
AND C.relnamespace = ns.oid
LEFT JOIN pg_attrdef A ON C.oid = A.adrelid
AND col.ordinal_position = A.adnum
LEFT JOIN pg_attribute b ON b.attrelid = C.oid
AND b.attname = col.
COLUMN_NAME LEFT JOIN pg_type et ON et.oid = b.atttypid
LEFT JOIN pg_collation coll ON coll.oid = b.attcollation
LEFT JOIN pg_namespace colnsp ON coll.collnamespace = colnsp.oid
LEFT JOIN pg_type bt ON et.typelem = bt.oid
LEFT JOIN pg_namespace nbt ON bt.typnamespace = nbt.oid
WHERE
col.table_schema = :schema
AND col.TABLE_NAME = :tableName
ORDER BY
col.table_schema,
col.TABLE_NAME,
col.ordinal_position";
var strKey = string.Empty;
try
{
var arr = tableName.Split('.');
var scName = arr.FirstOrDefault();
var tabName = arr.LastOrDefault();
tabInfo.Colnumns = Db.GetDataTable(strSql, new { tableName = tabName, schema = scName }).ConvertToListObject<ColumnInfo>();
List<string> lstColName = new List<string>();
NameValueCollection nvcColDeText = new NameValueCollection();
foreach (ColumnInfo colInfo in tabInfo.Colnumns)
{
lstColName.Add(colInfo.ColumnName);
nvcColDeText.Add(colInfo.ColumnName, colInfo.DeText);
strKey = (tableName + "@" + colInfo.ColumnName);
this.DictColumnInfo.Add(strKey, colInfo);
if (colInfo.IsIdentity)
{
this.Dict_Table_Identity_Column[tableName] = colInfo.ColumnName;
}
if (colInfo.IsPK)
{
tabInfo.PriKeyColName = colInfo.ColumnName;
if (colInfo.IsIdentity)
{
tabInfo.PriKeyType = PrimaryKeyType.AUTO;
}
else
{
tabInfo.PriKeyType = PrimaryKeyType.SET;
}
}
Global.Dict_PostgreSql_DbType.TryGetValue(colInfo.TypeName, out DbType type);
colInfo.DbType = type;
}
this.TableInfoDict.Add(tableName, tabInfo);
this.TableColumnNameDict.Add(tableName, lstColName);
this.TableColumnInfoDict.Add(tableName, tabInfo.Colnumns);
this.TableColumnComments.Add(tableName, nvcColDeText);
}
catch (Exception ex)
{
LogUtils.LogError("DB", Developer.SysDefault, ex, strKey);
}
});
lstTask.Add(task);
if (lstTask.Count(t => t.Status != TaskStatus.RanToCompletion) >= 50)
{
Task.WaitAny(lstTask.ToArray());
lstTask = lstTask.Where(t => t.Status != TaskStatus.RanToCompletion).ToList();
}
}
Task.WaitAll(lstTask.ToArray());
}
}
catch (Exception ex)
{
LogUtils.LogError("DB", Developer.SysDefault, ex);
return false;
}
return this.TableComments.Count == this.TableInfoDict.Count;
}
internal string IdentityColumnName(string tableName)
{
tableName = (tableName ?? string.Empty).ToLower();
string colName;
if (Dict_Table_Identity_Column.TryGetValue(tableName, out colName))
{
return colName;
}
return string.Empty;
}
public Dictionary<string, DateTime> GetTableStruct_Modify()
{
throw new Exception("暂未提供查询PostgreSql表结构修改时间的功能");
}
public bool IsExistTable(string tableName)
{
return TableNames.Contains(tableName);
}
public bool IsExistColumn(string tableName, string columnName)
{
var strKey = (tableName + "@" + columnName).ToLower();
return DictColumnInfo.ContainsKey(strKey);
}
public string GetColumnComment(string tableName, string columnName)
{
ColumnInfo colInfo = null;
var strKey = (tableName + "@" + columnName).ToLower();
DictColumnInfo.TryGetValue(strKey, out colInfo);
return colInfo?.DeText;
}
public string GetTableComment(string tableName)
{
Db.CheckTabStuct(tableName);
return TableComments[tableName];
}
public List<ColumnInfo> GetColumns(string tableName)
{
Db.CheckTabStuct(tableName);
tableName = (tableName ?? string.Empty).ToLower();
List<ColumnInfo> colInfos = null;
TableColumnInfoDict.TryGetValue(tableName, out colInfos);
return colInfos;
}
public bool SetTableComment(string tableName, string comment)
{
Db.CheckTabStuct(tableName);
tableName = TableInfoDict[tableName].TableName;
string upsert_sql = string.Empty;
comment = (comment ?? string.Empty).Replace("'", "");
try
{
var tabName = tableName.Split('.').LastOrDefault();
//切换schema更新表描述
upsert_sql = "set search_path to " + TableSchemas[tableName] + ";comment on table \"" + tabName + "\" is '" + comment + "'";
Db.ExecSql(upsert_sql);
TableComments[tableName] = comment;
var tabInfo = TableInfoDict[tableName];
tabInfo.TabComment = comment;
TableInfoDict[tableName] = tabInfo;
}
catch (Exception ex)
{
LogUtils.LogError("DB", Developer.SysDefault, ex, upsert_sql);
return false;
}
return true;
}
public bool SetColumnComment(string tableName, string columnName, string comment)
{
Db.CheckTabStuct(tableName, columnName);
tableName = TableInfoDict[tableName].TableName;
columnName = this[tableName, columnName].ColumnName;
string upsert_sql = string.Empty;
comment = (comment ?? string.Empty).Replace("'", "");
try
{
var tabName = tableName.Split('.').LastOrDefault();
//切换schema更新列描述
upsert_sql = "set search_path to " + TableSchemas[tableName] + ";comment on column \"" + tabName + "\".\"" + columnName + "\" is '" + comment + "'";
Db.ExecSql(upsert_sql);
List<ColumnInfo> lstColInfo = TableColumnInfoDict[tableName];
NameValueCollection nvcColDesc = new NameValueCollection();
lstColInfo.ForEach(t =>
{
if (t.ColumnName.Equals(columnName))
{
t.DeText = comment;
}
nvcColDesc.Add(t.ColumnName, t.DeText);
});
TableColumnInfoDict.Remove(tableName);
TableColumnInfoDict.Add(tableName, lstColInfo);
TableColumnComments.Remove(tableName);
TableColumnComments.Add(tableName, nvcColDesc);
var strKey = (tableName + "@" + columnName);
ColumnInfo colInfo = DictColumnInfo[strKey];
colInfo.DeText = comment;
DictColumnInfo[strKey] = colInfo;
}
catch (Exception ex)
{
LogUtils.LogError("DB", Developer.SysDefault, ex, upsert_sql);
return false;
}
return true;
}
public bool DropTable(string tableName)
{
Db.CheckTabStuct(tableName);
tableName = (tableName ?? string.Empty).ToLower();
string drop_sql = string.Empty;
try
{
drop_sql = "drop table " + tableName;
Db.ExecSql(drop_sql);
this.TableComments.Remove(tableName);
this.TableNames = TableComments.AllKeys.ToList();
this.TableInfoDict.Remove(tableName);
this.TableColumnInfoDict.Remove(tableName);
this.TableColumnComments.Remove(tableName);
var lstColName = TableColumnNameDict[tableName];
foreach (var colName in lstColName)
{
var strKey = (tableName + "@" + colName).ToLower();
this.DictColumnInfo.Remove(strKey);
}
this.TableColumnNameDict.Remove(tableName);
}
catch (Exception ex)
{
LogUtils.LogError("DB", Developer.SysDefault, ex, drop_sql);
return false;
}
return true;
}
public bool DropColumn(string tableName, string columnName)
{
Db.CheckTabStuct(tableName, columnName);
tableName = (tableName ?? string.Empty).ToLower();
columnName = (columnName ?? string.Empty).ToLower();
var strKey = (tableName + "@" + columnName);
string drop_sql = "alter table {0} drop column {1}";
try
{
drop_sql = string.Format(drop_sql, tableName, columnName);
Db.ExecSql(drop_sql);
this.DictColumnInfo.Remove(strKey);
var nvc = TableColumnComments[tableName];
nvc.Remove(columnName);
TableColumnNameDict[tableName] = nvc.AllKeys.ToList();
var lstColInfo = TableColumnInfoDict[tableName];
ColumnInfo curColInfo = null;
lstColInfo.ForEach(t =>
{
if (t.ColumnName.Equals(columnName))
{
curColInfo = t;
//tabInfo 对应的 主键类型和主键列 也需要 跟着修改。
if (curColInfo.IsPK)
{
var tabInfo = TableInfoDict[tableName];
tabInfo.PriKeyType = PrimaryKeyType.UNKNOWN;
tabInfo.PriKeyColName = null;
TableInfoDict[tableName] = tabInfo;
}
return;
}
});
lstColInfo.Remove(curColInfo);
TableColumnInfoDict[tableName] = lstColInfo;
}
catch (Exception ex)
{
LogUtils.LogError("DB", Developer.SysDefault, ex, drop_sql);
return false;
}
return true;
}
}
}