Skip to content

Commit a6594d8

Browse files
PG Schema cascade delete (#333)
* initial cascade delete support Signed-off-by: Steven Borrelli <steve@borrelli.org> * review fixes Signed-off-by: Steven Borrelli <steve@borrelli.org> * reformat comment on dropBehavior parameter Signed-off-by: Steven Borrelli <steve@borrelli.org> * add cascade delete Signed-off-by: Steven Borrelli <steve@borrelli.org> * remove cascade example from the public schema Signed-off-by: Steven Borrelli <steve@borrelli.org> * clean up comments and assignment Signed-off-by: Steven Borrelli <steve@borrelli.org> --------- Signed-off-by: Steven Borrelli <steve@borrelli.org>
1 parent deee375 commit a6594d8

File tree

11 files changed

+285
-2
lines changed

11 files changed

+285
-2
lines changed

apis/cluster/postgresql/v1alpha1/schema_types.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,16 @@ import (
2222
xpv1 "github.com/crossplane/crossplane-runtime/v2/apis/common/v1"
2323
)
2424

25+
// DropBehavior sets the method that is used to drop a schema.
26+
type DropBehavior string
27+
28+
const (
29+
// DropBehaviorCascade automatically drops objects contained in the schema.
30+
DropBehaviorCascade DropBehavior = "CASCADE"
31+
// DropBehaviorRestrict refuses to drop the schema if it contains any objects.
32+
DropBehaviorRestrict DropBehavior = "RESTRICT"
33+
)
34+
2535
// A SchemaSpec defines the desired state of a Schema.
2636
type SchemaSpec struct {
2737
xpv1.ResourceSpec `json:",inline"`
@@ -63,6 +73,15 @@ type SchemaParameters struct {
6373
// RevokePublicOnSchema apply a "REVOKE ALL ON SCHEMA public FROM public" statement
6474
// +optional
6575
RevokePublicOnSchema *bool `json:"revokePublicOnSchema,omitempty" default:"false"`
76+
77+
// DropBehavior configures deletion behavior: CASCADE will automatically drop
78+
// objects (tables, functions, etc.) that are contained in the schema, and in
79+
// turn all objects that depend on those objects. The default setting of
80+
// RESTRICT will refuse to drop the schema if it contains any objects.
81+
// +kubebuilder:validation:Enum=CASCADE;RESTRICT
82+
// +kubebuilder:default:=RESTRICT
83+
// +optional
84+
DropBehavior *DropBehavior `json:"dropBehavior,omitempty"`
6685
}
6786

6887
// A SchemaStatus represents the observed state of a Schema.

apis/cluster/postgresql/v1alpha1/zz_generated.deepcopy.go

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

apis/namespaced/postgresql/v1alpha1/schema_types.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,16 @@ import (
2323
xpv2 "github.com/crossplane/crossplane-runtime/v2/apis/common/v2"
2424
)
2525

26+
// DropBehavior sets the method that is used to drop a schema
27+
type DropBehavior string
28+
29+
const (
30+
// DropBehaviorCascade automatically drops objects contained in the schema.
31+
DropBehaviorCascade DropBehavior = "CASCADE"
32+
// DropBehaviorRestrict refuses to drop the schema if it contains any objects.
33+
DropBehaviorRestrict DropBehavior = "RESTRICT"
34+
)
35+
2636
// A SchemaSpec defines the desired state of a Schema.
2737
type SchemaSpec struct {
2838
xpv2.ManagedResourceSpec `json:",inline"`
@@ -64,6 +74,15 @@ type SchemaParameters struct {
6474
// RevokePublicOnSchema apply a "REVOKE ALL ON SCHEMA public FROM public" statement
6575
// +optional
6676
RevokePublicOnSchema *bool `json:"revokePublicOnSchema,omitempty" default:"false"`
77+
78+
// DropBehavior configures deletion behavior: CASCADE will automatically drop
79+
// objects (tables, functions, etc.) that are contained in the schema, and in
80+
// turn all objects that depend on those objects. The default setting of
81+
// RESTRICT will refuse to drop the schema if it contains any objects.
82+
// +kubebuilder:validation:Enum=CASCADE;RESTRICT
83+
// +kubebuilder:default:=RESTRICT
84+
// +optional
85+
DropBehavior *DropBehavior `json:"dropBehavior,omitempty"`
6786
}
6887

6988
// A SchemaStatus represents the observed state of a Schema.

apis/namespaced/postgresql/v1alpha1/zz_generated.deepcopy.go

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/namespaced/postgresql/schema.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ spec:
77
forProvider:
88
databaseRef:
99
name: example
10+
dropBehavior: CASCADE
1011
roleRef:
1112
name: example-role
1213
providerConfigRef:

package/crds/postgresql.sql.crossplane.io_schemas.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,17 @@ spec:
156156
type: string
157157
type: object
158158
type: object
159+
dropBehavior:
160+
default: RESTRICT
161+
description: |-
162+
DropBehavior configures deletion behavior: CASCADE will automatically drop
163+
objects (tables, functions, etc.) that are contained in the schema, and in
164+
turn all objects that depend on those objects. The default setting of
165+
RESTRICT will refuse to drop the schema if it contains any objects.
166+
enum:
167+
- CASCADE
168+
- RESTRICT
169+
type: string
159170
revokePublicOnSchema:
160171
description: RevokePublicOnSchema apply a "REVOKE ALL ON SCHEMA
161172
public FROM public" statement

package/crds/postgresql.sql.m.crossplane.io_schemas.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,17 @@ spec:
148148
type: string
149149
type: object
150150
type: object
151+
dropBehavior:
152+
default: RESTRICT
153+
description: |-
154+
DropBehavior configures deletion behavior: CASCADE will automatically drop
155+
objects (tables, functions, etc.) that are contained in the schema, and in
156+
turn all objects that depend on those objects. The default setting of
157+
RESTRICT will refuse to drop the schema if it contains any objects.
158+
enum:
159+
- CASCADE
160+
- RESTRICT
161+
type: string
151162
revokePublicOnSchema:
152163
description: RevokePublicOnSchema apply a "REVOKE ALL ON SCHEMA
153164
public FROM public" statement

pkg/controller/cluster/postgresql/schema/reconciler.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package schema
1818

1919
import (
2020
"context"
21+
"fmt"
2122
"strings"
2223

2324
"github.com/lib/pq"
@@ -189,7 +190,11 @@ func (c *external) Update(ctx context.Context, mg *v1alpha1.Schema) (managed.Ext
189190
}
190191

191192
func (c *external) Delete(ctx context.Context, mg *v1alpha1.Schema) (managed.ExternalDelete, error) {
192-
err := c.db.Exec(ctx, xsql.Query{String: "DROP SCHEMA IF EXISTS " + pq.QuoteIdentifier(meta.GetExternalName(mg))})
193+
dropBehavior := v1alpha1.DropBehaviorRestrict
194+
if mg.Spec.ForProvider.DropBehavior != nil {
195+
dropBehavior = *mg.Spec.ForProvider.DropBehavior
196+
}
197+
err := c.db.Exec(ctx, xsql.Query{String: fmt.Sprintf("DROP SCHEMA IF EXISTS %s %s", pq.QuoteIdentifier(meta.GetExternalName(mg)), string(dropBehavior))})
193198
return managed.ExternalDelete{}, errors.Wrap(err, errDropSchema)
194199
}
195200

pkg/controller/cluster/postgresql/schema/reconciler_test.go

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package schema
1919
import (
2020
"context"
2121
"database/sql"
22+
"strings"
2223
"testing"
2324

2425
"github.com/crossplane-contrib/provider-sql/apis/cluster/postgresql/v1alpha1"
@@ -499,6 +500,106 @@ func TestDelete(t *testing.T) {
499500
},
500501
want: errors.Wrap(errBoom, errDropSchema),
501502
},
503+
"DropBehaviorDefaultRestrict": {
504+
reason: "When dropBehavior is nil, it should default to RESTRICT",
505+
fields: fields{
506+
db: &mockDB{
507+
MockExec: func(ctx context.Context, q xsql.Query) error {
508+
if !strings.Contains(q.String, "RESTRICT") {
509+
t.Errorf("Expected query to contain RESTRICT, got: %s", q.String)
510+
}
511+
return nil
512+
},
513+
},
514+
},
515+
args: args{
516+
mg: &v1alpha1.Schema{
517+
ObjectMeta: cr.ObjectMeta,
518+
Spec: v1alpha1.SchemaSpec{
519+
ForProvider: v1alpha1.SchemaParameters{
520+
Database: ptr.To("db"),
521+
DropBehavior: nil,
522+
},
523+
},
524+
},
525+
},
526+
want: nil,
527+
},
528+
"DropBehaviorExplicitRestrict": {
529+
reason: "When dropBehavior is explicitly set to RESTRICT, it should use RESTRICT",
530+
fields: fields{
531+
db: &mockDB{
532+
MockExec: func(ctx context.Context, q xsql.Query) error {
533+
if !strings.Contains(q.String, "RESTRICT") {
534+
t.Errorf("Expected query to contain RESTRICT, got: %s", q.String)
535+
}
536+
return nil
537+
},
538+
},
539+
},
540+
args: args{
541+
mg: &v1alpha1.Schema{
542+
ObjectMeta: cr.ObjectMeta,
543+
Spec: v1alpha1.SchemaSpec{
544+
ForProvider: v1alpha1.SchemaParameters{
545+
Database: ptr.To("db"),
546+
DropBehavior: ptr.To(v1alpha1.DropBehaviorRestrict),
547+
},
548+
},
549+
},
550+
},
551+
want: nil,
552+
},
553+
"DropBehaviorCascade": {
554+
reason: "When dropBehavior is set to CASCADE, it should use CASCADE",
555+
fields: fields{
556+
db: &mockDB{
557+
MockExec: func(ctx context.Context, q xsql.Query) error {
558+
if !strings.Contains(q.String, "CASCADE") {
559+
t.Errorf("Expected query to contain CASCADE, got: %s", q.String)
560+
}
561+
return nil
562+
},
563+
},
564+
},
565+
args: args{
566+
mg: &v1alpha1.Schema{
567+
ObjectMeta: cr.ObjectMeta,
568+
Spec: v1alpha1.SchemaSpec{
569+
ForProvider: v1alpha1.SchemaParameters{
570+
Database: ptr.To("db"),
571+
DropBehavior: ptr.To(v1alpha1.DropBehaviorCascade),
572+
},
573+
},
574+
},
575+
},
576+
want: nil,
577+
},
578+
"DropSchemaWithIfExists": {
579+
reason: "Drop statement should include IF EXISTS clause",
580+
fields: fields{
581+
db: &mockDB{
582+
MockExec: func(ctx context.Context, q xsql.Query) error {
583+
if !strings.Contains(q.String, "IF EXISTS") {
584+
t.Errorf("Expected query to contain IF EXISTS, got: %s", q.String)
585+
}
586+
return nil
587+
},
588+
},
589+
},
590+
args: args{
591+
mg: &v1alpha1.Schema{
592+
ObjectMeta: cr.ObjectMeta,
593+
Spec: v1alpha1.SchemaSpec{
594+
ForProvider: v1alpha1.SchemaParameters{
595+
Database: ptr.To("db"),
596+
DropBehavior: ptr.To(v1alpha1.DropBehaviorRestrict),
597+
},
598+
},
599+
},
600+
},
601+
want: nil,
602+
},
502603
}
503604

504605
for name, tc := range cases {

pkg/controller/namespaced/postgresql/schema/reconciler.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package schema
1818

1919
import (
2020
"context"
21+
"fmt"
2122
"strings"
2223

2324
"github.com/lib/pq"
@@ -173,7 +174,11 @@ func (c *external) Update(ctx context.Context, mg *namespacedv1alpha1.Schema) (m
173174
}
174175

175176
func (c *external) Delete(ctx context.Context, mg *namespacedv1alpha1.Schema) (managed.ExternalDelete, error) {
176-
err := c.db.Exec(ctx, xsql.Query{String: "DROP SCHEMA IF EXISTS " + pq.QuoteIdentifier(meta.GetExternalName(mg))})
177+
dropBehavior := namespacedv1alpha1.DropBehaviorRestrict
178+
if mg.Spec.ForProvider.DropBehavior != nil {
179+
dropBehavior = *mg.Spec.ForProvider.DropBehavior
180+
}
181+
err := c.db.Exec(ctx, xsql.Query{String: fmt.Sprintf("DROP SCHEMA IF EXISTS %s %s", pq.QuoteIdentifier(meta.GetExternalName(mg)), string(dropBehavior))})
177182
return managed.ExternalDelete{}, errors.Wrap(err, errDropSchema)
178183
}
179184

0 commit comments

Comments
 (0)