db2text 0.2.1__tar.gz → 0.2.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. {db2text-0.2.1/db2text.egg-info → db2text-0.2.3}/PKG-INFO +1 -1
  2. {db2text-0.2.1 → db2text-0.2.3}/database2text/datafile/dbtmanual.md +73 -4
  3. {db2text-0.2.1 → db2text-0.2.3}/database2text/datafile/dot.mb +6 -4
  4. db2text-0.2.3/database2text/datafile/sample/dm/all.txt +30 -0
  5. {db2text-0.2.1 → db2text-0.2.3}/database2text/datafile/sample/mysql/all.txt +1 -1
  6. db2text-0.2.3/database2text/datafile/sample/mysql/wiki.txt +16 -0
  7. {db2text-0.2.1 → db2text-0.2.3}/database2text/datafile/sample/oracle/all.txt +1 -1
  8. db2text-0.2.3/database2text/datafile/sample/oracle/crmrpt.txt +26 -0
  9. {db2text-0.2.1 → db2text-0.2.3}/database2text/dbt.py +2 -1
  10. db2text-0.2.3/database2text/dm.py +146 -0
  11. {db2text-0.2.1 → db2text-0.2.3}/database2text/mssql.py +4 -1
  12. {db2text-0.2.1 → db2text-0.2.3}/database2text/mysql.py +3 -1
  13. {db2text-0.2.1 → db2text-0.2.3}/database2text/opengauss.py +1 -1
  14. {db2text-0.2.1 → db2text-0.2.3}/database2text/oracle11.py +4 -1
  15. {db2text-0.2.1 → db2text-0.2.3}/database2text/tool.py +78 -10
  16. db2text-0.2.3/db2text/__init__.py +2 -0
  17. db2text-0.2.3/db2text/data.py +19 -0
  18. db2text-0.2.3/db2text/db/__init__.py +1 -0
  19. db2text-0.2.3/db2text/util.py +108 -0
  20. {db2text-0.2.1 → db2text-0.2.3/db2text.egg-info}/PKG-INFO +1 -1
  21. {db2text-0.2.1 → db2text-0.2.3}/db2text.egg-info/SOURCES.txt +9 -1
  22. {db2text-0.2.1 → db2text-0.2.3}/db2text.egg-info/top_level.txt +1 -0
  23. {db2text-0.2.1 → db2text-0.2.3}/setup.py +1 -1
  24. {db2text-0.2.1 → db2text-0.2.3}/LICENSE +0 -0
  25. {db2text-0.2.1 → db2text-0.2.3}/MANIFEST.in +0 -0
  26. {db2text-0.2.1 → db2text-0.2.3}/README.md +0 -0
  27. {db2text-0.2.1 → db2text-0.2.3}/database2text/__init__.py +0 -0
  28. {db2text-0.2.1 → db2text-0.2.3}/database2text/datafile/sample/dbt_dot_mssql.txt +0 -0
  29. {db2text-0.2.1 → db2text-0.2.3}/database2text/datafile/sample/opengauss/all.txt +0 -0
  30. {db2text-0.2.1 → db2text-0.2.3}/database2text/datafile/sample/sqlserver/all.txt +0 -0
  31. {db2text-0.2.1 → db2text-0.2.3}/db2text.egg-info/dependency_links.txt +0 -0
  32. {db2text-0.2.1 → db2text-0.2.3}/db2text.egg-info/entry_points.txt +0 -0
  33. {db2text-0.2.1 → db2text-0.2.3}/db2text.egg-info/not-zip-safe +0 -0
  34. {db2text-0.2.1 → db2text-0.2.3}/db2text.egg-info/requires.txt +0 -0
  35. {db2text-0.2.1 → db2text-0.2.3}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: db2text
3
- Version: 0.2.1
3
+ Version: 0.2.3
4
4
  Summary: database to text
5
5
  Home-page: https://gitee.com/chenc224/dbt
6
6
  Author: Chen chuan
@@ -2,9 +2,33 @@
2
2
 
3
3
  ## 概述
4
4
 
5
- 这个工具主要用于读取数据库里的对象如表、视图等的结构,根据这些结构生成文本文件。
5
+ 这个软件包大致分为2个部分,一个是dbt命令行工具,可用于读取数据库里的对象如表、视图等的结构,根据这些结构生成、修改文本文件。典型应用场景如下:
6
6
 
7
- 可以用这些文本文件以及版本控制软件来备份、检查数据库结构的变动情况。还可以使用模板技术根据这些结构自动生成代码,比如可以自动更新c/c++源代码里的结构,这样在表结构更新时可以帮助你迅速完成底层数据结构的重建。
7
+ * 生成结构文本数据,配合版本控制,可以备份、检查、跟踪数据库结构的变动情况。
8
+ * 设置模板,自动更新c/c++的源代码,比如oracle pro*c里以下跟结构密切相关的代码都可以自动生成、更新
9
+ ```
10
+ struct stru_note_request {//消息请求队列 note_request
11
+ long id; //请求ID id
12
+ long group_id; //收件组ID group_id
13
+ char group_name[256]; //收件组名 group_name
14
+ char note_content[4001]; //消息内容 note_content
15
+ long note_type; //0-短信 1-微信 2-语音 3-工作台
16
+ long save_days; //单位-天
17
+ };
18
+
19
+ ```
20
+ * 设置模板,自动更新markdown文档,如
21
+
22
+ | 字段名 | 类型 | 空 | 默认值 | 说明 |
23
+ | :---- | :---- | :---- | :---- | :---- |
24
+ | xmjdid | bigint(20) | | | 项目进度id |
25
+ | xmid | bigint(20) | | | 项目id |
26
+ | cyrid | bigint(20) | | | 身份表里的身份id |
27
+ | clnr | longblob | | | 处理内容 |
28
+ | tjsj | datetime | | | 提交时间 |
29
+ | clff | varchar(50) | 可 | | 处理方法 |
30
+
31
+ 还有一个部分就是提供一个库,方便大家解析数据库,获取数据结构,进行一些自己的处理。dbt命令行工具可以认为就是用这个库写出来的一个应用吧。
8
32
 
9
33
  ## 安装
10
34
 
@@ -47,7 +71,7 @@ table= + abcd.*
47
71
 
48
72
  如`coding=gbk`则指定当前的配置文件的字符集gbk
49
73
 
50
- ## connnect段
74
+ ## connect段
51
75
 
52
76
  首先要有一行
53
77
 
@@ -115,7 +139,7 @@ pdfdir=指定pdf文件放在哪个目录,未指定则放在dotdir目录下
115
139
 
116
140
  filename=dot和pdf文件名,不带扩展名
117
141
 
118
- dotcmd=neato 指定graphviz布局算法,可以选择dot,neato,twopi,circo,fdp,sfdp,patchwork和osage这些,未指定使用neato
142
+ dotcmd=neato 指定graphviz布局算法,可以选择twopi、circo、fdp和osage这些,未指定使用fdp。不建议使用dot,patchwork,布局严重有问题,也不建议使用neato、sfdp,生成的pdf尺寸有点问题。
119
143
 
120
144
  page= 页名称列表,用空格隔开,如 用户类 权限相关 交易 流程控制等 other/其它
121
145
 
@@ -125,6 +149,35 @@ page设置的是页名称列表,每个页名称对应一个设置项,other
125
149
 
126
150
  每个页名称会生成一页pdf,最终合成得到一个多页的pdf。如果不需要多页pdf,可以不用设置page语句。
127
151
 
152
+ ## wiki段
153
+
154
+ 生成wiki里的表格。如果指定wiki,可以直接用wiki机器人自动更新wiki内容。如果不指定wiki,则输出文本内容供粘贴。
155
+
156
+ 生成内容类似下面这样
157
+
158
+ ```
159
+ {| class="wikitable"
160
+ |+ 表名 表的注释内容
161
+ ! 字段名 !! 类型 !! 说明
162
+ |-
163
+ || 字段1名字 || 字段1类型 || 字段1说明
164
+ |-
165
+ || 字段2名字 || 字段2类型 || 字段2说明
166
+ |}
167
+ ```
168
+
169
+ 自动更新内容时寻找前两行内容匹配的,注意表的注释内容有差异不影响匹配。从这两行开始直到|}结束的行就是需要更新的区域。
170
+
171
+ title = name:字段名 desc:注释 ts:类型 default:默认值 :说明
172
+
173
+ title设置表格有几列,列和数据之间的关系,列是数据中的md列表内容,如果不指定列,这一列由用户自行填写,不做更新。
174
+
175
+ wiki = wiki dbcfg配置的wiki登录方式,如果设置,可以用wiki机器人自动更新。如果未设置,则输出文本。
176
+
177
+ page = 页面名称 即相应页地址最后内容如http://地址/mediawiki/index.php/页面名称
178
+
179
+ level = 2 如果是一张新表,会在内容的最后增加这个表格,在表格前面还会加一个标题如 == 表名 表的注释内容 ==,这里的level指定=的个数,如果未设置,不增加这个标题行
180
+
128
181
  ## end段
129
182
 
130
183
  发现这个段就意味着执行器停止扫描,这样可以把一些暂时不用的代码放在end段后面。
@@ -145,6 +198,22 @@ page设置的是页名称列表,每个页名称对应一个设置项,other
145
198
 
146
199
  dbt执行时根据dbt.txt里的内容执行相应处理。主要流程就是连接数据库、读取数据库结构、导出结构或者根据结构调整文本。这里连接数据库、读取数据库结构考虑设计成模块化的,这样可以方便替换不同的数据库。后续导出结构或者根据结构调整文本其实不同的数据库是基本类似的,差异可能只是数据结构有不同,所以这一部分设计成通用的。
147
200
 
201
+ ## db2text 模块
202
+
203
+ 新构建db2text模块替代过往的database2text模块,老的模块结构不合理,需要优化,顺便把名字改短一点。
204
+
205
+ 模块提供一些全局的数据gd和一些函数、类供使用,计划将配置信息、中间结果等都放在gd里。
206
+
207
+ 如果使用import db2text as dt这样的导入方式,需要使用dt.gd.coding这样来使用gd里的数据,函数则使用dt.函数名来使用。建议使用这种方式避免名字污染。
208
+
209
+ 如果使用from db2text import * 这样的导入方式,可以使用gd.coding这样来使用gd里的数据,函数则直接用函数名来使用。
210
+
211
+ 后续dbt改用db2text,全部迁移后老的database2text将会废弃。
212
+
213
+ gd (global data)的说明可以直接察看data.py,里面有清单和说明。
214
+
215
+ 函数、公用类说明在下面,单起一章。
216
+
148
217
  ## 读数据库模块设计
149
218
 
150
219
  系统已经有一些模块可以用来处理oracle、mysql、mssql,如果有其它数据库要处理,可以在https://gitee.com/chenc224/dbt/issues提需求,也可以根据模块的设计思路自己写一个。
@@ -1,9 +1,11 @@
1
1
  graph 表结构 {
2
- size = "8,11"
3
- # page="8.5,11";
2
+ size = "8.5,11";
3
+ margin=0;
4
+ pad=0.1;
5
+ page="8.5,11";
4
6
  # charset=utf8;
5
- # ratio="auto"
6
- dpi = 300
7
+ ratio="fill"
8
+ # dpi = 300
7
9
  # rankdir = "TR"
8
10
  # node[shape=record fontname="wqy-zenhei" size="30,30"]
9
11
  node[shape=plaintext fontname="wqy-zenhei"]
@@ -0,0 +1,30 @@
1
+ code=utf8
2
+
3
+ :connect
4
+ driver=dm
5
+ dbcfg=dbt_dm
6
+ :readdata
7
+ :export
8
+ #export段设置导出数据库里的相应内容为文本文件
9
+ datadir=test/dm/export
10
+ :render
11
+ file=test/dm/all.md
12
+ help=n
13
+ #table=tgroup tuser t_user_group t_app_group
14
+ #使用help=y可以打印出传入模板的数据,方便写模板的时候使用
15
+ start=## {{ tname }}
16
+ ## {{ tname }} {{ tdesc }}
17
+ | 字段名 | 类型 | 长度 | 空 | 默认值 | 说明 |
18
+ | :---- | :---- | ----: | :---- | :---- | :---- |
19
+ {% for c in ori %}| {{ c.COLUMN_NAME }} | {{ c.DATA_TYPE }} | {{ c.DATA_LENGTH }} | {% if c.NULLABLE == "Y" %}可{% endif %} | {{ c.DATA_DEFAULT or "" }} | {{ c.COLUMN_COMMENT }} |
20
+ {% endfor %}
21
+ end=
22
+
23
+ #根据模板生成graphviz的dot文件,调用相应命令生成pdf文件
24
+ :dot
25
+ dotdir=test/dm
26
+ pdfdir=local/dm
27
+ filename=all
28
+ dotcmd=osage
29
+
30
+ :end
@@ -26,7 +26,7 @@ start=## {{ tname }}
26
26
  {% endfor %}
27
27
  end=
28
28
 
29
- #根据模板生成dot文件,调用neato生成pdf文件
29
+ #根据模板生成graphviz的dot文件,调用相应命令生成pdf文件
30
30
  :dot
31
31
  dotdir=test/mysql
32
32
  pdfdir=local/mysql
@@ -0,0 +1,16 @@
1
+ code=utf8
2
+
3
+ :connect
4
+ driver=mysql
5
+ dbcfg=webbase.ggcx
6
+ :readdata
7
+
8
+ #view = v_user v_group
9
+
10
+ :wiki
11
+ title = name:字段名 desc:注释 ts:类型 default:默认值 :说明
12
+ wiki=wiki
13
+ page=门户mysql数据库文档
14
+ level=2
15
+
16
+ :end
@@ -48,7 +48,7 @@ table=ts.*
48
48
  dotdir=test/oracle
49
49
  pdfdir=local/oracle
50
50
  filename=lofta
51
- dotcmd=osage
51
+ #dotcmd=fdp
52
52
  page=份额类 other
53
53
 
54
54
  份额类=ts[a-f].*
@@ -0,0 +1,26 @@
1
+ #基础说明可参考https://gitee.com/chenc224/dbt/blob/master/database2text/datafile/usermenual.md
2
+ #sample for oracle 11
3
+ #oracle11的样例文件
4
+ #号开头的行为注释,并不需要处理
5
+ #冒号开头的行表示一段内容的开始,直到另外一个冒号或者是文件尾
6
+ #执行时扫描配置文件,把每一段的内容传递给相应的处理函数处理
7
+ :connect
8
+ #connect段设置数据库类型以及连接所需要的数据,不同数据库需要的数据并不相同
9
+ #loginname=loginname
10
+ #password=password
11
+ #dbserver=host address or ip or tnsname
12
+ dbcfg=fundcrm@sjzx
13
+
14
+ :readdata
15
+ table=+ t_rpt_.*
16
+ owner=RTCRM
17
+
18
+ #根据模板生成dot文件,调用fdp生成pdf文件
19
+ :dot
20
+ dotdir=local/oracle
21
+ pdfdir=local/oracle
22
+ filename=rpt
23
+ dotcmd=fdp
24
+ #可以选择dot,neato,twopi,circo,fdp,sfdp,patchwork和osage指定graphviz布局算法
25
+
26
+ :end
@@ -4,6 +4,7 @@
4
4
  import sys,importlib,os,shutil,dbcfg
5
5
  import database2text.tool as dbtt
6
6
  from database2text.tool import *
7
+ import db2text as dt
7
8
 
8
9
  def 读文件():
9
10
  rd=[]
@@ -57,7 +58,7 @@ def main():
57
58
  elif hasattr(dbtt,section):
58
59
  getattr(dbtt,section)(stdata)
59
60
  else:
60
- print("can't find function %s in driver %s and dbtt" %(section,driver))
61
+ print(f"在配置文件中发现未知的节{section},请检查格式是否正确")
61
62
  sys.exit(-2)
62
63
  section=s[1:].strip()
63
64
  storidata.clear()
@@ -0,0 +1,146 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+
4
+ import sys,cx_Oracle,json
5
+ import database2text.tool as dbtt
6
+ from database2text.tool import *
7
+
8
+ class dm(object):
9
+ def ana_TABLE(otype):
10
+ for oname, in db.exec("select object_name from all_objects where object_type=:ot and lower(owner)=:owner order by 1",ot=otype,owner=owner):
11
+ if 检查匹配(otype,oname):
12
+ continue
13
+ if oname.startswith("SYS_EXPORT_TABLE"):
14
+ continue
15
+ odata="create table %s\n(\n" %(oname)
16
+ coldata=[] #记录列数据,包括type类型,name列名,desc注释信息,size长度,ns名称+长度如name[5]这样
17
+ md=[] #markdown等用的字段数据,每字段一个字典,包括 "name": 字段名,"desc": 注释,"type": 数据类型,"size":字段长度,"null":True of False,是否可为空,"default":默认值
18
+ maxcsize=db.res1("select max(length(column_name)) from all_tab_cols where owner='%s' and table_name='%s'" %(owner,oname))
19
+ tdesc=db.res1("select comments from all_tab_comments where owner=:1 and table_name=:2",[owner,oname])
20
+ if not tdesc:tdesc=""
21
+ oridata=[]
22
+ for col in db.exec2("select * from all_tab_cols where owner='%s' and table_name='%s' order by column_id" %(owner,oname)):
23
+ col["COLUMN_COMMENT"]=db.res1("select comments from all_col_comments where table_name=:1 and column_name=:2 and owner=:3",[oname,col["COLUMN_NAME"],owner])
24
+ if col["DATA_DEFAULT"]!=None:
25
+ col["DATA_DEFAULT"]=col["DATA_DEFAULT"].strip()
26
+ oridata.append(col)
27
+ for column_name,data_type,char_length,data_precision,data_scale,nullable,default_length,data_default in db.exec("select column_name,data_type,char_length,data_precision,data_scale,nullable,default_length,data_default from all_tab_cols where owner='%s' and table_name='%s' order by column_id" %(owner,oname)):
28
+ odata=odata+" %s%*s" %(column_name,maxcsize-len(column_name)+1," ")
29
+ ctype="char"
30
+ desc=db.res1("select comments from all_col_comments where table_name=:1 and column_name=:2 and owner=:3",[oname,column_name,owner])
31
+ if not desc:desc=""
32
+ if data_type in ("NUMBER","NUMERIC"):
33
+ if data_precision is not None and data_scale is not None:
34
+ if data_scale==0:
35
+ if data_precision<8:ctype="int"
36
+ else:ctype="long"
37
+ odata=odata+"NUMBER(%d)" %(data_precision)
38
+ ts=f"NUMBER({data_precision})"
39
+ else:
40
+ ctype="double"
41
+ odata=odata+"NUMBER(%d,%d)" %(data_precision,data_scale)
42
+ ts=f"NUMBER({data_precision}.{data_scale})"
43
+ elif data_precision is None and data_scale==0:
44
+ ctype="long"
45
+ odata=odata+"INTEGER"
46
+ ts="INTEGER"
47
+ elif char_length==0:
48
+ ctype="long"
49
+ odata=odata+"NUMBER"
50
+ ts="INTEGER"
51
+ else:
52
+ print("table %s column %s type %s length %s %s %s" %(oname,column_name,data_type,char_length,data_precision,data_scale))
53
+ sys.exit(-1)
54
+ cns=column_name
55
+ elif data_type in ("VARCHAR2","VARCHAR","CHAR","NVARCHAR2","NVARCHAR"):
56
+ if char_length==1:
57
+ ts=data_type
58
+ else:
59
+ ts=f"{data_type}({char_length})"
60
+ cns="%s[%d]" %(column_name,char_length+1)
61
+ odata=odata+"%s(%d)" %(data_type,char_length)
62
+ elif data_type.startswith("TIMESTAMP"):
63
+ cns="%s[20]" %(column_name)
64
+ odata=odata+"%s" %(data_type)
65
+ ts=data_type
66
+ elif data_type in("DATE"):
67
+ cns="%s[20]" %(column_name)
68
+ odata=odata+"%s" %(data_type)
69
+ ts=data_type
70
+ elif data_type in("CLOB","BLOB","NCLOB","LONG","RAW"):
71
+ cns="%s[2000]" %(column_name)
72
+ odata=odata+"%s" %(data_type)
73
+ ts=data_type
74
+ elif data_type in("INTEGER","BIGINT"):
75
+ cns="%s" %(column_name)
76
+ odata=odata+"%s" %(data_type)
77
+ ts=data_type
78
+ elif data_type in ("FLOAT"):
79
+ ctype="double"
80
+ odata=odata+"FLOAT(%d)" %(data_precision)
81
+ ts=data_type
82
+ elif data_type in ("ROWID"):
83
+ cns="%s[100]" %(column_name)
84
+ odata=odata+"%s" %(data_type)
85
+ ts=data_type
86
+ else:
87
+ print("table %s column %s type %s length %s %s %s" %(oname,column_name,data_type,char_length,data_precision,data_scale))
88
+ sys.exit(-1)
89
+ if default_length:
90
+ odata=odata+" default %s" %(data_default.strip())
91
+ if nullable=="N":
92
+ odata=odata+" not null"
93
+ odata=odata+",\n"
94
+ cns2=cns
95
+ if data_type in ("VARCHAR2","VARCHAR","CHAR","NVARCHAR2","NVARCHAR") and char_length==1:
96
+ cns=column_name
97
+ coldata.append({"type":ctype,"name":column_name,"ns":cns,"ns2":cns2,"desc":desc})
98
+ 主键列=[]
99
+ for pkcol, in db.exec(f"SELECT cols.column_name FROM user_constraints cons, user_cons_columns cols WHERE cons.constraint_type = 'P' AND cons.constraint_name = cols.constraint_name AND cons.owner = cols.owner and cons.owner = '{owner}' AND cols.table_name = '{oname}'"):
100
+ 主键列.append(pkcol)
101
+ md.append({"name":column_name,"desc":desc,"type":data_type,"size":char_length, "null":nullable=="N", "default":data_default,"ts":ts, "pk":column_name in 主键列})
102
+ odata=odata[:-2]
103
+ odata=odata+"\n);"
104
+ dbdata["sql"]["TABLE"][oname]=odata
105
+ dbdata["exp"]["TABLE"].append({"tname":oname,"tdesc":tdesc,"ori":oridata,"c":coldata,"md":md})
106
+
107
+ def ana_VIEW(otype):
108
+ for oname, in db.exec("select object_name from user_objects where object_type=:ot",ot=otype):
109
+ if "view" in stdata and oname.lower() not in stdata["view"].split() and oname not in stdata["view"].split():
110
+ continue
111
+ dbdata["sql"][otype][oname]=oracle.getobjtext(otype,oname)
112
+
113
+ def getobjtext(otype,oname):
114
+ c=db.conn.cursor()
115
+ try:
116
+ c.callproc('DBMS_METADATA.SET_TRANSFORM_PARAM',(-1, 'TABLESPACE',False))
117
+ c.callproc("DBMS_METADATA.SET_TRANSFORM_PARAM",(-1,'STORAGE',False))
118
+ c.callproc("DBMS_METADATA.SET_TRANSFORM_PARAM",(-1,'SEGMENT_ATTRIBUTES',False))
119
+ c.callproc("DBMS_METADATA.SET_TRANSFORM_PARAM",(-1,'PRETTY',False))
120
+ except:
121
+ pass
122
+ ssql=db.res1("SELECT dbms_metadata.get_ddl(:otype,:oname) FROM DUAL",otype=otype,oname=oname).read()
123
+ return ssql
124
+
125
+ def readdata(arg):
126
+ global owner
127
+ dbdata["sql"]={}
128
+ dbdata["exp"]={}
129
+ if "owner" in arg:
130
+ owner=arg["owner"]
131
+ for i in vars(dm):
132
+ if i.startswith("ana_"):
133
+ otype=i[4:]
134
+ dbdata["sql"][otype]={}
135
+ dbdata["exp"][otype]=[]
136
+ getattr(dm,i)(otype)
137
+
138
+ def connect(arg):
139
+ global owner
140
+ db.conn=dbtt.dbc.connect()
141
+ if "owner" in stdata:
142
+ owner=stdata["owner"]
143
+ else:
144
+ owner=db.res1("select user from dual")
145
+
146
+ __all__=[]
@@ -11,7 +11,7 @@ class mssql(object):
11
11
  def ana_TABLE(otype):
12
12
  dbdata["sql"]["TABLE"]={}
13
13
  dbdata["exp"]["TABLE"]=[]
14
- sql="SELECT table_catalog,table_schema,table_name FROM INFORMATION_SCHEMA.TABLES where table_type='BASE TABLE'"
14
+ sql="SELECT table_catalog,table_schema,table_name FROM INFORMATION_SCHEMA.TABLES where table_type='BASE TABLE' order by 3"
15
15
  if 目录:
16
16
  sql=f"{sql} and table_catalog='{目录}'"
17
17
  if 集合:
@@ -20,6 +20,9 @@ class mssql(object):
20
20
  if 检查匹配(otype,表名):
21
21
  continue
22
22
  表id=db.res1(f"select object_id('{catalog}.{schema}.{表名}')")
23
+ if not 表id:
24
+ print(f"{catalog}.{schema}.{表名} 找不到表id")
25
+ continue
23
26
  表注释=db.res1(f"select value from sys.extended_properties where major_id={表id} and class=1 and name='MS_Description'") or ""
24
27
  主键名=db.res1(f"SELECT k.CONSTRAINT_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS c JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS k ON c.CONSTRAINT_TYPE = 'PRIMARY KEY' AND c.CONSTRAINT_NAME = k.CONSTRAINT_NAME WHERE c.TABLE_NAME = '{表名}'")
25
28
  主键列=[]
@@ -7,7 +7,7 @@ from database2text.tool import *
7
7
 
8
8
  class mysql(object):
9
9
  def ana_TABLE(otype):
10
- for oname, in db.exec(f"select table_name from information_schema.tables where table_schema='{database}' and table_type='BASE TABLE'"):
10
+ for oname, in db.exec(f"select table_name from information_schema.tables where table_schema='{database}' and table_type='BASE TABLE' order by 1"):
11
11
  if 检查匹配(otype,oname):
12
12
  continue
13
13
  res=db.res1("show create table %s" %(oname))
@@ -28,6 +28,8 @@ class mysql(object):
28
28
 
29
29
  def ana_VIEW(otype):
30
30
  for oname, in db.exec(f"select table_name from information_schema.tables where table_schema='{database}' and table_type='VIEW'"):
31
+ if 检查匹配(otype,oname):
32
+ continue
31
33
  oridata=[]
32
34
  coldata=[]
33
35
  tdesc=db.res1("SELECT TABLE_COMMENT FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME ='%s' AND TABLE_SCHEMA = '%s'" %(oname,database))
@@ -9,7 +9,7 @@ from database2text.tool import *
9
9
 
10
10
  class opengauss(object):
11
11
  def ana_TABLE(otype):
12
- for 表id,表名 in db.exec(f"select oid,relname from pg_class where relkind='r' and relnamespace=(select oid from pg_namespace where nspname='{stdata['schema']}')"):
12
+ for 表id,表名 in db.exec(f"select oid,relname from pg_class where relkind='r' and relnamespace=(select oid from pg_namespace where nspname='{stdata['schema']}') order by oid"):
13
13
  odata=db.res1(f"select pg_get_tabledef({表id})")
14
14
  oridata=[]
15
15
  coldata=[]
@@ -7,7 +7,7 @@ from database2text.tool import *
7
7
 
8
8
  class oracle(object):
9
9
  def ana_TABLE(otype):
10
- for oname, in db.exec("select object_name from all_objects where object_type=:ot and owner=:owner",ot=otype,owner=owner):
10
+ for oname, in db.exec("select object_name from all_objects where object_type=:ot and owner=:owner order by 1",ot=otype,owner=owner):
11
11
  if 检查匹配(otype,oname):
12
12
  continue
13
13
  if oname.startswith("SYS_EXPORT_TABLE"):
@@ -119,8 +119,11 @@ class oracle(object):
119
119
  return ssql
120
120
 
121
121
  def readdata(arg):
122
+ global owner
122
123
  dbdata["sql"]={}
123
124
  dbdata["exp"]={}
125
+ if "owner" in arg:
126
+ owner=arg["owner"]
124
127
  for i in vars(oracle):
125
128
  if i.startswith("ana_"):
126
129
  otype=i[4:]
@@ -2,6 +2,8 @@
2
2
  # -*- coding: utf-8 -*-
3
3
 
4
4
  import sys,os,difflib,jinja2,re,json,datetime,importlib,pathlib
5
+ import db2text as dt
6
+ from pprint import pprint
5
7
 
6
8
  __all__=["db","ckd","dbdata","storidata","stdata","检查匹配","cpt"]
7
9
 
@@ -118,9 +120,14 @@ class dot(object):
118
120
  def __init__(self,arg):
119
121
  模板文件=pathlib.Path.joinpath(pathlib.Path(__file__).parent,"datafile","dot.mb")
120
122
  模板=open(模板文件,encoding="utf8").read()
121
- 分页数据=set()
122
- for page in arg.get("page","如果没有设置page就搞一个默认的").split():
123
- 分页数据.add(arg.get(page,"输出剩下的所有表"))
123
+ 分页数据=[]
124
+ for page in arg.get("page","其它").split():
125
+ if page in ["other","其它","其他"]:
126
+ if arg.get(page,"输出剩下的所有表") not in 分页数据:
127
+ 分页数据.append(arg.get(page,"输出剩下的所有表"))
128
+ elif page in arg:
129
+ if arg[page] not in 分页数据:
130
+ 分页数据.append(arg[page])
124
131
  文件序号=0
125
132
  已经使用=set()
126
133
  dot列表=[]
@@ -142,12 +149,11 @@ class dot(object):
142
149
  fdot.write(jinja2.Template(模板).render(data))
143
150
  fdot.close()
144
151
  dot列表.append(dotname)
145
- if "dotcmd" in arg:
146
- pdfname=os.path.join(arg.get("pdfdir","."),f"{arg['filename']}_{文件序号}.pdf")
147
- pdf列表.append(pdfname)
148
- cmd="%s -Tpdf %s -o %s" %(arg["dotcmd"],dotname,pdfname)
149
- print(cmd)
150
- os.system(cmd)
152
+ pdfname=os.path.join(arg.get("pdfdir","."),f"{arg['filename']}_{文件序号}.pdf")
153
+ pdf列表.append(pdfname)
154
+ cmd='%s -Tpdf %s -o %s' %(arg.get("dotcmd","fdp"),dotname,pdfname)
155
+ print(cmd)
156
+ os.system(cmd)
151
157
  if len(pdf列表)>0:
152
158
  import PyPDF3
153
159
  pdfout=PyPDF3.PdfFileMerger()
@@ -165,7 +171,7 @@ class render(object):
165
171
  self.rendertable(t)
166
172
  def rendertable(self,t):
167
173
  if "help" in stdata and stdata["help"].lower() in ["y","1"]:
168
- print(json.dumps(t,ensure_ascii=False,skipkeys=False,indent=4,cls=ComplexEncoder))
174
+ pprint(t)
169
175
  tpl="" #模板
170
176
  k=False
171
177
  for l in storidata:
@@ -243,6 +249,68 @@ def 检查匹配(类型或者筛选值,名称):
243
249
  排除标志=(加减=="-")
244
250
  return 排除标志
245
251
 
252
+ class wiki(object):
253
+ '处理wiki内容'
254
+ def __init__(self,arg):
255
+ import mwclient,dbcfg
256
+ self.arg=arg
257
+ dt.gd.deftitle=arg.get("title","name:字段名 desc:注释 ts:类型 default:默认值 :说明")
258
+ dbc=dbcfg.use(arg['wiki'])
259
+ cfg=dbc.cfg()
260
+ site = mwclient.Site(cfg["d"]["server"], scheme=cfg["d"]["scheme"],path=cfg["d"]["path"])
261
+ site.login(cfg["d"]["user"],cfg["d"]["password"])
262
+ page = site.pages[arg["page"]]
263
+ if not page.exists:
264
+ quit("未找到wiki页面,请检查配置文件")
265
+ dt.inittext(page.text())
266
+ for t in dbdata["exp"]["TABLE"]:
267
+ if not 检查匹配("TABLE",t["tname"]):
268
+ self.handle_table(t)
269
+ ischanged=False
270
+ if len(dt.gd.text)==len(dt.gd.oritext):
271
+ for i in range(min(len(dt.gd.text),len(dt.gd.oritext))):
272
+ if dt.gd.text[i].strip()!=dt.gd.oritext[i].strip():
273
+ print(f"第{i}行有差异")
274
+ print(dt.gd.text[i]+":")
275
+ print(dt.gd.oritext[i]+":")
276
+ ischanged=True
277
+ else:
278
+ print(f"数据有更新,原始数据{len(dt.gd.oritext)}行,更新数据{len(dt.gd.text)}行")
279
+ if ischanged or len(dt.gd.text)!=len(dt.gd.oritext): #更新后数据和原始数据不符,需要更新
280
+ page.edit(dt.gd.linebreak.join(dt.gd.text))
281
+
282
+ def handle_table(self,t):
283
+ dt.p(1,t["tname"])
284
+ thead=[ #定义模板的头部
285
+ '{| class="wikitable"',
286
+ f"|+ {t['tname']}"
287
+ ]
288
+ ttail=[
289
+ "|}"
290
+ ]
291
+ dt.capture(thead,ttail) #捕获符合条件的数据
292
+ dt.analydata() #解析数据到capturedata
293
+ mdata=dt.mergecolumndata(t) #合并原始数据和用户自己设置的数据返回
294
+ newdata=[]
295
+ if dt.gd.capturenothing and "level" in self.arg:
296
+ titlemark="="* int(self.arg["level"])
297
+ newdata.append(f"{titlemark} {t['tname']} {t['tdesc']} {titlemark}")
298
+ newdata.append('{| class="wikitable"')
299
+ newdata.append(f"|+ {t['tname']} {t['tdesc']}")
300
+ rowdata=[]
301
+ for j in dt.gd.deftitle.split():
302
+ cname,tname=j.split(":")
303
+ rowdata.append(tname)
304
+ newdata.append("|-")
305
+ newdata.append(("! "+" !! ".join(rowdata)).strip())
306
+ for i in dt.createcolumndata(mdata):
307
+ newdata.append("|-")
308
+ newdata.append(("| "+" || ".join(i)).strip())
309
+ newdata.append("|}")
310
+ if dt.gd.capturenothing: #第一次增加一个空行,好看一些
311
+ newdata.append("")
312
+ dt.replacenewdata(newdata) #使用新数据替换掉旧的
313
+
246
314
  db=dblib()
247
315
  ckd=checkdiff()
248
316
  dbdata={} #保存数据库里读到的数据
@@ -0,0 +1,2 @@
1
+ from . util import *
2
+ from . data import *
@@ -0,0 +1,19 @@
1
+ # db2text的全局数据
2
+
3
+ __all__=["gd"]
4
+
5
+ class gd(object): #全局数据
6
+ #以下为设置项
7
+ coding="utf8" #字符集,默认为utf8
8
+ linebreak="\n" #检测到换行符,则替换掉这个,没检测到,使用这个默认的
9
+ infolevel=1 #设置提示信息级别
10
+ localinfolevel=1 #局部消息级别
11
+
12
+ #以下为数据项
13
+ oritext=[] #保存文本的初始值
14
+ text=[] #保存处理后的文本
15
+ capturebegin=0 #捕获的数据在text中的开始序号
16
+ captureend=0 #捕获的数据在text中的结束序号
17
+ capturedata={} #保存捕获的数据,以字段名为键值,保存一个字典,指定一行的数据内容,键值为标题
18
+ capturenothing=True #标记是不是捕获到
19
+ deftitle="" #表头,如 name:字段名 desc:注释 ts:类型 default:默认值 :说明
@@ -0,0 +1 @@
1
+ print("util.init")
@@ -0,0 +1,108 @@
1
+ __all__=["inittext","p","capture","mergecolumndata","createcolumndata","replacenewdata","analydata"]
2
+
3
+ import db2text as dt
4
+ import time,copy
5
+ from pprint import pprint
6
+
7
+ def inittext(text):
8
+ '初始化文本,保存供后续核对,处理行分隔符等'
9
+ dt.gd.oritext=text.splitlines(False) #保存数据供核对
10
+ dt.gd.text=text.splitlines(False) #初始化要处理的数据
11
+ if text.find("\r\n")>=0:
12
+ dt.gd.linebreak="\r\n"
13
+ elif text.find("\r")>=0:
14
+ dt.gd.linebreak="\r"
15
+
16
+ def p(level,fmt,*info):
17
+ '输出级别不高于设置值的信息,级别为-1则输出到标准错误'
18
+ if level>dt.gd.infolevel and level!=-1:return #级别较低不输出,-1是错误输出,输出到标准错误上
19
+ if info is None or not info:
20
+ sinfo = time.strftime('%H:%M:%S') + "|%d: " % (level) + fmt
21
+ else:
22
+ sinfo=time.strftime('%H:%M:%S')+"|%d: " %(level) +fmt %(info)
23
+ if level>=0:
24
+ print(sinfo)
25
+ else:
26
+ sys.stderr.write(sinfo+"\n")
27
+
28
+ def capture(thead,ttail):
29
+ '在dt.gd.text里查找数据,找到则设置开始和结束位置,找不到设置开始位置为文档结尾'
30
+ dt.gd.capturenothing=False
31
+ for i in range(len(dt.gd.text)-len(thead)-len(ttail)):
32
+ for j in range(len(thead)): #依次比较头部
33
+ if not dt.gd.text[i+j].startswith(thead[j]):
34
+ break
35
+ else: #头部比较完成
36
+ for j in range(i+len(thead)+1,len(dt.gd.text)-len(ttail)+1):
37
+ for k in range(len(ttail)): #依次检查是否尾部
38
+ if not dt.gd.text[j+k].startswith(ttail[k]):
39
+ break
40
+ else: #头、尾都符合,是最终结果
41
+ dt.gd.capturebegin=i
42
+ dt.gd.captureend=j+len(ttail)-1
43
+ return
44
+ dt.gd.capturebegin=len(dt.gd.text)
45
+ dt.gd.captureend=len(dt.gd.text)
46
+ dt.gd.capturenothing=True
47
+
48
+ def analydata():
49
+ '根据预定义表头,解析数据成字典放在在capturedata中'
50
+ dt.gd.capturedata={}
51
+ if dt.gd.capturenothing:
52
+ return
53
+ oldtitle=[]
54
+ for i in range(dt.gd.capturebegin,dt.gd.captureend): #查找标题行,识别markdown和wiki格式
55
+ if (dt.gd.text[i].startswith("|") and dt.gd.text[i].endswith("|")) or dt.gd.text[i][0]=="!":
56
+ oldtitle=dt.gd.text[i].replace("!","|").replace("||","|").split("|") #markdown和wiki加工成类似的数据
57
+ oldtitle=oldtitle[1:]
58
+ break
59
+ if len(oldtitle)==0: #没找到正确的标题头
60
+ return
61
+ columnname="" #对应name的标题头,用来做数据的键值
62
+ for j in dt.gd.deftitle.split():
63
+ cname,tname=j.split(":")
64
+ if cname=="name":
65
+ columnname=tname
66
+ break
67
+ if not columnname:
68
+ return
69
+ titlecount=len(oldtitle)
70
+ keynum=-1 #记录字段名对应的列序号
71
+ for i in range(len(oldtitle)):
72
+ if oldtitle[i].strip()==columnname:
73
+ keynum=i
74
+ break
75
+ if keynum==-1:
76
+ return
77
+ for i in range(dt.gd.capturebegin,dt.gd.captureend):
78
+ t=dt.gd.text[i].replace("!","|").replace("||","|").split("|") #markdown和wiki加工成类似的数据
79
+ if len(t)<titlecount+1:
80
+ continue
81
+ t=t[1:]
82
+ dt.gd.capturedata[t[keynum].strip()]={}
83
+ for j in range(len(oldtitle)):
84
+ dt.gd.capturedata[t[keynum].strip()][oldtitle[j].strip()]=t[j].strip()
85
+
86
+ def mergecolumndata(t):
87
+ '''用于markdown和wiki等,合并计算最终的用于生成表格数据的列表
88
+ '''
89
+ retdata=[] #返回结果
90
+ for i in t["md"]: #数据以capturedata为初始值,因为这里可能有字段是用户设置,并非从数据库中获取
91
+ rowdata=copy.deepcopy(dt.gd.capturedata.get(i["name"],{}))
92
+ retdata.append(rowdata | i)
93
+ return retdata
94
+
95
+ def createcolumndata(mdata):
96
+ '根据最终的列表数据,按deftitle规定的格式生成按顺序的列表数据'
97
+ retdata=[]
98
+ for i in mdata:
99
+ rowdata=[]
100
+ for j in dt.gd.deftitle.split():
101
+ cname,tname=j.split(":")
102
+ rowdata.append(i.get(cname,i.get(tname,"")) or "")
103
+ retdata.append(rowdata)
104
+ return retdata
105
+
106
+ def replacenewdata(newdata):
107
+ '用新数据替换掉text中的相应部分'
108
+ dt.gd.text=dt.gd.text[:dt.gd.capturebegin]+newdata+dt.gd.text[dt.gd.captureend+1:]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: db2text
3
- Version: 0.2.1
3
+ Version: 0.2.3
4
4
  Summary: database to text
5
5
  Home-page: https://gitee.com/chenc224/dbt
6
6
  Author: Chen chuan
@@ -4,6 +4,7 @@ README.md
4
4
  setup.py
5
5
  database2text/__init__.py
6
6
  database2text/dbt.py
7
+ database2text/dm.py
7
8
  database2text/mssql.py
8
9
  database2text/mysql.py
9
10
  database2text/opengauss.py
@@ -12,14 +13,21 @@ database2text/tool.py
12
13
  database2text/datafile/dbtmanual.md
13
14
  database2text/datafile/dot.mb
14
15
  database2text/datafile/sample/dbt_dot_mssql.txt
16
+ database2text/datafile/sample/dm/all.txt
15
17
  database2text/datafile/sample/mysql/all.txt
18
+ database2text/datafile/sample/mysql/wiki.txt
16
19
  database2text/datafile/sample/opengauss/all.txt
17
20
  database2text/datafile/sample/oracle/all.txt
21
+ database2text/datafile/sample/oracle/crmrpt.txt
18
22
  database2text/datafile/sample/sqlserver/all.txt
23
+ db2text/__init__.py
24
+ db2text/data.py
25
+ db2text/util.py
19
26
  db2text.egg-info/PKG-INFO
20
27
  db2text.egg-info/SOURCES.txt
21
28
  db2text.egg-info/dependency_links.txt
22
29
  db2text.egg-info/entry_points.txt
23
30
  db2text.egg-info/not-zip-safe
24
31
  db2text.egg-info/requires.txt
25
- db2text.egg-info/top_level.txt
32
+ db2text.egg-info/top_level.txt
33
+ db2text/db/__init__.py
@@ -1 +1,2 @@
1
1
  database2text
2
+ db2text
@@ -8,7 +8,7 @@ with open("README.md", "r",encoding='utf-8') as fh:
8
8
 
9
9
  setuptools.setup(
10
10
  name="db2text",
11
- version="0.2.1",
11
+ version="0.2.3",
12
12
  author="Chen chuan",
13
13
  author_email="kcchen@139.com",
14
14
  description="database to text",
File without changes
File without changes
File without changes
File without changes