MCPcopy Index your code
hub / github.com/riverqueue/river / RunSafely

Method RunSafely

rivershared/sqlctemplate/sqlc_template.go:127–250  ·  view source on GitHub ↗

RunSafely is the same as Run, but returns an error in case of missing or extra templates.

(ctx context.Context, argPlaceholder, sql string, args []any)

Source from the content-addressed store, hash-verified

125// RunSafely is the same as Run, but returns an error in case of missing or
126// extra templates.
127func (r *Replacer) RunSafely(ctx context.Context, argPlaceholder, sql string, args []any) (string, []any, error) {
128 var (
129 container, containerOK = ctx.Value(contextKey{}).(*contextContainer)
130 sqlContainsTemplate = strings.Contains(sql, "/* TEMPLATE")
131 )
132 switch {
133 case !containerOK && !sqlContainsTemplate:
134 // Neither context container or template in SQL; short circuit fast because there's no work to do.
135 return sql, args, nil
136
137 case containerOK && !sqlContainsTemplate:
138 return "", nil, errors.New("sqlctemplate found context container but SQL contains no templates; bug?")
139
140 case !containerOK && sqlContainsTemplate:
141 return "", nil, errors.New("sqlctemplate found template(s) in SQL, but no context container; bug?")
142 }
143
144 cacheKey, cacheEligible := replacerCacheKeyFrom(sql, container)
145 if cacheEligible {
146 r.cacheMu.RLock()
147 var (
148 cachedSQL string
149 cachedSQLOK bool
150 )
151 if r.cache != nil { // protect against map not initialized yet
152 cachedSQL, cachedSQLOK = r.cache[cacheKey]
153 }
154 r.cacheMu.RUnlock()
155
156 // If all input templates were stable, the finished SQL will have been cached.
157 if cachedSQLOK {
158 if len(container.NamedArgs) > 0 {
159 // Named args must be appended in sorted order to match the
160 // placeholder positions baked into the cached SQL during
161 // RunSafely's cache miss path.
162 sortedNamedArgs := maputil.Keys(container.NamedArgs)
163 slices.Sort(sortedNamedArgs)
164 for _, name := range sortedNamedArgs {
165 args = append(args, container.NamedArgs[name])
166 }
167 }
168 return cachedSQL, args, nil
169 }
170 }
171
172 var (
173 templatesExpected = maputil.Keys(container.Replacements)
174 templatesMissing []string // not preallocated because we don't expect any missing parameters in the common case
175 )
176
177 replaceTemplate := func(sql string, templateRE *regexp.Regexp) string {
178 return templateRE.ReplaceAllStringFunc(sql, func(templateStr string) string {
179 // Really dumb, but Go doesn't provide any way to get submatches in a
180 // function, so we have to match twice.
181 // https://github.com/golang/go/issues/5690
182 matches := templateRE.FindStringSubmatch(templateStr)
183
184 template := matches[1]

Callers 7

BenchmarkReplacerFunction · 0.95
RunMethod · 0.95
TestReplacerFunction · 0.80
TestSchemaTemplateParamFunction · 0.80
TestSchemaTemplateParamFunction · 0.80
TestSchemaTemplateParamFunction · 0.80
RunMethod · 0.80

Calls 4

KeysFunction · 0.92
replacerCacheKeyFromFunction · 0.85
ErrorfMethod · 0.65
ValueMethod · 0.45

Tested by 5

BenchmarkReplacerFunction · 0.76
TestReplacerFunction · 0.64
TestSchemaTemplateParamFunction · 0.64
TestSchemaTemplateParamFunction · 0.64
TestSchemaTemplateParamFunction · 0.64