1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
#! /usr/bin/env python
"""Prints out SQL files with psql command execution.
Supported psql commands: \i, \cd, \q
Others are skipped.
Aditionally does some pre-processing for NDoc.
NDoc is looks nice but needs some hand-holding.
Bug:
- function def end detection searches for 'as'/'is' but does not check
word boundaries - finds them even in function name. That means in
main conf, as/is must be disabled and $ ' added. This script can
remove the unnecessary AS from output.
Niceties:
- Ndoc includes function def in output only if def is after comment.
But for SQL functions its better to have it after def.
This script can swap comment and def.
- Optionally remove CREATE FUNCTION (OR REPLACE) from def to
keep it shorter in doc.
Note:
- NDoc compares real function name and name in comment. if differ,
it decides detection failed.
"""
import sys, os, re, getopt
def usage(x):
print("usage: catsql [--ndoc] FILE [FILE ...]")
sys.exit(x)
# NDoc specific changes
cf_ndoc = 0
# compile regexes
func_re = r"create\s+(or\s+replace\s+)?function\s+"
func_rc = re.compile(func_re, re.I)
comm_rc = re.compile(r"^\s*([#]\s*)?(?P<com>--.*)", re.I)
end_rc = re.compile(r"\b([;]|begin|declare|end)\b", re.I)
as_rc = re.compile(r"\s+as\s+", re.I)
cmd_rc = re.compile(r"^\\([a-z]*)(\s+.*)?", re.I)
# conversion func
def fix_func(ln):
# if ndoc, replace AS with ' '
if cf_ndoc:
return as_rc.sub(' ', ln)
else:
return ln
# got function def
def proc_func(f, ln):
# remove CREATE OR REPLACE
if cf_ndoc:
ln = func_rc.sub('', ln)
ln = fix_func(ln)
pre_list = [ln]
comm_list = []
while 1:
ln = f.readline()
if not ln:
break
com = None
if cf_ndoc:
com = comm_rc.search(ln)
if cf_ndoc and com:
pos = com.start('com')
comm_list.append(ln[pos:])
elif end_rc.search(ln):
break
elif len(comm_list) > 0:
break
else:
pre_list.append(fix_func(ln))
if len(comm_list) > 2:
map(sys.stdout.write, comm_list)
map(sys.stdout.write, pre_list)
else:
map(sys.stdout.write, pre_list)
map(sys.stdout.write, comm_list)
if ln:
sys.stdout.write(fix_func(ln))
def cat_file(fn):
sys.stdout.write("\n")
f = open(fn)
while 1:
ln = f.readline()
if not ln:
break
m = cmd_rc.search(ln)
if m:
cmd = m.group(1)
if cmd == "i": # include a file
fn2 = m.group(2).strip()
cat_file(fn2)
elif cmd == "q": # quit
sys.exit(0)
elif cmd == "cd": # chdir
cd_dir = m.group(2).strip()
os.chdir(cd_dir)
else: # skip all others
pass
else:
if func_rc.search(ln): # function header
proc_func(f, ln)
else: # normal sql
sys.stdout.write(ln)
sys.stdout.write("\n")
def main():
global cf_ndoc
try:
opts, args = getopt.gnu_getopt(sys.argv[1:], 'h', ['ndoc'])
except getopt.error, d:
print(str(d))
usage(1)
for o, v in opts:
if o == "-h":
usage(0)
elif o == "--ndoc":
cf_ndoc = 1
for fn in args:
cat_file(fn)
if __name__ == '__main__':
main()
|