Skip to content

Commit

Permalink
implement dbAppendTable()
Browse files Browse the repository at this point in the history
- New `dbAppendTable()` that by default calls `sqlAppendTableTemplate()` and then `dbExecute()` with a `param` argument (#74).
  • Loading branch information
krlmlr committed Apr 24, 2018
1 parent dc78d56 commit 8e166b7
Show file tree
Hide file tree
Showing 27 changed files with 273 additions and 21 deletions.
2 changes: 2 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export(ANSI)
export(Id)
export(SQL)
export(SQLKeywords)
export(dbAppendTable)
export(dbBegin)
export(dbBind)
export(dbBreak)
Expand Down Expand Up @@ -73,6 +74,7 @@ exportClasses(DBIDriver)
exportClasses(DBIObject)
exportClasses(DBIResult)
exportClasses(SQL)
exportMethods(dbAppendTable)
exportMethods(dbCanConnect)
exportMethods(dbCreateTable)
exportMethods(dbDataType)
Expand Down
15 changes: 10 additions & 5 deletions R/table-create.R
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ NULL
#' method is ANSI SQL 99 compliant.
#' This method is mostly useful for backend implementers.
#'
#' The `row.names` argument must be passed explicitly in order to avoid
#' a compatibility warning. The default will be changed in a later release.
#'
#' @param con A database connection.
#' @param table Name of the table. Escaped with
#' [dbQuoteIdentifier()].
Expand Down Expand Up @@ -69,26 +72,28 @@ setMethod("sqlCreateTable", signature("DBIConnection"),
#' entirely different ways to create tables need to override this method.
#'
#' The default value for the `row.names` argument is different from
#' `sqlCreateTable()`, the argument order is also different. Both will be
#' adapted in a later release of DBI.
#' `sqlCreateTable()`, the argument order is also different. The
#' `sqlCreateTable()` method will be adapted in a later release of DBI.
#'
#' @inheritParams sqlCreateTable
#' @inheritParams dbDisconnect
#' @family DBIConnection generics
#' @export
#' @examples
#' con <- dbConnect(RSQLite::SQLite(), ":memory:")
#' dbCreateTable(con, "iris", iris)
#' dbReadTable(con, "iris")
#' dbDisconnect(con)
setGeneric("dbCreateTable",
def = function(con, table, fields, ..., row.names = FALSE, temporary = FALSE) standardGeneric("dbCreateTable")
def = function(conn, table, fields, ..., row.names = FALSE, temporary = FALSE) standardGeneric("dbCreateTable")
)

#' @rdname hidden_aliases
#' @export
setMethod("dbCreateTable", signature("DBIConnection"),
function(con, table, fields, ..., row.names = FALSE, temporary = FALSE) {
function(conn, table, fields, ..., row.names = FALSE, temporary = FALSE) {
query <- sqlCreateTable(
con = con,
con = conn,
table = table,
fields = fields,
row.names = row.names,
Expand Down
118 changes: 113 additions & 5 deletions R/table-insert.R
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
#' @include hidden.R
NULL

#' Insert rows into a table
#' Compose query to insert rows into a table
#'
#' `sqlAppendTable` generates a single SQL string that inserts a
#' data frame into an existing table. `sqlAppendTableTemplate` generates
#' `sqlAppendTable()` generates a single SQL string that inserts a
#' data frame into an existing table. `sqlAppendTableTemplate()` generates
#' a template suitable for use with [dbBind()].
#' The default methods are ANSI SQL 99 compliant.
#' These methods are mostly useful for backend implementers.
#'
#' The `row.names` argument must be passed explicitly in order to avoid
#' a compatibility warning. The default will be changed in a later release.
#'
#' @inheritParams sqlCreateTable
#' @inheritParams rownames
#' @param values A data frame. Factors will be converted to character vectors.
Expand Down Expand Up @@ -48,19 +52,123 @@ setMethod("sqlAppendTable", signature("DBIConnection"),
#' @inheritParams sqlCreateTable
#' @inheritParams sqlAppendTable
#' @inheritParams rownames
#' @param prefix Parameter prefix to put in front of column id.
#' @param prefix Parameter prefix to use for placeholders.
#' @param pattern Parameter pattern to use for placeholders:
#' - `""`: no pattern
#' - `"1"`: position
#' - anything else: field name
#' @export
#' @examples
#' sqlAppendTableTemplate(ANSI(), "iris", iris)
#'
#' sqlAppendTableTemplate(ANSI(), "mtcars", mtcars)
#' sqlAppendTableTemplate(ANSI(), "mtcars", mtcars, row.names = FALSE)
sqlAppendTableTemplate <- function(con, table, values, row.names = NA, prefix = "?", ...) {
sqlAppendTableTemplate <- function(con, table, values, row.names = NA, prefix = "?", ..., pattern = "") {
if (missing(row.names)) {
warning("Do not rely on the default value of the row.names argument for sqlAppendTableTemplate(), it will change in the future.",
call. = FALSE
)
}

table <- dbQuoteIdentifier(con, table)

values <- sqlRownamesToColumn(values[0, , drop = FALSE], row.names)
fields <- dbQuoteIdentifier(con, names(values))

if (pattern == "") {
suffix <- ""
} else if (pattern == "1") {
suffix <- as.character(seq_along(fields))
} else {
suffix <- names(fields)
}

# Convert fields into a character matrix
SQL(paste0(
"INSERT INTO ", table, "\n",
" (", paste(fields, collapse = ", "), ")\n",
"VALUES\n",
paste0(" (", paste0(prefix, seq_along(fields), collapse = ", "), ")", collapse = ",\n")
))
}

#' Insert rows into a table
#'
#' This method assumes that the table has been created beforehand, e.g.
#' with [dbCreateTable()].
#' The default implementation calls [sqlAppendTableTemplate()] and then
#' [dbExecute()] with the `param` argument. Backends compliant to
#' ANSI SQL 99 don't need to override it. Backends with a different SQL
#' syntax can override `sqlAppendTableTemplate()`, backends with
#' entirely different ways to create tables need to override this method.
#'
#' The default value for the `row.names` argument is different from
#' `sqlAppendTableToTemplate()`, the latter will be adapted in a later
#' release of DBI.
#'
#' @inheritParams sqlAppendTableTemplate
#' @inheritParams dbDisconnect
#' @param values A data frame of values. The column names must be consistent
#' with those in the target table in the database.
#' @family DBIConnection generics
#' @export
#' @examples
#' con <- dbConnect(RSQLite::SQLite(), ":memory:")
#' dbCreateTable(con, "iris", iris)
#' dbAppendTable(con, "iris", iris)
#' dbReadTable(con, "iris")
#' dbDisconnect(con)
setGeneric("dbAppendTable",
def = function(conn, table, values, ..., row.names = FALSE) standardGeneric("dbAppendTable")
)

#' @rdname hidden_aliases
#' @export
setMethod("dbAppendTable", signature("DBIConnection"),
function(conn, table, values, ..., row.names = FALSE) {
query <- sqlAppendTableTemplate(
con = conn,
table = table,
values = values,
row.names = row.names,
prefix = "?",
pattern = "",
...
)
values <- sqlRownamesToColumn(values, row.names)
dbExecute(conn, query, param = unname(as.list(values)))
}
)

#' @rdname sqlAppendTable
#' @inheritParams sqlCreateTable
#' @inheritParams sqlAppendTable
#' @inheritParams rownames
#' @param prefix Parameter prefix to use for placeholders.
#' @param pattern Parameter pattern to use for placeholders:
#' - `""`: no pattern
#' - `"1"`: position
#' - anything else: field name
#' @export
#' @examples
#' sqlAppendTableTemplate(ANSI(), "iris", iris)
#'
#' sqlAppendTableTemplate(ANSI(), "mtcars", mtcars)
#' sqlAppendTableTemplate(ANSI(), "mtcars", mtcars, row.names = FALSE)
sqlAppendTableTemplate <- function(con, table, values, row.names = NA, prefix = "?", ..., pattern = "") {
table <- dbQuoteIdentifier(con, table)

values <- sqlRownamesToColumn(values[0, , drop = FALSE], row.names)
fields <- dbQuoteIdentifier(con, names(values))

if (pattern == "") {
suffix <- ""
} else if (pattern == "1") {
suffix <- as.character(seq_along(fields))
} else {
suffix <- names(fields)
}

# Convert fields into a character matrix
SQL(paste0(
"INSERT INTO ", table, "\n",
Expand Down
3 changes: 2 additions & 1 deletion man/DBIConnection-class.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

66 changes: 66 additions & 0 deletions man/dbAppendTable.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 20 additions & 4 deletions man/dbCreateTable.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions man/dbDataType.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions man/dbDisconnect.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions man/dbExecute.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions man/dbExistsTable.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions man/dbGetException.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions man/dbGetInfo.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions man/dbGetQuery.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions man/dbIsReadOnly.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions man/dbIsValid.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 8e166b7

Please sign in to comment.