Skip to content

Commit

Permalink
Improved long strings handling and tests rewrite
Browse files Browse the repository at this point in the history
  • Loading branch information
zozlak committed Apr 9, 2017
1 parent a28a467 commit 359506b
Show file tree
Hide file tree
Showing 22 changed files with 1,308 additions and 2,135 deletions.
60 changes: 31 additions & 29 deletions DESCRIPTION
@@ -1,29 +1,31 @@
Package: RODBCext
Version: 0.2.7
Authors@R: c(person("Mateusz", "Zoltak", role = c("aut", "cre"),
email = "zozlak@zozlak.org"),
person("Brian", "Ripley", role = "aut"),
person("Michael", "Lapsley", role = "aut"),
person("Will", "Beasley", role = "ctb",
email = "wibeasley@hotmail.com"))
Title: Parameterized Queries Extension for RODBC
Description: An extension for RODBC package adding support for parameterized
queries.
SystemRequirements: An ODBC3 driver manager and drivers.
Depends:
R (>= 3.0.0),
RODBC (>= 1.3.0)
Suggests:
knitr,
rmarkdown
LazyLoad: yes
Biarch: yes
License: GPL-2 | GPL-3
Author: Mateusz Zoltak [aut, cre],
Brian Ripley [aut],
Michael Lapsley [aut],
Will Beasley [ctb]
Maintainer: Mateusz Zoltak <zozlak@zozlak.org>
NeedsCompilation: yes
VignetteBuilder: knitr
RoxygenNote: 5.0.1
Package: RODBCext
Version: 0.3.0
Authors@R: c(
person("Mateusz", "Zoltak", role = c("aut", "cre"), email = "zozlak@zozlak.org"),
person("Brian", "Ripley", role = "aut"),
person("Michael", "Lapsley", role = "aut"),
person("Will", "Beasley", role = "ctb", email = "wibeasley@hotmail.com"),
person("Juergen", "Altfeld", role = "ctb")
)
Title: Parameterized Queries Extension for RODBC
Description: An extension for RODBC package adding support for parameterized
queries.
SystemRequirements: An ODBC3 driver manager and drivers.
Depends:
R (>= 3.0.0),
RODBC (>= 1.3.0)
Suggests:
knitr,
rmarkdown,
testthat
LazyLoad: yes
Biarch: yes
License: GPL-2 | GPL-3
Author: Mateusz Zoltak [aut, cre],
Brian Ripley [aut],
Michael Lapsley [aut],
Will Beasley [ctb]
Maintainer: Mateusz Zoltak <zozlak@zozlak.org>
NeedsCompilation: yes
VignetteBuilder: knitr
RoxygenNote: 6.0.1
15 changes: 15 additions & 0 deletions NEWS
@@ -1,3 +1,18 @@

Changes in RODBCext 0.3.0 (2017-04-09)

* R dates ("Date", "POSIXlt" and "POSIXct" classes) are now handled
automatically. It is no longer needed to manually cast them to character
vectors.
* It is now possible to set query execution timeout.
* sqlPrepare() and sqlExecute() function support additional query_timeout
paramete
* odbcSetQueryTimeout() and odbcGetQueryTimeout() functions were added
* Support for reading long character columns improved.
* If the driver does not report column length, 65535 characters are read.
* If the driver reports extremaly large column length (more then 16M),
16M characters are read.

Changes in RODBCext 0.2.7 (2016-09-17)

* Added support for column types reporting 0 as its length (most notably
Expand Down
45 changes: 45 additions & 0 deletions R/odbcGetQueryTimeout.R
@@ -0,0 +1,45 @@
# Copyright (C) 2017 Juergen Altfeld, Mateusz Zoltak
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 or 3 of the License
# (at your option).
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# A copy of the GNU General Public License is available at
# http://www.r-project.org/Licenses/

#' @title Gets the current query timeout of a prepared query
#' @description A query has to be already prepared using SQLPrepare()
#'
#' Throws an error if an error occured
#' @param channel an RODBC channel containing an open connection
#' @return The current query timeout value in seconds. 0 means "no timeout"
#' @seealso \code{\link{odbcSetQueryTimeout}}, \code{\link{odbcConnect}},
#' \code{\link{odbcDriverConnect}}
#' @examples
#' \dontrun{
#' conn = odbcConnect('MyDataSource')
#'
#' sqlPrepare(conn, "SELECT * FROM myTable WHERE column = ?")
#' odbcGetQueryTimeout(conn) # shows the current query timeout of the prepared statement
#' sqlExecute(conn, 'myValue')
#' sqlFetchMore(conn)
#' }
#' @export
odbcGetQueryTimeout = function(channel)
{
stopifnot(odbcValidChannel(channel))

stat = .Call("RODBCGetQueryTimeout", attr(channel, "handle_ptr"))

if (stat == -1L) {
stop(paste0(RODBC::odbcGetErrMsg(channel), collapse = '\n'))
} else {
return(stat)
}
}
47 changes: 12 additions & 35 deletions R/odbcQueryTimeout.R → R/odbcSetQueryTimeout.R
Expand Up @@ -13,37 +13,6 @@
# A copy of the GNU General Public License is available at
# http://www.r-project.org/Licenses/

#' @tilte Gets the current query timeout of a prepared query
#' @description A query has to be already prepared using SQLPrepare()
#'
#' Throws an error if an error occured
#' @param channel an RODBC channel containing an open connection
#' @return The current query timeout value in seconds. 0 means "no timeout"
#' @seealso \code{\link{odbcSetQueryTimeout}}, \code{\link{odbcConnect}},
#' \code{\link{odbcDriverConnect}}
#' @examples
#' \dontrun{
#' conn = odbcConnect('MyDataSource')
#'
#' sqlPrepare(conn, "SELECT * FROM myTable WHERE column = ?")
#' odbcGetQueryTimeout(conn) # shows the current query timeout of the prepared statement
#' sqlExecute(conn, 'myValue')
#' sqlFetchMore(conn)
#' }
#' @export
odbcGetQueryTimeout = function(channel)
{
stopifnot(odbcValidChannel(channel))

stat = .Call("RODBCGetQueryTimeout", attr(channel, "handle_ptr"))

if (stat == -1L) {
stop(paste0(RODBC::odbcGetErrMsg(channel), collapse = '\n'))
} else {
return(stat)
}
}

#' @title Sets the query timeout of a prepared query
#' @description A query has to be already prepared using SQLPrepare()
#'
Expand All @@ -66,16 +35,24 @@ odbcGetQueryTimeout = function(channel)
#' }
#'
#' @export
odbcSetQueryTimeout <- function(channel, timeout = 30)
odbcSetQueryTimeout = function(channel, timeout = 0)
{
stopifnot(odbcValidChannel(channel))
stopifnot(
odbcValidChannel(channel),
is.vector(timeout), is.numeric(timeout), length(timeout) == 1, all(!is.na(timeout))
)

stat = .Call("RODBCSetQueryTimeout", attr(channel, "handle_ptr"), timeout)

if (stat == -1L) {
stop(paste0(RODBC::odbcGetErrMsg(channel), collapse = '\n'))
}
else {

# surprisingly many drivers do not support query timeouts silently
currTimeout = odbcGetQueryTimeout(channel)
if (currTimeout != timeout) {
stop('The ODBC driver returned no error but the timeout was not set.\nIt looks like the ODBC driver you are using does not support query timeouts.')
} else {
return(stat)
}
}
5 changes: 2 additions & 3 deletions R/sqlExecute.R
Expand Up @@ -95,8 +95,7 @@ sqlExecute = function(
is.vector(fetch), is.logical(fetch), length(fetch) == 1, all(!is.na(fetch)),
is.vector(errors), is.logical(errors), length(errors) == 1, all(!is.na(errors)),
is.vector(rows_at_time), is.numeric(rows_at_time), length(rows_at_time) == 1, all(!is.na(rows_at_time)),
is.vector(force_loop), is.logical(force_loop), length(force_loop) == 1, all(!is.na(force_loop)),
is.vector(query_timeout) & is.numeric(query_timeout) & length(query_timeout) == 1 & all(!is.na(query_timeout)) | is.na(query_timeout)
is.vector(force_loop), is.logical(force_loop), length(force_loop) == 1, all(!is.na(force_loop))
)

# workaround for queries which have to be planned before each execution
Expand All @@ -108,7 +107,7 @@ sqlExecute = function(
)
results = list()
for (i in seq_along(data[, 1])) {
results[[i]] = sqlExecute(channel, query, data[i, , drop = FALSE], fetch, errors, rows_at_time, FALSE, ...)
results[[i]] = sqlExecute(channel, query, data[i, , drop = FALSE], fetch, errors, rows_at_time, FALSE, query_timeout = query_timeout, ...)
}
return(do.call(rbind, results))
}
Expand Down
3 changes: 1 addition & 2 deletions R/sqlPrepare.R
Expand Up @@ -41,8 +41,7 @@ sqlPrepare = function(channel, query, errors = TRUE, query_timeout = NULL)
{
stopifnot(
odbcValidChannel(channel),
is.vector(query), is.character(query), length(query) == 1, all(!is.na(query)),
is.vector(query_timeout) & is.numeric(query_timeout) & length(query_timeout) == 1 & all(!is.na(query_timeout)) | is.na(query_timeout)
is.vector(query), is.character(query), length(query) == 1, all(!is.na(query))
)

enc = attr(channel, "encoding")
Expand Down
37 changes: 19 additions & 18 deletions RODBCext.Rproj
@@ -1,18 +1,19 @@
Version: 1.0

RestoreWorkspace: No
SaveWorkspace: No
AlwaysSaveHistory: No

EnableCodeIndexing: Yes
UseSpacesForTab: Yes
NumSpacesForTab: 2
Encoding: UTF-8

RnwWeave: knitr
LaTeX: pdfLaTeX

BuildType: Package
PackageInstallArgs: --no-multiarch --with-keep.source
PackageCheckArgs: --as-cran --no-multiarch
PackageRoxygenize: rd,collate,namespace
Version: 1.0

RestoreWorkspace: No
SaveWorkspace: No
AlwaysSaveHistory: No

EnableCodeIndexing: Yes
UseSpacesForTab: Yes
NumSpacesForTab: 2
Encoding: UTF-8

RnwWeave: knitr
LaTeX: pdfLaTeX

BuildType: Package
PackageUseDevtools: Yes
PackageInstallArgs: --no-multiarch --with-keep.source
PackageCheckArgs: --as-cran --no-multiarch
PackageRoxygenize: rd,collate,namespace,vignette
13 changes: 6 additions & 7 deletions man/odbcGetQueryTimeout.Rd

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

17 changes: 8 additions & 9 deletions man/odbcSetQueryTimeout.Rd

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

17 changes: 10 additions & 7 deletions src/RODBC.c
Expand Up @@ -126,7 +126,7 @@ void geterr(pRODBCHandle thisHandle)
if(retval != SQL_SUCCESS && retval != SQL_SUCCESS_WITH_INFO)
break;
sprintf(message,"%s %d %s", sqlstate, (int)NativeError, msg);
warning(message);
warning(message);
errlistAppend(thisHandle, message);
}
}
Expand Down Expand Up @@ -254,7 +254,7 @@ int cachenbind(pRODBCHandle thisHandle, int nRows)
goto error;
}

column->datalen = column->ColSize ? column->ColSize : DEFAULT_BUFF_SIZE;
column->datalen = column->ColSize ? column->ColSize + 1 : DEFAULT_BUFF_SIZE;

/* now bind the col to its data buffer */
/* MSDN say the BufferLength is ignored for fixed-size
Expand Down Expand Up @@ -287,12 +287,15 @@ int cachenbind(pRODBCHandle thisHandle, int nRows)
default:
{
SQLLEN datalen = thisHandle->ColData[i].ColSize;
if (datalen <= 0 || datalen < COLMAX) datalen = COLMAX;
/* sanity check as the reports are sometimes unreliable */
if (datalen > 65535) datalen = 65535;
if (datalen <= 0) {
datalen = DEFAULT_BUFF_SIZE - 1;
}
if (datalen > MAX_BUFF_SIZE) {
datalen = MAX_BUFF_SIZE - 1;
}
thisHandle->ColData[i].pData = Calloc(nRows * (datalen + 1), char);
thisHandle->ColData[i].datalen = datalen;
BIND(SQL_C_CHAR, pData, datalen);
thisHandle->ColData[i].datalen = datalen + 1;
BIND(SQL_C_CHAR, pData, datalen + 1);
}
}

Expand Down

0 comments on commit 359506b

Please sign in to comment.