From 5ff9a8a24e0bbbb96eb7cb5e4daf8c83f7d48157 Mon Sep 17 00:00:00 2001 From: Hein Date: Wed, 3 Dec 2025 11:42:32 +0200 Subject: [PATCH] Fixed blank params on funcspec --- pkg/funcspec/function_api.go | 42 +++++++++++++++++++-- pkg/funcspec/function_api_test.go | 62 +++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+), 4 deletions(-) diff --git a/pkg/funcspec/function_api.go b/pkg/funcspec/function_api.go index 8826914..d92c411 100644 --- a/pkg/funcspec/function_api.go +++ b/pkg/funcspec/function_api.go @@ -163,8 +163,9 @@ func (h *Handler) SqlQueryList(sqlquery string, pNoCount, pBlankparms, pAllowFil // Remove unused input variables if pBlankparms { for _, kw := range inputvars { - sqlquery = strings.ReplaceAll(sqlquery, kw, "") - logger.Debug("Removed unused variable: %s", kw) + replacement := getReplacementForBlankParam(sqlquery, kw) + sqlquery = strings.ReplaceAll(sqlquery, kw, replacement) + logger.Debug("Replaced unused variable %s with: %s", kw, replacement) } } @@ -501,8 +502,9 @@ func (h *Handler) SqlQuery(sqlquery string, pBlankparms bool) HTTPFuncType { // Remove unused input variables if pBlankparms { for _, kw := range inputvars { - sqlquery = strings.ReplaceAll(sqlquery, kw, "") - logger.Debug("Removed unused variable: %s", kw) + replacement := getReplacementForBlankParam(sqlquery, kw) + sqlquery = strings.ReplaceAll(sqlquery, kw, replacement) + logger.Debug("Replaced unused variable %s with: %s", kw, replacement) } } @@ -870,6 +872,38 @@ func IsNumeric(s string) bool { return err == nil } +// getReplacementForBlankParam determines the replacement value for an unused parameter +// based on whether it appears within quotes in the SQL query. +// It checks for PostgreSQL quotes: single quotes ('') and dollar quotes ($...$) +func getReplacementForBlankParam(sqlquery, param string) string { + // Find the parameter in the query + idx := strings.Index(sqlquery, param) + if idx < 0 { + return "" + } + + // Check characters immediately before and after the parameter + var charBefore, charAfter byte + + if idx > 0 { + charBefore = sqlquery[idx-1] + } + + endIdx := idx + len(param) + if endIdx < len(sqlquery) { + charAfter = sqlquery[endIdx] + } + + // Check if parameter is surrounded by quotes (single quote or dollar sign for PostgreSQL dollar-quoted strings) + if (charBefore == '\'' || charBefore == '$') && (charAfter == '\'' || charAfter == '$') { + // Parameter is in quotes, return empty string + return "" + } + + // Parameter is not in quotes, return NULL + return "NULL" +} + // makeResultReceiver creates a slice of interface{} pointers for scanning SQL rows // func makeResultReceiver(length int) []interface{} { // result := make([]interface{}, length) diff --git a/pkg/funcspec/function_api_test.go b/pkg/funcspec/function_api_test.go index c34c803..fb700d4 100644 --- a/pkg/funcspec/function_api_test.go +++ b/pkg/funcspec/function_api_test.go @@ -835,3 +835,65 @@ func TestReplaceMetaVariables(t *testing.T) { }) } } + +// TestGetReplacementForBlankParam tests the blank parameter replacement logic +func TestGetReplacementForBlankParam(t *testing.T) { + tests := []struct { + name string + sqlQuery string + param string + expected string + }{ + { + name: "Parameter in single quotes", + sqlQuery: "SELECT * FROM users WHERE name = '[username]'", + param: "[username]", + expected: "", + }, + { + name: "Parameter in dollar quotes", + sqlQuery: "SELECT * FROM users WHERE data = $[jsondata]$", + param: "[jsondata]", + expected: "", + }, + { + name: "Parameter not in quotes", + sqlQuery: "SELECT * FROM users WHERE id = [user_id]", + param: "[user_id]", + expected: "NULL", + }, + { + name: "Parameter not in quotes with AND", + sqlQuery: "SELECT * FROM users WHERE id = [user_id] AND status = 1", + param: "[user_id]", + expected: "NULL", + }, + { + name: "Parameter in mixed quote context - before quote", + sqlQuery: "SELECT * FROM users WHERE id = [user_id] AND name = 'test'", + param: "[user_id]", + expected: "NULL", + }, + { + name: "Parameter in mixed quote context - in quotes", + sqlQuery: "SELECT * FROM users WHERE name = '[username]' AND id = 1", + param: "[username]", + expected: "", + }, + { + name: "Parameter with dollar quote tag", + sqlQuery: "SELECT * FROM users WHERE body = $tag$[content]$tag$", + param: "[content]", + expected: "", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := getReplacementForBlankParam(tt.sqlQuery, tt.param) + if result != tt.expected { + t.Errorf("Expected replacement '%s', got '%s' for query: %s", tt.expected, result, tt.sqlQuery) + } + }) + } +}