Repository: schemahero/schemahero
Pull request #277 (2020-09-18 14:35:15)

Initial support for Cassandra
This PR adds support for Cassandra schemas.
This also creates a new CRD for `datatype`. This is commonly used in cassandra and should / could also be implemented in other database engines.

Docs PR is at schemahero/schemahero-docs#40


Patch:
diff --git a/.github/workflows/proaction.yaml b/.github/workflows/proaction.yaml
index 7b03208e..239271bb 100644
--- a/.github/workflows/proaction.yaml
+++ b/.github/workflows/proaction.yaml
@@ -12,7 +12,7 @@ jobs:
         with:
           github-token: ${{secrets.GITHUB_TOKEN}}
 
-      - uses: peter-evans/create-pull-request@v3.4.0
+      - uses: peter-evans/create-pull-request@v3.3.0
         with:
           commit-message: "[proaction] updating workflow"
           title: Updating workflow from Proaction
diff --git a/config/crds/v1/databases.schemahero.io_databases.yaml b/config/crds/v1/databases.schemahero.io_databases.yaml
index 0bdd16dc..574f9137 100644
--- a/config/crds/v1/databases.schemahero.io_databases.yaml
+++ b/config/crds/v1/databases.schemahero.io_databases.yaml
@@ -39,6 +39,277 @@ spec:
                 description: DatabaseConnection defines connection parameters for
                   the database driver
                 properties:
+                  cassandra:
+                    properties:
+                      hosts:
+                        items:
+                          type: string
+                        type: array
+                      keyspace:
+                        properties:
+                          value:
+                            type: string
+                          valueFrom:
+                            properties:
+                              secretKeyRef:
+                                properties:
+                                  key:
+                                    type: string
+                                  name:
+                                    type: string
+                                required:
+                                - key
+                                - name
+                                type: object
+                              ssm:
+                                properties:
+                                  accessKeyId:
+                                    properties:
+                                      value:
+                                        type: string
+                                      valueFrom:
+                                        properties:
+                                          secretKeyRef:
+                                            properties:
+                                              key:
+                                                type: string
+                                              name:
+                                                type: string
+                                            required:
+                                            - key
+                                            - name
+                                            type: object
+                                        type: object
+                                    required:
+                                    - value
+                                    type: object
+                                  name:
+                                    type: string
+                                  region:
+                                    type: string
+                                  secretAccessKey:
+                                    properties:
+                                      value:
+                                        type: string
+                                      valueFrom:
+                                        properties:
+                                          secretKeyRef:
+                                            properties:
+                                              key:
+                                                type: string
+                                              name:
+                                                type: string
+                                            required:
+                                            - key
+                                            - name
+                                            type: object
+                                        type: object
+                                    required:
+                                    - value
+                                    type: object
+                                  withDecryption:
+                                    type: boolean
+                                required:
+                                - name
+                                type: object
+                              vault:
+                                properties:
+                                  agentInject:
+                                    type: boolean
+                                  endpoint:
+                                    type: string
+                                  role:
+                                    type: string
+                                  secret:
+                                    type: string
+                                  serviceAccount:
+                                    type: string
+                                  serviceAccountNamespace:
+                                    type: string
+                                required:
+                                - role
+                                - secret
+                                type: object
+                            type: object
+                        type: object
+                      password:
+                        properties:
+                          value:
+                            type: string
+                          valueFrom:
+                            properties:
+                              secretKeyRef:
+                                properties:
+                                  key:
+                                    type: string
+                                  name:
+                                    type: string
+                                required:
+                                - key
+                                - name
+                                type: object
+                              ssm:
+                                properties:
+                                  accessKeyId:
+                                    properties:
+                                      value:
+                                        type: string
+                                      valueFrom:
+                                        properties:
+                                          secretKeyRef:
+                                            properties:
+                                              key:
+                                                type: string
+                                              name:
+                                                type: string
+                                            required:
+                                            - key
+                                            - name
+                                            type: object
+                                        type: object
+                                    required:
+                                    - value
+                                    type: object
+                                  name:
+                                    type: string
+                                  region:
+                                    type: string
+                                  secretAccessKey:
+                                    properties:
+                                      value:
+                                        type: string
+                                      valueFrom:
+                                        properties:
+                                          secretKeyRef:
+                                            properties:
+                                              key:
+                                                type: string
+                                              name:
+                                                type: string
+                                            required:
+                                            - key
+                                            - name
+                                            type: object
+                                        type: object
+                                    required:
+                                    - value
+                                    type: object
+                                  withDecryption:
+                                    type: boolean
+                                required:
+                                - name
+                                type: object
+                              vault:
+                                properties:
+                                  agentInject:
+                                    type: boolean
+                                  endpoint:
+                                    type: string
+                                  role:
+                                    type: string
+                                  secret:
+                                    type: string
+                                  serviceAccount:
+                                    type: string
+                                  serviceAccountNamespace:
+                                    type: string
+                                required:
+                                - role
+                                - secret
+                                type: object
+                            type: object
+                        type: object
+                      username:
+                        properties:
+                          value:
+                            type: string
+                          valueFrom:
+                            properties:
+                              secretKeyRef:
+                                properties:
+                                  key:
+                                    type: string
+                                  name:
+                                    type: string
+                                required:
+                                - key
+                                - name
+                                type: object
+                              ssm:
+                                properties:
+                                  accessKeyId:
+                                    properties:
+                                      value:
+                                        type: string
+                                      valueFrom:
+                                        properties:
+                                          secretKeyRef:
+                                            properties:
+                                              key:
+                                                type: string
+                                              name:
+                                                type: string
+                                            required:
+                                            - key
+                                            - name
+                                            type: object
+                                        type: object
+                                    required:
+                                    - value
+                                    type: object
+                                  name:
+                                    type: string
+                                  region:
+                                    type: string
+                                  secretAccessKey:
+                                    properties:
+                                      value:
+                                        type: string
+                                      valueFrom:
+                                        properties:
+                                          secretKeyRef:
+                                            properties:
+                                              key:
+                                                type: string
+                                              name:
+                                                type: string
+                                            required:
+                                            - key
+                                            - name
+                                            type: object
+                                        type: object
+                                    required:
+                                    - value
+                                    type: object
+                                  withDecryption:
+                                    type: boolean
+                                required:
+                                - name
+                                type: object
+                              vault:
+                                properties:
+                                  agentInject:
+                                    type: boolean
+                                  endpoint:
+                                    type: string
+                                  role:
+                                    type: string
+                                  secret:
+                                    type: string
+                                  serviceAccount:
+                                    type: string
+                                  serviceAccountNamespace:
+                                    type: string
+                                required:
+                                - role
+                                - secret
+                                type: object
+                            type: object
+                        type: object
+                    required:
+                    - hosts
+                    - keyspace
+                    type: object
                   cockroachdb:
                     properties:
                       dbname:
diff --git a/config/crds/v1/schemas.schemahero.io_datatypes.yaml b/config/crds/v1/schemas.schemahero.io_datatypes.yaml
new file mode 100644
index 00000000..a8474ada
--- /dev/null
+++ b/config/crds/v1/schemas.schemahero.io_datatypes.yaml
@@ -0,0 +1,78 @@
+
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+  annotations:
+    controller-gen.kubebuilder.io/version: v0.2.8
+  creationTimestamp: null
+  name: datatypes.schemas.schemahero.io
+spec:
+  group: schemas.schemahero.io
+  names:
+    kind: DataType
+    listKind: DataTypeList
+    plural: datatypes
+    singular: datatype
+  scope: Namespaced
+  versions:
+  - name: v1alpha4
+    schema:
+      openAPIV3Schema:
+        description: DataType is the Schema for the datatypes API
+        properties:
+          apiVersion:
+            description: 'APIVersion defines the versioned schema of this representation
+              of an object. Servers should convert recognized schemas to the latest
+              internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+            type: string
+          kind:
+            description: 'Kind is a string value representing the REST resource this
+              object represents. Servers may infer this from the endpoint the client
+              submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+            type: string
+          metadata:
+            type: object
+          spec:
+            description: DataTypeSpec defines the desired state of Type
+            properties:
+              database:
+                type: string
+              name:
+                type: string
+              schema:
+                properties:
+                  cassandra:
+                    properties:
+                      fields:
+                        items:
+                          properties:
+                            name:
+                              type: string
+                            type:
+                              type: string
+                          required:
+                          - name
+                          - type
+                          type: object
+                        type: array
+                      isDeleted:
+                        type: boolean
+                    type: object
+                type: object
+            required:
+            - database
+            - name
+            type: object
+          status:
+            description: DataTypeStatus defines the observed state of Type
+            type: object
+        type: object
+    served: true
+    storage: true
+status:
+  acceptedNames:
+    kind: ""
+    plural: ""
+  conditions: []
+  storedVersions: []
diff --git a/config/crds/v1/schemas.schemahero.io_tables.yaml b/config/crds/v1/schemas.schemahero.io_tables.yaml
index ecfe529c..7ae14595 100644
--- a/config/crds/v1/schemas.schemahero.io_tables.yaml
+++ b/config/crds/v1/schemas.schemahero.io_tables.yaml
@@ -46,6 +46,73 @@ spec:
                 type: array
               schema:
                 properties:
+                  cassandra:
+                    properties:
+                      clusteringOrder:
+                        properties:
+                          column:
+                            type: string
+                          isDescending:
+                            type: boolean
+                        required:
+                        - column
+                        type: object
+                      columns:
+                        items:
+                          properties:
+                            isStatic:
+                              type: boolean
+                            name:
+                              type: string
+                            type:
+                              type: string
+                          required:
+                          - name
+                          - type
+                          type: object
+                        type: array
+                      isDeleted:
+                        type: boolean
+                      primaryKey:
+                        items:
+                          items:
+                            type: string
+                          type: array
+                        type: array
+                      properties:
+                        properties:
+                          bloomFilterFPChance:
+                            type: string
+                          caching:
+                            additionalProperties:
+                              type: string
+                            type: object
+                          comment:
+                            type: string
+                          compaction:
+                            type: string
+                          compression:
+                            type: string
+                          crcCheckChance:
+                            type: string
+                          dcLocalReadRepairChance:
+                            type: string
+                          defaultTTL:
+                            type: integer
+                          gcGraceSeconds:
+                            type: integer
+                          maxIndexInterval:
+                            type: integer
+                          memtableFlushPeriodMs:
+                            type: integer
+                          minIndexInterval:
+                            type: integer
+                          readRepairChance:
+                            type: string
+                          speculativeRetry:
+                            type: string
+                        type: object
+                    type: object
                   cockroachdb:
                     properties:
                       columns:
diff --git a/config/crds/v1beta1/databases.schemahero.io_databases.yaml b/config/crds/v1beta1/databases.schemahero.io_databases.yaml
index 2917f603..7786b85e 100644
--- a/config/crds/v1beta1/databases.schemahero.io_databases.yaml
+++ b/config/crds/v1beta1/databases.schemahero.io_databases.yaml
@@ -39,6 +39,277 @@ spec:
               description: DatabaseConnection defines connection parameters for the
                 database driver
               properties:
+                cassandra:
+                  properties:
+                    hosts:
+                      items:
+                        type: string
+                      type: array
+                    keyspace:
+                      properties:
+                        value:
+                          type: string
+                        valueFrom:
+                          properties:
+                            secretKeyRef:
+                              properties:
+                                key:
+                                  type: string
+                                name:
+                                  type: string
+                              required:
+                              - key
+                              - name
+                              type: object
+                            ssm:
+                              properties:
+                                accessKeyId:
+                                  properties:
+                                    value:
+                                      type: string
+                                    valueFrom:
+                                      properties:
+                                        secretKeyRef:
+                                          properties:
+                                            key:
+                                              type: string
+                                            name:
+                                              type: string
+                                          required:
+                                          - key
+                                          - name
+                                          type: object
+                                      type: object
+                                  required:
+                                  - value
+                                  type: object
+                                name:
+                                  type: string
+                                region:
+                                  type: string
+                                secretAccessKey:
+                                  properties:
+                                    value:
+                                      type: string
+                                    valueFrom:
+                                      properties:
+                                        secretKeyRef:
+                                          properties:
+                                            key:
+                                              type: string
+                                            name:
+                                              type: string
+                                          required:
+                                          - key
+                                          - name
+                                          type: object
+                                      type: object
+                                  required:
+                                  - value
+                                  type: object
+                                withDecryption:
+                                  type: boolean
+                              required:
+                              - name
+                              type: object
+                            vault:
+                              properties:
+                                agentInject:
+                                  type: boolean
+                                endpoint:
+                                  type: string
+                                role:
+                                  type: string
+                                secret:
+                                  type: string
+                                serviceAccount:
+                                  type: string
+                                serviceAccountNamespace:
+                                  type: string
+                              required:
+                              - role
+                              - secret
+                              type: object
+                          type: object
+                      type: object
+                    password:
+                      properties:
+                        value:
+                          type: string
+                        valueFrom:
+                          properties:
+                            secretKeyRef:
+                              properties:
+                                key:
+                                  type: string
+                                name:
+                                  type: string
+                              required:
+                              - key
+                              - name
+                              type: object
+                            ssm:
+                              properties:
+                                accessKeyId:
+                                  properties:
+                                    value:
+                                      type: string
+                                    valueFrom:
+                                      properties:
+                                        secretKeyRef:
+                                          properties:
+                                            key:
+                                              type: string
+                                            name:
+                                              type: string
+                                          required:
+                                          - key
+                                          - name
+                                          type: object
+                                      type: object
+                                  required:
+                                  - value
+                                  type: object
+                                name:
+                                  type: string
+                                region:
+                                  type: string
+                                secretAccessKey:
+                                  properties:
+                                    value:
+                                      type: string
+                                    valueFrom:
+                                      properties:
+                                        secretKeyRef:
+                                          properties:
+                                            key:
+                                              type: string
+                                            name:
+                                              type: string
+                                          required:
+                                          - key
+                                          - name
+                                          type: object
+                                      type: object
+                                  required:
+                                  - value
+                                  type: object
+                                withDecryption:
+                                  type: boolean
+                              required:
+                              - name
+                              type: object
+                            vault:
+                              properties:
+                                agentInject:
+                                  type: boolean
+                                endpoint:
+                                  type: string
+                                role:
+                                  type: string
+                                secret:
+                                  type: string
+                                serviceAccount:
+                                  type: string
+                                serviceAccountNamespace:
+                                  type: string
+                              required:
+                              - role
+                              - secret
+                              type: object
+                          type: object
+                      type: object
+                    username:
+                      properties:
+                        value:
+                          type: string
+                        valueFrom:
+                          properties:
+                            secretKeyRef:
+                              properties:
+                                key:
+                                  type: string
+                                name:
+                                  type: string
+                              required:
+                              - key
+                              - name
+                              type: object
+                            ssm:
+                              properties:
+                                accessKeyId:
+                                  properties:
+                                    value:
+                                      type: string
+                                    valueFrom:
+                                      properties:
+                                        secretKeyRef:
+                                          properties:
+                                            key:
+                                              type: string
+                                            name:
+                                              type: string
+                                          required:
+                                          - key
+                                          - name
+                                          type: object
+                                      type: object
+                                  required:
+                                  - value
+                                  type: object
+                                name:
+                                  type: string
+                                region:
+                                  type: string
+                                secretAccessKey:
+                                  properties:
+                                    value:
+                                      type: string
+                                    valueFrom:
+                                      properties:
+                                        secretKeyRef:
+                                          properties:
+                                            key:
+                                              type: string
+                                            name:
+                                              type: string
+                                          required:
+                                          - key
+                                          - name
+                                          type: object
+                                      type: object
+                                  required:
+                                  - value
+                                  type: object
+                                withDecryption:
+                                  type: boolean
+                              required:
+                              - name
+                              type: object
+                            vault:
+                              properties:
+                                agentInject:
+                                  type: boolean
+                                endpoint:
+                                  type: string
+                                role:
+                                  type: string
+                                secret:
+                                  type: string
+                                serviceAccount:
+                                  type: string
+                                serviceAccountNamespace:
+                                  type: string
+                              required:
+                              - role
+                              - secret
+                              type: object
+                          type: object
+                      type: object
+                  required:
+                  - hosts
+                  - keyspace
+                  type: object
                 cockroachdb:
                   properties:
                     dbname:
diff --git a/config/crds/v1beta1/schemas.schemahero.io_datatypes.yaml b/config/crds/v1beta1/schemas.schemahero.io_datatypes.yaml
new file mode 100644
index 00000000..912d0775
--- /dev/null
+++ b/config/crds/v1beta1/schemas.schemahero.io_datatypes.yaml
@@ -0,0 +1,79 @@
+
+---
+apiVersion: apiextensions.k8s.io/v1beta1
+kind: CustomResourceDefinition
+metadata:
+  annotations:
+    controller-gen.kubebuilder.io/version: v0.2.8
+  creationTimestamp: null
+  name: datatypes.schemas.schemahero.io
+spec:
+  group: schemas.schemahero.io
+  names:
+    kind: DataType
+    listKind: DataTypeList
+    plural: datatypes
+    singular: datatype
+  scope: Namespaced
+  validation:
+    openAPIV3Schema:
+      description: DataType is the Schema for the datatypes API
+      properties:
+        apiVersion:
+          description: 'APIVersion defines the versioned schema of this representation
+            of an object. Servers should convert recognized schemas to the latest
+            internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+          type: string
+        kind:
+          description: 'Kind is a string value representing the REST resource this
+            object represents. Servers may infer this from the endpoint the client
+            submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+          type: string
+        metadata:
+          type: object
+        spec:
+          description: DataTypeSpec defines the desired state of Type
+          properties:
+            database:
+              type: string
+            name:
+              type: string
+            schema:
+              properties:
+                cassandra:
+                  properties:
+                    fields:
+                      items:
+                        properties:
+                          name:
+                            type: string
+                          type:
+                            type: string
+                        required:
+                        - name
+                        - type
+                        type: object
+                      type: array
+                    isDeleted:
+                      type: boolean
+                  type: object
+              type: object
+          required:
+          - database
+          - name
+          type: object
+        status:
+          description: DataTypeStatus defines the observed state of Type
+          type: object
+      type: object
+  version: v1alpha4
+  versions:
+  - name: v1alpha4
+    served: true
+    storage: true
+status:
+  acceptedNames:
+    kind: ""
+    plural: ""
+  conditions: []
+  storedVersions: []
diff --git a/config/crds/v1beta1/schemas.schemahero.io_tables.yaml b/config/crds/v1beta1/schemas.schemahero.io_tables.yaml
index 554e6ab6..dd723146 100644
--- a/config/crds/v1beta1/schemas.schemahero.io_tables.yaml
+++ b/config/crds/v1beta1/schemas.schemahero.io_tables.yaml
@@ -44,6 +44,73 @@ spec:
               type: array
             schema:
               properties:
+                cassandra:
+                  properties:
+                    clusteringOrder:
+                      properties:
+                        column:
+                          type: string
+                        isDescending:
+                          type: boolean
+                      required:
+                      - column
+                      type: object
+                    columns:
+                      items:
+                        properties:
+                          isStatic:
+                            type: boolean
+                          name:
+                            type: string
+                          type:
+                            type: string
+                        required:
+                        - name
+                        - type
+                        type: object
+                      type: array
+                    isDeleted:
+                      type: boolean
+                    primaryKey:
+                      items:
+                        items:
+                          type: string
+                        type: array
+                      type: array
+                    properties:
+                      properties:
+                        bloomFilterFPChance:
+                          type: string
+                        caching:
+                          additionalProperties:
+                            type: string
+                          type: object
+                        comment:
+                          type: string
+                        compaction:
+                          type: string
+                        compression:
+                          type: string
+                        crcCheckChance:
+                          type: string
+                        dcLocalReadRepairChance:
+                          type: string
+                        defaultTTL:
+                          type: integer
+                        gcGraceSeconds:
+                          type: integer
+                        maxIndexInterval:
+                          type: integer
+                        memtableFlushPeriodMs:
+                          type: integer
+                        minIndexInterval:
+                          type: integer
+                        readRepairChance:
+                          type: string
+                        speculativeRetry:
+                          type: string
+                      type: object
+                  type: object
                 cockroachdb:
                   properties:
                     columns:
diff --git a/go.mod b/go.mod
index a86d4655..2b45f077 100644
--- a/go.mod
+++ b/go.mod
@@ -6,6 +6,7 @@ require (
 	github.com/aws/aws-sdk-go-v2 v0.23.0
 	github.com/blang/semver v3.5.1+incompatible
 	github.com/go-sql-driver/mysql v1.5.0
+	github.com/gocql/gocql v0.0.0-20200815110948-5378c8f664e9
 	github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
 	github.com/hashicorp/golang-lru v0.5.3 // indirect
 	github.com/jackc/pgx/v4 v4.8.1
diff --git a/go.sum b/go.sum
index c5925fcd..624cd9ab 100644
--- a/go.sum
+++ b/go.sum
@@ -37,9 +37,11 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
 github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
 github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
+github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k=
 github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
 github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
 github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
+github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
 github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
@@ -153,6 +155,8 @@ github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB
 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
 github.com/gobuffalo/flect v0.2.0 h1:EWCvMGGxOjsgwlWaP+f4+Hh6yrrte7JeFL2S6b+0hdM=
 github.com/gobuffalo/flect v0.2.0/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80=
+github.com/gocql/gocql v0.0.0-20200815110948-5378c8f664e9 h1:SBOCi413wRa7i5ZET6dmeg8iqpKO/hE+buwIZ7WhNg4=
+github.com/gocql/gocql v0.0.0-20200815110948-5378c8f664e9/go.mod h1:DL0ekTmBSTdlNF25Orwt/JMzqIq3EJ4MVa/J/uK64OY=
 github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE=
 github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
 github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
@@ -171,6 +175,8 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
 github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
 github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/snappy v0.0.0-20170215233205-553a64147049 h1:K9KHZbXKpGydfDN0aZrsoHpLJlZsBrGMFWbgLDGnPZk=
+github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
 github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
@@ -206,6 +212,8 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de
 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
 github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
 github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
+github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8=
+github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
 github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk=
diff --git a/pkg/client/schemaheroclientset/typed/schemas/v1alpha3/doc.go b/pkg/apis/databases/v1alpha4/cassandra_types.go
similarity index 67%
rename from pkg/client/schemaheroclientset/typed/schemas/v1alpha3/doc.go
rename to pkg/apis/databases/v1alpha4/cassandra_types.go
index 6db43c0d..4d90b691 100644
--- a/pkg/client/schemaheroclientset/typed/schemas/v1alpha3/doc.go
+++ b/pkg/apis/databases/v1alpha4/cassandra_types.go
@@ -1,5 +1,5 @@
 /*
-Copyright 2020 Replicated, Inc.
+Copyright 2019 Replicated, Inc.
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
@@ -14,7 +14,11 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-// Code generated by client-gen. DO NOT EDIT.
-
-// This package has the automatically generated typed clients.
 package v1alpha4
+
+type CassandraConnection struct {
+	Hosts    []string         `json:"hosts"`
+	Keyspace ValueOrValueFrom `json:"keyspace"`
+	Username ValueOrValueFrom `json:"username,omitempty"`
+	Password ValueOrValueFrom `json:"password,omitempty"`
+}
diff --git a/pkg/apis/databases/v1alpha4/connection.go b/pkg/apis/databases/v1alpha4/connection.go
index 29300efe..e6866eaf 100644
--- a/pkg/apis/databases/v1alpha4/connection.go
+++ b/pkg/apis/databases/v1alpha4/connection.go
@@ -41,6 +41,8 @@ func (d Database) GetConnection(ctx context.Context) (string, string, error) {
 		isParamBased = d.Spec.Connection.CockroachDB.URI.IsEmpty()
 	} else if driver == "mysql" {
 		isParamBased = d.Spec.Connection.Mysql.URI.IsEmpty()
+	} else if driver == "cassandra" {
+		isParamBased = true
 	}
 
 	if isParamBased {
@@ -135,6 +137,8 @@ func (d Database) getConnectionFromParams(ctx context.Context) (string, string,
 			}
 			uri = fmt.Sprintf("%s?sslmode=%s", uri, sslMode)
 		}
+	} else if driver == "cassandra" {
+		return "", "", errors.New("not implemented")
 	} else if driver == "mysql" {
 		hostname, err := d.Spec.Connection.Mysql.Host.Read(clientset, d.Namespace)
 		if err != nil {
@@ -184,6 +188,8 @@ func (d Database) getConnectionFromURI(ctx context.Context) (string, string, err
 		valueOrValueFrom = d.Spec.Connection.Postgres.URI
 	} else if driver == "cockroachdb" {
 		valueOrValueFrom = d.Spec.Connection.CockroachDB.URI
+	} else if driver == "cassandra" {
+		return "", "", errors.New("reading cassandra connecting from uri is not supported")
 	} else if driver == "mysql" {
 		valueOrValueFrom = d.Spec.Connection.Mysql.URI
 	}
diff --git a/pkg/apis/databases/v1alpha4/database_types.go b/pkg/apis/databases/v1alpha4/database_types.go
index 39bd5843..a184a9d6 100644
--- a/pkg/apis/databases/v1alpha4/database_types.go
+++ b/pkg/apis/databases/v1alpha4/database_types.go
@@ -25,6 +25,7 @@ type DatabaseConnection struct {
 	Postgres    *PostgresConnection    `json:"postgres,omitempty"`
 	Mysql       *MysqlConnection       `json:"mysql,omitempty"`
 	CockroachDB *CockroachDBConnection `json:"cockroachdb,omitempty"`
+	Cassandra   *CassandraConnection   `json:"cassandra,omitempty"`
 }
 
 type SchemaHero struct {
diff --git a/pkg/apis/databases/v1alpha4/vault.go b/pkg/apis/databases/v1alpha4/vault.go
index fa3f06d6..db946ba9 100644
--- a/pkg/apis/databases/v1alpha4/vault.go
+++ b/pkg/apis/databases/v1alpha4/vault.go
@@ -18,6 +18,8 @@ package v1alpha4
 
 import (
 	"fmt"
+
+	"github.com/pkg/errors"
 )
 
 // UsingVault determines whether the specific Database connection is
@@ -55,12 +57,14 @@ func (d Database) getDbType() (string, error) {
 	if d.Spec.Connection.CockroachDB != nil {
 		return "cockroachdb", nil
 	}
+	if d.Spec.Connection.Cassandra != nil {
+		return "cassandra", nil
+	}
 	if d.Spec.Connection.Postgres != nil {
 		return "postgres", nil
 	}
 	if d.Spec.Connection.Mysql != nil {
 		return "mysql", nil
-
 	}
 	return "", fmt.Errorf("No database connection configured for database: %s", d.Name)
 }
@@ -102,6 +106,8 @@ postgres://{{ .Data.username }}:{{ .Data.password }}@postgres:5432/%s{{- end }}`
 {{ .Data.username }}:{{ .Data.password }}@tcp(mysql:3306)/%s{{- end }}`, v.Role, d.Name)
 
 		annotations["vault.hashicorp.com/agent-inject-template-schemaherouri"] = t
+	case "cassandra":
+		return nil, errors.New("not implemented")
 	}
 
 	annotations["vault.hashicorp.com/agent-inject"] = "true"
diff --git a/pkg/apis/databases/v1alpha4/zz_generated.deepcopy.go b/pkg/apis/databases/v1alpha4/zz_generated.deepcopy.go
index 3d601c8c..0dc8e096 100644
--- a/pkg/apis/databases/v1alpha4/zz_generated.deepcopy.go
+++ b/pkg/apis/databases/v1alpha4/zz_generated.deepcopy.go
@@ -24,6 +24,29 @@ import (
 	runtime "k8s.io/apimachinery/pkg/runtime"
 )
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *CassandraConnection) DeepCopyInto(out *CassandraConnection) {
+	*out = *in
+	if in.Hosts != nil {
+		in, out := &in.Hosts, &out.Hosts
+		*out = make([]string, len(*in))
+		copy(*out, *in)
+	}
+	in.Keyspace.DeepCopyInto(&out.Keyspace)
+	in.Username.DeepCopyInto(&out.Username)
+	in.Password.DeepCopyInto(&out.Password)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CassandraConnection.
+func (in *CassandraConnection) DeepCopy() *CassandraConnection {
+	if in == nil {
+		return nil
+	}
+	out := new(CassandraConnection)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *CockroachDBConnection) DeepCopyInto(out *CockroachDBConnection) {
 	*out = *in
@@ -91,6 +114,11 @@ func (in *DatabaseConnection) DeepCopyInto(out *DatabaseConnection) {
 		*out = new(CockroachDBConnection)
 		(*in).DeepCopyInto(*out)
 	}
+	if in.Cassandra != nil {
+		in, out := &in.Cassandra, &out.Cassandra
+		*out = new(CassandraConnection)
+		(*in).DeepCopyInto(*out)
+	}
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseConnection.
diff --git a/pkg/apis/schemas/v1alpha4/cassandra.go b/pkg/apis/schemas/v1alpha4/cassandra.go
new file mode 100644
index 00000000..35549d50
--- /dev/null
+++ b/pkg/apis/schemas/v1alpha4/cassandra.go
@@ -0,0 +1,64 @@
+/*
+Copyright 2019 Replicated, Inc.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v1alpha4
+
+type CassandraColumn struct {
+	Name     string `json:"name" yaml:"name"`
+	Type     string `json:"type" yaml:"type"`
+	IsStatic *bool  `json:"isStatic,omitempty" yaml:"isStatic,omitempty"`
+}
+
+type CassandraClusteringOrder struct {
+	Column       string `json:"column" yaml:"column"`
+	IsDescending *bool  `json:"isDescending,omitempty" yaml:"isDescending,omitempty"`
+}
+
+type CassandraTableProperties struct {
+	BloomFilterFPChance     string            `json:"bloomFilterFPChance,omitempty" yaml:"bloomFilterFPChance,omitempty"`
+	Caching                 map[string]string `json:"caching,omitempty" yaml:"caching,omitempty"`
+	Comment                 string            `json:"comment,omitempty" yaml:"comment,omitempty"`
+	Compaction              map[string]string `json:"compaction,omitempty" yaml:"compaction,omitempty"`
+	Compression             map[string]string `json:"compression,omitempty" yaml:"compression,omitempty"`
+	CRCCheckChance          string            `json:"crcCheckChance,omitempty" yaml:"crcCheckChance,omitempty"`
+	DCLocalReadRepairChance string            `json:"dcLocalReadRepairChance,omitempty" yaml:"dcLocalReadRepairChance,omitempty"`
+	DefaultTTL              *int              `json:"defaultTTL,omitempty" yaml:"defaultTTL,omitempty"`
+	GCGraceSeconds          *int              `json:"gcGraceSeconds,omitempty" yaml:"gcGraceSeconds,omitempty"`
+	MaxIndexInterval        *int              `json:"maxIndexInterval,omitempty" yaml:"maxIndexInterval,omitempty"`
+	MemtableFlushPeriodMS   *int              `json:"memtableFlushPeriodMs,omitempty" yaml:"memtableFlushPeriodMs,omitempty"`
+	MinIndexInterval        *int              `json:"minIndexInterval,omitempty" yaml:"minIndexInterval,omitempty"`
+	ReadRepairChance        string            `json:"readRepairChance,omitempty" yaml:"readRepairChance,omitempty"`
+	SpeculativeRetry        string            `json:"speculativeRetry,omitempty" yaml:"speculativeRetry,omitempty"`
+}
+
+type CassandraTableSchema struct {
+	IsDeleted       bool                      `json:"isDeleted,omitempty" yaml:"isDeleted,omitempty"`
+	PrimaryKey      [][]string                `json:"primaryKey,omitempty" yaml:"primaryKey,omitempty"`
+	ClusteringOrder *CassandraClusteringOrder `json:"clusteringOrder,omitempty" yaml:"clusteringOrder,omitempty"`
+	Columns         []*CassandraColumn        `json:"columns,omitempty" yaml:"columns,omitempty"`
+
+	Properties *CassandraTableProperties `json:"properties,omitempty" yaml:"properties,omitempty"`
+}
+
+type CassandraField struct {
+	Name string `json:"name" yaml:"name"`
+	Type string `json:"type" yaml:"type"`
+}
+
+type CassandraDataTypeSchema struct {
+	IsDeleted bool              `json:"isDeleted,omitempty" yaml:"isDeleted,omitempty"`
+	Fields    []*CassandraField `json:"fields,omitempty" yaml:"fields,omitempty"`
+}
diff --git a/pkg/apis/schemas/v1alpha4/table_types.go b/pkg/apis/schemas/v1alpha4/table_types.go
index c49dd977..fccebda0 100644
--- a/pkg/apis/schemas/v1alpha4/table_types.go
+++ b/pkg/apis/schemas/v1alpha4/table_types.go
@@ -26,9 +26,10 @@ import (
 )
 
 type TableSchema struct {
-	Postgres    *SQLTableSchema      `json:"postgres,omitempty" yaml:"postgres,omitempty"`
-	Mysql       *MysqlSQLTableSchema `json:"mysql,omitempty" yaml:"mysql,omitempty"`
-	CockroachDB *SQLTableSchema      `json:"cockroachdb,omitempty" yaml:"cockroachdb,omitempty"`
+	Postgres    *SQLTableSchema       `json:"postgres,omitempty" yaml:"postgres,omitempty"`
+	Mysql       *MysqlSQLTableSchema  `json:"mysql,omitempty" yaml:"mysql,omitempty"`
+	CockroachDB *SQLTableSchema       `json:"cockroachdb,omitempty" yaml:"cockroachdb,omitempty"`
+	Cassandra   *CassandraTableSchema `json:"cassandra,omitempty" yaml:"cassandra,omitempty"`
 }
 
 // TableSpec defines the desired state of Table
diff --git a/pkg/apis/schemas/v1alpha4/type_types.go b/pkg/apis/schemas/v1alpha4/type_types.go
new file mode 100644
index 00000000..a249ace8
--- /dev/null
+++ b/pkg/apis/schemas/v1alpha4/type_types.go
@@ -0,0 +1,63 @@
+/*
+Copyright 2019 Replicated, Inc.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v1alpha4
+
+import (
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+type DataTypeSchema struct {
+	Cassandra *CassandraDataTypeSchema `json:"cassandra,omitempty" yaml:"cassandra,omitempty"`
+}
+
+// DataTypeSpec defines the desired state of Type
+type DataTypeSpec struct {
+	Database string `json:"database" yaml:"database"`
+	Name     string `json:"name" yaml:"name"`
+
+	Schema *DataTypeSchema `json:"schema,omitempty" yaml:"schema,omitempty"`
+}
+
+// DataTypeStatus defines the observed state of Type
+type DataTypeStatus struct {
+}
+
+// +genclient
+// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+
+// DataType is the Schema for the datatypes API
+// +k8s:openapi-gen=true
+type DataType struct {
+	metav1.TypeMeta   `json:",inline"`
+	metav1.ObjectMeta `json:"metadata,omitempty"`
+
+	Spec   DataTypeSpec   `json:"spec,omitempty"`
+	Status DataTypeStatus `json:"status,omitempty"`
+}
+
+// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+
+// DataTypeList contains a list of DataType
+type DataTypeList struct {
+	metav1.TypeMeta `json:",inline"`
+	metav1.ListMeta `json:"metadata,omitempty"`
+	Items           []DataType `json:"items"`
+}
+
+func init() {
+	SchemeBuilder.Register(&DataType{}, &DataTypeList{})
+}
diff --git a/pkg/apis/schemas/v1alpha4/zz_generated.deepcopy.go b/pkg/apis/schemas/v1alpha4/zz_generated.deepcopy.go
index cd89ae27..b3e72dfc 100644
--- a/pkg/apis/schemas/v1alpha4/zz_generated.deepcopy.go
+++ b/pkg/apis/schemas/v1alpha4/zz_generated.deepcopy.go
@@ -24,6 +24,295 @@ import (
 	runtime "k8s.io/apimachinery/pkg/runtime"
 )
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *CassandraClusteringOrder) DeepCopyInto(out *CassandraClusteringOrder) {
+	*out = *in
+	if in.IsDescending != nil {
+		in, out := &in.IsDescending, &out.IsDescending
+		*out = new(bool)
+		**out = **in
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CassandraClusteringOrder.
+func (in *CassandraClusteringOrder) DeepCopy() *CassandraClusteringOrder {
+	if in == nil {
+		return nil
+	}
+	out := new(CassandraClusteringOrder)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *CassandraColumn) DeepCopyInto(out *CassandraColumn) {
+	*out = *in
+	if in.IsStatic != nil {
+		in, out := &in.IsStatic, &out.IsStatic
+		*out = new(bool)
+		**out = **in
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CassandraColumn.
+func (in *CassandraColumn) DeepCopy() *CassandraColumn {
+	if in == nil {
+		return nil
+	}
+	out := new(CassandraColumn)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *CassandraDataTypeSchema) DeepCopyInto(out *CassandraDataTypeSchema) {
+	*out = *in
+	if in.Fields != nil {
+		in, out := &in.Fields, &out.Fields
+		*out = make([]*CassandraField, len(*in))
+		for i := range *in {
+			if (*in)[i] != nil {
+				in, out := &(*in)[i], &(*out)[i]
+				*out = new(CassandraField)
+				**out = **in
+			}
+		}
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CassandraDataTypeSchema.
+func (in *CassandraDataTypeSchema) DeepCopy() *CassandraDataTypeSchema {
+	if in == nil {
+		return nil
+	}
+	out := new(CassandraDataTypeSchema)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *CassandraField) DeepCopyInto(out *CassandraField) {
+	*out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CassandraField.
+func (in *CassandraField) DeepCopy() *CassandraField {
+	if in == nil {
+		return nil
+	}
+	out := new(CassandraField)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *CassandraTableProperties) DeepCopyInto(out *CassandraTableProperties) {
+	*out = *in
+	if in.Caching != nil {
+		in, out := &in.Caching, &out.Caching
+		*out = make(map[string]string, len(*in))
+		for key, val := range *in {
+			(*out)[key] = val
+		}
+	}
+	if in.DefaultTTL != nil {
+		in, out := &in.DefaultTTL, &out.DefaultTTL
+		*out = new(int)
+		**out = **in
+	}
+	if in.GCGraceSeconds != nil {
+		in, out := &in.GCGraceSeconds, &out.GCGraceSeconds
+		*out = new(int)
+		**out = **in
+	}
+	if in.MaxIndexInterval != nil {
+		in, out := &in.MaxIndexInterval, &out.MaxIndexInterval
+		*out = new(int)
+		**out = **in
+	}
+	if in.MemtableFlushPeriodMS != nil {
+		in, out := &in.MemtableFlushPeriodMS, &out.MemtableFlushPeriodMS
+		*out = new(int)
+		**out = **in
+	}
+	if in.MinIndexInterval != nil {
+		in, out := &in.MinIndexInterval, &out.MinIndexInterval
+		*out = new(int)
+		**out = **in
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CassandraTableProperties.
+func (in *CassandraTableProperties) DeepCopy() *CassandraTableProperties {
+	if in == nil {
+		return nil
+	}
+	out := new(CassandraTableProperties)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *CassandraTableSchema) DeepCopyInto(out *CassandraTableSchema) {
+	*out = *in
+	if in.PrimaryKey != nil {
+		in, out := &in.PrimaryKey, &out.PrimaryKey
+		*out = make([][]string, len(*in))
+		for i := range *in {
+			if (*in)[i] != nil {
+				in, out := &(*in)[i], &(*out)[i]
+				*out = make([]string, len(*in))
+				copy(*out, *in)
+			}
+		}
+	}
+	if in.ClusteringOrder != nil {
+		in, out := &in.ClusteringOrder, &out.ClusteringOrder
+		*out = new(CassandraClusteringOrder)
+		(*in).DeepCopyInto(*out)
+	}
+	if in.Columns != nil {
+		in, out := &in.Columns, &out.Columns
+		*out = make([]*CassandraColumn, len(*in))
+		for i := range *in {
+			if (*in)[i] != nil {
+				in, out := &(*in)[i], &(*out)[i]
+				*out = new(CassandraColumn)
+				(*in).DeepCopyInto(*out)
+			}
+		}
+	}
+	if in.Properties != nil {
+		in, out := &in.Properties, &out.Properties
+		*out = new(CassandraTableProperties)
+		(*in).DeepCopyInto(*out)
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CassandraTableSchema.
+func (in *CassandraTableSchema) DeepCopy() *CassandraTableSchema {
+	if in == nil {
+		return nil
+	}
+	out := new(CassandraTableSchema)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *DataType) DeepCopyInto(out *DataType) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+	in.Spec.DeepCopyInto(&out.Spec)
+	out.Status = in.Status
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DataType.
+func (in *DataType) DeepCopy() *DataType {
+	if in == nil {
+		return nil
+	}
+	out := new(DataType)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *DataType) DeepCopyObject() runtime.Object {
+	if c := in.DeepCopy(); c != nil {
+		return c
+	}
+	return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *DataTypeList) DeepCopyInto(out *DataTypeList) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	in.ListMeta.DeepCopyInto(&out.ListMeta)
+	if in.Items != nil {
+		in, out := &in.Items, &out.Items
+		*out = make([]DataType, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DataTypeList.
+func (in *DataTypeList) DeepCopy() *DataTypeList {
+	if in == nil {
+		return nil
+	}
+	out := new(DataTypeList)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *DataTypeList) DeepCopyObject() runtime.Object {
+	if c := in.DeepCopy(); c != nil {
+		return c
+	}
+	return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *DataTypeSchema) DeepCopyInto(out *DataTypeSchema) {
+	*out = *in
+	if in.Cassandra != nil {
+		in, out := &in.Cassandra, &out.Cassandra
+		*out = new(CassandraDataTypeSchema)
+		(*in).DeepCopyInto(*out)
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DataTypeSchema.
+func (in *DataTypeSchema) DeepCopy() *DataTypeSchema {
+	if in == nil {
+		return nil
+	}
+	out := new(DataTypeSchema)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *DataTypeSpec) DeepCopyInto(out *DataTypeSpec) {
+	*out = *in
+	if in.Schema != nil {
+		in, out := &in.Schema, &out.Schema
+		*out = new(DataTypeSchema)
+		(*in).DeepCopyInto(*out)
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DataTypeSpec.
+func (in *DataTypeSpec) DeepCopy() *DataTypeSpec {
+	if in == nil {
+		return nil
+	}
+	out := new(DataTypeSpec)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *DataTypeStatus) DeepCopyInto(out *DataTypeStatus) {
+	*out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DataTypeStatus.
+func (in *DataTypeStatus) DeepCopy() *DataTypeStatus {
+	if in == nil {
+		return nil
+	}
+	out := new(DataTypeStatus)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *Migration) DeepCopyInto(out *Migration) {
 	*out = *in
@@ -457,6 +746,11 @@ func (in *TableSchema) DeepCopyInto(out *TableSchema) {
 		*out = new(SQLTableSchema)
 		(*in).DeepCopyInto(*out)
 	}
+	if in.Cassandra != nil {
+		in, out := &in.Cassandra, &out.Cassandra
+		*out = new(CassandraTableSchema)
+		(*in).DeepCopyInto(*out)
+	}
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TableSchema.
diff --git a/pkg/cli/schemaherokubectlcli/apply.go b/pkg/cli/schemaherokubectlcli/apply.go
index 82a77a7f..5ba1f205 100644
--- a/pkg/cli/schemaherokubectlcli/apply.go
+++ b/pkg/cli/schemaherokubectlcli/apply.go
@@ -26,8 +26,9 @@ func ApplyCmd() *cobra.Command {
 			driver := v.GetString("driver")
 			ddl := v.GetString("ddl")
 			uri := v.GetString("uri")
+			host := v.GetStringSlice("host")
 
-			if driver == "" || ddl == "" || uri == "" {
+			if driver == "" || ddl == "" || uri == "" || len(host) == 0 {
 				missing := []string{}
 				if driver == "" {
 					missing = append(missing, "driver")
@@ -35,11 +36,15 @@ func ApplyCmd() *cobra.Command {
 				if ddl == "" {
 					missing = append(missing, "ddl")
 				}
-				if uri == "" {
-					missing = append(missing, "uri")
+
+				// one of uri or host/port must be specified
+				if uri == "" && len(host) == 0 {
+					missing = append(missing, "uri or host(s)")
 				}
 
-				return fmt.Errorf("missing required params: %v", missing)
+				if len(missing) > 0 {
+					return fmt.Errorf("missing required params: %v", missing)
+				}
 			}
 
 			fi, err := os.Stat(v.GetString("ddl"))
@@ -52,6 +57,10 @@ func ApplyCmd() *cobra.Command {
 				OutputDir: v.GetString("output-dir"),
 				Driver:    v.GetString("driver"),
 				URI:       v.GetString("uri"),
+				Hosts:     v.GetStringSlice("host"),
+				Username:  v.GetString("username"),
+				Password:  v.GetString("password"),
+				Keyspace:  v.GetString("keyspace"),
 			}
 
 			if fi.Mode().IsDir() {
@@ -115,7 +124,14 @@ func ApplyCmd() *cobra.Command {
 	}
 
 	cmd.Flags().String("driver", "", "name of the database driver to use")
+
 	cmd.Flags().String("uri", "", "connection string uri to use")
+
+	cmd.Flags().String("username", "", "username to use when connecting")
+	cmd.Flags().String("password", "", "password to use when connecting")
+	cmd.Flags().StringSlice("host", []string{}, "hostname to use when connecting")
+	cmd.Flags().String("keyspace", "", "the keyspace to use for databases that support keyspaces")
+
 	cmd.Flags().String("ddl", "", "filename or directory name containing the rendered DDL commands to execute")
 
 	return cmd
diff --git a/pkg/cli/schemaherokubectlcli/plan.go b/pkg/cli/schemaherokubectlcli/plan.go
index e0d80ca1..eb8353f2 100644
--- a/pkg/cli/schemaherokubectlcli/plan.go
+++ b/pkg/cli/schemaherokubectlcli/plan.go
@@ -27,8 +27,9 @@ func PlanCmd() *cobra.Command {
 			driver := v.GetString("driver")
 			specFile := v.GetString("spec-file")
 			uri := v.GetString("uri")
+			host := v.GetStringSlice("host")
 
-			if driver == "" || specFile == "" || uri == "" {
+			if driver == "" || specFile == "" || uri == "" || len(host) == 0 {
 				missing := []string{}
 				if driver == "" {
 					missing = append(missing, "driver")
@@ -36,11 +37,15 @@ func PlanCmd() *cobra.Command {
 				if specFile == "" {
 					missing = append(missing, "spec-file")
 				}
-				if uri == "" {
-					missing = append(missing, "uri")
+
+				// one of uri or host/port must be specified
+				if uri == "" && len(host) == 0 {
+					missing = append(missing, "uri or host(s)")
 				}
 
-				return fmt.Errorf("missing required params: %v", missing)
+				if len(missing) > 0 {
+					return fmt.Errorf("missing required params: %v", missing)
+				}
 			}
 
 			fi, err := os.Stat(v.GetString("spec-file"))
@@ -73,12 +78,16 @@ func PlanCmd() *cobra.Command {
 				OutputDir: v.GetString("output-dir"),
 				Driver:    v.GetString("driver"),
 				URI:       v.GetString("uri"),
+				Hosts:     v.GetStringSlice("host"),
+				Username:  v.GetString("username"),
+				Password:  v.GetString("password"),
+				Keyspace:  v.GetString("keyspace"),
 			}
 
 			if fi.Mode().IsDir() {
 				err := filepath.Walk(v.GetString("spec-file"), func(path string, info os.FileInfo, err error) error {
 					if !info.IsDir() {
-						statements, err := db.PlanSyncFromFile(path)
+						statements, err := db.PlanSyncFromFile(path, v.GetString("spec-type"))
 						if err != nil {
 							return err
 						}
@@ -101,7 +110,7 @@ func PlanCmd() *cobra.Command {
 
 				return err
 			} else {
-				statements, err := db.PlanSyncFromFile(v.GetString("spec-file"))
+				statements, err := db.PlanSyncFromFile(v.GetString("spec-file"), v.GetString("spec-type"))
 				if err != nil {
 					return err
 				}
@@ -124,8 +133,16 @@ func PlanCmd() *cobra.Command {
 	}
 
 	cmd.Flags().String("driver", "", "name of the database driver to use")
+
 	cmd.Flags().String("uri", "", "connection string uri to use")
+
+	cmd.Flags().String("username", "", "username to use when connecting")
+	cmd.Flags().String("password", "", "password to use when connecting")
+	cmd.Flags().StringSlice("host", []string{}, "hostname to use when connecting")
+	cmd.Flags().String("keyspace", "", "the keyspace to use for databases that support keyspaces")
+
 	cmd.Flags().String("spec-file", "", "filename or directory name containing the spec(s) to apply")
+	cmd.Flags().String("spec-type", "table", "type of spec in spec-file")
 	cmd.Flags().String("out", "", "filename to write DDL statements to, if not present output file be written to stdout")
 	cmd.Flags().Bool("overwrite", true, "when set, will overwrite the out file, if it already exists")
 
diff --git a/pkg/client/schemaheroclientset/typed/databases/v1alpha3/databases_client.go b/pkg/client/schemaheroclientset/typed/databases/v1alpha3/databases_client.go
deleted file mode 100644
index f52d26fd..00000000
--- a/pkg/client/schemaheroclientset/typed/databases/v1alpha3/databases_client.go
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
-Copyright 2020 Replicated, Inc.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-// Code generated by client-gen. DO NOT EDIT.
-
-package v1alpha4
-
-import (
-	v1alpha4 "github.com/schemahero/schemahero/pkg/apis/databases/v1alpha4"
-	"github.com/schemahero/schemahero/pkg/client/schemaheroclientset/scheme"
-	rest "k8s.io/client-go/rest"
-)
-
-type Databasesv1alpha4Interface interface {
-	RESTClient() rest.Interface
-	DatabasesGetter
-}
-
-// Databasesv1alpha4Client is used to interact with features provided by the databases.schemahero.io group.
-type Databasesv1alpha4Client struct {
-	restClient rest.Interface
-}
-
-func (c *Databasesv1alpha4Client) Databases(namespace string) DatabaseInterface {
-	return newDatabases(c, namespace)
-}
-
-// NewForConfig creates a new Databasesv1alpha4Client for the given config.
-func NewForConfig(c *rest.Config) (*Databasesv1alpha4Client, error) {
-	config := *c
-	if err := setConfigDefaults(&config); err != nil {
-		return nil, err
-	}
-	client, err := rest.RESTClientFor(&config)
-	if err != nil {
-		return nil, err
-	}
-	return &Databasesv1alpha4Client{client}, nil
-}
-
-// NewForConfigOrDie creates a new Databasesv1alpha4Client for the given config and
-// panics if there is an error in the config.
-func NewForConfigOrDie(c *rest.Config) *Databasesv1alpha4Client {
-	client, err := NewForConfig(c)
-	if err != nil {
-		panic(err)
-	}
-	return client
-}
-
-// New creates a new Databasesv1alpha4Client for the given RESTClient.
-func New(c rest.Interface) *Databasesv1alpha4Client {
-	return &Databasesv1alpha4Client{c}
-}
-
-func setConfigDefaults(config *rest.Config) error {
-	gv := v1alpha4.SchemeGroupVersion
-	config.GroupVersion = &gv
-	config.APIPath = "/apis"
-	config.NegotiatedSerializer = scheme.Codecs.WithoutConversion()
-
-	if config.UserAgent == "" {
-		config.UserAgent = rest.DefaultKubernetesUserAgent()
-	}
-
-	return nil
-}
-
-// RESTClient returns a RESTClient that is used to communicate
-// with API server by this client implementation.
-func (c *Databasesv1alpha4Client) RESTClient() rest.Interface {
-	if c == nil {
-		return nil
-	}
-	return c.restClient
-}
diff --git a/pkg/client/schemaheroclientset/typed/databases/v1alpha3/doc.go b/pkg/client/schemaheroclientset/typed/databases/v1alpha3/doc.go
deleted file mode 100644
index 6db43c0d..00000000
--- a/pkg/client/schemaheroclientset/typed/databases/v1alpha3/doc.go
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
-Copyright 2020 Replicated, Inc.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-// Code generated by client-gen. DO NOT EDIT.
-
-// This package has the automatically generated typed clients.
-package v1alpha4
diff --git a/pkg/client/schemaheroclientset/typed/databases/v1alpha3/fake/doc.go b/pkg/client/schemaheroclientset/typed/databases/v1alpha3/fake/doc.go
deleted file mode 100644
index 083db25e..00000000
--- a/pkg/client/schemaheroclientset/typed/databases/v1alpha3/fake/doc.go
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
-Copyright 2020 Replicated, Inc.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-// Code generated by client-gen. DO NOT EDIT.
-
-// Package fake has the automatically generated clients.
-package fake
diff --git a/pkg/client/schemaheroclientset/typed/databases/v1alpha3/fake/fake_database.go b/pkg/client/schemaheroclientset/typed/databases/v1alpha3/fake/fake_database.go
deleted file mode 100644
index 85178f6f..00000000
--- a/pkg/client/schemaheroclientset/typed/databases/v1alpha3/fake/fake_database.go
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
-Copyright 2020 Replicated, Inc.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-// Code generated by client-gen. DO NOT EDIT.
-
-package fake
-
-import (
-	"context"
-
-	v1alpha4 "github.com/schemahero/schemahero/pkg/apis/databases/v1alpha4"
-	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	labels "k8s.io/apimachinery/pkg/labels"
-	schema "k8s.io/apimachinery/pkg/runtime/schema"
-	types "k8s.io/apimachinery/pkg/types"
-	watch "k8s.io/apimachinery/pkg/watch"
-	testing "k8s.io/client-go/testing"
-)
-
-// FakeDatabases implements DatabaseInterface
-type FakeDatabases struct {
-	Fake *FakeDatabasesv1alpha4
-	ns   string
-}
-
-var databasesResource = schema.GroupVersionResource{Group: "databases.schemahero.io", Version: "v1alpha4", Resource: "databases"}
-
-var databasesKind = schema.GroupVersionKind{Group: "databases.schemahero.io", Version: "v1alpha4", Kind: "Database"}
-
-// Get takes name of the database, and returns the corresponding database object, and an error if there is any.
-func (c *FakeDatabases) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha4.Database, err error) {
-	obj, err := c.Fake.
-		Invokes(testing.NewGetAction(databasesResource, c.ns, name), &v1alpha4.Database{})
-
-	if obj == nil {
-		return nil, err
-	}
-	return obj.(*v1alpha4.Database), err
-}
-
-// List takes label and field selectors, and returns the list of Databases that match those selectors.
-func (c *FakeDatabases) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha4.DatabaseList, err error) {
-	obj, err := c.Fake.
-		Invokes(testing.NewListAction(databasesResource, databasesKind, c.ns, opts), &v1alpha4.DatabaseList{})
-
-	if obj == nil {
-		return nil, err
-	}
-
-	label, _, _ := testing.ExtractFromListOptions(opts)
-	if label == nil {
-		label = labels.Everything()
-	}
-	list := &v1alpha4.DatabaseList{ListMeta: obj.(*v1alpha4.DatabaseList).ListMeta}
-	for _, item := range obj.(*v1alpha4.DatabaseList).Items {
-		if label.Matches(labels.Set(item.Labels)) {
-			list.Items = append(list.Items, item)
-		}
-	}
-	return list, err
-}
-
-// Watch returns a watch.Interface that watches the requested databases.
-func (c *FakeDatabases) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
-	return c.Fake.
-		InvokesWatch(testing.NewWatchAction(databasesResource, c.ns, opts))
-
-}
-
-// Create takes the representation of a database and creates it.  Returns the server's representation of the database, and an error, if there is any.
-func (c *FakeDatabases) Create(ctx context.Context, database *v1alpha4.Database, opts v1.CreateOptions) (result *v1alpha4.Database, err error) {
-	obj, err := c.Fake.
-		Invokes(testing.NewCreateAction(databasesResource, c.ns, database), &v1alpha4.Database{})
-
-	if obj == nil {
-		return nil, err
-	}
-	return obj.(*v1alpha4.Database), err
-}
-
-// Update takes the representation of a database and updates it. Returns the server's representation of the database, and an error, if there is any.
-func (c *FakeDatabases) Update(ctx context.Context, database *v1alpha4.Database, opts v1.UpdateOptions) (result *v1alpha4.Database, err error) {
-	obj, err := c.Fake.
-		Invokes(testing.NewUpdateAction(databasesResource, c.ns, database), &v1alpha4.Database{})
-
-	if obj == nil {
-		return nil, err
-	}
-	return obj.(*v1alpha4.Database), err
-}
-
-// UpdateStatus was generated because the type contains a Status member.
-// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
-func (c *FakeDatabases) UpdateStatus(ctx context.Context, database *v1alpha4.Database, opts v1.UpdateOptions) (*v1alpha4.Database, error) {
-	obj, err := c.Fake.
-		Invokes(testing.NewUpdateSubresourceAction(databasesResource, "status", c.ns, database), &v1alpha4.Database{})
-
-	if obj == nil {
-		return nil, err
-	}
-	return obj.(*v1alpha4.Database), err
-}
-
-// Delete takes name of the database and deletes it. Returns an error if one occurs.
-func (c *FakeDatabases) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
-	_, err := c.Fake.
-		Invokes(testing.NewDeleteAction(databasesResource, c.ns, name), &v1alpha4.Database{})
-
-	return err
-}
-
-// DeleteCollection deletes a collection of objects.
-func (c *FakeDatabases) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
-	action := testing.NewDeleteCollectionAction(databasesResource, c.ns, listOpts)
-
-	_, err := c.Fake.Invokes(action, &v1alpha4.DatabaseList{})
-	return err
-}
-
-// Patch applies the patch and returns the patched database.
-func (c *FakeDatabases) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha4.Database, err error) {
-	obj, err := c.Fake.
-		Invokes(testing.NewPatchSubresourceAction(databasesResource, c.ns, name, pt, data, subresources...), &v1alpha4.Database{})
-
-	if obj == nil {
-		return nil, err
-	}
-	return obj.(*v1alpha4.Database), err
-}
diff --git a/pkg/client/schemaheroclientset/typed/databases/v1alpha3/fake/fake_databases_client.go b/pkg/client/schemaheroclientset/typed/databases/v1alpha3/fake/fake_databases_client.go
deleted file mode 100644
index 1422f394..00000000
--- a/pkg/client/schemaheroclientset/typed/databases/v1alpha3/fake/fake_databases_client.go
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
-Copyright 2020 Replicated, Inc.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-// Code generated by client-gen. DO NOT EDIT.
-
-package fake
-
-import (
-	v1alpha4 "github.com/schemahero/schemahero/pkg/client/schemaheroclientset/typed/databases/v1alpha4"
-	rest "k8s.io/client-go/rest"
-	testing "k8s.io/client-go/testing"
-)
-
-type FakeDatabasesv1alpha4 struct {
-	*testing.Fake
-}
-
-func (c *FakeDatabasesv1alpha4) Databases(namespace string) v1alpha4.DatabaseInterface {
-	return &FakeDatabases{c, namespace}
-}
-
-// RESTClient returns a RESTClient that is used to communicate
-// with API server by this client implementation.
-func (c *FakeDatabasesv1alpha4) RESTClient() rest.Interface {
-	var ret *rest.RESTClient
-	return ret
-}
diff --git a/pkg/client/schemaheroclientset/typed/databases/v1alpha3/generated_expansion.go b/pkg/client/schemaheroclientset/typed/databases/v1alpha3/generated_expansion.go
deleted file mode 100644
index 8d3525ed..00000000
--- a/pkg/client/schemaheroclientset/typed/databases/v1alpha3/generated_expansion.go
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
-Copyright 2020 Replicated, Inc.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-// Code generated by client-gen. DO NOT EDIT.
-
-package v1alpha4
-
-type DatabaseExpansion interface{}
diff --git a/pkg/client/schemaheroclientset/typed/schemas/v1alpha3/fake/doc.go b/pkg/client/schemaheroclientset/typed/schemas/v1alpha3/fake/doc.go
deleted file mode 100644
index 083db25e..00000000
--- a/pkg/client/schemaheroclientset/typed/schemas/v1alpha3/fake/doc.go
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
-Copyright 2020 Replicated, Inc.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-// Code generated by client-gen. DO NOT EDIT.
-
-// Package fake has the automatically generated clients.
-package fake
diff --git a/pkg/client/schemaheroclientset/typed/schemas/v1alpha3/fake/fake_migration.go b/pkg/client/schemaheroclientset/typed/schemas/v1alpha3/fake/fake_migration.go
deleted file mode 100644
index be760133..00000000
--- a/pkg/client/schemaheroclientset/typed/schemas/v1alpha3/fake/fake_migration.go
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
-Copyright 2020 Replicated, Inc.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-// Code generated by client-gen. DO NOT EDIT.
-
-package fake
-
-import (
-	"context"
-
-	v1alpha4 "github.com/schemahero/schemahero/pkg/apis/schemas/v1alpha4"
-	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	labels "k8s.io/apimachinery/pkg/labels"
-	schema "k8s.io/apimachinery/pkg/runtime/schema"
-	types "k8s.io/apimachinery/pkg/types"
-	watch "k8s.io/apimachinery/pkg/watch"
-	testing "k8s.io/client-go/testing"
-)
-
-// FakeMigrations implements MigrationInterface
-type FakeMigrations struct {
-	Fake *FakeSchemasv1alpha4
-	ns   string
-}
-
-var migrationsResource = schema.GroupVersionResource{Group: "schemas.schemahero.io", Version: "v1alpha4", Resource: "migrations"}
-
-var migrationsKind = schema.GroupVersionKind{Group: "schemas.schemahero.io", Version: "v1alpha4", Kind: "Migration"}
-
-// Get takes name of the migration, and returns the corresponding migration object, and an error if there is any.
-func (c *FakeMigrations) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha4.Migration, err error) {
-	obj, err := c.Fake.
-		Invokes(testing.NewGetAction(migrationsResource, c.ns, name), &v1alpha4.Migration{})
-
-	if obj == nil {
-		return nil, err
-	}
-	return obj.(*v1alpha4.Migration), err
-}
-
-// List takes label and field selectors, and returns the list of Migrations that match those selectors.
-func (c *FakeMigrations) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha4.MigrationList, err error) {
-	obj, err := c.Fake.
-		Invokes(testing.NewListAction(migrationsResource, migrationsKind, c.ns, opts), &v1alpha4.MigrationList{})
-
-	if obj == nil {
-		return nil, err
-	}
-
-	label, _, _ := testing.ExtractFromListOptions(opts)
-	if label == nil {
-		label = labels.Everything()
-	}
-	list := &v1alpha4.MigrationList{ListMeta: obj.(*v1alpha4.MigrationList).ListMeta}
-	for _, item := range obj.(*v1alpha4.MigrationList).Items {
-		if label.Matches(labels.Set(item.Labels)) {
-			list.Items = append(list.Items, item)
-		}
-	}
-	return list, err
-}
-
-// Watch returns a watch.Interface that watches the requested migrations.
-func (c *FakeMigrations) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
-	return c.Fake.
-		InvokesWatch(testing.NewWatchAction(migrationsResource, c.ns, opts))
-
-}
-
-// Create takes the representation of a migration and creates it.  Returns the server's representation of the migration, and an error, if there is any.
-func (c *FakeMigrations) Create(ctx context.Context, migration *v1alpha4.Migration, opts v1.CreateOptions) (result *v1alpha4.Migration, err error) {
-	obj, err := c.Fake.
-		Invokes(testing.NewCreateAction(migrationsResource, c.ns, migration), &v1alpha4.Migration{})
-
-	if obj == nil {
-		return nil, err
-	}
-	return obj.(*v1alpha4.Migration), err
-}
-
-// Update takes the representation of a migration and updates it. Returns the server's representation of the migration, and an error, if there is any.
-func (c *FakeMigrations) Update(ctx context.Context, migration *v1alpha4.Migration, opts v1.UpdateOptions) (result *v1alpha4.Migration, err error) {
-	obj, err := c.Fake.
-		Invokes(testing.NewUpdateAction(migrationsResource, c.ns, migration), &v1alpha4.Migration{})
-
-	if obj == nil {
-		return nil, err
-	}
-	return obj.(*v1alpha4.Migration), err
-}
-
-// UpdateStatus was generated because the type contains a Status member.
-// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
-func (c *FakeMigrations) UpdateStatus(ctx context.Context, migration *v1alpha4.Migration, opts v1.UpdateOptions) (*v1alpha4.Migration, error) {
-	obj, err := c.Fake.
-		Invokes(testing.NewUpdateSubresourceAction(migrationsResource, "status", c.ns, migration), &v1alpha4.Migration{})
-
-	if obj == nil {
-		return nil, err
-	}
-	return obj.(*v1alpha4.Migration), err
-}
-
-// Delete takes name of the migration and deletes it. Returns an error if one occurs.
-func (c *FakeMigrations) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
-	_, err := c.Fake.
-		Invokes(testing.NewDeleteAction(migrationsResource, c.ns, name), &v1alpha4.Migration{})
-
-	return err
-}
-
-// DeleteCollection deletes a collection of objects.
-func (c *FakeMigrations) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
-	action := testing.NewDeleteCollectionAction(migrationsResource, c.ns, listOpts)
-
-	_, err := c.Fake.Invokes(action, &v1alpha4.MigrationList{})
-	return err
-}
-
-// Patch applies the patch and returns the patched migration.
-func (c *FakeMigrations) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha4.Migration, err error) {
-	obj, err := c.Fake.
-		Invokes(testing.NewPatchSubresourceAction(migrationsResource, c.ns, name, pt, data, subresources...), &v1alpha4.Migration{})
-
-	if obj == nil {
-		return nil, err
-	}
-	return obj.(*v1alpha4.Migration), err
-}
diff --git a/pkg/client/schemaheroclientset/typed/schemas/v1alpha3/fake/fake_schemas_client.go b/pkg/client/schemaheroclientset/typed/schemas/v1alpha3/fake/fake_schemas_client.go
deleted file mode 100644
index 5dd3ce9f..00000000
--- a/pkg/client/schemaheroclientset/typed/schemas/v1alpha3/fake/fake_schemas_client.go
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
-Copyright 2020 Replicated, Inc.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-// Code generated by client-gen. DO NOT EDIT.
-
-package fake
-
-import (
-	v1alpha4 "github.com/schemahero/schemahero/pkg/client/schemaheroclientset/typed/schemas/v1alpha4"
-	rest "k8s.io/client-go/rest"
-	testing "k8s.io/client-go/testing"
-)
-
-type FakeSchemasv1alpha4 struct {
-	*testing.Fake
-}
-
-func (c *FakeSchemasv1alpha4) Migrations(namespace string) v1alpha4.MigrationInterface {
-	return &FakeMigrations{c, namespace}
-}
-
-func (c *FakeSchemasv1alpha4) Tables(namespace string) v1alpha4.TableInterface {
-	return &FakeTables{c, namespace}
-}
-
-// RESTClient returns a RESTClient that is used to communicate
-// with API server by this client implementation.
-func (c *FakeSchemasv1alpha4) RESTClient() rest.Interface {
-	var ret *rest.RESTClient
-	return ret
-}
diff --git a/pkg/client/schemaheroclientset/typed/schemas/v1alpha3/fake/fake_table.go b/pkg/client/schemaheroclientset/typed/schemas/v1alpha3/fake/fake_table.go
deleted file mode 100644
index 3d6182f7..00000000
--- a/pkg/client/schemaheroclientset/typed/schemas/v1alpha3/fake/fake_table.go
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
-Copyright 2020 Replicated, Inc.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-// Code generated by client-gen. DO NOT EDIT.
-
-package fake
-
-import (
-	"context"
-
-	v1alpha4 "github.com/schemahero/schemahero/pkg/apis/schemas/v1alpha4"
-	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	labels "k8s.io/apimachinery/pkg/labels"
-	schema "k8s.io/apimachinery/pkg/runtime/schema"
-	types "k8s.io/apimachinery/pkg/types"
-	watch "k8s.io/apimachinery/pkg/watch"
-	testing "k8s.io/client-go/testing"
-)
-
-// FakeTables implements TableInterface
-type FakeTables struct {
-	Fake *FakeSchemasv1alpha4
-	ns   string
-}
-
-var tablesResource = schema.GroupVersionResource{Group: "schemas.schemahero.io", Version: "v1alpha4", Resource: "tables"}
-
-var tablesKind = schema.GroupVersionKind{Group: "schemas.schemahero.io", Version: "v1alpha4", Kind: "Table"}
-
-// Get takes name of the table, and returns the corresponding table object, and an error if there is any.
-func (c *FakeTables) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha4.Table, err error) {
-	obj, err := c.Fake.
-		Invokes(testing.NewGetAction(tablesResource, c.ns, name), &v1alpha4.Table{})
-
-	if obj == nil {
-		return nil, err
-	}
-	return obj.(*v1alpha4.Table), err
-}
-
-// List takes label and field selectors, and returns the list of Tables that match those selectors.
-func (c *FakeTables) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha4.TableList, err error) {
-	obj, err := c.Fake.
-		Invokes(testing.NewListAction(tablesResource, tablesKind, c.ns, opts), &v1alpha4.TableList{})
-
-	if obj == nil {
-		return nil, err
-	}
-
-	label, _, _ := testing.ExtractFromListOptions(opts)
-	if label == nil {
-		label = labels.Everything()
-	}
-	list := &v1alpha4.TableList{ListMeta: obj.(*v1alpha4.TableList).ListMeta}
-	for _, item := range obj.(*v1alpha4.TableList).Items {
-		if label.Matches(labels.Set(item.Labels)) {
-			list.Items = append(list.Items, item)
-		}
-	}
-	return list, err
-}
-
-// Watch returns a watch.Interface that watches the requested tables.
-func (c *FakeTables) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
-	return c.Fake.
-		InvokesWatch(testing.NewWatchAction(tablesResource, c.ns, opts))
-
-}
-
-// Create takes the representation of a table and creates it.  Returns the server's representation of the table, and an error, if there is any.
-func (c *FakeTables) Create(ctx context.Context, table *v1alpha4.Table, opts v1.CreateOptions) (result *v1alpha4.Table, err error) {
-	obj, err := c.Fake.
-		Invokes(testing.NewCreateAction(tablesResource, c.ns, table), &v1alpha4.Table{})
-
-	if obj == nil {
-		return nil, err
-	}
-	return obj.(*v1alpha4.Table), err
-}
-
-// Update takes the representation of a table and updates it. Returns the server's representation of the table, and an error, if there is any.
-func (c *FakeTables) Update(ctx context.Context, table *v1alpha4.Table, opts v1.UpdateOptions) (result *v1alpha4.Table, err error) {
-	obj, err := c.Fake.
-		Invokes(testing.NewUpdateAction(tablesResource, c.ns, table), &v1alpha4.Table{})
-
-	if obj == nil {
-		return nil, err
-	}
-	return obj.(*v1alpha4.Table), err
-}
-
-// UpdateStatus was generated because the type contains a Status member.
-// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
-func (c *FakeTables) UpdateStatus(ctx context.Context, table *v1alpha4.Table, opts v1.UpdateOptions) (*v1alpha4.Table, error) {
-	obj, err := c.Fake.
-		Invokes(testing.NewUpdateSubresourceAction(tablesResource, "status", c.ns, table), &v1alpha4.Table{})
-
-	if obj == nil {
-		return nil, err
-	}
-	return obj.(*v1alpha4.Table), err
-}
-
-// Delete takes name of the table and deletes it. Returns an error if one occurs.
-func (c *FakeTables) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
-	_, err := c.Fake.
-		Invokes(testing.NewDeleteAction(tablesResource, c.ns, name), &v1alpha4.Table{})
-
-	return err
-}
-
-// DeleteCollection deletes a collection of objects.
-func (c *FakeTables) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
-	action := testing.NewDeleteCollectionAction(tablesResource, c.ns, listOpts)
-
-	_, err := c.Fake.Invokes(action, &v1alpha4.TableList{})
-	return err
-}
-
-// Patch applies the patch and returns the patched table.
-func (c *FakeTables) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha4.Table, err error) {
-	obj, err := c.Fake.
-		Invokes(testing.NewPatchSubresourceAction(tablesResource, c.ns, name, pt, data, subresources...), &v1alpha4.Table{})
-
-	if obj == nil {
-		return nil, err
-	}
-	return obj.(*v1alpha4.Table), err
-}
diff --git a/pkg/client/schemaheroclientset/typed/schemas/v1alpha3/generated_expansion.go b/pkg/client/schemaheroclientset/typed/schemas/v1alpha3/generated_expansion.go
deleted file mode 100644
index e46c385a..00000000
--- a/pkg/client/schemaheroclientset/typed/schemas/v1alpha3/generated_expansion.go
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
-Copyright 2020 Replicated, Inc.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-// Code generated by client-gen. DO NOT EDIT.
-
-package v1alpha4
-
-type MigrationExpansion interface{}
-
-type TableExpansion interface{}
diff --git a/pkg/client/schemaheroclientset/typed/schemas/v1alpha3/migration.go b/pkg/client/schemaheroclientset/typed/schemas/v1alpha3/migration.go
deleted file mode 100644
index 7f1fe598..00000000
--- a/pkg/client/schemaheroclientset/typed/schemas/v1alpha3/migration.go
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
-Copyright 2020 Replicated, Inc.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-// Code generated by client-gen. DO NOT EDIT.
-
-package v1alpha4
-
-import (
-	"context"
-	"time"
-
-	v1alpha4 "github.com/schemahero/schemahero/pkg/apis/schemas/v1alpha4"
-	scheme "github.com/schemahero/schemahero/pkg/client/schemaheroclientset/scheme"
-	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	types "k8s.io/apimachinery/pkg/types"
-	watch "k8s.io/apimachinery/pkg/watch"
-	rest "k8s.io/client-go/rest"
-)
-
-// MigrationsGetter has a method to return a MigrationInterface.
-// A group's client should implement this interface.
-type MigrationsGetter interface {
-	Migrations(namespace string) MigrationInterface
-}
-
-// MigrationInterface has methods to work with Migration resources.
-type MigrationInterface interface {
-	Create(ctx context.Context, migration *v1alpha4.Migration, opts v1.CreateOptions) (*v1alpha4.Migration, error)
-	Update(ctx context.Context, migration *v1alpha4.Migration, opts v1.UpdateOptions) (*v1alpha4.Migration, error)
-	UpdateStatus(ctx context.Context, migration *v1alpha4.Migration, opts v1.UpdateOptions) (*v1alpha4.Migration, error)
-	Delete(ctx context.Context, name string, opts v1.DeleteOptions) error
-	DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error
-	Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha4.Migration, error)
-	List(ctx context.Context, opts v1.ListOptions) (*v1alpha4.MigrationList, error)
-	Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error)
-	Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha4.Migration, err error)
-	MigrationExpansion
-}
-
-// migrations implements MigrationInterface
-type migrations struct {
-	client rest.Interface
-	ns     string
-}
-
-// newMigrations returns a Migrations
-func newMigrations(c *Schemasv1alpha4Client, namespace string) *migrations {
-	return &migrations{
-		client: c.RESTClient(),
-		ns:     namespace,
-	}
-}
-
-// Get takes name of the migration, and returns the corresponding migration object, and an error if there is any.
-func (c *migrations) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha4.Migration, err error) {
-	result = &v1alpha4.Migration{}
-	err = c.client.Get().
-		Namespace(c.ns).
-		Resource("migrations").
-		Name(name).
-		VersionedParams(&options, scheme.ParameterCodec).
-		Do(ctx).
-		Into(result)
-	return
-}
-
-// List takes label and field selectors, and returns the list of Migrations that match those selectors.
-func (c *migrations) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha4.MigrationList, err error) {
-	var timeout time.Duration
-	if opts.TimeoutSeconds != nil {
-		timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
-	}
-	result = &v1alpha4.MigrationList{}
-	err = c.client.Get().
-		Namespace(c.ns).
-		Resource("migrations").
-		VersionedParams(&opts, scheme.ParameterCodec).
-		Timeout(timeout).
-		Do(ctx).
-		Into(result)
-	return
-}
-
-// Watch returns a watch.Interface that watches the requested migrations.
-func (c *migrations) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
-	var timeout time.Duration
-	if opts.TimeoutSeconds != nil {
-		timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
-	}
-	opts.Watch = true
-	return c.client.Get().
-		Namespace(c.ns).
-		Resource("migrations").
-		VersionedParams(&opts, scheme.ParameterCodec).
-		Timeout(timeout).
-		Watch(ctx)
-}
-
-// Create takes the representation of a migration and creates it.  Returns the server's representation of the migration, and an error, if there is any.
-func (c *migrations) Create(ctx context.Context, migration *v1alpha4.Migration, opts v1.CreateOptions) (result *v1alpha4.Migration, err error) {
-	result = &v1alpha4.Migration{}
-	err = c.client.Post().
-		Namespace(c.ns).
-		Resource("migrations").
-		VersionedParams(&opts, scheme.ParameterCodec).
-		Body(migration).
-		Do(ctx).
-		Into(result)
-	return
-}
-
-// Update takes the representation of a migration and updates it. Returns the server's representation of the migration, and an error, if there is any.
-func (c *migrations) Update(ctx context.Context, migration *v1alpha4.Migration, opts v1.UpdateOptions) (result *v1alpha4.Migration, err error) {
-	result = &v1alpha4.Migration{}
-	err = c.client.Put().
-		Namespace(c.ns).
-		Resource("migrations").
-		Name(migration.Name).
-		VersionedParams(&opts, scheme.ParameterCodec).
-		Body(migration).
-		Do(ctx).
-		Into(result)
-	return
-}
-
-// UpdateStatus was generated because the type contains a Status member.
-// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
-func (c *migrations) UpdateStatus(ctx context.Context, migration *v1alpha4.Migration, opts v1.UpdateOptions) (result *v1alpha4.Migration, err error) {
-	result = &v1alpha4.Migration{}
-	err = c.client.Put().
-		Namespace(c.ns).
-		Resource("migrations").
-		Name(migration.Name).
-		SubResource("status").
-		VersionedParams(&opts, scheme.ParameterCodec).
-		Body(migration).
-		Do(ctx).
-		Into(result)
-	return
-}
-
-// Delete takes name of the migration and deletes it. Returns an error if one occurs.
-func (c *migrations) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
-	return c.client.Delete().
-		Namespace(c.ns).
-		Resource("migrations").
-		Name(name).
-		Body(&opts).
-		Do(ctx).
-		Error()
-}
-
-// DeleteCollection deletes a collection of objects.
-func (c *migrations) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
-	var timeout time.Duration
-	if listOpts.TimeoutSeconds != nil {
-		timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second
-	}
-	return c.client.Delete().
-		Namespace(c.ns).
-		Resource("migrations").
-		VersionedParams(&listOpts, scheme.ParameterCodec).
-		Timeout(timeout).
-		Body(&opts).
-		Do(ctx).
-		Error()
-}
-
-// Patch applies the patch and returns the patched migration.
-func (c *migrations) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha4.Migration, err error) {
-	result = &v1alpha4.Migration{}
-	err = c.client.Patch(pt).
-		Namespace(c.ns).
-		Resource("migrations").
-		Name(name).
-		SubResource(subresources...).
-		VersionedParams(&opts, scheme.ParameterCodec).
-		Body(data).
-		Do(ctx).
-		Into(result)
-	return
-}
diff --git a/pkg/client/schemaheroclientset/typed/schemas/v1alpha3/schemas_client.go b/pkg/client/schemaheroclientset/typed/schemas/v1alpha3/schemas_client.go
deleted file mode 100644
index ecc55359..00000000
--- a/pkg/client/schemaheroclientset/typed/schemas/v1alpha3/schemas_client.go
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
-Copyright 2020 Replicated, Inc.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-// Code generated by client-gen. DO NOT EDIT.
-
-package v1alpha4
-
-import (
-	v1alpha4 "github.com/schemahero/schemahero/pkg/apis/schemas/v1alpha4"
-	"github.com/schemahero/schemahero/pkg/client/schemaheroclientset/scheme"
-	rest "k8s.io/client-go/rest"
-)
-
-type Schemasv1alpha4Interface interface {
-	RESTClient() rest.Interface
-	MigrationsGetter
-	TablesGetter
-}
-
-// Schemasv1alpha4Client is used to interact with features provided by the schemas.schemahero.io group.
-type Schemasv1alpha4Client struct {
-	restClient rest.Interface
-}
-
-func (c *Schemasv1alpha4Client) Migrations(namespace string) MigrationInterface {
-	return newMigrations(c, namespace)
-}
-
-func (c *Schemasv1alpha4Client) Tables(namespace string) TableInterface {
-	return newTables(c, namespace)
-}
-
-// NewForConfig creates a new Schemasv1alpha4Client for the given config.
-func NewForConfig(c *rest.Config) (*Schemasv1alpha4Client, error) {
-	config := *c
-	if err := setConfigDefaults(&config); err != nil {
-		return nil, err
-	}
-	client, err := rest.RESTClientFor(&config)
-	if err != nil {
-		return nil, err
-	}
-	return &Schemasv1alpha4Client{client}, nil
-}
-
-// NewForConfigOrDie creates a new Schemasv1alpha4Client for the given config and
-// panics if there is an error in the config.
-func NewForConfigOrDie(c *rest.Config) *Schemasv1alpha4Client {
-	client, err := NewForConfig(c)
-	if err != nil {
-		panic(err)
-	}
-	return client
-}
-
-// New creates a new Schemasv1alpha4Client for the given RESTClient.
-func New(c rest.Interface) *Schemasv1alpha4Client {
-	return &Schemasv1alpha4Client{c}
-}
-
-func setConfigDefaults(config *rest.Config) error {
-	gv := v1alpha4.SchemeGroupVersion
-	config.GroupVersion = &gv
-	config.APIPath = "/apis"
-	config.NegotiatedSerializer = scheme.Codecs.WithoutConversion()
-
-	if config.UserAgent == "" {
-		config.UserAgent = rest.DefaultKubernetesUserAgent()
-	}
-
-	return nil
-}
-
-// RESTClient returns a RESTClient that is used to communicate
-// with API server by this client implementation.
-func (c *Schemasv1alpha4Client) RESTClient() rest.Interface {
-	if c == nil {
-		return nil
-	}
-	return c.restClient
-}
diff --git a/pkg/client/schemaheroclientset/typed/schemas/v1alpha3/table.go b/pkg/client/schemaheroclientset/typed/schemas/v1alpha3/table.go
deleted file mode 100644
index 42ee9056..00000000
--- a/pkg/client/schemaheroclientset/typed/schemas/v1alpha3/table.go
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
-Copyright 2020 Replicated, Inc.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-// Code generated by client-gen. DO NOT EDIT.
-
-package v1alpha4
-
-import (
-	"context"
-	"time"
-
-	v1alpha4 "github.com/schemahero/schemahero/pkg/apis/schemas/v1alpha4"
-	scheme "github.com/schemahero/schemahero/pkg/client/schemaheroclientset/scheme"
-	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	types "k8s.io/apimachinery/pkg/types"
-	watch "k8s.io/apimachinery/pkg/watch"
-	rest "k8s.io/client-go/rest"
-)
-
-// TablesGetter has a method to return a TableInterface.
-// A group's client should implement this interface.
-type TablesGetter interface {
-	Tables(namespace string) TableInterface
-}
-
-// TableInterface has methods to work with Table resources.
-type TableInterface interface {
-	Create(ctx context.Context, table *v1alpha4.Table, opts v1.CreateOptions) (*v1alpha4.Table, error)
-	Update(ctx context.Context, table *v1alpha4.Table, opts v1.UpdateOptions) (*v1alpha4.Table, error)
-	UpdateStatus(ctx context.Context, table *v1alpha4.Table, opts v1.UpdateOptions) (*v1alpha4.Table, error)
-	Delete(ctx context.Context, name string, opts v1.DeleteOptions) error
-	DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error
-	Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha4.Table, error)
-	List(ctx context.Context, opts v1.ListOptions) (*v1alpha4.TableList, error)
-	Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error)
-	Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha4.Table, err error)
-	TableExpansion
-}
-
-// tables implements TableInterface
-type tables struct {
-	client rest.Interface
-	ns     string
-}
-
-// newTables returns a Tables
-func newTables(c *Schemasv1alpha4Client, namespace string) *tables {
-	return &tables{
-		client: c.RESTClient(),
-		ns:     namespace,
-	}
-}
-
-// Get takes name of the table, and returns the corresponding table object, and an error if there is any.
-func (c *tables) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha4.Table, err error) {
-	result = &v1alpha4.Table{}
-	err = c.client.Get().
-		Namespace(c.ns).
-		Resource("tables").
-		Name(name).
-		VersionedParams(&options, scheme.ParameterCodec).
-		Do(ctx).
-		Into(result)
-	return
-}
-
-// List takes label and field selectors, and returns the list of Tables that match those selectors.
-func (c *tables) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha4.TableList, err error) {
-	var timeout time.Duration
-	if opts.TimeoutSeconds != nil {
-		timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
-	}
-	result = &v1alpha4.TableList{}
-	err = c.client.Get().
-		Namespace(c.ns).
-		Resource("tables").
-		VersionedParams(&opts, scheme.ParameterCodec).
-		Timeout(timeout).
-		Do(ctx).
-		Into(result)
-	return
-}
-
-// Watch returns a watch.Interface that watches the requested tables.
-func (c *tables) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
-	var timeout time.Duration
-	if opts.TimeoutSeconds != nil {
-		timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
-	}
-	opts.Watch = true
-	return c.client.Get().
-		Namespace(c.ns).
-		Resource("tables").
-		VersionedParams(&opts, scheme.ParameterCodec).
-		Timeout(timeout).
-		Watch(ctx)
-}
-
-// Create takes the representation of a table and creates it.  Returns the server's representation of the table, and an error, if there is any.
-func (c *tables) Create(ctx context.Context, table *v1alpha4.Table, opts v1.CreateOptions) (result *v1alpha4.Table, err error) {
-	result = &v1alpha4.Table{}
-	err = c.client.Post().
-		Namespace(c.ns).
-		Resource("tables").
-		VersionedParams(&opts, scheme.ParameterCodec).
-		Body(table).
-		Do(ctx).
-		Into(result)
-	return
-}
-
-// Update takes the representation of a table and updates it. Returns the server's representation of the table, and an error, if there is any.
-func (c *tables) Update(ctx context.Context, table *v1alpha4.Table, opts v1.UpdateOptions) (result *v1alpha4.Table, err error) {
-	result = &v1alpha4.Table{}
-	err = c.client.Put().
-		Namespace(c.ns).
-		Resource("tables").
-		Name(table.Name).
-		VersionedParams(&opts, scheme.ParameterCodec).
-		Body(table).
-		Do(ctx).
-		Into(result)
-	return
-}
-
-// UpdateStatus was generated because the type contains a Status member.
-// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
-func (c *tables) UpdateStatus(ctx context.Context, table *v1alpha4.Table, opts v1.UpdateOptions) (result *v1alpha4.Table, err error) {
-	result = &v1alpha4.Table{}
-	err = c.client.Put().
-		Namespace(c.ns).
-		Resource("tables").
-		Name(table.Name).
-		SubResource("status").
-		VersionedParams(&opts, scheme.ParameterCodec).
-		Body(table).
-		Do(ctx).
-		Into(result)
-	return
-}
-
-// Delete takes name of the table and deletes it. Returns an error if one occurs.
-func (c *tables) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
-	return c.client.Delete().
-		Namespace(c.ns).
-		Resource("tables").
-		Name(name).
-		Body(&opts).
-		Do(ctx).
-		Error()
-}
-
-// DeleteCollection deletes a collection of objects.
-func (c *tables) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
-	var timeout time.Duration
-	if listOpts.TimeoutSeconds != nil {
-		timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second
-	}
-	return c.client.Delete().
-		Namespace(c.ns).
-		Resource("tables").
-		VersionedParams(&listOpts, scheme.ParameterCodec).
-		Timeout(timeout).
-		Body(&opts).
-		Do(ctx).
-		Error()
-}
-
-// Patch applies the patch and returns the patched table.
-func (c *tables) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha4.Table, err error) {
-	result = &v1alpha4.Table{}
-	err = c.client.Patch(pt).
-		Namespace(c.ns).
-		Resource("tables").
-		Name(name).
-		SubResource(subresources...).
-		VersionedParams(&opts, scheme.ParameterCodec).
-		Body(data).
-		Do(ctx).
-		Into(result)
-	return
-}
diff --git a/pkg/client/schemaheroclientset/typed/databases/v1alpha3/database.go b/pkg/client/schemaheroclientset/typed/schemas/v1alpha4/datatype.go
similarity index 55%
rename from pkg/client/schemaheroclientset/typed/databases/v1alpha3/database.go
rename to pkg/client/schemaheroclientset/typed/schemas/v1alpha4/datatype.go
index 599441af..ecf3e5a6 100644
--- a/pkg/client/schemaheroclientset/typed/databases/v1alpha3/database.go
+++ b/pkg/client/schemaheroclientset/typed/schemas/v1alpha4/datatype.go
@@ -22,7 +22,7 @@ import (
 	"context"
 	"time"
 
-	v1alpha4 "github.com/schemahero/schemahero/pkg/apis/databases/v1alpha4"
+	v1alpha4 "github.com/schemahero/schemahero/pkg/apis/schemas/v1alpha4"
 	scheme "github.com/schemahero/schemahero/pkg/client/schemaheroclientset/scheme"
 	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	types "k8s.io/apimachinery/pkg/types"
@@ -30,46 +30,46 @@ import (
 	rest "k8s.io/client-go/rest"
 )
 
-// DatabasesGetter has a method to return a DatabaseInterface.
+// DataTypesGetter has a method to return a DataTypeInterface.
 // A group's client should implement this interface.
-type DatabasesGetter interface {
-	Databases(namespace string) DatabaseInterface
+type DataTypesGetter interface {
+	DataTypes(namespace string) DataTypeInterface
 }
 
-// DatabaseInterface has methods to work with Database resources.
-type DatabaseInterface interface {
-	Create(ctx context.Context, database *v1alpha4.Database, opts v1.CreateOptions) (*v1alpha4.Database, error)
-	Update(ctx context.Context, database *v1alpha4.Database, opts v1.UpdateOptions) (*v1alpha4.Database, error)
-	UpdateStatus(ctx context.Context, database *v1alpha4.Database, opts v1.UpdateOptions) (*v1alpha4.Database, error)
+// DataTypeInterface has methods to work with DataType resources.
+type DataTypeInterface interface {
+	Create(ctx context.Context, dataType *v1alpha4.DataType, opts v1.CreateOptions) (*v1alpha4.DataType, error)
+	Update(ctx context.Context, dataType *v1alpha4.DataType, opts v1.UpdateOptions) (*v1alpha4.DataType, error)
+	UpdateStatus(ctx context.Context, dataType *v1alpha4.DataType, opts v1.UpdateOptions) (*v1alpha4.DataType, error)
 	Delete(ctx context.Context, name string, opts v1.DeleteOptions) error
 	DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error
-	Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha4.Database, error)
-	List(ctx context.Context, opts v1.ListOptions) (*v1alpha4.DatabaseList, error)
+	Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha4.DataType, error)
+	List(ctx context.Context, opts v1.ListOptions) (*v1alpha4.DataTypeList, error)
 	Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error)
-	Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha4.Database, err error)
-	DatabaseExpansion
+	Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha4.DataType, err error)
+	DataTypeExpansion
 }
 
-// databases implements DatabaseInterface
-type databases struct {
+// dataTypes implements DataTypeInterface
+type dataTypes struct {
 	client rest.Interface
 	ns     string
 }
 
-// newDatabases returns a Databases
-func newDatabases(c *Databasesv1alpha4Client, namespace string) *databases {
-	return &databases{
+// newDataTypes returns a DataTypes
+func newDataTypes(c *SchemasV1alpha4Client, namespace string) *dataTypes {
+	return &dataTypes{
 		client: c.RESTClient(),
 		ns:     namespace,
 	}
 }
 
-// Get takes name of the database, and returns the corresponding database object, and an error if there is any.
-func (c *databases) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha4.Database, err error) {
-	result = &v1alpha4.Database{}
+// Get takes name of the dataType, and returns the corresponding dataType object, and an error if there is any.
+func (c *dataTypes) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha4.DataType, err error) {
+	result = &v1alpha4.DataType{}
 	err = c.client.Get().
 		Namespace(c.ns).
-		Resource("databases").
+		Resource("datatypes").
 		Name(name).
 		VersionedParams(&options, scheme.ParameterCodec).
 		Do(ctx).
@@ -77,16 +77,16 @@ func (c *databases) Get(ctx context.Context, name string, options v1.GetOptions)
 	return
 }
 
-// List takes label and field selectors, and returns the list of Databases that match those selectors.
-func (c *databases) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha4.DatabaseList, err error) {
+// List takes label and field selectors, and returns the list of DataTypes that match those selectors.
+func (c *dataTypes) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha4.DataTypeList, err error) {
 	var timeout time.Duration
 	if opts.TimeoutSeconds != nil {
 		timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
 	}
-	result = &v1alpha4.DatabaseList{}
+	result = &v1alpha4.DataTypeList{}
 	err = c.client.Get().
 		Namespace(c.ns).
-		Resource("databases").
+		Resource("datatypes").
 		VersionedParams(&opts, scheme.ParameterCodec).
 		Timeout(timeout).
 		Do(ctx).
@@ -94,8 +94,8 @@ func (c *databases) List(ctx context.Context, opts v1.ListOptions) (result *v1al
 	return
 }
 
-// Watch returns a watch.Interface that watches the requested databases.
-func (c *databases) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
+// Watch returns a watch.Interface that watches the requested dataTypes.
+func (c *dataTypes) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
 	var timeout time.Duration
 	if opts.TimeoutSeconds != nil {
 		timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
@@ -103,34 +103,34 @@ func (c *databases) Watch(ctx context.Context, opts v1.ListOptions) (watch.Inter
 	opts.Watch = true
 	return c.client.Get().
 		Namespace(c.ns).
-		Resource("databases").
+		Resource("datatypes").
 		VersionedParams(&opts, scheme.ParameterCodec).
 		Timeout(timeout).
 		Watch(ctx)
 }
 
-// Create takes the representation of a database and creates it.  Returns the server's representation of the database, and an error, if there is any.
-func (c *databases) Create(ctx context.Context, database *v1alpha4.Database, opts v1.CreateOptions) (result *v1alpha4.Database, err error) {
-	result = &v1alpha4.Database{}
+// Create takes the representation of a dataType and creates it.  Returns the server's representation of the dataType, and an error, if there is any.
+func (c *dataTypes) Create(ctx context.Context, dataType *v1alpha4.DataType, opts v1.CreateOptions) (result *v1alpha4.DataType, err error) {
+	result = &v1alpha4.DataType{}
 	err = c.client.Post().
 		Namespace(c.ns).
-		Resource("databases").
+		Resource("datatypes").
 		VersionedParams(&opts, scheme.ParameterCodec).
-		Body(database).
+		Body(dataType).
 		Do(ctx).
 		Into(result)
 	return
 }
 
-// Update takes the representation of a database and updates it. Returns the server's representation of the database, and an error, if there is any.
-func (c *databases) Update(ctx context.Context, database *v1alpha4.Database, opts v1.UpdateOptions) (result *v1alpha4.Database, err error) {
-	result = &v1alpha4.Database{}
+// Update takes the representation of a dataType and updates it. Returns the server's representation of the dataType, and an error, if there is any.
+func (c *dataTypes) Update(ctx context.Context, dataType *v1alpha4.DataType, opts v1.UpdateOptions) (result *v1alpha4.DataType, err error) {
+	result = &v1alpha4.DataType{}
 	err = c.client.Put().
 		Namespace(c.ns).
-		Resource("databases").
-		Name(database.Name).
+		Resource("datatypes").
+		Name(dataType.Name).
 		VersionedParams(&opts, scheme.ParameterCodec).
-		Body(database).
+		Body(dataType).
 		Do(ctx).
 		Into(result)
 	return
@@ -138,25 +138,25 @@ func (c *databases) Update(ctx context.Context, database *v1alpha4.Database, opt
 
 // UpdateStatus was generated because the type contains a Status member.
 // Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
-func (c *databases) UpdateStatus(ctx context.Context, database *v1alpha4.Database, opts v1.UpdateOptions) (result *v1alpha4.Database, err error) {
-	result = &v1alpha4.Database{}
+func (c *dataTypes) UpdateStatus(ctx context.Context, dataType *v1alpha4.DataType, opts v1.UpdateOptions) (result *v1alpha4.DataType, err error) {
+	result = &v1alpha4.DataType{}
 	err = c.client.Put().
 		Namespace(c.ns).
-		Resource("databases").
-		Name(database.Name).
+		Resource("datatypes").
+		Name(dataType.Name).
 		SubResource("status").
 		VersionedParams(&opts, scheme.ParameterCodec).
-		Body(database).
+		Body(dataType).
 		Do(ctx).
 		Into(result)
 	return
 }
 
-// Delete takes name of the database and deletes it. Returns an error if one occurs.
-func (c *databases) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
+// Delete takes name of the dataType and deletes it. Returns an error if one occurs.
+func (c *dataTypes) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
 	return c.client.Delete().
 		Namespace(c.ns).
-		Resource("databases").
+		Resource("datatypes").
 		Name(name).
 		Body(&opts).
 		Do(ctx).
@@ -164,14 +164,14 @@ func (c *databases) Delete(ctx context.Context, name string, opts v1.DeleteOptio
 }
 
 // DeleteCollection deletes a collection of objects.
-func (c *databases) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
+func (c *dataTypes) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
 	var timeout time.Duration
 	if listOpts.TimeoutSeconds != nil {
 		timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second
 	}
 	return c.client.Delete().
 		Namespace(c.ns).
-		Resource("databases").
+		Resource("datatypes").
 		VersionedParams(&listOpts, scheme.ParameterCodec).
 		Timeout(timeout).
 		Body(&opts).
@@ -179,12 +179,12 @@ func (c *databases) DeleteCollection(ctx context.Context, opts v1.DeleteOptions,
 		Error()
 }
 
-// Patch applies the patch and returns the patched database.
-func (c *databases) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha4.Database, err error) {
-	result = &v1alpha4.Database{}
+// Patch applies the patch and returns the patched dataType.
+func (c *dataTypes) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha4.DataType, err error) {
+	result = &v1alpha4.DataType{}
 	err = c.client.Patch(pt).
 		Namespace(c.ns).
-		Resource("databases").
+		Resource("datatypes").
 		Name(name).
 		SubResource(subresources...).
 		VersionedParams(&opts, scheme.ParameterCodec).
diff --git a/pkg/client/schemaheroclientset/typed/schemas/v1alpha4/fake/fake_datatype.go b/pkg/client/schemaheroclientset/typed/schemas/v1alpha4/fake/fake_datatype.go
new file mode 100644
index 00000000..aa9f581e
--- /dev/null
+++ b/pkg/client/schemaheroclientset/typed/schemas/v1alpha4/fake/fake_datatype.go
@@ -0,0 +1,142 @@
+/*
+Copyright 2020 Replicated, Inc.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package fake
+
+import (
+	"context"
+
+	v1alpha4 "github.com/schemahero/schemahero/pkg/apis/schemas/v1alpha4"
+	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	labels "k8s.io/apimachinery/pkg/labels"
+	schema "k8s.io/apimachinery/pkg/runtime/schema"
+	types "k8s.io/apimachinery/pkg/types"
+	watch "k8s.io/apimachinery/pkg/watch"
+	testing "k8s.io/client-go/testing"
+)
+
+// FakeDataTypes implements DataTypeInterface
+type FakeDataTypes struct {
+	Fake *FakeSchemasV1alpha4
+	ns   string
+}
+
+var datatypesResource = schema.GroupVersionResource{Group: "schemas.schemahero.io", Version: "v1alpha4", Resource: "datatypes"}
+
+var datatypesKind = schema.GroupVersionKind{Group: "schemas.schemahero.io", Version: "v1alpha4", Kind: "DataType"}
+
+// Get takes name of the dataType, and returns the corresponding dataType object, and an error if there is any.
+func (c *FakeDataTypes) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha4.DataType, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewGetAction(datatypesResource, c.ns, name), &v1alpha4.DataType{})
+
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*v1alpha4.DataType), err
+}
+
+// List takes label and field selectors, and returns the list of DataTypes that match those selectors.
+func (c *FakeDataTypes) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha4.DataTypeList, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewListAction(datatypesResource, datatypesKind, c.ns, opts), &v1alpha4.DataTypeList{})
+
+	if obj == nil {
+		return nil, err
+	}
+
+	label, _, _ := testing.ExtractFromListOptions(opts)
+	if label == nil {
+		label = labels.Everything()
+	}
+	list := &v1alpha4.DataTypeList{ListMeta: obj.(*v1alpha4.DataTypeList).ListMeta}
+	for _, item := range obj.(*v1alpha4.DataTypeList).Items {
+		if label.Matches(labels.Set(item.Labels)) {
+			list.Items = append(list.Items, item)
+		}
+	}
+	return list, err
+}
+
+// Watch returns a watch.Interface that watches the requested dataTypes.
+func (c *FakeDataTypes) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
+	return c.Fake.
+		InvokesWatch(testing.NewWatchAction(datatypesResource, c.ns, opts))
+
+}
+
+// Create takes the representation of a dataType and creates it.  Returns the server's representation of the dataType, and an error, if there is any.
+func (c *FakeDataTypes) Create(ctx context.Context, dataType *v1alpha4.DataType, opts v1.CreateOptions) (result *v1alpha4.DataType, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewCreateAction(datatypesResource, c.ns, dataType), &v1alpha4.DataType{})
+
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*v1alpha4.DataType), err
+}
+
+// Update takes the representation of a dataType and updates it. Returns the server's representation of the dataType, and an error, if there is any.
+func (c *FakeDataTypes) Update(ctx context.Context, dataType *v1alpha4.DataType, opts v1.UpdateOptions) (result *v1alpha4.DataType, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewUpdateAction(datatypesResource, c.ns, dataType), &v1alpha4.DataType{})
+
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*v1alpha4.DataType), err
+}
+
+// UpdateStatus was generated because the type contains a Status member.
+// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
+func (c *FakeDataTypes) UpdateStatus(ctx context.Context, dataType *v1alpha4.DataType, opts v1.UpdateOptions) (*v1alpha4.DataType, error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewUpdateSubresourceAction(datatypesResource, "status", c.ns, dataType), &v1alpha4.DataType{})
+
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*v1alpha4.DataType), err
+}
+
+// Delete takes name of the dataType and deletes it. Returns an error if one occurs.
+func (c *FakeDataTypes) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
+	_, err := c.Fake.
+		Invokes(testing.NewDeleteAction(datatypesResource, c.ns, name), &v1alpha4.DataType{})
+
+	return err
+}
+
+// DeleteCollection deletes a collection of objects.
+func (c *FakeDataTypes) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
+	action := testing.NewDeleteCollectionAction(datatypesResource, c.ns, listOpts)
+
+	_, err := c.Fake.Invokes(action, &v1alpha4.DataTypeList{})
+	return err
+}
+
+// Patch applies the patch and returns the patched dataType.
+func (c *FakeDataTypes) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha4.DataType, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewPatchSubresourceAction(datatypesResource, c.ns, name, pt, data, subresources...), &v1alpha4.DataType{})
+
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*v1alpha4.DataType), err
+}
diff --git a/pkg/client/schemaheroclientset/typed/schemas/v1alpha4/fake/fake_schemas_client.go b/pkg/client/schemaheroclientset/typed/schemas/v1alpha4/fake/fake_schemas_client.go
index b7fe3ce3..6e035edc 100644
--- a/pkg/client/schemaheroclientset/typed/schemas/v1alpha4/fake/fake_schemas_client.go
+++ b/pkg/client/schemaheroclientset/typed/schemas/v1alpha4/fake/fake_schemas_client.go
@@ -28,6 +28,10 @@ type FakeSchemasV1alpha4 struct {
 	*testing.Fake
 }
 
+func (c *FakeSchemasV1alpha4) DataTypes(namespace string) v1alpha4.DataTypeInterface {
+	return &FakeDataTypes{c, namespace}
+}
+
 func (c *FakeSchemasV1alpha4) Migrations(namespace string) v1alpha4.MigrationInterface {
 	return &FakeMigrations{c, namespace}
 }
diff --git a/pkg/client/schemaheroclientset/typed/schemas/v1alpha4/generated_expansion.go b/pkg/client/schemaheroclientset/typed/schemas/v1alpha4/generated_expansion.go
index e46c385a..fe4c66ff 100644
--- a/pkg/client/schemaheroclientset/typed/schemas/v1alpha4/generated_expansion.go
+++ b/pkg/client/schemaheroclientset/typed/schemas/v1alpha4/generated_expansion.go
@@ -18,6 +18,8 @@ limitations under the License.
 
 package v1alpha4
 
+type DataTypeExpansion interface{}
+
 type MigrationExpansion interface{}
 
 type TableExpansion interface{}
diff --git a/pkg/client/schemaheroclientset/typed/schemas/v1alpha4/schemas_client.go b/pkg/client/schemaheroclientset/typed/schemas/v1alpha4/schemas_client.go
index 749641fa..66f0ffc1 100644
--- a/pkg/client/schemaheroclientset/typed/schemas/v1alpha4/schemas_client.go
+++ b/pkg/client/schemaheroclientset/typed/schemas/v1alpha4/schemas_client.go
@@ -26,6 +26,7 @@ import (
 
 type SchemasV1alpha4Interface interface {
 	RESTClient() rest.Interface
+	DataTypesGetter
 	MigrationsGetter
 	TablesGetter
 }
@@ -35,6 +36,10 @@ type SchemasV1alpha4Client struct {
 	restClient rest.Interface
 }
 
+func (c *SchemasV1alpha4Client) DataTypes(namespace string) DataTypeInterface {
+	return newDataTypes(c, namespace)
+}
+
 func (c *SchemasV1alpha4Client) Migrations(namespace string) MigrationInterface {
 	return newMigrations(c, namespace)
 }
diff --git a/pkg/database/cassandra/alias.go b/pkg/database/cassandra/alias.go
new file mode 100644
index 00000000..6fc74428
--- /dev/null
+++ b/pkg/database/cassandra/alias.go
@@ -0,0 +1,22 @@
+package cassandra
+
+var unparameterizedColumnTypes = []string{
+	// TODO
+}
+
+func unaliasUnparameterizedColumnType(requestedType string) string {
+	switch requestedType {
+	case "varchar":
+		return "text"
+	}
+
+	// TODO
+
+	// for _, unparameterizedColumnType := range unparameterizedColumnTypes {
+	// 	if unparameterizedColumnType == requestedType {
+	// 		return requestedType
+	// 	}
+	// }
+
+	return requestedType
+}
diff --git a/pkg/database/cassandra/alter.go b/pkg/database/cassandra/alter.go
new file mode 100644
index 00000000..537c4636
--- /dev/null
+++ b/pkg/database/cassandra/alter.go
@@ -0,0 +1,51 @@
+package cassandra
+
+import (
+	"fmt"
+	"strings"
+
+	schemasv1alpha4 "github.com/schemahero/schemahero/pkg/apis/schemas/v1alpha4"
+	"github.com/schemahero/schemahero/pkg/database/types"
+)
+
+func AlterColumnStatements(keyspace string, tableName string, desiredColumns []*schemasv1alpha4.CassandraColumn, existingColumn *types.Column) ([]string, error) {
+	alterStatement := fmt.Sprintf("alter column %s", existingColumn.Name)
+
+	for _, desiredColumn := range desiredColumns {
+		if desiredColumn.Name == existingColumn.Name {
+			column, err := schemaColumnToColumn(desiredColumn)
+			if err != nil {
+				return nil, err
+			}
+
+			if columnsMatch(existingColumn, column) {
+				return []string{}, nil
+			}
+
+			changes := []string{}
+			if existingColumn.DataType != column.DataType {
+				changes = append(changes, fmt.Sprintf("%s type %s", alterStatement, column.DataType))
+			}
+
+			if len(changes) == 0 {
+				return []string{}, nil
+			}
+
+			return []string{fmt.Sprintf(`alter table "%s.%s" %s`, keyspace, tableName, strings.Join(changes, ", "))}, nil
+		}
+	}
+
+	return []string{fmt.Sprintf(`alter table "%s.%s" drop column %s`, keyspace, tableName, existingColumn.Name)}, nil
+}
+
+func columnsMatch(col1 *types.Column, col2 *types.Column) bool {
+	if col1.DataType != col2.DataType {
+		return false
+	}
+
+	if col1.IsStatic != col2.IsStatic {
+		return false
+	}
+
+	return true
+}
diff --git a/pkg/database/cassandra/column.go b/pkg/database/cassandra/column.go
new file mode 100644
index 00000000..6af2bc27
--- /dev/null
+++ b/pkg/database/cassandra/column.go
@@ -0,0 +1,53 @@
+package cassandra
+
+import (
+	"fmt"
+
+	schemasv1alpha4 "github.com/schemahero/schemahero/pkg/apis/schemas/v1alpha4"
+	"github.com/schemahero/schemahero/pkg/database/types"
+)
+
+func schemaColumnToColumn(schemaColumn *schemasv1alpha4.CassandraColumn) (*types.Column, error) {
+	// TODO validate types
+
+	column := &types.Column{
+		Name: schemaColumn.Name,
+	}
+
+	if schemaColumn.IsStatic != nil {
+		column.IsStatic = *schemaColumn.IsStatic
+	}
+
+	unaliasedColumnType := unaliasUnparameterizedColumnType(schemaColumn.Type)
+	if unaliasedColumnType != "" {
+		column.DataType = unaliasedColumnType
+	}
+
+	if column.DataType == "" {
+		column.DataType = schemaColumn.Type
+	}
+
+	return column, nil
+}
+
+func cassandraColumnAsInsert(column *schemasv1alpha4.CassandraColumn) (string, error) {
+	// TODO before merge!  find the right gocql sanitize methods to call
+
+	result := fmt.Sprintf("%s %s", column.Name, column.Type)
+	if column.IsStatic != nil && *column.IsStatic {
+		result = fmt.Sprintf("%s static", result)
+	}
+
+	return result, nil
+}
+
+func InsertColumnStatement(keyspace string, tableName string, desiredColumn *schemasv1alpha4.CassandraColumn) (string, error) {
+	columnFields, err := cassandraColumnAsInsert(desiredColumn)
+	if err != nil {
+		return "", err
+	}
+
+	statement := fmt.Sprintf(`alter table "%s.%s" add %s`, keyspace, tableName, columnFields)
+
+	return statement, nil
+}
diff --git a/pkg/database/cassandra/connection.go b/pkg/database/cassandra/connection.go
new file mode 100644
index 00000000..c7843a9d
--- /dev/null
+++ b/pkg/database/cassandra/connection.go
@@ -0,0 +1,38 @@
+package cassandra
+
+import (
+	"github.com/gocql/gocql"
+	"github.com/pkg/errors"
+)
+
+type CassandraConnection struct {
+	session  *gocql.Session
+	keyspace string
+}
+
+func Connect(hosts []string, username string, password string, keyspace string) (*CassandraConnection, error) {
+	cluster := gocql.NewCluster(hosts...)
+
+	if username != "" {
+		cluster.Authenticator = gocql.PasswordAuthenticator{
+			Username: username,
+			Password: password,
+		}
+	}
+
+	session, err := cluster.CreateSession()
+	if err != nil {
+		return nil, errors.Wrap(err, "failed to create cassandra session")
+	}
+
+	cassandraConnection := CassandraConnection{
+		session:  session,
+		keyspace: keyspace,
+	}
+
+	return &cassandraConnection, nil
+}
+
+func (c *CassandraConnection) Close() {
+	c.session.Close()
+}
diff --git a/pkg/database/cassandra/create.go b/pkg/database/cassandra/create.go
new file mode 100644
index 00000000..0652a04a
--- /dev/null
+++ b/pkg/database/cassandra/create.go
@@ -0,0 +1,145 @@
+package cassandra
+
+import (
+	"encoding/json"
+	"fmt"
+	"strings"
+
+	"github.com/pkg/errors"
+	schemasv1alpha4 "github.com/schemahero/schemahero/pkg/apis/schemas/v1alpha4"
+)
+
+func CreateTypeStatement(keyspace string, typeName string, typeSchema *schemasv1alpha4.CassandraDataTypeSchema) (string, error) {
+	fields := []string{}
+	for _, desiredField := range typeSchema.Fields {
+		fieldFields, err := cassandraTypeAsInsert(desiredField)
+		if err != nil {
+			return "", err
+		}
+		fields = append(fields, fieldFields)
+	}
+
+	query := fmt.Sprintf("create type %q (%s)", typeName, strings.Join(fields, ", "))
+
+	return query, nil
+}
+
+func CreateTableStatement(keyspace string, tableName string, tableSchema *schemasv1alpha4.CassandraTableSchema) (string, error) {
+	columns := []string{}
+	for _, desiredColumn := range tableSchema.Columns {
+		columnsFields, err := cassandraColumnAsInsert(desiredColumn)
+		if err != nil {
+			return "", err
+		}
+		columns = append(columns, columnsFields)
+	}
+
+	// primary key
+	if tableSchema.PrimaryKey != nil {
+		compoundedKeys := []string{}
+		for _, primaryKey := range tableSchema.PrimaryKey {
+			if len(primaryKey) == 1 {
+				compoundedKeys = append(compoundedKeys, primaryKey[0])
+				continue
+			}
+
+			keyComponent := fmt.Sprintf("(%s)", strings.Join(primaryKey, ", "))
+			compoundedKeys = append(compoundedKeys, keyComponent)
+		}
+
+		pk := fmt.Sprintf("primary key (%s)", strings.Join(compoundedKeys, ", "))
+		columns = append(columns, pk)
+	}
+
+	query := fmt.Sprintf(`create table "%s.%s" (%s)`, keyspace, tableName, strings.Join(columns, ", "))
+
+	// clustering
+	if tableSchema.ClusteringOrder != nil {
+		order := ""
+		if tableSchema.ClusteringOrder.IsDescending != nil && *tableSchema.ClusteringOrder.IsDescending == true {
+			order = " desc"
+		}
+		clustering := fmt.Sprintf("with clustering order by (%s%s)", tableSchema.ClusteringOrder.Column, order)
+
+		query = fmt.Sprintf("%s %s", query, clustering)
+	}
+
+	// any specified properties
+	tableProperties := []string{}
+	if tableSchema.Properties != nil {
+		if tableSchema.Properties.BloomFilterFPChance != "" {
+			tableProperty := fmt.Sprintf(`bloom_filter_fp_chance = %s`, tableSchema.Properties.BloomFilterFPChance)
+			tableProperties = append(tableProperties, tableProperty)
+		}
+		if tableSchema.Properties.Caching != nil {
+			b, err := json.Marshal(tableSchema.Properties.Caching)
+			if err != nil {
+				return "", errors.Wrap(err, "failed to marshal caching property")
+			}
+			tableProperty := fmt.Sprintf(`caching = %s`, b)
+			tableProperties = append(tableProperties, tableProperty)
+		}
+		if tableSchema.Properties.Comment != "" {
+			tableProperty := fmt.Sprintf(`comment = '%s'`, tableSchema.Properties.Comment)
+			tableProperties = append(tableProperties, tableProperty)
+		}
+		if tableSchema.Properties.Compaction != nil {
+			b, err := json.Marshal(tableSchema.Properties.Compaction)
+			if err != nil {
+				return "", errors.Wrap(err, "failed to marshal compaction property")
+			}
+			tableProperty := fmt.Sprintf(`compaction = %s`, b)
+			tableProperties = append(tableProperties, tableProperty)
+		}
+		if tableSchema.Properties.Compression != nil {
+			b, err := json.Marshal(tableSchema.Properties.Compression)
+			if err != nil {
+				return "", errors.Wrap(err, "failed to marshal compression property")
+			}
+			tableProperty := fmt.Sprintf(`compression = %s`, b)
+			tableProperties = append(tableProperties, tableProperty)
+		}
+		if tableSchema.Properties.CRCCheckChance != "" {
+			tableProperty := fmt.Sprintf(`crc_check_chance = %s`, tableSchema.Properties.CRCCheckChance)
+			tableProperties = append(tableProperties, tableProperty)
+		}
+		if tableSchema.Properties.DCLocalReadRepairChance != "" {
+			tableProperty := fmt.Sprintf(`dclocal_read_repair_chance = %s`, tableSchema.Properties.DCLocalReadRepairChance)
+			tableProperties = append(tableProperties, tableProperty)
+		}
+		if tableSchema.Properties.DefaultTTL != nil {
+			tableProperty := fmt.Sprintf(`default_time_to_live = %d`, *tableSchema.Properties.DefaultTTL)
+			tableProperties = append(tableProperties, tableProperty)
+		}
+		if tableSchema.Properties.GCGraceSeconds != nil {
+			tableProperty := fmt.Sprintf(`grace_seconds = %d`, *tableSchema.Properties.GCGraceSeconds)
+			tableProperties = append(tableProperties, tableProperty)
+		}
+		if tableSchema.Properties.MaxIndexInterval != nil {
+			tableProperty := fmt.Sprintf(`max_index_interval = %d`, *tableSchema.Properties.MaxIndexInterval)
+			tableProperties = append(tableProperties, tableProperty)
+		}
+		if tableSchema.Properties.MemtableFlushPeriodMS != nil {
+			tableProperty := fmt.Sprintf(`memtable_flush_period_in_ms = %d`, *tableSchema.Properties.MemtableFlushPeriodMS)
+			tableProperties = append(tableProperties, tableProperty)
+		}
+		if tableSchema.Properties.MinIndexInterval != nil {
+			tableProperty := fmt.Sprintf(`min_index_interval = %d`, *tableSchema.Properties.MinIndexInterval)
+			tableProperties = append(tableProperties, tableProperty)
+		}
+		if tableSchema.Properties.ReadRepairChance != "" {
+			tableProperty := fmt.Sprintf(`read_repair_chance = %s`, tableSchema.Properties.ReadRepairChance)
+			tableProperties = append(tableProperties, tableProperty)
+		}
+		if tableSchema.Properties.SpeculativeRetry != "" {
+			tableProperty := fmt.Sprintf(`speculative_retry = '%s'`, tableSchema.Properties.SpeculativeRetry)
+			tableProperties = append(tableProperties, tableProperty)
+		}
+	}
+
+	if len(tableProperties) > 0 {
+		query = fmt.Sprintf("%s with %s", query, strings.Join(tableProperties, " AND "))
+	}
+
+	return query, nil
+}
diff --git a/pkg/database/cassandra/deploy.go b/pkg/database/cassandra/deploy.go
new file mode 100644
index 00000000..ceb96219
--- /dev/null
+++ b/pkg/database/cassandra/deploy.go
@@ -0,0 +1,301 @@
+package cassandra
+
+import (
+	"encoding/json"
+	"fmt"
+	"reflect"
+	"strings"
+
+	"github.com/pkg/errors"
+	schemasv1alpha4 "github.com/schemahero/schemahero/pkg/apis/schemas/v1alpha4"
+	"github.com/schemahero/schemahero/pkg/database/types"
+)
+
+func PlanCassandraType(hosts []string, username string, password string, keyspace string, typeName string, cassandraTypeSchema *schemasv1alpha4.CassandraDataTypeSchema) ([]string, error) {
+	c, err := Connect(hosts, username, password, keyspace)
+	if err != nil {
+		return nil, errors.Wrap(err, "failed to connect to cassandra")
+	}
+	defer c.Close()
+
+	// determine if the type exists
+	query := `select count(1) from system_schema.types where keyspace_name=? and type_name=?`
+	row := c.session.Query(query, keyspace, typeName)
+	typeExists := 0
+	if err := row.Scan(&typeExists); err != nil {
+		return nil, errors.Wrap(err, "failed to scan")
+	}
+
+	if typeExists == 0 && cassandraTypeSchema.IsDeleted {
+		return []string{}, nil
+	} else if typeExists > 0 && cassandraTypeSchema.IsDeleted {
+		return []string{
+			fmt.Sprintf(`drop ttype %s.%s`, keyspace, typeName),
+		}, nil
+	}
+
+	if typeExists == 0 {
+		// shortcut to just create it
+		query, err := CreateTypeStatement(keyspace, typeName, cassandraTypeSchema)
+		if err != nil {
+			return nil, errors.Wrap(err, "failed to create type statement")
+		}
+
+		return []string{query}, nil
+	}
+
+	return nil, errors.New("not implemented")
+}
+
+func PlanCassandraTable(hosts []string, username string, password string, keyspace string, tableName string, cassandraTableSchema *schemasv1alpha4.CassandraTableSchema) ([]string, error) {
+	c, err := Connect(hosts, username, password, keyspace)
+	if err != nil {
+		return nil, errors.Wrap(err, "failed to connect to cassandra")
+	}
+	defer c.Close()
+
+	// determine if the table exists
+	query := `select count(1) from system_schema.tables where keyspace_name=? and table_name = ?`
+	row := c.session.Query(query, keyspace, tableName)
+	tableExists := 0
+	if err := row.Scan(&tableExists); err != nil {
+		return nil, errors.Wrap(err, "failed to scan")
+	}
+
+	if tableExists == 0 && cassandraTableSchema.IsDeleted {
+		return []string{}, nil
+	} else if tableExists > 0 && cassandraTableSchema.IsDeleted {
+		return []string{
+			fmt.Sprintf(`drop table %s.%s`, keyspace, tableName),
+		}, nil
+	}
+
+	if tableExists == 0 {
+		// shortcut to just create it
+		query, err := CreateTableStatement(keyspace, tableName, cassandraTableSchema)
+		if err != nil {
+			return nil, errors.Wrap(err, "failed to create table statement")
+		}
+
+		return []string{query}, nil
+	}
+
+	statements := []string{}
+
+	columnStatements, err := buildColumnStatements(c, tableName, cassandraTableSchema)
+	if err != nil {
+		return nil, errors.Wrap(err, "failed to build column statements")
+	}
+	statements = append(statements, columnStatements...)
+
+	propertiesStatements, err := buildPropertiesStatements(c, tableName, cassandraTableSchema)
+	if err != nil {
+		return nil, errors.Wrap(err, "failed to build properties statements")
+	}
+	statements = append(statements, propertiesStatements...)
+
+	return statements, nil
+}
+
+func buildPropertiesStatements(c *CassandraConnection, tableName string, cassandraTableSchema *schemasv1alpha4.CassandraTableSchema) ([]string, error) {
+	query := `select bloom_filter_fp_chance, caching, comment, compaction, compression, crc_check_chance, dclocal_read_repair_chance
+from system_schema.tables
+where keyspace_name = ? and table_name = ?`
+	row := c.session.Query(query, c.keyspace, tableName)
+
+	currentProperties := schemasv1alpha4.CassandraTableProperties{}
+
+	var bloomFilterFPChance float64
+	var crcCheckChance float64
+	var dcLocalReadRepairChance float64
+
+	err := row.Scan(&bloomFilterFPChance, &currentProperties.Caching, &currentProperties.Comment,
+		&currentProperties.Compaction, &currentProperties.Compression, &crcCheckChance,
+		&dcLocalReadRepairChance)
+	if err != nil {
+		return nil, errors.Wrap(err, "failed to scan")
+	}
+
+	currentProperties.BloomFilterFPChance = fmt.Sprintf("%f", bloomFilterFPChance)
+	currentProperties.CRCCheckChance = fmt.Sprintf("%f", crcCheckChance)
+	currentProperties.DCLocalReadRepairChance = fmt.Sprintf("%f", dcLocalReadRepairChance)
+
+	needsUpdating := []string{}
+
+	if cassandraTableSchema.Properties != nil {
+		if currentProperties.BloomFilterFPChance != cassandraTableSchema.Properties.BloomFilterFPChance {
+			needsUpdating = append(needsUpdating, fmt.Sprintf("bloom_filter_fp_chance = %s", cassandraTableSchema.Properties.BloomFilterFPChance))
+		}
+		if !reflect.DeepEqual(currentProperties.Caching, cassandraTableSchema.Properties.Caching) {
+			if cassandraTableSchema.Properties.Caching == nil {
+				needsUpdating = append(needsUpdating, "caching = null")
+			} else {
+				b, err := json.Marshal(cassandraTableSchema.Properties.Caching)
+				if err != nil {
+					return nil, errors.Wrap(err, "failed to marshal caching property")
+				}
+
+				// TODO is there a better way? cassandra only accepts these maps with single quotes
+				formatted := strings.ReplaceAll(string(b), `"`, `'`)
+				needsUpdating = append(needsUpdating, fmt.Sprintf("caching = %s", formatted))
+			}
+		}
+		if currentProperties.Comment != cassandraTableSchema.Properties.Comment {
+			// TODO escape
+			needsUpdating = append(needsUpdating, fmt.Sprintf("comment = '%s'", cassandraTableSchema.Properties.Comment))
+		}
+		if !reflect.DeepEqual(currentProperties.Compaction, cassandraTableSchema.Properties.Compaction) {
+			if cassandraTableSchema.Properties.Compaction == nil {
+				needsUpdating = append(needsUpdating, "compaction = null")
+			} else {
+				b, err := json.Marshal(cassandraTableSchema.Properties.Compaction)
+				if err != nil {
+					return nil, errors.Wrap(err, "failed to marshal compaction property")
+				}
+
+				// TODO is there a better way? cassandra only accepts these maps with single quotes
+				formatted := strings.ReplaceAll(string(b), `"`, `'`)
+				needsUpdating = append(needsUpdating, fmt.Sprintf("compaction = %s", formatted))
+			}
+		}
+		if !reflect.DeepEqual(currentProperties.Compression, cassandraTableSchema.Properties.Compression) {
+			if cassandraTableSchema.Properties.Compression == nil {
+				needsUpdating = append(needsUpdating, "compression = null")
+			} else {
+				b, err := json.Marshal(cassandraTableSchema.Properties.Compression)
+				if err != nil {
+					return nil, errors.Wrap(err, "failed to marshal compression property")
+				}
+
+				// TODO is there a better way? cassandra only accepts these maps with single quotes
+				formatted := strings.ReplaceAll(string(b), `"`, `'`)
+				needsUpdating = append(needsUpdating, fmt.Sprintf("compression = %s", formatted))
+			}
+		}
+		if currentProperties.CRCCheckChance != cassandraTableSchema.Properties.CRCCheckChance {
+			// TODO escape
+			needsUpdating = append(needsUpdating, fmt.Sprintf("crc_check_chance = '%s'", cassandraTableSchema.Properties.CRCCheckChance))
+		}
+		if currentProperties.DCLocalReadRepairChance != cassandraTableSchema.Properties.DCLocalReadRepairChance {
+			// TODO escape
+			needsUpdating = append(needsUpdating, fmt.Sprintf("dclocal_read_repair_chance = '%s'", cassandraTableSchema.Properties.DCLocalReadRepairChance))
+		}
+		if currentProperties.DefaultTTL != cassandraTableSchema.Properties.DefaultTTL {
+			// TODO escape
+			needsUpdating = append(needsUpdating, fmt.Sprintf("default_time_to_live = %d", *cassandraTableSchema.Properties.DefaultTTL))
+		}
+		if currentProperties.GCGraceSeconds != cassandraTableSchema.Properties.GCGraceSeconds {
+			// TODO escape
+			needsUpdating = append(needsUpdating, fmt.Sprintf("gc_grace_period_seconds = %d", *cassandraTableSchema.Properties.GCGraceSeconds))
+		}
+		if currentProperties.MaxIndexInterval != cassandraTableSchema.Properties.MaxIndexInterval {
+			// TODO escape
+			needsUpdating = append(needsUpdating, fmt.Sprintf("max_index_interval = %d", *cassandraTableSchema.Properties.MaxIndexInterval))
+		}
+		if currentProperties.MemtableFlushPeriodMS != cassandraTableSchema.Properties.MemtableFlushPeriodMS {
+			// TODO escape
+			needsUpdating = append(needsUpdating, fmt.Sprintf("memtable_flush_period_in_ms = %d", *cassandraTableSchema.Properties.MemtableFlushPeriodMS))
+		}
+		if currentProperties.MinIndexInterval != cassandraTableSchema.Properties.MinIndexInterval {
+			// TODO escape
+			needsUpdating = append(needsUpdating, fmt.Sprintf("min_index_interval = %d", *cassandraTableSchema.Properties.MinIndexInterval))
+		}
+		if currentProperties.ReadRepairChance != cassandraTableSchema.Properties.ReadRepairChance {
+			// TODO escape
+			needsUpdating = append(needsUpdating, fmt.Sprintf("read_repair_chance = '%s'", cassandraTableSchema.Properties.ReadRepairChance))
+		}
+		if currentProperties.SpeculativeRetry != cassandraTableSchema.Properties.SpeculativeRetry {
+			// TODO escape
+			needsUpdating = append(needsUpdating, fmt.Sprintf("speculative_retry = '%s'", cassandraTableSchema.Properties.SpeculativeRetry))
+		}
+	}
+
+	if len(needsUpdating) == 0 {
+		return []string{}, nil
+	}
+
+	return []string{
+		fmt.Sprintf("alter table %s.%s with %s", c.keyspace, tableName, strings.Join(needsUpdating, " and ")),
+	}, nil
+}
+
+func buildColumnStatements(c *CassandraConnection, tableName string, cassandraTableSchema *schemasv1alpha4.CassandraTableSchema) ([]string, error) {
+	query := `select column_name, type from system_schema.columns where 
+keyspace_name = ? and table_name = ?`
+	scanner := c.session.Query(query, c.keyspace, tableName).Iter().Scanner()
+
+	alterAndDropStatements := []string{}
+	foundColumnNames := []string{}
+
+	for scanner.Next() {
+
+		var columnName, columnType string
+
+		if err := scanner.Scan(&columnName, &columnType); err != nil {
+			return nil, errors.Wrap(err, "failed to scan")
+		}
+
+		foundColumnNames = append(foundColumnNames, columnName)
+
+		existingColumn := types.Column{
+			Name:     columnName,
+			DataType: columnType,
+		}
+
+		columnStatement, err := AlterColumnStatements(c.keyspace, tableName, cassandraTableSchema.Columns, &existingColumn)
+		if err != nil {
+			return nil, errors.Wrap(err, "failed to create alter column statement")
+		}
+
+		alterAndDropStatements = append(alterAndDropStatements, columnStatement...)
+	}
+
+	for _, desiredColumn := range cassandraTableSchema.Columns {
+		isColumnPresent := false
+		for _, foundColumn := range foundColumnNames {
+			if foundColumn == desiredColumn.Name {
+				isColumnPresent = true
+			}
+		}
+
+		if !isColumnPresent {
+			statement, err := InsertColumnStatement(c.keyspace, tableName, desiredColumn)
+			if err != nil {
+				return nil, errors.Wrap(err, "failed to create insert column statement")
+			}
+
+			alterAndDropStatements = append(alterAndDropStatements, statement)
+		}
+	}
+
+	return alterAndDropStatements, nil
+}
+
+func DeployCassandraStatements(hosts []string, username string, password string, keyspace string, statements []string) error {
+	c, err := Connect(hosts, username, password, keyspace)
+	if err != nil {
+		return err
+	}
+	defer c.Close()
+
+	// execute
+	if err := executeStatements(c, statements); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func executeStatements(c *CassandraConnection, statements []string) error {
+	for _, statement := range statements {
+		if statement == "" {
+			continue
+		}
+		fmt.Printf("Executing query %q\n", statement)
+		if err := c.session.Query(statement); err != nil {
+			return err.Context().Err()
+		}
+	}
+
+	return nil
+}
diff --git a/pkg/database/cassandra/type.go b/pkg/database/cassandra/type.go
new file mode 100644
index 00000000..67c44ee4
--- /dev/null
+++ b/pkg/database/cassandra/type.go
@@ -0,0 +1,15 @@
+package cassandra
+
+import (
+	"fmt"
+
+	schemasv1alpha4 "github.com/schemahero/schemahero/pkg/apis/schemas/v1alpha4"
+)
+
+func cassandraTypeAsInsert(field *schemasv1alpha4.CassandraField) (string, error) {
+	// TODO before merge!  find the right gocql sanitize methods to call
+
+	result := fmt.Sprintf("%s %s", field.Name, field.Type)
+
+	return result, nil
+}
diff --git a/pkg/database/database.go b/pkg/database/database.go
index cc4ac6bb..53bd7def 100644
--- a/pkg/database/database.go
+++ b/pkg/database/database.go
@@ -9,6 +9,7 @@ import (
 
 	"github.com/pkg/errors"
 	schemasv1alpha4 "github.com/schemahero/schemahero/pkg/apis/schemas/v1alpha4"
+	"github.com/schemahero/schemahero/pkg/database/cassandra"
 	"github.com/schemahero/schemahero/pkg/database/mysql"
 	"github.com/schemahero/schemahero/pkg/database/postgres"
 	"github.com/schemahero/schemahero/pkg/logger"
@@ -21,6 +22,10 @@ type Database struct {
 	OutputDir string
 	Driver    string
 	URI       string
+	Hosts     []string
+	Username  string
+	Password  string
+	Keyspace  string
 }
 
 func (d *Database) CreateFixturesSync() error {
@@ -97,6 +102,8 @@ func (d *Database) CreateFixturesSync() error {
 			}
 
 			statements = append(statements, statement)
+		} else if d.Driver == "cassandra" {
+			return errors.New("not implemented")
 		}
 
 		return nil
@@ -124,16 +131,22 @@ func (d *Database) CreateFixturesSync() error {
 	return nil
 }
 
-func (d *Database) PlanSyncFromFile(filename string) ([]string, error) {
+func (d *Database) PlanSyncFromFile(filename string, specType string) ([]string, error) {
 	specContents, err := ioutil.ReadFile(filename)
 	if err != nil {
 		return nil, errors.Wrap(err, "failed to read file")
 	}
 
-	return d.PlanSync(specContents)
+	if specType == "table" {
+		return d.planTableSync(specContents)
+	} else if specType == "type" {
+		return d.planTypeSync(specContents)
+	}
+
+	return nil, errors.New("unknown spec type")
 }
 
-func (d *Database) PlanSync(specContents []byte) ([]string, error) {
+func (d *Database) planTableSync(specContents []byte) ([]string, error) {
 	var spec *schemasv1alpha4.TableSpec
 	parsedK8sObject := schemasv1alpha4.Table{}
 	if err := yaml.Unmarshal(specContents, &parsedK8sObject); err == nil {
@@ -165,11 +178,46 @@ func (d *Database) PlanSyncTableSpec(spec *schemasv1alpha4.TableSpec) ([]string,
 		return mysql.PlanMysqlTable(d.URI, spec.Name, spec.Schema.Mysql)
 	} else if d.Driver == "cockroachdb" {
 		return postgres.PlanPostgresTable(d.URI, spec.Name, spec.Schema.CockroachDB)
+	} else if d.Driver == "cassandra" {
+		return cassandra.PlanCassandraTable(d.Hosts, d.Username, d.Password, d.Keyspace, spec.Name, spec.Schema.Cassandra)
 	}
 
 	return nil, errors.Errorf("unknown database driver: %q", d.Driver)
 }
 
+func (d *Database) planTypeSync(specContents []byte) ([]string, error) {
+	var spec *schemasv1alpha4.DataTypeSpec
+	parsedK8sObject := schemasv1alpha4.DataType{}
+	if err := yaml.Unmarshal(specContents, &parsedK8sObject); err == nil {
+		if parsedK8sObject.Spec.Schema != nil {
+			spec = &parsedK8sObject.Spec
+		}
+	}
+
+	if spec == nil {
+		plainSpec := schemasv1alpha4.DataTypeSpec{}
+		if err := yaml.Unmarshal(specContents, &plainSpec); err != nil {
+			return nil, errors.Wrap(err, "failed to unmarshal spec")
+		}
+
+		spec = &plainSpec
+	}
+
+	return d.PlanSyncTypeSpec(spec)
+}
+
+func (d *Database) PlanSyncTypeSpec(spec *schemasv1alpha4.DataTypeSpec) ([]string, error) {
+	if spec.Schema == nil {
+		return []string{}, nil
+	}
+
+	if d.Driver == "cassandra" {
+		return cassandra.PlanCassandraType(d.Hosts, d.Username, d.Password, d.Keyspace, spec.Name, spec.Schema.Cassandra)
+	}
+
+	return nil, errors.Errorf("planning types is not supported for driver %q", d.Driver)
+}
+
 func (d *Database) ApplySync(statements []string) error {
 	if d.Driver == "postgres" {
 		return postgres.DeployPostgresStatements(d.URI, statements)
@@ -177,6 +225,8 @@ func (d *Database) ApplySync(statements []string) error {
 		return mysql.DeployMysqlStatements(d.URI, statements)
 	} else if d.Driver == "cockroachdb" {
 		return postgres.DeployPostgresStatements(d.URI, statements)
+	} else if d.Driver == "cassandra" {
+		return cassandra.DeployCassandraStatements(d.Hosts, d.Username, d.Password, d.Keyspace, statements)
 	}
 
 	return errors.Errorf("unknown database driver: %q", d.Driver)
diff --git a/pkg/database/types/column.go b/pkg/database/types/column.go
index 02ce4cb6..c296261b 100644
--- a/pkg/database/types/column.go
+++ b/pkg/database/types/column.go
@@ -28,6 +28,7 @@ type Column struct {
 	IsArray       bool
 	Charset       string
 	Collation     string
+	IsStatic      bool
 }
 
 func ColumnToMysqlSchemaColumn(column *Column) (*schemasv1alpha4.MysqlSQLTableColumn, error) {
diff --git a/pkg/installer/database_objects.go b/pkg/installer/database_objects.go
index e87c7ccd..bf3963bb 100644
--- a/pkg/installer/database_objects.go
+++ b/pkg/installer/database_objects.go
@@ -41,9 +41,13 @@ spec:
               description: DatabaseConnection defines connection parameters for the
                 database driver
               properties:
-                cockroachdb:
+                cassandra:
                   properties:
-                    dbname:
+                    hosts:
+                      items:
+                        type: string
+                      type: array
+                    keyspace:
                       properties:
                         value:
                           type: string
@@ -130,7 +134,7 @@ spec:
                               type: object
                           type: object
                       type: object
-                    host:
+                    password:
                       properties:
                         value:
                           type: string
@@ -217,7 +221,7 @@ spec:
                               type: object
                           type: object
                       type: object
-                    password:
+                    username:
                       properties:
                         value:
                           type: string
@@ -304,7 +308,13 @@ spec:
                               type: object
                           type: object
                       type: object
-                    port:
+                  required:
+                  - hosts
+                  - keyspace
+                  type: object
+                cockroachdb:
+                  properties:
+                    dbname:
                       properties:
                         value:
                           type: string
@@ -391,7 +401,7 @@ spec:
                               type: object
                           type: object
                       type: object
-                    sslmode:
+                    host:
                       properties:
                         value:
                           type: string
@@ -478,7 +488,7 @@ spec:
                               type: object
                           type: object
                       type: object
-                    uri:
+                    password:
                       properties:
                         value:
                           type: string
@@ -565,7 +575,7 @@ spec:
                               type: object
                           type: object
                       type: object
-                    user:
+                    port:
                       properties:
                         value:
                           type: string
@@ -652,12 +662,7 @@ spec:
                               type: object
                           type: object
                       type: object
-                  type: object
-                mysql:
-                  properties:
-                    collation:
-                      type: string
-                    dbname:
+                    sslmode:
                       properties:
                         value:
                           type: string
@@ -744,11 +749,186 @@ spec:
                               type: object
                           type: object
                       type: object
-                    defaultCharset:
+                    uri:
+                      properties:
+                        value:
+                          type: string
+                        valueFrom:
+                          properties:
+                            secretKeyRef:
+                              properties:
+                                key:
+                                  type: string
+                                name:
+                                  type: string
+                              required:
+                              - key
+                              - name
+                              type: object
+                            ssm:
+                              properties:
+                                accessKeyId:
+                                  properties:
+                                    value:
+                                      type: string
+                                    valueFrom:
+                                      properties:
+                                        secretKeyRef:
+                                          properties:
+                                            key:
+                                              type: string
+                                            name:
+                                              type: string
+                                          required:
+                                          - key
+                                          - name
+                                          type: object
+                                      type: object
+                                  required:
+                                  - value
+                                  type: object
+                                name:
+                                  type: string
+                                region:
+                                  type: string
+                                secretAccessKey:
+                                  properties:
+                                    value:
+                                      type: string
+                                    valueFrom:
+                                      properties:
+                                        secretKeyRef:
+                                          properties:
+                                            key:
+                                              type: string
+                                            name:
+                                              type: string
+                                          required:
+                                          - key
+                                          - name
+                                          type: object
+                                      type: object
+                                  required:
+                                  - value
+                                  type: object
+                                withDecryption:
+                                  type: boolean
+                              required:
+                              - name
+                              type: object
+                            vault:
+                              properties:
+                                agentInject:
+                                  type: boolean
+                                endpoint:
+                                  type: string
+                                role:
+                                  type: string
+                                secret:
+                                  type: string
+                                serviceAccount:
+                                  type: string
+                                serviceAccountNamespace:
+                                  type: string
+                              required:
+                              - role
+                              - secret
+                              type: object
+                          type: object
+                      type: object
+                    user:
+                      properties:
+                        value:
+                          type: string
+                        valueFrom:
+                          properties:
+                            secretKeyRef:
+                              properties:
+                                key:
+                                  type: string
+                                name:
+                                  type: string
+                              required:
+                              - key
+                              - name
+                              type: object
+                            ssm:
+                              properties:
+                                accessKeyId:
+                                  properties:
+                                    value:
+                                      type: string
+                                    valueFrom:
+                                      properties:
+                                        secretKeyRef:
+                                          properties:
+                                            key:
+                                              type: string
+                                            name:
+                                              type: string
+                                          required:
+                                          - key
+                                          - name
+                                          type: object
+                                      type: object
+                                  required:
+                                  - value
+                                  type: object
+                                name:
+                                  type: string
+                                region:
+                                  type: string
+                                secretAccessKey:
+                                  properties:
+                                    value:
+                                      type: string
+                                    valueFrom:
+                                      properties:
+                                        secretKeyRef:
+                                          properties:
+                                            key:
+                                              type: string
+                                            name:
+                                              type: string
+                                          required:
+                                          - key
+                                          - name
+                                          type: object
+                                      type: object
+                                  required:
+                                  - value
+                                  type: object
+                                withDecryption:
+                                  type: boolean
+                              required:
+                              - name
+                              type: object
+                            vault:
+                              properties:
+                                agentInject:
+                                  type: boolean
+                                endpoint:
+                                  type: string
+                                role:
+                                  type: string
+                                secret:
+                                  type: string
+                                serviceAccount:
+                                  type: string
+                                serviceAccountNamespace:
+                                  type: string
+                              required:
+                              - role
+                              - secret
+                              type: object
+                          type: object
+                      type: object
+                  type: object
+                mysql:
+                  properties:
+                    collation:
                       type: string
-                    disableTLS:
-                      type: boolean
-                    host:
+                    dbname:
                       properties:
                         value:
                           type: string
@@ -835,7 +1015,11 @@ spec:
                               type: object
                           type: object
                       type: object
-                    password:
+                    defaultCharset:
+                      type: string
+                    disableTLS:
+                      type: boolean
+                    host:
                       properties:
                         value:
                           type: string
@@ -922,7 +1106,7 @@ spec:
                               type: object
                           type: object
                       type: object
-                    port:
+                    password:
                       properties:
                         value:
                           type: string
@@ -1009,7 +1193,7 @@ spec:
                               type: object
                           type: object
                       type: object
-                    uri:
+                    port:
                       properties:
                         value:
                           type: string
@@ -1096,7 +1280,7 @@ spec:
                               type: object
                           type: object
                       type: object
-                    user:
+                    uri:
                       properties:
                         value:
                           type: string
@@ -1183,10 +1367,7 @@ spec:
                               type: object
                           type: object
                       type: object
-                  type: object
-                postgres:
-                  properties:
-                    dbname:
+                    user:
                       properties:
                         value:
                           type: string
@@ -1273,7 +1454,10 @@ spec:
                               type: object
                           type: object
                       type: object
-                    host:
+                  type: object
+                postgres:
+                  properties:
+                    dbname:
                       properties:
                         value:
                           type: string
@@ -1360,7 +1544,7 @@ spec:
                               type: object
                           type: object
                       type: object
-                    password:
+                    host:
                       properties:
                         value:
                           type: string
@@ -1447,7 +1631,7 @@ spec:
                               type: object
                           type: object
                       type: object
-                    port:
+                    password:
                       properties:
                         value:
                           type: string
@@ -1534,7 +1718,7 @@ spec:
                               type: object
                           type: object
                       type: object
-                    sslmode:
+                    port:
                       properties:
                         value:
                           type: string
@@ -1621,7 +1805,7 @@ spec:
                               type: object
                           type: object
                       type: object
-                    uri:
+                    sslmode:
                       properties:
                         value:
                           type: string
@@ -1708,7 +1892,7 @@ spec:
                               type: object
                           type: object
                       type: object
-                    user:
+                    uri:
                       properties:
                         value:
                           type: string
@@ -1795,23 +1979,110 @@ spec:
                               type: object
                           type: object
                       type: object
-                  type: object
-              type: object
-            enableShellCommand:
-              type: boolean
-            immediateDeploy:
-              type: boolean
-            schemahero:
-              properties:
-                image:
-                  type: string
-                nodeSelector:
-                  additionalProperties:
-                    type: string
-                  type: object
-              type: object
-          type: object
-        status:
+                    user:
+                      properties:
+                        value:
+                          type: string
+                        valueFrom:
+                          properties:
+                            secretKeyRef:
+                              properties:
+                                key:
+                                  type: string
+                                name:
+                                  type: string
+                              required:
+                              - key
+                              - name
+                              type: object
+                            ssm:
+                              properties:
+                                accessKeyId:
+                                  properties:
+                                    value:
+                                      type: string
+                                    valueFrom:
+                                      properties:
+                                        secretKeyRef:
+                                          properties:
+                                            key:
+                                              type: string
+                                            name:
+                                              type: string
+                                          required:
+                                          - key
+                                          - name
+                                          type: object
+                                      type: object
+                                  required:
+                                  - value
+                                  type: object
+                                name:
+                                  type: string
+                                region:
+                                  type: string
+                                secretAccessKey:
+                                  properties:
+                                    value:
+                                      type: string
+                                    valueFrom:
+                                      properties:
+                                        secretKeyRef:
+                                          properties:
+                                            key:
+                                              type: string
+                                            name:
+                                              type: string
+                                          required:
+                                          - key
+                                          - name
+                                          type: object
+                                      type: object
+                                  required:
+                                  - value
+                                  type: object
+                                withDecryption:
+                                  type: boolean
+                              required:
+                              - name
+                              type: object
+                            vault:
+                              properties:
+                                agentInject:
+                                  type: boolean
+                                endpoint:
+                                  type: string
+                                role:
+                                  type: string
+                                secret:
+                                  type: string
+                                serviceAccount:
+                                  type: string
+                                serviceAccountNamespace:
+                                  type: string
+                              required:
+                              - role
+                              - secret
+                              type: object
+                          type: object
+                      type: object
+                  type: object
+              type: object
+            enableShellCommand:
+              type: boolean
+            immediateDeploy:
+              type: boolean
+            schemahero:
+              properties:
+                image:
+                  type: string
+                nodeSelector:
+                  additionalProperties:
+                    type: string
+                  type: object
+              type: object
+          type: object
+        status:
           description: DatabaseStatus defines the observed state of Database
           properties:
             isConnected:
@@ -1878,6 +2149,277 @@ spec:
                 description: DatabaseConnection defines connection parameters for
                   the database driver
                 properties:
+                  cassandra:
+                    properties:
+                      hosts:
+                        items:
+                          type: string
+                        type: array
+                      keyspace:
+                        properties:
+                          value:
+                            type: string
+                          valueFrom:
+                            properties:
+                              secretKeyRef:
+                                properties:
+                                  key:
+                                    type: string
+                                  name:
+                                    type: string
+                                required:
+                                - key
+                                - name
+                                type: object
+                              ssm:
+                                properties:
+                                  accessKeyId:
+                                    properties:
+                                      value:
+                                        type: string
+                                      valueFrom:
+                                        properties:
+                                          secretKeyRef:
+                                            properties:
+                                              key:
+                                                type: string
+                                              name:
+                                                type: string
+                                            required:
+                                            - key
+                                            - name
+                                            type: object
+                                        type: object
+                                    required:
+                                    - value
+                                    type: object
+                                  name:
+                                    type: string
+                                  region:
+                                    type: string
+                                  secretAccessKey:
+                                    properties:
+                                      value:
+                                        type: string
+                                      valueFrom:
+                                        properties:
+                                          secretKeyRef:
+                                            properties:
+                                              key:
+                                                type: string
+                                              name:
+                                                type: string
+                                            required:
+                                            - key
+                                            - name
+                                            type: object
+                                        type: object
+                                    required:
+                                    - value
+                                    type: object
+                                  withDecryption:
+                                    type: boolean
+                                required:
+                                - name
+                                type: object
+                              vault:
+                                properties:
+                                  agentInject:
+                                    type: boolean
+                                  endpoint:
+                                    type: string
+                                  role:
+                                    type: string
+                                  secret:
+                                    type: string
+                                  serviceAccount:
+                                    type: string
+                                  serviceAccountNamespace:
+                                    type: string
+                                required:
+                                - role
+                                - secret
+                                type: object
+                            type: object
+                        type: object
+                      password:
+                        properties:
+                          value:
+                            type: string
+                          valueFrom:
+                            properties:
+                              secretKeyRef:
+                                properties:
+                                  key:
+                                    type: string
+                                  name:
+                                    type: string
+                                required:
+                                - key
+                                - name
+                                type: object
+                              ssm:
+                                properties:
+                                  accessKeyId:
+                                    properties:
+                                      value:
+                                        type: string
+                                      valueFrom:
+                                        properties:
+                                          secretKeyRef:
+                                            properties:
+                                              key:
+                                                type: string
+                                              name:
+                                                type: string
+                                            required:
+                                            - key
+                                            - name
+                                            type: object
+                                        type: object
+                                    required:
+                                    - value
+                                    type: object
+                                  name:
+                                    type: string
+                                  region:
+                                    type: string
+                                  secretAccessKey:
+                                    properties:
+                                      value:
+                                        type: string
+                                      valueFrom:
+                                        properties:
+                                          secretKeyRef:
+                                            properties:
+                                              key:
+                                                type: string
+                                              name:
+                                                type: string
+                                            required:
+                                            - key
+                                            - name
+                                            type: object
+                                        type: object
+                                    required:
+                                    - value
+                                    type: object
+                                  withDecryption:
+                                    type: boolean
+                                required:
+                                - name
+                                type: object
+                              vault:
+                                properties:
+                                  agentInject:
+                                    type: boolean
+                                  endpoint:
+                                    type: string
+                                  role:
+                                    type: string
+                                  secret:
+                                    type: string
+                                  serviceAccount:
+                                    type: string
+                                  serviceAccountNamespace:
+                                    type: string
+                                required:
+                                - role
+                                - secret
+                                type: object
+                            type: object
+                        type: object
+                      username:
+                        properties:
+                          value:
+                            type: string
+                          valueFrom:
+                            properties:
+                              secretKeyRef:
+                                properties:
+                                  key:
+                                    type: string
+                                  name:
+                                    type: string
+                                required:
+                                - key
+                                - name
+                                type: object
+                              ssm:
+                                properties:
+                                  accessKeyId:
+                                    properties:
+                                      value:
+                                        type: string
+                                      valueFrom:
+                                        properties:
+                                          secretKeyRef:
+                                            properties:
+                                              key:
+                                                type: string
+                                              name:
+                                                type: string
+                                            required:
+                                            - key
+                                            - name
+                                            type: object
+                                        type: object
+                                    required:
+                                    - value
+                                    type: object
+                                  name:
+                                    type: string
+                                  region:
+                                    type: string
+                                  secretAccessKey:
+                                    properties:
+                                      value:
+                                        type: string
+                                      valueFrom:
+                                        properties:
+                                          secretKeyRef:
+                                            properties:
+                                              key:
+                                                type: string
+                                              name:
+                                                type: string
+                                            required:
+                                            - key
+                                            - name
+                                            type: object
+                                        type: object
+                                    required:
+                                    - value
+                                    type: object
+                                  withDecryption:
+                                    type: boolean
+                                required:
+                                - name
+                                type: object
+                              vault:
+                                properties:
+                                  agentInject:
+                                    type: boolean
+                                  endpoint:
+                                    type: string
+                                  role:
+                                    type: string
+                                  secret:
+                                    type: string
+                                  serviceAccount:
+                                    type: string
+                                  serviceAccountNamespace:
+                                    type: string
+                                required:
+                                - role
+                                - secret
+                                type: object
+                            type: object
+                        type: object
+                    required:
+                    - hosts
+                    - keyspace
+                    type: object
                   cockroachdb:
                     properties:
                       dbname:
diff --git a/pkg/installer/table_objects.go b/pkg/installer/table_objects.go
index 30a19bf7..357dd1c5 100644
--- a/pkg/installer/table_objects.go
+++ b/pkg/installer/table_objects.go
@@ -46,6 +46,73 @@ spec:
               type: array
             schema:
               properties:
+                cassandra:
+                  properties:
+                    clusteringOrder:
+                      properties:
+                        column:
+                          type: string
+                        isDescending:
+                          type: boolean
+                      required:
+                      - column
+                      type: object
+                    columns:
+                      items:
+                        properties:
+                          isStatic:
+                            type: boolean
+                          name:
+                            type: string
+                          type:
+                            type: string
+                        required:
+                        - name
+                        - type
+                        type: object
+                      type: array
+                    isDeleted:
+                      type: boolean
+                    primaryKey:
+                      items:
+                        items:
+                          type: string
+                        type: array
+                      type: array
+                    properties:
+                      properties:
+                        bloomFilterFPChance:
+                          type: string
+                        caching:
+                          additionalProperties:
+                            type: string
+                          type: object
+                        comment:
+                          type: string
+                        compaction:
+                          type: string
+                        compression:
+                          type: string
+                        crcCheckChance:
+                          type: string
+                        dcLocalReadRepairChance:
+                          type: string
+                        defaultTTL:
+                          type: integer
+                        gcGraceSeconds:
+                          type: integer
+                        maxIndexInterval:
+                          type: integer
+                        memtableFlushPeriodMs:
+                          type: integer
+                        minIndexInterval:
+                          type: integer
+                        readRepairChance:
+                          type: string
+                        speculativeRetry:
+                          type: string
+                      type: object
+                  type: object
                 cockroachdb:
                   properties:
                     columns:
@@ -365,6 +432,73 @@ spec:
                 type: array
               schema:
                 properties:
+                  cassandra:
+                    properties:
+                      clusteringOrder:
+                        properties:
+                          column:
+                            type: string
+                          isDescending:
+                            type: boolean
+                        required:
+                        - column
+                        type: object
+                      columns:
+                        items:
+                          properties:
+                            isStatic:
+                              type: boolean
+                            name:
+                              type: string
+                            type:
+                              type: string
+                          required:
+                          - name
+                          - type
+                          type: object
+                        type: array
+                      isDeleted:
+                        type: boolean
+                      primaryKey:
+                        items:
+                          items:
+                            type: string
+                          type: array
+                        type: array
+                      properties:
+                        properties:
+                          bloomFilterFPChance:
+                            type: string
+                          caching:
+                            additionalProperties:
+                              type: string
+                            type: object
+                          comment:
+                            type: string
+                          compaction:
+                            type: string
+                          compression:
+                            type: string
+                          crcCheckChance:
+                            type: string
+                          dcLocalReadRepairChance:
+                            type: string
+                          defaultTTL:
+                            type: integer
+                          gcGraceSeconds:
+                            type: integer
+                          maxIndexInterval:
+                            type: integer
+                          memtableFlushPeriodMs:
+                            type: integer
+                          minIndexInterval:
+                            type: integer
+                          readRepairChance:
+                            type: string
+                          speculativeRetry:
+                            type: string
+                        type: object
+                    type: object
                   cockroachdb:
                     properties:
                       columns:


Test patch:
diff --git a/integration/tests/cassandra/Makefile b/integration/tests/cassandra/Makefile
new file mode 100644
index 00000000..85626f03
--- /dev/null
+++ b/integration/tests/cassandra/Makefile
@@ -0,0 +1,19 @@
+
+SHELL := /bin/bash
+
+export IMAGE
+export GO111MODULE=on
+
+.PHONY: run
+run: 3.11.8
+
+.PHONY: 3.11.8
+3.11.8: export CASSANDRA_VERSION = 3.11.8
+3.11.8:
+	make -C create-table run
+	make -C create-type run
+	make -C add-column run
+	make -C change-column-type run
+	make -C create-table-with-properties run
+	make -C alter-table-properties run
+	make -C alter-table-default-properties-unchanged run
\ No newline at end of file
diff --git a/integration/tests/cassandra/add-column/Makefile b/integration/tests/cassandra/add-column/Makefile
new file mode 100644
index 00000000..ff358f03
--- /dev/null
+++ b/integration/tests/cassandra/add-column/Makefile
@@ -0,0 +1,6 @@
+include ../common.mk
+
+TEST_NAME := cassandra-add-column
+SPEC_FILE := ./specs/users.yaml
+FIXTURES_FILE := ./fixtures.sql
+SPEC_TYPE := table
\ No newline at end of file
diff --git a/integration/tests/cassandra/add-column/expect.sql b/integration/tests/cassandra/add-column/expect.sql
new file mode 100644
index 00000000..797efeed
--- /dev/null
+++ b/integration/tests/cassandra/add-column/expect.sql
@@ -0,0 +1,1 @@
+alter table "schemahero.users" add name text;
diff --git a/integration/tests/cassandra/add-column/fixtures.sql b/integration/tests/cassandra/add-column/fixtures.sql
new file mode 100644
index 00000000..fdd9f337
--- /dev/null
+++ b/integration/tests/cassandra/add-column/fixtures.sql
@@ -0,0 +1,4 @@
+create table users (
+  id int primary key,
+  login varchar
+);
diff --git a/integration/tests/cassandra/add-column/specs/users.yaml b/integration/tests/cassandra/add-column/specs/users.yaml
new file mode 100644
index 00000000..dc4a14db
--- /dev/null
+++ b/integration/tests/cassandra/add-column/specs/users.yaml
@@ -0,0 +1,14 @@
+database: schemahero
+name: users
+requires: []
+schema:
+  cassandra:
+    primaryKey:
+      - [id]
+    columns:
+      - name: id
+        type: int
+      - name: login
+        type: text
+      - name: name
+        type: text
diff --git a/integration/tests/cassandra/alter-table-default-properties-unchanged/Makefile b/integration/tests/cassandra/alter-table-default-properties-unchanged/Makefile
new file mode 100644
index 00000000..a495476f
--- /dev/null
+++ b/integration/tests/cassandra/alter-table-default-properties-unchanged/Makefile
@@ -0,0 +1,6 @@
+include ../common.mk
+
+TEST_NAME := cassandra-alter-table-default-properties-unchanged
+SPEC_FILE := ./specs/users.yaml
+FIXTURES_FILE := ./fixtures.sql
+SPEC_TYPE := table
\ No newline at end of file
diff --git a/integration/tests/cassandra/alter-table-default-properties-unchanged/expect.sql b/integration/tests/cassandra/alter-table-default-properties-unchanged/expect.sql
new file mode 100644
index 00000000..e69de29b
diff --git a/integration/tests/cassandra/alter-table-default-properties-unchanged/fixtures.sql b/integration/tests/cassandra/alter-table-default-properties-unchanged/fixtures.sql
new file mode 100644
index 00000000..e03d58a7
--- /dev/null
+++ b/integration/tests/cassandra/alter-table-default-properties-unchanged/fixtures.sql
@@ -0,0 +1,6 @@
+create table users (
+  id int, 
+  login varchar, 
+  name varchar, 
+  primary key (id)
+);
\ No newline at end of file
diff --git a/integration/tests/cassandra/alter-table-default-properties-unchanged/specs/users.yaml b/integration/tests/cassandra/alter-table-default-properties-unchanged/specs/users.yaml
new file mode 100644
index 00000000..d2be6541
--- /dev/null
+++ b/integration/tests/cassandra/alter-table-default-properties-unchanged/specs/users.yaml
@@ -0,0 +1,15 @@
+database: schemahero
+name: users
+requires: []
+schema:
+  cassandra:
+    primaryKey:
+      - [id]
+    columns:
+      - name: id
+        type: int
+      - name: login
+        type: varchar
+      - name: name
+        type: varchar
+        default: ethan
diff --git a/integration/tests/cassandra/alter-table-properties/Makefile b/integration/tests/cassandra/alter-table-properties/Makefile
new file mode 100644
index 00000000..42c3b27c
--- /dev/null
+++ b/integration/tests/cassandra/alter-table-properties/Makefile
@@ -0,0 +1,6 @@
+include ../common.mk
+
+TEST_NAME := cassandra-alter-table-properties
+SPEC_FILE := ./specs/users.yaml
+FIXTURES_FILE := ./fixtures.sql
+SPEC_TYPE := table
\ No newline at end of file
diff --git a/integration/tests/cassandra/alter-table-properties/expect.sql b/integration/tests/cassandra/alter-table-properties/expect.sql
new file mode 100644
index 00000000..8fa7e5cc
--- /dev/null
+++ b/integration/tests/cassandra/alter-table-properties/expect.sql
@@ -0,0 +1,1 @@
+alter table schemahero.users with bloom_filter_fp_chance = 0.5 and caching = {'keys':'NONE','rows_per_partition':'20'} and comment = 'updated' and compaction = {'class':'org.apache.cassandra.db.compaction.SizeTieredCompactionStrategy','max_threshold':'64','min_threshold':'4'} and compression = {'chunk_length_in_kb':'128','class':'org.apache.cassandra.io.compress.LZ4Compressor'} and crc_check_chance = '0.5' and dclocal_read_repair_chance = '0.5' and default_time_to_live = 100 and max_index_interval = 1024 and memtable_flush_period_in_ms = 10 and min_index_interval = 64 and speculative_retry = 'ALWAYS';
diff --git a/integration/tests/cassandra/alter-table-properties/fixtures.sql b/integration/tests/cassandra/alter-table-properties/fixtures.sql
new file mode 100644
index 00000000..2a800c2e
--- /dev/null
+++ b/integration/tests/cassandra/alter-table-properties/fixtures.sql
@@ -0,0 +1,19 @@
+create table users (
+  id int, 
+  login varchar, 
+  name varchar, 
+  primary key (id)
+) 
+with 
+  bloom_filter_fp_chance = 0.01 AND
+  caching = {'keys': 'NONE', 'rows_per_partition': '10'} AND
+  comment = 'test' AND 
+  compaction = {'class': 'org.apache.cassandra.db.compaction.SizeTieredCompactionStrategy', 'max_threshold': '32', 'min_threshold': '4'} AND  
+  compression = {'chunk_length_in_kb': '64', 'class': 'org.apache.cassandra.io.compress.LZ4Compressor'} AND 
+  crc_check_chance = 1.0 AND 
+  dclocal_read_repair_chance = 0.1 AND 
+  default_time_to_live = 0 AND 
+  max_index_interval = 2048 AND 
+  memtable_flush_period_in_ms = 0 AND 
+  min_index_interval = 128 AND 
+  speculative_retry = '99PERCENTILE';
diff --git a/integration/tests/cassandra/alter-table-properties/specs/users.yaml b/integration/tests/cassandra/alter-table-properties/specs/users.yaml
new file mode 100644
index 00000000..1273ced4
--- /dev/null
+++ b/integration/tests/cassandra/alter-table-properties/specs/users.yaml
@@ -0,0 +1,38 @@
+database: schemahero
+name: users
+requires: []
+schema:
+  cassandra:
+    primaryKey:
+      - [id]
+    columns:
+      - name: id
+        type: int
+      - name: login
+        type: varchar
+      - name: name
+        type: varchar
+        default: ethan
+    properties:
+      bloomFilterFPChance: "0.5"
+      caching:
+        keys: 'NONE'
+        rows_per_partition: "20"
+      comment: "updated"
+      compaction: 
+        class: 'org.apache.cassandra.db.compaction.SizeTieredCompactionStrategy'
+        max_threshold: '64'
+        min_threshold: '4'
+      compression: 
+        chunk_length_in_kb: '128' 
+        class: 'org.apache.cassandra.io.compress.LZ4Compressor'
+      crcCheckChance: "0.5"
+      dcLocalReadRepairChance: "0.5"
+      defaultTTL: 100
+      gcGraceSecons: 43200
+      maxIndexInterval: 1024
+      memtableFlushPeriodMs: 10
+      minIndexInterval: 64
+      readRepairChange: "0.5"
+      speculativeRetry: ALWAYS
+
diff --git a/integration/tests/cassandra/change-column-type/Makefile b/integration/tests/cassandra/change-column-type/Makefile
new file mode 100644
index 00000000..c8bd5973
--- /dev/null
+++ b/integration/tests/cassandra/change-column-type/Makefile
@@ -0,0 +1,6 @@
+include ../common.mk
+
+TEST_NAME := cassandra-change-column-type
+SPEC_FILE := ./specs/users.yaml
+FIXTURES_FILE := ./fixtures.sql
+SPEC_TYPE := table
\ No newline at end of file
diff --git a/integration/tests/cassandra/change-column-type/expect.sql b/integration/tests/cassandra/change-column-type/expect.sql
new file mode 100644
index 00000000..406ac0de
--- /dev/null
+++ b/integration/tests/cassandra/change-column-type/expect.sql
@@ -0,0 +1,1 @@
+alter table "schemahero.users" alter column login type bigint;
diff --git a/integration/tests/cassandra/change-column-type/fixtures.sql b/integration/tests/cassandra/change-column-type/fixtures.sql
new file mode 100644
index 00000000..539ad439
--- /dev/null
+++ b/integration/tests/cassandra/change-column-type/fixtures.sql
@@ -0,0 +1,4 @@
+create table users (
+  id int primary key,
+  login int
+);
diff --git a/integration/tests/cassandra/change-column-type/specs/users.yaml b/integration/tests/cassandra/change-column-type/specs/users.yaml
new file mode 100644
index 00000000..080cf36f
--- /dev/null
+++ b/integration/tests/cassandra/change-column-type/specs/users.yaml
@@ -0,0 +1,12 @@
+database: schemahero
+name: users
+requires: []
+schema:
+  cassandra:
+    primaryKey:
+      - [id]
+    columns:
+      - name: id
+        type: int
+      - name: login
+        type: bigint
diff --git a/integration/tests/cassandra/common.mk b/integration/tests/cassandra/common.mk
new file mode 100644
index 00000000..95892b49
--- /dev/null
+++ b/integration/tests/cassandra/common.mk
@@ -0,0 +1,29 @@
+SHELL := /bin/bash
+DATABASE_CONTAINER_NAME := schemahero-database
+DRIVER := cassandra
+
+.PHONY: run
+run:
+	# Fixtures
+	docker pull cassandra:$(CASSANDRA_VERSION)
+	@-docker rm -f $(DATABASE_CONTAINER_NAME) > /dev/null 2>&1 ||:
+	docker run -p 15432:5432 --rm -d --name $(DATABASE_CONTAINER_NAME) -p 9042:9042 -v `pwd`/$(FIXTURES_FILE):/fixtures.sql cassandra:$(CASSANDRA_VERSION)
+	@-docker exec $(DATABASE_CONTAINER_NAME) /bin/bash -c "while ! cqlsh -e 'describe cluster'; do sleep 1; done" > /dev/null 2>&1
+	docker exec $(DATABASE_CONTAINER_NAME) cqlsh -e "CREATE KEYSPACE schemahero with replication = { 'class': 'SimpleStrategy', 'replication_factor': 1 }"
+
+	docker exec $(DATABASE_CONTAINER_NAME) cqlsh -k schemahero -f /fixtures.sql
+
+	# Plan
+	../../../../bin/kubectl-schemahero plan --keyspace schemahero --host 127.0.0.1:9042 --driver=$(DRIVER) --spec-type $(SPEC_TYPE) --spec-file $(SPEC_FILE) > out.sql
+
+	# Verify
+	@echo Verifying results for $(TEST_NAME)
+	diff -B expect.sql out.sql
+
+	# Apply
+	../../../../bin/kubectl-schemahero apply --keyspace schemahero --host 127.0.0.1:9042 --driver=$(DRIVER) --ddl out.sql
+
+	# Cleanup
+	@-sleep 5
+	rm ./out.sql
+	@-docker rm -f $(DATABASE_CONTAINER_NAME)
diff --git a/integration/tests/cassandra/create-table-with-properties/Makefile b/integration/tests/cassandra/create-table-with-properties/Makefile
new file mode 100644
index 00000000..c50723ea
--- /dev/null
+++ b/integration/tests/cassandra/create-table-with-properties/Makefile
@@ -0,0 +1,6 @@
+include ../common.mk
+
+TEST_NAME := cassandra-create-table-with-properties
+SPEC_FILE := ./specs/users.yaml
+FIXTURES_FILE := ./fixtures.sql
+SPEC_TYPE := table
\ No newline at end of file
diff --git a/integration/tests/cassandra/create-table-with-properties/expect.sql b/integration/tests/cassandra/create-table-with-properties/expect.sql
new file mode 100644
index 00000000..5689369d
--- /dev/null
+++ b/integration/tests/cassandra/create-table-with-properties/expect.sql
@@ -0,0 +1,1 @@
+create table "schemahero.users" (id int, login varchar, name varchar, primary key (id)) with bloom_filter_fp_chance = 0.01 AND caching = {"keys":"NONE","rows_per_partition'":"10"} AND comment = 'test' AND compaction = {"class":"org.apache.cassandra.db.compaction.SizeTieredCompactionStrategy","max_threshold":"32","min_threshold'":"4"} AND compression = {"chunk_length_in_kb":"64","class":"org.apache.cassandra.io.compress.LZ4Compressor"} AND crc_check_chance = 1.0 AND dclocal_read_repair_chance = 0.1 AND default_time_to_live = 0 AND grace_seconds = 86400 AND max_index_interval = 2048 AND memtable_flush_period_in_ms = 0 AND min_index_interval = 128 AND speculative_retry = '99PERCENTILE';
diff --git a/integration/tests/cassandra/create-table-with-properties/fixtures.sql b/integration/tests/cassandra/create-table-with-properties/fixtures.sql
new file mode 100644
index 00000000..b2d1d765
--- /dev/null
+++ b/integration/tests/cassandra/create-table-with-properties/fixtures.sql
@@ -0,0 +1,4 @@
+create table other (
+  id int primary key,
+  something varchar
+);
diff --git a/integration/tests/cassandra/create-table-with-properties/specs/users.yaml b/integration/tests/cassandra/create-table-with-properties/specs/users.yaml
new file mode 100644
index 00000000..9d8755e7
--- /dev/null
+++ b/integration/tests/cassandra/create-table-with-properties/specs/users.yaml
@@ -0,0 +1,38 @@
+database: schemahero
+name: users
+requires: []
+schema:
+  cassandra:
+    primaryKey:
+      - [id]
+    columns:
+      - name: id
+        type: int
+      - name: login
+        type: varchar
+      - name: name
+        type: varchar
+        default: ethan
+    properties:
+      bloomFilterFPChance: "0.01"
+      caching: 
+        keys: NONE
+        rows_per_partition': '10'
+      comment: "test"
+      compaction: 
+        class: 'org.apache.cassandra.db.compaction.SizeTieredCompactionStrategy'
+        max_threshold: '32'
+        min_threshold': '4'
+      compression:
+        chunk_length_in_kb: '64'
+        class: 'org.apache.cassandra.io.compress.LZ4Compressor'
+      crcCheckChance: "1.0"
+      dcLocalReadRepairChance: "0.1"
+      defaultTTL: 0
+      gcGraceSeconds: 86400
+      maxIndexInterval: 2048
+      memtableFlushPeriodMs: 0
+      minIndexInterval: 128
+      readRepairChange: "0.0"
+      speculativeRetry: 99PERCENTILE
+
diff --git a/integration/tests/cassandra/create-table/Makefile b/integration/tests/cassandra/create-table/Makefile
new file mode 100644
index 00000000..516bd2fc
--- /dev/null
+++ b/integration/tests/cassandra/create-table/Makefile
@@ -0,0 +1,6 @@
+include ../common.mk
+
+TEST_NAME := cassandra-create-table
+SPEC_FILE := ./specs/users.yaml
+FIXTURES_FILE := ./fixtures.sql
+SPEC_TYPE := table
\ No newline at end of file
diff --git a/integration/tests/cassandra/create-table/expect.sql b/integration/tests/cassandra/create-table/expect.sql
new file mode 100644
index 00000000..2a540d56
--- /dev/null
+++ b/integration/tests/cassandra/create-table/expect.sql
@@ -0,0 +1,1 @@
+create table "schemahero.users" (id int, login varchar, name varchar, primary key (id));
diff --git a/integration/tests/cassandra/create-table/fixtures.sql b/integration/tests/cassandra/create-table/fixtures.sql
new file mode 100644
index 00000000..b2d1d765
--- /dev/null
+++ b/integration/tests/cassandra/create-table/fixtures.sql
@@ -0,0 +1,4 @@
+create table other (
+  id int primary key,
+  something varchar
+);
diff --git a/integration/tests/cassandra/create-table/specs/users.yaml b/integration/tests/cassandra/create-table/specs/users.yaml
new file mode 100644
index 00000000..d2be6541
--- /dev/null
+++ b/integration/tests/cassandra/create-table/specs/users.yaml
@@ -0,0 +1,15 @@
+database: schemahero
+name: users
+requires: []
+schema:
+  cassandra:
+    primaryKey:
+      - [id]
+    columns:
+      - name: id
+        type: int
+      - name: login
+        type: varchar
+      - name: name
+        type: varchar
+        default: ethan
diff --git a/integration/tests/cassandra/create-type/Makefile b/integration/tests/cassandra/create-type/Makefile
new file mode 100644
index 00000000..4296e4b8
--- /dev/null
+++ b/integration/tests/cassandra/create-type/Makefile
@@ -0,0 +1,6 @@
+include ../common.mk
+
+TEST_NAME := cassandra-create-type
+SPEC_FILE := ./specs/basic_info.yaml
+FIXTURES_FILE := ./fixtures.sql
+SPEC_TYPE := type
\ No newline at end of file
diff --git a/integration/tests/cassandra/create-type/expect.sql b/integration/tests/cassandra/create-type/expect.sql
new file mode 100644
index 00000000..ee896512
--- /dev/null
+++ b/integration/tests/cassandra/create-type/expect.sql
@@ -0,0 +1,1 @@
+create type "basic_info" (birthday timestamp, nationality text, weight text, height text);
diff --git a/integration/tests/cassandra/create-type/fixtures.sql b/integration/tests/cassandra/create-type/fixtures.sql
new file mode 100644
index 00000000..b2d1d765
--- /dev/null
+++ b/integration/tests/cassandra/create-type/fixtures.sql
@@ -0,0 +1,4 @@
+create table other (
+  id int primary key,
+  something varchar
+);
diff --git a/integration/tests/cassandra/create-type/specs/basic_info.yaml b/integration/tests/cassandra/create-type/specs/basic_info.yaml
new file mode 100644
index 00000000..c83d4eba
--- /dev/null
+++ b/integration/tests/cassandra/create-type/specs/basic_info.yaml
@@ -0,0 +1,13 @@
+database: schemahero
+name: basic_info
+schema:
+  cassandra:
+    fields:
+      - name: birthday
+        type: timestamp
+      - name: nationality
+        type: text
+      - name: weight
+        type: text
+      - name: height
+        type: text
diff --git a/pkg/database/cassandra/column_test.go b/pkg/database/cassandra/column_test.go
new file mode 100644
index 00000000..01ddbc38
--- /dev/null
+++ b/pkg/database/cassandra/column_test.go
@@ -0,0 +1,58 @@
+package cassandra
+
+import (
+	"testing"
+
+	schemasv1alpha4 "github.com/schemahero/schemahero/pkg/apis/schemas/v1alpha4"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+var (
+	trueValue = true
+)
+
+func Test_cassandraColumnAsInsert(t *testing.T) {
+	tests := []struct {
+		name              string
+		column            *schemasv1alpha4.CassandraColumn
+		expectedStatement string
+	}{
+		{
+			name: "simple",
+			column: &schemasv1alpha4.CassandraColumn{
+				Name: "c",
+				Type: "int",
+			},
+			expectedStatement: `c int`,
+		},
+		{
+			name: "text",
+			column: &schemasv1alpha4.CassandraColumn{
+				Name: "t",
+				Type: "text",
+			},
+			expectedStatement: `t text`,
+		},
+		{
+			name: "simple",
+			column: &schemasv1alpha4.CassandraColumn{
+				Name:     "c",
+				Type:     "int",
+				IsStatic: &trueValue,
+			},
+			expectedStatement: `c int static`,
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			req := require.New(t)
+
+			generatedStatement, err := cassandraColumnAsInsert(test.column)
+			req.NoError(err)
+			assert.Equal(t, test.expectedStatement, generatedStatement)
+		})
+	}
+}
diff --git a/pkg/database/cassandra/create_test.go b/pkg/database/cassandra/create_test.go
new file mode 100644
index 00000000..e78aa0fa
--- /dev/null
+++ b/pkg/database/cassandra/create_test.go
@@ -0,0 +1,155 @@
+package cassandra
+
+import (
+	"testing"
+
+	schemasv1alpha4 "github.com/schemahero/schemahero/pkg/apis/schemas/v1alpha4"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+func Test_CreateTableStatement(t *testing.T) {
+	tests := []struct {
+		name              string
+		keyspace          string
+		tableName         string
+		tableSchema       schemasv1alpha4.CassandraTableSchema
+		expectedStatement string
+	}{
+		{
+			name:      "simple",
+			keyspace:  "k",
+			tableName: "t",
+			tableSchema: schemasv1alpha4.CassandraTableSchema{
+				Columns: []*schemasv1alpha4.CassandraColumn{
+					{
+						Name: "a",
+						Type: "int",
+					},
+				},
+			},
+			expectedStatement: `create table t (a int)`,
+		},
+		{
+			name:      "with pk",
+			keyspace:  "k",
+			tableName: "t",
+			tableSchema: schemasv1alpha4.CassandraTableSchema{
+				PrimaryKey: [][]string{
+					{
+						"a",
+					},
+				},
+				Columns: []*schemasv1alpha4.CassandraColumn{
+					{
+						Name: "a",
+						Type: "int",
+					},
+				},
+			},
+			expectedStatement: `create table t (a int, primary key (a))`,
+		},
+		{
+			name:      "with compound pk",
+			keyspace:  "k",
+			tableName: "t",
+			tableSchema: schemasv1alpha4.CassandraTableSchema{
+				PrimaryKey: [][]string{
+					{
+						"a",
+					},
+					{
+						"b",
+					},
+				},
+				Columns: []*schemasv1alpha4.CassandraColumn{
+					{
+						Name: "a",
+						Type: "int",
+					},
+					{
+						Name: "b",
+						Type: "int",
+					},
+				},
+			},
+			expectedStatement: `create table t (a int, b int, primary key (a, b))`,
+		},
+		{
+			name:      "with composite partition pk",
+			keyspace:  "k",
+			tableName: "t",
+			tableSchema: schemasv1alpha4.CassandraTableSchema{
+				PrimaryKey: [][]string{
+					{
+						"a", "b",
+					},
+					{
+						"c",
+					},
+				},
+				Columns: []*schemasv1alpha4.CassandraColumn{
+					{
+						Name: "a",
+						Type: "int",
+					},
+					{
+						Name: "b",
+						Type: "int",
+					},
+					{
+						Name: "c",
+						Type: "int",
+					},
+				},
+			},
+			expectedStatement: `create table t (a int, b int, c int, primary key ((a, b), c))`,
+		},
+		{
+			name:      "clustering order",
+			keyspace:  "k",
+			tableName: "t",
+			tableSchema: schemasv1alpha4.CassandraTableSchema{
+				ClusteringOrder: &schemasv1alpha4.CassandraClusteringOrder{
+					Column: "a",
+				},
+				Columns: []*schemasv1alpha4.CassandraColumn{
+					{
+						Name: "a",
+						Type: "int",
+					},
+				},
+			},
+			expectedStatement: `create table t (a int) with clustering order by (a)`,
+		},
+		{
+			name:      "clustering order desc",
+			keyspace:  "k",
+			tableName: "t",
+			tableSchema: schemasv1alpha4.CassandraTableSchema{
+				ClusteringOrder: &schemasv1alpha4.CassandraClusteringOrder{
+					Column:       "a",
+					IsDescending: &trueValue,
+				},
+				Columns: []*schemasv1alpha4.CassandraColumn{
+					{
+						Name: "a",
+						Type: "int",
+					},
+				},
+			},
+			expectedStatement: `create table t (a int) with clustering order by (a desc)`,
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			req := require.New(t)
+
+			generatedStatement, err := CreateTableStatement(test.keyspace, test.tableName, &test.tableSchema)
+			req.NoError(err)
+			assert.Equal(t, test.expectedStatement, generatedStatement)
+		})
+	}
+}