Skip to content

Commit 842488e

Browse files
authored
Fix GUC generator (#4291)
* fix path to tooling * add real values * fixes and meta * cosmetics and fixes
1 parent f07cde5 commit 842488e

File tree

3 files changed

+116
-48
lines changed

3 files changed

+116
-48
lines changed

.github/workflows/tsdb-refresh-gucs-list.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ jobs:
2525

2626
- name: Update list of GUCs
2727
run: |
28-
pip install -r ./helper-scripts/timescaledb/requirements.txt
29-
python ./helper-scripts/timescaledb/generate_guc_overview.py "${{ github.event.inputs.tag }}" ./_partials/_timescaledb-gucs.md
28+
pip install -r .helper-scripts/timescaledb/requirements.txt
29+
python .helper-scripts/timescaledb/generate_guc_overview.py "${{ github.event.inputs.tag }}" ./_partials/_timescaledb-gucs.md
3030
3131
- name: Create Pull Request
3232
uses: peter-evans/create-pull-request@v7

.helper-scripts/timescaledb/generate_guc_overview.py

Lines changed: 71 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,17 @@
2020
args = parser.parse_args()
2121

2222
TYPES = {
23-
"DefineCustomBoolVariable": "BOOLEAN",
24-
"DefineCustomIntVariable": "INTEGER",
25-
"DefineCustomEnumVariable": "ENUM",
23+
"DefineCustomBoolVariable" : "BOOLEAN",
24+
"DefineCustomIntVariable" : "INTEGER",
25+
"DefineCustomEnumVariable" : "ENUM",
2626
"DefineCustomStringVariable": "STRING",
27+
"DefineCustomRealVariable" : "REAL",
28+
}
29+
30+
SCOPES = {
31+
"PG16_GE" : "Postgres 16 or greater",
32+
"TS_DEBUG" : "Debug mode",
33+
"USE_TELEMETRY": "Telemetry enabled",
2734
}
2835

2936
# List of GUCs to exclude from the docs
@@ -62,8 +69,22 @@ def unwrap(gucs: list, guc_type: str) -> dict:
6269
map = {}
6370

6471
for guc in gucs:
65-
# sanitize data
66-
it = [re.sub(r"[\n\t]*", "", v).strip() for v in guc.split(",")]
72+
# unwrap element
73+
# first split on new line, then join on ,
74+
lines = [re.sub(r"[\n\t]*", "", v).strip() for v in guc.split("\n")]
75+
it = []
76+
lst = []
77+
for line in lines:
78+
# ends with "," --> take all preceding values from the list
79+
# concatenate them with this element minus the trailing ","
80+
# and reset the list again
81+
if line[-1:] == ",":
82+
val = "".join(lst) + line[:-1]
83+
lst = []
84+
it.append(val)
85+
else:
86+
# add the line to the list to concatenate later
87+
lst.append(line)
6788

6889
# sanitize elements
6990
name = re.sub(r"[\"\(\)]*", "", it[0])
@@ -73,20 +94,19 @@ def unwrap(gucs: list, guc_type: str) -> dict:
7394
# Exclude GUCs (if specified)
7495
if name not in EXCLUDE:
7596
map[name] = {
76-
"name": name,
97+
"name" : name,
7798
"short_desc": short_desc,
78-
"long_desc": long_desc,
79-
"value": get_value(guc_type, it),
80-
"type": guc_type,
81-
"scopes": [], # assigned later during scope discovery
99+
"long_desc" : long_desc,
100+
"value" : get_value(guc_type, it),
101+
"meta" : get_meta_data(guc_type, it),
102+
"type" : guc_type,
103+
"scopes" : [], # assigned later during scope discovery
82104
}
83-
84-
logging.info("registered %d GUCs of type: %s" % (len(map), guc_type))
85105
return map
86106

87107
def sanitize_description(text) -> str:
88108
# Remove all quotes and normalize whitespace to single line
89-
return ' '.join(text.replace('"', '').split()).strip()
109+
return strip_comment_pattern(' '.join(text.replace('"', '').split()).strip())
90110

91111
def strip_comment_pattern(text) -> str:
92112
pattern = r'/\*\s*[a-zA-Z0-9_]*=\s*\*/'
@@ -101,12 +121,21 @@ def get_value(type: str, parts: list) -> str:
101121
"""
102122
Get the value of the GUC based on the type
103123
"""
124+
# ENUM needs different handling, finding the struct and the strings
125+
# identifying the options
126+
127+
# Every other type
128+
return strip_comment_pattern(parts[4]).strip()
129+
130+
def get_meta_data(type: str, parts: list) -> str:
131+
"""
132+
Build any meta data if present based on the type
133+
"""
104134
if type == "BOOLEAN":
105-
if parts[5].upper()[0:4] == "PGC_":
106-
return strip_comment_pattern(parts[4]).strip()
107-
else:
108-
return strip_comment_pattern(parts[5]).strip()
109-
return strip_comment_pattern(parts[5]).strip()
135+
return ""
136+
if type in ["INTEGER", "REAL"]:
137+
return "min: `%s`, max: `%s`" % (strip_comment_pattern(parts[5]).strip(), strip_comment_pattern(parts[6]).strip())
138+
return ""
110139

111140
"""
112141
Parse GUCs and prepare them for rendering
@@ -118,26 +147,43 @@ def prepare(content: str) -> dict:
118147

119148
# Find all GUCs based on patterns and prepare them in a dict
120149
for pattern, val in TYPES.items():
150+
# Run twice to find variants, e.g., there is a nicer way with one regex to do this
151+
# but this is not time sensitive nor consuming, so we're good
152+
# - DefineCustomStringVariable(MAKE_EXTOPTION(
153+
# - DefineCustomStringVariable(/* name= */ MAKE_EXTOPTION(
121154
map.update(unwrap(re.findall(r"%s\(MAKE_EXTOPTION(.*?)\);" % pattern, content, re.DOTALL), val))
155+
map.update(unwrap(re.findall(r"%s\(\/\* name= \*\/ MAKE_EXTOPTION(.*?)\);" % pattern, content, re.DOTALL), val))
122156

123157
# TODO: find scopes
124158
# https://github.com/timescale/timescaledb/blob/2.19.x/src/guc.c#L797
159+
# SCOPES
125160

161+
# print summary
162+
summary = {}
163+
for v in map.values():
164+
if v["type"] not in summary.keys():
165+
summary[v["type"]] = 0
166+
summary[v["type"]] += 1
167+
for k, v in summary.items():
168+
logging.info("registered %d GUCs of type: %s" % (v, k))
126169

127170
# Return dict with alphabetically sorted keys
128171
return {i: map[i] for i in sorted(map.keys())}
129172

130173
"""
131174
Render the GUCs to file
132175
"""
133-
def render(gucs: dict, filename: str):
176+
def render(gucs: dict, filename: str, version: str):
134177
with open(filename, "w") as f:
135-
f.write("| Name | Type | Default | Long Description |\n")
136-
f.write("| -- | -- | -- |--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n")
178+
f.write("| Name | Type | Default | Description |\n")
179+
f.write("| -- | -- | -- | -- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n")
137180
for guc in gucs.values():
138-
f.write("| `%s` | `%s` | `%s` | %s |\n" % (
139-
guc["name"], guc["type"], guc["value"], guc["long_desc"]
140-
))
181+
desc = guc["long_desc"]
182+
if guc["meta"] != "":
183+
desc += "<br />" + guc["meta"]
184+
f.write("| `%s` | `%s` | `%s` | %s |\n" % (guc["name"], guc["type"], guc["value"], desc))
185+
f.write("\n")
186+
f.write("Version: [%s](https://github.com/timescale/timescaledb/releases/tag/%s)" % (version, version))
141187
logging.info("rendering completed to %s" % filename)
142188

143189
"""
@@ -147,6 +193,4 @@ def render(gucs: dict, filename: str):
147193
content = get_content("https://raw.githubusercontent.com/timescale/timescaledb/refs/tags/%s/src/guc.c" % args.tag)
148194
logging.info("fetched guc.c file for version: %s" % args.tag)
149195
gucs = prepare(content)
150-
render(gucs, args.destination)
151-
152-
# print(gucs)
196+
render(gucs, args.destination, args.tag)

0 commit comments

Comments
 (0)