diff --git a/R/table-create.R b/R/table-create.R index ffeaab880..a46b3eecd 100644 --- a/R/table-create.R +++ b/R/table-create.R @@ -24,6 +24,10 @@ NULL #' @param temporary If `TRUE`, will generate a temporary table statement. #' @inheritParams rownames #' @param ... Other arguments used by individual methods. +#' @param pk A character vector giving the names of primary key columns. +#' For some DBMS this must be specified at creation time. +#' Even if not strictly necessary, it may save substantial processing time later on. +#' This argument is processed with [dbQuoteIdentifier()]. #' @export #' @examples #' sqlCreateTable(ANSI(), "my-table", c(a = "integer", b = "text")) @@ -33,13 +37,13 @@ NULL #' sqlCreateTable(ANSI(), "mtcars", mtcars[, 1:5]) #' sqlCreateTable(ANSI(), "mtcars", mtcars[, 1:5], row.names = FALSE) setGeneric("sqlCreateTable", - def = function(con, table, fields, row.names = NA, temporary = FALSE, ...) standardGeneric("sqlCreateTable") + def = function(con, table, fields, row.names = NA, temporary = FALSE, ..., pk = NULL) standardGeneric("sqlCreateTable") ) #' @rdname hidden_aliases #' @export setMethod("sqlCreateTable", signature("DBIConnection"), - function(con, table, fields, row.names = NA, temporary = FALSE, ...) { + function(con, table, fields, row.names = NA, temporary = FALSE, ..., pk = NULL) { if (missing(row.names)) { warning("Do not rely on the default value of the row.names argument for sqlCreateTable(), it will change in the future.", call. = FALSE @@ -53,13 +57,22 @@ setMethod("sqlCreateTable", signature("DBIConnection"), fields <- vapply(fields, function(x) DBI::dbDataType(con, x), character(1)) } + if (!is.null(pk)) { + stopifnot(all(pk %in% names(fields))) + nullable <- ifelse(names(fields) %in% pk, " NOT NULL", "") + } else { + nullable <- "" + } + field_names <- dbQuoteIdentifier(con, names(fields)) field_types <- unname(fields) fields <- paste0(field_names, " ", field_types) SQL(paste0( "CREATE ", if (temporary) "TEMPORARY ", "TABLE ", table, " (\n", - " ", paste(fields, collapse = ",\n "), "\n)\n" + " ", paste0(fields, nullable, collapse = ",\n "), + if (!is.null(pk)) paste0(",\n PRIMARY KEY (", paste(dbQuoteIdentifier(con, pk), collapse = ", "), ")"), + "\n)\n" )) } ) @@ -91,13 +104,13 @@ setMethod("sqlCreateTable", signature("DBIConnection"), #' dbReadTable(con, "iris") #' dbDisconnect(con) setGeneric("dbCreateTable", - def = function(conn, name, fields, ..., row.names = NULL, temporary = FALSE) standardGeneric("dbCreateTable") + def = function(conn, name, fields, ..., row.names = NULL, temporary = FALSE, pk = NULL) standardGeneric("dbCreateTable") ) #' @rdname hidden_aliases #' @export setMethod("dbCreateTable", signature("DBIConnection"), - function(conn, name, fields, ..., row.names = NULL, temporary = FALSE) { + function(conn, name, fields, ..., row.names = NULL, temporary = FALSE, pk = NULL) { stopifnot(is.null(row.names)) stopifnot(is.logical(temporary), length(temporary) == 1L) @@ -107,7 +120,8 @@ setMethod("dbCreateTable", signature("DBIConnection"), fields = fields, row.names = row.names, temporary = temporary, - ... + ..., + pk = pk ) dbExecute(conn, query) invisible(TRUE) diff --git a/man/dbCreateTable.Rd b/man/dbCreateTable.Rd index 3cf7aa848..18c0cf9fd 100644 --- a/man/dbCreateTable.Rd +++ b/man/dbCreateTable.Rd @@ -4,7 +4,15 @@ \alias{dbCreateTable} \title{Create a table in the database} \usage{ -dbCreateTable(conn, name, fields, ..., row.names = NULL, temporary = FALSE) +dbCreateTable( + conn, + name, + fields, + ..., + row.names = NULL, + temporary = FALSE, + pk = NULL +) } \arguments{ \item{conn}{A \linkS4class{DBIConnection} object, as returned by @@ -26,6 +34,11 @@ A data frame: field types are generated using \item{row.names}{Must be \code{NULL}.} \item{temporary}{If \code{TRUE}, will generate a temporary table statement.} + +\item{pk}{Primary key columns. +For some DBMS this must be specified at creation time. +Even if not strictly necessary, it may save substantial processing time later on. +This argument is processed with \code{\link[=dbQuoteIdentifier]{dbQuoteIdentifier()}}.} } \description{ The default \code{dbCreateTable()} method calls \code{\link[=sqlCreateTable]{sqlCreateTable()}} and diff --git a/man/hidden_aliases.Rd b/man/hidden_aliases.Rd index 5cbbe639a..f24d71d54 100644 --- a/man/hidden_aliases.Rd +++ b/man/hidden_aliases.Rd @@ -158,9 +158,25 @@ \S4method{dbQuoteLiteral}{DBIConnection}(conn, x, ...) -\S4method{sqlCreateTable}{DBIConnection}(con, table, fields, row.names = NA, temporary = FALSE, ...) +\S4method{sqlCreateTable}{DBIConnection}( + con, + table, + fields, + row.names = NA, + temporary = FALSE, + ..., + pk = NULL +) -\S4method{dbCreateTable}{DBIConnection}(conn, name, fields, ..., row.names = NULL, temporary = FALSE) +\S4method{dbCreateTable}{DBIConnection}( + conn, + name, + fields, + ..., + row.names = NULL, + temporary = FALSE, + pk = NULL +) \S4method{sqlAppendTable}{DBIConnection}(con, table, values, row.names = NA, ...) diff --git a/man/sqlCreateTable.Rd b/man/sqlCreateTable.Rd index 3bfd9fb84..2caa9102a 100644 --- a/man/sqlCreateTable.Rd +++ b/man/sqlCreateTable.Rd @@ -4,7 +4,15 @@ \alias{sqlCreateTable} \title{Compose query to create a simple table} \usage{ -sqlCreateTable(con, table, fields, row.names = NA, temporary = FALSE, ...) +sqlCreateTable( + con, + table, + fields, + row.names = NA, + temporary = FALSE, + ..., + pk = NULL +) } \arguments{ \item{con}{A database connection.} @@ -35,6 +43,11 @@ For backward compatibility, \code{NULL} is equivalent to \code{FALSE}.} \item{temporary}{If \code{TRUE}, will generate a temporary table statement.} \item{...}{Other arguments used by individual methods.} + +\item{pk}{Primary key columns. +For some DBMS this must be specified at creation time. +Even if not strictly necessary, it may save substantial processing time later on. +This argument is processed with \code{\link[=dbQuoteIdentifier]{dbQuoteIdentifier()}}.} } \description{ Exposes an interface to simple \verb{CREATE TABLE} commands. The default