Skip to content

Instantly share code, notes, and snippets.

@lovasoa
Last active February 28, 2025 11:54
Show Gist options
  • Save lovasoa/4e8643264797d7087ff285ea57d40097 to your computer and use it in GitHub Desktop.
Save lovasoa/4e8643264797d7087ff285ea57d40097 to your computer and use it in GitHub Desktop.
convert sql block comments (`/*`) to standard line comments (`--`) https://sql-comments-updater.netlify.app/
#!/usr/bin/env python3
"""
SQL Block Comment to Line Comment Converter
This script processes SQL files or input text and replaces all block comments (/* */)
with standard SQL line comments (--), correctly handling nested block comments.
"""
import re
import sys
import argparse
from pathlib import Path
def replace_block_comments(sql_content: str) -> str:
"""
Replace SQL block comments (/* */) with line comments (--), handling nested comments.
Args:
sql_content (str): SQL content with block comments
Returns:
str: SQL content with block comments replaced by line comments
Examples:
>>> replace_block_comments("SELECT 1;")
'SELECT 1;'
>>> replace_block_comments("SELECT /* comment */ 1;")
'SELECT \\n-- comment\\n 1;'
>>> replace_block_comments("SELECT /* multi\\nline\\ncomment */ 1;")
'SELECT \\n-- multi\\n-- line\\n-- comment\\n 1;'
>>> replace_block_comments("/* Starts with comment */ SELECT 1;")
'\\n-- Starts with comment\\n SELECT 1;'
>>> replace_block_comments("SELECT 1 /* Ends with comment */;")
'SELECT 1 \\n-- Ends with comment\\n;'
>>> replace_block_comments("SELECT /* level1 \\n /* level2 comment */\\n */ 1;")
'SELECT \\n-- level1\\n-- -- level2 comment\\n 1;'
>>> replace_block_comments("SELECT '/* not a comment */' FROM table;")
"SELECT '/* not a comment */' FROM table;"
"""
output: list[str] = []
i = 0
length = len(sql_content)
in_block_comment = 0 # Tracks nesting level
comment_buffer: list[str] = []
inside_string = False
string_delimiter = ''
comment_nesting_levels: list[int] = [] # Track nesting level for each character in comment
while i < length:
if inside_string:
if sql_content[i] == string_delimiter:
inside_string = False
output.append(sql_content[i])
elif sql_content[i] in "'\"":
inside_string = True
string_delimiter = sql_content[i]
output.append(sql_content[i])
elif sql_content[i:i+2] == "/*":
if in_block_comment == 0:
comment_buffer = []
comment_nesting_levels = []
output.append("\n")
in_block_comment += 1
i += 1 # Skip next character too
elif sql_content[i:i+2] == "*/" and in_block_comment > 0:
in_block_comment -= 1
i += 1 # Skip next character too
if in_block_comment == 0:
# Convert buffered comment into line comments
comment_text = "".join(comment_buffer).strip()
if comment_text:
lines = comment_text.split("\n")
for line_idx, line in enumerate(lines):
stripped_line = line.strip()
if stripped_line:
# Find the nesting level for this line
line_start_idx = sum(len(l) + 1 for l in lines[:line_idx])
line_nesting = max([1] + [level for idx, level in enumerate(comment_nesting_levels)
if line_start_idx <= idx < line_start_idx + len(line)])
# Add appropriate number of dashes based on nesting level
leading_dash = "-- " * line_nesting
output.append(f"{leading_dash}{stripped_line}\n")
comment_buffer.clear()
comment_nesting_levels.clear()
elif in_block_comment > 0:
comment_buffer.append(sql_content[i])
comment_nesting_levels.append(in_block_comment)
else:
output.append(sql_content[i])
i += 1
result = "".join(output)
return result
def process_sql_file(file_path: Path) -> bool:
"""Process a single SQL file, replacing block comments with line comments."""
try:
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
new_content = replace_block_comments(content)
if new_content != content:
with open(file_path, 'w', encoding='utf-8') as f:
f.write(new_content)
print(f"Updated: {file_path}")
return True
except Exception as e:
print(f"Error processing {file_path}: {e}")
return False
def process_directory(directory_path: Path) -> tuple[int, int]:
"""Recursively process all SQL files in a directory."""
files_processed = 0
files_modified = 0
for sql_file in directory_path.rglob('*.sql'):
files_processed += 1
if process_sql_file(sql_file):
files_modified += 1
return files_processed, files_modified
def main():
"""Main function to parse arguments and process SQL files."""
parser = argparse.ArgumentParser(
description='Convert SQL block comments to line comments in .sql files.'
)
parser.add_argument('directory', help='Directory to recursively search for SQL files', type=Path)
parser.add_argument('--test', action='store_true', help='Run doctests instead of processing files')
args = parser.parse_args()
if args.test:
import doctest
doctest.testmod(verbose=True)
return
directory = args.directory
if not directory.is_dir():
print(f"Error: {directory} is not a valid directory.")
sys.exit(1)
print(f"Starting SQL comment conversion in {directory}...")
files_processed, files_modified = process_directory(directory)
print(f"\nSummary:\n Files processed: {files_processed}\n Files modified: {files_modified}")
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment