internal/postgres: refactor getPackageSymbols

getPackageSymbols is refactored using squirrel, so that it can be
modified for us in other similar queries.

For golang/go#44142

Change-Id: I974c51155d30c59cf61ded0386c5d3b2702188c5
Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/318651
Trust: Julie Qiu <julie@golang.org>
Run-TryBot: Julie Qiu <julie@golang.org>
TryBot-Result: kokoro <noreply+kokoro@google.com>
Reviewed-by: Jonathan Amsterdam <jba@google.com>
diff --git a/internal/postgres/package_symbol.go b/internal/postgres/package_symbol.go
index fcebeb3..1928258 100644
--- a/internal/postgres/package_symbol.go
+++ b/internal/postgres/package_symbol.go
@@ -9,6 +9,7 @@
 	"database/sql"
 	"fmt"
 
+	"github.com/Masterminds/squirrel"
 	"golang.org/x/pkgsite/internal"
 	"golang.org/x/pkgsite/internal/database"
 	"golang.org/x/pkgsite/internal/derrors"
@@ -20,33 +21,12 @@
 ) (_ *internal.SymbolHistory, err error) {
 	defer derrors.Wrap(&err, "getPackageSymbols(ctx, ddb, %q, %q)", packagePath, modulePath)
 	defer middleware.ElapsedStat(ctx, "getPackageSymbols")()
-	query := `
-		SELECT
-			s1.name AS symbol_name,
-			s2.name AS parent_symbol_name,
-			ps.section,
-			ps.type,
-			ps.synopsis,
-			m.version,
-			d.goos,
-			d.goarch
-		FROM modules m
-		INNER JOIN units u ON u.module_id = m.id
-		INNER JOIN documentation d ON d.unit_id = u.id
-		INNER JOIN documentation_symbols ds ON ds.documentation_id = d.id
-		INNER JOIN package_symbols ps ON ps.id = ds.package_symbol_id
-		INNER JOIN paths p1 ON u.path_id = p1.id
-		INNER JOIN symbol_names s1 ON ps.symbol_name_id = s1.id
-		INNER JOIN symbol_names s2 ON ps.parent_symbol_name_id = s2.id
-		WHERE
-			p1.path = $1
-			AND m.module_path = $2
-			AND NOT m.incompatible
-			AND m.version_type = 'release'
-		ORDER BY
-			CASE WHEN ps.type='Type' THEN 0 ELSE 1 END,
-			symbol_name;`
 
+	query := packageSymbolQuery(packagePath, modulePath)
+	q, args, err := query.PlaceholderFormat(squirrel.Dollar).ToSql()
+	if err != nil {
+		return nil, err
+	}
 	sh, collect := collectSymbolHistory(func(sh *internal.SymbolHistory, sm internal.SymbolMeta, v string, build internal.BuildContext) error {
 		if sm.Section == internal.SymbolSectionTypes && sm.Kind != internal.SymbolKindType {
 			_, err := sh.GetSymbol(sm.ParentName, v, build)
@@ -57,12 +37,38 @@
 		}
 		return nil
 	})
-	if err := ddb.RunQuery(ctx, query, collect, packagePath, modulePath); err != nil {
+	if err := ddb.RunQuery(ctx, q, collect, args...); err != nil {
 		return nil, err
 	}
 	return sh, nil
 }
 
+func packageSymbolQuery(pkgPath, modulePath string) squirrel.SelectBuilder {
+	return squirrel.Select(
+		"s1.name AS symbol_name",
+		"s2.name AS parent_symbol_name",
+		"ps.section",
+		"ps.type",
+		"ps.synopsis",
+		"m.version",
+		"d.goos",
+		"d.goarch",
+	).From("modules m").
+		Join("units u on u.module_id = m.id").
+		Join("documentation d ON d.unit_id = u.id").
+		Join("documentation_symbols ds ON ds.documentation_id = d.id").
+		Join("package_symbols ps ON ps.id = ds.package_symbol_id").
+		Join("paths p1 ON u.path_id = p1.id").
+		Join("symbol_names s1 ON ps.symbol_name_id = s1.id").
+		Join("symbol_names s2 ON ps.parent_symbol_name_id = s2.id").
+		Where(squirrel.Eq{"p1.path": pkgPath}).
+		Where(squirrel.Eq{"m.module_path": modulePath}).
+		Where("NOT m.incompatible").
+		Where(squirrel.Eq{"m.version_type": "release"}).
+		OrderBy("CASE WHEN ps.type='Type' THEN 0 ELSE 1 END").
+		OrderBy("symbol_name")
+}
+
 func collectSymbolHistory(check func(sh *internal.SymbolHistory, sm internal.SymbolMeta, v string, build internal.BuildContext) error) (*internal.SymbolHistory, func(rows *sql.Rows) error) {
 	sh := internal.NewSymbolHistory()
 	return sh, func(rows *sql.Rows) (err error) {