package main

import (
	"database/sql"
	"fmt"
	"strings"
)

// placeholderPG membangun daftar placeholder gaya PostgreSQL: $1, $2, ...
func placeholderPG(n int) string {
	bagian := make([]string, n)
	for i := range n {
		bagian[i] = fmt.Sprintf("$%d", i+1)
	}
	return strings.Join(bagian, ", ")
}

// konversiTandaTanya mengubah query bergaya MySQL (?) menjadi
// placeholder bernomor PostgreSQL ($1, $2, ...).
func konversiTandaTanya(query string) string {
	nomor := 0
	var b strings.Builder
	for _, r := range query {
		if r == '?' {
			nomor++
			fmt.Fprintf(&b, "$%d", nomor)
		} else {
			b.WriteRune(r)
		}
	}
	return b.String()
}

// klausaIN membangun klausa IN dengan satu placeholder per elemen.
func klausaIN(kolom string, nilai []int) string {
	return fmt.Sprintf("WHERE %s IN (%s)", kolom, placeholderPG(len(nilai)))
}

func main() {
	fmt.Println(placeholderPG(4))
	fmt.Println(konversiTandaTanya("SELECT nama FROM pengguna WHERE email = ? AND aktif = ?"))
	fmt.Println(klausaIN("id", []int{4, 8, 15, 16, 23, 42}))

	// Input jahat klasik: penggabungan string mengubah struktur query,
	// sedangkan parameter binding menjaga struktur tetap utuh.
	input := "' OR '1'='1"
	rentan := "SELECT id FROM pengguna WHERE email = '" + input + "'"
	fmt.Println("query rentan :", rentan)
	fmt.Println("query aman   : SELECT id FROM pengguna WHERE email = $1")

	// sql.NullString menyimpan nilai kolom yang mungkin NULL.
	var kosong sql.NullString
	terisi := sql.NullString{String: "Budi", Valid: true}
	fmt.Printf("kosong: valid=%t nilai=%q\n", kosong.Valid, kosong.String)
	fmt.Printf("terisi: valid=%t nilai=%q\n", terisi.Valid, terisi.String)
}
