@@ -58,8 +58,37 @@ func buildBulkInsertQuery(originalQuery string, numArgs int, numParamsPerArg int
5858 trimmedQuery = trimmedQuery [:len (trimmedQuery )- 1 ]
5959 }
6060
61- // search "VALUES" (case insensitive)
62- valuesUpperIndex := strings .LastIndex (strings .ToUpper (trimmedQuery ), "VALUES" )
61+ // Find the start of the suffix (e.g., "ON DUPLICATE", "ON CONFLICT")
62+ // We must do this *before* searching for "VALUES" to avoid matching "VALUES()"
63+ // functions inside the suffix.
64+ var querySuffixStr string
65+ var queryWithoutSuffix string
66+
67+ // Use LastIndex to find the main clause
68+ onDuplicateUpperIndex := strings .LastIndex (strings .ToUpper (trimmedQuery ), "ON DUPLICATE KEY UPDATE" )
69+ onConflictUpperIndex := strings .LastIndex (strings .ToUpper (trimmedQuery ), "ON CONFLICT" )
70+
71+ // Find the earliest starting position of any suffix keyword
72+ suffixBoundary := len (trimmedQuery )
73+ if onDuplicateUpperIndex != - 1 {
74+ suffixBoundary = onDuplicateUpperIndex
75+ }
76+ if onConflictUpperIndex != - 1 && onConflictUpperIndex < suffixBoundary {
77+ suffixBoundary = onConflictUpperIndex
78+ }
79+
80+ if suffixBoundary < len (trimmedQuery ) {
81+ // Suffix found
82+ queryWithoutSuffix = trimmedQuery [:suffixBoundary ]
83+ querySuffixStr = " " + strings .TrimSpace (trimmedQuery [suffixBoundary :])
84+ } else {
85+ // No suffix
86+ queryWithoutSuffix = trimmedQuery
87+ querySuffixStr = ""
88+ }
89+
90+ // search "VALUES" (case insensitive) in the part before the suffix
91+ valuesUpperIndex := strings .LastIndex (strings .ToUpper (queryWithoutSuffix ), "VALUES" )
6392 if valuesUpperIndex == - 1 {
6493 return "" , fmt .Errorf ("invalid query format: VALUES clause not found in original query: %s" , originalQuery )
6594 }
@@ -69,28 +98,11 @@ func buildBulkInsertQuery(originalQuery string, numArgs int, numParamsPerArg int
6998 // Add "VALUES" to this
7099 queryPrefixStr := strings .TrimSpace (trimmedQuery [:valuesUpperIndex ]) + " VALUES "
71100
72- // Find the suffix of the query (e.g., "ON DUPLICATE KEY UPDATE ...")
73- // The suffix starts after the original placeholder, e.g., after `VALUES (?, ?)`
74- var querySuffixStr string
75- // Start searching after the "VALUES" keyword
76- searchStartIndex := valuesUpperIndex + len ("VALUES" )
77- // Find the first closing parenthesis ')' after the "VALUES" clause
78- firstClosingParenIndex := strings .Index (trimmedQuery [searchStartIndex :], ")" )
79-
80- if firstClosingParenIndex != - 1 {
81- // The suffix is the part of the string after the closing parenthesis.
82- // We add 1 to skip the ')' character itself.
83- suffixStartIndex := searchStartIndex + firstClosingParenIndex + 1
84- if suffixStartIndex < len (trimmedQuery ) {
85- querySuffixStr = " " + strings .TrimSpace (trimmedQuery [suffixStartIndex :])
86- }
87- }
88-
89101 var queryBuilder strings.Builder
90102 queryBuilder .WriteString (queryPrefixStr )
91103
92104 valueStrings := make ([]string , numArgs )
93- for i := 0 ; i < numArgs ; i ++ {
105+ for i := range numArgs {
94106 placeholders := make ([]string , numParamsPerArg )
95107 for j := 0 ; j < numParamsPerArg ; j ++ {
96108 placeholders [j ] = "?"
0 commit comments