src/go/plugin/ibm.d/pkg/odbcbridge/README.md
The odbcbridge package provides a reliable ODBC connection interface for Go applications, specifically designed to handle AS/400 and other enterprise database systems. It solves critical issues with existing Go ODBC drivers through a direct C bridge implementation.
The popular alexbrainman/odbc Go driver has a critical bug where it doesn't properly clean up prepared statement handles when queries fail. This causes subsequent queries to fail with SQL0519 errors ("Prepared statement in use"), effectively breaking the connection until reconnection.
This package implements a C-based ODBC bridge that:
Go Application
↓
connection.go (Go interface)
↓
CGO Bridge
↓
bridge.c (C implementation)
↓
ODBC Driver (unixODBC/iODBC)
↓
Database
import "github.com/netdata/netdata/go/plugins/plugin/ibm.d/pkg/odbcbridge"
// Connect to database
conn, err := odbcbridge.ConnectOptimized(dsn)
if err != nil {
return err
}
defer conn.Close()
// Execute query
rows, err := conn.QueryContext(ctx, "SELECT * FROM QSYS2.SYSTEM_STATUS_INFO")
if err != nil {
return err
}
defer rows.Close()
// Process results
columns := rows.Columns()
values := make([]driver.Value, len(columns))
for rows.Next(values) == nil {
// Process row
for i, v := range values {
fmt.Printf("%s: %v\n", columns[i], v)
}
}
// Prepare statement
stmt, err := conn.PrepareContext(ctx, "SELECT * FROM TABLE WHERE ID = ?")
if err != nil {
return err
}
defer stmt.Close()
// Execute multiple times
for _, id := range ids {
rows, err := stmt.Execute()
if err != nil {
continue // Safe - no SQL0519!
}
// Process rows...
rows.Close()
}
var (
name string
count int64
ratio float64
)
err := rows.ScanTyped(&name, &count, &ratio)
if err != nil {
return err
}
The bridge automatically detects SQL types and converts them appropriately:
SQL_INTEGER, SQL_BIGINT → int64SQL_FLOAT, SQL_DOUBLE, SQL_DECIMAL → float64SQL_CHAR, SQL_VARCHAR → stringSQL_BINARY, SQL_VARBINARY → []byteint64 (not unsigned) to handle AS/400's negative row countsThe bridge provides detailed error information:
rows, err := conn.QueryContext(ctx, query)
if err != nil {
// Error includes SQLSTATE and native error codes
// Example: "SQLExecDirect: 42S02:1:-204:[IBM][System i Access ODBC Driver]
// [DB2 for i5/OS]SQL0204 - INVALID_TABLE in QSYS2 type *FILE not found."
log.Printf("Query failed: %v", err)
}
Requires:
unixodbc-dev on Ubuntu/Debian)# Install dependencies (Ubuntu/Debian)
sudo apt-get install unixodbc-dev
# Build
go build -tags cgo
// Run tests
go test -v ./...
// Test with specific DSN
DSN="Driver={IBM i Access ODBC Driver};System=pub400.com;..." go test -v
| Feature | alexbrainman/odbc | odbcbridge |
|---|---|---|
| SQL0519 Prevention | ❌ Bug causes SQL0519 | ✅ Proper cleanup |
| Statement Reuse | ❌ Creates new each time | ✅ Optimized reuse |
| AS/400 Row Counts | ❌ uint64 (wrong for negative) | ✅ int64 (correct) |
| Error Recovery | ❌ Connection unusable | ✅ Auto-recovery |
| Memory Leaks | ❌ Known issues | ✅ Proper cleanup |
| Context Support | ✅ Yes | ✅ Yes |
| Type Safety | ⚠️ Limited | ✅ Full type info |
Same as Netdata (GPL-3.0-or-later)