summary refs log tree commit diff stats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/core/macros.nim2
-rw-r--r--lib/core/typeinfo.nim20
-rw-r--r--lib/impure/db_mysql.nim169
-rw-r--r--lib/impure/db_odbc.nim463
-rw-r--r--lib/impure/db_postgres.nim87
-rw-r--r--lib/impure/db_sqlite.nim111
-rw-r--r--lib/nimbase.h4
-rw-r--r--lib/packages/docutils/highlite.nim36
-rw-r--r--lib/packages/docutils/rstgen.nim4
-rw-r--r--lib/posix/posix.nim4
-rw-r--r--lib/pure/asyncftpclient.nim4
-rw-r--r--lib/pure/db_common.nim103
-rw-r--r--lib/pure/events.nim2
-rw-r--r--lib/pure/httpclient.nim10
-rw-r--r--lib/pure/lexbase.nim25
-rw-r--r--lib/pure/math.nim62
-rw-r--r--lib/pure/nativesockets.nim5
-rw-r--r--lib/pure/nimprof.nim38
-rw-r--r--lib/pure/osproc.nim4
-rw-r--r--lib/pure/parseutils.nim7
-rw-r--r--lib/pure/redis.nim1096
-rw-r--r--lib/pure/stats.nim348
-rw-r--r--lib/pure/strutils.nim10
-rw-r--r--lib/pure/times.nim184
-rw-r--r--lib/system.nim2
-rw-r--r--lib/system/dyncalls.nim7
-rw-r--r--lib/system/jssys.nim19
-rw-r--r--lib/system/nimscript.nim2
-rw-r--r--lib/system/profiler.nim26
-rw-r--r--lib/system/repr.nim6
-rw-r--r--lib/wrappers/linenoise/clinenoise.c46
-rw-r--r--lib/wrappers/linenoise/clinenoise.h20
-rw-r--r--lib/wrappers/mysql.nim1
-rw-r--r--lib/wrappers/odbcsql.nim38
-rw-r--r--lib/wrappers/sqlite3.nim2
35 files changed, 1511 insertions, 1456 deletions
diff --git a/lib/core/macros.nim b/lib/core/macros.nim
index c8cb3d199..872d4848d 100644
--- a/lib/core/macros.nim
+++ b/lib/core/macros.nim
@@ -97,7 +97,7 @@ type
     nskUnknown, nskConditional, nskDynLib, nskParam,
     nskGenericParam, nskTemp, nskModule, nskType, nskVar, nskLet,
     nskConst, nskResult,
-    nskProc, nskMethod, nskIterator, nskClosureIterator,
+    nskProc, nskMethod, nskIterator,
     nskConverter, nskMacro, nskTemplate, nskField,
     nskEnumField, nskForVar, nskLabel,
     nskStub
diff --git a/lib/core/typeinfo.nim b/lib/core/typeinfo.nim
index 1f9fb1072..db5a83755 100644
--- a/lib/core/typeinfo.nim
+++ b/lib/core/typeinfo.nim
@@ -61,7 +61,10 @@ type
                           ## wrapped value and **must not** live longer than
                           ## its wrapped value.
     value: pointer
-    rawType: PNimType
+    when defined(js):
+      rawType: PNimType
+    else:
+      rawTypePtr: pointer
 
   ppointer = ptr pointer
   pbyteArray = ptr array[0.. 0xffff, int8]
@@ -71,6 +74,14 @@ type
     when defined(gogc):
       elemSize: int
   PGenSeq = ptr TGenericSeq
+
+when not defined(js):
+  template rawType(x: Any): PNimType =
+    cast[PNimType](x.rawTypePtr)
+
+  template `rawType=`(x: var Any, p: PNimType) =
+    x.rawTypePtr = cast[pointer](p)
+
 {.deprecated: [TAny: Any, TAnyKind: AnyKind].}
 
 when defined(gogc):
@@ -108,7 +119,7 @@ proc selectBranch(aa: pointer, n: ptr TNimNode): ptr TNimNode =
   else:
     result = n.sons[n.len]
 
-proc newAny(value: pointer, rawType: PNimType): Any =
+proc newAny(value: pointer, rawType: PNimType): Any {.inline.} =
   result.value = value
   result.rawType = rawType
 
@@ -126,8 +137,7 @@ proc toAny*[T](x: var T): Any {.inline.} =
   ## constructs a ``Any`` object from `x`. This captures `x`'s address, so
   ## `x` can be modified with its ``Any`` wrapper! The client needs to ensure
   ## that the wrapper **does not** live longer than `x`!
-  result.value = addr(x)
-  result.rawType = cast[PNimType](getTypeInfo(x))
+  newAny(addr(x), cast[PNimType](getTypeInfo(x)))
 
 proc kind*(x: Any): AnyKind {.inline.} =
   ## get the type kind
@@ -345,7 +355,7 @@ proc `[]`*(x: Any, fieldName: string): Any =
     result.value = x.value +!! n.offset
     result.rawType = n.typ
   elif x.rawType.kind == tyObject and x.rawType.base != nil:
-    return `[]`(Any(value: x.value, rawType: x.rawType.base), fieldName)
+    return `[]`(newAny(x.value, x.rawType.base), fieldName)
   else:
     raise newException(ValueError, "invalid field name: " & fieldName)
 
diff --git a/lib/impure/db_mysql.nim b/lib/impure/db_mysql.nim
index 7f7511264..170fee8b8 100644
--- a/lib/impure/db_mysql.nim
+++ b/lib/impure/db_mysql.nim
@@ -1,7 +1,7 @@
 #
 #
 #            Nim's Runtime Library
-#        (c) Copyright 2012 Andreas Rumpf
+#        (c) Copyright 2015 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
@@ -43,45 +43,26 @@
 
 import strutils, mysql
 
+import db_common
+export db_common
+
 type
-  DbConn* = PMySQL    ## encapsulates a database connection
+  DbConn* = PMySQL     ## encapsulates a database connection
   Row* = seq[string]   ## a row of a dataset. NULL database values will be
-                       ## transformed always to the empty string.
-  InstantRow* = tuple[row: cstringArray, len: int]  ## a handle that can be
-                                                    ## used to get a row's
-                                                    ## column text on demand
-  EDb* = object of IOError ## exception that is raised if a database error occurs
-
-  SqlQuery* = distinct string ## an SQL query string
-
-  FDb* = object of IOEffect ## effect that denotes a database operation
-  FReadDb* = object of FDb   ## effect that denotes a read operation
-  FWriteDb* = object of FDb  ## effect that denotes a write operation
-{.deprecated: [TRow: Row, TSqlQuery: SqlQuery, TDbConn: DbConn].}
-
-proc sql*(query: string): SqlQuery {.noSideEffect, inline.} =
-  ## constructs a SqlQuery from the string `query`. This is supposed to be
-  ## used as a raw-string-literal modifier:
-  ## ``sql"update user set counter = counter + 1"``
-  ##
-  ## If assertions are turned off, it does nothing. If assertions are turned
-  ## on, later versions will check the string for valid syntax.
-  result = SqlQuery(query)
+                       ## converted to nil.
+  InstantRow* = object ## a handle that can be used to get a row's
+                       ## column text on demand
+    row: cstringArray
+    len: int
+{.deprecated: [TRow: Row, TDbConn: DbConn].}
 
-proc dbError(db: DbConn) {.noreturn.} =
-  ## raises an EDb exception.
-  var e: ref EDb
+proc dbError*(db: DbConn) {.noreturn.} =
+  ## raises a DbError exception.
+  var e: ref DbError
   new(e)
   e.msg = $mysql.error(db)
   raise e
 
-proc dbError*(msg: string) {.noreturn.} =
-  ## raises an EDb exception with message `msg`.
-  var e: ref EDb
-  new(e)
-  e.msg = msg
-  raise e
-
 when false:
   proc dbQueryOpt*(db: DbConn, query: string, args: varargs[string, `$`]) =
     var stmt = mysql_stmt_init(db)
@@ -114,7 +95,7 @@ proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
       add(result, c)
 
 proc tryExec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]): bool {.
-  tags: [FReadDB, FWriteDb].} =
+  tags: [ReadDbEffect, WriteDbEffect].} =
   ## tries to execute the query and returns true if successful, false otherwise.
   var q = dbFormat(query, args)
   return mysql.realQuery(db, q, q.len) == 0'i32
@@ -124,7 +105,7 @@ proc rawExec(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) =
   if mysql.realQuery(db, q, q.len) != 0'i32: dbError(db)
 
 proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
-  tags: [FReadDB, FWriteDb].} =
+  tags: [ReadDbEffect, WriteDbEffect].} =
   ## executes the query and raises EDB if not successful.
   var q = dbFormat(query, args)
   if mysql.realQuery(db, q, q.len) != 0'i32: dbError(db)
@@ -139,7 +120,7 @@ proc properFreeResult(sqlres: mysql.PRES, row: cstringArray) =
   mysql.freeResult(sqlres)
 
 iterator fastRows*(db: DbConn, query: SqlQuery,
-                   args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
+                   args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
   ## executes the query and iterates over the result dataset.
   ##
   ## This is very fast, but potentially dangerous.  Use this iterator only
@@ -167,31 +148,113 @@ iterator fastRows*(db: DbConn, query: SqlQuery,
 
 iterator instantRows*(db: DbConn, query: SqlQuery,
                       args: varargs[string, `$`]): InstantRow
-                      {.tags: [FReadDb].} =
-  ## same as fastRows but returns a handle that can be used to get column text
-  ## on demand using []. Returned handle is valid only within the interator body.
+                      {.tags: [ReadDbEffect].} =
+  ## Same as fastRows but returns a handle that can be used to get column text
+  ## on demand using []. Returned handle is valid only within the iterator body.
+  rawExec(db, query, args)
+  var sqlres = mysql.useResult(db)
+  if sqlres != nil:
+    let L = int(mysql.numFields(sqlres))
+    var row: cstringArray
+    while true:
+      row = mysql.fetchRow(sqlres)
+      if row == nil: break
+      yield InstantRow(row: row, len: L)
+    properFreeResult(sqlres, row)
+
+proc setTypeName(t: var DbType; f: PFIELD) =
+  shallowCopy(t.name, $f.name)
+  t.maxReprLen = Natural(f.max_length)
+  if (NOT_NULL_FLAG and f.flags) != 0: t.notNull = true
+  case f.ftype
+  of TYPE_DECIMAL:
+    t.kind = dbDecimal
+  of TYPE_TINY:
+    t.kind = dbInt
+    t.size = 1
+  of TYPE_SHORT:
+    t.kind = dbInt
+    t.size = 2
+  of TYPE_LONG:
+    t.kind = dbInt
+    t.size = 4
+  of TYPE_FLOAT:
+    t.kind = dbFloat
+    t.size = 4
+  of TYPE_DOUBLE:
+    t.kind = dbFloat
+    t.size = 8
+  of TYPE_NULL:
+    t.kind = dbNull
+  of TYPE_TIMESTAMP:
+    t.kind = dbTimestamp
+  of TYPE_LONGLONG:
+    t.kind = dbInt
+    t.size = 8
+  of TYPE_INT24:
+    t.kind = dbInt
+    t.size = 3
+  of TYPE_DATE:
+    t.kind = dbDate
+  of TYPE_TIME:
+    t.kind = dbTime
+  of TYPE_DATETIME:
+    t.kind = dbDatetime
+  of TYPE_YEAR:
+    t.kind = dbDate
+  of TYPE_NEWDATE:
+    t.kind = dbDate
+  of TYPE_VARCHAR, TYPE_VAR_STRING, TYPE_STRING:
+    t.kind = dbVarchar
+  of TYPE_BIT:
+    t.kind = dbBit
+  of TYPE_NEWDECIMAL:
+    t.kind = dbDecimal
+  of TYPE_ENUM: t.kind = dbEnum
+  of TYPE_SET: t.kind = dbSet
+  of TYPE_TINY_BLOB, TYPE_MEDIUM_BLOB, TYPE_LONG_BLOB,
+     TYPE_BLOB: t.kind = dbBlob
+  of TYPE_GEOMETRY:
+    t.kind = dbGeometry
+
+proc setColumnInfo(columns: var DbColumns; res: PRES; L: int) =
+  setLen(columns, L)
+  for i in 0..<L:
+    let fp = mysql.fetch_field_direct(res, cint(i))
+    setTypeName(columns[i].typ, fp)
+    columns[i].name = $fp.name
+    columns[i].tableName = $fp.table
+    columns[i].primaryKey = (fp.flags and PRI_KEY_FLAG) != 0
+    #columns[i].foreignKey = there is no such thing in mysql
+
+iterator instantRows*(db: DbConn; columns: var DbColumns; query: SqlQuery;
+                      args: varargs[string, `$`]): InstantRow =
+  ## Same as fastRows but returns a handle that can be used to get column text
+  ## on demand using []. Returned handle is valid only within the iterator body.
   rawExec(db, query, args)
   var sqlres = mysql.useResult(db)
   if sqlres != nil:
     let L = int(mysql.numFields(sqlres))
+    setColumnInfo(columns, sqlres, L)
     var row: cstringArray
     while true:
       row = mysql.fetchRow(sqlres)
       if row == nil: break
-      yield (row: row, len: L)
+      yield InstantRow(row: row, len: L)
     properFreeResult(sqlres, row)
 
+
 proc `[]`*(row: InstantRow, col: int): string {.inline.} =
-  ## returns text for given column of the row
+  ## Returns text for given column of the row.
   $row.row[col]
 
 proc len*(row: InstantRow): int {.inline.} =
-  ## returns number of columns in the row
+  ## Returns number of columns in the row.
   row.len
 
 proc getRow*(db: DbConn, query: SqlQuery,
-             args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
-  ## retrieves a single row. If the query doesn't return any rows, this proc
+             args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
+  ## Retrieves a single row. If the query doesn't return any rows, this proc
   ## will return a Row with empty strings for each column.
   rawExec(db, query, args)
   var sqlres = mysql.useResult(db)
@@ -209,7 +272,7 @@ proc getRow*(db: DbConn, query: SqlQuery,
     properFreeResult(sqlres, row)
 
 proc getAllRows*(db: DbConn, query: SqlQuery,
-                 args: varargs[string, `$`]): seq[Row] {.tags: [FReadDB].} =
+                 args: varargs[string, `$`]): seq[Row] {.tags: [ReadDbEffect].} =
   ## executes the query and returns the whole result dataset.
   result = @[]
   rawExec(db, query, args)
@@ -232,19 +295,19 @@ proc getAllRows*(db: DbConn, query: SqlQuery,
     mysql.freeResult(sqlres)
 
 iterator rows*(db: DbConn, query: SqlQuery,
-               args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
+               args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
   ## same as `fastRows`, but slower and safe.
   for r in items(getAllRows(db, query, args)): yield r
 
 proc getValue*(db: DbConn, query: SqlQuery,
-               args: varargs[string, `$`]): string {.tags: [FReadDB].} =
+               args: varargs[string, `$`]): string {.tags: [ReadDbEffect].} =
   ## executes the query and returns the first column of the first row of the
   ## result dataset. Returns "" if the dataset contains no rows or the database
   ## value is NULL.
   result = getRow(db, query, args)[0]
 
 proc tryInsertId*(db: DbConn, query: SqlQuery,
-                  args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
+                  args: varargs[string, `$`]): int64 {.tags: [WriteDbEffect].} =
   ## executes the query (typically "INSERT") and returns the
   ## generated ID for the row or -1 in case of an error.
   var q = dbFormat(query, args)
@@ -254,7 +317,7 @@ proc tryInsertId*(db: DbConn, query: SqlQuery,
     result = mysql.insertId(db)
 
 proc insertId*(db: DbConn, query: SqlQuery,
-               args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
+               args: varargs[string, `$`]): int64 {.tags: [WriteDbEffect].} =
   ## executes the query (typically "INSERT") and returns the
   ## generated ID for the row.
   result = tryInsertID(db, query, args)
@@ -262,18 +325,18 @@ proc insertId*(db: DbConn, query: SqlQuery,
 
 proc execAffectedRows*(db: DbConn, query: SqlQuery,
                        args: varargs[string, `$`]): int64 {.
-                       tags: [FReadDB, FWriteDb].} =
+                       tags: [ReadDbEffect, WriteDbEffect].} =
   ## runs the query (typically "UPDATE") and returns the
   ## number of affected rows
   rawExec(db, query, args)
   result = mysql.affectedRows(db)
 
-proc close*(db: DbConn) {.tags: [FDb].} =
+proc close*(db: DbConn) {.tags: [DbEffect].} =
   ## closes the database connection.
   if db != nil: mysql.close(db)
 
 proc open*(connection, user, password, database: string): DbConn {.
-  tags: [FDb].} =
+  tags: [DbEffect].} =
   ## opens a database connection. Raises `EDb` if the connection could not
   ## be established.
   result = mysql.init(nil)
@@ -291,7 +354,7 @@ proc open*(connection, user, password, database: string): DbConn {.
     dbError(errmsg)
 
 proc setEncoding*(connection: DbConn, encoding: string): bool {.
-  tags: [FDb].} =
+  tags: [DbEffect].} =
   ## sets the encoding of a database connection, returns true for
   ## success, false for failure.
   result = mysql.set_character_set(connection, encoding) == 0
diff --git a/lib/impure/db_odbc.nim b/lib/impure/db_odbc.nim
new file mode 100644
index 000000000..6af69d842
--- /dev/null
+++ b/lib/impure/db_odbc.nim
@@ -0,0 +1,463 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2015 Nim Contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## A higher level `ODBC` database wrapper.
+##
+## This is the same interface that is implemented for other databases.
+##
+## This has NOT yet been (extensively) tested agains ODBC drivers for
+## Teradata, Oracle, Sybase, MSSqlvSvr, et. al.  databases
+##
+## Currently all queries are ANSI calls, not Unicode.
+##
+## Example:
+##
+## .. code-block:: Nim
+##
+##  import db_odbc, math
+##
+##  let theDb = open("localhost", "nim", "nim", "test")
+##
+##  theDb.exec(sql"Drop table if exists myTestTbl")
+##  theDb.exec(sql("create table myTestTbl (" &
+##      " Id    INT(11)     NOT NULL AUTO_INCREMENT PRIMARY KEY, " &
+##      " Name  VARCHAR(50) NOT NULL, " &
+##      " i     INT(11), " &
+##      " f     DECIMAL(18,10))"))
+##
+##  theDb.exec(sql"START TRANSACTION")
+##  for i in 1..1000:
+##    theDb.exec(sql"INSERT INTO myTestTbl (name,i,f) VALUES (?,?,?)",
+##          "Item#" & $i, i, sqrt(i.float))
+##  theDb.exec(sql"COMMIT")
+##
+##  for x in theDb.fastRows(sql"select * from myTestTbl"):
+##    echo x
+##
+##  let id = theDb.tryInsertId(sql"INSERT INTO myTestTbl (name,i,f) VALUES (?,?,?)",
+##          "Item#1001", 1001, sqrt(1001.0))
+##  echo "Inserted item: ", theDb.getValue(sql"SELECT name FROM myTestTbl WHERE id=?", id)
+##
+##  theDb.close()
+
+
+import strutils, odbcsql
+
+import db_common
+export db_common
+
+type
+  OdbcConnTyp = tuple[hDb: SqlHDBC, env: SqlHEnv, stmt: SqlHStmt]
+  DbConn* = OdbcConnTyp    ## encapsulates a database connection
+  Row* = seq[string]   ## a row of a dataset. NULL database values will be
+                       ## converted to nil.
+  InstantRow* = tuple[row: seq[string], len: int]  ## a handle that can be
+                                                    ## used to get a row's
+                                                    ## column text on demand
+
+{.deprecated: [TRow: Row, TSqlQuery: SqlQuery, TDbConn: DbConn].}
+
+var
+  buf: array[0..4096, char]
+
+proc properFreeResult(hType: int, sqlres: var SqlHandle) {.
+          tags: [WriteDbEffect], raises: [].} =
+  try:
+    discard SQLFreeHandle(hType.TSqlSmallInt, sqlres)
+    sqlres = nil
+  except: discard
+
+proc getErrInfo(db: var DbConn): tuple[res: int, ss, ne, msg: string] {.
+          tags: [ReadDbEffect], raises: [].} =
+  ## Returns ODBC error information
+  var
+    sqlState: array[0..512, char]
+    nativeErr: array[0..512, char]
+    errMsg: array[0..512, char]
+    retSz: TSqlSmallInt = 0
+    res: TSqlSmallInt = 0
+  try:
+    sqlState[0] = '\0'
+    nativeErr[0] = '\0'
+    errMsg[0] = '\0'
+    res = SQLErr(db.env, db.hDb, db.stmt,
+              cast[PSQLCHAR](sqlState.addr),
+              cast[PSQLCHAR](nativeErr.addr),
+              cast[PSQLCHAR](errMsg.addr),
+              511.TSqlSmallInt, retSz.addr.PSQLSMALLINT)
+  except:
+    discard
+  return (res.int, $sqlState, $nativeErr, $errMsg)
+
+proc dbError*(db: var DbConn) {.
+          tags: [ReadDbEffect, WriteDbEffect], raises: [DbError] .} =
+  ## Raises an `[DbError]` exception with ODBC error information
+  var
+    e: ref DbError
+    ss, ne, msg: string = ""
+    isAnError = false
+    res: int = 0
+    prevSs = ""
+  while true:
+    prevSs = ss
+    (res, ss, ne, msg) = db.getErrInfo()
+    if prevSs == ss:
+      break
+    # sqlState of 00000 is not an error
+    elif ss == "00000":
+      break
+    elif ss == "01000":
+      echo "\nWarning: ", ss, " ", msg
+      continue
+    else:
+      isAnError = true
+      echo "\nError: ", ss, " ", msg
+  if isAnError:
+    new(e)
+    e.msg = "ODBC Error"
+    if db.stmt != nil:
+      properFreeResult(SQL_HANDLE_STMT, db.stmt)
+    properFreeResult(SQL_HANDLE_DBC, db.hDb)
+    properFreeResult(SQL_HANDLE_ENV, db.env)
+    raise e
+
+proc SqlCheck(db: var DbConn, resVal: TSqlSmallInt) {.raises: [DbError]} =
+  ## Wrapper that checks if ``resVal`` is not SQL_SUCCESS and if so, raises [EDb]
+  if resVal != SQL_SUCCESS: dbError(db)
+
+proc SqlGetDBMS(db: var DbConn): string {.
+        tags: [ReadDbEffect, WriteDbEffect], raises: [] .} =
+  ## Returns the ODBC SQL_DBMS_NAME string
+  const
+    SQL_DBMS_NAME = 17.SqlUSmallInt
+  var
+    sz: TSqlSmallInt = 0
+  buf[0] = '\0'
+  try:
+    db.SqlCheck(SQLGetInfo(db.hDb, SQL_DBMS_NAME, cast[SqlPointer](buf.addr),
+                        4095.TSqlSmallInt, sz.addr))
+  except: discard
+  return $buf.cstring
+
+proc dbQuote*(s: string): string {.noSideEffect.} =
+  ## DB quotes the string.
+  result = "'"
+  for c in items(s):
+    if c == '\'': add(result, "''")
+    else: add(result, c)
+  add(result, '\'')
+
+proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string {.
+                  noSideEffect.} =
+  ## Replace any ``?`` placeholders with `args`,
+  ## and quotes the arguments
+  result = ""
+  var a = 0
+  for c in items(string(formatstr)):
+    if c == '?':
+      if args[a] == nil:
+        add(result, "NULL")
+      else:
+        add(result, dbQuote(args[a]))
+      inc(a)
+    else:
+      add(result, c)
+
+proc prepareFetch(db: var DbConn, query: SqlQuery,
+                args: varargs[string, `$`]) {.
+                tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
+  # Prepare a statement, execute it and fetch the data to the driver
+  # ready for retrieval of the data
+  # Used internally by iterators and retrieval procs
+  # requires calling
+  #      properFreeResult(SQL_HANDLE_STMT, db.stmt)
+  # when finished
+  db.SqlCheck(SQLAllocHandle(SQL_HANDLE_STMT, db.hDb, db.stmt))
+  var q = dbFormat(query, args)
+  db.SqlCheck(SQLPrepare(db.stmt, q.PSQLCHAR, q.len.TSqlSmallInt))
+  db.SqlCheck(SQLExecute(db.stmt))
+  db.SqlCheck(SQLFetch(db.stmt))
+
+proc prepareFetchDirect(db: var DbConn, query: SqlQuery,
+                args: varargs[string, `$`]) {.
+                tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
+  # Prepare a statement, execute it and fetch the data to the driver
+  # ready for retrieval of the data
+  # Used internally by iterators and retrieval procs
+  # requires calling
+  #      properFreeResult(SQL_HANDLE_STMT, db.stmt)
+  # when finished
+  db.SqlCheck(SQLAllocHandle(SQL_HANDLE_STMT, db.hDb, db.stmt))
+  var q = dbFormat(query, args)
+  db.SqlCheck(SQLExecDirect(db.stmt, q.PSQLCHAR, q.len.TSqlSmallInt))
+  db.SqlCheck(SQLFetch(db.stmt))
+
+proc tryExec*(db: var DbConn, query: SqlQuery, args: varargs[string, `$`]): bool {.
+  tags: [ReadDbEffect, WriteDbEffect], raises: [].} =
+  ## Tries to execute the query and returns true if successful, false otherwise.
+  var
+    res:TSqlSmallInt = -1
+  try:
+    db.prepareFetchDirect(query, args)
+    var
+      rCnt = -1
+    res = SQLRowCount(db.stmt, rCnt)
+    if res != SQL_SUCCESS: dbError(db)
+    properFreeResult(SQL_HANDLE_STMT, db.stmt)
+  except: discard
+  return res == SQL_SUCCESS
+
+proc rawExec(db: var DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
+            tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
+  db.prepareFetchDirect(query, args)
+
+proc exec*(db: var DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
+            tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
+  ## Executes the query and raises EDB if not successful.
+  db.prepareFetchDirect(query, args)
+  properFreeResult(SQL_HANDLE_STMT, db.stmt)
+
+proc newRow(L: int): Row {.noSideEFfect.} =
+  newSeq(result, L)
+  for i in 0..L-1: result[i] = ""
+
+iterator fastRows*(db: var DbConn, query: SqlQuery,
+                   args: varargs[string, `$`]): Row {.
+                tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
+  ## Executes the query and iterates over the result dataset.
+  ##
+  ## This is very fast, but potentially dangerous.  Use this iterator only
+  ## if you require **ALL** the rows.
+  ##
+  ## Breaking the fastRows() iterator during a loop may cause a driver error
+  ## for subsequenct queries
+  ##
+  ## Rows are retrieved from the server at each iteration.
+  var
+    rowRes: Row
+    sz: TSqlSmallInt = 0
+    cCnt: TSqlSmallInt = 0.TSqlSmallInt
+    rCnt = -1
+
+  db.prepareFetch(query, args)
+  db.SqlCheck(SQLNumResultCols(db.stmt, cCnt))
+  db.SqlCheck(SQLRowCount(db.stmt, rCnt))
+  rowRes = newRow(cCnt)
+  for rNr in 1..rCnt:
+    for colId in 1..cCnt:
+      buf[0] = '\0'
+      db.SqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR,
+                               cast[cstring](buf.addr), 4095.TSqlSmallInt, sz.addr))
+      rowRes[colId-1] = $buf.cstring
+    db.SqlCheck(SQLFetchScroll(db.stmt, SQL_FETCH_NEXT, 1))
+    yield rowRes
+  properFreeResult(SQL_HANDLE_STMT, db.stmt)
+
+iterator instantRows*(db: var DbConn, query: SqlQuery,
+                      args: varargs[string, `$`]): InstantRow
+                {.tags: [ReadDbEffect, WriteDbEffect].} =
+  ## Same as fastRows but returns a handle that can be used to get column text
+  ## on demand using []. Returned handle is valid only within the interator body.
+  var
+    rowRes: Row
+    sz: TSqlSmallInt = 0
+    cCnt: TSqlSmallInt = 0.TSqlSmallInt
+    rCnt = -1
+  db.prepareFetch(query, args)
+  db.SqlCheck(SQLNumResultCols(db.stmt, cCnt))
+  db.SqlCheck(SQLRowCount(db.stmt, rCnt))
+  rowRes = newRow(cCnt)
+  for rNr in 1..rCnt:
+    for colId in 1..cCnt:
+      buf[0] = '\0'
+      db.SqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR,
+                               cast[cstring](buf.addr), 4095.TSqlSmallInt, sz.addr))
+      rowRes[colId-1] = $buf.cstring
+    db.SqlCheck(SQLFetchScroll(db.stmt, SQL_FETCH_NEXT, 1))
+    yield (row: rowRes, len: cCnt.int)
+  properFreeResult(SQL_HANDLE_STMT, db.stmt)
+
+proc `[]`*(row: InstantRow, col: int): string {.inline.} =
+  ## Returns text for given column of the row
+  row.row[col]
+
+proc len*(row: InstantRow): int {.inline.} =
+  ## Returns number of columns in the row
+  row.len
+
+proc getRow*(db: var DbConn, query: SqlQuery,
+             args: varargs[string, `$`]): Row {.
+          tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
+  ## Retrieves a single row. If the query doesn't return any rows, this proc
+  ## will return a Row with empty strings for each column.
+  var
+    sz: TSqlSmallInt = 0.TSqlSmallInt
+    cCnt: TSqlSmallInt = 0.TSqlSmallInt
+    rCnt = -1
+  result = @[]
+  db.prepareFetch(query, args)
+  db.SqlCheck(SQLNumResultCols(db.stmt, cCnt))
+
+  db.SqlCheck(SQLRowCount(db.stmt, rCnt))
+  for colId in 1..cCnt:
+    db.SqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR,
+                             cast[cstring](buf.addr), 4095.TSqlSmallInt, sz.addr))
+    result.add($buf.cstring)
+  db.SqlCheck(SQLFetchScroll(db.stmt, SQL_FETCH_NEXT, 1))
+  properFreeResult(SQL_HANDLE_STMT, db.stmt)
+
+proc getAllRows*(db: var DbConn, query: SqlQuery,
+                 args: varargs[string, `$`]): seq[Row] {.
+           tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
+  ## Executes the query and returns the whole result dataset.
+  var
+    rowRes: Row
+    sz: TSqlSmallInt = 0
+    cCnt: TSqlSmallInt = 0.TSqlSmallInt
+    rCnt = -1
+  db.prepareFetch(query, args)
+  db.SqlCheck(SQLNumResultCols(db.stmt, cCnt))
+  db.SqlCheck(SQLRowCount(db.stmt, rCnt))
+  result = @[]
+  for rNr in 1..rCnt:
+    rowRes = @[]
+    buf[0] = '\0'
+    for colId in 1..cCnt:
+      db.SqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR,
+                               cast[SqlPointer](buf.addr), 4095.TSqlSmallInt, sz.addr))
+      rowRes.add($buf.cstring)
+    db.SqlCheck(SQLFetchScroll(db.stmt, SQL_FETCH_NEXT, 1))
+    result.add(rowRes)
+  properFreeResult(SQL_HANDLE_STMT, db.stmt)
+
+iterator rows*(db: var DbConn, query: SqlQuery,
+               args: varargs[string, `$`]): Row {.
+         tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
+  ## Same as `fastRows`, but slower and safe.
+  ##
+  ## This retrieves ALL rows into memory before
+  ## iterating through the rows.
+  ## Large dataset queries will impact on memory usage.
+  for r in items(getAllRows(db, query, args)): yield r
+
+proc getValue*(db: var DbConn, query: SqlQuery,
+               args: varargs[string, `$`]): string {.
+           tags: [ReadDbEffect, WriteDbEffect], raises: [].} =
+  ## Executes the query and returns the first column of the first row of the
+  ## result dataset. Returns "" if the dataset contains no rows or the database
+  ## value is NULL.
+  result = ""
+  try:
+    result = getRow(db, query, args)[0]
+  except: discard
+
+proc tryInsertId*(db: var DbConn, query: SqlQuery,
+                  args: varargs[string, `$`]): int64 {.
+            tags: [ReadDbEffect, WriteDbEffect], raises: [].} =
+  ## Executes the query (typically "INSERT") and returns the
+  ## generated ID for the row or -1 in case of an error.
+  if not tryExec(db, query, args):
+    result = -1'i64
+  else:
+    echo "DBMS: ",SqlGetDBMS(db).toLower()
+    result = -1'i64
+    try:
+      case SqlGetDBMS(db).toLower():
+      of "postgresql":
+        result = getValue(db, sql"SELECT LASTVAL();", []).parseInt
+      of "mysql":
+        result = getValue(db, sql"SELECT LAST_INSERT_ID();", []).parseInt
+      of "sqlite":
+        result = getValue(db, sql"SELECT LAST_INSERT_ROWID();", []).parseInt
+      of "microsoft sql server":
+        result = getValue(db, sql"SELECT SCOPE_IDENTITY();", []).parseInt
+      of "oracle":
+        result = getValue(db, sql"SELECT id.currval FROM DUAL;", []).parseInt
+      else: result = -1'i64
+    except: discard
+
+proc insertId*(db: var DbConn, query: SqlQuery,
+               args: varargs[string, `$`]): int64 {.
+         tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
+  ## Executes the query (typically "INSERT") and returns the
+  ## generated ID for the row.
+  result = tryInsertID(db, query, args)
+  if result < 0: dbError(db)
+
+proc execAffectedRows*(db: var DbConn, query: SqlQuery,
+                       args: varargs[string, `$`]): int64 {.
+             tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
+  ## Runs the query (typically "UPDATE") and returns the
+  ## number of affected rows
+  result = -1
+  var res = SQLAllocHandle(SQL_HANDLE_STMT, db.hDb, db.stmt.SqlHandle)
+  if res != SQL_SUCCESS: dbError(db)
+  var q = dbFormat(query, args)
+  res = SQLPrepare(db.stmt, q.PSQLCHAR, q.len.TSqlSmallInt)
+  if res != SQL_SUCCESS: dbError(db)
+  rawExec(db, query, args)
+  var rCnt = -1
+  result = SQLRowCount(db.hDb, rCnt)
+  if res != SQL_SUCCESS: dbError(db)
+  properFreeResult(SQL_HANDLE_STMT, db.stmt)
+  result = rCnt
+
+proc close*(db: var DbConn) {.
+      tags: [WriteDbEffect], raises: [].} =
+  ## Closes the database connection.
+  if db.hDb != nil:
+    try:
+      var res = SQLDisconnect(db.hDb)
+      if db.stmt != nil:
+        res = SQLFreeHandle(SQL_HANDLE_STMT, db.stmt)
+      res = SQLFreeHandle(SQL_HANDLE_DBC, db.hDb)
+      res = SQLFreeHandle(SQL_HANDLE_ENV, db.env)
+      db = (hDb: nil, env: nil, stmt: nil)
+    except:
+      discard
+
+proc open*(connection, user, password, database: string): DbConn {.
+  tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
+  ## Opens a database connection.
+  ##
+  ## Raises `EDb` if the connection could not be established.
+  ##
+  ## Currently the database parameter is ignored,
+  ## but included to match ``open()`` in the other db_xxxxx library modules.
+  var
+    val: TSqlInteger = SQL_OV_ODBC3
+    resLen = 0
+  result = (hDb: nil, env: nil, stmt: nil)
+  # allocate environment handle
+  var res = SQLAllocHandle(SQL_HANDLE_ENV, result.env, result.env)
+  if res != SQL_SUCCESS: dbError("Error: unable to initialise ODBC environment.")
+  res = SQLSetEnvAttr(result.env,
+                      SQL_ATTR_ODBC_VERSION.TSqlInteger,
+                      val, resLen.TSqlInteger)
+  if res != SQL_SUCCESS: dbError("Error: unable to set ODBC driver version.")
+  # allocate hDb handle
+  res = SQLAllocHandle(SQL_HANDLE_DBC, result.env, result.hDb)
+  if res != SQL_SUCCESS: dbError("Error: unable to allocate connection handle.")
+
+  # Connect: connection = dsn str,
+  res = SQLConnect(result.hDb,
+                  connection.PSQLCHAR , connection.len.TSqlSmallInt,
+                  user.PSQLCHAR, user.len.TSqlSmallInt,
+                  password.PSQLCHAR, password.len.TSqlSmallInt)
+  if res != SQL_SUCCESS:
+    result.dbError()
+
+proc setEncoding*(connection: DbConn, encoding: string): bool {.
+  tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
+  ## Currently not implemented for ODBC.
+  ##
+  ## Sets the encoding of a database connection, returns true for
+  ## success, false for failure.
+  #result = set_character_set(connection, encoding) == 0
+  dbError("setEncoding() is currently not implemented by the db_odbc module")
diff --git a/lib/impure/db_postgres.nim b/lib/impure/db_postgres.nim
index 5603d9686..9bdbae4c2 100644
--- a/lib/impure/db_postgres.nim
+++ b/lib/impure/db_postgres.nim
@@ -62,47 +62,28 @@
 ##             "Dominik")
 import strutils, postgres
 
+import db_common
+export db_common
+
 type
   DbConn* = PPGconn   ## encapsulates a database connection
   Row* = seq[string]  ## a row of a dataset. NULL database values will be
-                       ## transformed always to the empty string.
+                      ## converted to nil.
   InstantRow* = tuple[res: PPGresult, line: int32]  ## a handle that can be
                                                     ## used to get a row's
                                                     ## column text on demand
-  EDb* = object of IOError ## exception that is raised if a database error occurs
-
-  SqlQuery* = distinct string ## an SQL query string
   SqlPrepared* = distinct string ## a identifier for the prepared queries
 
-  FDb* = object of IOEffect ## effect that denotes a database operation
-  FReadDb* = object of FDb   ## effect that denotes a read operation
-  FWriteDb* = object of FDb  ## effect that denotes a write operation
-{.deprecated: [TRow: Row, TSqlQuery: SqlQuery, TDbConn: DbConn,
+{.deprecated: [TRow: Row, TDbConn: DbConn,
               TSqlPrepared: SqlPrepared].}
 
-proc sql*(query: string): SqlQuery {.noSideEffect, inline.} =
-  ## constructs a SqlQuery from the string `query`. This is supposed to be
-  ## used as a raw-string-literal modifier:
-  ## ``sql"update user set counter = counter + 1"``
-  ##
-  ## If assertions are turned off, it does nothing. If assertions are turned
-  ## on, later versions will check the string for valid syntax.
-  result = SqlQuery(query)
-
 proc dbError*(db: DbConn) {.noreturn.} =
-  ## raises an EDb exception.
-  var e: ref EDb
+  ## raises a DbError exception.
+  var e: ref DbError
   new(e)
   e.msg = $pqErrorMessage(db)
   raise e
 
-proc dbError*(msg: string) {.noreturn.} =
-  ## raises an EDb exception with message `msg`.
-  var e: ref EDb
-  new(e)
-  e.msg = msg
-  raise e
-
 proc dbQuote*(s: string): string =
   ## DB quotes the string.
   result = "'"
@@ -127,7 +108,7 @@ proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
       add(result, c)
 
 proc tryExec*(db: DbConn, query: SqlQuery,
-              args: varargs[string, `$`]): bool {.tags: [FReadDB, FWriteDb].} =
+              args: varargs[string, `$`]): bool {.tags: [ReadDbEffect, WriteDbEffect].} =
   ## tries to execute the query and returns true if successful, false otherwise.
   var res = pqexecParams(db, dbFormat(query, args), 0, nil, nil,
                         nil, nil, 0)
@@ -135,7 +116,8 @@ proc tryExec*(db: DbConn, query: SqlQuery,
   pqclear(res)
 
 proc tryExec*(db: DbConn, stmtName: SqlPrepared,
-              args: varargs[string, `$`]): bool {.tags: [FReadDB, FWriteDb].} =
+              args: varargs[string, `$`]): bool {.tags: [
+              ReadDbEffect, WriteDbEffect].} =
   ## tries to execute the query and returns true if successful, false otherwise.
   var arr = allocCStringArray(args)
   var res = pqexecPrepared(db, stmtName.string, int32(args.len), arr,
@@ -145,7 +127,7 @@ proc tryExec*(db: DbConn, stmtName: SqlPrepared,
   pqclear(res)
 
 proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
-  tags: [FReadDB, FWriteDb].} =
+  tags: [ReadDbEffect, WriteDbEffect].} =
   ## executes the query and raises EDB if not successful.
   var res = pqexecParams(db, dbFormat(query, args), 0, nil, nil,
                         nil, nil, 0)
@@ -153,7 +135,7 @@ proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
   pqclear(res)
 
 proc exec*(db: DbConn, stmtName: SqlPrepared,
-          args: varargs[string]) {.tags: [FReadDB, FWriteDb].} =
+          args: varargs[string]) {.tags: [ReadDbEffect, WriteDbEffect].} =
   var arr = allocCStringArray(args)
   var res = pqexecPrepared(db, stmtName.string, int32(args.len), arr,
                            nil, nil, 0)
@@ -196,7 +178,7 @@ proc setRow(res: PPGresult, r: var Row, line, cols: int32) =
       add(r[col], x)
 
 iterator fastRows*(db: DbConn, query: SqlQuery,
-                   args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
+                   args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
   ## executes the query and iterates over the result dataset. This is very
   ## fast, but potenially dangerous: If the for-loop-body executes another
   ## query, the results can be undefined. For Postgres it is safe though.
@@ -209,7 +191,7 @@ iterator fastRows*(db: DbConn, query: SqlQuery,
   pqclear(res)
 
 iterator fastRows*(db: DbConn, stmtName: SqlPrepared,
-                   args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
+                   args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
   ## executes the prepared query and iterates over the result dataset.
   var res = setupQuery(db, stmtName, args)
   var L = pqNfields(res)
@@ -221,9 +203,9 @@ iterator fastRows*(db: DbConn, stmtName: SqlPrepared,
 
 iterator instantRows*(db: DbConn, query: SqlQuery,
                       args: varargs[string, `$`]): InstantRow
-                      {.tags: [FReadDb].} =
+                      {.tags: [ReadDbEffect].} =
   ## same as fastRows but returns a handle that can be used to get column text
-  ## on demand using []. Returned handle is valid only within interator body.
+  ## on demand using []. Returned handle is valid only within iterator body.
   var res = setupQuery(db, query, args)
   for i in 0..pqNtuples(res)-1:
     yield (res: res, line: i)
@@ -231,9 +213,9 @@ iterator instantRows*(db: DbConn, query: SqlQuery,
 
 iterator instantRows*(db: DbConn, stmtName: SqlPrepared,
                       args: varargs[string, `$`]): InstantRow
-                      {.tags: [FReadDb].} =
+                      {.tags: [ReadDbEffect].} =
   ## same as fastRows but returns a handle that can be used to get column text
-  ## on demand using []. Returned handle is valid only within interator body.
+  ## on demand using []. Returned handle is valid only within iterator body.
   var res = setupQuery(db, stmtName, args)
   for i in 0..pqNtuples(res)-1:
     yield (res: res, line: i)
@@ -248,7 +230,7 @@ proc len*(row: InstantRow): int32 {.inline.} =
   pqNfields(row.res)
 
 proc getRow*(db: DbConn, query: SqlQuery,
-             args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
+             args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
   ## retrieves a single row. If the query doesn't return any rows, this proc
   ## will return a Row with empty strings for each column.
   var res = setupQuery(db, query, args)
@@ -258,7 +240,7 @@ proc getRow*(db: DbConn, query: SqlQuery,
   pqclear(res)
 
 proc getRow*(db: DbConn, stmtName: SqlPrepared,
-             args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
+             args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
   var res = setupQuery(db, stmtName, args)
   var L = pqNfields(res)
   result = newRow(L)
@@ -266,31 +248,34 @@ proc getRow*(db: DbConn, stmtName: SqlPrepared,
   pqClear(res)
 
 proc getAllRows*(db: DbConn, query: SqlQuery,
-                 args: varargs[string, `$`]): seq[Row] {.tags: [FReadDB].} =
+                 args: varargs[string, `$`]): seq[Row] {.
+                 tags: [ReadDbEffect].} =
   ## executes the query and returns the whole result dataset.
   result = @[]
   for r in fastRows(db, query, args):
     result.add(r)
 
 proc getAllRows*(db: DbConn, stmtName: SqlPrepared,
-                 args: varargs[string, `$`]): seq[Row] {.tags: [FReadDB].} =
+                 args: varargs[string, `$`]): seq[Row] {.tags:
+                 [ReadDbEffect].} =
   ## executes the prepared query and returns the whole result dataset.
   result = @[]
   for r in fastRows(db, stmtName, args):
     result.add(r)
 
 iterator rows*(db: DbConn, query: SqlQuery,
-               args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
+               args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
   ## same as `fastRows`, but slower and safe.
   for r in items(getAllRows(db, query, args)): yield r
 
 iterator rows*(db: DbConn, stmtName: SqlPrepared,
-               args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
+               args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
   ## same as `fastRows`, but slower and safe.
   for r in items(getAllRows(db, stmtName, args)): yield r
 
 proc getValue*(db: DbConn, query: SqlQuery,
-               args: varargs[string, `$`]): string {.tags: [FReadDB].} =
+               args: varargs[string, `$`]): string {.
+               tags: [ReadDbEffect].} =
   ## executes the query and returns the first column of the first row of the
   ## result dataset. Returns "" if the dataset contains no rows or the database
   ## value is NULL.
@@ -298,7 +283,8 @@ proc getValue*(db: DbConn, query: SqlQuery,
   result = if isNil(x): "" else: $x
 
 proc tryInsertID*(db: DbConn, query: SqlQuery,
-                  args: varargs[string, `$`]): int64  {.tags: [FWriteDb].}=
+                  args: varargs[string, `$`]): int64 {.
+                  tags: [WriteDbEffect].}=
   ## executes the query (typically "INSERT") and returns the
   ## generated ID for the row or -1 in case of an error. For Postgre this adds
   ## ``RETURNING id`` to the query, so it only works if your primary key is
@@ -311,7 +297,8 @@ proc tryInsertID*(db: DbConn, query: SqlQuery,
     result = -1
 
 proc insertID*(db: DbConn, query: SqlQuery,
-               args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
+               args: varargs[string, `$`]): int64 {.
+               tags: [WriteDbEffect].} =
   ## executes the query (typically "INSERT") and returns the
   ## generated ID for the row. For Postgre this adds
   ## ``RETURNING id`` to the query, so it only works if your primary key is
@@ -321,7 +308,7 @@ proc insertID*(db: DbConn, query: SqlQuery,
 
 proc execAffectedRows*(db: DbConn, query: SqlQuery,
                        args: varargs[string, `$`]): int64 {.tags: [
-                       FReadDB, FWriteDb].} =
+                       ReadDbEffect, WriteDbEffect].} =
   ## executes the query (typically "UPDATE") and returns the
   ## number of affected rows.
   var q = dbFormat(query, args)
@@ -332,7 +319,7 @@ proc execAffectedRows*(db: DbConn, query: SqlQuery,
 
 proc execAffectedRows*(db: DbConn, stmtName: SqlPrepared,
                        args: varargs[string, `$`]): int64 {.tags: [
-                       FReadDB, FWriteDb].} =
+                       ReadDbEffect, WriteDbEffect].} =
   ## executes the query (typically "UPDATE") and returns the
   ## number of affected rows.
   var arr = allocCStringArray(args)
@@ -343,12 +330,12 @@ proc execAffectedRows*(db: DbConn, stmtName: SqlPrepared,
   result = parseBiggestInt($pqcmdTuples(res))
   pqclear(res)
 
-proc close*(db: DbConn) {.tags: [FDb].} =
+proc close*(db: DbConn) {.tags: [DbEffect].} =
   ## closes the database connection.
   if db != nil: pqfinish(db)
 
 proc open*(connection, user, password, database: string): DbConn {.
-  tags: [FDb].} =
+  tags: [DbEffect].} =
   ## opens a database connection. Raises `EDb` if the connection could not
   ## be established.
   ##
@@ -370,7 +357,7 @@ proc open*(connection, user, password, database: string): DbConn {.
   if pqStatus(result) != CONNECTION_OK: dbError(result) # result = nil
 
 proc setEncoding*(connection: DbConn, encoding: string): bool {.
-  tags: [FDb].} =
+  tags: [DbEffect].} =
   ## sets the encoding of a database connection, returns true for
   ## success, false for failure.
   return pqsetClientEncoding(connection, encoding) == 0
diff --git a/lib/impure/db_sqlite.nim b/lib/impure/db_sqlite.nim
index 8366fdadc..c0d221a0d 100644
--- a/lib/impure/db_sqlite.nim
+++ b/lib/impure/db_sqlite.nim
@@ -1,7 +1,7 @@
 #
 #
 #            Nim's Runtime Library
-#        (c) Copyright 2012 Andreas Rumpf
+#        (c) Copyright 2015 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
@@ -40,47 +40,30 @@
 ##
 ##  theDb.close()
 
+{.deadCodeElim:on.}
+
 import strutils, sqlite3
 
+import db_common
+export db_common
+
 type
   DbConn* = PSqlite3  ## encapsulates a database connection
   Row* = seq[string]  ## a row of a dataset. NULL database values will be
-                       ## transformed always to the empty string.
+                       ## converted to nil.
   InstantRow* = Pstmt  ## a handle that can be used to get a row's column
                        ## text on demand
-  EDb* = object of IOError ## exception that is raised if a database error occurs
-
-  SqlQuery* = distinct string ## an SQL query string
+{.deprecated: [TRow: Row, TDbConn: DbConn].}
 
-  FDb* = object of IOEffect ## effect that denotes a database operation
-  FReadDb* = object of FDb   ## effect that denotes a read operation
-  FWriteDb* = object of FDb  ## effect that denotes a write operation
-{.deprecated: [TRow: Row, TSqlQuery: SqlQuery, TDbConn: DbConn].}
-
-proc sql*(query: string): SqlQuery {.noSideEffect, inline.} =
-  ## constructs a SqlQuery from the string `query`. This is supposed to be
-  ## used as a raw-string-literal modifier:
-  ## ``sql"update user set counter = counter + 1"``
-  ##
-  ## If assertions are turned off, it does nothing. If assertions are turned
-  ## on, later versions will check the string for valid syntax.
-  result = SqlQuery(query)
-
-proc dbError(db: DbConn) {.noreturn.} =
-  ## raises an EDb exception.
-  var e: ref EDb
+proc dbError*(db: DbConn) {.noreturn.} =
+  ## raises a DbError exception.
+  var e: ref DbError
   new(e)
   e.msg = $sqlite3.errmsg(db)
   raise e
 
-proc dbError*(msg: string) {.noreturn.} =
-  ## raises an EDb exception with message `msg`.
-  var e: ref EDb
-  new(e)
-  e.msg = msg
-  raise e
-
-proc dbQuote(s: string): string =
+proc dbQuote*(s: string): string =
+  ## DB quotes the string.
   if s.isNil: return "NULL"
   result = "'"
   for c in items(s):
@@ -99,7 +82,8 @@ proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
       add(result, c)
 
 proc tryExec*(db: DbConn, query: SqlQuery,
-              args: varargs[string, `$`]): bool {.tags: [FReadDb, FWriteDb].} =
+              args: varargs[string, `$`]): bool {.
+              tags: [ReadDbEffect, WriteDbEffect].} =
   ## tries to execute the query and returns true if successful, false otherwise.
   var q = dbFormat(query, args)
   var stmt: sqlite3.Pstmt
@@ -108,8 +92,8 @@ proc tryExec*(db: DbConn, query: SqlQuery,
       result = finalize(stmt) == SQLITE_OK
 
 proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`])  {.
-  tags: [FReadDb, FWriteDb].} =
-  ## executes the query and raises EDB if not successful.
+  tags: [ReadDbEffect, WriteDbEffect].} =
+  ## executes the query and raises DbError if not successful.
   if not tryExec(db, query, args): dbError(db)
 
 proc newRow(L: int): Row =
@@ -129,14 +113,14 @@ proc setRow(stmt: Pstmt, r: var Row, cols: cint) =
     if not isNil(x): add(r[col], x)
 
 iterator fastRows*(db: DbConn, query: SqlQuery,
-                   args: varargs[string, `$`]): Row  {.tags: [FReadDb].} =
+                   args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
   ## Executes the query and iterates over the result dataset.
   ##
   ## This is very fast, but potentially dangerous.  Use this iterator only
   ## if you require **ALL** the rows.
   ##
   ## Breaking the fastRows() iterator during a loop will cause the next
-  ## database query to raise an [EDb] exception ``unable to close due to ...``.
+  ## database query to raise a DbError exception ``unable to close due to ...``.
   var stmt = setupQuery(db, query, args)
   var L = (column_count(stmt))
   var result = newRow(L)
@@ -147,10 +131,43 @@ iterator fastRows*(db: DbConn, query: SqlQuery,
 
 iterator instantRows*(db: DbConn, query: SqlQuery,
                       args: varargs[string, `$`]): InstantRow
-                      {.tags: [FReadDb].} =
+                      {.tags: [ReadDbEffect].} =
+  ## same as fastRows but returns a handle that can be used to get column text
+  ## on demand using []. Returned handle is valid only within the iterator body.
+  var stmt = setupQuery(db, query, args)
+  while step(stmt) == SQLITE_ROW:
+    yield stmt
+  if finalize(stmt) != SQLITE_OK: dbError(db)
+
+proc toTypeKind(t: var DbType; x: int32) =
+  case x
+  of SQLITE_INTEGER:
+    t.kind = dbInt
+    t.size = 8
+  of SQLITE_FLOAT:
+    t.kind = dbFloat
+    t.size = 8
+  of SQLITE_BLOB: t.kind = dbBlob
+  of SQLITE_NULL: t.kind = dbNull
+  of SQLITE_TEXT: t.kind = dbVarchar
+  else: t.kind = dbUnknown
+
+proc setColumns(columns: var DbColumns; x: PStmt) =
+  let L = column_count(x)
+  setLen(columns, L)
+  for i in 0'i32 ..< L:
+    columns[i].name = $column_name(x, i)
+    columns[i].typ.name = $column_decltype(x, i)
+    toTypeKind(columns[i].typ, column_type(x, i))
+    columns[i].tableName = $column_table_name(x, i)
+
+iterator instantRows*(db: DbConn; columns: var DbColumns; query: SqlQuery,
+                      args: varargs[string, `$`]): InstantRow
+                      {.tags: [ReadDbEffect].} =
   ## same as fastRows but returns a handle that can be used to get column text
-  ## on demand using []. Returned handle is valid only within the interator body.
+  ## on demand using []. Returned handle is valid only within the iterator body.
   var stmt = setupQuery(db, query, args)
+  setColumns(columns, stmt)
   while step(stmt) == SQLITE_ROW:
     yield stmt
   if finalize(stmt) != SQLITE_OK: dbError(db)
@@ -164,7 +181,7 @@ proc len*(row: InstantRow): int32 {.inline.} =
   column_count(row)
 
 proc getRow*(db: DbConn, query: SqlQuery,
-             args: varargs[string, `$`]): Row {.tags: [FReadDb].} =
+             args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
   ## retrieves a single row. If the query doesn't return any rows, this proc
   ## will return a Row with empty strings for each column.
   var stmt = setupQuery(db, query, args)
@@ -175,19 +192,19 @@ proc getRow*(db: DbConn, query: SqlQuery,
   if finalize(stmt) != SQLITE_OK: dbError(db)
 
 proc getAllRows*(db: DbConn, query: SqlQuery,
-                 args: varargs[string, `$`]): seq[Row] {.tags: [FReadDb].} =
+                 args: varargs[string, `$`]): seq[Row] {.tags: [ReadDbEffect].} =
   ## executes the query and returns the whole result dataset.
   result = @[]
   for r in fastRows(db, query, args):
     result.add(r)
 
 iterator rows*(db: DbConn, query: SqlQuery,
-               args: varargs[string, `$`]): Row {.tags: [FReadDb].} =
+               args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
   ## same as `FastRows`, but slower and safe.
   for r in fastRows(db, query, args): yield r
 
 proc getValue*(db: DbConn, query: SqlQuery,
-               args: varargs[string, `$`]): string {.tags: [FReadDb].} =
+               args: varargs[string, `$`]): string {.tags: [ReadDbEffect].} =
   ## executes the query and returns the first column of the first row of the
   ## result dataset. Returns "" if the dataset contains no rows or the database
   ## value is NULL.
@@ -205,7 +222,7 @@ proc getValue*(db: DbConn, query: SqlQuery,
 
 proc tryInsertID*(db: DbConn, query: SqlQuery,
                   args: varargs[string, `$`]): int64
-                  {.tags: [FWriteDb], raises: [].} =
+                  {.tags: [WriteDbEffect], raises: [].} =
   ## executes the query (typically "INSERT") and returns the
   ## generated ID for the row or -1 in case of an error.
   var q = dbFormat(query, args)
@@ -218,7 +235,7 @@ proc tryInsertID*(db: DbConn, query: SqlQuery,
       result = -1
 
 proc insertID*(db: DbConn, query: SqlQuery,
-               args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
+               args: varargs[string, `$`]): int64 {.tags: [WriteDbEffect].} =
   ## executes the query (typically "INSERT") and returns the
   ## generated ID for the row. For Postgre this adds
   ## ``RETURNING id`` to the query, so it only works if your primary key is
@@ -228,18 +245,18 @@ proc insertID*(db: DbConn, query: SqlQuery,
 
 proc execAffectedRows*(db: DbConn, query: SqlQuery,
                        args: varargs[string, `$`]): int64 {.
-                       tags: [FReadDb, FWriteDb].} =
+                       tags: [ReadDbEffect, WriteDbEffect].} =
   ## executes the query (typically "UPDATE") and returns the
   ## number of affected rows.
   exec(db, query, args)
   result = changes(db)
 
-proc close*(db: DbConn) {.tags: [FDb].} =
+proc close*(db: DbConn) {.tags: [DbEffect].} =
   ## closes the database connection.
   if sqlite3.close(db) != SQLITE_OK: dbError(db)
 
 proc open*(connection, user, password, database: string): DbConn {.
-  tags: [FDb].} =
+  tags: [DbEffect].} =
   ## opens a database connection. Raises `EDb` if the connection could not
   ## be established. Only the ``connection`` parameter is used for ``sqlite``.
   var db: DbConn
@@ -249,7 +266,7 @@ proc open*(connection, user, password, database: string): DbConn {.
     dbError(db)
 
 proc setEncoding*(connection: DbConn, encoding: string): bool {.
-  tags: [FDb].} =
+  tags: [DbEffect].} =
   ## sets the encoding of a database connection, returns true for
   ## success, false for failure.
   ##
diff --git a/lib/nimbase.h b/lib/nimbase.h
index 0946b9a1f..bba5ac023 100644
--- a/lib/nimbase.h
+++ b/lib/nimbase.h
@@ -418,10 +418,6 @@ typedef int assert_numbits[sizeof(NI) == sizeof(void*) && NIM_INTBITS == sizeof(
 #  define NIM_EXTERNC
 #endif
 
-/* we have to tinker with TNimType as it's both part of system.nim and
-   typeinfo.nim but system.nim doesn't export it cleanly... */
-typedef struct TNimType TNimType;
-
 /* ---------------- platform specific includes ----------------------- */
 
 /* VxWorks related includes */
diff --git a/lib/packages/docutils/highlite.nim b/lib/packages/docutils/highlite.nim
index 640b8cd5a..1bc0af1b6 100644
--- a/lib/packages/docutils/highlite.nim
+++ b/lib/packages/docutils/highlite.nim
@@ -173,7 +173,41 @@ proc nimNextToken(g: var GeneralTokenizer) =
       while g.buf[pos] in {' ', '\x09'..'\x0D'}: inc(pos)
     of '#':
       g.kind = gtComment
-      while not (g.buf[pos] in {'\0', '\x0A', '\x0D'}): inc(pos)
+      inc(pos)
+      var isDoc = false
+      if g.buf[pos] == '#':
+        inc(pos)
+        isDoc = true
+      if g.buf[pos] == '[':
+        g.kind = gtLongComment
+        var nesting = 0
+        while true:
+          case g.buf[pos]
+          of '\0': break
+          of '#':
+            if isDoc:
+              if g.buf[pos+1] == '#' and g.buf[pos+2] == '[':
+                inc nesting
+            elif g.buf[pos+1] == '[':
+              inc nesting
+            inc pos
+          of ']':
+            if isDoc:
+              if g.buf[pos+1] == '#' and g.buf[pos+2] == '#':
+                if nesting == 0:
+                  inc(pos, 3)
+                  break
+                dec nesting
+            elif g.buf[pos+1] == '#':
+              if nesting == 0:
+                inc(pos, 2)
+                break
+              dec nesting
+            inc pos
+          else:
+            inc pos
+      else:
+        while g.buf[pos] notin {'\0', '\x0A', '\x0D'}: inc(pos)
     of 'a'..'z', 'A'..'Z', '_', '\x80'..'\xFF':
       var id = ""
       while g.buf[pos] in SymChars + {'_'}:
diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim
index 4a0304a7c..22d944597 100644
--- a/lib/packages/docutils/rstgen.nim
+++ b/lib/packages/docutils/rstgen.nim
@@ -534,7 +534,7 @@ proc generateDocumentationJumps(docs: IndexedDocs): string =
   for title in titles:
     chunks.add("<a href=\"" & title.link & "\">" & title.keyword & "</a>")
 
-  result.add(chunks.join(", ") & ".<br>")
+  result.add(chunks.join(", ") & ".<br/>")
 
 proc generateModuleJumps(modules: seq[string]): string =
   ## Returns a plain list of hyperlinks to the list of modules.
@@ -544,7 +544,7 @@ proc generateModuleJumps(modules: seq[string]): string =
   for name in modules:
     chunks.add("<a href=\"" & name & ".html\">" & name & "</a>")
 
-  result.add(chunks.join(", ") & ".<br>")
+  result.add(chunks.join(", ") & ".<br/>")
 
 proc readIndexDir(dir: string):
     tuple[modules: seq[string], symbols: seq[IndexEntry], docs: IndexedDocs] =
diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim
index 5f1dfcfcd..40b48f992 100644
--- a/lib/posix/posix.nim
+++ b/lib/posix/posix.nim
@@ -2165,6 +2165,10 @@ proc pwrite*(a1: cint, a2: pointer, a3: int, a4: Off): int {.
   importc, header: "<unistd.h>".}
 proc read*(a1: cint, a2: pointer, a3: int): int {.importc, header: "<unistd.h>".}
 proc readlink*(a1, a2: cstring, a3: int): int {.importc, header: "<unistd.h>".}
+proc ioctl*(f: FileHandle, device: uint): int {.importc: "ioctl",
+      header: "<sys/ioctl.h>", varargs, tags: [WriteIOEffect].}
+  ## A system call for device-specific input/output operations and other
+  ## operations which cannot be expressed by regular system calls
 
 proc rmdir*(a1: cstring): cint {.importc, header: "<unistd.h>".}
 proc setegid*(a1: Gid): cint {.importc, header: "<unistd.h>".}
diff --git a/lib/pure/asyncftpclient.nim b/lib/pure/asyncftpclient.nim
index b806f4235..fd899e080 100644
--- a/lib/pure/asyncftpclient.nim
+++ b/lib/pure/asyncftpclient.nim
@@ -288,7 +288,7 @@ proc defaultOnProgressChanged*(total, progress: BiggestInt,
   result.complete()
 
 proc retrFile*(ftp: AsyncFtpClient, file, dest: string,
-               onProgressChanged = defaultOnProgressChanged) {.async.} =
+               onProgressChanged: ProgressChangedProc = defaultOnProgressChanged) {.async.} =
   ## Downloads ``file`` and saves it to ``dest``.
   ## The ``EvRetr`` event is passed to the specified ``handleEvent`` function
   ## when the download is finished. The event's ``filename`` field will be equal
@@ -339,7 +339,7 @@ proc doUpload(ftp: AsyncFtpClient, file: File,
     await countdownFut or sendFut
 
 proc store*(ftp: AsyncFtpClient, file, dest: string,
-            onProgressChanged = defaultOnProgressChanged) {.async.} =
+            onProgressChanged: ProgressChangedProc = defaultOnProgressChanged) {.async.} =
   ## Uploads ``file`` to ``dest`` on the remote FTP server. Usage of this
   ## function asynchronously is recommended to view the progress of
   ## the download.
diff --git a/lib/pure/db_common.nim b/lib/pure/db_common.nim
new file mode 100644
index 000000000..957389605
--- /dev/null
+++ b/lib/pure/db_common.nim
@@ -0,0 +1,103 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2015 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Common datatypes and definitions for all ``db_*.nim`` (
+## `db_mysql <db_mysql.html>`_, `db_postgres <db_postgres.html>`_,
+## and `db_sqlite <db_sqlite.html>`_) modules.
+
+type
+  DbError* = object of IOError ## exception that is raised if a database error occurs
+
+  SqlQuery* = distinct string ## an SQL query string
+
+
+  DbEffect* = object of IOEffect ## effect that denotes a database operation
+  ReadDbEffect* = object of DbEffect   ## effect that denotes a read operation
+  WriteDbEffect* = object of DbEffect  ## effect that denotes a write operation
+
+  DbTypeKind* = enum  ## a superset of datatypes that might be supported.
+    dbUnknown,        ## unknown datatype
+    dbSerial,         ## datatype used for primary auto-increment keys
+    dbNull,           ## datatype used for the NULL value
+    dbBit,            ## bit datatype
+    dbBool,           ## boolean datatype
+    dbBlob,           ## blob datatype
+    dbFixedChar,      ## string of fixed length
+    dbVarchar,        ## string datatype
+    dbJson,           ## JSON datatype
+    dbXml,            ## XML datatype
+    dbInt,            ## some integer type
+    dbUInt,           ## some unsigned integer type
+    dbDecimal,        ## decimal numbers (fixed-point number)
+    dbFloat,          ## some floating point type
+    dbDate,           ## a year-month-day description
+    dbTime,           ## HH:MM:SS information
+    dbDatetime,       ## year-month-day and HH:MM:SS information,
+                      ## plus optional time or timezone information
+    dbTimestamp,      ## Timestamp values are stored as the number of seconds
+                      ## since the epoch ('1970-01-01 00:00:00' UTC).
+    dbTimeInterval,   ## an interval [a,b] of times
+    dbEnum,           ## some enum
+    dbSet,            ## set of enum values
+    dbArray,          ## an array of values
+    dbComposite,      ## composite type (record, struct, etc)
+    dbUrl,            ## a URL
+    dbUuid,           ## a UUID
+    dbInet,           ## an IP address
+    dbMacAddress,     ## a MAC address
+    dbGeometry,       ## some geometric type
+    dbPoint,          ## Point on a plane   (x,y)
+    dbLine,           ## Infinite line ((x1,y1),(x2,y2))
+    dbLseg,           ## Finite line segment   ((x1,y1),(x2,y2))
+    dbBox,            ## Rectangular box   ((x1,y1),(x2,y2))
+    dbPath,           ## Closed or open path (similar to polygon) ((x1,y1),...)
+    dbPolygon,        ## Polygon (similar to closed path)   ((x1,y1),...)
+    dbCircle,         ## Circle   <(x,y),r> (center point and radius)
+    dbUser1,          ## user definable datatype 1 (for unknown extensions)
+    dbUser2,          ## user definable datatype 2 (for unknown extensions)
+    dbUser3,          ## user definable datatype 3 (for unknown extensions)
+    dbUser4,          ## user definable datatype 4 (for unknown extensions)
+    dbUser5           ## user definable datatype 5 (for unknown extensions)
+
+  DbType* = object    ## describes a database type
+    kind*: DbTypeKind ## the kind of the described type
+    notNull*: bool    ## does the type contain NULL?
+    name*: string     ## the name of the type
+    size*: Natural    ## the size of the datatype; 0 if of variable size
+    maxReprLen*: Natural ## maximal length required for the representation
+    precision*, scale*: Natural ## precision and scale of the number
+    min*, max*: BiggestInt ## the minimum and maximum of allowed values
+    validValues*: seq[string] ## valid values of an enum or a set
+
+  DbColumn* = object   ## information about a database column
+    name*: string      ## name of the column
+    tableName*: string ## name of the table the column belongs to (optional)
+    typ*: DbType       ## type of the column
+    primaryKey*: bool  ## is this a primary key?
+    foreignKey*: bool  ## is this a foreign key?
+  DbColumns* = seq[DbColumn]
+
+{.deprecated: [EDb: DbError, TSqlQuery: SqlQuery, FDb: DbEffect,
+              FReadDb: ReadDbEffect, FWriteDb: WriteDbEffect].}
+
+template sql*(query: string): SqlQuery =
+  ## constructs a SqlQuery from the string `query`. This is supposed to be
+  ## used as a raw-string-literal modifier:
+  ## ``sql"update user set counter = counter + 1"``
+  ##
+  ## If assertions are turned off, it does nothing. If assertions are turned
+  ## on, later versions will check the string for valid syntax.
+  SqlQuery(query)
+
+proc dbError*(msg: string) {.noreturn, noinline.} =
+  ## raises an DbError exception with message `msg`.
+  var e: ref DbError
+  new(e)
+  e.msg = msg
+  raise e
diff --git a/lib/pure/events.nim b/lib/pure/events.nim
index 62800c5c8..23a8a2c58 100644
--- a/lib/pure/events.nim
+++ b/lib/pure/events.nim
@@ -57,7 +57,7 @@ proc addHandler*(handler: var EventHandler, fn: proc(e: EventArgs) {.closure.})
 
 proc removeHandler*(handler: var EventHandler, fn: proc(e: EventArgs) {.closure.}) =
   ## Removes the callback from the specified event handler.
-  for i in countup(0, len(handler.handlers) -1):
+  for i in countup(0, len(handler.handlers)-1):
     if fn == handler.handlers[i]:
       handler.handlers.del(i)
       break
diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim
index 8e182e274..1b91132db 100644
--- a/lib/pure/httpclient.nim
+++ b/lib/pure/httpclient.nim
@@ -110,7 +110,7 @@ type
   EInvalidProtocol: ProtocolError, EHttpRequestErr: HttpRequestError
 ].}
 
-const defUserAgent* = "Nim httpclient/0.1"
+const defUserAgent* = "Nim httpclient/" & NimVersion
 
 proc httpError(msg: string) =
   var e: ref ProtocolError
@@ -389,6 +389,7 @@ proc request*(url: string, httpMethod: string, extraHeaders = "",
   ## | An optional timeout can be specified in milliseconds, if reading from the
   ## server takes longer than specified an ETimeout exception will be raised.
   var r = if proxy == nil: parseUri(url) else: proxy.url
+  var hostUrl = if proxy == nil: r else: parseUri(url)
   var headers = substr(httpMethod, len("http"))
   # TODO: Use generateHeaders further down once it supports proxies.
   if proxy == nil:
@@ -402,10 +403,10 @@ proc request*(url: string, httpMethod: string, extraHeaders = "",
 
   headers.add(" HTTP/1.1\c\L")
 
-  if r.port == "":
-    add(headers, "Host: " & r.hostname & "\c\L")
+  if hostUrl.port == "":
+    add(headers, "Host: " & hostUrl.hostname & "\c\L")
   else:
-    add(headers, "Host: " & r.hostname & ":" & r.port & "\c\L")
+    add(headers, "Host: " & hostUrl.hostname & ":" & hostUrl.port & "\c\L")
 
   if userAgent != "":
     add(headers, "User-Agent: " & userAgent & "\c\L")
@@ -414,7 +415,6 @@ proc request*(url: string, httpMethod: string, extraHeaders = "",
     add(headers, "Proxy-Authorization: basic " & auth & "\c\L")
   add(headers, extraHeaders)
   add(headers, "\c\L")
-
   var s = newSocket()
   if s == nil: raiseOSError(osLastError())
   var port = net.Port(80)
diff --git a/lib/pure/lexbase.nim b/lib/pure/lexbase.nim
index bfecf6a58..cf2e8bb89 100644
--- a/lib/pure/lexbase.nim
+++ b/lib/pure/lexbase.nim
@@ -28,7 +28,10 @@ type
   BaseLexer* = object of RootObj ## the base lexer. Inherit your lexer from
                                  ## this object.
     bufpos*: int              ## the current position within the buffer
-    buf*: cstring             ## the buffer itself
+    when defined(js):         ## the buffer itself
+      buf*: string
+    else:
+      buf*: cstring
     bufLen*: int              ## length of buffer in characters
     input: Stream            ## the input stream
     lineNumber*: int          ## the current line number
@@ -43,7 +46,8 @@ const
 
 proc close*(L: var BaseLexer) =
   ## closes the base lexer. This closes `L`'s associated stream too.
-  dealloc(L.buf)
+  when not defined(js):
+    dealloc(L.buf)
   close(L.input)
 
 proc fillBuffer(L: var BaseLexer) =
@@ -58,8 +62,11 @@ proc fillBuffer(L: var BaseLexer) =
   toCopy = L.bufLen - L.sentinel - 1
   assert(toCopy >= 0)
   if toCopy > 0:
-    moveMem(L.buf, addr(L.buf[L.sentinel + 1]), toCopy * chrSize)
-    # "moveMem" handles overlapping regions
+    when defined(js):
+      for i in 0 ..< toCopy: L.buf[i] = L.buf[L.sentinel + 1 + i]
+    else:
+      # "moveMem" handles overlapping regions
+      moveMem(L.buf, addr L.buf[L.sentinel + 1], toCopy * chrSize)
   charsRead = readData(L.input, addr(L.buf[toCopy]),
                        (L.sentinel + 1) * chrSize) div chrSize
   s = toCopy + charsRead
@@ -81,7 +88,10 @@ proc fillBuffer(L: var BaseLexer) =
         # double the buffer's size and try again:
         oldBufLen = L.bufLen
         L.bufLen = L.bufLen * 2
-        L.buf = cast[cstring](realloc(L.buf, L.bufLen * chrSize))
+        when defined(js):
+          L.buf.setLen(L.bufLen)
+        else:
+          L.buf = cast[cstring](realloc(L.buf, L.bufLen * chrSize))
         assert(L.bufLen - oldBufLen == oldBufLen)
         charsRead = readData(L.input, addr(L.buf[oldBufLen]),
                              oldBufLen * chrSize) div chrSize
@@ -139,7 +149,10 @@ proc open*(L: var BaseLexer, input: Stream, bufLen: int = 8192;
   L.bufpos = 0
   L.bufLen = bufLen
   L.refillChars = refillChars
-  L.buf = cast[cstring](alloc(bufLen * chrSize))
+  when defined(js):
+    L.buf = newString(bufLen)
+  else:
+    L.buf = cast[cstring](alloc(bufLen * chrSize))
   L.sentinel = bufLen - 1
   L.lineStart = 0
   L.lineNumber = 1            # lines start at 1
diff --git a/lib/pure/math.nim b/lib/pure/math.nim
index 391a880ae..b0104336e 100644
--- a/lib/pure/math.nim
+++ b/lib/pure/math.nim
@@ -118,26 +118,6 @@ proc sum*[T](x: openArray[T]): T {.noSideEffect.} =
   ## If `x` is empty, 0 is returned.
   for i in items(x): result = result + i
 
-template toFloat(f: float): float = f
-
-proc mean*[T](x: openArray[T]): float {.noSideEffect.} =
-  ## Computes the mean of the elements in `x`, which are first converted to floats.
-  ## If `x` is empty, NaN is returned.
-  ## ``toFloat(x: T): float`` must be defined.
-  for i in items(x): result = result + toFloat(i)
-  result = result / toFloat(len(x))
-
-proc variance*[T](x: openArray[T]): float {.noSideEffect.} =
-  ## Computes the variance of the elements in `x`.
-  ## If `x` is empty, NaN is returned.
-  ## ``toFloat(x: T): float`` must be defined.
-  result = 0.0
-  var m = mean(x)
-  for i in items(x):
-    var diff = toFloat(i) - m
-    result = result + diff*diff
-  result = result / toFloat(len(x))
-
 proc random*(max: int): int {.benign.}
   ## Returns a random number in the range 0..max-1. The sequence of
   ## random number is always the same, unless `randomize` is called
@@ -376,48 +356,6 @@ proc random*[T](a: openArray[T]): T =
   ## returns a random element from the openarray `a`.
   result = a[random(a.low..a.len)]
 
-type
-  RunningStat* = object                 ## an accumulator for statistical data
-    n*: int                             ## number of pushed data
-    sum*, min*, max*, mean*: float      ## self-explaining
-    oldM, oldS, newS: float
-
-{.deprecated: [TFloatClass: FloatClass, TRunningStat: RunningStat].}
-
-proc push*(s: var RunningStat, x: float) =
-  ## pushes a value `x` for processing
-  inc(s.n)
-  # See Knuth TAOCP vol 2, 3rd edition, page 232
-  if s.n == 1:
-    s.min = x
-    s.max = x
-    s.oldM = x
-    s.mean = x
-    s.oldS = 0.0
-  else:
-    if s.min > x: s.min = x
-    if s.max < x: s.max = x
-    s.mean = s.oldM + (x - s.oldM)/toFloat(s.n)
-    s.newS = s.oldS + (x - s.oldM)*(x - s.mean)
-
-    # set up for next iteration:
-    s.oldM = s.mean
-    s.oldS = s.newS
-  s.sum = s.sum + x
-
-proc push*(s: var RunningStat, x: int) =
-  ## pushes a value `x` for processing. `x` is simply converted to ``float``
-  ## and the other push operation is called.
-  push(s, toFloat(x))
-
-proc variance*(s: RunningStat): float =
-  ## computes the current variance of `s`
-  if s.n > 1: result = s.newS / (toFloat(s.n - 1))
-
-proc standardDeviation*(s: RunningStat): float =
-  ## computes the current standard deviation of `s`
-  result = sqrt(variance(s))
-
 {.pop.}
 {.pop.}
 
diff --git a/lib/pure/nativesockets.nim b/lib/pure/nativesockets.nim
index 9e6ff21ef..b5a8d5777 100644
--- a/lib/pure/nativesockets.nim
+++ b/lib/pure/nativesockets.nim
@@ -206,8 +206,9 @@ proc getAddrInfo*(address: string, port: Port, domain: Domain = AF_INET,
   # OpenBSD doesn't support AI_V4MAPPED and doesn't define the macro AI_V4MAPPED.
   # FreeBSD doesn't support AI_V4MAPPED but defines the macro.
   # https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=198092
-  when not defined(freebsd) or defined(openbsd):
-    hints.ai_flags = AI_V4MAPPED
+  when not defined(freebsd) and not defined(openbsd) and not defined(netbsd):
+    if domain == AF_INET6:
+      hints.ai_flags = AI_V4MAPPED
   var gaiResult = getaddrinfo(address, $port, addr(hints), result)
   if gaiResult != 0'i32:
     when useWinVersion:
diff --git a/lib/pure/nimprof.nim b/lib/pure/nimprof.nim
index cfe6bc40d..e2397b91c 100644
--- a/lib/pure/nimprof.nim
+++ b/lib/pure/nimprof.nim
@@ -1,7 +1,7 @@
 #
 #
 #            Nim's Runtime Library
-#        (c) Copyright 2012 Andreas Rumpf
+#        (c) Copyright 2015 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
@@ -117,24 +117,38 @@ when defined(memProfiler):
   var
     gTicker {.threadvar.}: int
 
-  proc hook(st: StackTrace, size: int) {.nimcall.} =
+  proc requestedHook(): bool {.nimcall.} =
     if gTicker == 0:
-      gTicker = -1
-      when defined(ignoreAllocationSize):
-        hookAux(st, 1)
-      else:
-        hookAux(st, size)
       gTicker = SamplingInterval
+      result = true
     dec gTicker
 
+  proc hook(st: StackTrace, size: int) {.nimcall.} =
+    when defined(ignoreAllocationSize):
+      hookAux(st, 1)
+    else:
+      hookAux(st, size)
+
 else:
   var
     t0 {.threadvar.}: Ticks
+    gTicker: int # we use an additional counter to
+                 # avoid calling 'getTicks' too frequently
+
+  proc requestedHook(): bool {.nimcall.} =
+    if interval == 0: result = true
+    elif gTicker == 0:
+      gTicker = 500
+      if getTicks() - t0 > interval:
+        result = true
+    else:
+      dec gTicker
 
   proc hook(st: StackTrace) {.nimcall.} =
+    #echo "profiling! ", interval
     if interval == 0:
       hookAux(st, 1)
-    elif int64(t0) == 0 or getTicks() - t0 > interval:
+    else:
       hookAux(st, 1)
       t0 = getTicks()
 
@@ -145,9 +159,10 @@ proc cmpEntries(a, b: ptr ProfileEntry): int =
   result = b.getTotal - a.getTotal
 
 proc `//`(a, b: int): string =
-  result = format("$1/$2 = $3%", a, b, formatFloat(a / b * 100.0, ffDefault, 2))
+  result = format("$1/$2 = $3%", a, b, formatFloat(a / b * 100.0, ffDecimal, 2))
 
 proc writeProfile() {.noconv.} =
+  system.profilingRequestedHook = nil
   when declared(system.StackTrace):
     system.profilerHook = nil
   const filename = "profile_results.txt"
@@ -193,14 +208,15 @@ var
 proc disableProfiling*() =
   when declared(system.StackTrace):
     atomicDec disabled
-    system.profilerHook = nil
+    system.profilingRequestedHook = nil
 
 proc enableProfiling*() =
   when declared(system.StackTrace):
     if atomicInc(disabled) >= 0:
-      system.profilerHook = hook
+      system.profilingRequestedHook = requestedHook
 
 when declared(system.StackTrace):
+  system.profilingRequestedHook = requestedHook
   system.profilerHook = hook
   addQuitProc(writeProfile)
 
diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim
index 7a1e14a57..8560c3ee4 100644
--- a/lib/pure/osproc.nim
+++ b/lib/pure/osproc.nim
@@ -886,7 +886,7 @@ elif not defined(useNimRtl):
     discard write(data.pErrorPipe[writeIdx], addr error, sizeof(error))
     exitnow(1)
 
-  when defined(macosx) or defined(freebsd):
+  when defined(macosx) or defined(freebsd) or defined(netbsd) or defined(android):
     var environ {.importc.}: cstringArray
 
   proc startProcessAfterFork(data: ptr StartProcessData) =
@@ -916,7 +916,7 @@ elif not defined(useNimRtl):
     discard fcntl(data.pErrorPipe[writeIdx], F_SETFD, FD_CLOEXEC)
 
     if data.optionPoUsePath:
-      when defined(macosx) or defined(freebsd):
+      when defined(macosx) or defined(freebsd) or defined(netbsd) or defined(android):
         # MacOSX doesn't have execvpe, so we need workaround.
         # On MacOSX we can arrive here only from fork, so this is safe:
         environ = data.sysEnv
diff --git a/lib/pure/parseutils.nim b/lib/pure/parseutils.nim
index b3708838a..698bde42a 100644
--- a/lib/pure/parseutils.nim
+++ b/lib/pure/parseutils.nim
@@ -25,7 +25,7 @@ const
 proc toLower(c: char): char {.inline.} =
   result = if c in {'A'..'Z'}: chr(ord(c)-ord('A')+ord('a')) else: c
 
-proc parseHex*(s: string, number: var int, start = 0): int {.
+proc parseHex*(s: string, number: var int, start = 0; maxLen = 0): int {.
   rtl, extern: "npuParseHex", noSideEffect.}  =
   ## Parses a hexadecimal number and stores its value in ``number``.
   ##
@@ -45,11 +45,14 @@ proc parseHex*(s: string, number: var int, start = 0): int {.
   ##   discard parseHex("0x38", value)
   ##   assert value == -200
   ##
+  ## If 'maxLen==0' the length of the hexadecimal number has no
+  ## upper bound. Not more than ```maxLen`` characters are parsed.
   var i = start
   var foundDigit = false
   if s[i] == '0' and (s[i+1] == 'x' or s[i+1] == 'X'): inc(i, 2)
   elif s[i] == '#': inc(i)
-  while true:
+  let last = if maxLen == 0: s.len else: i+maxLen
+  while i < last:
     case s[i]
     of '_': discard
     of '0'..'9':
diff --git a/lib/pure/redis.nim b/lib/pure/redis.nim
deleted file mode 100644
index e3f18a496..000000000
--- a/lib/pure/redis.nim
+++ /dev/null
@@ -1,1096 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2012 Dominik Picheta
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## This module implements a redis client. It allows you to connect to a
-## redis-server instance, send commands and receive replies.
-##
-## **Beware**: Most (if not all) functions that return a ``RedisString`` may
-## return ``redisNil``, and functions which return a ``RedisList``
-## may return ``nil``.
-
-import sockets, os, strutils, parseutils
-
-const
-  redisNil* = "\0\0"
-
-type
-  Pipeline = ref object
-    enabled: bool
-    buffer: string
-    expected: int ## number of replies expected if pipelined
-
-type
-  SendMode = enum
-    normal, pipelined, multiple
-
-type
-  Redis* = object
-    socket: Socket
-    connected: bool
-    pipeline: Pipeline
-
-  RedisStatus* = string
-  RedisInteger* = BiggestInt
-  RedisString* = string ## Bulk reply
-  RedisList* = seq[RedisString] ## Multi-bulk reply
-
-  ReplyError* = object of IOError ## Invalid reply from redis
-  RedisError* = object of IOError        ## Error in redis
-
-{.deprecated: [TSendMode: SendMode, TRedis: Redis, TRedisStatus: RedisStatus,
-     TRedisInteger: RedisInteger, TRedisString: RedisString,
-     TRedisList: RedisList, EInvalidReply: ReplyError, ERedis: RedisError].}
-
-proc newPipeline(): Pipeline =
-  new(result)
-  result.buffer = ""
-  result.enabled = false
-  result.expected = 0
-
-proc open*(host = "localhost", port = 6379.Port): Redis =
-  ## Opens a connection to the redis server.
-  result.socket = socket(buffered = false)
-  if result.socket == invalidSocket:
-    raiseOSError(osLastError())
-  result.socket.connect(host, port)
-  result.pipeline = newPipeline()
-
-proc raiseInvalidReply(expected, got: char) =
-  raise newException(ReplyError,
-          "Expected '$1' at the beginning of a status reply got '$2'" %
-          [$expected, $got])
-
-proc raiseNoOK(status: string, pipelineEnabled: bool) =
-  if pipelineEnabled and not (status == "QUEUED" or status == "PIPELINED"):
-    raise newException(ReplyError, "Expected \"QUEUED\" or \"PIPELINED\" got \"$1\"" % status)
-  elif not pipelineEnabled and status != "OK":
-    raise newException(ReplyError, "Expected \"OK\" got \"$1\"" % status)
-
-template readSocket(r: Redis, dummyVal:expr): stmt =
-  var line {.inject.}: TaintedString = ""
-  if r.pipeline.enabled:
-    return dummyVal
-  else:
-    readLine(r.socket, line)
-
-proc parseStatus(r: Redis, line: string = ""): RedisStatus =
-  if r.pipeline.enabled:
-    return "PIPELINED"
-
-  if line == "":
-    raise newException(RedisError, "Server closed connection prematurely")
-
-  if line[0] == '-':
-    raise newException(RedisError, strip(line))
-  if line[0] != '+':
-    raiseInvalidReply('+', line[0])
-
-  return line.substr(1) # Strip '+'
-
-proc readStatus(r:Redis): RedisStatus =
-  r.readSocket("PIPELINED")
-  return r.parseStatus(line)
-
-proc parseInteger(r: Redis, line: string = ""): RedisInteger =
-  if r.pipeline.enabled: return -1
-
-  #if line == "+QUEUED":  # inside of multi
-  #  return -1
-
-  if line == "":
-    raise newException(RedisError, "Server closed connection prematurely")
-
-  if line[0] == '-':
-    raise newException(RedisError, strip(line))
-  if line[0] != ':':
-    raiseInvalidReply(':', line[0])
-
-  # Strip ':'
-  if parseBiggestInt(line, result, 1) == 0:
-    raise newException(ReplyError, "Unable to parse integer.")
-
-proc readInteger(r: Redis): RedisInteger =
-  r.readSocket(-1)
-  return r.parseInteger(line)
-
-proc recv(sock: Socket, size: int): TaintedString =
-  result = newString(size).TaintedString
-  if sock.recv(cstring(result), size) != size:
-    raise newException(ReplyError, "recv failed")
-
-proc parseSingleString(r: Redis, line:string, allowMBNil = false): RedisString =
-  if r.pipeline.enabled: return ""
-
-  # Error.
-  if line[0] == '-':
-    raise newException(RedisError, strip(line))
-
-  # Some commands return a /bulk/ value or a /multi-bulk/ nil. Odd.
-  if allowMBNil:
-    if line == "*-1":
-       return redisNil
-
-  if line[0] != '$':
-    raiseInvalidReply('$', line[0])
-
-  var numBytes = parseInt(line.substr(1))
-  if numBytes == -1:
-    return redisNil
-
-  var s = r.socket.recv(numBytes+2)
-  result = strip(s.string)
-
-proc readSingleString(r: Redis): RedisString =
-  r.readSocket("")
-  return r.parseSingleString(line)
-
-proc readNext(r: Redis): RedisList
-
-proc parseArrayLines(r: Redis, countLine:string): RedisList =
-  if countLine.string[0] != '*':
-    raiseInvalidReply('*', countLine.string[0])
-
-  var numElems = parseInt(countLine.string.substr(1))
-  if numElems == -1: return nil
-  result = @[]
-
-  for i in 1..numElems:
-    var parsed = r.readNext()
-    if not isNil(parsed):
-      for item in parsed:
-        result.add(item)
-
-proc readArrayLines(r: Redis): RedisList =
-  r.readSocket(nil)
-  return r.parseArrayLines(line)
-
-proc parseBulkString(r: Redis, allowMBNil = false, line:string = ""): RedisString =
-  if r.pipeline.enabled: return ""
-
-  return r.parseSingleString(line, allowMBNil)
-
-proc readBulkString(r: Redis, allowMBNil = false): RedisString =
-  r.readSocket("")
-  return r.parseBulkString(allowMBNil, line)
-
-proc readArray(r: Redis): RedisList =
-  r.readSocket(@[])
-  return r.parseArrayLines(line)
-
-proc readNext(r: Redis): RedisList =
-  r.readSocket(@[])
-
-  var res = case line[0]
-    of '+', '-': @[r.parseStatus(line)]
-    of ':': @[$(r.parseInteger(line))]
-    of '$': @[r.parseBulkString(true,line)]
-    of '*': r.parseArrayLines(line)
-    else:
-      raise newException(ReplyError, "readNext failed on line: " & line)
-      nil
-  r.pipeline.expected -= 1
-  return res
-
-proc flushPipeline*(r: Redis, wasMulti = false): RedisList =
-  ## Send buffered commands, clear buffer, return results
-  if r.pipeline.buffer.len > 0:
-    r.socket.send(r.pipeline.buffer)
-  r.pipeline.buffer = ""
-
-  r.pipeline.enabled = false
-  result = @[]
-
-  var tot = r.pipeline.expected
-
-  for i in 0..tot-1:
-    var ret = r.readNext()
-    for item in ret:
-     if not (item.contains("OK") or item.contains("QUEUED")):
-       result.add(item)
-
-  r.pipeline.expected = 0
-
-proc startPipelining*(r: Redis) =
-  ## Enable command pipelining (reduces network roundtrips).
-  ## Note that when enabled, you must call flushPipeline to actually send commands, except
-  ## for multi/exec() which enable and flush the pipeline automatically.
-  ## Commands return immediately with dummy values; actual results returned from
-  ## flushPipeline() or exec()
-  r.pipeline.expected = 0
-  r.pipeline.enabled = true
-
-proc sendCommand(r: Redis, cmd: string, args: varargs[string]) =
-  var request = "*" & $(1 + args.len()) & "\c\L"
-  request.add("$" & $cmd.len() & "\c\L")
-  request.add(cmd & "\c\L")
-  for i in items(args):
-    request.add("$" & $i.len() & "\c\L")
-    request.add(i & "\c\L")
-
-  if r.pipeline.enabled:
-    r.pipeline.buffer.add(request)
-    r.pipeline.expected += 1
-  else:
-    r.socket.send(request)
-
-proc sendCommand(r: Redis, cmd: string, arg1: string,
-                 args: varargs[string]) =
-  var request = "*" & $(2 + args.len()) & "\c\L"
-  request.add("$" & $cmd.len() & "\c\L")
-  request.add(cmd & "\c\L")
-  request.add("$" & $arg1.len() & "\c\L")
-  request.add(arg1 & "\c\L")
-  for i in items(args):
-    request.add("$" & $i.len() & "\c\L")
-    request.add(i & "\c\L")
-
-  if r.pipeline.enabled:
-    r.pipeline.expected += 1
-    r.pipeline.buffer.add(request)
-  else:
-    r.socket.send(request)
-
-# Keys
-
-proc del*(r: Redis, keys: varargs[string]): RedisInteger =
-  ## Delete a key or multiple keys
-  r.sendCommand("DEL", keys)
-  return r.readInteger()
-
-proc exists*(r: Redis, key: string): bool =
-  ## Determine if a key exists
-  r.sendCommand("EXISTS", key)
-  return r.readInteger() == 1
-
-proc expire*(r: Redis, key: string, seconds: int): bool =
-  ## Set a key's time to live in seconds. Returns `false` if the key could
-  ## not be found or the timeout could not be set.
-  r.sendCommand("EXPIRE", key, $seconds)
-  return r.readInteger() == 1
-
-proc expireAt*(r: Redis, key: string, timestamp: int): bool =
-  ## Set the expiration for a key as a UNIX timestamp. Returns `false`
-  ## if the key could not be found or the timeout could not be set.
-  r.sendCommand("EXPIREAT", key, $timestamp)
-  return r.readInteger() == 1
-
-proc keys*(r: Redis, pattern: string): RedisList =
-  ## Find all keys matching the given pattern
-  r.sendCommand("KEYS", pattern)
-  return r.readArray()
-
-proc scan*(r: Redis, cursor: var BiggestInt): RedisList =
-  ## Find all keys matching the given pattern and yield it to client in portions
-  ## using default Redis values for MATCH and COUNT parameters
-  r.sendCommand("SCAN", $cursor)
-  let reply = r.readArray()
-  cursor = strutils.parseBiggestInt(reply[0])
-  return reply[1..high(reply)]
-
-proc scan*(r: Redis, cursor: var BiggestInt, pattern: string): RedisList =
-  ## Find all keys matching the given pattern and yield it to client in portions
-  ## using cursor as a client query identifier. Using default Redis value for COUNT argument
-  r.sendCommand("SCAN", $cursor, ["MATCH", pattern])
-  let reply = r.readArray()
-  cursor = strutils.parseBiggestInt(reply[0])
-  return reply[1..high(reply)]
-
-proc scan*(r: Redis, cursor: var BiggestInt, pattern: string, count: int): RedisList =
-  ## Find all keys matching the given pattern and yield it to client in portions
-  ## using cursor as a client query identifier.
-  r.sendCommand("SCAN", $cursor, ["MATCH", pattern, "COUNT", $count])
-  let reply = r.readArray()
-  cursor = strutils.parseBiggestInt(reply[0])
-  return reply[1..high(reply)]
-
-proc move*(r: Redis, key: string, db: int): bool =
-  ## Move a key to another database. Returns `true` on a successful move.
-  r.sendCommand("MOVE", key, $db)
-  return r.readInteger() == 1
-
-proc persist*(r: Redis, key: string): bool =
-  ## Remove the expiration from a key.
-  ## Returns `true` when the timeout was removed.
-  r.sendCommand("PERSIST", key)
-  return r.readInteger() == 1
-
-proc randomKey*(r: Redis): RedisString =
-  ## Return a random key from the keyspace
-  r.sendCommand("RANDOMKEY")
-  return r.readBulkString()
-
-proc rename*(r: Redis, key, newkey: string): RedisStatus =
-  ## Rename a key.
-  ##
-  ## **WARNING:** Overwrites `newkey` if it exists!
-  r.sendCommand("RENAME", key, newkey)
-  raiseNoOK(r.readStatus(), r.pipeline.enabled)
-
-proc renameNX*(r: Redis, key, newkey: string): bool =
-  ## Same as ``rename`` but doesn't continue if `newkey` exists.
-  ## Returns `true` if key was renamed.
-  r.sendCommand("RENAMENX", key, newkey)
-  return r.readInteger() == 1
-
-proc ttl*(r: Redis, key: string): RedisInteger =
-  ## Get the time to live for a key
-  r.sendCommand("TTL", key)
-  return r.readInteger()
-
-proc keyType*(r: Redis, key: string): RedisStatus =
-  ## Determine the type stored at key
-  r.sendCommand("TYPE", key)
-  return r.readStatus()
-
-
-# Strings
-
-proc append*(r: Redis, key, value: string): RedisInteger =
-  ## Append a value to a key
-  r.sendCommand("APPEND", key, value)
-  return r.readInteger()
-
-proc decr*(r: Redis, key: string): RedisInteger =
-  ## Decrement the integer value of a key by one
-  r.sendCommand("DECR", key)
-  return r.readInteger()
-
-proc decrBy*(r: Redis, key: string, decrement: int): RedisInteger =
-  ## Decrement the integer value of a key by the given number
-  r.sendCommand("DECRBY", key, $decrement)
-  return r.readInteger()
-
-proc get*(r: Redis, key: string): RedisString =
-  ## Get the value of a key. Returns `redisNil` when `key` doesn't exist.
-  r.sendCommand("GET", key)
-  return r.readBulkString()
-
-proc getBit*(r: Redis, key: string, offset: int): RedisInteger =
-  ## Returns the bit value at offset in the string value stored at key
-  r.sendCommand("GETBIT", key, $offset)
-  return r.readInteger()
-
-proc getRange*(r: Redis, key: string, start, stop: int): RedisString =
-  ## Get a substring of the string stored at a key
-  r.sendCommand("GETRANGE", key, $start, $stop)
-  return r.readBulkString()
-
-proc getSet*(r: Redis, key: string, value: string): RedisString =
-  ## Set the string value of a key and return its old value. Returns `redisNil`
-  ## when key doesn't exist.
-  r.sendCommand("GETSET", key, value)
-  return r.readBulkString()
-
-proc incr*(r: Redis, key: string): RedisInteger =
-  ## Increment the integer value of a key by one.
-  r.sendCommand("INCR", key)
-  return r.readInteger()
-
-proc incrBy*(r: Redis, key: string, increment: int): RedisInteger =
-  ## Increment the integer value of a key by the given number
-  r.sendCommand("INCRBY", key, $increment)
-  return r.readInteger()
-
-proc setk*(r: Redis, key, value: string) =
-  ## Set the string value of a key.
-  ##
-  ## NOTE: This function had to be renamed due to a clash with the `set` type.
-  r.sendCommand("SET", key, value)
-  raiseNoOK(r.readStatus(), r.pipeline.enabled)
-
-proc setNX*(r: Redis, key, value: string): bool =
-  ## Set the value of a key, only if the key does not exist. Returns `true`
-  ## if the key was set.
-  r.sendCommand("SETNX", key, value)
-  return r.readInteger() == 1
-
-proc setBit*(r: Redis, key: string, offset: int,
-             value: string): RedisInteger =
-  ## Sets or clears the bit at offset in the string value stored at key
-  r.sendCommand("SETBIT", key, $offset, value)
-  return r.readInteger()
-
-proc setEx*(r: Redis, key: string, seconds: int, value: string): RedisStatus =
-  ## Set the value and expiration of a key
-  r.sendCommand("SETEX", key, $seconds, value)
-  raiseNoOK(r.readStatus(), r.pipeline.enabled)
-
-proc setRange*(r: Redis, key: string, offset: int,
-               value: string): RedisInteger =
-  ## Overwrite part of a string at key starting at the specified offset
-  r.sendCommand("SETRANGE", key, $offset, value)
-  return r.readInteger()
-
-proc strlen*(r: Redis, key: string): RedisInteger =
-  ## Get the length of the value stored in a key. Returns 0 when key doesn't
-  ## exist.
-  r.sendCommand("STRLEN", key)
-  return r.readInteger()
-
-# Hashes
-proc hDel*(r: Redis, key, field: string): bool =
-  ## Delete a hash field at `key`. Returns `true` if the field was removed.
-  r.sendCommand("HDEL", key, field)
-  return r.readInteger() == 1
-
-proc hExists*(r: Redis, key, field: string): bool =
-  ## Determine if a hash field exists.
-  r.sendCommand("HEXISTS", key, field)
-  return r.readInteger() == 1
-
-proc hGet*(r: Redis, key, field: string): RedisString =
-  ## Get the value of a hash field
-  r.sendCommand("HGET", key, field)
-  return r.readBulkString()
-
-proc hGetAll*(r: Redis, key: string): RedisList =
-  ## Get all the fields and values in a hash
-  r.sendCommand("HGETALL", key)
-  return r.readArray()
-
-proc hIncrBy*(r: Redis, key, field: string, incr: int): RedisInteger =
-  ## Increment the integer value of a hash field by the given number
-  r.sendCommand("HINCRBY", key, field, $incr)
-  return r.readInteger()
-
-proc hKeys*(r: Redis, key: string): RedisList =
-  ## Get all the fields in a hash
-  r.sendCommand("HKEYS", key)
-  return r.readArray()
-
-proc hLen*(r: Redis, key: string): RedisInteger =
-  ## Get the number of fields in a hash
-  r.sendCommand("HLEN", key)
-  return r.readInteger()
-
-proc hMGet*(r: Redis, key: string, fields: varargs[string]): RedisList =
-  ## Get the values of all the given hash fields
-  r.sendCommand("HMGET", key, fields)
-  return r.readArray()
-
-proc hMSet*(r: Redis, key: string,
-            fieldValues: openArray[tuple[field, value: string]]) =
-  ## Set multiple hash fields to multiple values
-  var args = @[key]
-  for field, value in items(fieldValues):
-    args.add(field)
-    args.add(value)
-  r.sendCommand("HMSET", args)
-  raiseNoOK(r.readStatus(), r.pipeline.enabled)
-
-proc hSet*(r: Redis, key, field, value: string): RedisInteger =
-  ## Set the string value of a hash field
-  r.sendCommand("HSET", key, field, value)
-  return r.readInteger()
-
-proc hSetNX*(r: Redis, key, field, value: string): RedisInteger =
-  ## Set the value of a hash field, only if the field does **not** exist
-  r.sendCommand("HSETNX", key, field, value)
-  return r.readInteger()
-
-proc hVals*(r: Redis, key: string): RedisList =
-  ## Get all the values in a hash
-  r.sendCommand("HVALS", key)
-  return r.readArray()
-
-# Lists
-
-proc bLPop*(r: Redis, keys: varargs[string], timeout: int): RedisList =
-  ## Remove and get the *first* element in a list, or block until
-  ## one is available
-  var args: seq[string] = @[]
-  for i in items(keys): args.add(i)
-  args.add($timeout)
-  r.sendCommand("BLPOP", args)
-  return r.readArray()
-
-proc bRPop*(r: Redis, keys: varargs[string], timeout: int): RedisList =
-  ## Remove and get the *last* element in a list, or block until one
-  ## is available.
-  var args: seq[string] = @[]
-  for i in items(keys): args.add(i)
-  args.add($timeout)
-  r.sendCommand("BRPOP", args)
-  return r.readArray()
-
-proc bRPopLPush*(r: Redis, source, destination: string,
-                 timeout: int): RedisString =
-  ## Pop a value from a list, push it to another list and return it; or
-  ## block until one is available.
-  ##
-  ## http://redis.io/commands/brpoplpush
-  r.sendCommand("BRPOPLPUSH", source, destination, $timeout)
-  return r.readBulkString(true) # Multi-Bulk nil allowed.
-
-proc lIndex*(r: Redis, key: string, index: int): RedisString =
-  ## Get an element from a list by its index
-  r.sendCommand("LINDEX", key, $index)
-  return r.readBulkString()
-
-proc lInsert*(r: Redis, key: string, before: bool, pivot, value: string):
-              RedisInteger =
-  ## Insert an element before or after another element in a list
-  var pos = if before: "BEFORE" else: "AFTER"
-  r.sendCommand("LINSERT", key, pos, pivot, value)
-  return r.readInteger()
-
-proc lLen*(r: Redis, key: string): RedisInteger =
-  ## Get the length of a list
-  r.sendCommand("LLEN", key)
-  return r.readInteger()
-
-proc lPop*(r: Redis, key: string): RedisString =
-  ## Remove and get the first element in a list
-  r.sendCommand("LPOP", key)
-  return r.readBulkString()
-
-proc lPush*(r: Redis, key, value: string, create: bool = true): RedisInteger =
-  ## Prepend a value to a list. Returns the length of the list after the push.
-  ## The ``create`` param specifies whether a list should be created if it
-  ## doesn't exist at ``key``. More specifically if ``create`` is true, `LPUSH`
-  ## will be used, otherwise `LPUSHX`.
-  if create:
-    r.sendCommand("LPUSH", key, value)
-  else:
-    r.sendCommand("LPUSHX", key, value)
-  return r.readInteger()
-
-proc lRange*(r: Redis, key: string, start, stop: int): RedisList =
-  ## Get a range of elements from a list. Returns `nil` when `key`
-  ## doesn't exist.
-  r.sendCommand("LRANGE", key, $start, $stop)
-  return r.readArray()
-
-proc lRem*(r: Redis, key: string, value: string, count: int = 0): RedisInteger =
-  ## Remove elements from a list. Returns the number of elements that have been
-  ## removed.
-  r.sendCommand("LREM", key, $count, value)
-  return r.readInteger()
-
-proc lSet*(r: Redis, key: string, index: int, value: string) =
-  ## Set the value of an element in a list by its index
-  r.sendCommand("LSET", key, $index, value)
-  raiseNoOK(r.readStatus(), r.pipeline.enabled)
-
-proc lTrim*(r: Redis, key: string, start, stop: int)  =
-  ## Trim a list to the specified range
-  r.sendCommand("LTRIM", key, $start, $stop)
-  raiseNoOK(r.readStatus(), r.pipeline.enabled)
-
-proc rPop*(r: Redis, key: string): RedisString =
-  ## Remove and get the last element in a list
-  r.sendCommand("RPOP", key)
-  return r.readBulkString()
-
-proc rPopLPush*(r: Redis, source, destination: string): RedisString =
-  ## Remove the last element in a list, append it to another list and return it
-  r.sendCommand("RPOPLPUSH", source, destination)
-  return r.readBulkString()
-
-proc rPush*(r: Redis, key, value: string, create: bool = true): RedisInteger =
-  ## Append a value to a list. Returns the length of the list after the push.
-  ## The ``create`` param specifies whether a list should be created if it
-  ## doesn't exist at ``key``. More specifically if ``create`` is true, `RPUSH`
-  ## will be used, otherwise `RPUSHX`.
-  if create:
-    r.sendCommand("RPUSH", key, value)
-  else:
-    r.sendCommand("RPUSHX", key, value)
-  return r.readInteger()
-
-# Sets
-
-proc sadd*(r: Redis, key: string, member: string): RedisInteger =
-  ## Add a member to a set
-  r.sendCommand("SADD", key, member)
-  return r.readInteger()
-
-proc scard*(r: Redis, key: string): RedisInteger =
-  ## Get the number of members in a set
-  r.sendCommand("SCARD", key)
-  return r.readInteger()
-
-proc sdiff*(r: Redis, keys: varargs[string]): RedisList =
-  ## Subtract multiple sets
-  r.sendCommand("SDIFF", keys)
-  return r.readArray()
-
-proc sdiffstore*(r: Redis, destination: string,
-                keys: varargs[string]): RedisInteger =
-  ## Subtract multiple sets and store the resulting set in a key
-  r.sendCommand("SDIFFSTORE", destination, keys)
-  return r.readInteger()
-
-proc sinter*(r: Redis, keys: varargs[string]): RedisList =
-  ## Intersect multiple sets
-  r.sendCommand("SINTER", keys)
-  return r.readArray()
-
-proc sinterstore*(r: Redis, destination: string,
-                 keys: varargs[string]): RedisInteger =
-  ## Intersect multiple sets and store the resulting set in a key
-  r.sendCommand("SINTERSTORE", destination, keys)
-  return r.readInteger()
-
-proc sismember*(r: Redis, key: string, member: string): RedisInteger =
-  ## Determine if a given value is a member of a set
-  r.sendCommand("SISMEMBER", key, member)
-  return r.readInteger()
-
-proc smembers*(r: Redis, key: string): RedisList =
-  ## Get all the members in a set
-  r.sendCommand("SMEMBERS", key)
-  return r.readArray()
-
-proc smove*(r: Redis, source: string, destination: string,
-           member: string): RedisInteger =
-  ## Move a member from one set to another
-  r.sendCommand("SMOVE", source, destination, member)
-  return r.readInteger()
-
-proc spop*(r: Redis, key: string): RedisString =
-  ## Remove and return a random member from a set
-  r.sendCommand("SPOP", key)
-  return r.readBulkString()
-
-proc srandmember*(r: Redis, key: string): RedisString =
-  ## Get a random member from a set
-  r.sendCommand("SRANDMEMBER", key)
-  return r.readBulkString()
-
-proc srem*(r: Redis, key: string, member: string): RedisInteger =
-  ## Remove a member from a set
-  r.sendCommand("SREM", key, member)
-  return r.readInteger()
-
-proc sunion*(r: Redis, keys: varargs[string]): RedisList =
-  ## Add multiple sets
-  r.sendCommand("SUNION", keys)
-  return r.readArray()
-
-proc sunionstore*(r: Redis, destination: string,
-                 key: varargs[string]): RedisInteger =
-  ## Add multiple sets and store the resulting set in a key
-  r.sendCommand("SUNIONSTORE", destination, key)
-  return r.readInteger()
-
-# Sorted sets
-
-proc zadd*(r: Redis, key: string, score: int, member: string): RedisInteger =
-  ## Add a member to a sorted set, or update its score if it already exists
-  r.sendCommand("ZADD", key, $score, member)
-  return r.readInteger()
-
-proc zcard*(r: Redis, key: string): RedisInteger =
-  ## Get the number of members in a sorted set
-  r.sendCommand("ZCARD", key)
-  return r.readInteger()
-
-proc zcount*(r: Redis, key: string, min: string, max: string): RedisInteger =
-  ## Count the members in a sorted set with scores within the given values
-  r.sendCommand("ZCOUNT", key, min, max)
-  return r.readInteger()
-
-proc zincrby*(r: Redis, key: string, increment: string,
-             member: string): RedisString =
-  ## Increment the score of a member in a sorted set
-  r.sendCommand("ZINCRBY", key, increment, member)
-  return r.readBulkString()
-
-proc zinterstore*(r: Redis, destination: string, numkeys: string,
-                 keys: openArray[string], weights: openArray[string] = [],
-                 aggregate: string = ""): RedisInteger =
-  ## Intersect multiple sorted sets and store the resulting sorted set in
-  ## a new key
-  var args = @[destination, numkeys]
-  for i in items(keys): args.add(i)
-
-  if weights.len != 0:
-    args.add("WITHSCORE")
-    for i in items(weights): args.add(i)
-  if aggregate.len != 0:
-    args.add("AGGREGATE")
-    args.add(aggregate)
-
-  r.sendCommand("ZINTERSTORE", args)
-
-  return r.readInteger()
-
-proc zrange*(r: Redis, key: string, start: string, stop: string,
-            withScores: bool): RedisList =
-  ## Return a range of members in a sorted set, by index
-  if not withScores:
-    r.sendCommand("ZRANGE", key, start, stop)
-  else:
-    r.sendCommand("ZRANGE", "WITHSCORES", key, start, stop)
-  return r.readArray()
-
-proc zrangebyscore*(r: Redis, key: string, min: string, max: string,
-                   withScore: bool = false, limit: bool = false,
-                   limitOffset: int = 0, limitCount: int = 0): RedisList =
-  ## Return a range of members in a sorted set, by score
-  var args = @[key, min, max]
-
-  if withScore: args.add("WITHSCORE")
-  if limit:
-    args.add("LIMIT")
-    args.add($limitOffset)
-    args.add($limitCount)
-
-  r.sendCommand("ZRANGEBYSCORE", args)
-  return r.readArray()
-
-proc zrank*(r: Redis, key: string, member: string): RedisString =
-  ## Determine the index of a member in a sorted set
-  r.sendCommand("ZRANK", key, member)
-  return r.readBulkString()
-
-proc zrem*(r: Redis, key: string, member: string): RedisInteger =
-  ## Remove a member from a sorted set
-  r.sendCommand("ZREM", key, member)
-  return r.readInteger()
-
-proc zremrangebyrank*(r: Redis, key: string, start: string,
-                     stop: string): RedisInteger =
-  ## Remove all members in a sorted set within the given indexes
-  r.sendCommand("ZREMRANGEBYRANK", key, start, stop)
-  return r.readInteger()
-
-proc zremrangebyscore*(r: Redis, key: string, min: string,
-                      max: string): RedisInteger =
-  ## Remove all members in a sorted set within the given scores
-  r.sendCommand("ZREMRANGEBYSCORE", key, min, max)
-  return r.readInteger()
-
-proc zrevrange*(r: Redis, key: string, start: string, stop: string,
-               withScore: bool): RedisList =
-  ## Return a range of members in a sorted set, by index,
-  ## with scores ordered from high to low
-  if withScore:
-    r.sendCommand("ZREVRANGE", "WITHSCORE", key, start, stop)
-  else: r.sendCommand("ZREVRANGE", key, start, stop)
-  return r.readArray()
-
-proc zrevrangebyscore*(r: Redis, key: string, min: string, max: string,
-                   withScore: bool = false, limit: bool = false,
-                   limitOffset: int = 0, limitCount: int = 0): RedisList =
-  ## Return a range of members in a sorted set, by score, with
-  ## scores ordered from high to low
-  var args = @[key, min, max]
-
-  if withScore: args.add("WITHSCORE")
-  if limit:
-    args.add("LIMIT")
-    args.add($limitOffset)
-    args.add($limitCount)
-
-  r.sendCommand("ZREVRANGEBYSCORE", args)
-  return r.readArray()
-
-proc zrevrank*(r: Redis, key: string, member: string): RedisString =
-  ## Determine the index of a member in a sorted set, with
-  ## scores ordered from high to low
-  r.sendCommand("ZREVRANK", key, member)
-  return r.readBulkString()
-
-proc zscore*(r: Redis, key: string, member: string): RedisString =
-  ## Get the score associated with the given member in a sorted set
-  r.sendCommand("ZSCORE", key, member)
-  return r.readBulkString()
-
-proc zunionstore*(r: Redis, destination: string, numkeys: string,
-                 keys: openArray[string], weights: openArray[string] = [],
-                 aggregate: string = ""): RedisInteger =
-  ## Add multiple sorted sets and store the resulting sorted set in a new key
-  var args = @[destination, numkeys]
-  for i in items(keys): args.add(i)
-
-  if weights.len != 0:
-    args.add("WEIGHTS")
-    for i in items(weights): args.add(i)
-  if aggregate.len != 0:
-    args.add("AGGREGATE")
-    args.add(aggregate)
-
-  r.sendCommand("ZUNIONSTORE", args)
-
-  return r.readInteger()
-
-# HyperLogLog
-
-proc pfadd*(r: Redis, key: string, elements: varargs[string]): RedisInteger =
-  ## Add variable number of elements into special 'HyperLogLog' set type
-  r.sendCommand("PFADD", key, elements)
-  return r.readInteger()
-
-proc pfcount*(r: Redis, key: string): RedisInteger =
-  ## Count approximate number of elements in 'HyperLogLog'
-  r.sendCommand("PFCOUNT", key)
-  return r.readInteger()
-
-proc pfmerge*(r: Redis, destination: string, sources: varargs[string]) =
-  ## Merge several source HyperLogLog's into one specified by destKey
-  r.sendCommand("PFMERGE", destination, sources)
-  raiseNoOK(r.readStatus(), r.pipeline.enabled)
-
-# Pub/Sub
-
-# TODO: pub/sub -- I don't think this will work synchronously.
-discard """
-proc psubscribe*(r: Redis, pattern: openarray[string]): ???? =
-  ## Listen for messages published to channels matching the given patterns
-  r.socket.send("PSUBSCRIBE $#\c\L" % pattern)
-  return ???
-
-proc publish*(r: Redis, channel: string, message: string): RedisInteger =
-  ## Post a message to a channel
-  r.socket.send("PUBLISH $# $#\c\L" % [channel, message])
-  return r.readInteger()
-
-proc punsubscribe*(r: Redis, [pattern: openarray[string], : string): ???? =
-  ## Stop listening for messages posted to channels matching the given patterns
-  r.socket.send("PUNSUBSCRIBE $# $#\c\L" % [[pattern.join(), ])
-  return ???
-
-proc subscribe*(r: Redis, channel: openarray[string]): ???? =
-  ## Listen for messages published to the given channels
-  r.socket.send("SUBSCRIBE $#\c\L" % channel.join)
-  return ???
-
-proc unsubscribe*(r: Redis, [channel: openarray[string], : string): ???? =
-  ## Stop listening for messages posted to the given channels
-  r.socket.send("UNSUBSCRIBE $# $#\c\L" % [[channel.join(), ])
-  return ???
-
-"""
-
-# Transactions
-
-proc discardMulti*(r: Redis) =
-  ## Discard all commands issued after MULTI
-  r.sendCommand("DISCARD")
-  raiseNoOK(r.readStatus(), r.pipeline.enabled)
-
-proc exec*(r: Redis): RedisList =
-  ## Execute all commands issued after MULTI
-  r.sendCommand("EXEC")
-  r.pipeline.enabled = false
-  # Will reply with +OK for MULTI/EXEC and +QUEUED for every command
-  # between, then with the results
-  return r.flushPipeline(true)
-
-
-proc multi*(r: Redis) =
-  ## Mark the start of a transaction block
-  r.startPipelining()
-  r.sendCommand("MULTI")
-  raiseNoOK(r.readStatus(), r.pipeline.enabled)
-
-proc unwatch*(r: Redis) =
-  ## Forget about all watched keys
-  r.sendCommand("UNWATCH")
-  raiseNoOK(r.readStatus(), r.pipeline.enabled)
-
-proc watch*(r: Redis, key: varargs[string]) =
-  ## Watch the given keys to determine execution of the MULTI/EXEC block
-  r.sendCommand("WATCH", key)
-  raiseNoOK(r.readStatus(), r.pipeline.enabled)
-
-# Connection
-
-proc auth*(r: Redis, password: string) =
-  ## Authenticate to the server
-  r.sendCommand("AUTH", password)
-  raiseNoOK(r.readStatus(), r.pipeline.enabled)
-
-proc echoServ*(r: Redis, message: string): RedisString =
-  ## Echo the given string
-  r.sendCommand("ECHO", message)
-  return r.readBulkString()
-
-proc ping*(r: Redis): RedisStatus =
-  ## Ping the server
-  r.sendCommand("PING")
-  return r.readStatus()
-
-proc quit*(r: Redis) =
-  ## Close the connection
-  r.sendCommand("QUIT")
-  raiseNoOK(r.readStatus(), r.pipeline.enabled)
-
-proc select*(r: Redis, index: int): RedisStatus =
-  ## Change the selected database for the current connection
-  r.sendCommand("SELECT", $index)
-  return r.readStatus()
-
-# Server
-
-proc bgrewriteaof*(r: Redis) =
-  ## Asynchronously rewrite the append-only file
-  r.sendCommand("BGREWRITEAOF")
-  raiseNoOK(r.readStatus(), r.pipeline.enabled)
-
-proc bgsave*(r: Redis) =
-  ## Asynchronously save the dataset to disk
-  r.sendCommand("BGSAVE")
-  raiseNoOK(r.readStatus(), r.pipeline.enabled)
-
-proc configGet*(r: Redis, parameter: string): RedisList =
-  ## Get the value of a configuration parameter
-  r.sendCommand("CONFIG", "GET", parameter)
-  return r.readArray()
-
-proc configSet*(r: Redis, parameter: string, value: string) =
-  ## Set a configuration parameter to the given value
-  r.sendCommand("CONFIG", "SET", parameter, value)
-  raiseNoOK(r.readStatus(), r.pipeline.enabled)
-
-proc configResetStat*(r: Redis) =
-  ## Reset the stats returned by INFO
-  r.sendCommand("CONFIG", "RESETSTAT")
-  raiseNoOK(r.readStatus(), r.pipeline.enabled)
-
-proc dbsize*(r: Redis): RedisInteger =
-  ## Return the number of keys in the selected database
-  r.sendCommand("DBSIZE")
-  return r.readInteger()
-
-proc debugObject*(r: Redis, key: string): RedisStatus =
-  ## Get debugging information about a key
-  r.sendCommand("DEBUG", "OBJECT", key)
-  return r.readStatus()
-
-proc debugSegfault*(r: Redis) =
-  ## Make the server crash
-  r.sendCommand("DEBUG", "SEGFAULT")
-
-proc flushall*(r: Redis): RedisStatus =
-  ## Remove all keys from all databases
-  r.sendCommand("FLUSHALL")
-  raiseNoOK(r.readStatus(), r.pipeline.enabled)
-
-proc flushdb*(r: Redis): RedisStatus =
-  ## Remove all keys from the current database
-  r.sendCommand("FLUSHDB")
-  raiseNoOK(r.readStatus(), r.pipeline.enabled)
-
-proc info*(r: Redis): RedisString =
-  ## Get information and statistics about the server
-  r.sendCommand("INFO")
-  return r.readBulkString()
-
-proc lastsave*(r: Redis): RedisInteger =
-  ## Get the UNIX time stamp of the last successful save to disk
-  r.sendCommand("LASTSAVE")
-  return r.readInteger()
-
-discard """
-proc monitor*(r: Redis) =
-  ## Listen for all requests received by the server in real time
-  r.socket.send("MONITOR\c\L")
-  raiseNoOK(r.readStatus(), r.pipeline.enabled)
-"""
-
-proc save*(r: Redis) =
-  ## Synchronously save the dataset to disk
-  r.sendCommand("SAVE")
-  raiseNoOK(r.readStatus(), r.pipeline.enabled)
-
-proc shutdown*(r: Redis) =
-  ## Synchronously save the dataset to disk and then shut down the server
-  r.sendCommand("SHUTDOWN")
-  var s = "".TaintedString
-  r.socket.readLine(s)
-  if s.string.len != 0: raise newException(RedisError, s.string)
-
-proc slaveof*(r: Redis, host: string, port: string) =
-  ## Make the server a slave of another instance, or promote it as master
-  r.sendCommand("SLAVEOF", host, port)
-  raiseNoOK(r.readStatus(), r.pipeline.enabled)
-
-iterator hPairs*(r: Redis, key: string): tuple[key, value: string] =
-  ## Iterator for keys and values in a hash.
-  var
-    contents = r.hGetAll(key)
-    k = ""
-  for i in items(contents):
-    if k == "":
-      k = i
-    else:
-      yield (k, i)
-      k = ""
-
-proc someTests(r: Redis, how: SendMode):seq[string] =
-  var list:seq[string] = @[]
-
-  if how == pipelined:
-    r.startPipelining()
-  elif how ==  multiple:
-    r.multi()
-
-  r.setk("nim:test", "Testing something.")
-  r.setk("nim:utf8", "こんにちは")
-  r.setk("nim:esc", "\\ths ągt\\")
-  r.setk("nim:int", "1")
-  list.add(r.get("nim:esc"))
-  list.add($(r.incr("nim:int")))
-  list.add(r.get("nim:int"))
-  list.add(r.get("nim:utf8"))
-  list.add($(r.hSet("test1", "name", "A Test")))
-  var res = r.hGetAll("test1")
-  for r in res:
-    list.add(r)
-  list.add(r.get("invalid_key"))
-  list.add($(r.lPush("mylist","itema")))
-  list.add($(r.lPush("mylist","itemb")))
-  r.lTrim("mylist",0,1)
-  var p = r.lRange("mylist", 0, -1)
-
-  for i in items(p):
-    if not isNil(i):
-      list.add(i)
-
-  list.add(r.debugObject("mylist"))
-
-  r.configSet("timeout", "299")
-  var g = r.configGet("timeout")
-  for i in items(g):
-    list.add(i)
-
-  list.add(r.echoServ("BLAH"))
-
-  case how
-  of normal:
-    return list
-  of pipelined:
-    return r.flushPipeline()
-  of multiple:
-    return r.exec()
-
-proc assertListsIdentical(listA, listB: seq[string]) =
-  assert(listA.len == listB.len)
-  var i = 0
-  for item in listA:
-    assert(item == listB[i])
-    i = i + 1
-
-when not defined(testing) and isMainModule:
-  when false:
-    var r = open()
-
-    # Test with no pipelining
-    var listNormal = r.someTests(normal)
-
-    # Test with pipelining enabled
-    var listPipelined = r.someTests(pipelined)
-    assertListsIdentical(listNormal, listPipelined)
-
-    # Test with multi/exec() (automatic pipelining)
-    var listMulti = r.someTests(multiple)
-    assertListsIdentical(listNormal, listMulti)
diff --git a/lib/pure/stats.nim b/lib/pure/stats.nim
new file mode 100644
index 000000000..ec4cd182b
--- /dev/null
+++ b/lib/pure/stats.nim
@@ -0,0 +1,348 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2015 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+## Statistical analysis framework for performing
+## basic statistical analysis of data.
+## The data is analysed in a single pass, when a data value
+## is pushed to the ``RunningStat`` or ``RunningRegress`` objects
+##
+## ``RunningStat`` calculates for a single data set
+## - n (data count)
+## - min  (smallest value)
+## - max  (largest value)
+## - sum
+## - mean
+## - variance
+## - varianceS (sample var)
+## - standardDeviation
+## - standardDeviationS  (sample stddev)
+## - skewness (the third statistical moment)
+## - kurtosis (the fourth statistical moment)
+##
+## ``RunningRegress`` calculates for two sets of data
+## - n
+## - slope
+## - intercept
+## - correlation
+##
+## Procs have been provided to calculate statistics on arrays and sequences.
+##
+## However, if more than a single statistical calculation is required, it is more
+## efficient to push the data once to the RunningStat object, and
+## call the numerous statistical procs for the RunningStat object.
+##
+## .. code-block:: Nim
+##
+##  var rs: RunningStat
+##  rs.push(MySeqOfData)
+##  rs.mean()
+##  rs.variance()
+##  rs.skewness()
+##  rs.kurtosis()
+
+from math import FloatClass, sqrt, pow, round
+
+{.push debugger:off .} # the user does not want to trace a part
+                       # of the standard library!
+{.push checks:off, line_dir:off, stack_trace:off.}
+
+type
+  RunningStat* = object             ## an accumulator for statistical data
+    n*: int                         ## number of pushed data
+    min*, max*, sum*: float         ## self-explaining
+    mom1, mom2, mom3, mom4: float   ## statistical moments, mom1 is mean
+
+
+  RunningRegress* = object  ## an accumulator for regression calculations
+    n*: int                 ## number of pushed data
+    x_stats*: RunningStat   ## stats for first set of data
+    y_stats*: RunningStat   ## stats for second set of data
+    s_xy: float             ## accumulated data for combined xy
+
+{.deprecated: [TFloatClass: FloatClass, TRunningStat: RunningStat].}
+
+# ----------- RunningStat --------------------------
+proc clear*(s: var RunningStat) =
+  ## reset `s`
+  s.n = 0
+  s.min = toBiggestFloat(int.high)
+  s.max = 0.0
+  s.sum = 0.0
+  s.mom1 = 0.0
+  s.mom2 = 0.0
+  s.mom3 = 0.0
+  s.mom4 = 0.0
+
+proc push*(s: var RunningStat, x: float) =
+  ## pushes a value `x` for processing
+  if s.n == 0: s.min = x
+  inc(s.n)
+  # See Knuth TAOCP vol 2, 3rd edition, page 232
+  if s.min > x: s.min = x
+  if s.max < x: s.max = x
+  s.sum += x
+  let n = toFloat(s.n)
+  let delta = x - s.mom1
+  let delta_n = delta / toFloat(s.n)
+  let delta_n2 = delta_n * delta_n
+  let term1 = delta * delta_n * toFloat(s.n - 1)
+  s.mom4 += term1 * delta_n2 * (n*n - 3*n + 3) +
+              6*delta_n2*s.mom2 - 4*delta_n*s.mom3
+  s.mom3 += term1 * delta_n * (n - 2) - 3*delta_n*s.mom2
+  s.mom2 += term1
+  s.mom1 += delta_n
+
+proc push*(s: var RunningStat, x: int) =
+  ## pushes a value `x` for processing.
+  ##
+  ## `x` is simply converted to ``float``
+  ## and the other push operation is called.
+  s.push(toFloat(x))
+
+proc push*(s: var RunningStat, x: openarray[float|int]) =
+  ## pushes all values of `x` for processing.
+  ##
+  ## Int values of `x` are simply converted to ``float`` and
+  ## the other push operation is called.
+  for val in x:
+    s.push(val)
+
+proc mean*(s: RunningStat): float =
+  ## computes the current mean of `s`
+  result = s.mom1
+
+proc variance*(s: RunningStat): float =
+  ## computes the current population variance of `s`
+  result = s.mom2 / toFloat(s.n)
+
+proc varianceS*(s: RunningStat): float =
+  ## computes the current sample variance of `s`
+  if s.n > 1: result = s.mom2 / toFloat(s.n - 1)
+
+proc standardDeviation*(s: RunningStat): float =
+  ## computes the current population standard deviation of `s`
+  result = sqrt(variance(s))
+
+proc standardDeviationS*(s: RunningStat): float =
+  ## computes the current sample standard deviation of `s`
+  result = sqrt(varianceS(s))
+
+proc skewness*(s: RunningStat): float =
+  ## computes the current population skewness of `s`
+  result = sqrt(toFloat(s.n)) * s.mom3 / pow(s.mom2, 1.5)
+
+proc skewnessS*(s: RunningStat): float =
+  ## computes the current sample skewness of `s`
+  let s2 = skewness(s)
+  result = sqrt(toFloat(s.n*(s.n-1)))*s2 / toFloat(s.n-2)
+
+proc kurtosis*(s: RunningStat): float =
+  ## computes the current population kurtosis of `s`
+  result = toFloat(s.n) * s.mom4 / (s.mom2 * s.mom2) - 3.0
+
+proc kurtosisS*(s: RunningStat): float =
+  ## computes the current sample kurtosis of `s`
+  result = toFloat(s.n-1) / toFloat((s.n-2)*(s.n-3)) *
+              (toFloat(s.n+1)*kurtosis(s) + 6)
+
+proc `+`*(a, b: RunningStat): RunningStat =
+  ## combine two RunningStats.
+  ##
+  ## Useful if performing parallel analysis of data series
+  ## and need to re-combine parallel result sets
+  result.clear()
+  result.n = a.n + b.n
+
+  let delta = b.mom1 - a.mom1
+  let delta2 = delta*delta
+  let delta3 = delta*delta2
+  let delta4 = delta2*delta2
+  let n = toFloat(result.n)
+
+  result.mom1 = (a.n.float*a.mom1 + b.n.float*b.mom1) / n
+  result.mom2 = a.mom2 + b.mom2 + delta2 * a.n.float * b.n.float / n
+  result.mom3 = a.mom3 + b.mom3 +
+                delta3 * a.n.float * b.n.float * (a.n.float - b.n.float)/(n*n);
+  result.mom3 += 3.0*delta * (a.n.float*b.mom2 - b.n.float*a.mom2) / n
+  result.mom4 = a.mom4 + b.mom4 +
+            delta4*a.n.float*b.n.float * toFloat(a.n*a.n - a.n*b.n + b.n*b.n) /
+                (n*n*n)
+  result.mom4 += 6.0*delta2 * (a.n.float*a.n.float*b.mom2 + b.n.float*b.n.float*a.mom2) /
+                (n*n) +
+                4.0*delta*(a.n.float*b.mom3 - b.n.float*a.mom3) / n
+  result.max = max(a.max, b.max)
+  result.min = max(a.min, b.min)
+
+proc `+=`*(a: var RunningStat, b: RunningStat) {.inline.} =
+  ## add a second RunningStats `b` to `a`
+  a = a + b
+# ---------------------- standalone array/seq stats ---------------------
+proc mean*[T](x: openArray[T]): float =
+  ## computes the mean of `x`
+  var rs: RunningStat
+  rs.push(x)
+  result = rs.mean()
+
+proc variance*[T](x: openArray[T]): float =
+  ## computes the population variance of `x`
+  var rs: RunningStat
+  rs.push(x)
+  result = rs.variance()
+
+proc varianceS*[T](x: openArray[T]): float =
+  ## computes the sample variance of `x`
+  var rs: RunningStat
+  rs.push(x)
+  result = rs.varianceS()
+
+proc standardDeviation*[T](x: openArray[T]): float =
+  ## computes the population standardDeviation of `x`
+  var rs: RunningStat
+  rs.push(x)
+  result = rs.standardDeviation()
+
+proc standardDeviationS*[T](x: openArray[T]): float =
+  ## computes the sanple standardDeviation of `x`
+  var rs: RunningStat
+  rs.push(x)
+  result = rs.standardDeviationS()
+
+proc skewness*[T](x: openArray[T]): float =
+  ## computes the population skewness of `x`
+  var rs: RunningStat
+  rs.push(x)
+  result = rs.skewness()
+
+proc skewnessS*[T](x: openArray[T]): float =
+  ## computes the sample skewness of `x`
+  var rs: RunningStat
+  rs.push(x)
+  result = rs.skewnessS()
+
+proc kurtosis*[T](x: openArray[T]): float =
+  ## computes the population kurtosis of `x`
+  var rs: RunningStat
+  rs.push(x)
+  result = rs.kurtosis()
+
+proc kurtosisS*[T](x: openArray[T]): float =
+  ## computes the sample kurtosis of `x`
+  var rs: RunningStat
+  rs.push(x)
+  result = rs.kurtosisS()
+
+# ---------------------- Running Regression -----------------------------
+
+proc clear*(r: var RunningRegress) =
+  ## reset `r`
+  r.x_stats.clear()
+  r.y_stats.clear()
+  r.s_xy = 0.0
+  r.n = 0
+
+proc push*(r: var RunningRegress, x, y: float) =
+  ## pushes two values `x` and `y` for processing
+  r.s_xy += (r.x_stats.mean() - x)*(r.y_stats.mean() - y)*
+                toFloat(r.n) / toFloat(r.n + 1)
+  r.x_stats.push(x)
+  r.y_stats.push(y)
+  inc(r.n)
+
+proc push*(r: var RunningRegress, x, y: int) {.inline.} =
+  ## pushes two values `x` and `y` for processing.
+  ##
+  ## `x` and `y` are converted to ``float``
+  ## and the other push operation is called.
+  r.push(toFloat(x), toFloat(y))
+
+proc push*(r: var RunningRegress, x, y: openarray[float|int]) =
+  ## pushes two sets of values `x` and `y` for processing.
+  assert(x.len == y.len)
+  for i in 0..<x.len:
+    r.push(x[i], y[i])
+
+proc slope*(r: RunningRegress): float =
+  ## computes the current slope of `r`
+  let s_xx = r.x_stats.varianceS()*toFloat(r.n - 1)
+  result = r.s_xy / s_xx
+
+proc intercept*(r: RunningRegress): float =
+  ## computes the current intercept of `r`
+  result = r.y_stats.mean() - r.slope()*r.x_stats.mean()
+
+proc correlation*(r: RunningRegress): float =
+  ## computes the current correlation of the two data
+  ## sets pushed into `r`
+  let t = r.x_stats.standardDeviation() * r.y_stats.standardDeviation()
+  result = r.s_xy / ( toFloat(r.n) * t )
+
+proc `+`*(a, b: RunningRegress):  RunningRegress =
+  ## combine two `RunningRegress` objects.
+  ##
+  ## Useful if performing parallel analysis of data series
+  ## and need to re-combine parallel result sets
+  result.clear()
+  result.x_stats = a.x_stats + b.x_stats
+  result.y_stats = a.y_stats + b.y_stats
+  result.n = a.n + b.n
+
+  let delta_x = b.x_stats.mean() - a.x_stats.mean()
+  let delta_y = b.y_stats.mean() - a.y_stats.mean()
+  result.s_xy = a.s_xy + b.s_xy +
+      toFloat(a.n*b.n)*delta_x*delta_y/toFloat(result.n)
+
+proc `+=`*(a: var RunningRegress, b: RunningRegress) =
+  ## add RunningRegress `b` to `a`
+  a = a + b
+
+{.pop.}
+{.pop.}
+
+when isMainModule:
+  proc clean(x: float): float =
+    result = round(1.0e8*x).float * 1.0e-8
+
+  var rs: RunningStat
+  rs.push(@[1.0, 2.0, 1.0, 4.0, 1.0, 4.0, 1.0, 2.0])
+  doAssert(rs.n == 8)
+  doAssert(clean(rs.mean) == 2.0)
+  doAssert(clean(rs.variance()) == 1.5)
+  doAssert(clean(rs.varianceS()) == 1.71428571)
+  doAssert(clean(rs.skewness()) == 0.81649658)
+  doAssert(clean(rs.skewnessS()) == 1.01835015)
+  doAssert(clean(rs.kurtosis()) == -1.0)
+  doAssert(clean(rs.kurtosisS()) == -0.7000000000000001)
+
+  var rs1, rs2: RunningStat
+  rs1.push(@[1.0, 2.0, 1.0, 4.0])
+  rs2.push(@[1.0, 4.0, 1.0, 2.0])
+  let rs3 = rs1 + rs2
+  doAssert(clean(rs3.mom2) == clean(rs.mom2))
+  doAssert(clean(rs3.mom3) == clean(rs.mom3))
+  doAssert(clean(rs3.mom4) == clean(rs.mom4))
+  rs1 += rs2
+  doAssert(clean(rs1.mom2) == clean(rs.mom2))
+  doAssert(clean(rs1.mom3) == clean(rs.mom3))
+  doAssert(clean(rs1.mom4) == clean(rs.mom4))
+  rs1.clear()
+  rs1.push(@[1.0, 2.2, 1.4, 4.9])
+  doAssert(rs1.sum == 9.5)
+  doAssert(rs1.mean() == 2.375)
+
+  var rr: RunningRegress
+  rr.push(@[0.0,1.0,2.8,3.0,4.0], @[0.0,1.0,2.3,3.0,4.0])
+  doAssert(rr.slope() == 0.9695585996955861)
+  doAssert(rr.intercept() == -0.03424657534246611)
+  doAssert(rr.correlation() == 0.9905100362239381)
+  var rr1, rr2: RunningRegress
+  rr1.push(@[0.0,1.0], @[0.0,1.0])
+  rr2.push(@[2.8,3.0,4.0], @[2.3,3.0,4.0])
+  let rr3 = rr1 + rr2
+  doAssert(rr3.correlation() == rr.correlation())
+  doAssert(clean(rr3.slope()) == clean(rr.slope()))
+  doAssert(clean(rr3.intercept()) == clean(rr.intercept()))
diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim
index aa29bb073..a446f85b4 100644
--- a/lib/pure/strutils.nim
+++ b/lib/pure/strutils.nim
@@ -1210,22 +1210,21 @@ proc unescape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect,
   ## If `s` does not begin with ``prefix`` and end with ``suffix`` a
   ## ValueError exception will be raised.
   result = newStringOfCap(s.len)
-  var i = 0
+  var i = prefix.len
   if not s.startsWith(prefix):
     raise newException(ValueError,
                        "String does not start with a prefix of: " & prefix)
-  inc(i)
   while true:
     if i == s.len-suffix.len: break
     case s[i]
     of '\\':
       case s[i+1]:
       of 'x':
-        inc i
+        inc i, 2
         var c: int
-        i += parseutils.parseHex(s, c, i)
+        i += parseutils.parseHex(s, c, i, maxLen=2)
         result.add(chr(c))
-        inc(i, 2)
+        dec i, 2
       of '\\':
         result.add('\\')
       of '\'':
@@ -1721,3 +1720,4 @@ when isMainModule:
   doAssert isUpper("ABC")
   doAssert(not isUpper("AAcc"))
   doAssert(not isUpper("A#$"))
+  doAssert(unescape(r"\x013", "", "") == "\x013")
diff --git a/lib/pure/times.nim b/lib/pure/times.nim
index c9854a650..03745d54e 100644
--- a/lib/pure/times.nim
+++ b/lib/pure/times.nim
@@ -29,7 +29,7 @@
 ##  echo "epochTime() float value: ", epochTime()
 ##  echo "getTime()   float value: ", toSeconds(getTime())
 ##  echo "cpuTime()   float value: ", cpuTime()
-##  echo "An hour from now      : ", getLocalTime(getTime()) + initInterval(0,0,0,1)
+##  echo "An hour from now      : ", getLocalTime(getTime()) + 1.hours
 ##  echo "An hour from (UTC) now: ", getGmTime(getTime()) + initInterval(0,0,0,1)
 
 {.push debugger:off.} # the user does not want to trace a part
@@ -171,11 +171,6 @@ type
 {.deprecated: [TMonth: Month, TWeekDay: WeekDay, TTime: Time,
     TTimeInterval: TimeInterval, TTimeInfo: TimeInfo].}
 
-proc miliseconds*(t: TimeInterval): int {.deprecated.} = t.milliseconds
-
-proc `miliseconds=`*(t:var TimeInterval, milliseconds: int) {.deprecated.} =
-  t.milliseconds = milliseconds
-
 proc getTime*(): Time {.tags: [TimeEffect], benign.}
   ## gets the current calendar time as a UNIX epoch value (number of seconds
   ## elapsed since 1970) with integer precission. Use epochTime for higher
@@ -245,13 +240,59 @@ proc getStartMilsecs*(): int {.deprecated, tags: [TimeEffect], benign.}
 proc initInterval*(milliseconds, seconds, minutes, hours, days, months,
                    years: int = 0): TimeInterval =
   ## creates a new ``TimeInterval``.
-  result.milliseconds = milliseconds
-  result.seconds = seconds
-  result.minutes = minutes
-  result.hours = hours
-  result.days = days
-  result.months = months
-  result.years = years
+  ##
+  ## You can also use the convenience procedures called ``milliseconds``,
+  ## ``seconds``, ``minutes``, ``hours``, ``days``, ``months``, and ``years``.
+  ##
+  ## Example:
+  ##
+  ## .. code-block:: nim
+  ##
+  ##     let day = initInterval(hours=24)
+  ##     let tomorrow = getTime() + day
+  ##     echo(tomorrow)
+  var carryO = 0
+  result.milliseconds = `mod`(milliseconds, 1000)
+  carryO = `div`(milliseconds, 1000)
+  result.seconds = `mod`(carryO + seconds, 60)
+  carryO = `div`(seconds, 60)
+  result.minutes = `mod`(carryO + minutes, 60)
+  carryO = `div`(minutes, 60)
+  result.hours = `mod`(carryO + hours, 24)
+  carryO = `div`(hours, 24)
+  result.days = carryO + days
+  carryO = 0
+  result.months = `mod`(months, 12)
+  carryO = `div`(months, 12)
+  result.years = carryO + years
+
+proc `+`*(ti1, ti2: TimeInterval): TimeInterval =
+  ## Adds two ``TimeInterval`` objects together.
+  var carryO = 0
+  result.milliseconds = `mod`(ti1.milliseconds + ti2.milliseconds, 1000)
+  carryO = `div`(ti1.milliseconds + ti2.milliseconds, 1000)
+  result.seconds = `mod`(carryO + ti1.seconds + ti2.seconds, 60)
+  carryO = `div`(ti1.seconds + ti2.seconds, 60)
+  result.minutes = `mod`(carryO + ti1.minutes + ti2.minutes, 60)
+  carryO = `div`(ti1.minutes + ti2.minutes, 60)
+  result.hours = `mod`(carryO + ti1.hours + ti2.hours, 24)
+  carryO = `div`(ti1.hours + ti2.hours, 24)
+  result.days = carryO + ti1.days + ti2.days
+  carryO = 0
+  result.months = `mod`(ti1.months + ti2.months, 12)
+  carryO = `div`(ti1.months + ti2.months, 12)
+  result.years = carryO + ti1.years + ti2.years
+
+proc `-`*(ti1, ti2: TimeInterval): TimeInterval =
+  ## Subtracts TimeInterval ``ti1`` from ``ti2``.
+  result = ti1
+  result.milliseconds -= ti2.milliseconds
+  result.seconds -= ti2.seconds
+  result.minutes -= ti2.minutes
+  result.hours -= ti2.hours
+  result.days -= ti2.days
+  result.months -= ti2.months
+  result.years -= ti2.years
 
 proc isLeapYear*(year: int): bool =
   ## returns true if ``year`` is a leap year
@@ -288,13 +329,22 @@ proc toSeconds(a: TimeInfo, interval: TimeInterval): float =
 
   newinterv.months += interval.years * 12
   var curMonth = anew.month
-  for mth in 1 .. newinterv.months:
-    result += float(getDaysInMonth(curMonth, anew.year) * 24 * 60 * 60)
-    if curMonth == mDec:
-      curMonth = mJan
-      anew.year.inc()
-    else:
-      curMonth.inc()
+  if newinterv.months < 0:   # subtracting
+    for mth in countDown(-1 * newinterv.months, 1):
+      result -= float(getDaysInMonth(curMonth, anew.year) * 24 * 60 * 60)
+      if curMonth == mJan:
+        curMonth = mDec
+        anew.year.dec()
+      else:
+        curMonth.dec()
+  else:  # adding
+    for mth in 1 .. newinterv.months:
+      result += float(getDaysInMonth(curMonth, anew.year) * 24 * 60 * 60)
+      if curMonth == mDec:
+        curMonth = mJan
+        anew.year.inc()
+      else:
+        curMonth.inc()
   result += float(newinterv.days * 24 * 60 * 60)
   result += float(newinterv.hours * 60 * 60)
   result += float(newinterv.minutes * 60)
@@ -302,28 +352,39 @@ proc toSeconds(a: TimeInfo, interval: TimeInterval): float =
   result += newinterv.milliseconds / 1000
 
 proc `+`*(a: TimeInfo, interval: TimeInterval): TimeInfo =
-  ## adds ``interval`` time.
+  ## adds ``interval`` time from TimeInfo ``a``.
   ##
   ## **Note:** This has been only briefly tested and it may not be
   ## very accurate.
   let t = toSeconds(timeInfoToTime(a))
   let secs = toSeconds(a, interval)
-  #if a.tzname == "UTC":
-  #  result = getGMTime(fromSeconds(t + secs))
-  #else:
   result = getLocalTime(fromSeconds(t + secs))
 
 proc `-`*(a: TimeInfo, interval: TimeInterval): TimeInfo =
-  ## subtracts ``interval`` time.
+  ## subtracts ``interval`` time from TimeInfo ``a``.
   ##
   ## **Note:** This has been only briefly tested, it is inaccurate especially
   ## when you subtract so much that you reach the Julian calendar.
   let t = toSeconds(timeInfoToTime(a))
-  let secs = toSeconds(a, interval)
-  #if a.tzname == "UTC":
-  #  result = getGMTime(fromSeconds(t - secs))
-  #else:
-  result = getLocalTime(fromSeconds(t - secs))
+  var intval: TimeInterval
+  intval.milliseconds = - interval.milliseconds
+  intval.seconds = - interval.seconds
+  intval.minutes = - interval.minutes
+  intval.hours = - interval.hours
+  intval.days = - interval.days
+  intval.months = - interval.months
+  intval.years = - interval.years
+  let secs = toSeconds(a, intval)
+  result = getLocalTime(fromSeconds(t + secs))
+
+proc miliseconds*(t: TimeInterval): int {.deprecated.} = t.milliseconds
+
+proc `miliseconds=`*(t: var TimeInterval, milliseconds: int) {.deprecated.} =
+  ## An alias for a misspelled field in ``TimeInterval``.
+  ##
+  ## **Warning:** This should not be used! It will be removed in the next
+  ## version.
+  t.milliseconds = milliseconds
 
 when not defined(JS):
   proc epochTime*(): float {.rtl, extern: "nt$1", tags: [TimeEffect].}
@@ -603,6 +664,69 @@ proc `$`*(m: Month): string =
       "November", "December"]
   return lookup[m]
 
+proc milliseconds*(ms: int): TimeInterval {.inline.} =
+  ## TimeInterval of `ms` milliseconds
+  ##
+  ## Note: not all time functions have millisecond resolution
+  initInterval(`mod`(ms,1000), `div`(ms,1000))
+
+proc seconds*(s: int): TimeInterval {.inline.} =
+  ## TimeInterval of `s` seconds
+  ##
+  ## ``echo getTime() + 5.second``
+  initInterval(0,`mod`(s,60), `div`(s,60))
+
+proc minutes*(m: int): TimeInterval {.inline.} =
+  ## TimeInterval of `m` minutes
+  ##
+  ## ``echo getTime() + 5.minutes``
+  initInterval(0,0,`mod`(m,60), `div`(m,60))
+
+proc hours*(h: int): TimeInterval {.inline.} =
+  ## TimeInterval of `h` hours
+  ##
+  ## ``echo getTime() + 2.hours``
+  initInterval(0,0,0,`mod`(h,24),`div`(h,24))
+
+proc days*(d: int): TimeInterval {.inline.} =
+  ## TimeInterval of `d` days
+  ##
+  ## ``echo getTime() + 2.days``
+  initInterval(0,0,0,0,d)
+
+proc months*(m: int): TimeInterval {.inline.} =
+  ## TimeInterval of `m` months
+  ##
+  ## ``echo getTime() + 2.months``
+  initInterval(0,0,0,0,0,`mod`(m,12),`div`(m,12))
+
+proc years*(y: int): TimeInterval {.inline.} =
+  ## TimeInterval of `y` years
+  ##
+  ## ``echo getTime() + 2.years``
+  initInterval(0,0,0,0,0,0,y)
+
+proc `+=`*(t: var Time, ti: TimeInterval) =
+  ## modifies `t` by adding the interval `ti`
+  t = timeInfoToTime(getLocalTime(t) + ti)
+
+proc `+`*(t: Time, ti: TimeInterval): Time =
+  ## adds the interval `ti` to Time `t`
+  ## by converting to localTime, adding the interval, and converting back
+  ##
+  ## ``echo getTime() + 1.day``
+  result = timeInfoToTime(getLocalTime(t) + ti)
+
+proc `-=`*(t: var Time, ti: TimeInterval) =
+  ## modifies `t` by subtracting the interval `ti`
+  t = timeInfoToTime(getLocalTime(t) - ti)
+
+proc `-`*(t: Time, ti: TimeInterval): Time =
+  ## adds the interval `ti` to Time `t`
+  ##
+  ## ``echo getTime() - 1.day``
+  result = timeInfoToTime(getLocalTime(t) - ti)
+
 proc formatToken(info: TimeInfo, token: string, buf: var string) =
   ## Helper of the format proc to parse individual tokens.
   ##
diff --git a/lib/system.nim b/lib/system.nim
index ce7687c34..bb8254364 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -840,7 +840,7 @@ proc `div` *(x, y: int32): int32 {.magic: "DivI", noSideEffect.}
   ##   1 div 2 == 0
   ##   2 div 2 == 1
   ##   3 div 2 == 1
-  ##   7 div 5 == 2
+  ##   7 div 5 == 1
 
 when defined(nimnomagic64):
   proc `div` *(x, y: int64): int64 {.magic: "DivI", noSideEffect.}
diff --git a/lib/system/dyncalls.nim b/lib/system/dyncalls.nim
index 22ac613f8..6dc8999d1 100644
--- a/lib/system/dyncalls.nim
+++ b/lib/system/dyncalls.nim
@@ -68,9 +68,10 @@ when defined(posix):
 
   proc nimLoadLibrary(path: string): LibHandle =
     result = dlopen(path, RTLD_NOW)
-    let error = dlerror()
-    if error != nil:
-      c_fprintf(c_stdout, "%s\n", error)
+    when defined(nimDebugDlOpen):
+      let error = dlerror()
+      if error != nil:
+        c_fprintf(c_stdout, "%s\n", error)
 
   proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr =
     result = dlsym(lib, name)
diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim
index 54c6796c9..5bac54772 100644
--- a/lib/system/jssys.nim
+++ b/lib/system/jssys.nim
@@ -533,15 +533,20 @@ proc nimCopy(dest, src: JSRef, ti: PNimType): JSRef =
     nimCopyAux(result, src, ti.node)
   of tySequence, tyArrayConstr, tyOpenArray, tyArray:
     asm """
-      if (`dest` === null || `dest` === undefined) {
-        `dest` = new Array(`src`.length);
+      if (`src` === null) {
+        `result` = null;
       }
       else {
-        `dest`.length = `src`.length;
-      }
-      `result` = `dest`;
-      for (var i = 0; i < `src`.length; ++i) {
-        `result`[i] = nimCopy(`result`[i], `src`[i], `ti`.base);
+        if (`dest` === null || `dest` === undefined) {
+          `dest` = new Array(`src`.length);
+        }
+        else {
+          `dest`.length = `src`.length;
+        }
+        `result` = `dest`;
+        for (var i = 0; i < `src`.length; ++i) {
+          `result`[i] = nimCopy(`result`[i], `src`[i], `ti`.base);
+        }
       }
     """
   of tyString:
diff --git a/lib/system/nimscript.nim b/lib/system/nimscript.nim
index aaba11324..772d25343 100644
--- a/lib/system/nimscript.nim
+++ b/lib/system/nimscript.nim
@@ -242,7 +242,7 @@ template task*(name: untyped; description: string; body: untyped): untyped =
   ## .. code-block:: nim
   ##  task build, "default build is via the C backend":
   ##    setCommand "c"
-  proc `name Task`() = body
+  proc `name Task`*() = body
 
   let cmd = getCommand()
   if cmd.len == 0 or cmd ==? "help":
diff --git a/lib/system/profiler.nim b/lib/system/profiler.nim
index 4f600417e..ae8ff4e19 100644
--- a/lib/system/profiler.nim
+++ b/lib/system/profiler.nim
@@ -50,10 +50,15 @@ proc captureStackTrace(f: PFrame, st: var StackTrace) =
     inc(i)
     b = b.prev
 
+var
+  profilingRequestedHook*: proc (): bool {.nimcall, benign.}
+    ## set this variable to provide a procedure that implements a profiler in
+    ## user space. See the `nimprof` module for a reference implementation.
+
 when defined(memProfiler):
   type
     MemProfilerHook* = proc (st: StackTrace, requestedSize: int) {.nimcall, benign.}
-  {.deprecated: [TMemProfilerHook: MemProfilerHook].}
+
   var
     profilerHook*: MemProfilerHook
       ## set this variable to provide a procedure that implements a profiler in
@@ -65,17 +70,13 @@ when defined(memProfiler):
     hook(st, requestedSize)
 
   proc nimProfile(requestedSize: int) =
-    if not isNil(profilerHook):
+    if not isNil(profilingRequestedHook) and profilingRequestedHook():
       callProfilerHook(profilerHook, requestedSize)
 else:
-  const
-    SamplingInterval = 50_000
-      # set this to change the default sampling interval
   var
     profilerHook*: ProfilerHook
       ## set this variable to provide a procedure that implements a profiler in
       ## user space. See the `nimprof` module for a reference implementation.
-    gTicker {.threadvar.}: int
 
   proc callProfilerHook(hook: ProfilerHook) {.noinline.} =
     # 'noinline' so that 'nimProfile' does not perform the stack allocation
@@ -86,16 +87,7 @@ else:
 
   proc nimProfile() =
     ## This is invoked by the compiler in every loop and on every proc entry!
-    if gTicker == 0:
-      gTicker = -1
-      if not isNil(profilerHook):
-        # disable recursive calls: XXX should use try..finally,
-        # but that's too expensive!
-        let oldHook = profilerHook
-        profilerHook = nil
-        callProfilerHook(oldHook)
-        profilerHook = oldHook
-      gTicker = SamplingInterval
-    dec gTicker
+    if not isNil(profilingRequestedHook) and profilingRequestedHook():
+      callProfilerHook(profilerHook)
 
 {.pop.}
diff --git a/lib/system/repr.nim b/lib/system/repr.nim
index 1f81a0813..986994203 100644
--- a/lib/system/repr.nim
+++ b/lib/system/repr.nim
@@ -259,8 +259,10 @@ when not defined(useNimRtl):
     of tyInt16: add result, $int(cast[ptr int16](p)[])
     of tyInt32: add result, $int(cast[ptr int32](p)[])
     of tyInt64: add result, $(cast[ptr int64](p)[])
-    of tyUInt8: add result, $ze(cast[ptr int8](p)[])
-    of tyUInt16: add result, $ze(cast[ptr int16](p)[])
+    of tyUInt8: add result, $(cast[ptr uint8](p)[])
+    of tyUInt16: add result, $(cast[ptr uint16](p)[])
+    of tyUInt32: add result, $(cast[ptr uint32](p)[])
+    of tyUInt64: add result, $(cast[ptr uint64](p)[])
 
     of tyFloat: add result, $(cast[ptr float](p)[])
     of tyFloat32: add result, $(cast[ptr float32](p)[])
diff --git a/lib/wrappers/linenoise/clinenoise.c b/lib/wrappers/linenoise/clinenoise.c
index b4ae32472..a03e6f379 100644
--- a/lib/wrappers/linenoise/clinenoise.c
+++ b/lib/wrappers/linenoise/clinenoise.c
@@ -139,7 +139,7 @@ struct linenoiseState {
     int ofd;            /* Terminal stdout file descriptor. */
     char *buf;          /* Edited line buffer. */
     size_t buflen;      /* Edited line buffer size. */
-    const char *prompt; /* Prompt to display. */
+    char *prompt; /* Prompt to display. */
     size_t plen;        /* Prompt length. */
     size_t pos;         /* Current cursor position. */
     size_t oldpos;      /* Previous refresh cursor position. */
@@ -172,7 +172,7 @@ enum KEY_ACTION{
 };
 
 static void linenoiseAtExit(void);
-int linenoiseHistoryAdd(const char *line);
+int linenoiseHistoryAdd(char *line);
 static void refreshLine(struct linenoiseState *l);
 
 /* Debugging macro. */
@@ -413,14 +413,14 @@ void linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn) {
  * in order to add completion options given the input string when the
  * user typed <tab>. See the example.c source code for a very easy to
  * understand example. */
-void linenoiseAddCompletion(linenoiseCompletions *lc, const char *str) {
+void linenoiseAddCompletion(linenoiseCompletions *lc, char *str) {
     size_t len = strlen(str);
     char *copy, **cvec;
 
-    copy = malloc(len+1);
+    copy = (char*)malloc(len+1);
     if (copy == NULL) return;
     memcpy(copy,str,len+1);
-    cvec = realloc(lc->cvec,sizeof(char*)*(lc->len+1));
+    cvec = (char**)realloc(lc->cvec,sizeof(char*)*(lc->len+1));
     if (cvec == NULL) {
         free(copy);
         return;
@@ -445,12 +445,12 @@ static void abInit(struct abuf *ab) {
     ab->len = 0;
 }
 
-static void abAppend(struct abuf *ab, const char *s, int len) {
-    char *new = realloc(ab->b,ab->len+len);
+static void abAppend(struct abuf *ab, char *s, int len) {
+    char *neww = (char*)realloc(ab->b,ab->len+len);
 
-    if (new == NULL) return;
-    memcpy(new+ab->len,s,len);
-    ab->b = new;
+    if (neww == NULL) return;
+    memcpy(neww+ab->len,s,len);
+    ab->b = neww;
     ab->len += len;
 }
 
@@ -723,7 +723,7 @@ void linenoiseEditDeletePrevWord(struct linenoiseState *l) {
  * when ctrl+d is typed.
  *
  * The function returns the length of the current buffer. */
-static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen, const char *prompt)
+static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen, char *prompt)
 {
     struct linenoiseState l;
 
@@ -929,7 +929,7 @@ void linenoisePrintKeyCodes(void) {
 
 /* This function calls the line editing function linenoiseEdit() using
  * the STDIN file descriptor set in raw mode. */
-static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) {
+static int linenoiseRaw(char *buf, size_t buflen, char *prompt) {
     int count;
 
     if (buflen == 0) {
@@ -959,7 +959,7 @@ static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) {
  * for a blacklist of stupid terminals, and later either calls the line
  * editing function or uses dummy fgets() so that you will be able to type
  * something even in the most desperate of the conditions. */
-char *linenoise(const char *prompt) {
+char *linenoise(char *prompt) {
     char buf[LINENOISE_MAX_LINE];
     int count;
 
@@ -1009,14 +1009,14 @@ static void linenoiseAtExit(void) {
  * histories, but will work well for a few hundred of entries.
  *
  * Using a circular buffer is smarter, but a bit more complex to handle. */
-int linenoiseHistoryAdd(const char *line) {
+int linenoiseHistoryAdd(char *line) {
     char *linecopy;
 
     if (history_max_len == 0) return 0;
 
     /* Initialization on first call. */
     if (history == NULL) {
-        history = malloc(sizeof(char*)*history_max_len);
+        history = (char**)malloc(sizeof(char*)*history_max_len);
         if (history == NULL) return 0;
         memset(history,0,(sizeof(char*)*history_max_len));
     }
@@ -1043,14 +1043,14 @@ int linenoiseHistoryAdd(const char *line) {
  * just the latest 'len' elements if the new history length value is smaller
  * than the amount of items already inside the history. */
 int linenoiseHistorySetMaxLen(int len) {
-    char **new;
+    char **neww;
 
     if (len < 1) return 0;
     if (history) {
         int tocopy = history_len;
 
-        new = malloc(sizeof(char*)*len);
-        if (new == NULL) return 0;
+        neww = (char**)malloc(sizeof(char*)*len);
+        if (neww == NULL) return 0;
 
         /* If we can't copy everything, free the elements we'll not use. */
         if (len < tocopy) {
@@ -1059,10 +1059,10 @@ int linenoiseHistorySetMaxLen(int len) {
             for (j = 0; j < tocopy-len; j++) free(history[j]);
             tocopy = len;
         }
-        memset(new,0,sizeof(char*)*len);
-        memcpy(new,history+(history_len-tocopy), sizeof(char*)*tocopy);
+        memset(neww,0,sizeof(char*)*len);
+        memcpy(neww,history+(history_len-tocopy), sizeof(char*)*tocopy);
         free(history);
-        history = new;
+        history = neww;
     }
     history_max_len = len;
     if (history_len > history_max_len)
@@ -1072,7 +1072,7 @@ int linenoiseHistorySetMaxLen(int len) {
 
 /* Save the history in the specified file. On success 0 is returned
  * otherwise -1 is returned. */
-int linenoiseHistorySave(const char *filename) {
+int linenoiseHistorySave(char *filename) {
     FILE *fp = fopen(filename,"w");
     int j;
 
@@ -1088,7 +1088,7 @@ int linenoiseHistorySave(const char *filename) {
  *
  * If the file exists and the operation succeeded 0 is returned, otherwise
  * on error -1 is returned. */
-int linenoiseHistoryLoad(const char *filename) {
+int linenoiseHistoryLoad(char *filename) {
     FILE *fp = fopen(filename,"r");
     char buf[LINENOISE_MAX_LINE];
 
diff --git a/lib/wrappers/linenoise/clinenoise.h b/lib/wrappers/linenoise/clinenoise.h
index fbb01cfaa..a15845f86 100644
--- a/lib/wrappers/linenoise/clinenoise.h
+++ b/lib/wrappers/linenoise/clinenoise.h
@@ -39,30 +39,22 @@
 #ifndef __LINENOISE_H
 #define __LINENOISE_H
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 typedef struct linenoiseCompletions {
   size_t len;
   char **cvec;
 } linenoiseCompletions;
 
-typedef void(linenoiseCompletionCallback)(const char *, linenoiseCompletions *);
+typedef void(linenoiseCompletionCallback)(char *, linenoiseCompletions *);
 void linenoiseSetCompletionCallback(linenoiseCompletionCallback *);
-void linenoiseAddCompletion(linenoiseCompletions *, const char *);
+void linenoiseAddCompletion(linenoiseCompletions *, char *);
 
-char *linenoise(const char *prompt);
-int linenoiseHistoryAdd(const char *line);
+char *linenoise(char *prompt);
+int linenoiseHistoryAdd(char *line);
 int linenoiseHistorySetMaxLen(int len);
-int linenoiseHistorySave(const char *filename);
-int linenoiseHistoryLoad(const char *filename);
+int linenoiseHistorySave(char *filename);
+int linenoiseHistoryLoad(char *filename);
 void linenoiseClearScreen(void);
 void linenoiseSetMultiLine(int ml);
 void linenoisePrintKeyCodes(void);
 
-#ifdef __cplusplus
-}
-#endif
-
 #endif /* __LINENOISE_H */
diff --git a/lib/wrappers/mysql.nim b/lib/wrappers/mysql.nim
index 8253e53a5..af504864d 100644
--- a/lib/wrappers/mysql.nim
+++ b/lib/wrappers/mysql.nim
@@ -418,6 +418,7 @@ type
     decimals*: cuint          # Number of decimals in field
     charsetnr*: cuint         # Character set
     ftype*: Enum_field_types  # Type of field. See mysql_com.h for types
+    extension*: pointer
 
   FIELD* = St_mysql_field
   PFIELD* = ptr FIELD
diff --git a/lib/wrappers/odbcsql.nim b/lib/wrappers/odbcsql.nim
index 43ad80f76..1b2544ec0 100644
--- a/lib/wrappers/odbcsql.nim
+++ b/lib/wrappers/odbcsql.nim
@@ -641,11 +641,42 @@ const
   ODBC_CONFIG_SYS_DSN* = 5
   ODBC_REMOVE_SYS_DSN* = 6
 
+  SQL_ACTIVE_CONNECTIONS* = 0   # SQLGetInfo
+  SQL_DATA_SOURCE_NAME* = 2
+  SQL_DATA_SOURCE_READ_ONLY* = 25
+  SQL_DATABASE_NAME* = 2
+  SQL_DBMS_NAME* = 17
+  SQL_DBMS_VERSION* = 18
+  SQL_DRIVER_HDBC* = 3
+  SQL_DRIVER_HENV* = 4
+  SQL_DRIVER_HSTMT* = 5
+  SQL_DRIVER_NAME* = 6
+  SQL_DRIVER_VER* = 7
+  SQL_FETCH_DIRECTION* = 8
+  SQL_ODBC_VER* = 10
+  SQL_DRIVER_ODBC_VER* = 77
+  SQL_SERVER_NAME* = 13
+  SQL_ACTIVE_ENVIRONMENTS* = 116
+  SQL_ACTIVE_STATEMENTS* = 1
+  SQL_SQL_CONFORMANCE* = 118
+  SQL_DATETIME_LITERALS* = 119
+  SQL_ASYNC_MODE* = 10021
+  SQL_BATCH_ROW_COUNT* = 120
+  SQL_BATCH_SUPPORT* = 121
+  SQL_CATALOG_LOCATION* = 114
+  #SQL_CATALOG_NAME* = 10003
+  SQL_CATALOG_NAME_SEPARATOR* = 41
+  SQL_CATALOG_TERM* = 42
+  SQL_CATALOG_USAGE* = 92
+  #SQL_COLLATION_SEQ* = 10004
+  SQL_COLUMN_ALIAS* = 87
+  #SQL_USER_NAME* = 47
+
 proc SQLAllocHandle*(HandleType: TSqlSmallInt, InputHandle: SqlHandle,
                      OutputHandlePtr: var SqlHandle): TSqlSmallInt{.
     dynlib: odbclib, importc.}
 proc SQLSetEnvAttr*(EnvironmentHandle: SqlHEnv, Attribute: TSqlInteger,
-                    Value: SqlPointer, StringLength: TSqlInteger): TSqlSmallInt{.
+                    Value: TSqlInteger, StringLength: TSqlInteger): TSqlSmallInt{.
     dynlib: odbclib, importc.}
 proc SQLGetEnvAttr*(EnvironmentHandle: SqlHEnv, Attribute: TSqlInteger,
                     Value: SqlPointer, BufferLength: TSqlInteger,
@@ -807,5 +838,10 @@ proc SQLStatistics*(hstmt: SqlHStmt, CatalogName: PSQLCHAR,
                     NameLength3: TSqlSmallInt, Unique: SqlUSmallInt,
                     Reserved: SqlUSmallInt): TSqlSmallInt {.
                     dynlib: odbclib, importc.}
+proc SQLErr*(henv: SqlHEnv, hdbc: SqlHDBC, hstmt: SqlHStmt,
+              szSqlState, pfNativeError, szErrorMsg: PSQLCHAR,
+              cbErrorMsgMax: TSqlSmallInt,
+              pcbErrorMsg: PSQLINTEGER): TSqlSmallInt {.
+                    dynlib: odbclib, importc: "SQLError".}
 
 {.pop.}
diff --git a/lib/wrappers/sqlite3.nim b/lib/wrappers/sqlite3.nim
index c5019960c..e7fd2bc36 100644
--- a/lib/wrappers/sqlite3.nim
+++ b/lib/wrappers/sqlite3.nim
@@ -239,6 +239,8 @@ proc column_count*(pStmt: Pstmt): int32{.cdecl, dynlib: Lib,
     importc: "sqlite3_column_count".}
 proc column_name*(para1: Pstmt, para2: int32): cstring{.cdecl, dynlib: Lib,
     importc: "sqlite3_column_name".}
+proc column_table_name*(para1: Pstmt; para2: int32): cstring{.cdecl, dynlib: Lib,
+    importc: "sqlite3_column_table_name".}
 proc column_name16*(para1: Pstmt, para2: int32): pointer{.cdecl, dynlib: Lib,
     importc: "sqlite3_column_name16".}
 proc column_decltype*(para1: Pstmt, i: int32): cstring{.cdecl, dynlib: Lib,