@@ -21,6 +21,8 @@ import com.snowplowanalytics.iglu.schemaddl.redshift.generators.{DdlFile, DdlGen
21
21
import com .snowplowanalytics .snowplow .rdbloader .LoadStatements
22
22
import com .snowplowanalytics .snowplow .rdbloader .common .Common
23
23
import com .snowplowanalytics .snowplow .rdbloader .common .config .TransformerConfig .Compression
24
+ import com .snowplowanalytics .snowplow .rdbloader .common .LoaderMessage .TypesInfo .WideRow .WideRowFormat .{JSON , PARQUET }
25
+ import com .snowplowanalytics .snowplow .rdbloader .common .LoaderMessage .TypesInfo
24
26
import com .snowplowanalytics .snowplow .rdbloader .config .{Config , StorageTarget }
25
27
import com .snowplowanalytics .snowplow .rdbloader .db .Columns .{ColumnsToCopy , ColumnsToSkip , EventTableColumns }
26
28
import com .snowplowanalytics .snowplow .rdbloader .db .Migration .{Block , Entity , Item , NoPreStatements , NoStatements }
@@ -43,7 +45,7 @@ object Redshift {
43
45
44
46
def build (config : Config [StorageTarget ]): Either [String , Target ] = {
45
47
config.storage match {
46
- case StorageTarget .Redshift (_, _, _, _, roleArn, schema, _, _, maxError, _) =>
48
+ case StorageTarget .Redshift (_, _, _, _, roleArn, schema, _, _, maxError, _, experimentalFeatures ) =>
47
49
val result = new Target {
48
50
49
51
override val requiresEventsColumns : Boolean = false
@@ -67,19 +69,39 @@ object Redshift {
67
69
Block (preTransaction.reverse, inTransaction.reverse, Entity .Table (schema, target))
68
70
}
69
71
70
- override def extendTable (info : ShreddedType .Info ): Option [Block ] =
71
- throw new IllegalStateException (" Redshift Loader does not support loading wide row" )
72
-
73
- override def getLoadStatements (discovery : DataDiscovery , eventTableColumns : EventTableColumns , loadAuthMethod : LoadAuthMethod ): LoadStatements = {
74
- val shreddedStatements = discovery
75
- .shreddedTypes
76
- .filterNot(_.isAtomic)
77
- .map(shreddedType => Statement .ShreddedCopy (shreddedType, discovery.compression))
78
-
79
- val atomic = Statement .EventsCopy (discovery.base, discovery.compression, ColumnsToCopy (AtomicColumns .Columns ), ColumnsToSkip .none, discovery.typesInfo, loadAuthMethod)
80
- NonEmptyList (atomic, shreddedStatements)
72
+ override def extendTable (info : ShreddedType .Info ): Option [Block ] = {
73
+ val columnName = info.getNameFull
74
+ val frTableName = Fragment .const(EventsTable .withSchema(schema))
75
+ val addColumnSql = sql " ALTER TABLE $frTableName ADD COLUMN $columnName SUPER "
76
+ Some (Block (List (Item .AddColumn (addColumnSql, Nil )), Nil , Entity .Column (info)))
81
77
}
82
78
79
+ override def getLoadStatements (discovery : DataDiscovery , eventTableColumns : EventTableColumns , loadAuthMethod : LoadAuthMethod ): LoadStatements =
80
+ discovery.typesInfo match {
81
+ case TypesInfo .Shredded (_) =>
82
+ val shreddedStatements = discovery
83
+ .shreddedTypes
84
+ .filterNot(_.isAtomic)
85
+ .map(shreddedType => Statement .ShreddedCopy (shreddedType, discovery.compression))
86
+
87
+ val atomic = Statement .EventsCopy (discovery.base, discovery.compression, ColumnsToCopy (AtomicColumns .Columns ), ColumnsToSkip .none, discovery.typesInfo, loadAuthMethod)
88
+ NonEmptyList (atomic, shreddedStatements)
89
+ case TypesInfo .WideRow (_, _) if ! experimentalFeatures.enableWideRow =>
90
+ throw new IllegalStateException (" Experimental widerow loading for Redshift is not enabled" )
91
+ case TypesInfo .WideRow (_, _) =>
92
+ val columnsToCopy = ColumnsToCopy .fromDiscoveredData(discovery)
93
+ NonEmptyList .one(
94
+ Statement .EventsCopy (
95
+ discovery.base,
96
+ discovery.compression,
97
+ columnsToCopy,
98
+ ColumnsToSkip .none,
99
+ discovery.typesInfo,
100
+ loadAuthMethod
101
+ )
102
+ )
103
+ }
104
+
83
105
override def createTable (schemas : SchemaList ): Block = {
84
106
val subschemas = FlatSchema .extractProperties(schemas)
85
107
val tableName = StringUtils .getTableName(schemas.latest)
@@ -110,7 +132,7 @@ object Redshift {
110
132
val frRoleArn = Fragment .const0(s " aws_iam_role= $roleArn" )
111
133
val frPath = Fragment .const0(source)
112
134
sql " COPY $frTableName FROM ' $frPath' CREDENTIALS ' $frRoleArn' DELIMITER ' $EventFieldSeparator' "
113
- case Statement .EventsCopy (path, compression, columnsToCopy, _, _ , _) =>
135
+ case Statement .EventsCopy (path, compression, columnsToCopy, _, typesInfo , _) =>
114
136
// For some reasons Redshift JDBC doesn't handle interpolation in COPY statements
115
137
val frTableName = Fragment .const(EventsTable .withSchema(schema))
116
138
val frPath = Fragment .const0(Common .entityPathFull(path, Common .AtomicType ))
@@ -119,13 +141,18 @@ object Redshift {
119
141
val frMaxError = Fragment .const0(maxError.toString)
120
142
val frCompression = getCompressionFormat(compression)
121
143
val frColumns = Fragment .const0(columnsToCopy.names.map(_.value).mkString(" ," ))
144
+ val frFileFormat = typesInfo match {
145
+ case TypesInfo .Shredded (_) => " CSV DELIMITER '$EventsFieldSeparator'"
146
+ case TypesInfo .WideRow (JSON , _) => " JSON 'auto'"
147
+ case TypesInfo .WideRow (PARQUET , _) => " PARQUET"
148
+ }
122
149
123
150
sql """ COPY $frTableName ( $frColumns) FROM ' $frPath'
124
151
| CREDENTIALS ' $frRoleArn'
152
+ | FORMAT $frFileFormat
125
153
| REGION ' $frRegion'
126
154
| MAXERROR $frMaxError
127
155
| TIMEFORMAT 'auto'
128
- | DELIMITER ' $EventFieldSeparator'
129
156
| EMPTYASNULL
130
157
| FILLRECORD
131
158
| TRUNCATECOLUMNS
@@ -163,7 +190,7 @@ object Redshift {
163
190
| ACCEPTINVCHARS
164
191
| $frCompression""" .stripMargin
165
192
case ShreddedType .Widerow (_) =>
166
- throw new IllegalStateException (" Widerow loading is not yet supported for Redshift " )
193
+ throw new IllegalStateException (" Cannot perform a shredded copy from widerow files " )
167
194
}
168
195
case Statement .CreateTransient =>
169
196
Fragment .const0(s " CREATE TABLE ${EventsTable .TransitTable (schema).withSchema} ( LIKE ${EventsTable .AtomicEvents (schema).withSchema} ) " )
0 commit comments