From f432ec8f6ea479da5e9c733839522c6c430b31bf Mon Sep 17 00:00:00 2001 From: toppyy <43851547+toppyy@users.noreply.github.com> Date: Tue, 15 Oct 2024 20:47:36 +0300 Subject: [PATCH 01/10] Add postgresImportLargeObject and postgresWriteToLargeObject --- NAMESPACE | 2 + R/PqConnection.R | 62 +++++++++++++++++++++++++ R/cpp11.R | 8 ++++ man/postgresImportLargeObject.Rd | 29 ++++++++++++ man/postgresWriteToLargeObject.Rd | 34 ++++++++++++++ src/DbConnection.cpp | 28 ++++++++++++ src/DbConnection.h | 4 ++ src/connection.cpp | 10 ++++ src/cpp11.cpp | 68 +++++++++++++++++----------- src/pch.h | 1 + tests/testthat/data/large_object.txt | 1 + tests/testthat/test-large-objects.R | 25 ++++++++++ 12 files changed, 246 insertions(+), 26 deletions(-) create mode 100644 man/postgresImportLargeObject.Rd create mode 100644 man/postgresWriteToLargeObject.Rd create mode 100644 tests/testthat/data/large_object.txt create mode 100644 tests/testthat/test-large-objects.R diff --git a/NAMESPACE b/NAMESPACE index 6a09679a..12c4d716 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -6,8 +6,10 @@ export(Postgres) export(Redshift) export(postgresDefault) export(postgresHasDefault) +export(postgresImportLargeObject) export(postgresIsTransacting) export(postgresWaitForNotify) +export(postgresWriteToLargeObject) exportClasses(PqConnection) exportClasses(PqDriver) exportClasses(PqResult) diff --git a/R/PqConnection.R b/R/PqConnection.R index e01b0378..31768d16 100644 --- a/R/PqConnection.R +++ b/R/PqConnection.R @@ -135,3 +135,65 @@ postgresWaitForNotify <- function(conn, timeout = 1) { postgresIsTransacting <- function(conn) { connection_is_transacting(conn@ptr) } + + +#' Imports a large object from file +#' +#' Returns an object idenfier (Oid) for the imported large object +#' +#' @export +#' @param conn a [PqConnection-class] object, produced by +#' [DBI::dbConnect()] +#' @param filename A path to the large object to import +#' @param oid The oid to write to. Defaults to 0 which assigns an unsed oid +#' @return An integer, the identifier of the large object +#' @examples +#' con <- postgresDefault() +#' path_to_file <- 'my_image.png' +#' dbWithTransaction(con, { +#' oid <- postgresImportLargeObject(con, test_file_path) +#' }) +postgresImportLargeObject <- function(conn, filename, oid = 0) { + + if (!postgresIsTransacting(conn)) { + stopc("Cannot import a large object outside of a transaction") + } + + if (oid < 0) stopc("'oid' cannot be negative.") + if (is.null(oid) | is.na(oid)) stopc("'oid' cannot be NULL/NA") + + connection_import_lo_from_file(conn@ptr, filename, oid) +} + +#' Write to large object +#' +#' Writes a number of bytes from a raw vector to a large object. +#' +#' @export +#' @param conn a [PqConnection-class] object, produced by +#' [DBI::dbConnect()] +#' @param oid The oid to write to +#' @param buf Buffer of bytes to write. A vector of type "raw". +#' @param bytes_to_write Number of bytes to write from 'buf' +#' @return The number of bytes written to large object +#' @examples +#' con <- postgresDefault() +#' oid <- dbGetQuery(con, "select lo_create(0) as oid")$oid[1] +#' raw_data <- serialize(mtcars,NULL) +#' dbWithTransaction(con,{ +#' bytes_written <- postgresWriteToLargeObject(con, oid, raw_data, length(raw_data)) +#' }) +#' bytes <- dbGetQuery(con, "select lo_get($1) as lo_data", params=list(oid))$lo_data[1] +#' mtcars_db <- unserialize(unlist(bytes)) +postgresWriteToLargeObject <- function(conn, oid, buf, bytes_to_write) { + + if (!postgresIsTransacting(conn)) { + stopc("Cannot write to a large object outside of a transaction") + } + + if (oid < 0) stopc("'oid' cannot be negative.") + if (is.null(oid) | is.na(oid)) stopc("'oid' cannot be NULL/NA") + if (oid <= 0) stopc("'bytes_to_write' must be greater than zero.") + + connection_write_to_lo(conn@ptr, oid, buf, bytes_to_write) +} \ No newline at end of file diff --git a/R/cpp11.R b/R/cpp11.R index 5c1d0cd2..a72df67a 100644 --- a/R/cpp11.R +++ b/R/cpp11.R @@ -36,6 +36,14 @@ connection_set_transacting <- function(con, transacting) { invisible(.Call(`_RPostgres_connection_set_transacting`, con, transacting)) } +connection_write_to_lo <- function(con, oid, lo_buf_raw, bytes_to_write) { + .Call(`_RPostgres_connection_write_to_lo`, con, oid, lo_buf_raw, bytes_to_write) +} + +connection_import_lo_from_file <- function(con, filename, oid) { + .Call(`_RPostgres_connection_import_lo_from_file`, con, filename, oid) +} + connection_copy_data <- function(con, sql, df) { invisible(.Call(`_RPostgres_connection_copy_data`, con, sql, df)) } diff --git a/man/postgresImportLargeObject.Rd b/man/postgresImportLargeObject.Rd new file mode 100644 index 00000000..42dbf40d --- /dev/null +++ b/man/postgresImportLargeObject.Rd @@ -0,0 +1,29 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/PqConnection.R +\name{postgresImportLargeObject} +\alias{postgresImportLargeObject} +\title{Imports a large object from file} +\usage{ +postgresImportLargeObject(conn, filename, oid = 0) +} +\arguments{ +\item{conn}{a \linkS4class{PqConnection} object, produced by +\code{\link[DBI:dbConnect]{DBI::dbConnect()}}} + +\item{filename}{A path to the large object to import} + +\item{oid}{The oid to write to. Defaults to 0 which assigns an unsed oid} +} +\value{ +An integer, the identifier of the large object +} +\description{ +Returns an object idenfier (Oid) for the imported large object +} +\examples{ +con <- postgresDefault() +path_to_file <- 'my_image.png' +dbWithTransaction(con, { + oid <- postgresImportLargeObject(con, test_file_path) +}) +} diff --git a/man/postgresWriteToLargeObject.Rd b/man/postgresWriteToLargeObject.Rd new file mode 100644 index 00000000..d15a0377 --- /dev/null +++ b/man/postgresWriteToLargeObject.Rd @@ -0,0 +1,34 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/PqConnection.R +\name{postgresWriteToLargeObject} +\alias{postgresWriteToLargeObject} +\title{Write to large object} +\usage{ +postgresWriteToLargeObject(conn, oid, buf, bytes_to_write) +} +\arguments{ +\item{conn}{a \linkS4class{PqConnection} object, produced by +\code{\link[DBI:dbConnect]{DBI::dbConnect()}}} + +\item{oid}{The oid to write to} + +\item{buf}{Buffer of bytes to write. A vector of type "raw".} + +\item{bytes_to_write}{Number of bytes to write from 'buf'} +} +\value{ +The number of bytes written to large object +} +\description{ +Writes a number of bytes from a raw vector to a large object. +} +\examples{ +con <- postgresDefault() +oid <- dbGetQuery(con, "select lo_create(0) as oid")$oid[1] +raw_data <- serialize(mtcars,NULL) +dbWithTransaction(con,{ + bytes_written <- postgresWriteToLargeObject(con, oid, raw_data, length(raw_data)) +}) +bytes <- dbGetQuery(con, "select lo_get($1) as lo_data", params=list(oid))$lo_data[1] +mtcars_db <- unserialize(unlist(bytes)) +} diff --git a/src/DbConnection.cpp b/src/DbConnection.cpp index cf66200d..66e29d50 100644 --- a/src/DbConnection.cpp +++ b/src/DbConnection.cpp @@ -128,6 +128,34 @@ bool DbConnection::has_query() { return pCurrentResult_ != NULL; } + +int DbConnection::write_to_lo(int oid, const char *lo_buf, size_t bytes_to_write) { + int fd = lo_open(pConn_, oid, INV_READ|INV_WRITE); + if (fd < 0) { + cpp11::warning("Cannot access oid %d. No bytes written.", oid); + return(0); + } + int bytes_written = lo_write(pConn_, fd, lo_buf, bytes_to_write); + lo_close(pConn_,fd); + return(bytes_written); +} + +int DbConnection::import_lo_from_file(std::string filename, int p_oid) { + + FILE* fd = fopen(filename.c_str(), "r"); + if (fd == NULL) { + cpp11::warning("Unable to open file from path '%s'", filename.c_str()); + return(0); + } + fclose(fd); + + Oid lo_oid = lo_import_with_oid(pConn_, filename.c_str(), p_oid); + if (lo_oid == InvalidOid) { + return 0; + } + return(lo_oid); +} + void DbConnection::copy_data(std::string sql, cpp11::list df) { LOG_DEBUG << sql; diff --git a/src/DbConnection.h b/src/DbConnection.h index a0a70481..c1301c2d 100644 --- a/src/DbConnection.h +++ b/src/DbConnection.h @@ -35,6 +35,10 @@ class DbConnection : boost::noncopyable { bool has_query(); void copy_data(std::string sql, cpp11::list df); + + int write_to_lo(int oid, const char *lo_buf, size_t bytes_to_write); + int import_lo_from_file(std::string file_path, int p_oid); + void check_connection(); cpp11::list info(); diff --git a/src/connection.cpp b/src/connection.cpp index 2e0cb172..1caf76fb 100644 --- a/src/connection.cpp +++ b/src/connection.cpp @@ -91,6 +91,16 @@ void connection_set_transacting(DbConnection* con, bool transacting) { } // Specific functions +[[cpp11::register]] +int connection_write_to_lo(DbConnection* con, int oid, cpp11::raws lo_buf_raw, size_t bytes_to_write) { + const char *lo_buf = (char*) RAW(lo_buf_raw); + return con->write_to_lo(oid, lo_buf, bytes_to_write); +} + +[[cpp11::register]] +int connection_import_lo_from_file(DbConnection* con, std::string filename, int oid) { + return con->import_lo_from_file(filename, oid); +} [[cpp11::register]] void connection_copy_data(DbConnection* con, std::string sql, cpp11::list df) { diff --git a/src/cpp11.cpp b/src/cpp11.cpp index bf613ab9..b1f6fe22 100644 --- a/src/cpp11.cpp +++ b/src/cpp11.cpp @@ -71,6 +71,20 @@ extern "C" SEXP _RPostgres_connection_set_transacting(SEXP con, SEXP transacting END_CPP11 } // connection.cpp +int connection_write_to_lo(DbConnection* con, int oid, cpp11::raws lo_buf_raw, size_t bytes_to_write); +extern "C" SEXP _RPostgres_connection_write_to_lo(SEXP con, SEXP oid, SEXP lo_buf_raw, SEXP bytes_to_write) { + BEGIN_CPP11 + return cpp11::as_sexp(connection_write_to_lo(cpp11::as_cpp>(con), cpp11::as_cpp>(oid), cpp11::as_cpp>(lo_buf_raw), cpp11::as_cpp>(bytes_to_write))); + END_CPP11 +} +// connection.cpp +int connection_import_lo_from_file(DbConnection* con, std::string filename, int oid); +extern "C" SEXP _RPostgres_connection_import_lo_from_file(SEXP con, SEXP filename, SEXP oid) { + BEGIN_CPP11 + return cpp11::as_sexp(connection_import_lo_from_file(cpp11::as_cpp>(con), cpp11::as_cpp>(filename), cpp11::as_cpp>(oid))); + END_CPP11 +} +// connection.cpp void connection_copy_data(DbConnection* con, std::string sql, cpp11::list df); extern "C" SEXP _RPostgres_connection_copy_data(SEXP con, SEXP sql, SEXP df) { BEGIN_CPP11 @@ -197,32 +211,34 @@ extern "C" SEXP _RPostgres_result_column_info(SEXP res) { extern "C" { static const R_CallMethodDef CallEntries[] = { - {"_RPostgres_client_version", (DL_FUNC) &_RPostgres_client_version, 0}, - {"_RPostgres_connection_copy_data", (DL_FUNC) &_RPostgres_connection_copy_data, 3}, - {"_RPostgres_connection_create", (DL_FUNC) &_RPostgres_connection_create, 3}, - {"_RPostgres_connection_get_temp_schema", (DL_FUNC) &_RPostgres_connection_get_temp_schema, 1}, - {"_RPostgres_connection_info", (DL_FUNC) &_RPostgres_connection_info, 1}, - {"_RPostgres_connection_is_transacting", (DL_FUNC) &_RPostgres_connection_is_transacting, 1}, - {"_RPostgres_connection_quote_identifier", (DL_FUNC) &_RPostgres_connection_quote_identifier, 2}, - {"_RPostgres_connection_quote_string", (DL_FUNC) &_RPostgres_connection_quote_string, 2}, - {"_RPostgres_connection_release", (DL_FUNC) &_RPostgres_connection_release, 1}, - {"_RPostgres_connection_set_temp_schema", (DL_FUNC) &_RPostgres_connection_set_temp_schema, 2}, - {"_RPostgres_connection_set_transacting", (DL_FUNC) &_RPostgres_connection_set_transacting, 2}, - {"_RPostgres_connection_valid", (DL_FUNC) &_RPostgres_connection_valid, 1}, - {"_RPostgres_connection_wait_for_notify", (DL_FUNC) &_RPostgres_connection_wait_for_notify, 2}, - {"_RPostgres_encode_data_frame", (DL_FUNC) &_RPostgres_encode_data_frame, 1}, - {"_RPostgres_encode_vector", (DL_FUNC) &_RPostgres_encode_vector, 1}, - {"_RPostgres_encrypt_password", (DL_FUNC) &_RPostgres_encrypt_password, 2}, - {"_RPostgres_init_logging", (DL_FUNC) &_RPostgres_init_logging, 1}, - {"_RPostgres_result_bind", (DL_FUNC) &_RPostgres_result_bind, 2}, - {"_RPostgres_result_column_info", (DL_FUNC) &_RPostgres_result_column_info, 1}, - {"_RPostgres_result_create", (DL_FUNC) &_RPostgres_result_create, 3}, - {"_RPostgres_result_fetch", (DL_FUNC) &_RPostgres_result_fetch, 2}, - {"_RPostgres_result_has_completed", (DL_FUNC) &_RPostgres_result_has_completed, 1}, - {"_RPostgres_result_release", (DL_FUNC) &_RPostgres_result_release, 1}, - {"_RPostgres_result_rows_affected", (DL_FUNC) &_RPostgres_result_rows_affected, 1}, - {"_RPostgres_result_rows_fetched", (DL_FUNC) &_RPostgres_result_rows_fetched, 1}, - {"_RPostgres_result_valid", (DL_FUNC) &_RPostgres_result_valid, 1}, + {"_RPostgres_client_version", (DL_FUNC) &_RPostgres_client_version, 0}, + {"_RPostgres_connection_copy_data", (DL_FUNC) &_RPostgres_connection_copy_data, 3}, + {"_RPostgres_connection_create", (DL_FUNC) &_RPostgres_connection_create, 3}, + {"_RPostgres_connection_get_temp_schema", (DL_FUNC) &_RPostgres_connection_get_temp_schema, 1}, + {"_RPostgres_connection_import_lo_from_file", (DL_FUNC) &_RPostgres_connection_import_lo_from_file, 3}, + {"_RPostgres_connection_info", (DL_FUNC) &_RPostgres_connection_info, 1}, + {"_RPostgres_connection_is_transacting", (DL_FUNC) &_RPostgres_connection_is_transacting, 1}, + {"_RPostgres_connection_quote_identifier", (DL_FUNC) &_RPostgres_connection_quote_identifier, 2}, + {"_RPostgres_connection_quote_string", (DL_FUNC) &_RPostgres_connection_quote_string, 2}, + {"_RPostgres_connection_release", (DL_FUNC) &_RPostgres_connection_release, 1}, + {"_RPostgres_connection_set_temp_schema", (DL_FUNC) &_RPostgres_connection_set_temp_schema, 2}, + {"_RPostgres_connection_set_transacting", (DL_FUNC) &_RPostgres_connection_set_transacting, 2}, + {"_RPostgres_connection_valid", (DL_FUNC) &_RPostgres_connection_valid, 1}, + {"_RPostgres_connection_wait_for_notify", (DL_FUNC) &_RPostgres_connection_wait_for_notify, 2}, + {"_RPostgres_connection_write_to_lo", (DL_FUNC) &_RPostgres_connection_write_to_lo, 4}, + {"_RPostgres_encode_data_frame", (DL_FUNC) &_RPostgres_encode_data_frame, 1}, + {"_RPostgres_encode_vector", (DL_FUNC) &_RPostgres_encode_vector, 1}, + {"_RPostgres_encrypt_password", (DL_FUNC) &_RPostgres_encrypt_password, 2}, + {"_RPostgres_init_logging", (DL_FUNC) &_RPostgres_init_logging, 1}, + {"_RPostgres_result_bind", (DL_FUNC) &_RPostgres_result_bind, 2}, + {"_RPostgres_result_column_info", (DL_FUNC) &_RPostgres_result_column_info, 1}, + {"_RPostgres_result_create", (DL_FUNC) &_RPostgres_result_create, 3}, + {"_RPostgres_result_fetch", (DL_FUNC) &_RPostgres_result_fetch, 2}, + {"_RPostgres_result_has_completed", (DL_FUNC) &_RPostgres_result_has_completed, 1}, + {"_RPostgres_result_release", (DL_FUNC) &_RPostgres_result_release, 1}, + {"_RPostgres_result_rows_affected", (DL_FUNC) &_RPostgres_result_rows_affected, 1}, + {"_RPostgres_result_rows_fetched", (DL_FUNC) &_RPostgres_result_rows_fetched, 1}, + {"_RPostgres_result_valid", (DL_FUNC) &_RPostgres_result_valid, 1}, {NULL, NULL, 0} }; } diff --git a/src/pch.h b/src/pch.h index 23ca8422..f15e4cf3 100644 --- a/src/pch.h +++ b/src/pch.h @@ -1,5 +1,6 @@ #include #include +#include #include diff --git a/tests/testthat/data/large_object.txt b/tests/testthat/data/large_object.txt new file mode 100644 index 00000000..7d72bd78 --- /dev/null +++ b/tests/testthat/data/large_object.txt @@ -0,0 +1 @@ +postgres \ No newline at end of file diff --git a/tests/testthat/test-large-objects.R b/tests/testthat/test-large-objects.R new file mode 100644 index 00000000..f3ae0fe0 --- /dev/null +++ b/tests/testthat/test-large-objects.R @@ -0,0 +1,25 @@ +test_that("can write and read a large object", { + con <- postgresDefault() + oid <- dbGetQuery(con, "select lo_create(0) as oid")$oid[1] + data <- "test string" + raw_data <- serialize(data,NULL) + dbWithTransaction(con,{ + bytes_written <- postgresWriteToLargeObject(con,oid,raw_data,length(raw_data)) + }) + result <- dbGetQuery(con, "select lo_get($1) as lo_data", params=list(oid)) + expect_equal(unserialize(unlist(result$lo_data[1])), data) + expect_equal(length(raw_data),bytes_written) +}) + + +test_that("can import and read a large object", { + con <- postgresDefault() + test_file_path <- paste0(test_path(),'/data/large_object.txt') + dbWithTransaction(con, { + oid <- postgresImportLargeObject(con, test_file_path) + }) + expect_gt(oid,0) + lo_data <- unlist(dbGetQuery(con, "select lo_get($1) as lo_data", params=list(oid))$lo_data[1]) + large_object_txt <- as.raw(c(0x70, 0x6f, 0x73, 0x74, 0x67, 0x72, 0x65, 0x73)) # the string 'postgres' + expect_equal(lo_data, large_object_txt) +}) From 878d932ce9928ee29557aa91ba467be21a14c124 Mon Sep 17 00:00:00 2001 From: toby <43851547+toppyy@users.noreply.github.com> Date: Sat, 19 Oct 2024 10:48:21 +0300 Subject: [PATCH 02/10] rm WriteToLargeObject as unnecessary --- NAMESPACE | 1 - R/PqConnection.R | 33 ------------------------ R/cpp11.R | 4 --- man/postgresWriteToLargeObject.Rd | 34 ------------------------- src/DbConnection.cpp | 12 --------- src/DbConnection.h | 1 - src/connection.cpp | 6 ----- src/cpp11.cpp | 8 ------ tests/testthat/test-ImportLargeObject.R | 12 +++++++++ tests/testthat/test-large-objects.R | 25 ------------------ 10 files changed, 12 insertions(+), 124 deletions(-) delete mode 100644 man/postgresWriteToLargeObject.Rd create mode 100644 tests/testthat/test-ImportLargeObject.R delete mode 100644 tests/testthat/test-large-objects.R diff --git a/NAMESPACE b/NAMESPACE index 12c4d716..19ef3ccf 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -9,7 +9,6 @@ export(postgresHasDefault) export(postgresImportLargeObject) export(postgresIsTransacting) export(postgresWaitForNotify) -export(postgresWriteToLargeObject) exportClasses(PqConnection) exportClasses(PqDriver) exportClasses(PqResult) diff --git a/R/PqConnection.R b/R/PqConnection.R index 31768d16..af4e34ff 100644 --- a/R/PqConnection.R +++ b/R/PqConnection.R @@ -164,36 +164,3 @@ postgresImportLargeObject <- function(conn, filename, oid = 0) { connection_import_lo_from_file(conn@ptr, filename, oid) } - -#' Write to large object -#' -#' Writes a number of bytes from a raw vector to a large object. -#' -#' @export -#' @param conn a [PqConnection-class] object, produced by -#' [DBI::dbConnect()] -#' @param oid The oid to write to -#' @param buf Buffer of bytes to write. A vector of type "raw". -#' @param bytes_to_write Number of bytes to write from 'buf' -#' @return The number of bytes written to large object -#' @examples -#' con <- postgresDefault() -#' oid <- dbGetQuery(con, "select lo_create(0) as oid")$oid[1] -#' raw_data <- serialize(mtcars,NULL) -#' dbWithTransaction(con,{ -#' bytes_written <- postgresWriteToLargeObject(con, oid, raw_data, length(raw_data)) -#' }) -#' bytes <- dbGetQuery(con, "select lo_get($1) as lo_data", params=list(oid))$lo_data[1] -#' mtcars_db <- unserialize(unlist(bytes)) -postgresWriteToLargeObject <- function(conn, oid, buf, bytes_to_write) { - - if (!postgresIsTransacting(conn)) { - stopc("Cannot write to a large object outside of a transaction") - } - - if (oid < 0) stopc("'oid' cannot be negative.") - if (is.null(oid) | is.na(oid)) stopc("'oid' cannot be NULL/NA") - if (oid <= 0) stopc("'bytes_to_write' must be greater than zero.") - - connection_write_to_lo(conn@ptr, oid, buf, bytes_to_write) -} \ No newline at end of file diff --git a/R/cpp11.R b/R/cpp11.R index a72df67a..15734e20 100644 --- a/R/cpp11.R +++ b/R/cpp11.R @@ -36,10 +36,6 @@ connection_set_transacting <- function(con, transacting) { invisible(.Call(`_RPostgres_connection_set_transacting`, con, transacting)) } -connection_write_to_lo <- function(con, oid, lo_buf_raw, bytes_to_write) { - .Call(`_RPostgres_connection_write_to_lo`, con, oid, lo_buf_raw, bytes_to_write) -} - connection_import_lo_from_file <- function(con, filename, oid) { .Call(`_RPostgres_connection_import_lo_from_file`, con, filename, oid) } diff --git a/man/postgresWriteToLargeObject.Rd b/man/postgresWriteToLargeObject.Rd deleted file mode 100644 index d15a0377..00000000 --- a/man/postgresWriteToLargeObject.Rd +++ /dev/null @@ -1,34 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/PqConnection.R -\name{postgresWriteToLargeObject} -\alias{postgresWriteToLargeObject} -\title{Write to large object} -\usage{ -postgresWriteToLargeObject(conn, oid, buf, bytes_to_write) -} -\arguments{ -\item{conn}{a \linkS4class{PqConnection} object, produced by -\code{\link[DBI:dbConnect]{DBI::dbConnect()}}} - -\item{oid}{The oid to write to} - -\item{buf}{Buffer of bytes to write. A vector of type "raw".} - -\item{bytes_to_write}{Number of bytes to write from 'buf'} -} -\value{ -The number of bytes written to large object -} -\description{ -Writes a number of bytes from a raw vector to a large object. -} -\examples{ -con <- postgresDefault() -oid <- dbGetQuery(con, "select lo_create(0) as oid")$oid[1] -raw_data <- serialize(mtcars,NULL) -dbWithTransaction(con,{ - bytes_written <- postgresWriteToLargeObject(con, oid, raw_data, length(raw_data)) -}) -bytes <- dbGetQuery(con, "select lo_get($1) as lo_data", params=list(oid))$lo_data[1] -mtcars_db <- unserialize(unlist(bytes)) -} diff --git a/src/DbConnection.cpp b/src/DbConnection.cpp index 66e29d50..2771029f 100644 --- a/src/DbConnection.cpp +++ b/src/DbConnection.cpp @@ -128,18 +128,6 @@ bool DbConnection::has_query() { return pCurrentResult_ != NULL; } - -int DbConnection::write_to_lo(int oid, const char *lo_buf, size_t bytes_to_write) { - int fd = lo_open(pConn_, oid, INV_READ|INV_WRITE); - if (fd < 0) { - cpp11::warning("Cannot access oid %d. No bytes written.", oid); - return(0); - } - int bytes_written = lo_write(pConn_, fd, lo_buf, bytes_to_write); - lo_close(pConn_,fd); - return(bytes_written); -} - int DbConnection::import_lo_from_file(std::string filename, int p_oid) { FILE* fd = fopen(filename.c_str(), "r"); diff --git a/src/DbConnection.h b/src/DbConnection.h index c1301c2d..21989e4b 100644 --- a/src/DbConnection.h +++ b/src/DbConnection.h @@ -36,7 +36,6 @@ class DbConnection : boost::noncopyable { void copy_data(std::string sql, cpp11::list df); - int write_to_lo(int oid, const char *lo_buf, size_t bytes_to_write); int import_lo_from_file(std::string file_path, int p_oid); diff --git a/src/connection.cpp b/src/connection.cpp index 1caf76fb..424ddd7e 100644 --- a/src/connection.cpp +++ b/src/connection.cpp @@ -91,12 +91,6 @@ void connection_set_transacting(DbConnection* con, bool transacting) { } // Specific functions -[[cpp11::register]] -int connection_write_to_lo(DbConnection* con, int oid, cpp11::raws lo_buf_raw, size_t bytes_to_write) { - const char *lo_buf = (char*) RAW(lo_buf_raw); - return con->write_to_lo(oid, lo_buf, bytes_to_write); -} - [[cpp11::register]] int connection_import_lo_from_file(DbConnection* con, std::string filename, int oid) { return con->import_lo_from_file(filename, oid); diff --git a/src/cpp11.cpp b/src/cpp11.cpp index b1f6fe22..58dcf195 100644 --- a/src/cpp11.cpp +++ b/src/cpp11.cpp @@ -71,13 +71,6 @@ extern "C" SEXP _RPostgres_connection_set_transacting(SEXP con, SEXP transacting END_CPP11 } // connection.cpp -int connection_write_to_lo(DbConnection* con, int oid, cpp11::raws lo_buf_raw, size_t bytes_to_write); -extern "C" SEXP _RPostgres_connection_write_to_lo(SEXP con, SEXP oid, SEXP lo_buf_raw, SEXP bytes_to_write) { - BEGIN_CPP11 - return cpp11::as_sexp(connection_write_to_lo(cpp11::as_cpp>(con), cpp11::as_cpp>(oid), cpp11::as_cpp>(lo_buf_raw), cpp11::as_cpp>(bytes_to_write))); - END_CPP11 -} -// connection.cpp int connection_import_lo_from_file(DbConnection* con, std::string filename, int oid); extern "C" SEXP _RPostgres_connection_import_lo_from_file(SEXP con, SEXP filename, SEXP oid) { BEGIN_CPP11 @@ -225,7 +218,6 @@ static const R_CallMethodDef CallEntries[] = { {"_RPostgres_connection_set_transacting", (DL_FUNC) &_RPostgres_connection_set_transacting, 2}, {"_RPostgres_connection_valid", (DL_FUNC) &_RPostgres_connection_valid, 1}, {"_RPostgres_connection_wait_for_notify", (DL_FUNC) &_RPostgres_connection_wait_for_notify, 2}, - {"_RPostgres_connection_write_to_lo", (DL_FUNC) &_RPostgres_connection_write_to_lo, 4}, {"_RPostgres_encode_data_frame", (DL_FUNC) &_RPostgres_encode_data_frame, 1}, {"_RPostgres_encode_vector", (DL_FUNC) &_RPostgres_encode_vector, 1}, {"_RPostgres_encrypt_password", (DL_FUNC) &_RPostgres_encrypt_password, 2}, diff --git a/tests/testthat/test-ImportLargeObject.R b/tests/testthat/test-ImportLargeObject.R new file mode 100644 index 00000000..9eaaacff --- /dev/null +++ b/tests/testthat/test-ImportLargeObject.R @@ -0,0 +1,12 @@ + +test_that("can import and read a large object", { + con <- postgresDefault() + test_file_path <- paste0(test_path(),'/data/large_object.txt') + dbWithTransaction(con, { + oid <- postgresImportLargeObject(con, test_file_path) + }) + expect_gt(oid,0) + lo_data <- unlist(dbGetQuery(con, "select lo_get($1) as lo_data", params=list(oid))$lo_data[1]) + large_object_txt <- as.raw(c(0x70, 0x6f, 0x73, 0x74, 0x67, 0x72, 0x65, 0x73)) # the string 'postgres' + expect_equal(lo_data, large_object_txt) +}) diff --git a/tests/testthat/test-large-objects.R b/tests/testthat/test-large-objects.R deleted file mode 100644 index f3ae0fe0..00000000 --- a/tests/testthat/test-large-objects.R +++ /dev/null @@ -1,25 +0,0 @@ -test_that("can write and read a large object", { - con <- postgresDefault() - oid <- dbGetQuery(con, "select lo_create(0) as oid")$oid[1] - data <- "test string" - raw_data <- serialize(data,NULL) - dbWithTransaction(con,{ - bytes_written <- postgresWriteToLargeObject(con,oid,raw_data,length(raw_data)) - }) - result <- dbGetQuery(con, "select lo_get($1) as lo_data", params=list(oid)) - expect_equal(unserialize(unlist(result$lo_data[1])), data) - expect_equal(length(raw_data),bytes_written) -}) - - -test_that("can import and read a large object", { - con <- postgresDefault() - test_file_path <- paste0(test_path(),'/data/large_object.txt') - dbWithTransaction(con, { - oid <- postgresImportLargeObject(con, test_file_path) - }) - expect_gt(oid,0) - lo_data <- unlist(dbGetQuery(con, "select lo_get($1) as lo_data", params=list(oid))$lo_data[1]) - large_object_txt <- as.raw(c(0x70, 0x6f, 0x73, 0x74, 0x67, 0x72, 0x65, 0x73)) # the string 'postgres' - expect_equal(lo_data, large_object_txt) -}) From 32fd13fe6d9101a6543ebaf49cb8d51cd3ed773c Mon Sep 17 00:00:00 2001 From: toby <43851547+toppyy@users.noreply.github.com> Date: Sat, 19 Oct 2024 10:53:04 +0300 Subject: [PATCH 03/10] filename -> filepath --- R/PqConnection.R | 13 +++++++------ man/postgresImportLargeObject.Rd | 6 +++--- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/R/PqConnection.R b/R/PqConnection.R index af4e34ff..1433e0cd 100644 --- a/R/PqConnection.R +++ b/R/PqConnection.R @@ -144,23 +144,24 @@ postgresIsTransacting <- function(conn) { #' @export #' @param conn a [PqConnection-class] object, produced by #' [DBI::dbConnect()] -#' @param filename A path to the large object to import -#' @param oid The oid to write to. Defaults to 0 which assigns an unsed oid -#' @return An integer, the identifier of the large object +#' @param filepath a path to the large object to import +#' @param oid the oid to write to. Defaults to 0 which assigns an unused oid +#' @return the identifier of the large object, an integer #' @examples #' con <- postgresDefault() #' path_to_file <- 'my_image.png' #' dbWithTransaction(con, { #' oid <- postgresImportLargeObject(con, test_file_path) #' }) -postgresImportLargeObject <- function(conn, filename, oid = 0) { +postgresImportLargeObject <- function(conn, filepath = NULL, oid = 0) { if (!postgresIsTransacting(conn)) { stopc("Cannot import a large object outside of a transaction") } - if (oid < 0) stopc("'oid' cannot be negative.") + if (is.null(filepath)) stopc("'filepath' cannot be NULL") + if (oid < 0) stopc("'oid' cannot be negative") if (is.null(oid) | is.na(oid)) stopc("'oid' cannot be NULL/NA") - connection_import_lo_from_file(conn@ptr, filename, oid) + connection_import_lo_from_file(conn@ptr, filepath, oid) } diff --git a/man/postgresImportLargeObject.Rd b/man/postgresImportLargeObject.Rd index 42dbf40d..cb562047 100644 --- a/man/postgresImportLargeObject.Rd +++ b/man/postgresImportLargeObject.Rd @@ -4,15 +4,15 @@ \alias{postgresImportLargeObject} \title{Imports a large object from file} \usage{ -postgresImportLargeObject(conn, filename, oid = 0) +postgresImportLargeObject(conn, filepath = NULL, oid = 0) } \arguments{ \item{conn}{a \linkS4class{PqConnection} object, produced by \code{\link[DBI:dbConnect]{DBI::dbConnect()}}} -\item{filename}{A path to the large object to import} +\item{filepath}{A path to the large object to import} -\item{oid}{The oid to write to. Defaults to 0 which assigns an unsed oid} +\item{oid}{The oid to write to. Defaults to 0 which assigns an unused oid} } \value{ An integer, the identifier of the large object From 2476b0ee17cf76892f4c930dff3f8cbf4af51cb3 Mon Sep 17 00:00:00 2001 From: toby <43851547+toppyy@users.noreply.github.com> Date: Sat, 19 Oct 2024 11:36:46 +0300 Subject: [PATCH 04/10] check file access in R; add tests for errors --- R/PqConnection.R | 7 +++++- man/postgresImportLargeObject.Rd | 6 ++--- src/DbConnection.cpp | 11 --------- tests/testthat/test-ImportLargeObject.R | 31 ++++++++++++++++++++++--- 4 files changed, 37 insertions(+), 18 deletions(-) diff --git a/R/PqConnection.R b/R/PqConnection.R index 1433e0cd..796d8849 100644 --- a/R/PqConnection.R +++ b/R/PqConnection.R @@ -162,6 +162,11 @@ postgresImportLargeObject <- function(conn, filepath = NULL, oid = 0) { if (is.null(filepath)) stopc("'filepath' cannot be NULL") if (oid < 0) stopc("'oid' cannot be negative") if (is.null(oid) | is.na(oid)) stopc("'oid' cannot be NULL/NA") + if (file.access(filepath,4) == -1) stopc(paste0("Unable to read from filepath '",filepath,"'")) + + + out_oid = connection_import_lo_from_file(conn@ptr, filepath, oid) + if (out_oid == 0) stopc("Import failed. Maybe you tried to write to an existing oid?") + return(out_oid) - connection_import_lo_from_file(conn@ptr, filepath, oid) } diff --git a/man/postgresImportLargeObject.Rd b/man/postgresImportLargeObject.Rd index cb562047..e29749f9 100644 --- a/man/postgresImportLargeObject.Rd +++ b/man/postgresImportLargeObject.Rd @@ -10,12 +10,12 @@ postgresImportLargeObject(conn, filepath = NULL, oid = 0) \item{conn}{a \linkS4class{PqConnection} object, produced by \code{\link[DBI:dbConnect]{DBI::dbConnect()}}} -\item{filepath}{A path to the large object to import} +\item{filepath}{a path to the large object to import} -\item{oid}{The oid to write to. Defaults to 0 which assigns an unused oid} +\item{oid}{the oid to write to. Defaults to 0 which assigns an unused oid} } \value{ -An integer, the identifier of the large object +the identifier of the large object, an integer } \description{ Returns an object idenfier (Oid) for the imported large object diff --git a/src/DbConnection.cpp b/src/DbConnection.cpp index 2771029f..a24e1f98 100644 --- a/src/DbConnection.cpp +++ b/src/DbConnection.cpp @@ -129,18 +129,7 @@ bool DbConnection::has_query() { } int DbConnection::import_lo_from_file(std::string filename, int p_oid) { - - FILE* fd = fopen(filename.c_str(), "r"); - if (fd == NULL) { - cpp11::warning("Unable to open file from path '%s'", filename.c_str()); - return(0); - } - fclose(fd); - Oid lo_oid = lo_import_with_oid(pConn_, filename.c_str(), p_oid); - if (lo_oid == InvalidOid) { - return 0; - } return(lo_oid); } diff --git a/tests/testthat/test-ImportLargeObject.R b/tests/testthat/test-ImportLargeObject.R index 9eaaacff..bf2b9f62 100644 --- a/tests/testthat/test-ImportLargeObject.R +++ b/tests/testthat/test-ImportLargeObject.R @@ -1,12 +1,37 @@ test_that("can import and read a large object", { con <- postgresDefault() + on.exit(dbDisconnect(con)) test_file_path <- paste0(test_path(),'/data/large_object.txt') - dbWithTransaction(con, { - oid <- postgresImportLargeObject(con, test_file_path) - }) + dbWithTransaction(con, { oid <- postgresImportLargeObject(con, test_file_path) }) expect_gt(oid,0) lo_data <- unlist(dbGetQuery(con, "select lo_get($1) as lo_data", params=list(oid))$lo_data[1]) large_object_txt <- as.raw(c(0x70, 0x6f, 0x73, 0x74, 0x67, 0x72, 0x65, 0x73)) # the string 'postgres' expect_equal(lo_data, large_object_txt) }) + + +test_that("importing to an existing oid throws error", { + con <- postgresDefault() + on.exit(dbDisconnect(con)) + test_file_path <- paste0(test_path(),'/data/large_object.txt') + oid <- 1234 + dbWithTransaction(con, { oid <- postgresImportLargeObject(con, test_file_path, oid) }) + + expect_error( + dbWithTransaction(con, { oid <- postgresImportLargeObject(con, test_file_path, oid) }) + ) + dbExecute(con, "select lo_unlink($1) as lo_data", params=list(oid)) +}) + + +test_that("import from a non-existing path throws error", { + con <- postgresDefault() + on.exit(dbDisconnect(con)) + test_file_path <- paste0(test_path(),'/data/large_object_that_does_not_exist.txt') + expect_error( + dbWithTransaction(con, { oid <- postgresImportLargeObject(con, test_file_path) }) + ) +}) + + From 56ae5bdb503260c0a00604977b7f2f7e8137e05e Mon Sep 17 00:00:00 2001 From: toby <43851547+toppyy@users.noreply.github.com> Date: Sat, 19 Oct 2024 11:59:02 +0300 Subject: [PATCH 05/10] fix example for ImportLargeObject --- R/PqConnection.R | 5 +++-- man/postgresImportLargeObject.Rd | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/R/PqConnection.R b/R/PqConnection.R index 796d8849..adb3ee53 100644 --- a/R/PqConnection.R +++ b/R/PqConnection.R @@ -149,9 +149,10 @@ postgresIsTransacting <- function(conn) { #' @return the identifier of the large object, an integer #' @examples #' con <- postgresDefault() -#' path_to_file <- 'my_image.png' +#' filepath <- 'some_file.txt' +#' file.create(filepath) #' dbWithTransaction(con, { -#' oid <- postgresImportLargeObject(con, test_file_path) +#' oid <- postgresImportLargeObject(con, filepath) #' }) postgresImportLargeObject <- function(conn, filepath = NULL, oid = 0) { diff --git a/man/postgresImportLargeObject.Rd b/man/postgresImportLargeObject.Rd index e29749f9..5da325fc 100644 --- a/man/postgresImportLargeObject.Rd +++ b/man/postgresImportLargeObject.Rd @@ -22,8 +22,9 @@ Returns an object idenfier (Oid) for the imported large object } \examples{ con <- postgresDefault() -path_to_file <- 'my_image.png' +filepath <- 'some_file.txt' +file.create(filepath) dbWithTransaction(con, { - oid <- postgresImportLargeObject(con, test_file_path) + oid <- postgresImportLargeObject(con, filepath) }) } From 757486621056227fa20666b3cfea14c191f9f626 Mon Sep 17 00:00:00 2001 From: toby <43851547+toppyy@users.noreply.github.com> Date: Sat, 19 Oct 2024 12:22:28 +0300 Subject: [PATCH 06/10] Separate NULL/NA check for oid --- R/PqConnection.R | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/R/PqConnection.R b/R/PqConnection.R index adb3ee53..75400102 100644 --- a/R/PqConnection.R +++ b/R/PqConnection.R @@ -162,7 +162,8 @@ postgresImportLargeObject <- function(conn, filepath = NULL, oid = 0) { if (is.null(filepath)) stopc("'filepath' cannot be NULL") if (oid < 0) stopc("'oid' cannot be negative") - if (is.null(oid) | is.na(oid)) stopc("'oid' cannot be NULL/NA") + if (is.null(oid)) stopc("'oid' cannot be NULL") + if (is.na(oid)) stopc("'oid' cannot be NA") if (file.access(filepath,4) == -1) stopc(paste0("Unable to read from filepath '",filepath,"'")) From 5fae348ab502461039ec46eb7b96b1e8c5827a1f Mon Sep 17 00:00:00 2001 From: toby <43851547+toppyy@users.noreply.github.com> Date: Sat, 19 Oct 2024 12:39:30 +0300 Subject: [PATCH 07/10] don't run example for ImportLargeObject as depends on files --- R/PqConnection.R | 5 +++-- man/postgresImportLargeObject.Rd | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/R/PqConnection.R b/R/PqConnection.R index 75400102..18f7a94f 100644 --- a/R/PqConnection.R +++ b/R/PqConnection.R @@ -148,12 +148,13 @@ postgresIsTransacting <- function(conn) { #' @param oid the oid to write to. Defaults to 0 which assigns an unused oid #' @return the identifier of the large object, an integer #' @examples +#' \dontrun{ #' con <- postgresDefault() -#' filepath <- 'some_file.txt' -#' file.create(filepath) +#' filepath <- 'your_image.png' #' dbWithTransaction(con, { #' oid <- postgresImportLargeObject(con, filepath) #' }) +#' } postgresImportLargeObject <- function(conn, filepath = NULL, oid = 0) { if (!postgresIsTransacting(conn)) { diff --git a/man/postgresImportLargeObject.Rd b/man/postgresImportLargeObject.Rd index 5da325fc..1b7f6851 100644 --- a/man/postgresImportLargeObject.Rd +++ b/man/postgresImportLargeObject.Rd @@ -21,10 +21,11 @@ the identifier of the large object, an integer Returns an object idenfier (Oid) for the imported large object } \examples{ +\dontrun{ con <- postgresDefault() -filepath <- 'some_file.txt' -file.create(filepath) +filepath <- 'your_image.png' dbWithTransaction(con, { oid <- postgresImportLargeObject(con, filepath) }) } +} From e71926bf5d4c1d5b3f8ea87d356b7895c7367ae2 Mon Sep 17 00:00:00 2001 From: toby <43851547+toppyy@users.noreply.github.com> Date: Sat, 19 Oct 2024 12:51:26 +0300 Subject: [PATCH 08/10] add postgresImportLargeObject under pg misc. functions --- _pkgdown.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/_pkgdown.yml b/_pkgdown.yml index 028f3a7c..af8c57e7 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -41,6 +41,7 @@ reference: - '`RPostgres-package`' - postgresHasDefault - postgresWaitForNotify + - postgresImportLargeObject development: mode: auto From 490dcd1ff3f167e132afca20f0a5e15c498b004c Mon Sep 17 00:00:00 2001 From: toby <43851547+toppyy@users.noreply.github.com> Date: Sun, 20 Oct 2024 10:15:03 +0300 Subject: [PATCH 09/10] Retrieve error msg with PQerrorMessage; use the Oid-type for oids --- R/PqConnection.R | 9 ++------- src/DbConnection.cpp | 3 ++- src/DbConnection.h | 2 +- src/connection.cpp | 2 +- src/cpp11.cpp | 4 ++-- 5 files changed, 8 insertions(+), 12 deletions(-) diff --git a/R/PqConnection.R b/R/PqConnection.R index 18f7a94f..34b52cc3 100644 --- a/R/PqConnection.R +++ b/R/PqConnection.R @@ -162,14 +162,9 @@ postgresImportLargeObject <- function(conn, filepath = NULL, oid = 0) { } if (is.null(filepath)) stopc("'filepath' cannot be NULL") - if (oid < 0) stopc("'oid' cannot be negative") if (is.null(oid)) stopc("'oid' cannot be NULL") if (is.na(oid)) stopc("'oid' cannot be NA") - if (file.access(filepath,4) == -1) stopc(paste0("Unable to read from filepath '",filepath,"'")) - - - out_oid = connection_import_lo_from_file(conn@ptr, filepath, oid) - if (out_oid == 0) stopc("Import failed. Maybe you tried to write to an existing oid?") - return(out_oid) + if (oid < 0) stopc("'oid' cannot be negative") + connection_import_lo_from_file(conn@ptr, filepath, oid) } diff --git a/src/DbConnection.cpp b/src/DbConnection.cpp index a24e1f98..6d43cb43 100644 --- a/src/DbConnection.cpp +++ b/src/DbConnection.cpp @@ -128,8 +128,9 @@ bool DbConnection::has_query() { return pCurrentResult_ != NULL; } -int DbConnection::import_lo_from_file(std::string filename, int p_oid) { +Oid DbConnection::import_lo_from_file(std::string filename, Oid p_oid) { Oid lo_oid = lo_import_with_oid(pConn_, filename.c_str(), p_oid); + if (lo_oid == InvalidOid) cpp11::stop(PQerrorMessage(pConn_)); return(lo_oid); } diff --git a/src/DbConnection.h b/src/DbConnection.h index 21989e4b..8e28834f 100644 --- a/src/DbConnection.h +++ b/src/DbConnection.h @@ -36,7 +36,7 @@ class DbConnection : boost::noncopyable { void copy_data(std::string sql, cpp11::list df); - int import_lo_from_file(std::string file_path, int p_oid); + Oid import_lo_from_file(std::string file_path, Oid p_oid); void check_connection(); diff --git a/src/connection.cpp b/src/connection.cpp index 424ddd7e..b2e38ece 100644 --- a/src/connection.cpp +++ b/src/connection.cpp @@ -92,7 +92,7 @@ void connection_set_transacting(DbConnection* con, bool transacting) { // Specific functions [[cpp11::register]] -int connection_import_lo_from_file(DbConnection* con, std::string filename, int oid) { +Oid connection_import_lo_from_file(DbConnection* con, std::string filename, Oid oid) { return con->import_lo_from_file(filename, oid); } diff --git a/src/cpp11.cpp b/src/cpp11.cpp index 58dcf195..080b6075 100644 --- a/src/cpp11.cpp +++ b/src/cpp11.cpp @@ -71,10 +71,10 @@ extern "C" SEXP _RPostgres_connection_set_transacting(SEXP con, SEXP transacting END_CPP11 } // connection.cpp -int connection_import_lo_from_file(DbConnection* con, std::string filename, int oid); +Oid connection_import_lo_from_file(DbConnection* con, std::string filename, Oid oid); extern "C" SEXP _RPostgres_connection_import_lo_from_file(SEXP con, SEXP filename, SEXP oid) { BEGIN_CPP11 - return cpp11::as_sexp(connection_import_lo_from_file(cpp11::as_cpp>(con), cpp11::as_cpp>(filename), cpp11::as_cpp>(oid))); + return cpp11::as_sexp(connection_import_lo_from_file(cpp11::as_cpp>(con), cpp11::as_cpp>(filename), cpp11::as_cpp>(oid))); END_CPP11 } // connection.cpp From f30f6ac05dedc6cf5c19de1c56e43e99db9fcb7d Mon Sep 17 00:00:00 2001 From: toby <43851547+toppyy@users.noreply.github.com> Date: Sun, 20 Oct 2024 10:21:19 +0300 Subject: [PATCH 10/10] styler applied --- R/PqConnection.R | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/R/PqConnection.R b/R/PqConnection.R index 34b52cc3..71f49086 100644 --- a/R/PqConnection.R +++ b/R/PqConnection.R @@ -149,10 +149,10 @@ postgresIsTransacting <- function(conn) { #' @return the identifier of the large object, an integer #' @examples #' \dontrun{ -#' con <- postgresDefault() +#' con <- postgresDefault() #' filepath <- 'your_image.png' #' dbWithTransaction(con, { -#' oid <- postgresImportLargeObject(con, filepath) +#' oid <- postgresImportLargeObject(con, filepath) #' }) #' } postgresImportLargeObject <- function(conn, filepath = NULL, oid = 0) { @@ -164,7 +164,7 @@ postgresImportLargeObject <- function(conn, filepath = NULL, oid = 0) { if (is.null(filepath)) stopc("'filepath' cannot be NULL") if (is.null(oid)) stopc("'oid' cannot be NULL") if (is.na(oid)) stopc("'oid' cannot be NA") - if (oid < 0) stopc("'oid' cannot be negative") + if (oid < 0) stopc("'oid' cannot be negative") connection_import_lo_from_file(conn@ptr, filepath, oid) }