func SubstituteNamedParameters(parsedSql *ParsedSql, translator Translator) string {
	originalSql := parsedSql.String()
	actualSql := tk.NewStrBuffer()
	paramNames := parsedSql.Names
	lastIndex := 0
	for i, v := range paramNames {
		indexes := parsedSql.Indexes[i]
		startIndex := indexes[0]
		endIndex := indexes[1]
		actualSql.Add(originalSql[lastIndex:startIndex])
		actualSql.Add(translator.GetPlaceholder(i, v))
		lastIndex = endIndex
	}
	actualSql.Add(originalSql[lastIndex:])
	return actualSql.String()
}
Parse the SQL statement and locate any placeholders or named parameters.
Named parameters are substituted for a '?' placeholder

param parsedSql
the parsed represenation of the SQL statement
param paramSource
the source for named parameters
return the SQL statement with substituted parameters
see #parseSqlStatement
package db
import (
	tk "github.com/quintans/toolkit"
	coll "github.com/quintans/toolkit/collections"
	. "github.com/quintans/toolkit/ext"
	"unicode"
)
const PARAMETER_SEPARATORS = `"':&,;()|=+-*%/\<>^`
//Set of characters that qualify as comment or quotes starting characters.
var START_SKIP = []string{"'", "\"", "--", "/*"}
// Set of characters that at are the corresponding comment or quotes ending characters.
var STOP_SKIP = []string{"'", "\"", "\n", "*/"}
func ParseSqlStatement(statement string) *ParsedSql {
	namedParameters := coll.NewHashSet()
	parsedSql := NewParsedSql(statement)
	length := len(statement)
	for i, c := range statement {
		if c == ':' || c == '&' {
			j := i + 1
			if j < length && statement[j] == ':' && c == ':' {
				i = i + 2
				continue
			}
			for j < length && !isParameterSeparator(rune(statement[j])) {
				j++
			}
			if (j - i) > 1 {
				parameter := Str(statement[i+1 : j])
				if !namedParameters.Contains(parameter) {
					namedParameters.Add(parameter)
				}
				parsedSql.AddNamedParameter(parameter.String(), i, j)
			}
			i = j - 1
		}
		i++
	}
	return parsedSql
}
func skipCommentsAndQuotes(statement string, position int) int {
	for i := 0; i < len(START_SKIP); i++ {
		if statement[position] == START_SKIP[i][0] {
			match := true
			for j := 1; j < len(START_SKIP[i]); j++ {
				if statement[position+j] != START_SKIP[i][j] {
					match = false
					break
				}
			}
			if match {
				offset := len(START_SKIP[i])
				for m := position + offset; m < len(statement); m++ {
					if statement[m] == STOP_SKIP[i][0] {
						endMatch := true
						endPos := m
						for n := 1; n < len(STOP_SKIP[i]); n++ {
							if (m + n) >= len(statement) {
								return len(statement)
							}
							if statement[m+n] != STOP_SKIP[i][n] {
								endMatch = false
								break
							}
							endPos = m + n
						}
						if endMatch {
							return endPos + 1
						}
					}
				}
				return len(statement)
			}
		}
	}
	return position
}
func isParameterSeparator(c rune) bool {
	if unicode.IsSpace(c) {
		return true
	}
	for _, ps := range PARAMETER_SEPARATORS {
		if c == ps {
			return true
		}
	}
	return false
}