설명 없음
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

get_sqlserver_columns.go 8.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. package dbs
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "strings"
  6. "time"
  7. "git.x2erp.com/qdy/go-svc-mcp/internal/mcp"
  8. )
  9. func init() {
  10. mcp.Register("get_sqlserver_columns", "根据表名称获取SQL Server表的所有字段相关信息",
  11. map[string]interface{}{
  12. "type": "object",
  13. "properties": map[string]interface{}{
  14. "table_name": map[string]interface{}{
  15. "type": "string",
  16. "description": "表名称",
  17. },
  18. "schema": map[string]interface{}{
  19. "type": "string",
  20. "description": "模式名称(默认为dbo)",
  21. "default": "",
  22. },
  23. "include_comments": map[string]interface{}{
  24. "type": "boolean",
  25. "description": "是否包含字段注释",
  26. "default": true,
  27. },
  28. "database_key": map[string]interface{}{
  29. "type": "string",
  30. "description": "数据库配置键名(如:business),可选,默认使用主数据库",
  31. "enum": []string{"warehouse", "business"},
  32. "default": "warehouse",
  33. },
  34. },
  35. "required": []string{"table_name"},
  36. },
  37. func(input json.RawMessage, deps *mcp.ToolDependencies) (interface{}, error) {
  38. var params struct {
  39. TableName string `json:"table_name"`
  40. Schema string `json:"schema"`
  41. IncludeComments bool `json:"include_comments"`
  42. DatabaseKey string `json:"database_key"`
  43. }
  44. if len(input) > 0 {
  45. if err := json.Unmarshal(input, &params); err != nil {
  46. return nil, err
  47. }
  48. }
  49. // 获取数据库工厂
  50. dbFactory, err := GetDBFactory(params.DatabaseKey, deps)
  51. if err != nil {
  52. return nil, err
  53. }
  54. // 获取数据库类型,确保是SQL Server
  55. dbType := dbFactory.GetDBType()
  56. if dbType != "sqlserver" {
  57. return nil, fmt.Errorf("当前数据库类型为 %s,此工具仅支持SQL Server数据库", dbType)
  58. }
  59. // 设置默认模式
  60. schema := strings.TrimSpace(params.Schema)
  61. if schema == "" {
  62. schema = "dbo"
  63. }
  64. tableName := strings.TrimSpace(params.TableName)
  65. if tableName == "" {
  66. return nil, fmt.Errorf("表名称不能为空")
  67. }
  68. // 构建查询SQL
  69. var query string
  70. if params.IncludeComments {
  71. query = `
  72. SELECT
  73. c.ORDINAL_POSITION as column_id,
  74. c.COLUMN_NAME as column_name,
  75. c.DATA_TYPE as data_type,
  76. c.IS_NULLABLE as nullable,
  77. c.COLUMN_DEFAULT as column_default,
  78. c.CHARACTER_MAXIMUM_LENGTH,
  79. c.NUMERIC_PRECISION,
  80. c.NUMERIC_SCALE,
  81. CAST(
  82. CASE WHEN pk.COLUMN_NAME IS NOT NULL THEN 1 ELSE 0 END
  83. AS BIT) as is_primary_key,
  84. '' as column_comment -- 先占位,后面单独查询
  85. FROM INFORMATION_SCHEMA.COLUMNS c
  86. LEFT JOIN (
  87. SELECT
  88. ku.COLUMN_NAME,
  89. tc.TABLE_SCHEMA,
  90. tc.TABLE_NAME
  91. FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc
  92. JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE ku
  93. ON tc.CONSTRAINT_NAME = ku.CONSTRAINT_NAME
  94. AND tc.TABLE_SCHEMA = ku.TABLE_SCHEMA
  95. WHERE tc.CONSTRAINT_TYPE = 'PRIMARY KEY'
  96. ) pk ON c.TABLE_SCHEMA = pk.TABLE_SCHEMA
  97. AND c.TABLE_NAME = pk.TABLE_NAME
  98. AND c.COLUMN_NAME = pk.COLUMN_NAME
  99. WHERE c.TABLE_SCHEMA = @p1
  100. AND c.TABLE_NAME = @p2
  101. ORDER BY c.ORDINAL_POSITION`
  102. } else {
  103. query = `
  104. SELECT
  105. c.ORDINAL_POSITION as column_id,
  106. c.COLUMN_NAME as column_name,
  107. c.DATA_TYPE as data_type,
  108. c.IS_NULLABLE as nullable,
  109. c.COLUMN_DEFAULT as column_default,
  110. c.CHARACTER_MAXIMUM_LENGTH,
  111. c.NUMERIC_PRECISION,
  112. c.NUMERIC_SCALE,
  113. CAST(
  114. CASE WHEN pk.COLUMN_NAME IS NOT NULL THEN 1 ELSE 0 END
  115. AS BIT) as is_primary_key
  116. FROM INFORMATION_SCHEMA.COLUMNS c
  117. LEFT JOIN (
  118. SELECT
  119. ku.COLUMN_NAME,
  120. tc.TABLE_SCHEMA,
  121. tc.TABLE_NAME
  122. FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc
  123. JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE ku
  124. ON tc.CONSTRAINT_NAME = ku.CONSTRAINT_NAME
  125. AND tc.TABLE_SCHEMA = ku.TABLE_SCHEMA
  126. WHERE tc.CONSTRAINT_TYPE = 'PRIMARY KEY'
  127. ) pk ON c.TABLE_SCHEMA = pk.TABLE_SCHEMA
  128. AND c.TABLE_NAME = pk.TABLE_NAME
  129. AND c.COLUMN_NAME = pk.COLUMN_NAME
  130. WHERE c.TABLE_SCHEMA = @p1
  131. AND c.TABLE_NAME = @p2
  132. ORDER BY c.ORDINAL_POSITION`
  133. }
  134. // 执行查询
  135. results, err := dbFactory.QuerySliceMapWithParams(query, schema, tableName)
  136. if err != nil {
  137. return nil, fmt.Errorf("查询表字段信息失败: %v", err)
  138. }
  139. if len(results) == 0 {
  140. // 尝试查询表是否存在
  141. tableExistsQuery := `SELECT COUNT(*) as table_count FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = @p1 AND TABLE_NAME = @p2`
  142. tableCheckResults, err := dbFactory.QuerySliceMapWithParams(tableExistsQuery, schema, tableName)
  143. if err == nil && len(tableCheckResults) > 0 {
  144. if count, ok := tableCheckResults[0]["table_count"].(int64); ok && count == 0 {
  145. return nil, fmt.Errorf("表 '%s' 不存在于模式 '%s' 中", tableName, schema)
  146. }
  147. }
  148. return nil, fmt.Errorf("表 '%s' 存在但没有字段信息或表为空", tableName)
  149. }
  150. // 处理字段信息
  151. for i := range results {
  152. // 处理nullable字段
  153. if nullable, ok := results[i]["nullable"].(string); ok {
  154. results[i]["is_nullable"] = nullable == "YES"
  155. delete(results[i], "nullable")
  156. }
  157. // 处理注释字段
  158. if params.IncludeComments {
  159. // 查询扩展属性获取字段注释
  160. columnName := results[i]["column_name"].(string)
  161. descQuery := `
  162. SELECT value as column_comment
  163. FROM fn_listextendedproperty ('MS_Description', 'SCHEMA', @p1, 'TABLE', @p2, 'COLUMN', @p3)`
  164. descResults, err := dbFactory.QuerySliceMapWithParams(descQuery, schema, tableName, columnName)
  165. if err == nil && len(descResults) > 0 {
  166. if desc, ok := descResults[0]["column_comment"].(string); ok && desc != "" {
  167. results[i]["column_comment"] = desc
  168. } else {
  169. results[i]["column_comment"] = ""
  170. }
  171. } else {
  172. results[i]["column_comment"] = ""
  173. }
  174. }
  175. // 处理默认值
  176. if defaultValue, ok := results[i]["column_default"]; ok && defaultValue == nil {
  177. results[i]["column_default"] = ""
  178. }
  179. // 处理主键字段
  180. if isPK, ok := results[i]["is_primary_key"].(bool); ok {
  181. results[i]["is_primary_key"] = isPK
  182. }
  183. // 构建完整数据类型
  184. if dataType, ok := results[i]["data_type"].(string); ok {
  185. fullType := dataType
  186. if length, ok := results[i]["character_maximum_length"].(int64); ok && length > 0 && length != 2147483647 {
  187. if length == -1 {
  188. fullType = fmt.Sprintf("%s(max)", dataType)
  189. } else {
  190. fullType = fmt.Sprintf("%s(%d)", dataType, length)
  191. }
  192. } else if precision, ok := results[i]["numeric_precision"].(int64); ok && precision > 0 {
  193. if scale, ok := results[i]["numeric_scale"].(int64); ok && scale > 0 {
  194. fullType = fmt.Sprintf("%s(%d,%d)", dataType, precision, scale)
  195. } else {
  196. fullType = fmt.Sprintf("%s(%d)", dataType, precision)
  197. }
  198. }
  199. results[i]["full_data_type"] = fullType
  200. }
  201. }
  202. // 获取表注释
  203. tableCommentQuery := `
  204. SELECT value as table_comment
  205. FROM fn_listextendedproperty ('MS_Description', 'SCHEMA', @p1, 'TABLE', @p2, NULL, NULL)`
  206. commentResults, err := dbFactory.QuerySliceMapWithParams(tableCommentQuery, schema, tableName)
  207. tableComment := ""
  208. if err == nil && len(commentResults) > 0 {
  209. if comment, ok := commentResults[0]["table_comment"].(string); ok {
  210. tableComment = comment
  211. }
  212. }
  213. return map[string]interface{}{
  214. "tenant_id": deps.ReqCtx.TenantID,
  215. "user_id": deps.ReqCtx.UserID,
  216. "database_type": dbType,
  217. "database_name": dbFactory.GetDatabaseName(),
  218. "schema": schema,
  219. "table_name": tableName,
  220. "table_comment": tableComment,
  221. "include_comments": params.IncludeComments,
  222. "columns": results,
  223. "total_columns": len(results),
  224. "timestamp": time.Now().Format(time.RFC3339),
  225. "note": "SQL Server字段注释通过扩展属性MS_Description设置",
  226. }, nil
  227. },
  228. )
  229. }