@@ -410,6 +410,85 @@ go_emit_str(GoTok *tk, StringInfo out)
410410 appendStringInfoChar (out , '\'' );
411411}
412412
413+ /* emit a Go format string as one SQL format() accepts.
414+
415+ * Go's verbs all render their operand as text, which is what format()'s %s
416+ * does, so every directive becomes %s. format() takes only a '-' flag and a
417+ * width, so Go's other flags and its precision field are dropped: the operand
418+ * still appears, just without the padding Go would have applied. A doubled
419+ * A doubled %% passes through, and a '%' that starts no directive is escaped
420+ * to %%, so it reaches the caller as a literal percent instead of tripping
421+ * format() at run time.
422+ */
423+ static void
424+ go_emit_format_str (GoTok * tk , StringInfo out )
425+ {
426+ StringInfoData raw ;
427+ int i ;
428+
429+ initStringInfo (& raw );
430+ go_emit_str (tk , & raw ); /* a quoted SQL literal, escapes decoded */
431+
432+ for (i = 0 ; i < raw .len ; i ++ )
433+ {
434+ char c = raw .data [i ];
435+ bool dash = false;
436+ int j ,
437+ wstart ,
438+ wlen ;
439+
440+ if (c != '%' )
441+ {
442+ appendStringInfoChar (out , c );
443+ continue ;
444+ }
445+ if (i + 1 < raw .len && raw .data [i + 1 ] == '%' )
446+ {
447+ appendStringInfoString (out , "%%" );
448+ i ++ ;
449+ continue ;
450+ }
451+
452+ j = i + 1 ;
453+ while (j < raw .len && (raw .data [j ] == '-' || raw .data [j ] == '+' ||
454+ raw .data [j ] == ' ' || raw .data [j ] == '#' ||
455+ raw .data [j ] == '0' ))
456+ {
457+ if (raw .data [j ] == '-' )
458+ dash = true;
459+ j ++ ;
460+ }
461+ wstart = j ;
462+ while (j < raw .len && raw .data [j ] >= '0' && raw .data [j ] <= '9' )
463+ j ++ ;
464+ wlen = j - wstart ;
465+ if (j < raw .len && raw .data [j ] == '.' )
466+ {
467+ j ++ ;
468+ while (j < raw .len && raw .data [j ] >= '0' && raw .data [j ] <= '9' )
469+ j ++ ;
470+ }
471+ if (j >= raw .len ||
472+ !((raw .data [j ] >= 'a' && raw .data [j ] <= 'z' ) ||
473+ (raw .data [j ] >= 'A' && raw .data [j ] <= 'Z' )))
474+ {
475+ /* starts no directive: escape it, since a lone '%' is itself
476+ * an error to format() */
477+ appendStringInfoString (out , "%%" );
478+ continue ;
479+ }
480+
481+ appendStringInfoChar (out , '%' );
482+ if (dash )
483+ appendStringInfoChar (out , '-' );
484+ if (wlen > 0 )
485+ appendBinaryStringInfo (out , raw .data + wstart , wlen );
486+ appendStringInfoChar (out , 's' );
487+ i = j ;
488+ }
489+ pfree (raw .data );
490+ }
491+
413492/* map a Go base type name to a PostgreSQL type; NULL if unknown */
414493static const char *
415494go_base_type (const char * s , int len )
@@ -580,6 +659,22 @@ go_emit_call(Go *g, int i, int b, StringInfo out, int *ni)
580659 close = go_match (g , lp , b );
581660 if (close < 0 )
582661 return false;
662+
663+ /* fmt.Sprintf in expression position: reproduce the format string,
664+ * rewriting Go's verbs into the ones SQL format() understands */
665+ if (go_ci (pkg , "fmt" ) && go_ci (meth , "Sprintf" ) &&
666+ close > lp + 1 && g -> t [lp + 1 ].kind == GO_STR )
667+ {
668+ go_sp (out );
669+ appendStringInfoString (out , "format(" );
670+ go_emit_format_str (& g -> t [lp + 1 ], out );
671+ if (lp + 2 < close )
672+ go_emit_range (g , lp + 2 , close , out );
673+ appendStringInfoChar (out , ')' );
674+ * ni = close + 1 ;
675+ return true;
676+ }
677+
583678 for (k = 0 ; r [k ].pkg ; k ++ )
584679 if ((int ) strlen (r [k ].pkg ) == pkg -> len &&
585680 strncmp (r [k ].pkg , pkg -> s , pkg -> len ) == 0 &&
0 commit comments