diff

package
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Oct 14, 2026 License: Apache-2.0 Imports: 10 Imported by: 0

Documentation

Overview

Package diff performs pure relational comparison between live and desired database schemas.

Dependency rule: diff is pure. It depends only on internal/schema, internal/scope, and internal/plan. It has zero dependencies on database/sql, context, or dialect rendering.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func RefuseUnmanagedCatalogRecreates

func RefuseUnmanagedCatalogRecreates(desired *schema.CatalogSpec, live *CatalogLiveState, targetSchema string) error

RefuseUnmanagedCatalogRecreates fails closed when a desired publication or event trigger shares a name with an operator-created live object whose definition cannot be reconciled without DROP+CREATE. CatalogDiff never emits that DROP (marker-narrow policy); without this check Sync would silently leave the desired state unconverged.

Types

type CatalogLiveState

type CatalogLiveState struct {
	Publications     map[string]*PublicationState
	EventTriggers    map[string]*EventTriggerState
	Subscriptions    map[string]*SubscriptionState
	ReplicationSlots map[string]*ReplicationSlotState
}

CatalogLiveState is the live publication/event-trigger/subscription/slot state inspected from the database catalogs.

type Change

type Change struct {
	Type              plan.ChangeType
	Schema            string
	Table             string
	Column            *schema.Column
	OldColumn         *schema.Column
	Index             *schema.Index
	OldIndex          *schema.Index
	ForeignKey        *schema.ForeignKey
	Check             *schema.CheckConstraint
	Enum              *schema.Enum
	OldEnum           *schema.Enum
	EnumValue         string
	OldEnumValue      string
	TableData         *schema.Table
	Extension         *schema.Extension
	Policy            *schema.Policy
	OldPolicy         *schema.Policy
	Routine           *schema.Routine
	Domain            *schema.Domain
	OldDomain         *schema.Domain
	RangeType         *schema.RangeType
	CompositeType     *schema.CompositeType
	CompositeAttr     *schema.CompositeAttribute
	Sequence          *schema.Sequence
	OldSequence       *schema.Sequence
	Role              *schema.Role
	Grant             *schema.Grant
	Publication       *schema.Publication
	OldPublication    *schema.Publication
	EventTrigger      *schema.EventTrigger
	OldEventTrigger   *schema.EventTrigger
	Subscription      *schema.Subscription
	OldSubscription   *schema.Subscription
	ReplicationSlot   *schema.ReplicationSlot
	Trigger           *schema.Trigger
	View              *schema.View
	Replace           bool
	Destructive       bool
	IsRenameCandidate bool
	OldComment        string

	// Routine signature drift and dependency rebuild flags
	IsSignatureChange   bool
	IsDependencyRebuild bool

	// Structural flags for hazard analysis
	ColumnNotNull    bool
	ColumnHasDefault bool

	// Column change flags
	TypeChanged      bool
	TypeNarrowed     bool
	NullChanged      bool
	DefaultChanged   bool
	GeneratedChanged bool

	// Unmanaged object dependencies (views, triggers, functions depending on this table/column)
	UnmanagedDeps []string

	// Partitioning metadata
	ParentTable      string
	PartitionBounds  string
	ParentHasDefault bool
	IsPendingDetach  bool
}

Change represents a pure difference between live and desired database schemas.

func CatalogDiff

func CatalogDiff(desired *schema.CatalogSpec, live *CatalogLiveState, targetSchema string) []Change

CatalogDiff computes publication, event-trigger, subscription, and logical-slot sync steps from the desired CatalogSQL state and the live catalog state.

Destructive drops are narrow: publications, event triggers, and subscriptions are dropped only when they carry the grizzle-managed marker comment — including the recreate (DROP+CREATE) path used when definition drift cannot be expressed as ALTER. Operator-created catalog objects without the marker are never swept. Standalone logical slots never auto-sweep (COMMENT ON unsupported); drops require an explicit pg_drop_replication_slot. Objects named in the desired state are managed for create/alter; drops still require the marker.

targetSchema qualifies unqualified table names in desired publications.

func Diff

func Diff(live, desired *schema.Schema, targetSchema, shadowSchema string, filters scope.Filters) ([]Change, error)

Diff compares live and desired schemas using the given scope filters and returns pure changes.

func DiffWithMappings

func DiffWithMappings(live, desired *schema.Schema, targetSchema, shadowSchema string, filters scope.Filters, schemaMappings map[string]string) ([]Change, error)

DiffWithMappings compares live and desired schemas using schemaMappings for normalization and returns pure changes.

func RolesDiff

func RolesDiff(desired *schema.RolesSpec, live *RoleState, targetSchema string) []Change

RolesDiff compares the desired RolesSpec (parsed from RolesSQL) with live role/ACL state and returns pure changes.

Scope rules:

  • Roles are created for every desired declaration and for grantees referenced by grants (NOLOGIN group roles), stamped with the managed-role marker comment.
  • Live roles carrying the marker that are absent from the desired spec are dropped behind AllowDropRole; operator-created roles are never swept.
  • Grants are diffed per (object, grantee): missing privileges GRANT, surplus privileges REVOKE (gated by AllowRevoke). Grants to grantees outside the managed set are operator state and left untouched.
  • ACL grants held by roles being dropped are revoked before DROP ROLE so PostgreSQL can remove the role even when explicit ACL dependencies remain.
  • Grant-option drift is reconciled by re-grant (option missing) or revoke + re-grant (option unwanted).

targetSchema qualifies unqualified TABLE/SEQUENCE object names so desired grants written against the working schema match the schema-qualified names reported by ACL introspection.

type EventTriggerState

type EventTriggerState struct {
	Name string
	// Managed marks a live event trigger stamped with the grizzle-managed
	// marker comment.
	Managed bool

	Event string
	Tags  []string
	// Function is the canonical schema-qualified routine identity, including
	// identity arguments when inspected from PostgreSQL.
	Function string
	Enabled  bool
}

EventTriggerState is the dialect-independent live event-trigger snapshot consumed by CatalogDiff.

type LiveRoleAttrs

type LiveRoleAttrs struct {
	CanLogin        bool
	ConnLimit       int
	ValidUntil      string
	Inherit         bool
	CreateDB        bool
	CreateRole      bool
	HasPassword     bool
	PasswordMatches bool // true when no desired password or hashes match
	Config          map[string]string
}

LiveRoleAttrs is the live attribute snapshot for one role.

type PublicationState

type PublicationState struct {
	Name string
	// Managed marks a live publication stamped with the grizzle-managed
	// marker comment. Only managed live-only publications are droppable.
	Managed bool

	AllTables bool
	Tables    []string // canonical schema-qualified identities
	Schemas   []string // canonical identifiers

	PublishInsert   bool
	PublishUpdate   bool
	PublishDelete   bool
	PublishTruncate bool
}

PublicationState is the dialect-independent live publication snapshot consumed by CatalogDiff.

type ReplicationSlotState

type ReplicationSlotState struct {
	Name      string
	Plugin    string
	Temporary bool
	// ActivePID is non-nil when a backend holds the slot; drops are refused.
	ActivePID *int
	// OwnedBySubscription is true when this slot name matches a live
	// subscription's subslotname; such slots are not managed separately.
	OwnedBySubscription bool
}

ReplicationSlotState is the dialect-independent live logical slot snapshot. Replication slots do not support COMMENT ON; Managed is unused for sweep decisions — live-only slots are never auto-dropped.

type RoleACLGrant

type RoleACLGrant struct {
	Grantee      string   // role name or PUBLIC
	ObjectKind   string   // TABLE, SEQUENCE, DATABASE, SCHEMA, FUNCTION
	ObjectName   string   // canonical, schema-qualified where applicable
	Privileges   []string // sorted privilege_type values
	GrantOptions []string // subset of Privileges held WITH GRANT OPTION
}

RoleACLGrant is one live ACL entry (grantee, object) with its privileges.

type RoleState

type RoleState struct {
	// RoleNames maps canonical PostgreSQL role identity -> catalog name
	// (non-system roles).
	RoleNames map[string]string
	// ManagedRoles are canonical names of roles stamped with the
	// grizzle-managed marker comment.
	ManagedRoles map[string]bool
	// RoleAttrs maps canonical role identity -> live attributes.
	RoleAttrs map[string]*LiveRoleAttrs
	// Grants is the live ACL inventory.
	Grants []*RoleACLGrant
}

RoleState is the live role/ACL snapshot consumed by RolesDiff. It is built by the dialect layer (postgres.InspectLiveRoles) so this package stays free of dialect imports.

type SubscriptionState

type SubscriptionState struct {
	Name string
	// Managed marks a live subscription stamped with the grizzle-managed
	// marker comment. Only managed live-only subscriptions are droppable.
	Managed bool

	ConnInfo     string
	SlotName     string
	Publications []string
	Enabled      bool
}

SubscriptionState is the dialect-independent live subscription snapshot consumed by CatalogDiff.

Jump to

Keyboard shortcuts

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