diff --git a/NEWS.md b/NEWS.md index 9cb57abd15..d1b6b90bcd 100644 --- a/NEWS.md +++ b/NEWS.md @@ -69,6 +69,8 @@ unit = "s") 8. Compiler support for OpenMP is now detected during installation, which allows data.table to compile from source (in single threaded mode) on macOS which, frustratingly, does not include OpenMP support by default, [#2161](https://github.com/Rdatatable/data.table/issues/2161), unlike Windows and Linux. A helpful message is emitted during installation from source, and on package startup as before. Many thanks to @jimhester for the PR. This was typically a problem just after release to CRAN in the few days before macOS binaries (which do support OpenMP) are made available by CRAN. +9. `rbindlist` now supports columns of type `expression`, [#546](https://github.com/Rdatatable/data.table/issues/546). Thanks @jangorecki for the report. + ## BUG FIXES 1. A NULL timezone on POSIXct was interpreted by `as.IDate` and `as.ITime` as UTC rather than the session's default timezone (`tz=""`) , [#4085](https://github.com/Rdatatable/data.table/issues/4085). diff --git a/inst/tests/tests.Rraw b/inst/tests/tests.Rraw index 9f8a488482..7fc969b55d 100644 --- a/inst/tests/tests.Rraw +++ b/inst/tests/tests.Rraw @@ -16673,7 +16673,12 @@ test(2128.3, DT[, .SD, .SDcols=function(x) x==1], error='conditions were not met test(2128.4, DT[, .SD, .SDcols=function(x) 2L], error='conditions were not met for: [a, b, c]') test(2128.5, DT[, .SD, .SDcols=function(x) NA], error='conditions were not met for: [a, b, c]') +# expression columns in rbindlist, #546 +A = data.table(c1 = 1, c2 = 'asd', c3 = expression(as.character(Sys.time()))) +B = data.table(c1 = 3, c2 = 'qwe', c3 = expression(as.character(Sys.time()+5))) +test(2129, rbind(A,B)$c3, expression(as.character(Sys.time()), as.character(Sys.time()+5))) -################################### -# Add new tests above this line # -################################### + +######################## +# Add new tests here # +######################## diff --git a/src/assign.c b/src/assign.c index f09d904119..d8526550cf 100644 --- a/src/assign.c +++ b/src/assign.c @@ -1061,7 +1061,8 @@ const char *memrecycle(SEXP target, SEXP where, int start, int len, SEXP source, BODY(SEXP, STRING_PTR, SEXP, val, SET_STRING_ELT(target, off+i, cval)) } case VECSXP : - if (TYPEOF(source)!=VECSXP) + case EXPRSXP : // #546 + if (TYPEOF(source)!=VECSXP && TYPEOF(source)!=EXPRSXP) BODY(SEXP, &, SEXP, val, SET_VECTOR_ELT(target, off+i, cval)) else BODY(SEXP, VECTOR_PTR, SEXP, val, SET_VECTOR_ELT(target, off+i, cval)) diff --git a/src/rbindlist.c b/src/rbindlist.c index d85c9d916e..fe997b6da5 100644 --- a/src/rbindlist.c +++ b/src/rbindlist.c @@ -292,7 +292,8 @@ SEXP rbindlist(SEXP l, SEXP usenamesArg, SEXP fillArg, SEXP idcolArg) } SEXP thisCol = VECTOR_ELT(li, w); int thisType = TYPEOF(thisCol); - if (TYPEORDER(thisType)>TYPEORDER(maxType)) maxType=thisType; + // Use >= for #546 -- TYPEORDER=0 for both LGLSXP and EXPRSXP (but also NULL) + if (TYPEORDER(thisType)>=TYPEORDER(maxType) && !isNull(thisCol)) maxType=thisType; if (isFactor(thisCol)) { if (isNull(getAttrib(thisCol,R_LevelsSymbol))) error("Column %d of item %d has type 'factor' but has no levels; i.e. malformed.", w+1, i+1); factor = true; @@ -513,9 +514,9 @@ SEXP rbindlist(SEXP l, SEXP usenamesArg, SEXP fillArg, SEXP idcolArg) if (w==-1 || !length(thisCol=VECTOR_ELT(li, w))) { // !length for zeroCol warning above; #1871 writeNA(target, ansloc, thisnrow); // writeNA is integer64 aware and writes INT64_MIN } else { - if (TYPEOF(target)==VECSXP && TYPEOF(thisCol)!=VECSXP) { + if ((TYPEOF(target)==VECSXP || TYPEOF(target)==EXPRSXP) && TYPEOF(thisCol)!=TYPEOF(target)) { // do an as.list() on the atomic column; #3528 - thisCol = PROTECT(coerceVector(thisCol, VECSXP)); nprotect++; + thisCol = PROTECT(coerceVector(thisCol, TYPEOF(target))); nprotect++; } // else coerces if needed within memrecycle; with a no-alloc direct coerce from 1.12.4 (PR #3909) const char *ret = memrecycle(target, R_NilValue, ansloc, thisnrow, thisCol, idcol+j+1, foundName);