@@ -107,10 +107,10 @@ case class ResolveDefaultColumns(catalog: SessionCatalog) extends Rule[LogicalPl
107107 insertTableSchemaWithoutPartitionColumns.map { schema : StructType =>
108108 val regenerated : InsertIntoStatement =
109109 regenerateUserSpecifiedCols(i, schema)
110- val expanded : LogicalPlan =
110+ val ( expanded : LogicalPlan , addedDefaults : Boolean ) =
111111 addMissingDefaultValuesForInsertFromInlineTable(node, schema, i.userSpecifiedCols.size)
112112 val replaced : Option [LogicalPlan ] =
113- replaceExplicitDefaultValuesForInputOfInsertInto(schema, expanded)
113+ replaceExplicitDefaultValuesForInputOfInsertInto(schema, expanded, addedDefaults )
114114 replaced.map { r : LogicalPlan =>
115115 node = r
116116 for (child <- children.reverse) {
@@ -131,10 +131,10 @@ case class ResolveDefaultColumns(catalog: SessionCatalog) extends Rule[LogicalPl
131131 insertTableSchemaWithoutPartitionColumns.map { schema =>
132132 val regenerated : InsertIntoStatement = regenerateUserSpecifiedCols(i, schema)
133133 val project : Project = i.query.asInstanceOf [Project ]
134- val expanded : Project =
134+ val ( expanded : Project , addedDefaults : Boolean ) =
135135 addMissingDefaultValuesForInsertFromProject(project, schema, i.userSpecifiedCols.size)
136136 val replaced : Option [LogicalPlan ] =
137- replaceExplicitDefaultValuesForInputOfInsertInto(schema, expanded)
137+ replaceExplicitDefaultValuesForInputOfInsertInto(schema, expanded, addedDefaults )
138138 replaced.map { r =>
139139 regenerated.copy(query = r)
140140 }.getOrElse(i)
@@ -270,67 +270,83 @@ case class ResolveDefaultColumns(catalog: SessionCatalog) extends Rule[LogicalPl
270270
271271 /**
272272 * Updates an inline table to generate missing default column values.
273+ * Returns the resulting plan plus a boolean indicating whether such values were added.
273274 */
274- private def addMissingDefaultValuesForInsertFromInlineTable (
275+ def addMissingDefaultValuesForInsertFromInlineTable (
275276 node : LogicalPlan ,
276277 insertTableSchemaWithoutPartitionColumns : StructType ,
277- numUserSpecifiedColumns : Int ): LogicalPlan = {
278+ numUserSpecifiedColumns : Int ): ( LogicalPlan , Boolean ) = {
278279 val schema = insertTableSchemaWithoutPartitionColumns
279- val newDefaultExpressions : Seq [Expression ] =
280- getDefaultExpressionsForInsert(schema, numUserSpecifiedColumns)
281- val newNames : Seq [String ] = if (numUserSpecifiedColumns > 0 ) {
282- schema.fields.drop(numUserSpecifiedColumns).map(_.name)
283- } else {
284- schema.fields.map(_.name)
285- }
286- node match {
287- case _ if newDefaultExpressions.isEmpty => node
280+ val newDefaultExpressions : Seq [UnresolvedAttribute ] =
281+ getNewDefaultExpressionsForInsert(schema, numUserSpecifiedColumns, node.output.size)
282+ val newNames : Seq [String ] = schema.fields.map(_.name)
283+ val resultPlan : LogicalPlan = node match {
284+ case _ if newDefaultExpressions.isEmpty =>
285+ node
288286 case table : UnresolvedInlineTable =>
289287 table.copy(
290- names = table.names ++ newNames,
288+ names = newNames,
291289 rows = table.rows.map { row => row ++ newDefaultExpressions })
292290 case local : LocalRelation =>
293- // Note that we have consumed a LocalRelation but return an UnresolvedInlineTable, because
294- // addMissingDefaultValuesForInsertFromProject must replace unresolved DEFAULT references.
295- UnresolvedInlineTable (
296- local.output.map(_.name) ++ newNames,
297- local.data.map { row =>
298- val colTypes = StructType (local.output.map(col => StructField (col.name, col.dataType)))
299- row.toSeq(colTypes).map(Literal (_)) ++ newDefaultExpressions
291+ val newDefaultExpressionsRow = new GenericInternalRow (
292+ // Note that this code path only runs when there is a user-specified column list of fewer
293+ // column than the target table; otherwise, the above 'newDefaultExpressions' is empty and
294+ // we match the first case in this list instead.
295+ schema.fields.drop(local.output.size).map {
296+ case f if f.metadata.contains(CURRENT_DEFAULT_COLUMN_METADATA_KEY ) =>
297+ analyze(f, " INSERT" ) match {
298+ case lit : Literal => lit.value
299+ case _ => null
300+ }
301+ case _ => null
300302 })
301- case _ => node
303+ LocalRelation (
304+ output = schema.toAttributes,
305+ data = local.data.map { row =>
306+ new JoinedRow (row, newDefaultExpressionsRow)
307+ })
308+ case _ =>
309+ node
302310 }
311+ (resultPlan, newDefaultExpressions.nonEmpty)
303312 }
304313
305314 /**
306315 * Adds a new expressions to a projection to generate missing default column values.
316+ * Returns the logical plan plus a boolean indicating if such defaults were added.
307317 */
308318 private def addMissingDefaultValuesForInsertFromProject (
309319 project : Project ,
310320 insertTableSchemaWithoutPartitionColumns : StructType ,
311- numUserSpecifiedColumns : Int ): Project = {
321+ numUserSpecifiedColumns : Int ): ( Project , Boolean ) = {
312322 val schema = insertTableSchemaWithoutPartitionColumns
313323 val newDefaultExpressions : Seq [Expression ] =
314- getDefaultExpressionsForInsert (schema, numUserSpecifiedColumns)
324+ getNewDefaultExpressionsForInsert (schema, numUserSpecifiedColumns, project.projectList.size )
315325 val newAliases : Seq [NamedExpression ] =
316326 newDefaultExpressions.zip(schema.fields).map {
317327 case (expr, field) => Alias (expr, field.name)()
318328 }
319- project.copy(projectList = project.projectList ++ newAliases)
329+ (project.copy(projectList = project.projectList ++ newAliases),
330+ newDefaultExpressions.nonEmpty)
320331 }
321332
322333 /**
323334 * This is a helper for the addMissingDefaultValuesForInsertFromInlineTable methods above.
324335 */
325- private def getDefaultExpressionsForInsert (
326- schema : StructType ,
327- numUserSpecifiedColumns : Int ): Seq [Expression ] = {
336+ private def getNewDefaultExpressionsForInsert (
337+ insertTableSchemaWithoutPartitionColumns : StructType ,
338+ numUserSpecifiedColumns : Int ,
339+ numProvidedValues : Int ): Seq [UnresolvedAttribute ] = {
328340 val remainingFields : Seq [StructField ] = if (numUserSpecifiedColumns > 0 ) {
329- schema .fields.drop(numUserSpecifiedColumns)
341+ insertTableSchemaWithoutPartitionColumns .fields.drop(numUserSpecifiedColumns)
330342 } else {
331343 Seq .empty
332344 }
333345 val numDefaultExpressionsToAdd = getStructFieldsForDefaultExpressions(remainingFields).size
346+ // Limit the number of new DEFAULT expressions to the difference of the number of columns in
347+ // the target table and the number of provided values in the source relation. This clamps the
348+ // total final number of provided values to the number of columns in the target table.
349+ .min(insertTableSchemaWithoutPartitionColumns.size - numProvidedValues)
334350 Seq .fill(numDefaultExpressionsToAdd)(UnresolvedAttribute (CURRENT_DEFAULT_COLUMN_NAME ))
335351 }
336352
@@ -351,7 +367,8 @@ case class ResolveDefaultColumns(catalog: SessionCatalog) extends Rule[LogicalPl
351367 */
352368 private def replaceExplicitDefaultValuesForInputOfInsertInto (
353369 insertTableSchemaWithoutPartitionColumns : StructType ,
354- input : LogicalPlan ): Option [LogicalPlan ] = {
370+ input : LogicalPlan ,
371+ addedDefaults : Boolean ): Option [LogicalPlan ] = {
355372 val schema = insertTableSchemaWithoutPartitionColumns
356373 val defaultExpressions : Seq [Expression ] = schema.fields.map {
357374 case f if f.metadata.contains(CURRENT_DEFAULT_COLUMN_METADATA_KEY ) => analyze(f, " INSERT" )
@@ -371,7 +388,11 @@ case class ResolveDefaultColumns(catalog: SessionCatalog) extends Rule[LogicalPl
371388 case project : Project =>
372389 replaceExplicitDefaultValuesForProject(defaultExpressions, project)
373390 case local : LocalRelation =>
374- Some (local)
391+ if (addedDefaults) {
392+ Some (local)
393+ } else {
394+ None
395+ }
375396 }
376397 }
377398
0 commit comments