Setiap program yang bekerja dengan database dimulai dari langkah yang sama: membuka handle koneksi. Di Go, langkah tersebut memakai sql.Open dengan dua argumen — nama driver dan Data Source Name (DSN) berisi alamat serta kredensial. Di balik kesederhanaannya tersimpan dua hal yang sering mengejutkan pemula: sql.Open sebenarnya tidak langsung tersambung ke server, dan kesalahan penulisan DSN bisa lolos tanpa terdeteksi bila kita tidak memverifikasinya. Chapter ini membahas format DSN PostgreSQL, perilaku malas sql.Open, verifikasi dengan Ping, serta pesan error nyata yang muncul ketika driver lupa diimpor.
Format Data Source Name (DSN) #
DSN adalah string ringkas yang menjelaskan ke mana dan bagaimana program harus tersambung. Driver pgx menerima dua format: bentuk URL dan bentuk pasangan kunci=nilai:
postgres://budi:[email protected]:5432/toko?sslmode=disablehost=db.example.com port=5432 user=budi password=YOUR_DATABASE_PASSWORD dbname=toko sslmode=disableKomponen penyusunnya sama di kedua format:
| Parameter | Contoh nilai | Arti |
|---|---|---|
host | db.example.com | Alamat server PostgreSQL |
port | 5432 | Port server; 5432 adalah bawaan PostgreSQL |
user | budi | Nama pengguna database |
password | YOUR_DATABASE_PASSWORD | Kredensial pengguna |
dbname | toko | Nama database yang dipakai |
sslmode | disable, require | Mode enkripsi koneksi |
sql.Open Bersifat Malas #
Pemanggilan sql.Open("pgx", dsn) hanya melakukan dua hal: memeriksa bahwa nama driver "pgx" sudah terdaftar, lalu menyimpan DSN di dalam objek *sql.DB yang dikembalikan. Tidak ada satu paket jaringan pun yang dikirim ke server pada saat itu. Koneksi fisik baru dibangun ketika operasi pertama berjalan — Ping, Query, atau Exec.
Konsekuensinya serius: nama host yang salah, password yang kedaluwarsa, atau database yang belum dibuat semuanya lolos dari sql.Open tanpa error. Masalah baru muncul saat query pertama tiba — momen terburuk untuk mengetahui aplikasi tidak bisa tersambung. Karena itu, program produksi selalu memverifikasi koneksi segera setelah sql.Open. Kode berikut membutuhkan PostgreSQL aktif untuk dijalankan; versi lengkapnya dipraktikkan di proyek REST API dengan PostgreSQL
:
package main
import (
"context"
"database/sql"
"fmt"
"log"
"time"
_ "github.com/jackc/pgx/v5/stdlib"
)
func main() {
dsn := "postgres://budi:[email protected]:5432/toko?sslmode=disable"
db, err := sql.Open("pgx", dsn)
if err != nil {
log.Fatal(err)
}
defer db.Close()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := db.PingContext(ctx); err != nil {
log.Fatal("database tidak terjangkau: ", err)
}
fmt.Println("tersambung ke PostgreSQL")
}Perhatikan dua pemeriksaan error dengan peran berbeda: error dari sql.Open hanya muncul untuk masalah administratif seperti nama driver salah, sedangkan PingContext-lah yang benar-benar menyentuh server dan membuktikan DSN kita valid. context.WithTimeout membatasi waktu ping lima detik supaya startup tidak menggantung selamanya ketika server tidak merespons.
Demo: Mengurai DSN dan Mengamati Error Driver #
Berkas contoh.go pada bagian bawah halaman mengurai DSN bergaya URL menjadi komponen-komponennya menggunakan net/url, lalu sengaja memanggil sql.Open tanpa blank import driver — sehingga pesan error asli Go 1.27 tentang driver tak terdaftar bisa kita amati:
package main
import (
"database/sql"
"fmt"
"net/url"
"strings"
)
// parseDSN mengurai DSN bergaya URL PostgreSQL menjadi komponennya.
func parseDSN(dsn string) (map[string]string, error) {
u, err := url.Parse(dsn)
if err != nil {
return nil, err
}
hasil := map[string]string{
"host": u.Hostname(),
"port": u.Port(),
"dbname": strings.TrimPrefix(u.Path, "/"),
}
if u.User != nil {
hasil["user"] = u.User.Username()
if sandi, ok := u.User.Password(); ok {
hasil["password"] = sandi
}
}
hasil["sslmode"] = u.Query().Get("sslmode")
return hasil, nil
}
func main() {
dsn := "postgres://budi:[email protected]:5432/toko?sslmode=disable"
bagian, err := parseDSN(dsn)
if err != nil {
fmt.Println("gagal mengurai DSN:", err)
return
}
for _, k := range []string{"host", "port", "user", "password", "dbname", "sslmode"} {
fmt.Printf("%s=%s\n", k, bagian[k])
}
// Nama driver "pgx" sengaja tidak didaftarkan (program ini tidak
// memakai blank import driver) agar pesan error aslinya terlihat.
db, err := sql.Open("pgx", dsn)
fmt.Println("err dari sql.Open:", err)
fmt.Println("db bernilai nil?:", db == nil)
}Output yang Dihasilkan:
host=db.example.com
port=5432
user=budi
password=YOUR_DATABASE_PASSWORD
dbname=toko
sslmode=disable
err dari sql.Open: sql: unknown driver "pgx" (forgotten import?)
db bernilai nil?: trueMari kita telaah baris demi baris program di atas:
- Baris 12 (
url.Parse): memecah DSN menjadi struktururl.URL— host, path, query, dan kredensial dipisah otomatis oleh standard library. - Baris 16-19 (
hasil := map[string]string{...}):u.Hostname()mengambil alamat tanpa port,u.Port()mengambil port, danstrings.TrimPrefixmembuang garis miring di depan nama database. - Baris 21-25 (
if u.User != nil): bagianuser:passwordpada URL diakses lewatu.User;Password()mengembalikan nilai keduaokkarena password boleh kosong. - Baris 27 (
u.Query().Get): parameter querysslmodedibaca seperti query string HTTP biasa. - Baris 39-41 (
for _, k := range ...): key map dicetak dalam urutan tetap lewat slice literal, bukan diiterasi langsung — iterasi map di Go memiliki urutan acak. - Baris 45-47 (
sql.Open): tanpa blank import pgx, nama"pgx"tidak terdaftar sehingga Go mengembalikan pesansql: unknown driver "pgx" (forgotten import?)— pengingat langsung dari standard library bahwa import driver terlupakan.
Program demo ini sekaligus menjelaskan mengapa error sql.Open jarang berhubungan dengan server: pemeriksaan terjadi di sisi aplikasi, pada tabel registrasi driver.
Menutup Koneksi dengan defer db.Close() #
Pemanggilan db.Close() menutup seluruh koneksi yang dikelola pool di dalam *sql.DB. Karena satu sql.DB dirancang hidup sepanjang umur aplikasi, Close pada umumnya hanya dipanggil saat aplikasi mematikan diri — biasanya lewat defer db.Close() di main atau di rute shutdown. Membuka dan menutup sql.DB di setiap fungsi justru merusak pool dan menghabiskan waktu handshake TCP.
Koneksi kini bisa dibuka dan diverifikasi. Tetapi di balik satu sql.DB, berapa koneksi yang benar-benar dibuat? Chapter berikutnya mengulas connection pool — MaxOpenConns, MaxIdleConns, dan umur koneksi — beserta cara menghitung angka yang tepat untuk aplikasi kita.
Referensi #
- Opening a database handle
— panduan resmi
sql.Opendan ping. - Dokumentasi package database/sql
— spesifikasi
Open,Ping, danClose. - pgx v5 stdlib
— driver yang mendaftarkan nama
"pgx". - pgx — PostgreSQL Driver and Toolkit — repositori resmi pgx beserta format DSN yang didukung.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
contoh.go
Unduh berkaspackage main
import (
"database/sql"
"fmt"
"net/url"
"strings"
)
// parseDSN mengurai DSN bergaya URL PostgreSQL menjadi komponennya.
func parseDSN(dsn string) (map[string]string, error) {
u, err := url.Parse(dsn)
if err != nil {
return nil, err
}
hasil := map[string]string{
"host": u.Hostname(),
"port": u.Port(),
"dbname": strings.TrimPrefix(u.Path, "/"),
}
if u.User != nil {
hasil["user"] = u.User.Username()
if sandi, ok := u.User.Password(); ok {
hasil["password"] = sandi
}
}
hasil["sslmode"] = u.Query().Get("sslmode")
return hasil, nil
}
func main() {
dsn := "postgres://budi:[email protected]:5432/toko?sslmode=disable"
bagian, err := parseDSN(dsn)
if err != nil {
fmt.Println("gagal mengurai DSN:", err)
return
}
for _, k := range []string{"host", "port", "user", "password", "dbname", "sslmode"} {
fmt.Printf("%s=%s\n", k, bagian[k])
}
// Nama driver "pgx" sengaja tidak didaftarkan (program ini tidak
// memakai blank import driver) agar pesan error aslinya terlihat.
db, err := sql.Open("pgx", dsn)
fmt.Println("err dari sql.Open:", err)
fmt.Println("db bernilai nil?:", db == nil)
}