This analysis compares performance between the odbc package and the RStudio Professional Drivers against the RPostgreSQL package. These tests are compared on small and large tables:

There are many ways to collect data into R from a database. These methods were examined, ranked in order from best performing to least:

Setup

knitr::opts_chunk$set(collapse = TRUE)
library(DBI)
library(RPostgreSQL)
library(microbenchmark)
library(tidyverse)
library(config)
conf <- get()

odbc connection

con1 <- dbConnect(odbc::odbc(),
  Driver = conf$driver,
  Database = conf$database,
  Port = conf$port,
  Server = conf$server, 
  UID = conf$UID, 
  PWD = conf$PWD
)

RPostgreSQL connection

con2 <- dbConnect(RPostgreSQL::PostgreSQL(),
  dbname = conf$database,
  port = conf$port,
  host = conf$server, 
  user = conf$UID, 
  password = conf$PWD
)

Query functions

Median result (odbc / RPostgreSQL):

cat("# dbSendQuery\n")
# dbSendQuery
f <- function(con){
  rs <- dbSendQuery(con, "select * from mtcars")
  dbFetch(rs)
  dbClearResult(rs)
}
microbenchmark(
  odbc = f(con1),
  RPostgreSQL = f(con2),
  times = 100
)
Unit: milliseconds
        expr       min        lq     mean    median        uq      max neval
        odbc  4.713763  4.994552  5.26611  5.132599  5.259943 15.93048   100
 RPostgreSQL 11.618141 12.353627 14.13958 12.702549 13.121257 72.67674   100
cat("# dbGetQuery\n")
# dbGetQuery
microbenchmark(
  odbc = dbGetQuery(con1, "select * from mtcars"),
  RPostgreSQL = dbGetQuery(con2, "select * from mtcars"),
  times = 100
)
Unit: milliseconds
        expr       min        lq      mean    median        uq        max neval
        odbc  4.635817  4.919612  5.135489  5.102818  5.288199   7.078986   100
 RPostgreSQL 11.991524 12.559285 15.137716 12.964886 13.385518 215.381654   100
cat("# dbReadTable\n")
# dbReadTable
microbenchmark(
  odbc = dbReadTable(con1, "mtcars"),
  RPostgreSQL = dbReadTable(con2, "mtcars"),
  times = 100
)
Unit: milliseconds
        expr       min        lq      mean    median       uq      max neval
        odbc  5.345695  5.684614  6.239272  5.879808  6.09132 20.97787   100
 RPostgreSQL 12.131515 12.582161 13.306330 12.887246 13.55050 27.46183   100
cat("# collect\n")
# collect
t1 <- tbl(con1, "mtcars")
t2 <- tbl(con2, "mtcars")
microbenchmark(
  odbc = collect(t1),
  RPostgreSQL = collect(t2),
  times = 100
)
Unit: milliseconds
        expr       min        lq      mean    median        uq      max neval
        odbc  7.337842  7.516808  8.036092  7.639473  7.950758 20.11218   100
 RPostgreSQL 14.095849 14.473211 15.475779 14.699705 15.466099 41.38101   100
cat("# dplyr\n")
# dplyr
microbenchmark(
  odbc = tbl(con1, "mtcars") %>% collect,
  RPostgreSQL = tbl(con2, "mtcars") %>% collect,
  times = 100
)
Unit: milliseconds
        expr      min       lq     mean   median       uq      max neval
        odbc 14.38527 14.94451 15.40478 15.11696 15.38241 21.05757   100
 RPostgreSQL 26.59585 27.89762 29.22869 28.61760 29.11067 80.26529   100

Select all

Median result (small table / large table):

cat("# Small table\n")
# Small table
microbenchmark(
  odbc = dbGetQuery(con1, "select * from mtcars"),
  RPostgreSQL = dbGetQuery(con2, "select * from mtcars"),
  times = 100
)
Unit: milliseconds
        expr       min        lq      mean    median        uq       max neval
        odbc  4.726137  5.057129  5.351527  5.213648  5.430405  14.34878   100
 RPostgreSQL 12.027707 12.913718 15.706497 13.275593 13.692919 214.52382   100
cat("# Large table\n")
# Large table
microbenchmark(
  odbc = dbGetQuery(con1, "select * from flights"),
  RPostgreSQL = dbGetQuery(con2, "select * from flights"),
  times = 5
)
Unit: seconds
        expr      min       lq     mean   median       uq      max neval
        odbc 2.548805 2.722749 2.697878 2.728980 2.731829 2.757028     5
 RPostgreSQL 4.755813 4.782716 4.886354 4.958044 4.966141 4.969057     5

Count records

Median result (small table / large table):

cat("# Small table\n")
# Small table
microbenchmark(
  odbc = dbGetQuery(con1, "select count(*) from mtcars"),
  RPostgreSQL = dbGetQuery(con2, "select count(*) from mtcars"),
  times = 100
)
Unit: milliseconds
        expr      min       lq     mean   median       uq      max neval
        odbc 4.286390 4.576856 4.756610 4.716919 4.837279 6.141267   100
 RPostgreSQL 2.210132 2.318461 2.501728 2.426959 2.698684 4.173576   100
cat("# Large table\n")
# Large table
microbenchmark(
  odbc = dbGetQuery(con1, "select count(*) from flights"),
  RPostgreSQL = dbGetQuery(con2, "select count(*) from flights"),
  times = 100
)
Unit: milliseconds
        expr      min       lq     mean   median       uq      max neval
        odbc 30.28474 31.02547 32.36276 31.28322 31.62073 96.40938   100
 RPostgreSQL 27.63234 28.30320 28.69686 28.67596 28.96722 30.84177   100

Aggregate

Median result (month / origin & dest):

cat("# By month\n")
# By month
microbenchmark(
  odbc = dbGetQuery(con1, "select month, count(*) from flights group by month"),
  RPostgreSQL = dbGetQuery(con2, "select month, count(*) from flights group by month"),
  times = 25
)
Unit: milliseconds
        expr      min       lq     mean   median       uq      max neval
        odbc 67.62446 68.79539 69.95684 69.17403 69.90676 85.68561    25
 RPostgreSQL 66.93089 67.32935 67.69938 67.59283 67.87060 69.81070    25
cat("# By origin & dest\n")
# By origin & dest
microbenchmark(
  odbc = dbGetQuery(con1, "select origin, dest, count(*) from flights group by origin, dest"),
  RPostgreSQL = dbGetQuery(con2, "select origin, dest, count(*) from flights group by origin, dest"),
  times = 25
)
Unit: milliseconds
        expr      min       lq     mean   median       uq     max neval
        odbc 125.1644 126.6711 127.6276 127.8869 128.4314 129.493    25
 RPostgreSQL 123.0584 125.9069 129.3218 126.5206 127.9533 170.240    25

Join and aggregate

Median result:

cat("# Join and aggregate\n")
# Join and aggregate
microbenchmark(
  odbc = dbGetQuery(con1, "select name, count(*) from flights, airports where origin = faa group by name"),
  RPostgreSQL = dbGetQuery(con2, "select name, count(*) from flights, airports where origin = faa group by name"),
  times = 25
)
Unit: milliseconds
        expr      min       lq     mean   median       uq      max neval
        odbc 153.1274 153.8547 155.2041 154.6656 156.9790 159.3070    25
 RPostgreSQL 150.9227 152.8797 154.9814 153.8712 156.3154 178.3588    25

Write table

Median result (small / large):

cat("# Small table\n")
# Small table
microbenchmark(
  odbc = dbWriteTable(con1, "tmp1", mtcars, overwrite = TRUE),
  RPostgreSQL = dbWriteTable(con2, "tmp2", mtcars, overwrite = TRUE),
  times = 25
)
Unit: milliseconds
        expr      min       lq     mean   median       uq      max neval
        odbc 86.44851 87.82760 89.44893 88.75644 90.06617 101.0527    25
 RPostgreSQL 36.15477 37.30507 49.84299 39.25221 40.89235 264.8357    25
cat("# Large table\n")
# Large table
tmpdat <- nycflights13::flights[1:1000,]
microbenchmark(
  odbc = dbWriteTable(con1, "tmp3", tmpdat, overwrite = TRUE),
  RPostgreSQL = dbWriteTable(con2, "tmp4", tmpdat, overwrite = TRUE),
  times = 5
)
Unit: milliseconds
        expr      min        lq      mean    median        uq        max neval
        odbc 865.7683 871.08201 932.02205 876.78658 879.12109 1167.35222     5
 RPostgreSQL  46.9099  47.91023  50.68187  49.70822  49.95069   58.93033     5

Summary

There was no one decisive winner for all tests. The odbc R package along with the RStudio Professional Drivers was decisively faster than RPostgreSQL when collecting records. For all other operations, the differences in performance between the two packages were comparable.

LS0tCnRpdGxlOiAnUGVyZm9ybWFuY2UgY29tcGFyaXNvbjogb2RiYyB2cyBSUG9zdGdyZVNRTCcKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKVGhpcyBhbmFseXNpcyBjb21wYXJlcyBwZXJmb3JtYW5jZSBiZXR3ZWVuIHRoZSBvZGJjIHBhY2thZ2UgYW5kIHRoZSBSU3R1ZGlvIFByb2Zlc3Npb25hbCBEcml2ZXJzIGFnYWluc3QgdGhlIFJQb3N0Z3JlU1FMIHBhY2thZ2UuIFRoZXNlIHRlc3RzIGFyZSBjb21wYXJlZCBvbiBzbWFsbCBhbmQgbGFyZ2UgdGFibGVzOgoKYGBge3IgZWNobz1GQUxTRX0KZGF0YS5mcmFtZSgKICBUZXN0ID0gYygiUmVhZCIsICJDb3VudCIsICJHcm91cCIsICJKb2luIiwgIldyaXRlIiksCiAgb2RiYyA9IGMoMjcwMCwgMzEsIDEyNy4xLCAxNTQuOCwgODc2LjgpLAogIFJQb3N0Z3JlU1FMID0gYyg1MTAwLCAyOC44LCAxMjYuMCwgMTU1LjAsIDQ5LjcpLAogIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRQopICU+JQogIG11dGF0ZShvZGJjID0gbG9nMTAob2RiYykpICU+JQogIG11dGF0ZShSUG9zdGdyZVNRTCA9IGxvZzEwKFJQb3N0Z3JlU1FMKSkgJT4lCiAgZ2F0aGVyKCJwYWNrYWdlIiwgImxvZzEwKG1zKSIsIG9kYmMsIFJQb3N0Z3JlU1FMKSAlPiUKICBnZ3Bsb3QoYWVzKHJlb3JkZXIoVGVzdCwgYGxvZzEwKG1zKWAsIG1lYW4pLCBgbG9nMTAobXMpYCwgZmlsbD1wYWNrYWdlKSkgKyAKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSAiZG9kZ2UiKSArIAogIGNvb3JkX2ZsaXAoKSArCiAgZ2d0aXRsZSgiUGVyZm9ybWFuY2UgQ29tcGFyaXNvbjogb2RiYyB2cyBSUG9zdGdyZVNRTCIsICJNZWRpYW4gdGltZSBpbiBsb2cxMCBtaWxsaXNlY29uZHMiKSArCiAgeGxhYigiIikgKwogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGU9IkJsdWVzIikgKwogIHRoZW1lX21pbmltYWwoKQpgYGAKClRoZXJlIGFyZSBtYW55IHdheXMgdG8gY29sbGVjdCBkYXRhIGludG8gUiBmcm9tIGEgZGF0YWJhc2UuIFRoZXNlIG1ldGhvZHMgd2VyZSBleGFtaW5lZCwgcmFua2VkIGluIG9yZGVyIGZyb20gYmVzdCBwZXJmb3JtaW5nIHRvIGxlYXN0OgoKYGBge3IsIGVjaG89RkFMU0V9CmRhdGEuZnJhbWUoCiAgRnVuY3Rpb24gPSBjKCJkYlNlbmRRdWVyeSIsICJkYkdldFF1ZXJ5IiwgImRiUmVhZFRhYmxlIiwgImRwbHlyIGNvbGxlY3Qgb25seSIsICJkcGx5ciIpLAogIE1TID0gYygxMi4wLCAxMi41LCAxMi45LCAxNC40LCAyNy42KQopICU+JQogIGdncGxvdChhZXMocmVvcmRlcihGdW5jdGlvbiwgLU1TKSwgTVMpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJJZGVudGl0eSIsIGZpbGwgPSAic3RlZWxibHVlIikgKwogIGNvb3JkX2ZsaXAoKSArCiAgZ2d0aXRsZSgiUGVyZm9ybWFuY2UgY29tcGFyaXNvbjogUXVlcnkgbWV0aG9kcyIsICJNZWRpYW4gdGltZSBpbiBtaWxsaXNlY29uZHMiKSArCiAgeGxhYigiIikgKwogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGU9IkJsdWVzIikgKwogIHRoZW1lX21pbmltYWwoKQpgYGAKCiMjIyBTZXR1cAoKYGBge3Igc2V0dXB9CmtuaXRyOjpvcHRzX2NodW5rJHNldChjb2xsYXBzZSA9IFRSVUUpCmxpYnJhcnkoREJJKQpsaWJyYXJ5KFJQb3N0Z3JlU1FMKQpsaWJyYXJ5KG1pY3JvYmVuY2htYXJrKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShjb25maWcpCmNvbmYgPC0gZ2V0KCkKYGBgCgojIyMgb2RiYyBjb25uZWN0aW9uCgpgYGB7cn0KY29uMSA8LSBkYkNvbm5lY3Qob2RiYzo6b2RiYygpLAogIERyaXZlciA9IGNvbmYkZHJpdmVyLAogIERhdGFiYXNlID0gY29uZiRkYXRhYmFzZSwKICBQb3J0ID0gY29uZiRwb3J0LAogIFNlcnZlciA9IGNvbmYkc2VydmVyLCAKICBVSUQgPSBjb25mJFVJRCwgCiAgUFdEID0gY29uZiRQV0QKKQpgYGAKCiMjIyBSUG9zdGdyZVNRTCBjb25uZWN0aW9uCgpgYGB7cn0KY29uMiA8LSBkYkNvbm5lY3QoUlBvc3RncmVTUUw6OlBvc3RncmVTUUwoKSwKICBkYm5hbWUgPSBjb25mJGRhdGFiYXNlLAogIHBvcnQgPSBjb25mJHBvcnQsCiAgaG9zdCA9IGNvbmYkc2VydmVyLCAKICB1c2VyID0gY29uZiRVSUQsIAogIHBhc3N3b3JkID0gY29uZiRQV0QKKQpgYGAKCiMjIyBRdWVyeSBmdW5jdGlvbnMKCk1lZGlhbiByZXN1bHQgKG9kYmMgLyBSUG9zdGdyZVNRTCk6CgoqIGRiU2VuZFF1ZXJ5ICg0LjkgbXMgLyAxMi4wIG1zKQoqIGRiR2V0UXVlcnkgKDQuOSBtcyAvIDEyLjUgbXMpCiogZGJSZWFkVGFibGUgKDUuOCBtcyAvIDEyLjkgbXMpCiogY29sbGVjdCAoNy40IG1zIC8gMTQuNCBtcykKKiBkcGx5ciAoMTQuOSBtcyAvIDI3LjYgbXMpCgpgYGB7ciwgY29sbGFwc2U9VFJVRX0KY2F0KCIjIGRiU2VuZFF1ZXJ5XG4iKQpmIDwtIGZ1bmN0aW9uKGNvbil7CiAgcnMgPC0gZGJTZW5kUXVlcnkoY29uLCAic2VsZWN0ICogZnJvbSBtdGNhcnMiKQogIGRiRmV0Y2gocnMpCiAgZGJDbGVhclJlc3VsdChycykKfQptaWNyb2JlbmNobWFyaygKICBvZGJjID0gZihjb24xKSwKICBSUG9zdGdyZVNRTCA9IGYoY29uMiksCiAgdGltZXMgPSAxMDAKKQpjYXQoIiMgZGJHZXRRdWVyeVxuIikKbWljcm9iZW5jaG1hcmsoCiAgb2RiYyA9IGRiR2V0UXVlcnkoY29uMSwgInNlbGVjdCAqIGZyb20gbXRjYXJzIiksCiAgUlBvc3RncmVTUUwgPSBkYkdldFF1ZXJ5KGNvbjIsICJzZWxlY3QgKiBmcm9tIG10Y2FycyIpLAogIHRpbWVzID0gMTAwCikKY2F0KCIjIGRiUmVhZFRhYmxlXG4iKQptaWNyb2JlbmNobWFyaygKICBvZGJjID0gZGJSZWFkVGFibGUoY29uMSwgIm10Y2FycyIpLAogIFJQb3N0Z3JlU1FMID0gZGJSZWFkVGFibGUoY29uMiwgIm10Y2FycyIpLAogIHRpbWVzID0gMTAwCikKY2F0KCIjIGNvbGxlY3RcbiIpCnQxIDwtIHRibChjb24xLCAibXRjYXJzIikKdDIgPC0gdGJsKGNvbjIsICJtdGNhcnMiKQptaWNyb2JlbmNobWFyaygKICBvZGJjID0gY29sbGVjdCh0MSksCiAgUlBvc3RncmVTUUwgPSBjb2xsZWN0KHQyKSwKICB0aW1lcyA9IDEwMAopCmNhdCgiIyBkcGx5clxuIikKbWljcm9iZW5jaG1hcmsoCiAgb2RiYyA9IHRibChjb24xLCAibXRjYXJzIikgJT4lIGNvbGxlY3QsCiAgUlBvc3RncmVTUUwgPSB0YmwoY29uMiwgIm10Y2FycyIpICU+JSBjb2xsZWN0LAogIHRpbWVzID0gMTAwCikKYGBgCgojIyMgU2VsZWN0IGFsbAoKTWVkaWFuIHJlc3VsdCAoc21hbGwgdGFibGUgLyBsYXJnZSB0YWJsZSk6CgoqIG9kYmMgKDUuMiBtcyAvIDIuNyBzKQoqIFJQb3N0Z3JlcyAoMTMuMiBtcyAvIDUuMCBzKQoKYGBge3IsIGNvbGxhcHNlPVRSVUV9CmNhdCgiIyBTbWFsbCB0YWJsZVxuIikKbWljcm9iZW5jaG1hcmsoCiAgb2RiYyA9IGRiR2V0UXVlcnkoY29uMSwgInNlbGVjdCAqIGZyb20gbXRjYXJzIiksCiAgUlBvc3RncmVTUUwgPSBkYkdldFF1ZXJ5KGNvbjIsICJzZWxlY3QgKiBmcm9tIG10Y2FycyIpLAogIHRpbWVzID0gMTAwCikKY2F0KCIjIExhcmdlIHRhYmxlXG4iKQptaWNyb2JlbmNobWFyaygKICBvZGJjID0gZGJHZXRRdWVyeShjb24xLCAic2VsZWN0ICogZnJvbSBmbGlnaHRzIiksCiAgUlBvc3RncmVTUUwgPSBkYkdldFF1ZXJ5KGNvbjIsICJzZWxlY3QgKiBmcm9tIGZsaWdodHMiKSwKICB0aW1lcyA9IDUKKQpgYGAKCiMjIyBDb3VudCByZWNvcmRzCgpNZWRpYW4gcmVzdWx0IChzbWFsbCB0YWJsZSAvIGxhcmdlIHRhYmxlKToKCiogb2RiYyAoNC42IG1zIC8gMzEuMCBtcykKKiBSUG9zdGdyZVNRTCAoMi4zICBtcyAvIDI4LjggbXMpCgpgYGB7ciwgY29sbGFwc2U9VFJVRX0KY2F0KCIjIFNtYWxsIHRhYmxlXG4iKQptaWNyb2JlbmNobWFyaygKICBvZGJjID0gZGJHZXRRdWVyeShjb24xLCAic2VsZWN0IGNvdW50KCopIGZyb20gbXRjYXJzIiksCiAgUlBvc3RncmVTUUwgPSBkYkdldFF1ZXJ5KGNvbjIsICJzZWxlY3QgY291bnQoKikgZnJvbSBtdGNhcnMiKSwKICB0aW1lcyA9IDEwMAopCmNhdCgiIyBMYXJnZSB0YWJsZVxuIikKbWljcm9iZW5jaG1hcmsoCiAgb2RiYyA9IGRiR2V0UXVlcnkoY29uMSwgInNlbGVjdCBjb3VudCgqKSBmcm9tIGZsaWdodHMiKSwKICBSUG9zdGdyZVNRTCA9IGRiR2V0UXVlcnkoY29uMiwgInNlbGVjdCBjb3VudCgqKSBmcm9tIGZsaWdodHMiKSwKICB0aW1lcyA9IDEwMAopCmBgYAoKIyMjIEFnZ3JlZ2F0ZQoKTWVkaWFuIHJlc3VsdCAobW9udGggLyBvcmlnaW4gJiBkZXN0KToKCiogb2RiYyAoNzAuMCBtcyAvIDEyNy4xIG1zKQoqIFJQb3N0Z3JlU1FMICg2OC40IG1zIC8gMTI2LjAgbXMpCgpgYGB7ciwgY29sbGFwc2U9VFJVRX0KY2F0KCIjIEJ5IG1vbnRoXG4iKQptaWNyb2JlbmNobWFyaygKICBvZGJjID0gZGJHZXRRdWVyeShjb24xLCAic2VsZWN0IG1vbnRoLCBjb3VudCgqKSBmcm9tIGZsaWdodHMgZ3JvdXAgYnkgbW9udGgiKSwKICBSUG9zdGdyZVNRTCA9IGRiR2V0UXVlcnkoY29uMiwgInNlbGVjdCBtb250aCwgY291bnQoKikgZnJvbSBmbGlnaHRzIGdyb3VwIGJ5IG1vbnRoIiksCiAgdGltZXMgPSAyNQopCmNhdCgiIyBCeSBvcmlnaW4gJiBkZXN0XG4iKQptaWNyb2JlbmNobWFyaygKICBvZGJjID0gZGJHZXRRdWVyeShjb24xLCAic2VsZWN0IG9yaWdpbiwgZGVzdCwgY291bnQoKikgZnJvbSBmbGlnaHRzIGdyb3VwIGJ5IG9yaWdpbiwgZGVzdCIpLAogIFJQb3N0Z3JlU1FMID0gZGJHZXRRdWVyeShjb24yLCAic2VsZWN0IG9yaWdpbiwgZGVzdCwgY291bnQoKikgZnJvbSBmbGlnaHRzIGdyb3VwIGJ5IG9yaWdpbiwgZGVzdCIpLAogIHRpbWVzID0gMjUKKQpgYGAKCiMjIyBKb2luIGFuZCBhZ2dyZWdhdGUKCk1lZGlhbiByZXN1bHQ6CgoqIG9kYmMgKDE1NC43IG1zKQoqIFJQb3N0Z3JlU1FMICgxNTMuOSBtcykKCmBgYHtyLCBjb2xsYXBzZT1UUlVFfQpjYXQoIiMgSm9pbiBhbmQgYWdncmVnYXRlXG4iKQptaWNyb2JlbmNobWFyaygKICBvZGJjID0gZGJHZXRRdWVyeShjb24xLCAic2VsZWN0IG5hbWUsIGNvdW50KCopIGZyb20gZmxpZ2h0cywgYWlycG9ydHMgd2hlcmUgb3JpZ2luID0gZmFhIGdyb3VwIGJ5IG5hbWUiKSwKICBSUG9zdGdyZVNRTCA9IGRiR2V0UXVlcnkoY29uMiwgInNlbGVjdCBuYW1lLCBjb3VudCgqKSBmcm9tIGZsaWdodHMsIGFpcnBvcnRzIHdoZXJlIG9yaWdpbiA9IGZhYSBncm91cCBieSBuYW1lIiksCiAgdGltZXMgPSAyNQopCmBgYAoKIyMjIFdyaXRlIHRhYmxlCgpNZWRpYW4gcmVzdWx0IChzbWFsbCAvIGxhcmdlKToKCiogb2RiYyAoODguOCBtcyAvIDg3Ni44IG1zKQoqIFJQb3N0Z3JlU1FMICgzOS4zIG1zIC8gNDkuNyBtcykKCmBgYHtyLCBjb2xsYXBzZT1UUlVFfQpjYXQoIiMgU21hbGwgdGFibGVcbiIpCm1pY3JvYmVuY2htYXJrKAogIG9kYmMgPSBkYldyaXRlVGFibGUoY29uMSwgInRtcDEiLCBtdGNhcnMsIG92ZXJ3cml0ZSA9IFRSVUUpLAogIFJQb3N0Z3JlU1FMID0gZGJXcml0ZVRhYmxlKGNvbjIsICJ0bXAyIiwgbXRjYXJzLCBvdmVyd3JpdGUgPSBUUlVFKSwKICB0aW1lcyA9IDI1CikKY2F0KCIjIExhcmdlIHRhYmxlXG4iKQp0bXBkYXQgPC0gbnljZmxpZ2h0czEzOjpmbGlnaHRzWzE6MTAwMCxdCm1pY3JvYmVuY2htYXJrKAogIG9kYmMgPSBkYldyaXRlVGFibGUoY29uMSwgInRtcDMiLCB0bXBkYXQsIG92ZXJ3cml0ZSA9IFRSVUUpLAogIFJQb3N0Z3JlU1FMID0gZGJXcml0ZVRhYmxlKGNvbjIsICJ0bXA0IiwgdG1wZGF0LCBvdmVyd3JpdGUgPSBUUlVFKSwKICB0aW1lcyA9IDUKKQpgYGAKCiMjIyBTdW1tYXJ5CgpUaGVyZSB3YXMgbm8gb25lIGRlY2lzaXZlIHdpbm5lciBmb3IgYWxsIHRlc3RzLiBUaGUgb2RiYyBSIHBhY2thZ2UgYWxvbmcgd2l0aCB0aGUgUlN0dWRpbyBQcm9mZXNzaW9uYWwgRHJpdmVycyB3YXMgZGVjaXNpdmVseSBmYXN0ZXIgdGhhbiBSUG9zdGdyZVNRTCB3aGVuIGNvbGxlY3RpbmcgcmVjb3Jkcy4gRm9yIGFsbCBvdGhlciBvcGVyYXRpb25zLCB0aGUgZGlmZmVyZW5jZXMgaW4gcGVyZm9ybWFuY2UgYmV0d2VlbiB0aGUgdHdvIHBhY2thZ2VzIHdlcmUgY29tcGFyYWJsZS4KCiogU2VsZWN0IGFsbCAob2RiYyBmYXN0ZXIpCiogQ291bnQgYWxsIHJlY29yZHMgKFJQb3N0Z3JlU1FMIGZhc3RlcikKKiBBZ2dyZWdhdGUgKFJQb3N0Z3JlU1FMIGZhc3RlcikKKiBKb2luIGFuZCBBZ2dyZWdhdGUgKG9kYmMgZmFzdGVyKQoqIFdyaXRlIHRhYmxlIChvZGJjIGZhc3RlcikK