1717
1818package org .apache .kyuubi .spark .connector .hive
1919
20+ import java .net .URI
2021import java .util
2122import java .util .Locale
2223
@@ -26,16 +27,19 @@ import scala.collection.mutable
2627import org .apache .hadoop .fs .Path
2728import org .apache .spark .internal .Logging
2829import org .apache .spark .sql .SparkSession
29- import org .apache .spark .sql .catalyst .catalog .CatalogTable
30- import org .apache .spark .sql .connector .catalog .{SupportsRead , SupportsWrite , Table , TableCapability }
30+ import org .apache .spark .sql .catalyst .InternalRow
31+ import org .apache .spark .sql .catalyst .analysis .NoSuchPartitionException
32+ import org .apache .spark .sql .catalyst .catalog .{CatalogTable , CatalogTablePartition }
33+ import org .apache .spark .sql .catalyst .expressions .{Cast , GenericInternalRow , Literal }
34+ import org .apache .spark .sql .connector .catalog .{SupportsPartitionManagement , SupportsRead , SupportsWrite , Table , TableCapability }
3135import org .apache .spark .sql .connector .catalog .TableCapability .{BATCH_READ , BATCH_WRITE , OVERWRITE_BY_FILTER , OVERWRITE_DYNAMIC }
3236import org .apache .spark .sql .connector .expressions .Transform
3337import org .apache .spark .sql .connector .read .ScanBuilder
3438import org .apache .spark .sql .connector .write .{LogicalWriteInfo , WriteBuilder }
3539import org .apache .spark .sql .execution .datasources .v2 .orc .OrcScanBuilder
3640import org .apache .spark .sql .execution .datasources .v2 .parquet .ParquetScanBuilder
3741import org .apache .spark .sql .hive .kyuubi .connector .HiveBridgeHelper .{BucketSpecHelper , LogicalExpressions }
38- import org .apache .spark .sql .types .StructType
42+ import org .apache .spark .sql .types .{ StringType , StructType }
3943import org .apache .spark .sql .util .CaseInsensitiveStringMap
4044
4145import org .apache .kyuubi .spark .connector .hive .KyuubiHiveConnectorConf .{READ_CONVERT_METASTORE_ORC , READ_CONVERT_METASTORE_PARQUET }
@@ -46,7 +50,7 @@ case class HiveTable(
4650 sparkSession : SparkSession ,
4751 catalogTable : CatalogTable ,
4852 hiveTableCatalog : HiveTableCatalog )
49- extends Table with SupportsRead with SupportsWrite with Logging {
53+ extends Table with SupportsRead with SupportsWrite with SupportsPartitionManagement with Logging {
5054
5155 lazy val dataSchema : StructType = catalogTable.dataSchema
5256
@@ -112,4 +116,92 @@ case class HiveTable(
112116 override def capabilities (): util.Set [TableCapability ] = {
113117 util.EnumSet .of(BATCH_READ , BATCH_WRITE , OVERWRITE_BY_FILTER , OVERWRITE_DYNAMIC )
114118 }
119+
120+ override def createPartition (ident : InternalRow , properties : util.Map [String , String ]): Unit = {
121+ val spec = toPartitionSpec(ident)
122+ val location = Option (properties.get(HiveTableProperties .LOCATION )).map(new URI (_))
123+ val newPart = CatalogTablePartition (
124+ spec,
125+ catalogTable.storage.copy(locationUri = location),
126+ properties.asScala.toMap)
127+ hiveTableCatalog.externalCatalog.createPartitions(
128+ catalogTable.database,
129+ catalogTable.identifier.table,
130+ Seq (newPart),
131+ ignoreIfExists = false )
132+ }
133+
134+ override def dropPartition (ident : InternalRow ): Boolean = {
135+ try {
136+ hiveTableCatalog.externalCatalog.dropPartitions(
137+ catalogTable.database,
138+ catalogTable.identifier.table,
139+ Seq (toPartitionSpec(ident)),
140+ ignoreIfNotExists = false ,
141+ purge = false ,
142+ retainData = false )
143+ true
144+ } catch {
145+ case _ : NoSuchPartitionException => false
146+ }
147+ }
148+
149+ override def replacePartitionMetadata (
150+ ident : InternalRow ,
151+ properties : util.Map [String , String ]): Unit = {
152+ throw new UnsupportedOperationException (" Replace partition is not supported" )
153+ }
154+
155+ override def loadPartitionMetadata (ident : InternalRow ): util.Map [String , String ] = {
156+ val spec = toPartitionSpec(ident)
157+ val partition = hiveTableCatalog.externalCatalog.getPartition(
158+ catalogTable.database,
159+ catalogTable.identifier.table,
160+ spec)
161+ val metadata = new util.HashMap [String , String ](partition.parameters.asJava)
162+ partition.storage.locationUri.foreach { uri =>
163+ metadata.put(HiveTableProperties .LOCATION , uri.toString)
164+ }
165+ metadata
166+ }
167+
168+ override def listPartitionIdentifiers (
169+ names : Array [String ],
170+ ident : InternalRow ): Array [InternalRow ] = {
171+ val partialSpec = if (names.isEmpty) {
172+ None
173+ } else {
174+ val fields = names.map(partitionSchema(_))
175+ val schema = StructType (fields)
176+ Some (toPartitionSpec(ident, schema))
177+ }
178+ hiveTableCatalog.externalCatalog.listPartitions(
179+ catalogTable.database,
180+ catalogTable.identifier.table,
181+ partialSpec).map { part =>
182+ val values = partitionSchema.map { field =>
183+ val strValue = part.spec(field.name)
184+ Cast (Literal (strValue), field.dataType).eval()
185+ }
186+ new GenericInternalRow (values.toArray)
187+ }.toArray
188+ }
189+
190+ private def toPartitionSpec (ident : InternalRow , schema : StructType ): Map [String , String ] = {
191+ require(
192+ schema.size == ident.numFields,
193+ s " Schema size ( ${schema.size}) does not match numFields ( ${ident.numFields}) " )
194+ schema.zipWithIndex.map { case (field, index) =>
195+ val value = ident.get(index, field.dataType)
196+ val filedValue = Cast (
197+ Literal (value, field.dataType),
198+ StringType ,
199+ Some (sparkSession.sessionState.conf.sessionLocalTimeZone)).eval().toString
200+ field.name -> filedValue
201+ }.toMap
202+ }
203+
204+ private def toPartitionSpec (ident : InternalRow ): Map [String , String ] = {
205+ toPartitionSpec(ident, partitionSchema)
206+ }
115207}
0 commit comments