Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Schema DDL: separate redshift-specific code from standard SQL (Closes #372) #373

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@
*/
package com.snowplowanalytics.iglu.schemaddl.redshift

import com.snowplowanalytics.iglu.schemaddl.sql.{AlterTableStatement, ColumnAttribute, DataType, Ddl, Default, Nullability}

/**
* Class holding data to alter some table with single [[AlterTableStatement]]
*
* @see http://docs.aws.amazon.com/redshift/latest/dg/r_ALTER_TABLE.html
*
* ALTER TABLE table_name
Expand All @@ -37,56 +40,18 @@ package com.snowplowanalytics.iglu.schemaddl.redshift
* FOREIGN KEY (column_name [, ... ] )
* REFERENCES reftable [ ( refcolumn ) ]}
*/
case class AlterTable(tableName: String, statement: AlterTableStatement) extends Statement {
def toDdl = s"ALTER TABLE $tableName ${statement.toDdl}"
}

/**
* Sum-type to represent some statement
*/
sealed trait AlterTableStatement extends Ddl

sealed trait DropModeValue extends Ddl
case object CascadeDrop extends DropModeValue { def toDdl = "CASCADE" }
case object RestrictDrop extends DropModeValue { def toDdl = "RESTRICT" }
sealed trait RedShiftAlterTableStatement extends RedShiftDdl

case class DropMode(value: DropModeValue) extends Ddl {
def toDdl = value.toDdl
}

case class AddConstraint(tableConstraint: TableConstraint) extends AlterTableStatement {
def toDdl = s"ADD ${tableConstraint.toDdl}"
}

case class DropConstraint(constraintName: String, mode: Option[DropMode]) extends AlterTableStatement {
def toDdl = s"DROP $constraintName${mode.map(" " + _.toDdl).getOrElse("")}"
}

case class OwnerTo(newOwner: String) extends AlterTableStatement {
def toDdl = s"OWNER TO $newOwner"
}

case class RenameTo(newName: String) extends AlterTableStatement {
def toDdl = s"RENAME TO $newName"
}

case class RenameColumn(columnName: String, newName: String) extends AlterTableStatement {
def toDdl = s"RENAME COLUMN $columnName TO $newName"
}

case class AddColumn(
private[redshift] case class AddColumn[T <: RedShiftDdl](
columnName: String,
columnType: DataType,
default: Option[Default],
encode: Option[CompressionEncoding],
nullability: Option[Nullability]
columnType: DataType[Ddl],
default: Option[Default[T]],
encode: Option[ColumnAttribute[T]],
nullability: Option[Nullability[T]]
) extends AlterTableStatement {
def toDdl = {
val attrs = List(nullability, encode, default).flatten.map(_.toDdl).mkString(" ")
s"""ADD COLUMN "$columnName" ${columnType.toDdl} $attrs"""
}
}

case class DropColumn(columnName: String, mode: Option[DropMode]) extends Ddl {
def toDdl = s"DROP COLUMN $columnName${mode.map(" " + _.toDdl).getOrElse("")}"
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
*/
package com.snowplowanalytics.iglu.schemaddl.redshift

import com.snowplowanalytics.iglu.schemaddl.sql.{ColumnAttribute, ColumnConstraint, DataType, Ddl}

/**
* Class holding all information about Redshift's column
*
Expand All @@ -20,12 +22,12 @@ package com.snowplowanalytics.iglu.schemaddl.redshift
* @param columnAttributes set of column_attributes such as ENCODE
* @param columnConstraints set of column_constraints such as NOT NULL
*/
case class Column(
private[redshift] case class Column[T <: RedShiftDdl](
columnName: String,
dataType: DataType,
columnAttributes: Set[ColumnAttribute] = Set.empty[ColumnAttribute],
columnConstraints: Set[ColumnConstraint] = Set.empty[ColumnConstraint]
) extends Ddl {
dataType: DataType[Ddl],
columnAttributes: Set[ColumnAttribute[T]] = Set.empty[ColumnAttribute[T]],
columnConstraints: Set[ColumnConstraint[T]] = Set.empty[ColumnConstraint[T]]
) extends RedShiftDdl {

/**
* Formatted column's DDL
Expand All @@ -46,7 +48,7 @@ case class Column(
*
* @return string representing column without formatting
*/
def toDdl = toFormattedDdl((1, 1, 1, 1, 1))
override def toDdl = toFormattedDdl((1, 1, 1, 1, 1))

// Get warnings only from data types suggestions
override val warnings = dataType.warnings
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,43 +10,32 @@
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
*/
package com.snowplowanalytics.iglu.schemaddl.redshift
package com.snowplowanalytics.iglu.schemaddl
package redshift

/**
* column_attributes are:
* [ DEFAULT default_expr ]
* [ IDENTITY ( seed, step ) ]
* [ ENCODE encoding ]
* [ DISTKEY ]
* [ SORTKEY ]
*/
sealed trait ColumnAttribute extends Ddl

case class Default(value: String) extends ColumnAttribute {
def toDdl = s"DEFAULT $value"
}
import com.snowplowanalytics.iglu.schemaddl.sql.ColumnAttribute

case class Identity(seed: Int, step: Int) extends ColumnAttribute {
case class Identity(seed: Int, step: Int) extends ColumnAttribute[RedShiftDdl] {
def toDdl = s"IDENTITY ($seed, $step)"
}

case object DistKey extends ColumnAttribute {
case object DistKey extends ColumnAttribute[RedShiftDdl] {
def toDdl = "DISTKEY"
}

case object SortKey extends ColumnAttribute {
case object SortKey extends ColumnAttribute[RedShiftDdl] {
def toDdl = "SORTKEY"
}

/**
* Compression encodings
* http://docs.aws.amazon.com/redshift/latest/dg/c_Compression_encodings.html
*/
case class CompressionEncoding(value: CompressionEncodingValue) extends ColumnAttribute {
case class CompressionEncoding(value: CompressionEncodingValue) extends ColumnAttribute[RedShiftDdl] {
def toDdl = s"ENCODE ${value.toDdl}"
}

sealed trait CompressionEncodingValue extends Ddl
sealed trait CompressionEncodingValue extends RedShiftDdl

case object RawEncoding extends CompressionEncodingValue { def toDdl = "RAW" }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
*/
package com.snowplowanalytics.iglu.schemaddl.redshift
package com.snowplowanalytics.iglu.schemaddl
package redshift

import com.snowplowanalytics.iglu.schemaddl.sql.{Statement, TableAttribute, TableConstraint}

/**
* Class holding all information about Redshift's table
Expand All @@ -20,28 +23,28 @@ package com.snowplowanalytics.iglu.schemaddl.redshift
* @param tableConstraints set of table_constraints such as PRIMARY KEY
* @param tableAttributes set of table_attributes such as DISTSTYLE
*/
case class CreateTable(
tableName: String,
columns: List[Column],
tableConstraints: Set[TableConstraint] = Set.empty[TableConstraint],
tableAttributes: Set[TableAttribute] = Set.empty[TableAttribute]
) extends Statement {
private[redshift] case class CreateTable[T <: RedShiftDdl](
tableName: String,
columns: List[Column[T]],
tableConstraints: Set[TableConstraint] = Set.empty[TableConstraint],
tableAttributes: Set[TableAttribute[T]] = Set.empty[TableAttribute[T]]
) extends Statement {

def toDdl = {
val columnsDdl = columns.map(_.toFormattedDdl(tabulation)
.replaceAll("\\s+$", ""))
.mkString(",\n")
s"""CREATE TABLE IF NOT EXISTS $tableName (
|$columnsDdl$getConstraints
|)$getAttributes""".stripMargin
|$columnsDdl$getConstraints
|)$getAttributes""".stripMargin
}

// Collect warnings from every column
override val warnings = columns.flatMap(_.warnings)

// Tuple with lengths of each column in formatted DDL file
private val tabulation = {
def getLength(f: Column => Int): Int =
def getLength(f: Column[T] => Int): Int =
columns.foldLeft(0)((acc, b) => if (acc > f(b)) acc else f(b))

val prepend = 4
Expand All @@ -54,26 +57,25 @@ case class CreateTable(
}

/**
* Format constraints for table
*
* @return string with formatted table_constaints
*/
* Format constraints for table
*
* @return string with formatted table_constaints
*/
private def getConstraints: String = {
if (tableConstraints.isEmpty) ""
else ",\n" + tableConstraints.map(c => withTabs(tabulation._1, " ") + c.toDdl).

mkString("\n")
}
/**
* Format attributes for table
*
* @return string with formatted table_attributes
*/
* Format attributes for table
*
* @return string with formatted table_attributes
*/
private def getAttributes: String = {
if (tableConstraints.isEmpty) ""
else "\n" + tableAttributes.map(_.toDdl).

mkString("\n")
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright (c) 2014-2016 Snowplow Analytics Ltd. All rights reserved.
*
* This program is licensed to you under the Apache License Version 2.0,
* and you may not use this file except in compliance with the Apache License Version 2.0.
* You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the Apache License Version 2.0 is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
*/
package com.snowplowanalytics.iglu.schemaddl.redshift

import com.snowplowanalytics.iglu.schemaddl.sql.Ddl

/**
* Base class for everything that can be represented as Redshift DDL
*/
trait RedShiftDdl extends Ddl
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,22 @@ package com.snowplowanalytics.iglu.schemaddl.redshift
// Scalaz
import scalaz.NonEmptyList

// This project
import com.snowplowanalytics.iglu.schemaddl.sql.TableAttribute

/**
* table_attributes are:
* [ DISTSTYLE { EVEN | KEY | ALL } ]
* [ DISTKEY ( column_name ) ]
* [ [COMPOUND | INTERLEAVED ] SORTKEY ( column_name [, ...] ) ]
*/
sealed trait TableAttribute extends Ddl

sealed trait DiststyleValue extends Ddl
sealed trait DiststyleValue extends RedShiftDdl
case object Even extends DiststyleValue { def toDdl = "EVEN" }
case object Key extends DiststyleValue { def toDdl = "KEY" }
case object All extends DiststyleValue { def toDdl = "ALL" }

sealed trait Sortstyle extends Ddl
sealed trait Sortstyle extends RedShiftDdl

case object CompoundSortstyle extends Sortstyle {
def toDdl = "COMPOUND"
Expand All @@ -38,16 +40,16 @@ case object InterleavedSortstyle extends Sortstyle {
def toDdl = "INTERLEAVED"
}

case class Diststyle(diststyle: DiststyleValue) extends TableAttribute {
case class Diststyle(diststyle: DiststyleValue) extends TableAttribute[RedShiftDdl] {
def toDdl = "DISTSTYLE " + diststyle.toDdl
}

// Don't confuse with redshift.DistKey which is applicable for columns
case class DistKeyTable(columnName: String) extends TableAttribute {
case class DistKeyTable(columnName: String) extends TableAttribute[RedShiftDdl] {
def toDdl = s"DISTKEY ($columnName)"
}

// Don't confuse with redshift.SortKey which is applicable for columns
case class SortKeyTable(sortstyle: Option[Sortstyle], columns: NonEmptyList[String]) extends TableAttribute {
case class SortKeyTable(sortstyle: Option[Sortstyle], columns: NonEmptyList[String]) extends TableAttribute[RedShiftDdl] {
def toDdl = sortstyle.map(_.toDdl + " ").getOrElse("") + "SORTKEY (" + columns.list.mkString(",") + ")"
}
Loading