Skip to content

Commit a131af7

Browse files
committed
Replace build_copy_queries() with dm_sql()
1 parent f651728 commit a131af7

File tree

5 files changed

+150
-156
lines changed

5 files changed

+150
-156
lines changed

R/db-interface.R

Lines changed: 71 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,13 @@
55
#' and a [`dm`] object as its second argument.
66
#' The latter is copied to the former.
77
#' The default is to create temporary tables, set `temporary = FALSE` to create permanent tables.
8-
#' Unless `set_key_constraints` is `FALSE`, primary key constraints are set on all databases,
9-
#' and in addition foreign key constraints are set on MSSQL and Postgres databases.
8+
#' Unless `set_key_constraints` is `FALSE`, primary key, foreign key, and unique constraints
9+
#' are set on all databases.
1010
#'
1111
#' @inheritParams dm_examine_constraints
1212
#'
1313
#' @param dest An object of class `"src"` or `"DBIConnection"`.
1414
#' @param dm A `dm` object.
15-
#' @inheritParams rlang::args_dots_empty
1615
#' @param set_key_constraints If `TRUE` will mirror `dm` primary and foreign key constraints on a database
1716
#' and create indexes for foreign key constraints.
1817
#' Set to `FALSE` if your data model currently does not satisfy primary or foreign key constraints.
@@ -23,38 +22,9 @@
2322
#' `table_names` is not `NULL`.
2423
#'
2524
#' Not all DBMS are supported.
26-
#' @param table_names Desired names for the tables on `dest`; the names within the `dm` remain unchanged.
27-
#' Can be `NULL`, a named character vector, or a vector of [DBI::Id] objects.
28-
#'
29-
#' If left `NULL` (default), the names will be determined automatically depending on the `temporary` argument:
30-
#'
31-
#' 1. `temporary = TRUE` (default): unique table names based on the names of the tables in the `dm` are created.
32-
#' 1. `temporary = FALSE`: the table names in the `dm` are used as names for the tables on `dest`.
33-
#'
34-
#' If a function or one-sided formula, `table_names` is converted to a function
35-
#' using [rlang::as_function()].
36-
#' This function is called with the unquoted table names of the `dm` object
37-
#' as the only argument.
38-
#' The output of this function is processed by [DBI::dbQuoteIdentifier()],
39-
#' that result should be a vector of identifiers of the same length
40-
#' as the original table names.
41-
#'
42-
#' Use a variant of
43-
#' `table_names = ~ DBI::SQL(paste0("schema_name", ".", .x))`
44-
#' to specify the same schema for all tables.
45-
#' Use `table_names = identity` with `temporary = TRUE`
46-
#' to avoid giving temporary tables unique names.
47-
#'
48-
#' If a named character vector,
49-
#' the names of this vector need to correspond to the table names in the `dm`,
50-
#' and its values are the desired names on `dest`.
51-
#' The value is processed by [DBI::dbQuoteIdentifier()],
52-
#' that result should be a vector of identifiers of the same length
53-
#' as the original table names.
54-
#'
55-
#' Use qualified names corresponding to your database's syntax
56-
#' to specify e.g. database and schema for your tables.
57-
#' @param unique_table_names,copy_to Must be `NULL`.
25+
#' @inheritParams dm_sql
26+
#' @inheritParams rlang::args_dots_empty
27+
#' @param unique_table_names,copy_to Deprecated.
5828
#'
5929
#' @family DB interaction functions
6030
#'
@@ -100,14 +70,10 @@ copy_dm_to <- function(
10070
# 3. implement the key situation within our `dm` on the DB
10171

10272
if (!is.null(unique_table_names)) {
103-
deprecate_warn(
73+
deprecate_stop(
10474
"0.1.4", "dm::copy_dm_to(unique_table_names = )",
105-
details = "Use `table_names = identity` to use unchanged names for temporary tables."
75+
details = "Use `table_names = set_names(names(dm))` to use unchanged names for temporary tables."
10676
)
107-
108-
if (is.null(table_names) && temporary && !unique_table_names) {
109-
table_names <- identity
110-
}
11177
}
11278

11379
if (!is.null(copy_to)) {
@@ -124,110 +90,113 @@ copy_dm_to <- function(
12490
check_suggested("dbplyr", "copy_dm_to")
12591

12692
dest <- src_from_src_or_con(dest)
127-
src_names <- src_tbls_impl(dm)
12893

129-
if (is_db(dest)) {
130-
dest_con <- con_from_src_or_con(dest)
131-
132-
# in case `table_names` was chosen by the user, check if the input makes sense:
133-
# 1. is there one name per dm-table?
134-
# 2. are there any duplicated table names?
135-
# 3. is it a named character or ident_q vector with the correct names?
136-
if (is.null(table_names)) {
137-
table_names_out <- repair_table_names_for_db(src_names, temporary, dest_con, schema)
138-
# https://github.com/tidyverse/dbplyr/issues/487
139-
if (is_mssql(dest)) {
140-
temporary <- FALSE
141-
}
142-
} else {
143-
if (!is.null(schema)) abort_one_of_schema_table_names()
144-
if (is_function(table_names) || is_bare_formula(table_names)) {
145-
table_name_fun <- as_function(table_names)
146-
table_names_out <- set_names(table_name_fun(src_names), src_names)
147-
} else {
148-
table_names_out <- table_names
149-
}
150-
check_naming(names(table_names_out), src_names)
151-
152-
if (anyDuplicated(table_names_out)) {
153-
problem <- table_names_out[duplicated(table_names_out)][[1]]
154-
abort_copy_dm_to_table_names_duplicated(problem)
155-
}
94+
if (!is_db(dest)) {
95+
deprecate_stop(
96+
"0.1.6", "dm::copy_dm_to(dest = 'must refer to a DBI connection')",
97+
"dm::collect.dm()"
98+
)
99+
}
156100

157-
names(table_names_out) <- src_names
101+
src_names <- src_tbls_impl(dm)
102+
dest_con <- con_from_src_or_con(dest)
103+
104+
# in case `table_names` was chosen by the user, check if the input makes sense:
105+
# 1. is there one name per dm-table?
106+
# 2. are there any duplicated table names?
107+
# 3. is it a named character or ident_q vector with the correct names?
108+
if (is.null(table_names)) {
109+
table_names_out <- repair_table_names_for_db(src_names, temporary, dest_con, schema)
110+
# https://github.com/tidyverse/dbplyr/issues/487
111+
if (is_mssql(dest)) {
112+
temporary <- FALSE
158113
}
159114
} else {
160-
# FIXME: Other data sources than local and database possible
161-
deprecate_warn(
162-
"0.1.6", "dm::copy_dm_to(dest = 'must refer to a remote data source')",
163-
"dm::collect.dm()"
164-
)
165-
table_names_out <- set_names(src_names)
115+
if (!is.null(schema)) abort_one_of_schema_table_names()
116+
if (is_function(table_names) || is_bare_formula(table_names)) {
117+
table_name_fun <- as_function(table_names)
118+
table_names_out <- set_names(table_name_fun(src_names), src_names)
119+
} else {
120+
table_names_out <- table_names
121+
}
122+
check_naming(names(table_names_out), src_names)
123+
124+
if (anyDuplicated(table_names_out)) {
125+
problem <- table_names_out[duplicated(table_names_out)][[1]]
126+
abort_copy_dm_to_table_names_duplicated(problem)
127+
}
128+
129+
names(table_names_out) <- src_names
166130
}
167131

168-
# FIXME: if same_src(), can use compute() but need to set NOT NULL and other
169-
# constraints
132+
table_names_out <- ddl_check_table_names(table_names_out, dm)
170133

171-
# Shortcut necessary to avoid copying into .GlobalEnv
172-
if (!is_db(dest)) {
173-
return(dm)
134+
if (isTRUE(set_key_constraints)) {
135+
dm_for_sql <- dm
136+
} else {
137+
def_no_keys <- dm_get_def(dm)
138+
def_no_keys$uks[] <- list(new_uk())
139+
def_no_keys$fks[] <- list(new_fk())
140+
# Must keep primary keys
141+
dm_for_sql <- dm_from_def(def_no_keys)
174142
}
175143

176-
queries <- build_copy_queries(dest_con, dm, set_key_constraints, temporary, table_names_out)
144+
sql <- dm_sql(dm_for_sql, dest_con, table_names_out, temporary)
145+
146+
# FIXME: Extract function
147+
# FIXME: Make descriptions part of the dm_sql() output
148+
149+
pre <- unlist(sql$pre)
150+
load <- unlist(sql$load)
151+
post <- unlist(sql$post)
177152

178-
ticker_create <- new_ticker(
153+
ticker_pre <- new_ticker(
179154
"creating tables",
180-
n = length(queries$sql_table),
155+
n = length(pre),
181156
progress = progress,
182157
top_level_fun = "copy_dm_to"
183158
)
184159

185160
# create tables
186-
walk(queries$sql_table, ticker_create(~ {
161+
walk(pre, ticker_pre(~ {
187162
DBI::dbExecute(dest_con, .x, immediate = TRUE)
188163
}))
189164

190-
ticker_populate <- new_ticker(
165+
ticker_load <- new_ticker(
191166
"populating tables",
192-
n = length(queries$name),
167+
n = length(load),
193168
progress = progress,
194169
top_level_fun = "copy_dm_to"
195170
)
196171

197172
# populate tables
198-
pwalk(
199-
queries[c("name", "remote_name")],
200-
ticker_populate(~ db_append_table(
201-
con = dest_con,
202-
remote_table = .y,
203-
table = dm[[.x]],
204-
progress = progress,
205-
autoinc = dm_get_all_pks(dm, table = !!.x)$autoincrement
206-
))
207-
)
173+
walk(load, ticker_load(~ {
174+
DBI::dbExecute(dest_con, .x, immediate = TRUE)
175+
}))
208176

209-
ticker_index <- new_ticker(
177+
ticker_post <- new_ticker(
210178
"creating indexes",
211-
n = sum(lengths(queries$sql_index)),
179+
n = length(post),
212180
progress = progress,
213181
top_level_fun = "copy_dm_to"
214182
)
215183

216184
# create indexes
217-
walk(unlist(queries$sql_index), ticker_index(~ {
185+
walk(post, ticker_post(~ {
218186
DBI::dbExecute(dest_con, .x, immediate = TRUE)
219187
}))
220188

221189
# remote dm is same as source dm with replaced data
190+
# FIXME: Extract function
222191
def <- dm_get_def(dm)
223192

224193
remote_tables <- map2(
225194
table_names_out,
226195
map(def$data, colnames),
227-
~ tbl(dest_con, ..1, vars = ..2)
196+
~ tbl(dest_con, .x, vars = .y)
228197
)
229198

230-
def$data <- unname(remote_tables[names(dm)])
199+
def$data <- unname(remote_tables)
231200
remote_dm <- dm_from_def(def)
232201

233202
invisible(debug_dm_validate(remote_dm))

man/copy_dm_to.Rd

Lines changed: 6 additions & 35 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/testthat/_snaps/db-interface.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
# copy_dm_to() copies data frames from any source
2+
3+
Code
4+
copy_dm_to(default_local_src(), dm_for_filter())
5+
Condition
6+
Error:
7+
! The `dest` argument of `copy_dm_to()` must refer to a DBI connection as of dm 0.1.6.
8+
i Please use `collect.dm()` instead.
9+
110
# copy_dm_to() rejects overwrite and types arguments
211

312
Code

0 commit comments

Comments
 (0)