Documentation
¶
Overview ¶
Package ddl provides the schema-modification execution surface for DALgo. It defines the SchemaModifier capability interface, the composable AlterOp model for collection alterations, the TransactionalDDL capability for atomicity advertisement, and top-level helper functions that wrap a type assertion on dal.DB.
ddl imports github.com/dal-go/dalgo/dbschema for the structural types it operates on (CollectionDef, FieldDef, IndexDef, etc.) AND for the shared NotSupportedError typed error. Drivers that implement DDL satisfy ddl.SchemaModifier; drivers that don't cause helper functions to return *dbschema.NotSupportedError.
Index ¶
- func AlterCollection(ctx context.Context, db dal.DB, name string, ops ...AlterOp) error
- func CreateCollection(ctx context.Context, db dal.DB, c dbschema.CollectionDef, opts ...Option) error
- func DropCollection(ctx context.Context, db dal.DB, name string, opts ...Option) error
- func SupportsTransactionalDDL(db dal.DB) bool
- type AlterOp
- func AddField(f dbschema.FieldDef, opts ...Option) AlterOp
- func AddIndex(idx dbschema.IndexDef, opts ...Option) AlterOp
- func DropField(name dal.FieldName, opts ...Option) AlterOp
- func DropIndex(name string, opts ...Option) AlterOp
- func ModifyField(name dal.FieldName, newDef dbschema.FieldDef, opts ...Option) AlterOp
- func RenameField(oldName, newName dal.FieldName, opts ...Option) AlterOp
- type Applier
- type Option
- type Options
- type PartialSuccessError
- type SchemaModifier
- type TransactionalDDL
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func AlterCollection ¶
AlterCollection applies a batch of AlterOp values to an existing collection via the driver's SchemaModifier. Returns *dbschema.NotSupportedError if db does not implement SchemaModifier.
Transactional drivers (advertised via TransactionalDDL) roll back on partial failure. Non-transactional drivers MAY return *PartialSuccessError. Consumers wanting strict atomicity should check SupportsTransactionalDDL(db) before calling.
func CreateCollection ¶
func CreateCollection(ctx context.Context, db dal.DB, c dbschema.CollectionDef, opts ...Option) error
CreateCollection creates a new collection (and any inline indexes declared on c.Indexes) via the driver's SchemaModifier. Returns *dbschema.NotSupportedError if db does not implement SchemaModifier.
func DropCollection ¶
DropCollection drops a collection and its indexes via the driver's SchemaModifier. Returns *dbschema.NotSupportedError if db does not implement SchemaModifier.
func SupportsTransactionalDDL ¶
SupportsTransactionalDDL is the convenience helper that encapsulates the type assertion and the convention that "doesn't implement = treat as non-transactional." Consumers SHOULD use this rather than performing the assertion themselves.
Types ¶
type AlterOp ¶
type AlterOp interface {
// ApplyTo dispatches this AlterOp to the matching ApplyXxx method
// on the given Applier, passing the op's stored fields and resolved
// Options. Returns the Applier's error verbatim. Used by driver-side
// SchemaModifier.AlterCollection implementations to handle a slice
// of AlterOp values without type-switching on unexported concrete
// types.
ApplyTo(ctx context.Context, a Applier) error
// contains filtered or unexported methods
}
AlterOp is the sealed interface for collection-altering operations passed to AlterCollection. Sealed via an unexported marker method (alterOp) so the set of valid alterations is closed at the package boundary — drivers know which cases exist and translate accordingly. New alteration kinds require adding to this package.
MVP constructors:
- Field-level: AddField, DropField, ModifyField, RenameField
- Index-level: AddIndex, DropIndex
All six constructors accept opts ...Option for opt-in idempotency (reusing the same Option type as CreateCollection / DropCollection). Drivers MUST silently ignore semantically-mismatched options.
func AddField ¶
AddField returns an AlterOp that adds a field to the collection. IfNotExists makes it idempotent (existing field of same name = no-op). IfExists is meaningless and silently ignored.
func AddIndex ¶
AddIndex returns an AlterOp that creates an index on the collection. On engines that support combined ALTER TABLE ... ADD INDEX syntax (MySQL), the driver MAY fold this into a single statement alongside other AlterOps in the same batch.
IfNotExists makes it idempotent (existing index of same name = no-op). IfExists is meaningless and silently ignored.
func DropField ¶
DropField returns an AlterOp that drops a field by name. IfExists makes it idempotent (missing field = no-op). IfNotExists is meaningless and silently ignored.
func DropIndex ¶
DropIndex returns an AlterOp that drops an index by name. IfExists makes it idempotent (missing index = no-op). IfNotExists is meaningless and silently ignored.
func ModifyField ¶
ModifyField returns an AlterOp that replaces an existing field's definition with newDef. The driver diffs old vs new and emits the minimal engine-specific change. When name != newDef.Name, the operation also renames the field.
opts is accepted for surface symmetry but both IfNotExists and IfExists are semantically meaningless on ModifyField. Drivers MUST silently ignore them.
func RenameField ¶
RenameField returns an AlterOp that renames a field from oldName to newName.
opts is accepted for surface symmetry but both IfNotExists and IfExists are semantically meaningless on RenameField. Drivers MUST silently ignore them.
type Applier ¶
type Applier interface {
// ApplyAddField is called by addFieldOp.ApplyTo.
ApplyAddField(ctx context.Context, f dbschema.FieldDef, opts Options) error
// ApplyDropField is called by dropFieldOp.ApplyTo.
ApplyDropField(ctx context.Context, name dal.FieldName, opts Options) error
// ApplyModifyField is called by modifyFieldOp.ApplyTo. name is
// the current field name; newDef is its replacement (which MAY
// have a different Name, indicating a rename as part of modify).
ApplyModifyField(ctx context.Context, name dal.FieldName, newDef dbschema.FieldDef, opts Options) error
// ApplyRenameField is called by renameFieldOp.ApplyTo.
ApplyRenameField(ctx context.Context, oldName, newName dal.FieldName, opts Options) error
// ApplyAddIndex is called by addIndexOp.ApplyTo.
ApplyAddIndex(ctx context.Context, idx dbschema.IndexDef, opts Options) error
// ApplyDropIndex is called by dropIndexOp.ApplyTo.
ApplyDropIndex(ctx context.Context, name string, opts Options) error
}
Applier is the visitor interface for AlterOp dispatch. Driver-side implementations of SchemaModifier.AlterCollection construct an Applier (typically a struct holding the in-flight transaction and the target table name), then call AlterOp.ApplyTo on each op in their batch. ApplyTo dispatches to the matching ApplyXxx method on the Applier, where the driver translates the operation into engine-specific SQL or other side effects.
All six methods take ctx as the first parameter (forwarded by AlterOp.ApplyTo) and return error so driver-side failures surface to the AlterCollection caller.
The resolved Options value (already processed by ResolveOptions at AlterOp-constructor time) is passed last. Drivers MAY consult opts.IfNotExists / opts.IfExists for the field/index-level AlterOps where those flags are semantically meaningful (see the alter-ops Feature for which combinations matter per op).
Applier is NOT sealed — driver packages MUST implement it. Test stubs (e.g. a recording fakeApplier) MAY also implement it. New AlterOp variants added in future dalgo releases will extend this interface, which is a breaking change for existing implementers (compile-time error) — the same property the AlterOp sealed interface has on the producer side.
type Option ¶
type Option func(*Options)
Option is the functional-option type accepted by CreateCollection, DropCollection, and all six AlterOp constructors via variadic opts ...Option. AlterCollection itself does NOT accept Option values directly — each AlterOp carries its own resolved Options set by the caller through the constructor.
func IfExists ¶
func IfExists() Option
IfExists makes a Drop operation idempotent: the target being absent is a no-op rather than an error. Meaningless and silently ignored on Create / Add operations.
func IfNotExists ¶
func IfNotExists() Option
IfNotExists makes a Create / Add operation idempotent: the target already existing is a no-op rather than an error. Meaningless and silently ignored on Drop operations.
type Options ¶
Options is the resolved set of functional options for the collection-level and AlterOp-level operations.
IfNotExists makes Create / Add operations idempotent — the target existing already is a no-op rather than an error. IfExists makes Drop operations idempotent — the target being absent is a no-op.
Drivers MUST silently ignore semantically-mismatched options:
- IfNotExists on Drop operations
- IfExists on Create / Add operations
- Any option on ModifyField or RenameField
The meaning is unambiguous (there is nothing to do with a mismatched hint), so a real error there would be a footgun.
func ResolveOptions ¶
ResolveOptions applies opts to a zero-value Options and returns the result. Drivers and AlterOp constructors use this to fold a variadic options slice into a struct.
type PartialSuccessError ¶
type PartialSuccessError struct {
// Op names the batched operation (currently always "AlterCollection").
Op string
// Collection is the target collection name.
Collection string
// Backend optionally identifies the driver.
Backend string
// Applied lists the ops that completed successfully, in original
// order.
Applied []AlterOp
// FirstFailed is the op that failed.
FirstFailed AlterOp
// NotAttempted lists the ops that came after FirstFailed and were
// not tried. May be empty if the driver attempted every op
// regardless of earlier failures.
NotAttempted []AlterOp
// Cause is the underlying error from the failed op (driver-specific).
Cause error
}
PartialSuccessError is the typed error returned by AlterCollection (and any future batched DDL call) when a non-transactional driver succeeds at some sub-operations and fails at others.
Distinct from *dbschema.NotSupportedError: NotSupportedError means "the driver can't do this at all"; PartialSuccessError means "the driver started doing this, then failed partway."
Transactional drivers — those for which TransactionalDDL.SupportsTransactionalDDL returns true — MUST NOT produce *PartialSuccessError. Their failure mode is a regular error (rollback already performed; nothing was applied).
Unwrap returns Cause so errors.Is(err, dal.ErrNotSupported) propagates transitively if the underlying failure was a not-supported case.
func (*PartialSuccessError) Error ¶
func (e *PartialSuccessError) Error() string
Error returns a readable single-line summary.
func (*PartialSuccessError) Unwrap ¶
func (e *PartialSuccessError) Unwrap() error
Unwrap returns Cause so errors.Is and errors.As propagate through the underlying driver-specific failure.
type SchemaModifier ¶
type SchemaModifier interface {
// CreateCollection creates the collection (table) and any inline
// indexes declared on c.Indexes. Caller passes opts for opt-in
// idempotency.
CreateCollection(ctx context.Context, c dbschema.CollectionDef, opts ...Option) error
// DropCollection drops the collection and its indexes. Caller
// passes opts for opt-in idempotency.
DropCollection(ctx context.Context, name string, opts ...Option) error
// AlterCollection applies ops to the existing collection. The
// driver decides how to apply the batch (one combined ALTER
// statement on PostgreSQL; a sequence on SQLite; etc.).
// Transactional drivers (advertised via TransactionalDDL) MUST
// roll back on partial failure; non-transactional drivers MAY
// return *PartialSuccessError listing applied/failed/not-attempted ops.
AlterCollection(ctx context.Context, name string, ops ...AlterOp) error
}
SchemaModifier is the capability interface drivers implement to support DDL operations on dalgo collections. The interface is three methods:
- CreateCollection creates a collection (table) along with any inline indexes declared in CollectionDef.Indexes.
- DropCollection drops a collection along with its indexes.
- AlterCollection applies a batch of AlterOp values (field-level and index-level alterations) to an existing collection. The driver decides how to apply the batch atomically; consumers check TransactionalDDL to know whether to expect rollback.
SchemaModifier is NOT embedded into dal.DB. DDL is genuinely optional for some backends — read-only wrappers, analytics drivers, mocks. Drivers opt in by implementing SchemaModifier on their dal.DB value (or a related type reachable via type assertion). The top-level helper functions CreateCollection, DropCollection, and AlterCollection type-assert against SchemaModifier and return *dbschema.NotSupportedError on the failed assertion.
Index-level operations after initial collection creation are NOT separate methods — they are AlterOp values passed to AlterCollection. See AddIndex and DropIndex.
type TransactionalDDL ¶
type TransactionalDDL interface {
SupportsTransactionalDDL() bool
}
TransactionalDDL is the optional capability interface drivers implement to advertise that they guarantee all-or-nothing atomicity for DDL calls that perform multiple sub-operations (notably AlterCollection with multiple AlterOps, or CreateCollection with inline indexes on some engines).
The pattern mirrors dal.ConcurrencyAware: a one-method optional interface that consumers type-assert against. Drivers that don't implement TransactionalDDL are treated as non-transactional.
When SupportsTransactionalDDL returns true: if any sub-operation fails, the driver MUST roll back all previously-applied sub-operations in the same call. The whole call returns a non-nil error; the DB is left in its pre-call state.
When SupportsTransactionalDDL returns false (or the interface is not implemented): the driver MAY apply some sub-operations and fail on others. Callers receive a *PartialSuccessError listing applied / failed / not-attempted ops.
The return value is constant from the moment a DB value is returned by its constructor until it is discarded; the same stability contract as dal.ConcurrencyAware.