File: catsql.py

package info (click to toggle)
pgq-node 3.4-3
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 352 kB
  • sloc: sql: 1,398; python: 309; makefile: 20; sh: 1
file content (144 lines) | stat: -rwxr-xr-x 3,598 bytes parent folder | download | duplicates (8)
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
141
142
143
144
#! /usr/bin/env python3

"""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:
        for el in comm_list:
            sys.stdout.write(el)
        for el in pre_list:
            sys.stdout.write(el)
    else:
        for el in pre_list:
            sys.stdout.write(el)
        for el in comm_list:
            sys.stdout.write(el)
    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 as 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()