password: '1234', database: 'ruby') res = client.query("SHOW TABLES FROM DB") require 'pg' conn = PGconn.connect("localhost", 5432, '', '', "ruby", "root", "1234") res = conn.exec('select tablename, tableowner from pg_tables') ActiveRecord gem with common interface but only support MySQL, PostgreSQL, SQLite. Refs: https://guides.rubyonrails.org/active_rec ord_querying.html
CockroachDB etc.) and NoSQL (DynamoDB etc.) and Analytics DB (Athena, Snowflake etc.) • User only needs import database/sql with simple syntax • Support database edge cases ◦ e.g. Scan postgreSQL Array, JSONB types Refs: https://go.dev/wiki/SQLDrivers
PostgreSQL Driver Athena Driver use implement • define a DB <<interface>> provides all user-capabilities • database driver: a different package that implement all methods of common db <<interface>> • Application Backend: Use database driver directly Backend Engineer (User) Driver Maintainer
MySQL Driver PostgreSQL Driver Athena Driver use implement Backend Engineer (User) Driver Maintainer • Common DB <<interface>> serves both database users and driver maintainers. ◦ But, different concerns between High-level and Low-Level • Common DB <<interface>> also introduces a coupling between users and drivers. The simplest Go-like approach
effort to implement on each driver • Each driver has their own version of common stuffs ◦ no standardization ◦ introduce inconsistency Mysql database client implement Connection pooling Concurrency handling Go typecasting PostgreSQL Driver PostgreSQL database client implement Connection pooling Concurrency handling Go typecasting The simplest Go-like approach
Total time waited for new connections. waitDuration atomic.Int64 freeConn []*driverConn // free connections ordered by returnedAt oldest to newest } // driverConn wraps a driver.Conn with a mutex, to // be held during all calls into the Conn. (including any calls onto // interfaces returned via that Conn, such as calls on Tx, Stmt, // Result, Rows) type driverConn struct { db *DB createdAt time.Time sync.Mutex // guards following ci driver.Conn } Wrapping !! database/sql package design approach
driver.Conn etc..) MySQL Driver PostgreSQL Driver Athena Driver use implement database/sql (*sql.DB, *sql.Tx, *sql.Stmt etc.) use Connection pooling Concurrency handling Go typecasting Retry The database/sql package becomes a central hub for implementing common database interactions Individual database drivers can then focus on the specifics of their database type, including native functionality and handling their edge cases. database/sql package design approach
driver.Conn etc..) MySQL Driver PostgreSQL Driver Athena Driver use implement database/sql (*sql.DB, *sql.Tx, *sql.Stmt etc.) use Connection pooling Concurrency handling Go typecasting Retry A layered structure: • database/sql/driver provides the foundation • database/sql builds upon it with common interaction logic. database/sql package design approach
id int var username string _ = rows.Scan(&id, &username) var email sql.NullString _ = rows.Scan(&id, &username, &email) var activities []string _ = rows.Scan(&id, &username, &email, pq.Array(&activities)) package sql // Scanner is an interface used by [Rows.Scan]. type Scanner interface { Scan(src any) error } • database/sql package define a sql.Scanner interface, that is designed to be optional. • database drivers can handle their own type conversions internally, if that type implementing sql.Scanner interface • E.g PostgreSQL Array or JsonB optional interface sql.Scanner
etc..) MySQL Driver PostgreSQL Driver Athena Driver use implement database/sql (*sql.DB, *sql.Tx, *sql.Stmt etc.) use The database/sql/driver interface is designed for internal use within the database/sql package. This separation keeps user-facing code clean and avoids unnecessary dependencies on the driver implementation details. database/sql package design approach <<interface>> struct{}
etc..) MySQL Driver PostgreSQL Driver Athena Driver use implement database/sql (*sql.DB, *sql.Tx, *sql.Stmt etc.) use The database/sql/driver interface can focus on a limited set of methods essential for database/sql functionality. This promotes stability, as changes to these methods are less frequent compared to user-facing APIs. database/sql package design approach <<interface>> struct{}
etc..) MySQL Driver PostgreSQL Driver Athena Driver use implement database/sql (*sql.DB, *sql.Tx, *sql.Stmt etc.) use To promote further reusability and simplicity, the database/sql/driver interface divided into smaller, more specific interfaces. driver.Driver, driver.Queryer, driver.Conn etc.driver.Driver, driver.Queryer, driver.Conn database/sql package design approach <<interface>> struct{}
but deep • Clean separation of concerns • Focus on stability for core functionalities • Balance between providing a generic interface for common database interactions and database-specific features Design thinking