pwdata

package
v0.5.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 18, 2026 License: Apache-2.0 Imports: 12 Imported by: 0

Documentation

Overview

Package pwdata serves the pw dev data pane from inside the application.

It lives in the application process because that is the only place the development database can be reached. An embedded SQLite connection is a process-local handle rather than an endpoint, and an in-memory one has no external existence at all, so a tool outside the process can address neither. Running here also means every read and write goes through the pool, the driver, and the diagnostics the project actually configured.

Nothing here reaches a release build. The framework starts it only under the pwdev build mode, and only when pw dev has told the process where its console is, so an application compiled by pw build links no call that starts it.

Index

Constants

This section is empty.

Variables

View Source
var ErrUnsupportedParams = errors.New(
	"this statement takes a parameter no form field can produce, so it cannot be run from here; " +
		"call it from a test or a handler instead")

ErrUnsupportedParams is what a declared statement reports when a form cannot produce one of its arguments.

The statement is still listed. A developer scanning for it should find it and read why it cannot be run here, rather than conclude that generation missed it or that the statement does not exist.

View Source
var SupportedArgKinds = map[string]string{
	"string":    "ArgString",
	"int":       "ArgInt",
	"int64":     "ArgInt64",
	"int32":     "ArgInt32",
	"float64":   "ArgFloat64",
	"float32":   "ArgFloat32",
	"bool":      "ArgBool",
	"time.Time": "ArgTime",
}

SupportedArgKinds are the parameter types a form can produce a value for. A declared statement taking anything else is registered without a form and says so, rather than being left out of the list entirely.

Functions

func ArgBool

func ArgBool(name, value string) (bool, error)

func ArgFloat32

func ArgFloat32(name, value string) (float32, error)

func ArgFloat64

func ArgFloat64(name, value string) (float64, error)

func ArgInt

func ArgInt(name, value string) (int, error)

func ArgInt32

func ArgInt32(name, value string) (int32, error)

func ArgInt64

func ArgInt64(name, value string) (int64, error)

func ArgString

func ArgString(_, value string) (string, error)

ArgString passes text through. It exists so that generated code calls a converter for every parameter rather than special-casing one.

func ArgTime

func ArgTime(name, value string) (time.Time, error)

ArgTime accepts the forms a browser date or datetime field produces, and RFC 3339 for a value pasted from somewhere else.

func RegisterQuery

func RegisterQuery(query Query)

RegisterQuery adds a declared statement. It is called from generated code at package initialisation, so it takes no error: a duplicate is a generation defect rather than something a running pane can report.

Types

type Column

type Column struct {
	Name    string `json:"name"`
	Type    string `json:"type"`
	NotNull bool   `json:"notNull"`
	// PrimaryKey is the one-based position in the primary key, or zero when the
	// column is not part of one. Position matters because an edit addresses a
	// row by its whole key, in order.
	PrimaryKey int `json:"primaryKey"`
}

Column is one column of one table, as the engine's own catalog describes it.

type Connection

type Connection struct {
	// Label identifies the connection, spelled group#ordinal by the runtime.
	Label string
	Group string
	// Driver is per connection rather than per project, so the dialect is
	// resolved here. Nothing forbids two groups on two engines.
	Driver string
	// ReadOnly marks a replica. The pane refuses to write through one, which is
	// not a policy it applies but a fact about the connection.
	ReadOnly bool
	// contains filtered or unexported fields
}

Connection is one configured pool the pane can address.

A project may declare several, and requirement:read-write-splitting means a group can hold replicas. The pane addresses one at a time rather than the group, because selection inside a group is round robin and a page that could not say which replica answered would be unable to show the one question replicas raise: whether this one has caught up.

func NewConnection

func NewConnection(label, group, driver string, readOnly bool, db sqlbind.SQLExecutor) Connection

NewConnection describes one pool for the pane. db is a *sql.DB or the native executor of an engine that bypasses database/sql.

func (*Connection) Columns

func (c *Connection) Columns(ctx context.Context, table string) ([]Column, error)

Columns describes one table.

func (*Connection) DeleteRow

func (c *Connection) DeleteRow(ctx context.Context, edit RowEdit) (int64, error)

DeleteRow removes one row addressed by its primary key.

func (*Connection) Engine

func (c *Connection) Engine() string

Engine names the dialect this connection speaks.

func (*Connection) Exec

func (c *Connection) Exec(ctx context.Context, statement string) Result

Exec runs a statement the developer wrote and returns whatever it produced.

This is a development database reached from a loopback console in a build mode api:cli-build cannot emit, and a developer who can edit rows can already write any statement through the schema. Bounding what they may type would therefore buy nothing; what is bounded is the number of rows that come back.

func (*Connection) Explain

func (c *Connection) Explain(ctx context.Context, statement string) Result

Explain runs the plan-only form of a statement.

The prefixes are the ones requirement:query-diagnostics already uses for a slow statement, so a plan read here and a plan attached to a data:query-record come from the same request. ANALYZE is never used: it would execute the statement a second time, which is the wrong thing to do to a write and a waste on a read.

func (*Connection) ExplainQuery

func (c *Connection) ExplainQuery(ctx context.Context, pkg, name string, args []string) Result

ExplainQuery reads the plan of a declared statement, built with the arguments a run would use, so the plan is of the statement the application would run.

func (*Connection) ForeignKeys

func (c *Connection) ForeignKeys(ctx context.Context, table string) map[string]ForeignKey

ForeignKeys reports what one table references.

A dialect with no statement for it, or a database whose catalog refuses the question, yields none rather than an error: the grid is still readable without links, and losing the whole page over a missing affordance would be the wrong trade.

func (*Connection) InsertRow

func (c *Connection) InsertRow(ctx context.Context, edit RowEdit) (int64, error)

InsertRow adds one row. Values not named are left to the column default, which is the difference between an insert here and one that would overwrite what the schema decided.

func (*Connection) MigrationState

func (c *Connection) MigrationState(ctx context.Context) (Migration, error)

MigrationState reads the applied version.

Display only. api:cli-migrate owns applying and rolling back, and api:cli-dev already rolls back and reseeds on its own when a migration source changes — a second actor deciding the same thing from a page would be one too many.

func (*Connection) Referenced

func (c *Connection) Referenced(ctx context.Context, table, column, value string) (Page, error)

Referenced reads the rows one foreign key value points at.

The column and table come from the catalog, and the value travels as a bind parameter, so following a link is a selection rather than a query the page composed. That is what keeps this on the browsing side of the pane instead of being the filter box requirement:dev-data-pane declines to offer.

func (*Connection) Rows

func (c *Connection) Rows(ctx context.Context, table string, offset int) (Page, error)

Rows reads one page of one table.

The table name is matched against the catalog before it reaches a statement, so what a request carries is a selection rather than SQL. Ordering is by primary key where there is one, because a page whose order changes between reads shows the same row twice and never shows another.

func (*Connection) RunQuery

func (c *Connection) RunQuery(ctx context.Context, pkg, name string, args []string) Result

RunQuery executes one declared statement with the supplied arguments.

The statement text comes from the generated builder, so the pane runs what the application would run rather than an imitation of it, and a query whose SQL is assembled conditionally is assembled the same way here.

func (*Connection) Tables

func (c *Connection) Tables(ctx context.Context) ([]Table, error)

Tables lists what the connected database holds.

func (*Connection) UpdateRow

func (c *Connection) UpdateRow(ctx context.Context, edit RowEdit) (int64, error)

UpdateRow writes one row addressed by its primary key.

type ForeignKey

type ForeignKey struct {
	// Column is the column in this table.
	Column string `json:"column"`
	// Table and Target are what it references.
	Table  string `json:"table"`
	Target string `json:"target"`
}

ForeignKey is one reference a table makes.

It is what turns a grid of identifiers into something navigable: a column holding 42 means nothing until the row it points at is one click away, and that click is the difference between reading data and reading a table.

type Migration

type Migration struct {
	// Present is false when the table is absent, which means nothing has been
	// applied here rather than that something went wrong.
	Present bool
	Version int64
	Applied int
}

Migration is what the connected database says about its own schema version.

type Page

type Page struct {
	Table   string      `json:"table"`
	Columns []Column    `json:"columns"`
	Rows    [][]*string `json:"rows"`
	Offset  int         `json:"offset"`
	Limit   int         `json:"limit"`
	// Ordered reports whether the page has a stable order. Without a primary
	// key it does not, and saying so beats implying a stability the engine is
	// not promising.
	Ordered bool `json:"ordered"`
	More    bool `json:"more"`
}

Page is one bounded read of one table.

type Param

type Param struct {
	Name string `json:"name"`
	// Kind is the Go type as written in the generated signature, which is what
	// the form has to be able to produce.
	Kind string `json:"kind"`
}

Param is one parameter of a declared query, as its generated builder declares it.

type Query

type Query struct {
	Package  string  `json:"package"`
	Name     string  `json:"name"`
	Exported bool    `json:"exported"`
	Params   []Param `json:"params"`
	// Build turns the supplied arguments into the statement the application
	// itself would run. It is the generated builder, so what the pane executes
	// is the project's own SQL rather than a second rendering of it.
	Build func(args []string) (sqlbind.Statement, error) `json:"-"`
}

Query is one declared statement, registered from inside the package the generator emitted it into.

Registration happens there because a generated builder named for an unexported statement is unreachable from anywhere else, and the statements a developer most wants to try are not always the exported ones.

func Queries

func Queries() []Query

Queries lists what was registered, ordered so the page is stable across runs.

type Result

type Result struct {
	Columns []string    `json:"columns"`
	Rows    [][]*string `json:"rows"`
	// Affected is set for a statement that changed rows rather than returning
	// them. Negative means the driver would not say.
	Affected int64  `json:"affected"`
	Returned bool   `json:"returned"`
	SQL      string `json:"sql"`
	Error    string `json:"error,omitempty"`
	// Truncated reports that the result had more rows than the pane will show.
	Truncated bool `json:"truncated"`
}

Result is what a statement produced, whether it returned rows or a count.

type RowEdit

type RowEdit struct {
	Table string `json:"table"`
	// Key maps primary key column names to their current values. It is empty
	// for an insert.
	Key map[string]string `json:"key"`
	// Values maps column names to their new values. A column absent here is
	// left alone on an update.
	Values map[string]string `json:"values"`
	// Nulls names columns to set to NULL, which an empty string cannot express.
	Nulls []string `json:"nulls"`
}

RowEdit addresses one row and says what to do with it.

The key is the whole primary key, because that is what addresses exactly one row; a table without one cannot be edited here and says so. Values are bind parameters throughout — only column names reach the statement text, and only after the catalog has confirmed them.

type Server

type Server struct {
	// contains filtered or unexported fields
}

Server is the pane over one or more connections.

func New

func New(connections []Connection, environment string) *Server

New builds a server over the connections the application opened.

The order is preserved, because it is the order the project declared and a developer reads the list expecting to recognise it.

func NewSingle

func NewSingle(db sqlbind.SQLExecutor, driver, environment string) *Server

NewSingle is the ordinary case: a configuration that declares one database and never names a group.

func (*Server) Connections

func (s *Server) Connections() []Connection

Connections lists what the pane can address.

func (*Server) Default

func (s *Server) Default() *Connection

Default is the connection a page selects when none was asked for.

A writable one, because the pane edits and a page that opened on a replica would refuse the first edit attempted on it for a reason the developer did not choose. Falling back to the first connection keeps a project whose connections are all replicas usable for reading.

func (*Server) Handler

func (s *Server) Handler() http.Handler

Handler serves the data pane: schema, rows, edits, a statement console, and the declared queries the project generated.

It is mounted by the framework on a loopback listener of its own, never on the application's, so nothing here is reachable from the port the application serves.

func (*Server) Lookup

func (s *Server) Lookup(label string) *Connection

Lookup finds a connection by label, or returns the default when the label is empty or unknown.

type Table

type Table struct {
	Name string `json:"name"`
	// Framework marks a table the framework owns rather than the application.
	// It is shown and readable — a developer looking at their own development
	// database is not the exposure policy:query-log-safety bounds — but it is
	// marked, because a row in one of these was written by code the developer
	// did not write and is not theirs to reason about.
	Framework bool `json:"framework"`
}

Table is one table and what the pane knows about it before reading any row.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL