Documentation
¶
Overview ¶
Package tableaccess is the PostgreSQL database.Manager: the administrative surface that creates roles and databases and grants table privileges, as distinct from the query path a database.Client serves.
It is provisioning code. The calls here are the ones a service makes when it stands up a tenant's database or hands a new role the privileges it needs, not ones a request handler makes.
Why there is one of these per dialect ¶
None of this is portable SQL. CREATE USER, GRANT, and the catalog queries that answer "does this role exist" differ per engine, and — the part that decides the shape of the code — identifiers and role names cannot travel as bind parameters at all, because these are utility statements. Every dialect therefore has to solve credential handling its own way, and its own way is what each of these packages is.
Here that means the credential never appears in statement text. CREATE USER runs inside a transaction that first stashes the name and password in transaction-local settings as bind parameters, then executes a constant DO block which reads them back and lets the server apply its own identifier and literal quoting. The point is otelsql: it copies statement text onto a span attribute when query logging is on, and a directly interpolated password would ride out to whatever consumes those spans. set_config's local flag scopes the settings to the transaction, so nothing survives on the pooled connection for the next caller to read.
Where a name is interpolated rather than bound — DROP USER, CREATE DATABASE, GRANT — it goes through the dialect's identifier quoting, and the privilege name is checked against this package's own constants rather than passed through.
A name already taken comes back wrapping database.ErrUserAlreadyExists, recognized by the SQLSTATE Postgres raises for a duplicate object, so errors/http and errors/grpc render it as a conflict rather than a 500. The driver's error stays underneath it.
Nothing on a span or in a log here carries a password.
Index ¶
- type Manager
- func (p *Manager) CreateDatabase(ctx context.Context, dbName, owner string) error
- func (p *Manager) CreateUser(ctx context.Context, username, password string) (err error)
- func (p *Manager) DatabaseExists(ctx context.Context, dbName string) (bool, error)
- func (p *Manager) DeleteDatabase(ctx context.Context, dbName string) error
- func (p *Manager) DeleteUser(ctx context.Context, username string) error
- func (p *Manager) GrantUserAccessToTable(ctx context.Context, username, schema, table, privilege string) error
- func (p *Manager) UserCanAccessDatabase(ctx context.Context, username, dbName string) (bool, error)
- func (p *Manager) UserExists(ctx context.Context, username string) (bool, error)
- type Option
- type Privilege
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
Manager is the PostgreSQL database.Manager implementation. It is exported, and returned by NewManager, so a caller who has chosen PostgreSQL can depend on that choice rather than on the interface every dialect's manager shares.
func (*Manager) CreateDatabase ¶
func (*Manager) CreateUser ¶
CreateUser creates a role with the given password.
The password never appears in statement text. CREATE USER is a utility statement and accepts no bind parameters, so the direct spelling has to interpolate the credential into the SQL — and otelsql copies statement text onto the db.statement span attribute whenever LOG_QUERIES is on, which puts a live credential on a span that may well be exported to a third party. Binding the arguments into settings and letting the server do the quoting means every statement that goes over the wire is a constant.
The transaction is what scopes the settings: set_config's local flag ties them to it, so they are gone whether it commits or rolls back, and no later caller on a pooled connection can read them. CREATE ROLE is transactional in Postgres, so the role and the settings share one unit of work.
A username already in use comes back wrapping database.ErrUserAlreadyExists, which errors/http and errors/grpc map to a conflict rather than a 500. The driver's own error is preserved underneath it: the SQLSTATE is what identified the failure, and a caller that wants the detail should not have to re-run the statement to get it.
func (*Manager) DatabaseExists ¶
func (*Manager) DeleteDatabase ¶
func (*Manager) DeleteUser ¶
func (*Manager) GrantUserAccessToTable ¶
func (p *Manager) GrantUserAccessToTable(ctx context.Context, username, schema, table, privilege string) error
GrantUserAccessToTable grants a specific privilege on a table to a user.
func (*Manager) UserCanAccessDatabase ¶
type Option ¶
type Option func(*options)
Option configures the Manager this package constructs. The zero configuration works: an absent logger logs nowhere and an absent tracer provider traces nowhere.
There is no metrics provider. Everything here is an administrative act — a role created, a database dropped, a grant issued — performed a handful of times over a deployment's life by something that already reports what it did. A counter over those is a number nobody has a question for; the span and the log line, which say which role and which database, are what somebody auditing the change actually wants.
func WithPillars ¶
func WithPillars(pillars *observability.Pillars) Option
WithPillars supplies logger and tracer provider at once. Options apply in order, so a WithPillars followed by a narrower option wins for that component. The pillars' metrics provider is ignored — see Option.
func WithTracerProvider ¶
WithTracerProvider attaches a tracer provider, enabling spans on every operation.
type Privilege ¶
type Privilege string
const ( PrivilegeSelect Privilege = "SELECT" PrivilegeInsert Privilege = "INSERT" PrivilegeUpdate Privilege = "UPDATE" PrivilegeDelete Privilege = "DELETE" PrivilegeTruncate Privilege = "TRUNCATE" PrivilegeReferences Privilege = "REFERENCES" PrivilegeTrigger Privilege = "TRIGGER" PrivilegeConnect Privilege = "CONNECT" // for database-level ops )