Skip to content

Instantly share code, notes, and snippets.

@JamoCA
Last active April 20, 2026 18:22
Show Gist options
  • Select an option

  • Save JamoCA/4bc3d41106b1da4f8511020985ecd350 to your computer and use it in GitHub Desktop.

Select an option

Save JamoCA/4bc3d41106b1da4f8511020985ecd350 to your computer and use it in GitHub Desktop.
markdownTableToQuery UDF - Converts a markdown table string to a ColdFusion query with VARCHAR columns.
<cfscript>
/**
* Converts a markdown table string to a ColdFusion query with VARCHAR columns.
* James Moberg - 2026-04-17 myCFML.com / SunStarMedia.com
* https://gist.github.com/JamoCA/4bc3d41106b1da4f8511020985ecd350
* @markdown The markdown table string to parse.
* @return A query object with columns derived from the table header.
*/
public query function markdownTableToQuery(required string markdown) hint="Converts a markdown table to a CF query with VARCHAR columns" {
// Local closure: parses a single markdown row into an array of trimmed cells.
// Handles escaped pipes (\|) and strips leading/trailing pipes.
var parseRow = function(required string row) {
var line = trim(arguments.row);
// Strip one leading and one trailing pipe if present
if (left(line, 1) eq "|") {
line = mid(line, 2, len(line) - 1);
}
if (right(line, 1) eq "|") {
line = left(line, len(line) - 1);
}
// Temporarily protect escaped pipes with a safe placeholder token,
// split on the real pipes, then restore the escaped pipes as literal "|".
var placeholder = chr(31); // unit separator
if (find(placeholder, line)) placeholder = chr(28); // file separator
if (find(placeholder, line)) placeholder = chr(29); // group separator
line = replace(line, "\|", placeholder, "all");
// includeEmptyFields=true so empty cells survive; multiCharDelim=false
var cells = listtoarray(line, "|", true, false);
var out = [];
for (var cell in cells) {
out.append(trim(replace(cell, placeholder, "|", "all")));
}
return out;
};
var lines = [];
var rawLines = listtoarray(arguments.markdown, chr(10), false, true);
// Trim each line and drop blanks
for (var rawLine in rawLines) {
var trimmed = trim(replace(rawLine, chr(13), "", "all"));
if (len(trimmed)) {
lines.append(trimmed);
}
}
if (arraylen(lines) lt 2) {
throw(type = "InvalidMarkdownTable", message = "Markdown table must contain at least a header row and a separator row.");
}
// Parse header row into column names
var headerCells = parseRow(lines[ 1 ]);
if (!arraylen(headerCells)) {
throw(type = "InvalidMarkdownTable", message = "Header row contains no columns.");
}
// Validate separator row (line 2): cells made of dashes, optional leading/trailing colons
var separatorCells = parseRow(lines[ 2 ]);
if (arraylen(separatorCells) lt arraylen(headerCells)) {
throw(type = "InvalidMarkdownTable", message = "Separator row has fewer columns than header.");
}
for (var sep in separatorCells) {
if (!refind("^:?-{3,}:?$", sep)) {
throw(type = "InvalidMarkdownTable", message = "Invalid separator row: '#sep#'.");
}
}
// Build column type list (all VARCHAR)
var columnTypes = [];
for (var i = 1; i lte arraylen(headerCells); i++) {
columnTypes.append("VARCHAR");
}
var result = querynew(arraytolist(headerCells), arraytolist(columnTypes));
// Populate data rows (everything after the separator)
for (var i = 3; i lte arraylen(lines); i++) {
var rowCells = parseRow(lines[ i ]);
// Normalize cell count to match headers (pad short rows, truncate long ones)
while (arraylen(rowCells) lt arraylen(headerCells)) {
rowCells.append("");
}
if (arraylen(rowCells) gt arraylen(headerCells)) {
rowCells = rowCells.slice(1, arraylen(headerCells));
}
queryaddrow(result);
for (var c = 1; c lte arraylen(headerCells); c++) {
querysetcell(result, headerCells[ c ], rowCells[ c ], result.recordcount);
}
}
return result;
}
</cfscript>
<!---
<cfsavecontent variable="markdown">
| Name | Expression | Notes |
|---------|----------------------|----------------|
| Alice | a \| b | pipe in middle |
| Bob | \| starts with pipe | leading |
| Carol | ends with pipe \| | trailing |
| Dave | \| | only a pipe |
</cfsavecontent>
<cfset q = markdownTableToQuery(markdown)>
<cfdump var="#q#">
--->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment