package org.apache.spark.sql.execution.datasources

import org.apache.spark.sql.catalyst.CaseSensitivityUtils._
import org.apache.spark.sql.catalyst.TableIdentifier
import org.apache.spark.sql.execution.RunnableCommand
import org.apache.spark.sql.execution.datasources.SqlContextAccessor._
import org.apache.spark.sql.sources.RegisterAllTableRelations
import org.apache.spark.sql.{DatasourceResolver, Row, SQLContext}

/**
  * Registers a table (i.e. REGISTER TABLE). In order to use this, a data source
  * needs to implement [[RegisterAllTableRelations]].
  *
  * @param tableName Table name.
  * @param provider Data source.
  * @param options Options.
  * @param ignoreConflicts If true, the conflicting table will be overwritten.
  * @param allowExisting If true, the existing table will be skipped and _not_ overwritten.
  */
private[sql] case class RegisterTableCommand(
    tableName: String,
    provider: String,
    options: Map[String, String],
    ignoreConflicts: Boolean,
    allowExisting: Boolean)
  extends RunnableCommand {

  override def run(sqlContext: SQLContext): Seq[Row] = {
    /**
      * Before executing, we check whether there is already a relation with that
      * name in the catalog.
      */
    val tableExists = sqlContext.catalog.tableExists(TableIdentifier(tableName))

    if (tableExists && !ignoreConflicts && !allowExisting) {
      sys.error(s"Relation $tableName already exists in Spark catalog.")
    } else if (tableExists && allowExisting) {
      logInfo(s"Table '$tableName' already exists and IF NOT EXISTS was specified. Skipping...")
    } else {
      /** Instantiate the provider */
      val resolver = DatasourceResolver.resolverFor(sqlContext)
      val resolvedProvider = resolver.newInstanceOfTyped[RegisterAllTableRelations](provider)

      /** Get the relation from the provider */
      val relation = resolvedProvider.getTableRelation(tableName, sqlContext, options)

      relation match {
        case None =>
          sys.error(s"Relation $tableName was not found in the catalog.")
        case Some(r) =>
          val lp = r.logicalPlan(sqlContext)
          if (lp.resolved) {
            sqlContext.validatedSchema(lp.schema).recover {
              case d: DuplicateFieldsException =>
                throw new RuntimeException(
                  s"Provider '$provider' returned a relation that has duplicate fields.",
                  d)
            }.get
          } else {
            // TODO(AC): With the new view interface, this can be checked
            logWarning(s"Adding relation $tableName with potentially unreachable fields.")
          }
          sqlContext.registerRawPlan(lp, tableName)
      }
    }
    Seq.empty
  }
}