From f0221fe220bd82defe131c3052a447b7e60c1774 Mon Sep 17 00:00:00 2001
From: Cyn <39891200+cynsupercat@users.noreply.github.com>
Date: Mon, 10 Apr 2023 13:21:21 +1000
Subject: [PATCH 1/3] Add Inline query + args print

---
 boil/debug.go                                 | 38 +++++++++++++
 boil/debug_test.go                            | 57 +++++++++++++++++++
 .../driver/override/main/17_upsert.go.tpl     |  6 +-
 .../driver/override/main/17_upsert.go.tpl     | 12 ++--
 .../driver/override/main/17_upsert.go.tpl     |  6 +-
 .../driver/override/main/17_upsert.go.tpl     |  6 +-
 queries/query.go                              | 19 ++-----
 7 files changed, 111 insertions(+), 33 deletions(-)
 create mode 100644 boil/debug_test.go

diff --git a/boil/debug.go b/boil/debug.go
index 354d08ba7..5be6c90d8 100644
--- a/boil/debug.go
+++ b/boil/debug.go
@@ -2,8 +2,11 @@ package boil
 
 import (
 	"context"
+	"fmt"
 	"io"
 	"os"
+	"regexp"
+	"strings"
 )
 
 // DebugMode is a flag controlling whether generated sql statements and
@@ -47,3 +50,38 @@ func DebugWriterFrom(ctx context.Context) io.Writer {
 	}
 	return DebugWriter
 }
+
+// PrintQuery prints a modified query string with placeholders replaced by their
+// corresponding argument values to writer.
+func PrintQuery(writer io.Writer, query string, args ...interface{}) {
+	substitutedQuery := substituteQueryArgs(query, args...)
+	fmt.Fprintln(writer, substitutedQuery)
+}
+
+// substituteQueryArgs takes a SQL query string and an array of
+// // arguments. It returns a modified query string with placeholders replaced by their
+// // corresponding argument values.
+func substituteQueryArgs(query string, args ...interface{}) string {
+	// find all occurrences of placeholders ($1, $2, etc.) in the query
+	re := regexp.MustCompile(`\$\d+`)
+	matches := re.FindAllString(query, -1)
+
+	for i, match := range matches {
+		var arg string
+
+		switch v := args[i].(type) {
+		case string:
+			arg = fmt.Sprintf("'%s'", v)
+		case []byte:
+			arg = fmt.Sprintf("'%s'", string(v))
+		default:
+			arg = fmt.Sprintf("%v", v)
+		}
+
+		// replace the placeholder with the argument value
+		query = strings.Replace(query, match, arg, 1)
+	}
+
+	// return the final query with argument values
+	return query
+}
diff --git a/boil/debug_test.go b/boil/debug_test.go
new file mode 100644
index 000000000..a91d92956
--- /dev/null
+++ b/boil/debug_test.go
@@ -0,0 +1,57 @@
+package boil
+
+import (
+	"testing"
+)
+
+func TestSubstituteQueryArgs(t *testing.T) {
+	tests := []struct {
+		query string
+		args  []interface{}
+		want  string
+	}{
+		{
+			query: `INSERT INTO "my_table" ("id","name","age","height","is_active","data") VALUES ($1,$2,$3,$4,$5,$6)`,
+			args: []interface{}{
+				1, "Alice", 25, 1.68, true, []byte(`{"key":"value"}`),
+			},
+			want: `INSERT INTO "my_table" ("id","name","age","height","is_active","data") VALUES (1,'Alice',25,1.68,true,'{"key":"value"}')`,
+		},
+		{
+			query: `UPDATE "my_table" SET "name"=$1,"age"=$2,"height"=$3,"is_active"=$4,"data"=$5 WHERE "id"=$6`,
+			args: []interface{}{
+				"Bob", 30, 1.78, false, []byte(`{"key":123}`), 2,
+			},
+			want: `UPDATE "my_table" SET "name"='Bob',"age"=30,"height"=1.78,"is_active"=false,"data"='{"key":123}' WHERE "id"=2`,
+		},
+		{
+			query: `DELETE FROM "my_table" WHERE "id"=$1`,
+			args: []interface{}{
+				4,
+			},
+			want: `DELETE FROM "my_table" WHERE "id"=4`,
+		},
+		{
+			query: `SELECT * FROM "my_table"`,
+			args:  []interface{}{},
+			want:  `SELECT * FROM "my_table"`,
+		},
+		{
+			query: `UPDATE "my_table" SET "name"=$1,"age"=$2,"height"=$3,"is_active"=$4,"data"=$5 WHERE "id"=$6`,
+			args: []interface{}{
+				"Bob", 30, 1.78, false, []byte(`{"key":123}`), 2,
+			},
+			want: `UPDATE "my_table" SET "name"='Bob',"age"=30,"height"=1.78,"is_active"=false,"data"='{"key":123}' WHERE "id"=2`,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.query, func(t *testing.T) {
+			got := substituteQueryArgs(tt.query, tt.args...)
+
+			if got != tt.want {
+				t.Errorf("substituteQueryArgs() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
diff --git a/drivers/sqlboiler-mssql/driver/override/main/17_upsert.go.tpl b/drivers/sqlboiler-mssql/driver/override/main/17_upsert.go.tpl
index 5665d3495..d663aaa7d 100644
--- a/drivers/sqlboiler-mssql/driver/override/main/17_upsert.go.tpl
+++ b/drivers/sqlboiler-mssql/driver/override/main/17_upsert.go.tpl
@@ -130,14 +130,12 @@ func (o *{{$alias.UpSingular}}) Upsert({{if .NoContext}}exec boil.Executor{{else
 
 	{{if .NoContext -}}
 	if boil.DebugMode {
-		fmt.Fprintln(boil.DebugWriter, cache.query)
-		fmt.Fprintln(boil.DebugWriter, vals)
+		boil.PrintQuery(boil.DebugWriter, cache.query, vals...)
 	}
 	{{else -}}
 	if boil.IsDebug(ctx) {
 		writer := boil.DebugWriterFrom(ctx)
-		fmt.Fprintln(writer, cache.query)
-		fmt.Fprintln(writer, vals)
+		boil.PrintQuery(writer, cache.query, vals...)
 	}
 	{{end -}}
 
diff --git a/drivers/sqlboiler-mysql/driver/override/main/17_upsert.go.tpl b/drivers/sqlboiler-mysql/driver/override/main/17_upsert.go.tpl
index a1e4e95a4..83a5891e2 100644
--- a/drivers/sqlboiler-mysql/driver/override/main/17_upsert.go.tpl
+++ b/drivers/sqlboiler-mysql/driver/override/main/17_upsert.go.tpl
@@ -138,14 +138,12 @@ func (o *{{$alias.UpSingular}}) Upsert({{if .NoContext}}exec boil.Executor{{else
 
 	{{if .NoContext -}}
 	if boil.DebugMode {
-		fmt.Fprintln(boil.DebugWriter, cache.query)
-		fmt.Fprintln(boil.DebugWriter, vals)
+		boil.PrintQuery(boil.DebugWriter, cache.query, vals...)
 	}
 	{{else -}}
 	if boil.IsDebug(ctx) {
 		writer := boil.DebugWriterFrom(ctx)
-		fmt.Fprintln(writer, cache.query)
-		fmt.Fprintln(writer, vals)
+		boil.PrintQuery(writer, cache.query, vals...)
 	}
 	{{end -}}
 
@@ -200,14 +198,12 @@ func (o *{{$alias.UpSingular}}) Upsert({{if .NoContext}}exec boil.Executor{{else
 
 	{{if .NoContext -}}
 	if boil.DebugMode {
-		fmt.Fprintln(boil.DebugWriter, cache.retQuery)
-		fmt.Fprintln(boil.DebugWriter, nzUniqueCols...)
+		boil.PrintQuery(boil.DebugWriter, cache.retQuery, nzUniqueCols...)
 	}
 	{{else -}}
 	if boil.IsDebug(ctx) {
 		writer := boil.DebugWriterFrom(ctx)
-		fmt.Fprintln(writer, cache.retQuery)
-		fmt.Fprintln(writer, nzUniqueCols...)
+		boil.PrintQuery(writer, cache.retQuery, nzUniqueCols...)
 	}
 	{{end -}}
 
diff --git a/drivers/sqlboiler-psql/driver/override/main/17_upsert.go.tpl b/drivers/sqlboiler-psql/driver/override/main/17_upsert.go.tpl
index 41c2485f5..98f9dac53 100644
--- a/drivers/sqlboiler-psql/driver/override/main/17_upsert.go.tpl
+++ b/drivers/sqlboiler-psql/driver/override/main/17_upsert.go.tpl
@@ -130,14 +130,12 @@ func (o *{{$alias.UpSingular}}) Upsert({{if .NoContext}}exec boil.Executor{{else
 
 	{{if .NoContext -}}
 	if boil.DebugMode {
-		fmt.Fprintln(boil.DebugWriter, cache.query)
-		fmt.Fprintln(boil.DebugWriter, vals)
+		boil.PrintQuery(boil.DebugWriter, cache.query, vals...)
 	}
 	{{else -}}
 	if boil.IsDebug(ctx) {
 		writer := boil.DebugWriterFrom(ctx)
-		fmt.Fprintln(writer, cache.query)
-		fmt.Fprintln(writer, vals)
+		boil.PrintQuery(writer, cache.query, vals...)
 	}
 	{{end -}}
 
diff --git a/drivers/sqlboiler-sqlite3/driver/override/main/17_upsert.go.tpl b/drivers/sqlboiler-sqlite3/driver/override/main/17_upsert.go.tpl
index 648a061f5..2e00753ce 100644
--- a/drivers/sqlboiler-sqlite3/driver/override/main/17_upsert.go.tpl
+++ b/drivers/sqlboiler-sqlite3/driver/override/main/17_upsert.go.tpl
@@ -125,14 +125,12 @@ func (o *{{$alias.UpSingular}}) Upsert({{if .NoContext}}exec boil.Executor{{else
 
 	{{if .NoContext -}}
 	if boil.DebugMode {
-		fmt.Fprintln(boil.DebugWriter, cache.query)
-		fmt.Fprintln(boil.DebugWriter, vals)
+		boil.PrintQuery(boil.DebugWriter, cache.query, vals...)
 	}
 	{{else -}}
 	if boil.IsDebug(ctx) {
 		writer := boil.DebugWriterFrom(ctx)
-		fmt.Fprintln(writer, cache.query)
-		fmt.Fprintln(writer, vals)
+		boil.PrintQuery(writer, cache.query, vals...)
 	}
 	{{end -}}
 
diff --git a/queries/query.go b/queries/query.go
index ea8052ec8..26ecc0e1d 100644
--- a/queries/query.go
+++ b/queries/query.go
@@ -3,7 +3,6 @@ package queries
 import (
 	"context"
 	"database/sql"
-	"fmt"
 	"regexp"
 
 	"github.com/volatiletech/sqlboiler/v4/boil"
@@ -118,8 +117,7 @@ func RawG(query string, args ...interface{}) *Query {
 func (q *Query) Exec(exec boil.Executor) (sql.Result, error) {
 	qs, args := BuildQuery(q)
 	if boil.DebugMode {
-		fmt.Fprintln(boil.DebugWriter, qs)
-		fmt.Fprintln(boil.DebugWriter, args)
+		boil.PrintQuery(boil.DebugWriter, qs, args...)
 	}
 	return exec.Exec(qs, args...)
 }
@@ -128,8 +126,7 @@ func (q *Query) Exec(exec boil.Executor) (sql.Result, error) {
 func (q *Query) QueryRow(exec boil.Executor) *sql.Row {
 	qs, args := BuildQuery(q)
 	if boil.DebugMode {
-		fmt.Fprintln(boil.DebugWriter, qs)
-		fmt.Fprintln(boil.DebugWriter, args)
+		boil.PrintQuery(boil.DebugWriter, qs, args...)
 	}
 	return exec.QueryRow(qs, args...)
 }
@@ -138,8 +135,7 @@ func (q *Query) QueryRow(exec boil.Executor) *sql.Row {
 func (q *Query) Query(exec boil.Executor) (*sql.Rows, error) {
 	qs, args := BuildQuery(q)
 	if boil.DebugMode {
-		fmt.Fprintln(boil.DebugWriter, qs)
-		fmt.Fprintln(boil.DebugWriter, args)
+		boil.PrintQuery(boil.DebugWriter, qs, args...)
 	}
 	return exec.Query(qs, args...)
 }
@@ -149,8 +145,7 @@ func (q *Query) ExecContext(ctx context.Context, exec boil.ContextExecutor) (sql
 	qs, args := BuildQuery(q)
 	if boil.IsDebug(ctx) {
 		writer := boil.DebugWriterFrom(ctx)
-		fmt.Fprintln(writer, qs)
-		fmt.Fprintln(writer, args)
+		boil.PrintQuery(writer, qs, args...)
 	}
 	return exec.ExecContext(ctx, qs, args...)
 }
@@ -160,8 +155,7 @@ func (q *Query) QueryRowContext(ctx context.Context, exec boil.ContextExecutor)
 	qs, args := BuildQuery(q)
 	if boil.IsDebug(ctx) {
 		writer := boil.DebugWriterFrom(ctx)
-		fmt.Fprintln(writer, qs)
-		fmt.Fprintln(writer, args)
+		boil.PrintQuery(writer, qs, args...)
 	}
 	return exec.QueryRowContext(ctx, qs, args...)
 }
@@ -171,8 +165,7 @@ func (q *Query) QueryContext(ctx context.Context, exec boil.ContextExecutor) (*s
 	qs, args := BuildQuery(q)
 	if boil.IsDebug(ctx) {
 		writer := boil.DebugWriterFrom(ctx)
-		fmt.Fprintln(writer, qs)
-		fmt.Fprintln(writer, args)
+		boil.PrintQuery(writer, qs, args...)
 	}
 	return exec.QueryContext(ctx, qs, args...)
 }

From fd6a4c86ee491ee5750749a209f1fb20a6002db5 Mon Sep 17 00:00:00 2001
From: Cyn <39891200+cynsupercat@users.noreply.github.com>
Date: Mon, 10 Apr 2023 13:37:18 +1000
Subject: [PATCH 2/3] Updated func comment (remove extraneous //)

---
 boil/debug.go | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/boil/debug.go b/boil/debug.go
index 5be6c90d8..d4b337e83 100644
--- a/boil/debug.go
+++ b/boil/debug.go
@@ -58,9 +58,9 @@ func PrintQuery(writer io.Writer, query string, args ...interface{}) {
 	fmt.Fprintln(writer, substitutedQuery)
 }
 
-// substituteQueryArgs takes a SQL query string and an array of
-// // arguments. It returns a modified query string with placeholders replaced by their
-// // corresponding argument values.
+// substituteQueryArgs takes a query string and an array of arguments.
+// It returns a modified query string with placeholders replaced by their
+// corresponding argument values.
 func substituteQueryArgs(query string, args ...interface{}) string {
 	// find all occurrences of placeholders ($1, $2, etc.) in the query
 	re := regexp.MustCompile(`\$\d+`)

From 8f3b938991c3f2501a97792a5e6e6bc2db86aac7 Mon Sep 17 00:00:00 2001
From: Cyn <39891200+cynsupercat@users.noreply.github.com>
Date: Mon, 10 Apr 2023 13:46:59 +1000
Subject: [PATCH 3/3] Updated test to remove redundant test cases

---
 boil/debug_test.go | 14 --------------
 1 file changed, 14 deletions(-)

diff --git a/boil/debug_test.go b/boil/debug_test.go
index a91d92956..1b0f16ac8 100644
--- a/boil/debug_test.go
+++ b/boil/debug_test.go
@@ -17,20 +17,6 @@ func TestSubstituteQueryArgs(t *testing.T) {
 			},
 			want: `INSERT INTO "my_table" ("id","name","age","height","is_active","data") VALUES (1,'Alice',25,1.68,true,'{"key":"value"}')`,
 		},
-		{
-			query: `UPDATE "my_table" SET "name"=$1,"age"=$2,"height"=$3,"is_active"=$4,"data"=$5 WHERE "id"=$6`,
-			args: []interface{}{
-				"Bob", 30, 1.78, false, []byte(`{"key":123}`), 2,
-			},
-			want: `UPDATE "my_table" SET "name"='Bob',"age"=30,"height"=1.78,"is_active"=false,"data"='{"key":123}' WHERE "id"=2`,
-		},
-		{
-			query: `DELETE FROM "my_table" WHERE "id"=$1`,
-			args: []interface{}{
-				4,
-			},
-			want: `DELETE FROM "my_table" WHERE "id"=4`,
-		},
 		{
 			query: `SELECT * FROM "my_table"`,
 			args:  []interface{}{},