Skip to content

Commit 7c2edc6

Browse files
committed
SNAPSHOT: Replace build_copy_queries() with dm_sql()
1 parent f345abd commit 7c2edc6

File tree

8 files changed

+135
-251
lines changed

8 files changed

+135
-251
lines changed

R/db-interface.R

Lines changed: 78 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -5,64 +5,26 @@
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,
8+
#' Unless `set_key_constraints` is `FALSE`, primary key, foreign key, and unique constraints are set on all databases,
99
#' and in addition foreign key constraints are set on MSSQL and Postgres databases.
1010
#'
11-
#' @details
12-
#' No tables will be overwritten; passing `overwrite = TRUE` to the function will give an error.
13-
#' Types are determined separately for each table, setting the `types` argument will
14-
#' also throw an error.
15-
#' The arguments are included in the signature to avoid passing them via the
16-
#' `...` ellipsis.
17-
#'
1811
#' @inheritParams dm_examine_constraints
1912
#'
2013
#' @param dest An object of class `"src"` or `"DBIConnection"`.
2114
#' @param dm A `dm` object.
22-
#' @param overwrite,types,indexes,unique_indexes Must remain `NULL`.
2315
#' @param set_key_constraints If `TRUE` will mirror `dm` primary and foreign key constraints on a database
2416
#' and create unique indexes.
2517
#' Set to `FALSE` if your data model currently does not satisfy primary or foreign key constraints.
26-
#' @param unique_table_names Deprecated.
2718
#' @param temporary If `TRUE`, only temporary tables will be created.
2819
#' These tables will vanish when disconnecting from the database.
2920
#' @param schema Name of schema to copy the `dm` to.
30-
#' If `schema` is provided, an error will be thrown if `temporary = FALSE` or
31-
#' `table_names` is not `NULL`.
32-
#'
33-
#' Not all DBMS are supported.
34-
#' @param table_names Desired names for the tables on `dest`; the names within the `dm` remain unchanged.
35-
#' Can be `NULL`, a named character vector, a function or a one-sided formula.
36-
#'
37-
#' If left `NULL` (default), the names will be determined automatically depending on the `temporary` argument:
38-
#'
39-
#' 1. `temporary = TRUE` (default): unique table names based on the names of the tables in the `dm` are created.
40-
#' 1. `temporary = FALSE`: the table names in the `dm` are used as names for the tables on `dest`.
41-
#'
42-
#' If a function or one-sided formula, `table_names` is converted to a function
43-
#' using [rlang::as_function()].
44-
#' This function is called with the unquoted table names of the `dm` object
45-
#' as the only argument.
46-
#' The output of this function is processed by [DBI::dbQuoteIdentifier()],
47-
#' that result should be a vector of identifiers of the same length
48-
#' as the original table names.
49-
#'
50-
#' Use a variant of
51-
#' `table_names = ~ DBI::SQL(paste0("schema_name", ".", .x))`
52-
#' to specify the same schema for all tables.
53-
#' Use `table_names = identity` with `temporary = TRUE`
54-
#' to avoid giving temporary tables unique names.
55-
#'
56-
#' If a named character vector,
57-
#' the names of this vector need to correspond to the table names in the `dm`,
58-
#' and its values are the desired names on `dest`.
59-
#' The value is processed by [DBI::dbQuoteIdentifier()],
60-
#' that result should be a vector of identifiers of the same length
61-
#' as the original table names.
21+
#' If `schema` is provided, an error will be thrown if `temporary = FALSE` or
22+
#' `table_names` is not `NULL`.
6223
#'
63-
#' Use qualified names corresponding to your database's syntax
64-
#' to specify e.g. database and schema for your tables.
65-
#' @param copy_to,... Deprecated.
24+
#' Not all DBMS are supported.
25+
#' @inheritParams dm_sql
26+
#' @inheritParams rlang::args_dots_empty
27+
#' @param unique_table_names,copy_to Deprecated.
6628
#'
6729
#' @family DB interaction functions
6830
#'
@@ -94,10 +56,6 @@ copy_dm_to <- function(
9456
dest,
9557
dm,
9658
...,
97-
types = NULL,
98-
overwrite = NULL,
99-
indexes = NULL,
100-
unique_indexes = NULL,
10159
set_key_constraints = TRUE,
10260
unique_table_names = NULL,
10361
table_names = NULL,
@@ -111,156 +69,134 @@ copy_dm_to <- function(
11169
# 2. copy the tables to `dest`
11270
# 3. implement the key situation within our `dm` on the DB
11371

114-
if (!is_null(overwrite)) {
115-
abort_no_overwrite()
116-
}
117-
118-
if (!is_null(types)) {
119-
abort_no_types()
120-
}
121-
122-
if (!is_null(indexes)) {
123-
abort_no_indexes()
124-
}
125-
126-
if (!is_null(unique_indexes)) {
127-
abort_no_unique_indexes()
128-
}
129-
13072
if (!is.null(unique_table_names)) {
131-
deprecate_soft(
73+
deprecate_stop(
13274
"0.1.4", "dm::copy_dm_to(unique_table_names = )",
133-
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."
13476
)
135-
136-
if (is.null(table_names) && temporary && !unique_table_names) {
137-
table_names <- identity
138-
}
13977
}
14078

14179
if (!is.null(copy_to)) {
142-
deprecate_soft(
80+
deprecate_stop(
14381
"1.0.0", "dm::copy_dm_to(copy_to = )",
144-
details = "Use `dm_ddl()` for more control over the schema creation process."
82+
details = "Use `dm_sql()` for more control over the schema creation process."
14583
)
14684
}
14785

148-
if (dots_n(...) > 0) {
149-
deprecate_soft(
150-
"1.0.0", "dm::copy_dm_to(... = )",
151-
details = "Use `dm_ddl()` for more control over the schema creation process."
152-
)
153-
}
86+
check_dots_empty()
87+
88+
check_not_zoomed(dm)
15489

15590
check_suggested("dbplyr", use = TRUE)
15691

15792
dest <- src_from_src_or_con(dest)
158-
src_names <- src_tbls_impl(dm)
15993

160-
if (is_db(dest)) {
161-
dest_con <- con_from_src_or_con(dest)
162-
163-
# in case `table_names` was chosen by the user, check if the input makes sense:
164-
# 1. is there one name per dm-table?
165-
# 2. are there any duplicated table names?
166-
# 3. is it a named character or ident_q vector with the correct names?
167-
if (is.null(table_names)) {
168-
table_names_out <- repair_table_names_for_db(src_names, temporary, dest_con, schema)
169-
# https://github.com/tidyverse/dbplyr/issues/487
170-
if (is_mssql(dest)) {
171-
temporary <- FALSE
172-
}
173-
} else {
174-
if (!is.null(schema)) abort_one_of_schema_table_names()
175-
if (is_function(table_names) || is_bare_formula(table_names)) {
176-
table_name_fun <- as_function(table_names)
177-
table_names_out <- set_names(table_name_fun(src_names), src_names)
178-
} else {
179-
table_names_out <- table_names
180-
}
181-
check_naming(names(table_names_out), src_names)
182-
183-
if (anyDuplicated(table_names_out)) {
184-
problem <- table_names_out[duplicated(table_names_out)][[1]]
185-
abort_copy_dm_to_table_names_duplicated(problem)
186-
}
187-
188-
names(table_names_out) <- src_names
189-
}
190-
} else {
191-
# FIXME: Other data sources than local and database possible
192-
deprecate_soft(
193-
"0.1.6", "dm::copy_dm_to(dest = 'must refer to a remote data source')",
94+
if (!is_db(dest)) {
95+
deprecate_stop(
96+
"0.1.6", "dm::copy_dm_to(dest = 'must refer to a DBI connection')",
19497
"dm::collect.dm()"
19598
)
196-
table_names_out <- set_names(src_names)
19799
}
198100

199-
check_not_zoomed(dm)
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
113+
}
114+
} else {
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)
200123

201-
# FIXME: if same_src(), can use compute() but need to set NOT NULL and other
202-
# constraints
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+
}
203128

204-
# Shortcut necessary to avoid copying into .GlobalEnv
205-
if (!is_db(dest)) {
206-
return(dm)
129+
names(table_names_out) <- src_names
130+
}
131+
132+
table_names_out <- ddl_check_table_names(table_names_out, dm)
133+
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)
207142
}
208143

209-
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)
210145

211-
ticker_create <- new_ticker(
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)
152+
153+
ticker_pre <- new_ticker(
212154
"creating tables",
213-
n = length(queries$sql_table),
155+
n = length(pre),
214156
progress = progress,
215157
top_level_fun = "copy_dm_to"
216158
)
217159

218160
# create tables
219-
walk(queries$sql_table, ticker_create(~ {
161+
walk(pre, ticker_pre(~ {
220162
DBI::dbExecute(dest_con, .x, immediate = TRUE)
221163
}))
222164

223-
ticker_populate <- new_ticker(
165+
ticker_load <- new_ticker(
224166
"populating tables",
225-
n = length(queries$name),
167+
n = length(load),
226168
progress = progress,
227169
top_level_fun = "copy_dm_to"
228170
)
229171

230172
# populate tables
231-
pwalk(
232-
queries[c("name", "remote_name")],
233-
ticker_populate(~ db_append_table(
234-
con = dest_con,
235-
remote_table = .y,
236-
table = dm[[.x]],
237-
progress = progress,
238-
autoinc = dm_get_all_pks(dm, table = !!.x)$autoincrement
239-
))
240-
)
173+
walk(load, ticker_load(~ {
174+
DBI::dbExecute(dest_con, .x, immediate = TRUE)
175+
}))
241176

242-
ticker_index <- new_ticker(
177+
ticker_post <- new_ticker(
243178
"creating indexes",
244-
n = sum(lengths(queries$sql_index)),
179+
n = length(post),
245180
progress = progress,
246181
top_level_fun = "copy_dm_to"
247182
)
248183

249184
# create indexes
250-
walk(unlist(queries$sql_index), ticker_index(~ {
185+
walk(post, ticker_post(~ {
251186
DBI::dbExecute(dest_con, .x, immediate = TRUE)
252187
}))
253188

254189
# remote dm is same as source dm with replaced data
190+
# FIXME: Extract function
255191
def <- dm_get_def(dm)
256192

257193
remote_tables <- map2(
258194
table_names_out,
259195
map(def$data, colnames),
260-
~ tbl(dest_con, ..1, vars = ..2)
196+
~ tbl(dest_con, .x, vars = .y)
261197
)
262198

263-
def$data <- unname(remote_tables[names(dm)])
199+
def$data <- unname(remote_tables)
264200
remote_dm <- dm_from_def(def)
265201

266202
invisible(debug_dm_validate(remote_dm))

R/error-helpers.R

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -187,30 +187,6 @@ error_txt_no_overwrite <- function(fun_name) {
187187
glue("`{fun_name}()` does not support the `overwrite` argument.")
188188
}
189189

190-
abort_no_types <- function() {
191-
abort(error_txt_no_types(), class = dm_error_full("no_types"))
192-
}
193-
194-
error_txt_no_types <- function() {
195-
"`copy_dm_to()` does not support the `types` argument."
196-
}
197-
198-
abort_no_indexes <- function() {
199-
abort(error_txt_no_indexes(), class = dm_error_full("no_indexes"))
200-
}
201-
202-
error_txt_no_indexes <- function() {
203-
"`copy_dm_to()` does not support the `indexes` argument."
204-
}
205-
206-
abort_no_unique_indexes <- function() {
207-
abort(error_txt_no_unique_indexes(), class = dm_error_full("no_unique_indexes"))
208-
}
209-
210-
error_txt_no_unique_indexes <- function() {
211-
"`copy_dm_to()` does not support the `unique_indexes` argument."
212-
}
213-
214190
abort_update_not_supported <- function() {
215191
abort(error_txt_update_not_supported(), class = dm_error_full("update_not_supported"))
216192
}

R/zzx-deprecated.R

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -127,12 +127,20 @@ cdm_copy_to <- function(dest, dm, ..., types = NULL, overwrite = NULL, indexes =
127127
}
128128
}
129129

130-
copy_dm_to(
131-
dest = dest, dm = dm, ... = ..., types = types,
132-
overwrite = overwrite, indexes = indexes, unique_indexes = unique_indexes,
130+
inject(copy_dm_to(
131+
dest = dest,
132+
dm = dm,
133+
... = ...,
134+
!!!compact(list(
135+
types = types,
136+
overwrite = overwrite,
137+
indexes = indexes,
138+
unique_indexes = unique_indexes
139+
)),
133140
set_key_constraints = set_key_constraints,
134-
table_names = table_names, temporary = temporary
135-
)
141+
table_names = table_names,
142+
temporary = temporary
143+
))
136144
}
137145

138146
#' @rdname deprecated

0 commit comments

Comments
 (0)