Repository: mattn/go-sqlite3
Pull request #558 (2018-05-12 00:55:00)

Adds GetFilename (sqlite3_db_filename) to SqliteConn
Enables a user with a `SqliteConn` reference to find out, among other things, what it's a connection to without having to manually track connection handles. Also very handy for diagnostic purposes.

coveralls: 
[![Coverage Status](https://coveralls.io/builds/16949233/badge)](https://coveralls.io/builds/16949233)

Coverage decreased (-0.3%) to 67.788% when pulling **03710cee1d290cbbb5eac5680d3e4e00eb3abd14 on zgramana:enable-db-filename** into **a72efd674f654961a11ac41aae72c0e43963fe6c on mattn:master**.

coveralls: 
[![Coverage Status](https://coveralls.io/builds/16949233/badge)](https://coveralls.io/builds/16949233)

Coverage decreased (-0.3%) to 67.788% when pulling **03710cee1d290cbbb5eac5680d3e4e00eb3abd14 on zgramana:enable-db-filename** into **a72efd674f654961a11ac41aae72c0e43963fe6c on mattn:master**.


Patch:
diff --git a/.gitignore b/.gitignore
index 8a0e48d..fa0e6b5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,13 @@
 *.exe
 *.dll
 *.o
+
+# VSCode
+.vscode
+
+# Exclude from upgrade
+upgrade/*.c
+upgrade/*.h
+
+# Exclude upgrade binary
+upgrade/upgrade
diff --git a/.travis.yml b/.travis.yml
index 46e70cb..91b61c4 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,19 +1,102 @@
 language: go
-sudo: required
-dist: trusty
+
+os:
+  - linux
+  - osx
+
+addons:
+  apt:
+    update: true
+
 env:
-  - GOTAGS=
-  - GOTAGS=libsqlite3
-  - GOTAGS=trace
-  - GOTAGS=vtable
+  matrix:
+    - GOTAGS=
+    - GOTAGS=libsqlite3
+    - GOTAGS="sqlite_allow_uri_authority sqlite_app_armor sqlite_foreign_keys sqlite_fts5 sqlite_icu sqlite_introspect sqlite_json sqlite_secure_delete sqlite_see sqlite_stat4 sqlite_trace sqlite_userauth"
+    - GOTAGS=sqlite_vacuum_full
+    - GOTAGS=sqlite_vacuum_incr
+    - GOTAGS=sqlite_vtable
+
 go:
-  - 1.7.x
   - 1.8.x
   - 1.9.x
-  - master
+  - 1.10.x
+
+matrix:
+  include:
+    - os: linux
+      go: 1.8.x
+      env: GOTAGS= GOOS=windows GOARCH=amd64
+    - os: linux
+      go: 1.8.x
+      env: GOTAGS=libsqlite3 GOOS=windows GOARCH=amd64
+    - os: linux
+      go: 1.8.x
+      env: GOTAGS="sqlite_allow_uri_authority sqlite_app_armor sqlite_foreign_keys sqlite_fts5 sqlite_icu sqlite_introspect sqlite_json sqlite_secure_delete sqlite_see sqlite_stat4 sqlite_trace sqlite_userauth" GOOS=windows GOARCH=amd64
+    - os: linux
+      go: 1.8.x
+      env: GOTAGS=sqlite_vacuum_full GOOS=windows GOARCH=amd64
+    - os: linux
+      go: 1.8.x
+      env: GOTAGS=sqlite_vacuum_incr GOOS=windows GOARCH=amd64
+    - os: linux
+      go: 1.8.x
+      env: GOTAGS=sqlite_vtable GOOS=windows GOARCH=amd64
+    - os: linux
+      go: 1.9.x
+      env: GOTAGS= GOOS=windows GOARCH=amd64
+    - os: linux
+      go: 1.9.x
+      env: GOTAGS=libsqlite3 GOOS=windows GOARCH=amd64
+    - os: linux
+      go: 1.9.x
+      env: GOTAGS="sqlite_allow_uri_authority sqlite_app_armor sqlite_foreign_keys sqlite_fts5 sqlite_icu sqlite_introspect sqlite_json sqlite_secure_delete sqlite_see sqlite_stat4 sqlite_trace sqlite_userauth" GOOS=windows GOARCH=amd64
+    - os: linux
+      go: 1.9.x
+      env: GOTAGS=sqlite_vacuum_full GOOS=windows GOARCH=amd64
+    - os: linux
+      go: 1.9.x
+      env: GOTAGS=sqlite_vacuum_incr GOOS=windows GOARCH=amd64
+    - os: linux
+      go: 1.9.x
+      env: GOTAGS=sqlite_vtable GOOS=windows GOARCH=amd64
+    - os: linux
+      go: 1.10.x
+      env: GOTAGS= GOOS=windows GOARCH=amd64
+    - os: linux
+      go: 1.10.x
+      env: GOTAGS=libsqlite3 GOOS=windows GOARCH=amd64
+    - os: linux
+      go: 1.10.x
+      env: GOTAGS="sqlite_allow_uri_authority sqlite_app_armor sqlite_foreign_keys sqlite_fts5 sqlite_icu sqlite_introspect sqlite_json sqlite_secure_delete sqlite_see sqlite_stat4 sqlite_trace sqlite_userauth" GOOS=windows GOARCH=amd64
+    - os: linux
+      go: 1.10.x
+      env: GOTAGS=sqlite_vacuum_full GOOS=windows GOARCH=amd64
+    - os: linux
+      go: 1.10.x
+      env: GOTAGS=sqlite_vacuum_incr GOOS=windows GOARCH=amd64
+    - os: linux
+      go: 1.10.x
+      env: GOTAGS=sqlite_vtable GOOS=windows GOARCH=amd64
+
+
 before_install:
-  - go get github.com/mattn/goveralls
-  - go get golang.org/x/tools/cmd/cover
+  - |
+    if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then 
+      brew update
+      brew upgrade icu4c
+    fi
+  - |
+    go get github.com/smartystreets/goconvey
+    if [[ "${GOOS}" != "windows" ]]; then
+      go get github.com/mattn/goveralls
+      go get golang.org/x/tools/cmd/cover
+    fi
+
 script:
-  - $HOME/gopath/bin/goveralls -repotoken 3qJVUE0iQwqnCbmNcDsjYu1nh4J4KIFXx
-  - go test -race -v . -tags "$GOTAGS"
+  - GOOS=$(go env GOOS) GOARCH=$(go env GOARCH) go build -v -tags "${GOTAGS}" .
+  - |
+    if [[ "${GOOS}" != "windows" ]]; then
+      $HOME/gopath/bin/goveralls -repotoken 3qJVUE0iQwqnCbmNcDsjYu1nh4J4KIFXx
+      go test -race -v . -tags "${GOTAGS}"
+    fi
diff --git a/README.md b/README.md
index 4870adf..ca84ffd 100644
--- a/README.md
+++ b/README.md
@@ -6,13 +6,43 @@ go-sqlite3
 [![Coverage Status](https://coveralls.io/repos/mattn/go-sqlite3/badge.svg?branch=master)](https://coveralls.io/r/mattn/go-sqlite3?branch=master)
 [![Go Report Card](https://goreportcard.com/badge/github.com/mattn/go-sqlite3)](https://goreportcard.com/report/github.com/mattn/go-sqlite3)
 
-Description
------------
+# Description
 
 sqlite3 driver conforming to the built-in database/sql interface
 
-Installation
-------------
+Supported Golang version:
+- 1.8.x
+- 1.9.x
+- 1.10.x
+
+### Overview
+
+- [Installation](#installation)
+- [API Reference](#api-reference)
+- [Connection String](#connection-string)
+- [Features](#features)
+- [Compilation](#compilation)
+  - [Android](#android)
+  - [ARM](#arm)
+  - [Cross Compile](#cross-compile)
+  - [Google Cloud Platform](#google-cloud-platform)
+  - [Linux](#linux)
+    - [Alpine](#alpine)
+    - [Fedora](#fedora)
+    - [Ubuntu](#ubuntu)
+  - [Mac OSX](#mac-osx)
+  - [Windows](#windows)
+  - [Errors](#errors)
+- [User Authentication](#user-authentication)
+  - [Compile](#compile)
+  - [Usage](#usage)
+  - 
+- [Extensions](#extensions)
+  - [Spatialite](#spatialite)
+- [FAQ](#faq)
+- [License](#license)
+
+# Installation
 
 This package can be installed with the go get command:
 
@@ -22,56 +52,408 @@ _go-sqlite3_ is *cgo* package.
 If you want to build your app using go-sqlite3, you need gcc.
 However, after you have built and installed _go-sqlite3_ with `go install github.com/mattn/go-sqlite3` (which requires gcc), you can build your app without relying on gcc in future.
 
-Documentation
--------------
+***Important: because this is a `CGO` enabled package you are required to set the environment variable `CGO_ENABLED=1` and have a `gcc` compile present within your path.***
+
+# API Reference
 
 API documentation can be found here: http://godoc.org/github.com/mattn/go-sqlite3
 
-Examples can be found under the `./_example` directory
+Examples can be found under the [examples](./_example) directory
+
+# Connection String
+
+When creating a new SQLite database or connection to an existing one, with the file name additional options can be given.
+This is also known as a DSN string. (Data Source Name).
+
+Options are append after the filename of the SQLite database.
+The database filename and options are seperated by an `?` (Question Mark).
+
+This also applies when using an in-memory database instead of a file.
+
+Options can be given using the following format: `KEYWORD=VALUE` and multiple options can be combined with the `&` ampersand.
+
+This library supports dsn options of SQLite itself and provides additional options.
+
+Boolean values can be one of:
+* `0` `no` `false` `off`
+* `1` `yes` `true` `on`
+
+| Name | Key | Value(s) | Description |
+|------|-----|----------|-------------|
+| UA - Create | `_auth` | - | Create User Authentication, for more information see [User Authentication](#user-authentication) |
+| UA - Username | `_auth_user` | `string` | Username for User Authentication, for more information see [User Authentication](#user-authentication) |
+| UA - Password | `_auth_pass` | `string` | Password for User Authentication, for more information see [User Authentication](#user-authentication) |
+| UA - Crypt | `_auth_crypt` | <ul><li>SHA1</li><li>SSHA1</li><li>SHA256</li><li>SSHA256</li><li>SHA384</li><li>SSHA384</li><li>SHA512</li><li>SSHA512</li></ul> | Password encoder to use for User Authentication, for more information see [User Authentication](#user-authentication) |
+| UA - Salt | `_auth_salt` | `string` | Salt to use if the configure password encoder requires a salt, for User Authentication, for more information see [User Authentication](#user-authentication) |
+| Auto Vacuum | `_auto_vacuum` \| `_vacuum` | <ul><li>`0` \| `none`</li><li>`1` \| `full`</li><li>`2` \| `incremental`</li></ul> | For more information see [PRAGMA auto_vacuum](https://www.sqlite.org/pragma.html#pragma_auto_vacuum) |
+| Busy Timeout | `_busy_timeout` \| `_timeout` | `int` | Specify value for sqlite3_busy_timeout. For more information see [PRAGMA busy_timeout](https://www.sqlite.org/pragma.html#pragma_busy_timeout) |
+| Case Sensitive LIKE | `_case_sensitive_like` \| `_cslike` | `boolean` | For more information see [PRAGMA case_sensitive_like](https://www.sqlite.org/pragma.html#pragma_case_sensitive_like) |
+| Defer Foreign Keys | `_defer_foreign_keys` \| `_defer_fk` | `boolean` | For more information see [PRAGMA defer_foreign_keys](https://www.sqlite.org/pragma.html#pragma_defer_foreign_keys) |
+| Foreign Keys | `_foreign_keys` \| `_fk` | `boolean` | For more information see [PRAGMA foreign_keys](https://www.sqlite.org/pragma.html#pragma_foreign_keys) |
+| Ignore CHECK Constraints | `_ignore_check_constraints` | `boolean` | For more information see [PRAGMA ignore_check_constraints](https://www.sqlite.org/pragma.html#pragma_ignore_check_constraints) |
+| Immutable | `immutable` | `boolean` | For more information see [Immutable](https://www.sqlite.org/c3ref/open.html) |
+| Journal Mode | `_journal_mode` \| `_journal` | <ul><li>DELETE</li><li>TRUNCATE</li><li>PERSIST</li><li>MEMORY</li><li>WAL</li><li>OFF</li></ul> | For more information see [PRAGMA journal_mode](https://www.sqlite.org/pragma.html#pragma_journal_mode) |
+| Locking Mode | `_locking_mode` \| `_locking` | <ul><li>NORMAL</li><li>EXCLUSIVE</li></ul> | For more information see [PRAGMA locking_mode](https://www.sqlite.org/pragma.html#pragma_locking_mode) |
+| Mode | `mode` | <ul><li>ro</li><li>rw</li><li>rwc</li><li>memory</li></ul> | Access Mode of the database. For more information see [SQLite Open](https://www.sqlite.org/c3ref/open.html) |
+| Mutex Locking | `_mutex` | <ul><li>no</li><li>full</li></ul> | Specify mutex mode. |
+| Query Only | `_query_only` | `boolean` | For more information see [PRAGMA query_only](https://www.sqlite.org/pragma.html#pragma_query_only) |
+| Recursive Triggers | `_recursive_triggers` \| `_rt` | `boolean` | For more information see [PRAGMA recursive_triggers](https://www.sqlite.org/pragma.html#pragma_recursive_triggers) |
+| Secure Delete | `_secure_delete` | `boolean` \| `FAST` | For more information see [PRAGMA secure_delete](https://www.sqlite.org/pragma.html#pragma_secure_delete) |
+| Shared-Cache Mode | `cache` | <ul><li>shared</li><li>private</li></ul> | Set cache mode for more information see [sqlite.org](https://www.sqlite.org/sharedcache.html) |
+| Synchronous | `_synchronous` \| `_sync` | <ul><li>0 \| OFF</li><li>1 \| NORMAL</li><li>2 \| FULL</li><li>3 \| EXTRA</li></ul> | For more information see [PRAGMA synchronous](https://www.sqlite.org/pragma.html#pragma_synchronous) |
+| Time Zone Location | `_loc` | auto | Specify location of time format. |
+| Transaction Lock | `_txlock` | <ul><li>immediate</li><li>deferred</li><li>exclusive</li></ul> | Specify locking behavior for transactions. |
+| Writable Schema | `_writable_schema` | `Boolean` | When this pragma is on, the SQLITE_MASTER tables in which database can be changed using ordinary UPDATE, INSERT, and DELETE statements. Warning: misuse of this pragma can easily result in a corrupt database file. |
+
+## DSN Examples
+
+```
+file:test.db?cache=shared&mode=memory
+```
+
+# Features
+
+This package allows additional configuration of features available within SQLite3 to be enabled or disabled by golang build constraints also known as build `tags`.
+
+[Click here for more information about build tags / constraints.](https://golang.org/pkg/go/build/#hdr-Build_Constraints)
+
+### Usage
+
+If you wish to build this library with additional extensions / features.
+Use the following command.
+
+```bash
+go build --tags "<FEATURE>"
+```
+
+For available features see the extension list.
+When using multiple build tags, all the different tags should be space delimted.
+
+Example:
+
+```bash
+go build --tags "icu json1 fts5 secure_delete"
+```
+
+### Feature / Extension List
+
+| Extension | Build Tag | Description |
+|-----------|-----------|-------------|
+| Additional Statistics | sqlite_stat4 | This option adds additional logic to the ANALYZE command and to the query planner that can help SQLite to chose a better query plan under certain situations. The ANALYZE command is enhanced to collect histogram data from all columns of every index and store that data in the sqlite_stat4 table.<br><br>The query planner will then use the histogram data to help it make better index choices. The downside of this compile-time option is that it violates the query planner stability guarantee making it more difficult to ensure consistent performance in mass-produced applications.<br><br>SQLITE_ENABLE_STAT4 is an enhancement of SQLITE_ENABLE_STAT3. STAT3 only recorded histogram data for the left-most column of each index whereas the STAT4 enhancement records histogram data from all columns of each index.<br><br>The SQLITE_ENABLE_STAT3 compile-time option is a no-op and is ignored if the SQLITE_ENABLE_STAT4 compile-time option is used |
+| Allow URI Authority | sqlite_allow_uri_authority | URI filenames normally throws an error if the authority section is not either empty or "localhost".<br><br>However, if SQLite is compiled with the SQLITE_ALLOW_URI_AUTHORITY compile-time option, then the URI is converted into a Uniform Naming Convention (UNC) filename and passed down to the underlying operating system that way |
+| App Armor | sqlite_app_armor | When defined, this C-preprocessor macro activates extra code that attempts to detect misuse of the SQLite API, such as passing in NULL pointers to required parameters or using objects after they have been destroyed. <br><br>App Armor is not available under `Windows`. |
+| Disable Load Extensions | sqlite_omit_load_extension | Loading of external extensions is enabled by default.<br><br>To disable extension loading add the build tag `sqlite_omit_load_extension`. |
+| Foreign Keys | sqlite_foreign_keys | This macro determines whether enforcement of foreign key constraints is enabled or disabled by default for new database connections.<br><br>Each database connection can always turn enforcement of foreign key constraints on and off and run-time using the foreign_keys pragma.<br><br>Enforcement of foreign key constraints is normally off by default, but if this compile-time parameter is set to 1, enforcement of foreign key constraints will be on by default | 
+| Full Auto Vacuum | sqlite_vacuum_full | Set the default auto vacuum to full |
+| Incremental Auto Vacuum | sqlite_vacuum_incr | Set the default auto vacuum to incremental |
+| Full Text Search Engine | sqlite_fts5 | When this option is defined in the amalgamation, versions 5 of the full-text search engine (fts5) is added to the build automatically |
+|  International Components for Unicode | sqlite_icu | This option causes the International Components for Unicode or "ICU" extension to SQLite to be added to the build |
+| Introspect PRAGMAS | sqlite_introspect | This option adds some extra PRAGMA statements. <ul><li>PRAGMA function_list</li><li>PRAGMA module_list</li><li>PRAGMA pragma_list</li></ul> |
+| JSON SQL Functions | sqlite_json | When this option is defined in the amalgamation, the JSON SQL functions are added to the build automatically |
+| Secure Delete | sqlite_secure_delete | This compile-time option changes the default setting of the secure_delete pragma.<br><br>When this option is not used, secure_delete defaults to off. When this option is present, secure_delete defaults to on.<br><br>The secure_delete setting causes deleted content to be overwritten with zeros. There is a small performance penalty since additional I/O must occur.<br><br>On the other hand, secure_delete can prevent fragments of sensitive information from lingering in unused parts of the database file after it has been deleted. See the documentation on the secure_delete pragma for additional information |
+| Secure Delete (FAST) | sqlite_secure_delete_fast | For more information see [PRAGMA secure_delete](https://www.sqlite.org/pragma.html#pragma_secure_delete) |
+| Tracing / Debug | sqlite_trace | Activate trace functions |
+| User Authentication | sqlite_userauth | SQLite User Authentication see [User Authentication](#user-authentication) for more information. |
+
+# Compilation
+
+This package requires `CGO_ENABLED=1` ennvironment variable if not set by default, and the presence of the `gcc` compiler.
+
+If you need to add additional CFLAGS or LDFLAGS to the build command, and do not want to modify this package. Then this can be achieved by  using the `CGO_CFLAGS` and `CGO_LDFLAGS` environment variables.
+
+## Android
+
+This package can be compiled for android.
+Compile with:
+
+```bash
+go build --tags "android"
+```
+
+For more information see [#201](https://github.com/mattn/go-sqlite3/issues/201)
+
+# ARM
+
+To compile for `ARM` use the following environment.
+
+```bash
+env CC=arm-linux-gnueabihf-gcc CXX=arm-linux-gnueabihf-g++ \
+    CGO_ENABLED=1 GOOS=linux GOARCH=arm GOARM=7 \
+    go build -v 
+```
+
+Additional information:
+- [#242](https://github.com/mattn/go-sqlite3/issues/242)
+- [#504](https://github.com/mattn/go-sqlite3/issues/504)
+
+# Cross Compile
+
+This library can be cross-compiled.
+
+In some cases you are required to the `CC` environment variable with the cross compiler.
+
+Additional information:
+- [#491](https://github.com/mattn/go-sqlite3/issues/491)
+- [#560](https://github.com/mattn/go-sqlite3/issues/560)
+
+# Google Cloud Platform
+
+Building on GCP is not possible because `Google Cloud Platform does not allow `gcc` to be executed.
+
+Please work only with compiled final binaries.
+
+## Linux
+
+To compile this package on Linux you must install the development tools for your linux distribution.
+
+To compile under linux use the build tag `linux`.
+
+```bash
+go build --tags "linux"
+```
+
+If you wish to link directly to libsqlite3 then you can use the `libsqlite3` build tag.
+
+```
+go build --tags "libsqlite3 linux"
+```
+
+### Alpine
+
+When building in an `alpine` container run the following command before building.
+
+```
+apk add --update gcc musl-dev
+```
+
+### Fedora
+
+```bash
+sudo yum groupinstall "Development Tools" "Development Libraries"
+```
+
+### Ubuntu
+
+```bash
+sudo apt-get install build-essential
+```
+
+## Mac OSX
+
+OSX should have all the tools present to compile this package, if not install XCode this will add all the developers tools.
+
+Required dependency
+
+```bash
+brew install sqlite3
+```
+
+For OSX there is an additional package install which is required if you whish to build the `icu` extension.
+
+This additional package can be installed with `homebrew`.
+
+```bash
+brew upgrade icu4c
+```
+
+To compile for Mac OSX.
+
+```bash
+go build --tags "darwin"
+```
 
-FAQ
----
+If you wish to link directly to libsqlite3 then you can use the `libsqlite3` build tag.
 
-* Want to build go-sqlite3 with libsqlite3 on my linux.
+```
+go build --tags "libsqlite3 darwin"
+```
 
-    Use `go build --tags "libsqlite3 linux"`
+Additional information:
+- [#206](https://github.com/mattn/go-sqlite3/issues/206)
+- [#404](https://github.com/mattn/go-sqlite3/issues/404)
 
-* Want to build go-sqlite3 with libsqlite3 on OS X.
+## Windows
 
-    Install sqlite3 from homebrew: `brew install sqlite3`
+To compile this package on Windows OS you must have the `gcc` compiler installed.
 
-    Use `go build --tags "libsqlite3 darwin"`
+1) Install a Windows `gcc` toolchain.
+2) Add the `bin` folders to the Windows path if the installer did not do this by default.
+3) Open a terminal for the TDM-GCC toolchain, can be found in the Windows Start menu.
+4) Navigate to your project folder and run the `go build ...` command for this package.
 
-* Want to build go-sqlite3 with icu extension.
+For example the TDM-GCC Toolchain can be found [here](ttps://sourceforge.net/projects/tdm-gcc/).
 
-   Use `go build --tags "icu"`
+## Errors
 
-   Available extensions: `json1`, `fts5`, `icu`
+- Compile error: `can not be used when making a shared object; recompile with -fPIC`
 
-* Can't build go-sqlite3 on windows 64bit.
+    When receiving a compile time error referencing recompile with `-FPIC` then you
+    are probably using a hardend system.
+
+    You can copile the library on a hardend system with the following command.
+
+    ```bash
+    go build -ldflags '-extldflags=-fno-PIC'
+    ```
+
+    More details see [#120](https://github.com/mattn/go-sqlite3/issues/120)
+
+- Can't build go-sqlite3 on windows 64bit.
 
     > Probably, you are using go 1.0, go1.0 has a problem when it comes to compiling/linking on windows 64bit.
     > See: [#27](https://github.com/mattn/go-sqlite3/issues/27)
 
-* Getting insert error while query is opened.
+- `go get github.com/mattn/go-sqlite3` throws compilation error.
+
+    `gcc` throws: `internal compiler error`
+
+    Remove the download repository from your disk and try re-install with:
+
+    ```bash
+    go install github.com/mattn/go-sqlite3
+    ```
+
+# User Authentication
+
+This package supports the SQLite User Authentication module.
+
+## Compile
+
+To use the User authentication module the package has to be compiled with the tag `sqlite_userauth`. See [Features](#features).
+
+## Usage
+
+### Create protected database
+
+To create a database protected by user authentication provide the following argument to the connection string `_auth`.
+This will enable user authentication within the database. This option however requires two additional arguments:
+
+- `_auth_user`
+- `_auth_pass`
+
+When `_auth` is present on the connection string user authentication will be enabled and the provided user will be created
+as an `admin` user. After initial creation, the parameter `_auth` has no effect anymore and can be omitted from the connection string.
+
+Example connection string:
+
+Create an user authentication database with user `admin` and password `admin`.
+
+`file:test.s3db?_auth&_auth_user=admin&_auth_pass=admin`
+
+Create an user authentication database with user `admin` and password `admin` and use `SHA1` for the password encoding.
+
+`file:test.s3db?_auth&_auth_user=admin&_auth_pass=admin&_auth_crypt=sha1`
+
+### Password Encoding
+
+The passwords within the user authentication module of SQLite are encoded with the SQLite function `sqlite_cryp`.
+This function uses a ceasar-cypher which is quite insecure.
+This library provides several additional password encoders which can be configured through the connection string.
+
+The password cypher can be configured with the key `_auth_crypt`. And if the configured password encoder also requires an
+salt this can be configured with `_auth_salt`.
+
+#### Available Encoders
+
+- SHA1
+- SSHA1 (Salted SHA1)
+- SHA256
+- SSHA256 (salted SHA256)
+- SHA384
+- SSHA384 (salted SHA384)
+- SHA512
+- SSHA512 (salted SHA512)
+
+### Restrictions
+
+Operations on the database regarding to user management can only be preformed by an administrator user.
+
+### Support
+
+The user authentication supports two kinds of users
+
+- administrators
+- regular users
+
+### User Management
+
+User management can be done by directly using the `*SQLiteConn` or by SQL.
+
+#### SQL
+
+The following sql functions are available for user management.
+
+| Function | Arguments | Description |
+|----------|-----------|-------------|
+| `authenticate` | username `string`, password `string` | Will authenticate an user, this is done by the connection; and should not be used manually. |
+| `auth_user_add` | username `string`, password `string`, admin `int` | This function will add an user to the database.<br>if the database is not protected by user authentication it will enable it. Argument `admin` is an integer identifying if the added user should be an administrator. Only Administrators can add administrators. |
+| `auth_user_change` | username `string`, password `string`, admin `int` | Function to modify an user. Users can change their own password, but only an administrator can change the administrator flag. |
+| `authUserDelete` | username `string` | Delete an user from the database. Can only be used by an administrator. The current logged in administrator cannot be deleted. This is to make sure their is always an administrator remaining. |
+
+These functions will return an integer.
+
+- 0 (SQLITE_OK)
+- 23 (SQLITE_AUTH) Failed to perform due to authentication or insufficient privileges
+
+##### Examples
+
+```sql
+// Autheticate user
+// Create Admin User
+SELECT auth_user_add('admin2', 'admin2', 1);
+
+// Change password for user
+SELECT auth_user_change('user', 'userpassword', 0);
+
+// Delete user
+SELECT user_delete('user');
+```
+
+#### *SQLiteConn
+
+The following functions are available for User authentication from the `*SQLiteConn`.
+
+| Function | Description |
+|----------|-------------|
+| `Authenticate(username, password string) error` | Authenticate user |
+| `AuthUserAdd(username, password string, admin bool) error` | Add user |
+| `AuthUserChange(username, password string, admin bool) error` | Modify user |
+| `AuthUserDelete(username string) error` | Delete user |
+
+### Attached database
+
+When using attached databases. SQLite will use the authentication from the `main` database for the attached database(s).
+
+# Extensions
+
+If you want your own extension to be listed here or you want to add a reference to an extension; please submit an Issue for this.
+
+## Spatialite
+
+Spatialite is available as an extension to SQLite, and can be used in combination with this repository.
+For an example see [shaxbee/go-spatialite](https://github.com/shaxbee/go-spatialite).
+
+# FAQ
+
+- Getting insert error while query is opened.
 
     > You can pass some arguments into the connection string, for example, a URI.
     > See: [#39](https://github.com/mattn/go-sqlite3/issues/39)
 
-* Do you want to cross compile? mingw on Linux or Mac?
+- Do you want to cross compile? mingw on Linux or Mac?
 
     > See: [#106](https://github.com/mattn/go-sqlite3/issues/106)
     > See also: http://www.limitlessfx.com/cross-compile-golang-app-for-windows-from-linux.html
 
-* Want to get time.Time with current locale
+- Want to get time.Time with current locale
 
     Use `_loc=auto` in SQLite3 filename schema like `file:foo.db?_loc=auto`.
 
-* Can I use this in multiple routines concurrently?
+- Can I use this in multiple routines concurrently?
 
     Yes for readonly. But, No for writable. See [#50](https://github.com/mattn/go-sqlite3/issues/50), [#51](https://github.com/mattn/go-sqlite3/issues/51), [#209](https://github.com/mattn/go-sqlite3/issues/209), [#274](https://github.com/mattn/go-sqlite3/issues/274).
 
-* Why is it racy if I use a `sql.Open("sqlite3", ":memory:")` database?
+- Why is it racy if I use a `sql.Open("sqlite3", ":memory:")` database?
 
     Each connection to :memory: opens a brand new in-memory sql database, so if
     the stdlib's sql engine happens to open another connection and you've only
@@ -80,10 +462,24 @@ FAQ
     connection to this string will point to the same in-memory database. See
     [#204](https://github.com/mattn/go-sqlite3/issues/204) for more info.
 
-License
--------
+- Reading from database with large amount of goroutines fails on OSX.
+
+    OS X limits OS-wide to not have more than 1000 files open simultaneously by default.
 
-MIT: http://mattn.mit-license.org/2012
+    For more information see [#289](https://github.com/mattn/go-sqlite3/issues/289)
+
+- Trying to execure a `.` (dot) command throws an error.
+
+    Error: `Error: near ".": syntax error`
+    Dot command are part of SQLite3 CLI not of this library.
+
+    You need to implement the feature or call the sqlite3 cli.
+
+    More infomation see [#305](https://github.com/mattn/go-sqlite3/issues/305)
+
+# License
+
+MIT: http://mattn.mit-license.org/2018
 
 sqlite3-binding.c, sqlite3-binding.h, sqlite3ext.h
 
@@ -91,7 +487,8 @@ The -binding suffix was added to avoid build failures under gccgo.
 
 In this repository, those files are an amalgamation of code that was copied from SQLite3. The license of that code is the same as the license of SQLite3.
 
-Author
-------
+# Author
 
 Yasuhiro Matsumoto (a.k.a mattn)
+
+G.J.R. Timmer
diff --git a/callback.go b/callback.go
index 29ece3d..5a735c0 100644
--- a/callback.go
+++ b/callback.go
@@ -331,8 +331,18 @@ func callbackRetText(ctx *C.sqlite3_context, v reflect.Value) error {
 	return nil
 }
 
+func callbackRetNil(ctx *C.sqlite3_context, v reflect.Value) error {
+	return nil
+}
+
 func callbackRet(typ reflect.Type) (callbackRetConverter, error) {
 	switch typ.Kind() {
+	case reflect.Interface:
+		errorInterface := reflect.TypeOf((*error)(nil)).Elem()
+		if typ.Implements(errorInterface) {
+			return callbackRetNil, nil
+		}
+		fallthrough
 	case reflect.Slice:
 		if typ.Elem().Kind() != reflect.Uint8 {
 			return nil, errors.New("the only supported slice type is []byte")
diff --git a/sqlite3-binding.c b/sqlite3-binding.c
index f3d13e7..cf030cf 100644
--- a/sqlite3-binding.c
+++ b/sqlite3-binding.c
@@ -209943,4 +209943,359 @@ SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; }
 #else // USE_LIBSQLITE3
  // If users really want to link against the system sqlite3 we
 // need to make this file a noop.
- #endif
\ No newline at end of file
+ #endif
+/*
+** 2014-09-08
+**
+** The author disclaims copyright to this source code.  In place of
+** a legal notice, here is a blessing:
+**
+**    May you do good and not evil.
+**    May you find forgiveness for yourself and forgive others.
+**    May you share freely, never taking more than you give.
+**
+*************************************************************************
+**
+** This file contains the bulk of the implementation of the
+** user-authentication extension feature.  Some parts of the user-
+** authentication code are contained within the SQLite core (in the
+** src/ subdirectory of the main source code tree) but those parts
+** that could reasonable be separated out are moved into this file.
+**
+** To compile with the user-authentication feature, append this file to
+** end of an SQLite amalgamation, then add the SQLITE_USER_AUTHENTICATION
+** compile-time option.  See the user-auth.txt file in the same source
+** directory as this file for additional information.
+*/
+#ifdef SQLITE_USER_AUTHENTICATION
+#ifndef SQLITEINT_H
+# include "sqliteInt.h"
+#endif
+
+/*
+** Prepare an SQL statement for use by the user authentication logic.
+** Return a pointer to the prepared statement on success.  Return a
+** NULL pointer if there is an error of any kind.
+*/
+static sqlite3_stmt *sqlite3UserAuthPrepare(
+  sqlite3 *db,
+  const char *zFormat,
+  ...
+){
+  sqlite3_stmt *pStmt;
+  char *zSql;
+  int rc;
+  va_list ap;
+  int savedFlags = db->flags;
+
+  va_start(ap, zFormat);
+  zSql = sqlite3_vmprintf(zFormat, ap);
+  va_end(ap);
+  if( zSql==0 ) return 0;
+  db->flags |= SQLITE_WriteSchema;
+  rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
+  db->flags = savedFlags;
+  sqlite3_free(zSql);
+  if( rc ){
+    sqlite3_finalize(pStmt);
+    pStmt = 0;
+  }
+  return pStmt;
+}
+
+/*
+** Check to see if the sqlite_user table exists in database zDb.
+*/
+static int userTableExists(sqlite3 *db, const char *zDb){
+  int rc;
+  sqlite3_mutex_enter(db->mutex);
+  sqlite3BtreeEnterAll(db);
+  if( db->init.busy==0 ){
+    char *zErr = 0;
+    sqlite3Init(db, &zErr);
+    sqlite3DbFree(db, zErr);
+  }
+  rc = sqlite3FindTable(db, "sqlite_user", zDb)!=0;
+  sqlite3BtreeLeaveAll(db);
+  sqlite3_mutex_leave(db->mutex);
+  return rc;
+}
+
+/*
+** Check to see if database zDb has a "sqlite_user" table and if it does
+** whether that table can authenticate zUser with nPw,zPw.  Write one of
+** the UAUTH_* user authorization level codes into *peAuth and return a
+** result code.
+*/
+static int userAuthCheckLogin(
+  sqlite3 *db,               /* The database connection to check */
+  const char *zDb,           /* Name of specific database to check */
+  u8 *peAuth                 /* OUT: One of UAUTH_* constants */
+){
+  sqlite3_stmt *pStmt;
+  int rc;
+
+  *peAuth = UAUTH_Unknown;
+  if( !userTableExists(db, "main") ){
+    *peAuth = UAUTH_Admin;  /* No sqlite_user table.  Everybody is admin. */
+    return SQLITE_OK;
+  }
+  if( db->auth.zAuthUser==0 ){
+    *peAuth = UAUTH_Fail;
+    return SQLITE_OK;
+  }
+  pStmt = sqlite3UserAuthPrepare(db,
+            "SELECT pw=sqlite_crypt(?1,pw), isAdmin FROM \"%w\".sqlite_user"
+            " WHERE uname=?2", zDb);
+  if( pStmt==0 ) return SQLITE_NOMEM;
+  sqlite3_bind_blob(pStmt, 1, db->auth.zAuthPW, db->auth.nAuthPW,SQLITE_STATIC);
+  sqlite3_bind_text(pStmt, 2, db->auth.zAuthUser, -1, SQLITE_STATIC);
+  rc = sqlite3_step(pStmt);
+  if( rc==SQLITE_ROW && sqlite3_column_int(pStmt,0) ){
+    *peAuth = sqlite3_column_int(pStmt, 1) + UAUTH_User;
+  }else{
+    *peAuth = UAUTH_Fail;
+  }
+  return sqlite3_finalize(pStmt);
+}
+int sqlite3UserAuthCheckLogin(
+  sqlite3 *db,               /* The database connection to check */
+  const char *zDb,           /* Name of specific database to check */
+  u8 *peAuth                 /* OUT: One of UAUTH_* constants */
+){
+  int rc;
+  u8 savedAuthLevel;
+  assert( zDb!=0 );
+  assert( peAuth!=0 );
+  savedAuthLevel = db->auth.authLevel;
+  db->auth.authLevel = UAUTH_Admin;
+  rc = userAuthCheckLogin(db, zDb, peAuth);
+  db->auth.authLevel = savedAuthLevel;
+  return rc;
+}
+
+/*
+** If the current authLevel is UAUTH_Unknown, the take actions to figure
+** out what authLevel should be
+*/
+void sqlite3UserAuthInit(sqlite3 *db){
+  if( db->auth.authLevel==UAUTH_Unknown ){
+    u8 authLevel = UAUTH_Fail;
+    sqlite3UserAuthCheckLogin(db, "main", &authLevel);
+    db->auth.authLevel = authLevel;
+    if( authLevel<UAUTH_Admin ) db->flags &= ~SQLITE_WriteSchema;
+  }
+}
+
+/*
+** Implementation of the sqlite_crypt(X,Y) function.
+**
+** If Y is NULL then generate a new hash for password X and return that
+** hash.  If Y is not null, then generate a hash for password X using the
+** same salt as the previous hash Y and return the new hash.
+*/
+void sqlite3CryptFunc(
+  sqlite3_context *context,
+  int NotUsed,
+  sqlite3_value **argv
+){
+  const char *zIn;
+  int nIn, ii;
+  u8 *zOut;
+  char zSalt[8];
+  zIn = sqlite3_value_blob(argv[0]);
+  nIn = sqlite3_value_bytes(argv[0]);
+  if( sqlite3_value_type(argv[1])==SQLITE_BLOB
+   && sqlite3_value_bytes(argv[1])==nIn+sizeof(zSalt)
+  ){
+    memcpy(zSalt, sqlite3_value_blob(argv[1]), sizeof(zSalt));
+  }else{
+    sqlite3_randomness(sizeof(zSalt), zSalt);
+  }
+  zOut = sqlite3_malloc( nIn+sizeof(zSalt) );
+  if( zOut==0 ){
+    sqlite3_result_error_nomem(context);
+  }else{
+    memcpy(zOut, zSalt, sizeof(zSalt));
+    for(ii=0; ii<nIn; ii++){
+      zOut[ii+sizeof(zSalt)] = zIn[ii]^zSalt[ii&0x7];
+    }
+    sqlite3_result_blob(context, zOut, nIn+sizeof(zSalt), sqlite3_free);
+  }
+}
+
+/*
+** If a database contains the SQLITE_USER table, then the
+** sqlite3_user_authenticate() interface must be invoked with an
+** appropriate username and password prior to enable read and write
+** access to the database.
+**
+** Return SQLITE_OK on success or SQLITE_ERROR if the username/password
+** combination is incorrect or unknown.
+**
+** If the SQLITE_USER table is not present in the database file, then
+** this interface is a harmless no-op returnning SQLITE_OK.
+*/
+int sqlite3_user_authenticate(
+  sqlite3 *db,           /* The database connection */
+  const char *zUsername, /* Username */
+  const char *zPW,       /* Password or credentials */
+  int nPW                /* Number of bytes in aPW[] */
+){
+  int rc;
+  u8 authLevel = UAUTH_Fail;
+  db->auth.authLevel = UAUTH_Unknown;
+  sqlite3_free(db->auth.zAuthUser);
+  sqlite3_free(db->auth.zAuthPW);
+  memset(&db->auth, 0, sizeof(db->auth));
+  db->auth.zAuthUser = sqlite3_mprintf("%s", zUsername);
+  if( db->auth.zAuthUser==0 ) return SQLITE_NOMEM;
+  db->auth.zAuthPW = sqlite3_malloc( nPW+1 );
+  if( db->auth.zAuthPW==0 ) return SQLITE_NOMEM;
+  memcpy(db->auth.zAuthPW,zPW,nPW);
+  db->auth.nAuthPW = nPW;
+  rc = sqlite3UserAuthCheckLogin(db, "main", &authLevel);
+  db->auth.authLevel = authLevel;
+  sqlite3ExpirePreparedStatements(db);
+  if( rc ){
+    return rc;           /* OOM error, I/O error, etc. */
+  }
+  if( authLevel<UAUTH_User ){
+    return SQLITE_AUTH;  /* Incorrect username and/or password */
+  }
+  return SQLITE_OK;      /* Successful login */
+}
+
+/*
+** The sqlite3_user_add() interface can be used (by an admin user only)
+** to create a new user.  When called on a no-authentication-required
+** database, this routine converts the database into an authentication-
+** required database, automatically makes the added user an
+** administrator, and logs in the current connection as that user.
+** The sqlite3_user_add() interface only works for the "main" database, not
+** for any ATTACH-ed databases.  Any call to sqlite3_user_add() by a
+** non-admin user results in an error.
+*/
+int sqlite3_user_add(
+  sqlite3 *db,           /* Database connection */
+  const char *zUsername, /* Username to be added */
+  const char *aPW,       /* Password or credentials */
+  int nPW,               /* Number of bytes in aPW[] */
+  int isAdmin            /* True to give new user admin privilege */
+){
+  sqlite3_stmt *pStmt;
+  int rc;
+  sqlite3UserAuthInit(db);
+  if( db->auth.authLevel<UAUTH_Admin ) return SQLITE_AUTH;
+  if( !userTableExists(db, "main") ){
+    if( !isAdmin ) return SQLITE_AUTH;
+    pStmt = sqlite3UserAuthPrepare(db, 
+              "CREATE TABLE sqlite_user(\n"
+              "  uname TEXT PRIMARY KEY,\n"
+              "  isAdmin BOOLEAN,\n"
+              "  pw BLOB\n"
+              ") WITHOUT ROWID;");
+    if( pStmt==0 ) return SQLITE_NOMEM;
+    sqlite3_step(pStmt);
+    rc = sqlite3_finalize(pStmt);
+    if( rc ) return rc;
+  }
+  pStmt = sqlite3UserAuthPrepare(db, 
+            "INSERT INTO sqlite_user(uname,isAdmin,pw)"
+            " VALUES(%Q,%d,sqlite_crypt(?1,NULL))",
+            zUsername, isAdmin!=0);
+  if( pStmt==0 ) return SQLITE_NOMEM;
+  sqlite3_bind_blob(pStmt, 1, aPW, nPW, SQLITE_STATIC);
+  sqlite3_step(pStmt);
+  rc = sqlite3_finalize(pStmt);
+  if( rc ) return rc;
+  if( db->auth.zAuthUser==0 ){
+    assert( isAdmin!=0 );
+    sqlite3_user_authenticate(db, zUsername, aPW, nPW);
+  }
+  return SQLITE_OK;
+}
+
+/*
+** The sqlite3_user_change() interface can be used to change a users
+** login credentials or admin privilege.  Any user can change their own
+** login credentials.  Only an admin user can change another users login
+** credentials or admin privilege setting.  No user may change their own 
+** admin privilege setting.
+*/
+int sqlite3_user_change(
+  sqlite3 *db,           /* Database connection */
+  const char *zUsername, /* Username to change */
+  const char *aPW,       /* Modified password or credentials */
+  int nPW,               /* Number of bytes in aPW[] */
+  int isAdmin            /* Modified admin privilege for the user */
+){
+  sqlite3_stmt *pStmt;
+  int rc;
+  u8 authLevel;
+
+  authLevel = db->auth.authLevel;
+  if( authLevel<UAUTH_User ){
+    /* Must be logged in to make a change */
+    return SQLITE_AUTH;
+  }
+  if( strcmp(db->auth.zAuthUser, zUsername)!=0 ){
+    if( db->auth.authLevel<UAUTH_Admin ){
+      /* Must be an administrator to change a different user */
+      return SQLITE_AUTH;
+    }
+  }else if( isAdmin!=(authLevel==UAUTH_Admin) ){
+    /* Cannot change the isAdmin setting for self */
+    return SQLITE_AUTH;
+  }
+  db->auth.authLevel = UAUTH_Admin;
+  if( !userTableExists(db, "main") ){
+    /* This routine is a no-op if the user to be modified does not exist */
+  }else{
+    pStmt = sqlite3UserAuthPrepare(db,
+              "UPDATE sqlite_user SET isAdmin=%d, pw=sqlite_crypt(?1,NULL)"
+              " WHERE uname=%Q", isAdmin, zUsername);
+    if( pStmt==0 ){
+      rc = SQLITE_NOMEM;
+    }else{
+      sqlite3_bind_blob(pStmt, 1, aPW, nPW, SQLITE_STATIC);
+      sqlite3_step(pStmt);
+      rc = sqlite3_finalize(pStmt);
+    }
+  }
+  db->auth.authLevel = authLevel;
+  return rc;
+}
+
+/*
+** The sqlite3_user_delete() interface can be used (by an admin user only)
+** to delete a user.  The currently logged-in user cannot be deleted,
+** which guarantees that there is always an admin user and hence that
+** the database cannot be converted into a no-authentication-required
+** database.
+*/
+int sqlite3_user_delete(
+  sqlite3 *db,           /* Database connection */
+  const char *zUsername  /* Username to remove */
+){
+  sqlite3_stmt *pStmt;
+  if( db->auth.authLevel<UAUTH_Admin ){
+    /* Must be an administrator to delete a user */
+    return SQLITE_AUTH;
+  }
+  if( strcmp(db->auth.zAuthUser, zUsername)==0 ){
+    /* Cannot delete self */
+    return SQLITE_AUTH;
+  }
+  if( !userTableExists(db, "main") ){
+    /* This routine is a no-op if the user to be deleted does not exist */
+    return SQLITE_OK;
+  }
+  pStmt = sqlite3UserAuthPrepare(db,
+              "DELETE FROM sqlite_user WHERE uname=%Q", zUsername);
+  if( pStmt==0 ) return SQLITE_NOMEM;
+  sqlite3_step(pStmt);
+  return sqlite3_finalize(pStmt);
+}
+
+#endif /* SQLITE_USER_AUTHENTICATION */
diff --git a/sqlite3-binding.h b/sqlite3-binding.h
index 4da4101..702a254 100644
--- a/sqlite3-binding.h
+++ b/sqlite3-binding.h
@@ -11179,4 +11179,100 @@ struct fts5_api {
 #else // USE_LIBSQLITE3
  // If users really want to link against the system sqlite3 we
 // need to make this file a noop.
- #endif
\ No newline at end of file
+ #endif
+/*
+** 2014-09-08
+**
+** The author disclaims copyright to this source code.  In place of
+** a legal notice, here is a blessing:
+**
+**    May you do good and not evil.
+**    May you find forgiveness for yourself and forgive others.
+**    May you share freely, never taking more than you give.
+**
+*************************************************************************
+**
+** This file contains the application interface definitions for the
+** user-authentication extension feature.
+**
+** To compile with the user-authentication feature, append this file to
+** end of an SQLite amalgamation header file ("sqlite3.h"), then add
+** the SQLITE_USER_AUTHENTICATION compile-time option.  See the
+** user-auth.txt file in the same source directory as this file for
+** additional information.
+*/
+#ifdef SQLITE_USER_AUTHENTICATION
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+** If a database contains the SQLITE_USER table, then the
+** sqlite3_user_authenticate() interface must be invoked with an
+** appropriate username and password prior to enable read and write
+** access to the database.
+**
+** Return SQLITE_OK on success or SQLITE_ERROR if the username/password
+** combination is incorrect or unknown.
+**
+** If the SQLITE_USER table is not present in the database file, then
+** this interface is a harmless no-op returnning SQLITE_OK.
+*/
+int sqlite3_user_authenticate(
+  sqlite3 *db,           /* The database connection */
+  const char *zUsername, /* Username */
+  const char *aPW,       /* Password or credentials */
+  int nPW                /* Number of bytes in aPW[] */
+);
+
+/*
+** The sqlite3_user_add() interface can be used (by an admin user only)
+** to create a new user.  When called on a no-authentication-required
+** database, this routine converts the database into an authentication-
+** required database, automatically makes the added user an
+** administrator, and logs in the current connection as that user.
+** The sqlite3_user_add() interface only works for the "main" database, not
+** for any ATTACH-ed databases.  Any call to sqlite3_user_add() by a
+** non-admin user results in an error.
+*/
+int sqlite3_user_add(
+  sqlite3 *db,           /* Database connection */
+  const char *zUsername, /* Username to be added */
+  const char *aPW,       /* Password or credentials */
+  int nPW,               /* Number of bytes in aPW[] */
+  int isAdmin            /* True to give new user admin privilege */
+);
+
+/*
+** The sqlite3_user_change() interface can be used to change a users
+** login credentials or admin privilege.  Any user can change their own
+** login credentials.  Only an admin user can change another users login
+** credentials or admin privilege setting.  No user may change their own 
+** admin privilege setting.
+*/
+int sqlite3_user_change(
+  sqlite3 *db,           /* Database connection */
+  const char *zUsername, /* Username to change */
+  const char *aPW,       /* New password or credentials */
+  int nPW,               /* Number of bytes in aPW[] */
+  int isAdmin            /* Modified admin privilege for the user */
+);
+
+/*
+** The sqlite3_user_delete() interface can be used (by an admin user only)
+** to delete a user.  The currently logged-in user cannot be deleted,
+** which guarantees that there is always an admin user and hence that
+** the database cannot be converted into a no-authentication-required
+** database.
+*/
+int sqlite3_user_delete(
+  sqlite3 *db,           /* Database connection */
+  const char *zUsername  /* Username to remove */
+);
+
+#ifdef __cplusplus
+}  /* end of the 'extern "C"' block */
+#endif
+
+#endif /* SQLITE_USER_AUTHENTICATION */
diff --git a/sqlite3.go b/sqlite3.go
index 2edeb2f..17291dc 100644
--- a/sqlite3.go
+++ b/sqlite3.go
@@ -1,20 +1,28 @@
-// +build cgo
-
 // Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
+// Copyright (C) 2018 G.J.R. Timmer <gjr.timmer@gmail.com>.
 //
 // Use of this source code is governed by an MIT-style
 // license that can be found in the LICENSE file.
 
+// +build cgo
+
 package sqlite3
 
 /*
 #cgo CFLAGS: -std=gnu99
-#cgo CFLAGS: -DSQLITE_ENABLE_RTREE -DSQLITE_THREADSAFE=1 -DHAVE_USLEEP=1
-#cgo linux,!android CFLAGS: -DHAVE_PREAD64=1 -DHAVE_PWRITE64=1
-#cgo CFLAGS: -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_FTS3_PARENTHESIS -DSQLITE_ENABLE_FTS4_UNICODE61
+#cgo CFLAGS: -DSQLITE_ENABLE_RTREE
+#cgo CFLAGS: -DSQLITE_THREADSAFE=1
+#cgo CFLAGS: -DHAVE_USLEEP=1
+#cgo CFLAGS: -DSQLITE_ENABLE_FTS3
+#cgo CFLAGS: -DSQLITE_ENABLE_FTS3_PARENTHESIS
+#cgo CFLAGS: -DSQLITE_ENABLE_FTS4_UNICODE61
 #cgo CFLAGS: -DSQLITE_TRACE_SIZE_LIMIT=15
+#cgo CFLAGS: -DSQLITE_OMIT_DEPRECATED
 #cgo CFLAGS: -DSQLITE_DISABLE_INTRINSIC
+#cgo CFLAGS: -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1
+#cgo CFLAGS: -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT
 #cgo CFLAGS: -Wno-deprecated-declarations
+#cgo linux,!android CFLAGS: -DHAVE_PREAD64=1 -DHAVE_PWRITE64=1
 #ifndef USE_LIBSQLITE3
 #include <sqlite3-binding.h>
 #else
@@ -772,36 +780,140 @@ func errorString(err Error) string {
 }
 
 // Open database and return a new connection.
+//
+// A pragma can take either zero or one argument.
+// The argument is may be either in parentheses or it may be separated from
+// the pragma name by an equal sign. The two syntaxes yield identical results.
+// In many pragmas, the argument is a boolean. The boolean can be one of:
+//    1 yes true on
+//    0 no false off
+//
 // You can specify a DSN string using a URI as the filename.
 //   test.db
 //   file:test.db?cache=shared&mode=memory
 //   :memory:
 //   file::memory:
+//
+//   mode
+//     Access mode of the database.
+//     https://www.sqlite.org/c3ref/open.html
+//     Values:
+//      - ro
+//      - rw
+//      - rwc
+//      - memory
+//
+//   shared
+//     SQLite Shared-Cache Mode
+//     https://www.sqlite.org/sharedcache.html
+//     Values:
+//       - shared
+//       - private
+//
+//   immutable=Boolean
+//     The immutable parameter is a boolean query parameter that indicates
+//     that the database file is stored on read-only media. When immutable is set,
+//     SQLite assumes that the database file cannot be changed,
+//     even by a process with higher privilege,
+//     and so the database is opened read-only and all locking and change detection is disabled.
+//     Caution: Setting the immutable property on a database file that
+//     does in fact change can result in incorrect query results and/or SQLITE_CORRUPT errors.
+//
 // go-sqlite3 adds the following query parameters to those used by SQLite:
 //   _loc=XXX
 //     Specify location of time format. It's possible to specify "auto".
-//   _busy_timeout=XXX
-//     Specify value for sqlite3_busy_timeout.
+//
+//   _mutex=XXX
+//     Specify mutex mode. XXX can be "no", "full".
+//
 //   _txlock=XXX
 //     Specify locking behavior for transactions.  XXX can be "immediate",
 //     "deferred", "exclusive".
-//   _foreign_keys=X
-//     Enable or disable enforcement of foreign keys.  X can be 1 or 0.
-//   _recursive_triggers=X
-//     Enable or disable recursive triggers.  X can be 1 or 0.
-//   _mutex=XXX
-//     Specify mutex mode. XXX can be "no", "full".
+//
+//   _auto_vacuum=X | _vacuum=X
+//     0 | none - Auto Vacuum disabled
+//     1 | full - Auto Vacuum FULL
+//     2 | incremental - Auto Vacuum Incremental
+//
+//   _busy_timeout=XXX"| _timeout=XXX
+//     Specify value for sqlite3_busy_timeout.
+//
+//   _case_sensitive_like=Boolean | _cslike=Boolean
+//     https://www.sqlite.org/pragma.html#pragma_case_sensitive_like
+//     Default or disabled the LIKE operation is case-insensitive.
+//     When enabling this options behaviour of LIKE will become case-sensitive.
+//
+//   _defer_foreign_keys=Boolean | _defer_fk=Boolean
+//     Defer Foreign Keys until outermost transaction is committed.
+//
+//   _foreign_keys=Boolean | _fk=Boolean
+//     Enable or disable enforcement of foreign keys.
+//
+//   _ignore_check_constraints=Boolean
+//     This pragma enables or disables the enforcement of CHECK constraints.
+//     The default setting is off, meaning that CHECK constraints are enforced by default.
+//
+//   _journal_mode=MODE | _journal=MODE
+//     Set journal mode for the databases associated with the current connection.
+//     https://www.sqlite.org/pragma.html#pragma_journal_mode
+//
+//   _locking_mode=X | _locking=X
+//     Sets the database connection locking-mode.
+//     The locking-mode is either NORMAL or EXCLUSIVE.
+//     https://www.sqlite.org/pragma.html#pragma_locking_mode
+//
+//   _query_only=Boolean
+//     The query_only pragma prevents all changes to database files when enabled.
+//
+//   _recursive_triggers=Boolean | _rt=Boolean
+//     Enable or disable recursive triggers.
+//
+//   _secure_delete=Boolean|FAST
+//     When secure_delete is on, SQLite overwrites deleted content with zeros.
+//     https://www.sqlite.org/pragma.html#pragma_secure_delete
+//
+//   _synchronous=X | _sync=X
+//     Change the setting of the "synchronous" flag.
+//     https://www.sqlite.org/pragma.html#pragma_synchronous
+//
+//   _writable_schema=Boolean
+//     When this pragma is on, the SQLITE_MASTER tables in which database
+//     can be changed using ordinary UPDATE, INSERT, and DELETE statements.
+//     Warning: misuse of this pragma can easily result in a corrupt database file.
+//
+//
 func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) {
 	if C.sqlite3_threadsafe() == 0 {
 		return nil, errors.New("sqlite library was not compiled for thread-safe operation")
 	}
 
+	var pkey string
+
+	// Options
 	var loc *time.Location
+	authCreate := false
+	authUser := ""
+	authPass := ""
+	authCrypt := ""
+	authSalt := ""
+	mutex := C.int(C.SQLITE_OPEN_FULLMUTEX)
 	txlock := "BEGIN"
+
+	// PRAGMA's
+	autoVacuum := -1
 	busyTimeout := 5000
+	caseSensitiveLike := -1
+	deferForeignKeys := -1
 	foreignKeys := -1
+	ignoreCheckConstraints := -1
+	journalMode := "DELETE"
+	lockingMode := "NORMAL"
+	queryOnly := -1
 	recursiveTriggers := -1
-	mutex := C.int(C.SQLITE_OPEN_FULLMUTEX)
+	secureDelete := "DEFAULT"
+	synchronousMode := "NORMAL"
+	writableSchema := -1
+
 	pos := strings.IndexRune(dsn, '?')
 	if pos >= 1 {
 		params, err := url.ParseQuery(dsn[pos+1:])
@@ -809,11 +921,29 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) {
 			return nil, err
 		}
 
+		// Authentication
+		if _, ok := params["_auth"]; ok {
+			authCreate = true
+		}
+		if val := params.Get("_auth_user"); val != "" {
+			authUser = val
+		}
+		if val := params.Get("_auth_pass"); val != "" {
+			authPass = val
+		}
+		if val := params.Get("_auth_crypt"); val != "" {
+			authCrypt = val
+		}
+		if val := params.Get("_auth_salt"); val != "" {
+			authSalt = val
+		}
+
 		// _loc
 		if val := params.Get("_loc"); val != "" {
-			if val == "auto" {
+			switch strings.ToLower(val) {
+			case "auto":
 				loc = time.Local
-			} else {
+			default:
 				loc, err = time.LoadLocation(val)
 				if err != nil {
 					return nil, fmt.Errorf("Invalid _loc: %v: %v", val, err)
@@ -821,18 +951,21 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) {
 			}
 		}
 
-		// _busy_timeout
-		if val := params.Get("_busy_timeout"); val != "" {
-			iv, err := strconv.ParseInt(val, 10, 64)
-			if err != nil {
-				return nil, fmt.Errorf("Invalid _busy_timeout: %v: %v", val, err)
+		// _mutex
+		if val := params.Get("_mutex"); val != "" {
+			switch strings.ToLower(val) {
+			case "no":
+				mutex = C.SQLITE_OPEN_NOMUTEX
+			case "full":
+				mutex = C.SQLITE_OPEN_FULLMUTEX
+			default:
+				return nil, fmt.Errorf("Invalid _mutex: %v", val)
 			}
-			busyTimeout = int(iv)
 		}
 
 		// _txlock
 		if val := params.Get("_txlock"); val != "" {
-			switch val {
+			switch strings.ToLower(val) {
 			case "immediate":
 				txlock = "BEGIN IMMEDIATE"
 			case "exclusive":
@@ -844,39 +977,262 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) {
 			}
 		}
 
-		// _foreign_keys
-		if val := params.Get("_foreign_keys"); val != "" {
-			switch val {
-			case "1":
-				foreignKeys = 1
-			case "0":
+		// Auto Vacuum (_vacuum)
+		//
+		// https://www.sqlite.org/pragma.html#pragma_auto_vacuum
+		//
+		pkey = "" // Reset pkey
+		if _, ok := params["_auto_vacuum"]; ok {
+			pkey = "_auto_vacuum"
+		}
+		if _, ok := params["_vacuum"]; ok {
+			pkey = "_vacuum"
+		}
+		if val := params.Get(pkey); val != "" {
+			switch strings.ToLower(val) {
+			case "0", "none":
+				autoVacuum = 0
+			case "1", "full":
+				autoVacuum = 1
+			case "2", "incremental":
+				autoVacuum = 2
+			default:
+				return nil, fmt.Errorf("Invalid _auto_vacuum: %v, expecting value of '0 NONE 1 FULL 2 INCREMENTAL'", val)
+			}
+		}
+
+		// Busy Timeout (_busy_timeout)
+		//
+		// https://www.sqlite.org/pragma.html#pragma_busy_timeout
+		//
+		pkey = "" // Reset pkey
+		if _, ok := params["_busy_timeout"]; ok {
+			pkey = "_busy_timeout"
+		}
+		if _, ok := params["_timeout"]; ok {
+			pkey = "_timeout"
+		}
+		if val := params.Get(pkey); val != "" {
+			iv, err := strconv.ParseInt(val, 10, 64)
+			if err != nil {
+				return nil, fmt.Errorf("Invalid _busy_timeout: %v: %v", val, err)
+			}
+			busyTimeout = int(iv)
+		}
+
+		// Case Sensitive Like (_cslike)
+		//
+		// https://www.sqlite.org/pragma.html#pragma_case_sensitive_like
+		//
+		pkey = "" // Reset pkey
+		if _, ok := params["_case_sensitive_like"]; ok {
+			pkey = "_case_sensitive_like"
+		}
+		if _, ok := params["_cslike"]; ok {
+			pkey = "_cslike"
+		}
+		if val := params.Get(pkey); val != "" {
+			switch strings.ToLower(val) {
+			case "0", "no", "false", "off":
+				caseSensitiveLike = 0
+			case "1", "yes", "true", "on":
+				caseSensitiveLike = 1
+			default:
+				return nil, fmt.Errorf("Invalid _case_sensitive_like: %v, expecting boolean value of '0 1 false true no yes off on'", val)
+			}
+		}
+
+		// Defer Foreign Keys (_defer_foreign_keys | _defer_fk)
+		//
+		// https://www.sqlite.org/pragma.html#pragma_defer_foreign_keys
+		//
+		pkey = "" // Reset pkey
+		if _, ok := params["_defer_foreign_keys"]; ok {
+			pkey = "_defer_foreign_keys"
+		}
+		if _, ok := params["_defer_fk"]; ok {
+			pkey = "_defer_fk"
+		}
+		if val := params.Get(pkey); val != "" {
+			switch strings.ToLower(val) {
+			case "0", "no", "false", "off":
+				deferForeignKeys = 0
+			case "1", "yes", "true", "on":
+				deferForeignKeys = 1
+			default:
+				return nil, fmt.Errorf("Invalid _defer_foreign_keys: %v, expecting boolean value of '0 1 false true no yes off on'", val)
+			}
+		}
+
+		// Foreign Keys (_foreign_keys | _fk)
+		//
+		// https://www.sqlite.org/pragma.html#pragma_foreign_keys
+		//
+		pkey = "" // Reset pkey
+		if _, ok := params["_foreign_keys"]; ok {
+			pkey = "_foreign_keys"
+		}
+		if _, ok := params["_fk"]; ok {
+			pkey = "_fk"
+		}
+		if val := params.Get(pkey); val != "" {
+			switch strings.ToLower(val) {
+			case "0", "no", "false", "off":
 				foreignKeys = 0
+			case "1", "yes", "true", "on":
+				foreignKeys = 1
 			default:
-				return nil, fmt.Errorf("Invalid _foreign_keys: %v", val)
+				return nil, fmt.Errorf("Invalid _foreign_keys: %v, expecting boolean value of '0 1 false true no yes off on'", val)
 			}
 		}
 
-		// _recursive_triggers
-		if val := params.Get("_recursive_triggers"); val != "" {
-			switch val {
-			case "1":
-				recursiveTriggers = 1
-			case "0":
+		// Ignore CHECK Constrains (_ignore_check_constraints)
+		//
+		// https://www.sqlite.org/pragma.html#pragma_ignore_check_constraints
+		//
+		if val := params.Get("_ignore_check_constraints"); val != "" {
+			switch strings.ToLower(val) {
+			case "0", "no", "false", "off":
+				ignoreCheckConstraints = 0
+			case "1", "yes", "true", "on":
+				ignoreCheckConstraints = 1
+			default:
+				return nil, fmt.Errorf("Invalid _ignore_check_constraints: %v, expecting boolean value of '0 1 false true no yes off on'", val)
+			}
+		}
+
+		// Journal Mode (_journal_mode | _journal)
+		//
+		// https://www.sqlite.org/pragma.html#pragma_journal_mode
+		//
+		pkey = "" // Reset pkey
+		if _, ok := params["_journal_mode"]; ok {
+			pkey = "_journal_mode"
+		}
+		if _, ok := params["_journal"]; ok {
+			pkey = "_journal"
+		}
+		if val := params.Get(pkey); val != "" {
+			switch strings.ToUpper(val) {
+			case "DELETE", "TRUNCATE", "PERSIST", "MEMORY", "OFF":
+				journalMode = strings.ToUpper(val)
+			case "WAL":
+				journalMode = strings.ToUpper(val)
+
+				// For WAL Mode set Synchronous Mode to 'NORMAL'
+				// See https://www.sqlite.org/pragma.html#pragma_synchronous
+				synchronousMode = "NORMAL"
+			default:
+				return nil, fmt.Errorf("Invalid _journal: %v, expecting value of 'DELETE TRUNCATE PERSIST MEMORY WAL OFF'", val)
+			}
+		}
+
+		// Locking Mode (_locking)
+		//
+		// https://www.sqlite.org/pragma.html#pragma_locking_mode
+		//
+		pkey = "" // Reset pkey
+		if _, ok := params["_locking_mode"]; ok {
+			pkey = "_locking_mode"
+		}
+		if _, ok := params["_locking"]; ok {
+			pkey = "_locking"
+		}
+		if val := params.Get("_locking"); val != "" {
+			switch strings.ToUpper(val) {
+			case "NORMAL", "EXCLUSIVE":
+				lockingMode = strings.ToUpper(val)
+			default:
+				return nil, fmt.Errorf("Invalid _locking_mode: %v, expecting value of 'NORMAL EXCLUSIVE", val)
+			}
+		}
+
+		// Query Only (_query_only)
+		//
+		// https://www.sqlite.org/pragma.html#pragma_query_only
+		//
+		if val := params.Get("_query_only"); val != "" {
+			switch strings.ToLower(val) {
+			case "0", "no", "false", "off":
+				queryOnly = 0
+			case "1", "yes", "true", "on":
+				queryOnly = 1
+			default:
+				return nil, fmt.Errorf("Invalid _query_only: %v, expecting boolean value of '0 1 false true no yes off on'", val)
+			}
+		}
+
+		// Recursive Triggers (_recursive_triggers)
+		//
+		// https://www.sqlite.org/pragma.html#pragma_recursive_triggers
+		//
+		pkey = "" // Reset pkey
+		if _, ok := params["_recursive_triggers"]; ok {
+			pkey = "_recursive_triggers"
+		}
+		if _, ok := params["_rt"]; ok {
+			pkey = "_rt"
+		}
+		if val := params.Get(pkey); val != "" {
+			switch strings.ToLower(val) {
+			case "0", "no", "false", "off":
 				recursiveTriggers = 0
+			case "1", "yes", "true", "on":
+				recursiveTriggers = 1
 			default:
-				return nil, fmt.Errorf("Invalid _recursive_triggers: %v", val)
+				return nil, fmt.Errorf("Invalid _recursive_triggers: %v, expecting boolean value of '0 1 false true no yes off on'", val)
 			}
 		}
 
-		// _mutex
-		if val := params.Get("_mutex"); val != "" {
-			switch val {
-			case "no":
-				mutex = C.SQLITE_OPEN_NOMUTEX
-			case "full":
-				mutex = C.SQLITE_OPEN_FULLMUTEX
+		// Secure Delete (_secure_delete)
+		//
+		// https://www.sqlite.org/pragma.html#pragma_secure_delete
+		//
+		if val := params.Get("_secure_delete"); val != "" {
+			switch strings.ToLower(val) {
+			case "0", "no", "false", "off":
+				secureDelete = "OFF"
+			case "1", "yes", "true", "on":
+				secureDelete = "ON"
+			case "fast":
+				secureDelete = "FAST"
 			default:
-				return nil, fmt.Errorf("Invalid _mutex: %v", val)
+				return nil, fmt.Errorf("Invalid _secure_delete: %v, expecting boolean value of '0 1 false true no yes off on fast'", val)
+			}
+		}
+
+		// Synchronous Mode (_synchronous | _sync)
+		//
+		// https://www.sqlite.org/pragma.html#pragma_synchronous
+		//
+		pkey = "" // Reset pkey
+		if _, ok := params["_synchronous"]; ok {
+			pkey = "_synchronous"
+		}
+		if _, ok := params["_sync"]; ok {
+			pkey = "_sync"
+		}
+		if val := params.Get(pkey); val != "" {
+			switch strings.ToUpper(val) {
+			case "0", "OFF", "1", "NORMAL", "2", "FULL", "3", "EXTRA":
+				synchronousMode = strings.ToUpper(val)
+			default:
+				return nil, fmt.Errorf("Invalid _synchronous: %v, expecting value of '0 OFF 1 NORMAL 2 FULL 3 EXTRA'", val)
+			}
+		}
+
+		// Writable Schema (_writeable_schema)
+		//
+		// https://www.sqlite.org/pragma.html#pragma_writeable_schema
+		//
+		if val := params.Get("_writable_schema"); val != "" {
+			switch strings.ToLower(val) {
+			case "0", "no", "false", "off":
+				writableSchema = 0
+			case "1", "yes", "true", "on":
+				writableSchema = 1
+			default:
+				return nil, fmt.Errorf("Invalid _writable_schema: %v, expecting boolean value of '0 1 false true no yes off on'", val)
 			}
 		}
 
@@ -913,30 +1269,268 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) {
 		}
 		return nil
 	}
-	if foreignKeys == 0 {
-		if err := exec("PRAGMA foreign_keys = OFF;"); err != nil {
+
+	// USER AUTHENTICATION
+	//
+	// User Authentication is always performed even when
+	// sqlite_userauth is not compiled in, because without user authentication
+	// the authentication is a no-op.
+	//
+	// Workflow
+	//	- Authenticate
+	//		ON::SUCCESS		=> Continue
+	//		ON::SQLITE_AUTH => Return error and exit Open(...)
+	//
+	//  - Activate User Authentication
+	//		Check if the user wants to activate User Authentication.
+	//		If so then first create a temporary AuthConn to the database
+	//		This is possible because we are already succesfully authenticated.
+	//
+	//	- Check if `sqlite_user`` table exists
+	//		YES				=> Add the provided user from DSN as Admin User and
+	//						   activate user authentication.
+	//		NO				=> Continue
+	//
+
+	// Create connection to SQLite
+	conn := &SQLiteConn{db: db, loc: loc, txlock: txlock}
+
+	// Password Cipher has to be registerd before authentication
+	if len(authCrypt) > 0 {
+		switch strings.ToUpper(authCrypt) {
+		case "SHA1":
+			if err := conn.RegisterFunc("sqlite_crypt", CryptEncoderSHA1, true); err != nil {
+				return nil, fmt.Errorf("CryptEncoderSHA1: %s", err)
+			}
+		case "SSHA1":
+			if len(authSalt) == 0 {
+				return nil, fmt.Errorf("_auth_crypt=ssha1, requires _auth_salt")
+			}
+			if err := conn.RegisterFunc("sqlite_crypt", CryptEncoderSSHA1(authSalt), true); err != nil {
+				return nil, fmt.Errorf("CryptEncoderSSHA1: %s", err)
+			}
+		case "SHA256":
+			if err := conn.RegisterFunc("sqlite_crypt", CryptEncoderSHA256, true); err != nil {
+				return nil, fmt.Errorf("CryptEncoderSHA256: %s", err)
+			}
+		case "SSHA256":
+			if len(authSalt) == 0 {
+				return nil, fmt.Errorf("_auth_crypt=ssha256, requires _auth_salt")
+			}
+			if err := conn.RegisterFunc("sqlite_crypt", CryptEncoderSSHA256(authSalt), true); err != nil {
+				return nil, fmt.Errorf("CryptEncoderSSHA256: %s", err)
+			}
+		case "SHA384":
+			if err := conn.RegisterFunc("sqlite_crypt", CryptEncoderSHA384, true); err != nil {
+				return nil, fmt.Errorf("CryptEncoderSHA384: %s", err)
+			}
+		case "SSHA384":
+			if len(authSalt) == 0 {
+				return nil, fmt.Errorf("_auth_crypt=ssha384, requires _auth_salt")
+			}
+			if err := conn.RegisterFunc("sqlite_crypt", CryptEncoderSSHA384(authSalt), true); err != nil {
+				return nil, fmt.Errorf("CryptEncoderSSHA384: %s", err)
+			}
+		case "SHA512":
+			if err := conn.RegisterFunc("sqlite_crypt", CryptEncoderSHA512, true); err != nil {
+				return nil, fmt.Errorf("CryptEncoderSHA512: %s", err)
+			}
+		case "SSHA512":
+			if len(authSalt) == 0 {
+				return nil, fmt.Errorf("_auth_crypt=ssha512, requires _auth_salt")
+			}
+			if err := conn.RegisterFunc("sqlite_crypt", CryptEncoderSSHA512(authSalt), true); err != nil {
+				return nil, fmt.Errorf("CryptEncoderSSHA512: %s", err)
+			}
+		}
+	}
+
+	// Preform Authentication
+	if err := conn.Authenticate(authUser, authPass); err != nil {
+		return nil, err
+	}
+
+	// Register: authenticate
+	// Authenticate will perform an authentication of the provided username
+	// and password against the database.
+	//
+	// If a database contains the SQLITE_USER table, then the
+	// call to Authenticate must be invoked with an
+	// appropriate username and password prior to enable read and write
+	//access to the database.
+	//
+	// Return SQLITE_OK on success or SQLITE_ERROR if the username/password
+	// combination is incorrect or unknown.
+	//
+	// If the SQLITE_USER table is not present in the database file, then
+	// this interface is a harmless no-op returnning SQLITE_OK.
+	if err := conn.RegisterFunc("authenticate", conn.authenticate, true); err != nil {
+		return nil, err
+	}
+	//
+	// Register: auth_user_add
+	// auth_user_add can be used (by an admin user only)
+	// to create a new user. When called on a no-authentication-required
+	// database, this routine converts the database into an authentication-
+	// required database, automatically makes the added user an
+	// administrator, and logs in the current connection as that user.
+	// The AuthUserAdd only works for the "main" database, not
+	// for any ATTACH-ed databases. Any call to AuthUserAdd by a
+	// non-admin user results in an error.
+	if err := conn.RegisterFunc("auth_user_add", conn.authUserAdd, true); err != nil {
+		return nil, err
+	}
+	//
+	// Register: auth_user_change
+	// auth_user_change can be used to change a users
+	// login credentials or admin privilege.  Any user can change their own
+	// login credentials. Only an admin user can change another users login
+	// credentials or admin privilege setting. No user may change their own
+	// admin privilege setting.
+	if err := conn.RegisterFunc("auth_user_change", conn.authUserChange, true); err != nil {
+		return nil, err
+	}
+	//
+	// Register: auth_user_delete
+	// auth_user_delete can be used (by an admin user only)
+	// to delete a user. The currently logged-in user cannot be deleted,
+	// which guarantees that there is always an admin user and hence that
+	// the database cannot be converted into a no-authentication-required
+	// database.
+	if err := conn.RegisterFunc("auth_user_delete", conn.authUserDelete, true); err != nil {
+		return nil, err
+	}
+
+	// Register: auth_enabled
+	// auth_enabled can be used to check if user authentication is enabled
+	if err := conn.RegisterFunc("auth_enabled", conn.authEnabled, true); err != nil {
+		return nil, err
+	}
+
+	// Auto Vacuum
+	// Moved auto_vacuum command, the user preference for auto_vacuum needs to be implemented directly after
+	// the authentication and before the sqlite_user table gets created if the user
+	// decides to activate User Authentication because
+	// auto_vacuum needs to be set before any tables are created
+	// and activating user authentication creates the internal table `sqlite_user`.
+	if autoVacuum > -1 {
+		if err := exec(fmt.Sprintf("PRAGMA auto_vacuum = %d;", autoVacuum)); err != nil {
+			C.sqlite3_close_v2(db)
+			return nil, err
+		}
+	}
+
+	// Check if user wants to activate User Authentication
+	if authCreate {
+		// Before going any further, we need to check that the user
+		// has provided an username and password within the DSN.
+		// We are not allowed to continue.
+		if len(authUser) < 0 {
+			return nil, fmt.Errorf("Missing '_auth_user' while user authentication was requested with '_auth'")
+		}
+		if len(authPass) < 0 {
+			return nil, fmt.Errorf("Missing '_auth_pass' while user authentication was requested with '_auth'")
+		}
+
+		// Check if User Authentication is Enabled
+		authExists := conn.AuthEnabled()
+		if !authExists {
+			if err := conn.AuthUserAdd(authUser, authPass, true); err != nil {
+				return nil, err
+			}
+		}
+	}
+
+	// Case Sensitive LIKE
+	if caseSensitiveLike > -1 {
+		if err := exec(fmt.Sprintf("PRAGMA case_sensitive_like = %d;", caseSensitiveLike)); err != nil {
 			C.sqlite3_close_v2(db)
 			return nil, err
 		}
-	} else if foreignKeys == 1 {
-		if err := exec("PRAGMA foreign_keys = ON;"); err != nil {
+	}
+
+	// Defer Foreign Keys
+	if deferForeignKeys > -1 {
+		if err := exec(fmt.Sprintf("PRAGMA defer_foreign_keys = %d;", deferForeignKeys)); err != nil {
 			C.sqlite3_close_v2(db)
 			return nil, err
 		}
 	}
-	if recursiveTriggers == 0 {
-		if err := exec("PRAGMA recursive_triggers = OFF;"); err != nil {
+
+	// Forgein Keys
+	if foreignKeys > -1 {
+		if err := exec(fmt.Sprintf("PRAGMA foreign_keys = %d;", foreignKeys)); err != nil {
 			C.sqlite3_close_v2(db)
 			return nil, err
 		}
-	} else if recursiveTriggers == 1 {
-		if err := exec("PRAGMA recursive_triggers = ON;"); err != nil {
+	}
+
+	// Ignore CHECK Constraints
+	if ignoreCheckConstraints > -1 {
+		if err := exec(fmt.Sprintf("PRAGMA ignore_check_constraints = %d;", ignoreCheckConstraints)); err != nil {
 			C.sqlite3_close_v2(db)
 			return nil, err
 		}
 	}
 
-	conn := &SQLiteConn{db: db, loc: loc, txlock: txlock}
+	// Journal Mode
+	// Because default Journal Mode is DELETE this PRAGMA can always be executed.
+	if err := exec(fmt.Sprintf("PRAGMA journal_mode = %s;", journalMode)); err != nil {
+		C.sqlite3_close_v2(db)
+		return nil, err
+	}
+
+	// Locking Mode
+	// Because the default is NORMAL and this is not changed in this package
+	// by using the compile time SQLITE_DEFAULT_LOCKING_MODE this PRAGMA can always be executed
+	if err := exec(fmt.Sprintf("PRAGMA locking_mode = %s;", lockingMode)); err != nil {
+		C.sqlite3_close_v2(db)
+		return nil, err
+	}
+
+	// Query Only
+	if queryOnly > -1 {
+		if err := exec(fmt.Sprintf("PRAGMA query_only = %d;", queryOnly)); err != nil {
+			C.sqlite3_close_v2(db)
+			return nil, err
+		}
+	}
+
+	// Recursive Triggers
+	if recursiveTriggers > -1 {
+		if err := exec(fmt.Sprintf("PRAGMA recursive_triggers = %d;", recursiveTriggers)); err != nil {
+			C.sqlite3_close_v2(db)
+			return nil, err
+		}
+	}
+
+	// Secure Delete
+	//
+	// Because this package can set the compile time flag SQLITE_SECURE_DELETE with a build tag
+	// the default value for secureDelete var is 'DEFAULT' this way
+	// you can compile with secure_delete 'ON' and disable it for a specific database connection.
+	if secureDelete != "DEFAULT" {
+		if err := exec(fmt.Sprintf("PRAGMA secure_delete = %s;", secureDelete)); err != nil {
+			C.sqlite3_close_v2(db)
+			return nil, err
+		}
+	}
+
+	// Synchronous Mode
+	//
+	// Because default is NORMAL this statement is always executed
+	if err := exec(fmt.Sprintf("PRAGMA synchronous = %s;", synchronousMode)); err != nil {
+		C.sqlite3_close_v2(db)
+		return nil, err
+	}
+
+	// Writable Schema
+	if writableSchema > -1 {
+		if err := exec(fmt.Sprintf("PRAGMA writable_schema = %d;", writableSchema)); err != nil {
+			C.sqlite3_close_v2(db)
+			return nil, err
+		}
+	}
 
 	if len(d.Extensions) > 0 {
 		if err := conn.loadExtensions(d.Extensions); err != nil {
@@ -1018,6 +1612,17 @@ const (
 	SQLITE_LIMIT_WORKER_THREADS      = C.SQLITE_LIMIT_WORKER_THREADS
 )
 
+// GetFilename returns the absolute path to the file containing
+// the requested schema. When passed an empty string, it will
+// instead use the database's default schema: "main".
+// See: sqlite3_db_filename, https://www.sqlite.org/c3ref/db_filename.html
+func (c *SQLiteConn) GetFilename(schemaName string) string {
+	if schemaName == "" {
+		schemaName = "main"
+	}
+	return C.GoString(C.sqlite3_db_filename(c.db, C.CString(schemaName)))
+}
+
 // GetLimit returns the current value of a run-time limit.
 // See: sqlite3_limit, http://www.sqlite.org/c3ref/limit.html
 func (c *SQLiteConn) GetLimit(id int) int {
@@ -1100,11 +1705,15 @@ func (s *SQLiteStmt) bind(args []namedValue) error {
 		case float64:
 			rv = C.sqlite3_bind_double(s.s, n, C.double(v))
 		case []byte:
-			ln := len(v)
-			if ln == 0 {
-				v = placeHolder
+			if v == nil {
+				rv = C.sqlite3_bind_null(s.s, n)
+			} else {
+				ln := len(v)
+				if ln == 0 {
+					v = placeHolder
+				}
+				rv = C._sqlite3_bind_blob(s.s, n, unsafe.Pointer(&v[0]), C.int(ln))
 			}
-			rv = C._sqlite3_bind_blob(s.s, n, unsafe.Pointer(&v[0]), C.int(ln))
 		case time.Time:
 			b := []byte(v.Format(SQLiteTimestampFormats[0]))
 			rv = C._sqlite3_bind_text(s.s, n, (*C.char)(unsafe.Pointer(&b[0])), C.int(len(b)))
diff --git a/sqlite3.goconvey b/sqlite3.goconvey
new file mode 100644
index 0000000..295b895
--- /dev/null
+++ b/sqlite3.goconvey
@@ -0,0 +1,8 @@
+// GoConvey Test Profile
+
+// Activate Coverage
+-cover
+
+-tags=sqlite_userauth
+
+// EOF
\ No newline at end of file
diff --git a/sqlite3_func_crypt.go b/sqlite3_func_crypt.go
new file mode 100644
index 0000000..3774a97
--- /dev/null
+++ b/sqlite3_func_crypt.go
@@ -0,0 +1,120 @@
+// Copyright (C) 2018 G.J.R. Timmer <gjr.timmer@gmail.com>.
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file.
+
+package sqlite3
+
+import (
+	"crypto/sha1"
+	"crypto/sha256"
+	"crypto/sha512"
+)
+
+// This file provides several different implementations for the
+// default embedded sqlite_crypt function.
+// This function is uses a ceasar-cypher by default
+// and is used within the UserAuthentication module to encode
+// the password.
+//
+// The provided functions can be used as an overload to the sqlite_crypt
+// function through the use of the RegisterFunc on the connection.
+//
+// Because the functions can serv a purpose to an end-user
+// without using the UserAuthentication module
+// the functions are default compiled in.
+//
+// From SQLITE3 - user-auth.txt
+// The sqlite_user.pw field is encoded by a built-in SQL function
+// "sqlite_crypt(X,Y)".  The two arguments are both BLOBs.  The first argument
+// is the plaintext password supplied to the sqlite3_user_authenticate()
+// interface.  The second argument is the sqlite_user.pw value and is supplied
+// so that the function can extract the "salt" used by the password encoder.
+// The result of sqlite_crypt(X,Y) is another blob which is the value that
+// ends up being stored in sqlite_user.pw.  To verify credentials X supplied
+// by the sqlite3_user_authenticate() routine, SQLite runs:
+//
+//     sqlite_user.pw == sqlite_crypt(X, sqlite_user.pw)
+//
+// To compute an appropriate sqlite_user.pw value from a new or modified
+// password X, sqlite_crypt(X,NULL) is run.  A new random salt is selected
+// when the second argument is NULL.
+//
+// The built-in version of of sqlite_crypt() uses a simple Ceasar-cypher
+// which prevents passwords from being revealed by searching the raw database
+// for ASCII text, but is otherwise trivally broken.  For better password
+// security, the database should be encrypted using the SQLite Encryption
+// Extension or similar technology.  Or, the application can use the
+// sqlite3_create_function() interface to provide an alternative
+// implementation of sqlite_crypt() that computes a stronger password hash,
+// perhaps using a cryptographic hash function like SHA1.
+
+// CryptEncoderSHA1 encodes a password with SHA1
+func CryptEncoderSHA1(pass []byte, hash interface{}) []byte {
+	h := sha1.Sum(pass)
+	return h[:]
+}
+
+// CryptEncoderSSHA1 encodes a password with SHA1 with the
+// configured salt.
+func CryptEncoderSSHA1(salt string) func(pass []byte, hash interface{}) []byte {
+	return func(pass []byte, hash interface{}) []byte {
+		s := []byte(salt)
+		p := append(pass, s...)
+		h := sha1.Sum(p)
+		return h[:]
+	}
+}
+
+// CryptEncoderSHA256 encodes a password with SHA256
+func CryptEncoderSHA256(pass []byte, hash interface{}) []byte {
+	h := sha256.Sum256(pass)
+	return h[:]
+}
+
+// CryptEncoderSSHA256 encodes a password with SHA256
+// with the configured salt
+func CryptEncoderSSHA256(salt string) func(pass []byte, hash interface{}) []byte {
+	return func(pass []byte, hash interface{}) []byte {
+		s := []byte(salt)
+		p := append(pass, s...)
+		h := sha256.Sum256(p)
+		return h[:]
+	}
+}
+
+// CryptEncoderSHA384 encodes a password with SHA256
+func CryptEncoderSHA384(pass []byte, hash interface{}) []byte {
+	h := sha512.Sum384(pass)
+	return h[:]
+}
+
+// CryptEncoderSSHA384 encodes a password with SHA256
+// with the configured salt
+func CryptEncoderSSHA384(salt string) func(pass []byte, hash interface{}) []byte {
+	return func(pass []byte, hash interface{}) []byte {
+		s := []byte(salt)
+		p := append(pass, s...)
+		h := sha512.Sum384(p)
+		return h[:]
+	}
+}
+
+// CryptEncoderSHA512 encodes a password with SHA256
+func CryptEncoderSHA512(pass []byte, hash interface{}) []byte {
+	h := sha512.Sum512(pass)
+	return h[:]
+}
+
+// CryptEncoderSSHA512 encodes a password with SHA256
+// with the configured salt
+func CryptEncoderSSHA512(salt string) func(pass []byte, hash interface{}) []byte {
+	return func(pass []byte, hash interface{}) []byte {
+		s := []byte(salt)
+		p := append(pass, s...)
+		h := sha512.Sum512(p)
+		return h[:]
+	}
+}
+
+// EOF
diff --git a/sqlite3_go18.go b/sqlite3_go18.go
index dd0f5a7..43e6418 100644
--- a/sqlite3_go18.go
+++ b/sqlite3_go18.go
@@ -1,10 +1,9 @@
-// +build cgo
-
 // Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
 //
 // Use of this source code is governed by an MIT-style
 // license that can be found in the LICENSE file.
 
+// +build cgo
 // +build go1.8
 
 package sqlite3
diff --git a/sqlite3_libsqlite3.go b/sqlite3_libsqlite3.go
index e4557e6..83c2ad9 100644
--- a/sqlite3_libsqlite3.go
+++ b/sqlite3_libsqlite3.go
@@ -2,6 +2,7 @@
 //
 // Use of this source code is governed by an MIT-style
 // license that can be found in the LICENSE file.
+
 // +build libsqlite3
 
 package sqlite3
diff --git a/sqlite3_load_extension.go b/sqlite3_load_extension.go
index bb7e25f..e73982a 100644
--- a/sqlite3_load_extension.go
+++ b/sqlite3_load_extension.go
@@ -2,6 +2,7 @@
 //
 // Use of this source code is governed by an MIT-style
 // license that can be found in the LICENSE file.
+
 // +build !sqlite_omit_load_extension
 
 package sqlite3
diff --git a/sqlite3_omit_load_extension.go b/sqlite3_load_extension_omit.go
similarity index 99%
rename from sqlite3_omit_load_extension.go
rename to sqlite3_load_extension_omit.go
index c7b3bfe..7ea3294 100644
--- a/sqlite3_omit_load_extension.go
+++ b/sqlite3_load_extension_omit.go
@@ -2,6 +2,7 @@
 //
 // Use of this source code is governed by an MIT-style
 // license that can be found in the LICENSE file.
+
 // +build sqlite_omit_load_extension
 
 package sqlite3
diff --git a/sqlite3_opt_allow_uri_authority.go b/sqlite3_opt_allow_uri_authority.go
new file mode 100644
index 0000000..c92e817
--- /dev/null
+++ b/sqlite3_opt_allow_uri_authority.go
@@ -0,0 +1,15 @@
+// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
+// Copyright (C) 2018 G.J.R. Timmer <gjr.timmer@gmail.com>.
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file.
+
+// +build sqlite_allow_uri_authority
+
+package sqlite3
+
+/*
+#cgo CFLAGS: -DSQLITE_ALLOW_URI_AUTHORITY
+#cgo LDFLAGS: -lm
+*/
+import "C"
diff --git a/sqlite3_opt_app_armor.go b/sqlite3_opt_app_armor.go
new file mode 100644
index 0000000..89947cd
--- /dev/null
+++ b/sqlite3_opt_app_armor.go
@@ -0,0 +1,16 @@
+// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
+// Copyright (C) 2018 G.J.R. Timmer <gjr.timmer@gmail.com>.
+
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file.
+
+// +build !windows
+// +build sqlite_app_armor
+
+package sqlite3
+
+/*
+#cgo CFLAGS: -DSQLITE_ENABLE_API_ARMOR
+#cgo LDFLAGS: -lm
+*/
+import "C"
diff --git a/sqlite3_opt_foreign_keys.go b/sqlite3_opt_foreign_keys.go
new file mode 100644
index 0000000..fd4f5a7
--- /dev/null
+++ b/sqlite3_opt_foreign_keys.go
@@ -0,0 +1,15 @@
+// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
+// Copyright (C) 2018 G.J.R. Timmer <gjr.timmer@gmail.com>.
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file.
+
+// +build sqlite_foreign_keys
+
+package sqlite3
+
+/*
+#cgo CFLAGS: -DSQLITE_DEFAULT_FOREIGN_KEYS=1
+#cgo LDFLAGS: -lm
+*/
+import "C"
diff --git a/sqlite3_fts3_test.go b/sqlite3_opt_fts3_test.go
similarity index 100%
rename from sqlite3_fts3_test.go
rename to sqlite3_opt_fts3_test.go
diff --git a/sqlite3_fts5.go b/sqlite3_opt_fts5.go
similarity index 90%
rename from sqlite3_fts5.go
rename to sqlite3_opt_fts5.go
index 0e65d69..fa98400 100644
--- a/sqlite3_fts5.go
+++ b/sqlite3_opt_fts5.go
@@ -2,7 +2,8 @@
 //
 // Use of this source code is governed by an MIT-style
 // license that can be found in the LICENSE file.
-// +build fts5
+
+// +build sqlite_fts5 fts5
 
 package sqlite3
 
diff --git a/sqlite3_icu.go b/sqlite3_opt_icu.go
similarity index 68%
rename from sqlite3_icu.go
rename to sqlite3_opt_icu.go
index e960626..32a1336 100644
--- a/sqlite3_icu.go
+++ b/sqlite3_opt_icu.go
@@ -2,12 +2,15 @@
 //
 // Use of this source code is governed by an MIT-style
 // license that can be found in the LICENSE file.
-// +build icu
+
+// +build sqlite_icu icu
 
 package sqlite3
 
 /*
 #cgo LDFLAGS: -licuuc -licui18n
 #cgo CFLAGS: -DSQLITE_ENABLE_ICU
+#cgo darwin CFLAGS: -I/usr/local/opt/icu4c/include
+#cgo darwin LDFLAGS: -L/usr/local/opt/icu4c/lib
 */
 import "C"
diff --git a/sqlite3_opt_introspect.go b/sqlite3_opt_introspect.go
new file mode 100644
index 0000000..10d4433
--- /dev/null
+++ b/sqlite3_opt_introspect.go
@@ -0,0 +1,15 @@
+// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
+// Copyright (C) 2018 G.J.R. Timmer <gjr.timmer@gmail.com>.
+
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file.
+
+// +build sqlite_introspect
+
+package sqlite3
+
+/*
+#cgo CFLAGS: -DSQLITE_INTROSPECTION_PRAGMAS
+#cgo LDFLAGS: -lm
+*/
+import "C"
diff --git a/sqlite3_json1.go b/sqlite3_opt_json1.go
similarity index 85%
rename from sqlite3_json1.go
rename to sqlite3_opt_json1.go
index a7b2473..47d3599 100644
--- a/sqlite3_json1.go
+++ b/sqlite3_opt_json1.go
@@ -2,7 +2,8 @@
 //
 // Use of this source code is governed by an MIT-style
 // license that can be found in the LICENSE file.
-// +build json1
+
+// +build sqlite_json sqlite_json1 json1
 
 package sqlite3
 
diff --git a/sqlite3_opt_secure_delete.go b/sqlite3_opt_secure_delete.go
new file mode 100644
index 0000000..0e2f280
--- /dev/null
+++ b/sqlite3_opt_secure_delete.go
@@ -0,0 +1,15 @@
+// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
+// Copyright (C) 2018 G.J.R. Timmer <gjr.timmer@gmail.com>.
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file.
+
+// +build sqlite_secure_delete
+
+package sqlite3
+
+/*
+#cgo CFLAGS: -DSQLITE_SECURE_DELETE=1
+#cgo LDFLAGS: -lm
+*/
+import "C"
diff --git a/sqlite3_opt_secure_delete_fast.go b/sqlite3_opt_secure_delete_fast.go
new file mode 100644
index 0000000..8f86b21
--- /dev/null
+++ b/sqlite3_opt_secure_delete_fast.go
@@ -0,0 +1,15 @@
+// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
+// Copyright (C) 2018 G.J.R. Timmer <gjr.timmer@gmail.com>.
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file.
+
+// +build sqlite_secure_delete_fast
+
+package sqlite3
+
+/*
+#cgo CFLAGS: -DSQLITE_SECURE_DELETE=FAST
+#cgo LDFLAGS: -lm
+*/
+import "C"
diff --git a/sqlite3_opt_stat4.go b/sqlite3_opt_stat4.go
new file mode 100644
index 0000000..3a34c12
--- /dev/null
+++ b/sqlite3_opt_stat4.go
@@ -0,0 +1,15 @@
+// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
+// Copyright (C) 2018 G.J.R. Timmer <gjr.timmer@gmail.com>.
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file.
+
+// +build sqlite_stat4
+
+package sqlite3
+
+/*
+#cgo CFLAGS: -DSQLITE_ENABLE_STAT4
+#cgo LDFLAGS: -lm
+*/
+import "C"
diff --git a/sqlite3_opt_userauth.go b/sqlite3_opt_userauth.go
new file mode 100644
index 0000000..94203b3
--- /dev/null
+++ b/sqlite3_opt_userauth.go
@@ -0,0 +1,289 @@
+// Copyright (C) 2018 G.J.R. Timmer <gjr.timmer@gmail.com>.
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file.
+
+// +build sqlite_userauth
+
+package sqlite3
+
+/*
+#cgo CFLAGS: -DSQLITE_USER_AUTHENTICATION
+#cgo LDFLAGS: -lm
+#ifndef USE_LIBSQLITE3
+#include <sqlite3-binding.h>
+#else
+#include <sqlite3.h>
+#endif
+#include <stdlib.h>
+
+static int
+_sqlite3_user_authenticate(sqlite3* db, const char* zUsername, const char* aPW, int nPW)
+{
+  return sqlite3_user_authenticate(db, zUsername, aPW, nPW);
+}
+
+static int
+_sqlite3_user_add(sqlite3* db, const char* zUsername, const char* aPW, int nPW, int isAdmin)
+{
+  return sqlite3_user_add(db, zUsername, aPW, nPW, isAdmin);
+}
+
+static int
+_sqlite3_user_change(sqlite3* db, const char* zUsername, const char* aPW, int nPW, int isAdmin)
+{
+  return sqlite3_user_change(db, zUsername, aPW, nPW, isAdmin);
+}
+
+static int
+_sqlite3_user_delete(sqlite3* db, const char* zUsername)
+{
+  return sqlite3_user_delete(db, zUsername);
+}
+
+static int
+_sqlite3_auth_enabled(sqlite3* db)
+{
+	int exists = -1;
+
+	sqlite3_stmt *stmt;
+	sqlite3_prepare_v2(db, "select count(type) from sqlite_master WHERE type='table' and name='sqlite_user';", -1, &stmt, NULL);
+
+	while ( sqlite3_step(stmt) == SQLITE_ROW) {
+		exists = sqlite3_column_int(stmt, 0);
+	}
+
+	sqlite3_finalize(stmt);
+
+	return exists;
+}
+*/
+import "C"
+import (
+	"errors"
+	"unsafe"
+)
+
+const (
+	SQLITE_AUTH = C.SQLITE_AUTH
+)
+
+var (
+	ErrUnauthorized  = errors.New("SQLITE_AUTH: Unauthorized")
+	ErrAdminRequired = errors.New("SQLITE_AUTH: Unauthorized; Admin Privileges Required")
+)
+
+// Authenticate will perform an authentication of the provided username
+// and password against the database.
+//
+// If a database contains the SQLITE_USER table, then the
+// call to Authenticate must be invoked with an
+// appropriate username and password prior to enable read and write
+//access to the database.
+//
+// Return SQLITE_OK on success or SQLITE_ERROR if the username/password
+// combination is incorrect or unknown.
+//
+// If the SQLITE_USER table is not present in the database file, then
+// this interface is a harmless no-op returnning SQLITE_OK.
+func (c *SQLiteConn) Authenticate(username, password string) error {
+	rv := c.authenticate(username, password)
+	switch rv {
+	case C.SQLITE_ERROR, C.SQLITE_AUTH:
+		return ErrUnauthorized
+	case C.SQLITE_OK:
+		return nil
+	default:
+		return c.lastError()
+	}
+}
+
+// authenticate provides the actual authentication to SQLite.
+// This is not exported for usage in Go.
+// It is however exported for usage within SQL by the user.
+//
+// Returns:
+//	C.SQLITE_OK (0)
+//	C.SQLITE_ERROR (1)
+//  C.SQLITE_AUTH (23)
+func (c *SQLiteConn) authenticate(username, password string) int {
+	// Allocate C Variables
+	cuser := C.CString(username)
+	cpass := C.CString(password)
+
+	// Free C Variables
+	defer func() {
+		C.free(unsafe.Pointer(cuser))
+		C.free(unsafe.Pointer(cpass))
+	}()
+
+	return int(C._sqlite3_user_authenticate(c.db, cuser, cpass, C.int(len(password))))
+}
+
+// AuthUserAdd can be used (by an admin user only)
+// to create a new user. When called on a no-authentication-required
+// database, this routine converts the database into an authentication-
+// required database, automatically makes the added user an
+// administrator, and logs in the current connection as that user.
+// The AuthUserAdd only works for the "main" database, not
+// for any ATTACH-ed databases. Any call to AuthUserAdd by a
+// non-admin user results in an error.
+func (c *SQLiteConn) AuthUserAdd(username, password string, admin bool) error {
+	isAdmin := 0
+	if admin {
+		isAdmin = 1
+	}
+
+	rv := c.authUserAdd(username, password, isAdmin)
+	switch rv {
+	case C.SQLITE_ERROR, C.SQLITE_AUTH:
+		return ErrAdminRequired
+	case C.SQLITE_OK:
+		return nil
+	default:
+		return c.lastError()
+	}
+}
+
+// authUserAdd enables the User Authentication if not enabled.
+// Otherwise it will add a user.
+//
+// When user authentication is already enabled then this function
+// can only be called by an admin.
+//
+// This is not exported for usage in Go.
+// It is however exported for usage within SQL by the user.
+//
+// Returns:
+//	C.SQLITE_OK (0)
+//	C.SQLITE_ERROR (1)
+//  C.SQLITE_AUTH (23)
+func (c *SQLiteConn) authUserAdd(username, password string, admin int) int {
+	// Allocate C Variables
+	cuser := C.CString(username)
+	cpass := C.CString(password)
+
+	// Free C Variables
+	defer func() {
+		C.free(unsafe.Pointer(cuser))
+		C.free(unsafe.Pointer(cpass))
+	}()
+
+	return int(C._sqlite3_user_add(c.db, cuser, cpass, C.int(len(password)), C.int(admin)))
+}
+
+// AuthUserChange can be used to change a users
+// login credentials or admin privilege.  Any user can change their own
+// login credentials. Only an admin user can change another users login
+// credentials or admin privilege setting. No user may change their own
+// admin privilege setting.
+func (c *SQLiteConn) AuthUserChange(username, password string, admin bool) error {
+	isAdmin := 0
+	if admin {
+		isAdmin = 1
+	}
+
+	rv := c.authUserChange(username, password, isAdmin)
+	switch rv {
+	case C.SQLITE_ERROR, C.SQLITE_AUTH:
+		return ErrAdminRequired
+	case C.SQLITE_OK:
+		return nil
+	default:
+		return c.lastError()
+	}
+}
+
+// authUserChange allows to modify a user.
+// Users can change their own password.
+//
+// Only admins can change passwords for other users
+// and modify the admin flag.
+//
+// The admin flag of the current logged in user cannot be changed.
+// THis ensures that their is always an admin.
+//
+// This is not exported for usage in Go.
+// It is however exported for usage within SQL by the user.
+//
+// Returns:
+//	C.SQLITE_OK (0)
+//	C.SQLITE_ERROR (1)
+//  C.SQLITE_AUTH (23)
+func (c *SQLiteConn) authUserChange(username, password string, admin int) int {
+	// Allocate C Variables
+	cuser := C.CString(username)
+	cpass := C.CString(password)
+
+	// Free C Variables
+	defer func() {
+		C.free(unsafe.Pointer(cuser))
+		C.free(unsafe.Pointer(cpass))
+	}()
+
+	return int(C._sqlite3_user_change(c.db, cuser, cpass, C.int(len(password)), C.int(admin)))
+}
+
+// AuthUserDelete can be used (by an admin user only)
+// to delete a user. The currently logged-in user cannot be deleted,
+// which guarantees that there is always an admin user and hence that
+// the database cannot be converted into a no-authentication-required
+// database.
+func (c *SQLiteConn) AuthUserDelete(username string) error {
+	rv := c.authUserDelete(username)
+	switch rv {
+	case C.SQLITE_ERROR, C.SQLITE_AUTH:
+		return ErrAdminRequired
+	case C.SQLITE_OK:
+		return nil
+	default:
+		return c.lastError()
+	}
+}
+
+// authUserDelete can be used to delete a user.
+//
+// This function can only be executed by an admin.
+//
+// This is not exported for usage in Go.
+// It is however exported for usage within SQL by the user.
+//
+// Returns:
+//	C.SQLITE_OK (0)
+//	C.SQLITE_ERROR (1)
+//  C.SQLITE_AUTH (23)
+func (c *SQLiteConn) authUserDelete(username string) int {
+	// Allocate C Variables
+	cuser := C.CString(username)
+
+	// Free C Variables
+	defer func() {
+		C.free(unsafe.Pointer(cuser))
+	}()
+
+	return int(C._sqlite3_user_delete(c.db, cuser))
+}
+
+// AuthEnabled checks if the database is protected by user authentication
+func (c *SQLiteConn) AuthEnabled() (exists bool) {
+	rv := c.authEnabled()
+	if rv == 1 {
+		exists = true
+	}
+
+	return
+}
+
+// authEnabled perform the actual check for user authentication.
+//
+// This is not exported for usage in Go.
+// It is however exported for usage within SQL by the user.
+//
+// Returns:
+//	0 - Disabled
+//  1 - Enabled
+func (c *SQLiteConn) authEnabled() int {
+	return int(C._sqlite3_auth_enabled(c.db))
+}
+
+// EOF
diff --git a/sqlite3_opt_userauth_omit.go b/sqlite3_opt_userauth_omit.go
new file mode 100644
index 0000000..302cd57
--- /dev/null
+++ b/sqlite3_opt_userauth_omit.go
@@ -0,0 +1,152 @@
+// Copyright (C) 2018 G.J.R. Timmer <gjr.timmer@gmail.com>.
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file.
+
+// +build !sqlite_userauth
+
+package sqlite3
+
+import (
+	"C"
+)
+
+// Authenticate will perform an authentication of the provided username
+// and password against the database.
+//
+// If a database contains the SQLITE_USER table, then the
+// call to Authenticate must be invoked with an
+// appropriate username and password prior to enable read and write
+//access to the database.
+//
+// Return SQLITE_OK on success or SQLITE_ERROR if the username/password
+// combination is incorrect or unknown.
+//
+// If the SQLITE_USER table is not present in the database file, then
+// this interface is a harmless no-op returnning SQLITE_OK.
+func (c *SQLiteConn) Authenticate(username, password string) error {
+	// NOOP
+	return nil
+}
+
+// authenticate provides the actual authentication to SQLite.
+// This is not exported for usage in Go.
+// It is however exported for usage within SQL by the user.
+//
+// Returns:
+//	C.SQLITE_OK (0)
+//	C.SQLITE_ERROR (1)
+//  C.SQLITE_AUTH (23)
+func (c *SQLiteConn) authenticate(username, password string) int {
+	// NOOP
+	return 0
+}
+
+// AuthUserAdd can be used (by an admin user only)
+// to create a new user.  When called on a no-authentication-required
+// database, this routine converts the database into an authentication-
+// required database, automatically makes the added user an
+// administrator, and logs in the current connection as that user.
+// The AuthUserAdd only works for the "main" database, not
+// for any ATTACH-ed databases. Any call to AuthUserAdd by a
+// non-admin user results in an error.
+func (c *SQLiteConn) AuthUserAdd(username, password string, admin bool) error {
+	// NOOP
+	return nil
+}
+
+// authUserAdd enables the User Authentication if not enabled.
+// Otherwise it will add a user.
+//
+// When user authentication is already enabled then this function
+// can only be called by an admin.
+//
+// This is not exported for usage in Go.
+// It is however exported for usage within SQL by the user.
+//
+// Returns:
+//	C.SQLITE_OK (0)
+//	C.SQLITE_ERROR (1)
+//  C.SQLITE_AUTH (23)
+func (c *SQLiteConn) authUserAdd(username, password string, admin int) int {
+	// NOOP
+	return 0
+}
+
+// AuthUserChange can be used to change a users
+// login credentials or admin privilege.  Any user can change their own
+// login credentials.  Only an admin user can change another users login
+// credentials or admin privilege setting.  No user may change their own
+// admin privilege setting.
+func (c *SQLiteConn) AuthUserChange(username, password string, admin bool) error {
+	// NOOP
+	return nil
+}
+
+// authUserChange allows to modify a user.
+// Users can change their own password.
+//
+// Only admins can change passwords for other users
+// and modify the admin flag.
+//
+// The admin flag of the current logged in user cannot be changed.
+// THis ensures that their is always an admin.
+//
+// This is not exported for usage in Go.
+// It is however exported for usage within SQL by the user.
+//
+// Returns:
+//	C.SQLITE_OK (0)
+//	C.SQLITE_ERROR (1)
+//  C.SQLITE_AUTH (23)
+func (c *SQLiteConn) authUserChange(username, password string, admin int) int {
+	// NOOP
+	return 0
+}
+
+// AuthUserDelete can be used (by an admin user only)
+// to delete a user.  The currently logged-in user cannot be deleted,
+// which guarantees that there is always an admin user and hence that
+// the database cannot be converted into a no-authentication-required
+// database.
+func (c *SQLiteConn) AuthUserDelete(username string) error {
+	// NOOP
+	return nil
+}
+
+// authUserDelete can be used to delete a user.
+//
+// This function can only be executed by an admin.
+//
+// This is not exported for usage in Go.
+// It is however exported for usage within SQL by the user.
+//
+// Returns:
+//	C.SQLITE_OK (0)
+//	C.SQLITE_ERROR (1)
+//  C.SQLITE_AUTH (23)
+func (c *SQLiteConn) authUserDelete(username string) int {
+	// NOOP
+	return 0
+}
+
+// AuthEnabled checks if the database is protected by user authentication
+func (c *SQLiteConn) AuthEnabled() (exists bool) {
+	// NOOP
+	return false
+}
+
+// authEnabled perform the actual check for user authentication.
+//
+// This is not exported for usage in Go.
+// It is however exported for usage within SQL by the user.
+//
+// Returns:
+//	0 - Disabled
+//  1 - Enabled
+func (c *SQLiteConn) authEnabled() int {
+	// NOOP
+	return 0
+}
+
+// EOF
diff --git a/sqlite3_opt_vacuum_full.go b/sqlite3_opt_vacuum_full.go
new file mode 100644
index 0000000..a202c80
--- /dev/null
+++ b/sqlite3_opt_vacuum_full.go
@@ -0,0 +1,15 @@
+// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
+// Copyright (C) 2018 G.J.R. Timmer <gjr.timmer@gmail.com>.
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file.
+
+// +build sqlite_vacuum_full
+
+package sqlite3
+
+/*
+#cgo CFLAGS: -DSQLITE_DEFAULT_AUTOVACUUM=1
+#cgo LDFLAGS: -lm
+*/
+import "C"
diff --git a/sqlite3_opt_vacuum_incr.go b/sqlite3_opt_vacuum_incr.go
new file mode 100644
index 0000000..62bbf50
--- /dev/null
+++ b/sqlite3_opt_vacuum_incr.go
@@ -0,0 +1,15 @@
+// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
+// Copyright (C) 2018 G.J.R. Timmer <gjr.timmer@gmail.com>.
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file.
+
+// +build sqlite_vacuum_incr
+
+package sqlite3
+
+/*
+#cgo CFLAGS: -DSQLITE_DEFAULT_AUTOVACUUM=2
+#cgo LDFLAGS: -lm
+*/
+import "C"
diff --git a/sqlite3_vtable.go b/sqlite3_opt_vtable.go
similarity index 98%
rename from sqlite3_vtable.go
rename to sqlite3_opt_vtable.go
index 8bef291..73d66c0 100644
--- a/sqlite3_vtable.go
+++ b/sqlite3_opt_vtable.go
@@ -2,14 +2,18 @@
 //
 // Use of this source code is governed by an MIT-style
 // license that can be found in the LICENSE file.
-// +build vtable
+
+// +build sqlite_vtable vtable
 
 package sqlite3
 
 /*
 #cgo CFLAGS: -std=gnu99
-#cgo CFLAGS: -DSQLITE_ENABLE_RTREE -DSQLITE_THREADSAFE
-#cgo CFLAGS: -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_FTS3_PARENTHESIS -DSQLITE_ENABLE_FTS4_UNICODE61
+#cgo CFLAGS: -DSQLITE_ENABLE_RTREE
+#cgo CFLAGS: -DSQLITE_THREADSAFE
+#cgo CFLAGS: -DSQLITE_ENABLE_FTS3
+#cgo CFLAGS: -DSQLITE_ENABLE_FTS3_PARENTHESIS
+#cgo CFLAGS: -DSQLITE_ENABLE_FTS4_UNICODE61
 #cgo CFLAGS: -DSQLITE_TRACE_SIZE_LIMIT=15
 #cgo CFLAGS: -DSQLITE_ENABLE_COLUMN_METADATA=1
 #cgo CFLAGS: -Wno-deprecated-declarations
diff --git a/sqlite3_other.go b/sqlite3_other.go
index f721b5e..b09f48e 100644
--- a/sqlite3_other.go
+++ b/sqlite3_other.go
@@ -2,6 +2,7 @@
 //
 // Use of this source code is governed by an MIT-style
 // license that can be found in the LICENSE file.
+
 // +build !windows
 
 package sqlite3
diff --git a/sqlite3_solaris.go b/sqlite3_solaris.go
index dbbdf75..ea41c66 100644
--- a/sqlite3_solaris.go
+++ b/sqlite3_solaris.go
@@ -2,6 +2,7 @@
 //
 // Use of this source code is governed by an MIT-style
 // license that can be found in the LICENSE file.
+
 // +build solaris
 
 package sqlite3
diff --git a/sqlite3_trace.go b/sqlite3_trace.go
index 8f90a56..ee93a0f 100644
--- a/sqlite3_trace.go
+++ b/sqlite3_trace.go
@@ -2,7 +2,8 @@
 //
 // Use of this source code is governed by an MIT-style
 // license that can be found in the LICENSE file.
-// +build trace
+
+// +build sqlite_trace trace
 
 package sqlite3
 
diff --git a/sqlite3_usleep_windows.go b/sqlite3_usleep_windows.go
new file mode 100644
index 0000000..1971a48
--- /dev/null
+++ b/sqlite3_usleep_windows.go
@@ -0,0 +1,39 @@
+// Copyright (C) 2018 G.J.R. Timmer <gjr.timmer@gmail.com>.
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file.
+
+// +build cgo
+
+package sqlite3
+
+// usleep is a function available on *nix based systems.
+// This function is not present in Windows.
+// Windows has a sleep function but this works with seconds
+// and not with microseconds as usleep.
+//
+// This code should improve performance on windows because
+// without the presence of usleep SQLite waits 1 second.
+//
+// Source: https://stackoverflow.com/questions/5801813/c-usleep-is-obsolete-workarounds-for-windows-mingw?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa
+
+/*
+#include <windows.h>
+
+void usleep(__int64 usec)
+{
+    HANDLE timer;
+    LARGE_INTEGER ft;
+
+    // Convert to 100 nanosecond interval, negative value indicates relative time
+    ft.QuadPart = -(10*usec);
+
+    timer = CreateWaitableTimer(NULL, TRUE, NULL);
+    SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0);
+    WaitForSingleObject(timer, INFINITE);
+    CloseHandle(timer);
+}
+*/
+import "C"
+
+// EOF
diff --git a/sqlite3_windows.go b/sqlite3_windows.go
index 163e8f7..4222e7f 100644
--- a/sqlite3_windows.go
+++ b/sqlite3_windows.go
@@ -2,13 +2,17 @@
 //
 // Use of this source code is governed by an MIT-style
 // license that can be found in the LICENSE file.
+
 // +build windows
 
 package sqlite3
 
 /*
-#cgo CFLAGS: -I. -fno-stack-check -fno-stack-protector -mno-stack-arg-probe
-#cgo windows,386 CFLAGS: -D_USE_32BIT_TIME_T
+#cgo CFLAGS: -I.
+#cgo CFLAGS: -fno-stack-check
+#cgo CFLAGS: -fno-stack-protector
+#cgo CFLAGS: -mno-stack-arg-probe
 #cgo LDFLAGS: -lmingwex -lmingw32
+#cgo windows,386 CFLAGS: -D_USE_32BIT_TIME_T
 */
 import "C"
diff --git a/tool/upgrade.go b/tool/upgrade.go
deleted file mode 100644
index 275f915..0000000
--- a/tool/upgrade.go
+++ /dev/null
@@ -1,111 +0,0 @@
-// +build ignore
-
-package main
-
-import (
-	"archive/zip"
-	"bufio"
-	"bytes"
-	"fmt"
-	"io"
-	"io/ioutil"
-	"log"
-	"net/http"
-	"os"
-	"path"
-	"path/filepath"
-	"strings"
-
-	"github.com/PuerkitoBio/goquery"
-)
-
-func main() {
-	site := "https://www.sqlite.org/download.html"
-	fmt.Printf("scraping %v\n", site)
-	doc, err := goquery.NewDocument(site)
-	if err != nil {
-		log.Fatal(err)
-	}
-	var url string
-	doc.Find("a").Each(func(_ int, s *goquery.Selection) {
-		if url == "" && strings.HasPrefix(s.Text(), "sqlite-amalgamation-") {
-			url = "https://www.sqlite.org/2018/" + s.Text()
-		}
-	})
-	if url == "" {
-		return
-	}
-	fmt.Printf("downloading %v\n", url)
-	resp, err := http.Get(url)
-	if err != nil {
-		log.Fatal(err)
-	}
-
-	b, err := ioutil.ReadAll(resp.Body)
-	if err != nil {
-		resp.Body.Close()
-		log.Fatal(err)
-	}
-
-	fmt.Printf("extracting %v\n", path.Base(url))
-	r, err := zip.NewReader(bytes.NewReader(b), resp.ContentLength)
-	if err != nil {
-		resp.Body.Close()
-		log.Fatal(err)
-	}
-	resp.Body.Close()
-
-	for _, zf := range r.File {
-		var f *os.File
-		switch path.Base(zf.Name) {
-		case "sqlite3.c":
-			f, err = os.Create("sqlite3-binding.c")
-		case "sqlite3.h":
-			f, err = os.Create("sqlite3-binding.h")
-		case "sqlite3ext.h":
-			f, err = os.Create("sqlite3ext.h")
-		default:
-			continue
-		}
-		if err != nil {
-			log.Fatal(err)
-		}
-		zr, err := zf.Open()
-		if err != nil {
-			log.Fatal(err)
-		}
-
-		_, err = io.WriteString(f, "#ifndef USE_LIBSQLITE3\n")
-		if err != nil {
-			zr.Close()
-			f.Close()
-			log.Fatal(err)
-		}
-		scanner := bufio.NewScanner(zr)
-		for scanner.Scan() {
-			text := scanner.Text()
-			if text == `#include "sqlite3.h"` {
-				text = `#include "sqlite3-binding.h"`
-			}
-			_, err = fmt.Fprintln(f, text)
-			if err != nil {
-				break
-			}
-		}
-		err = scanner.Err()
-		if err != nil {
-			zr.Close()
-			f.Close()
-			log.Fatal(err)
-		}
-		_, err = io.WriteString(f, "#else // USE_LIBSQLITE3\n // If users really want to link against the system sqlite3 we\n// need to make this file a noop.\n #endif")
-		if err != nil {
-			zr.Close()
-			f.Close()
-			log.Fatal(err)
-		}
-		zr.Close()
-		f.Close()
-		fmt.Printf("extracted %v\n", filepath.Base(f.Name()))
-	}
-}
diff --git a/upgrade/package.go b/upgrade/package.go
new file mode 100644
index 0000000..2b12a77
--- /dev/null
+++ b/upgrade/package.go
@@ -0,0 +1,7 @@
+// Package upgrade
+//
+// Dummy to ensure package can be loaded
+//
+// This file is to avoid the following error:
+// can't load package: package go-sqlite3/upgrade: build constraints exclude all Go files in go-sqlite3\upgrade
+package upgrade
diff --git a/upgrade/profile.goconvey b/upgrade/profile.goconvey
new file mode 100644
index 0000000..55049ab
--- /dev/null
+++ b/upgrade/profile.goconvey
@@ -0,0 +1,2 @@
+// Goconvey Profile
+IGNORE
diff --git a/upgrade/upgrade.go b/upgrade/upgrade.go
new file mode 100644
index 0000000..d800227
--- /dev/null
+++ b/upgrade/upgrade.go
@@ -0,0 +1,218 @@
+// +build !cgo
+// +build upgrade
+
+package main
+
+import (
+	"archive/zip"
+	"bufio"
+	"bytes"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"log"
+	"net/http"
+	"os"
+	"path"
+	"path/filepath"
+	"strings"
+	"time"
+
+	"github.com/PuerkitoBio/goquery"
+)
+
+func download(prefix string) (url string, content []byte, err error) {
+	year := time.Now().Year()
+
+	site := "https://www.sqlite.org/download.html"
+	//fmt.Printf("scraping %v\n", site)
+	doc, err := goquery.NewDocument(site)
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	doc.Find("a").Each(func(_ int, s *goquery.Selection) {
+		if strings.HasPrefix(s.Text(), prefix) {
+			url = fmt.Sprintf("https://www.sqlite.org/%d/", year) + s.Text()
+		}
+	})
+
+	if url == "" {
+		return "", nil, fmt.Errorf("Unable to find prefix '%s' on sqlite.org", prefix)
+	}
+
+	fmt.Printf("Downloading %v\n", url)
+	resp, err := http.Get(url)
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	// Ready Body Content
+	content, err = ioutil.ReadAll(resp.Body)
+	defer resp.Body.Close()
+	if err != nil {
+		return "", nil, err
+	}
+
+	return url, content, nil
+}
+
+func mergeFile(src string, dst string) error {
+	defer func() error {
+		fmt.Printf("Removing: %s\n", src)
+		err := os.Remove(src)
+
+		if err != nil {
+			return err
+		}
+
+		return nil
+	}()
+
+	// Open destination
+	fdst, err := os.OpenFile(dst, os.O_APPEND|os.O_WRONLY, 0666)
+	if err != nil {
+		return err
+	}
+	defer fdst.Close()
+
+	// Read source content
+	content, err := ioutil.ReadFile(src)
+	if err != nil {
+		return err
+	}
+
+	// Add Additional newline
+	if _, err := fdst.WriteString("\n"); err != nil {
+		return err
+	}
+
+	fmt.Printf("Merging: %s into %s\n", src, dst)
+	if _, err = fdst.Write(content); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func main() {
+	fmt.Println("Go-SQLite3 Upgrade Tool")
+
+	// Download Amalgamation
+	_, amalgamation, err := download("sqlite-amalgamation-")
+	if err != nil {
+		fmt.Println("Failed to download: sqlite-amalgamation; %s", err)
+	}
+
+	// Download Source
+	_, source, err := download("sqlite-src-")
+	if err != nil {
+		fmt.Println("Failed to download: sqlite-src; %s", err)
+	}
+
+	// Create Amalgamation Zip Reader
+	rAmalgamation, err := zip.NewReader(bytes.NewReader(amalgamation), int64(len(amalgamation)))
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	// Create Source Zip Reader
+	rSource, err := zip.NewReader(bytes.NewReader(source), int64(len(source)))
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	// Extract Amalgamation
+	for _, zf := range rAmalgamation.File {
+		var f *os.File
+		switch path.Base(zf.Name) {
+		case "sqlite3.c":
+			f, err = os.Create("sqlite3-binding.c")
+		case "sqlite3.h":
+			f, err = os.Create("sqlite3-binding.h")
+		case "sqlite3ext.h":
+			f, err = os.Create("sqlite3ext.h")
+		default:
+			continue
+		}
+		if err != nil {
+			log.Fatal(err)
+		}
+		zr, err := zf.Open()
+		if err != nil {
+			log.Fatal(err)
+		}
+
+		_, err = io.WriteString(f, "#ifndef USE_LIBSQLITE3\n")
+		if err != nil {
+			zr.Close()
+			f.Close()
+			log.Fatal(err)
+		}
+		scanner := bufio.NewScanner(zr)
+		for scanner.Scan() {
+			text := scanner.Text()
+			if text == `#include "sqlite3.h"` {
+				text = `#include "sqlite3-binding.h"`
+			}
+			_, err = fmt.Fprintln(f, text)
+			if err != nil {
+				break
+			}
+		}
+		err = scanner.Err()
+		if err != nil {
+			zr.Close()
+			f.Close()
+			log.Fatal(err)
+		}
+		_, err = io.WriteString(f, "#else // USE_LIBSQLITE3\n // If users really want to link against the system sqlite3 we\n// need to make this file a noop.\n #endif")
+		if err != nil {
+			zr.Close()
+			f.Close()
+			log.Fatal(err)
+		}
+		zr.Close()
+		f.Close()
+		fmt.Printf("Extracted: %v\n", filepath.Base(f.Name()))
+	}
+
+	//Extract Source
+	for _, zf := range rSource.File {
+		var f *os.File
+		switch path.Base(zf.Name) {
+		case "userauth.c":
+			f, err = os.Create("userauth.c")
+		case "sqlite3userauth.h":
+			f, err = os.Create("userauth.h")
+		default:
+			continue
+		}
+		if err != nil {
+			log.Fatal(err)
+		}
+		zr, err := zf.Open()
+		if err != nil {
+			log.Fatal(err)
+		}
+
+		_, err = io.Copy(f, zr)
+		if err != nil {
+			log.Fatal(err)
+		}
+
+		zr.Close()
+		f.Close()
+		fmt.Printf("extracted %v\n", filepath.Base(f.Name()))
+	}
+
+	// Merge SQLite User Authentication into amalgamation
+	if err := mergeFile("userauth.c", "sqlite3-binding.c"); err != nil {
+		log.Fatal(err)
+	}
+	if err := mergeFile("userauth.h", "sqlite3-binding.h"); err != nil {
+		log.Fatal(err)
+	}
+
+	os.Exit(0)
+}


Test patch:
diff --git a/sqlite3_opt_userauth_test.go b/sqlite3_opt_userauth_test.go
new file mode 100644
index 0000000..1a29575
--- /dev/null
+++ b/sqlite3_opt_userauth_test.go
@@ -0,0 +1,1382 @@
+// Copyright (C) 2018 G.J.R. Timmer <gjr.timmer@gmail.com>.
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file.
+
+// +build sqlite_userauth
+
+package sqlite3
+
+import (
+	"database/sql"
+	"fmt"
+	"os"
+	"testing"
+
+	. "github.com/smartystreets/goconvey/convey"
+)
+
+var (
+	conn        *SQLiteConn
+	connect     func(t *testing.T, f string, username, password string) (file string, db *sql.DB, c *SQLiteConn, err error)
+	authEnabled func(db *sql.DB) (exists bool, err error)
+	addUser     func(db *sql.DB, username, password string, admin int) (rv int, err error)
+	userExists  func(db *sql.DB, username string) (rv int, err error)
+	isAdmin     func(db *sql.DB, username string) (rv bool, err error)
+	modifyUser  func(db *sql.DB, username, password string, admin int) (rv int, err error)
+	deleteUser  func(db *sql.DB, username string) (rv int, err error)
+)
+
+func init() {
+	// Create database connection
+	sql.Register("sqlite3_with_conn",
+		&SQLiteDriver{
+			ConnectHook: func(c *SQLiteConn) error {
+				conn = c
+				return nil
+			},
+		})
+
+	connect = func(t *testing.T, f string, username, password string) (file string, db *sql.DB, c *SQLiteConn, err error) {
+		conn = nil // Clear connection
+		file = f   // Copy provided file (f) => file
+		if file == "" {
+			// Create dummy file
+			file = TempFilename(t)
+		}
+
+		db, err = sql.Open("sqlite3_with_conn", "file:"+file+fmt.Sprintf("?_auth&_auth_user=%s&_auth_pass=%s", username, password))
+		if err != nil {
+			defer os.Remove(file)
+			return file, nil, nil, err
+		}
+
+		// Dummy query to force connection and database creation
+		// Will return ErrUnauthorized (SQLITE_AUTH) if user authentication fails
+		if _, err = db.Exec("SELECT 1;"); err != nil {
+			defer os.Remove(file)
+			defer db.Close()
+			return file, nil, nil, err
+		}
+		c = conn
+
+		return
+	}
+
+	authEnabled = func(db *sql.DB) (exists bool, err error) {
+		err = db.QueryRow("select count(type) from sqlite_master WHERE type='table' and name='sqlite_user';").Scan(&exists)
+		return
+	}
+
+	addUser = func(db *sql.DB, username, password string, admin int) (rv int, err error) {
+		err = db.QueryRow("select auth_user_add(?, ?, ?);", username, password, admin).Scan(&rv)
+		return
+	}
+
+	userExists = func(db *sql.DB, username string) (rv int, err error) {
+		err = db.QueryRow("select count(uname) from sqlite_user where uname=?", username).Scan(&rv)
+		return
+	}
+
+	isAdmin = func(db *sql.DB, username string) (rv bool, err error) {
+		err = db.QueryRow("select isAdmin from sqlite_user where uname=?", username).Scan(&rv)
+		return
+	}
+
+	modifyUser = func(db *sql.DB, username, password string, admin int) (rv int, err error) {
+		err = db.QueryRow("select auth_user_change(?, ?, ?);", username, password, admin).Scan(&rv)
+		return
+	}
+
+	deleteUser = func(db *sql.DB, username string) (rv int, err error) {
+		err = db.QueryRow("select auth_user_delete(?);", username).Scan(&rv)
+		return
+	}
+}
+
+func TestUserAuthentication(t *testing.T) {
+	Convey("Create Database", t, func() {
+		f, db, c, err := connect(t, "", "admin", "admin")
+		So(db, ShouldNotBeNil)
+		So(c, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer db.Close()
+		defer os.Remove(f)
+
+		b, err := authEnabled(db)
+		So(b, ShouldEqual, true)
+		So(err, ShouldBeNil)
+
+		e, err := userExists(db, "admin")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err := isAdmin(db, "admin")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, true)
+	})
+
+	Convey("Authorization Success", t, func() {
+		f1, db1, c1, err := connect(t, "", "admin", "admin")
+		So(f1, ShouldNotBeBlank)
+		So(db1, ShouldNotBeNil)
+		So(c1, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer os.Remove(f1)
+
+		e, err := userExists(db1, "admin")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err := isAdmin(db1, "admin")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, true)
+		db1.Close()
+
+		// Preform authentication
+		f2, db2, c2, err := connect(t, f1, "admin", "admin")
+		So(f2, ShouldNotBeBlank)
+		So(f1, ShouldEqual, f2)
+		So(db2, ShouldNotBeNil)
+		So(c2, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer db2.Close()
+	})
+
+	Convey("Authorization Success (*SQLiteConn)", t, func() {
+		f1, db1, c1, err := connect(t, "", "admin", "admin")
+		So(f1, ShouldNotBeBlank)
+		So(db1, ShouldNotBeNil)
+		So(c1, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer os.Remove(f1)
+		defer db1.Close()
+
+		e, err := userExists(db1, "admin")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err := isAdmin(db1, "admin")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, true)
+
+		// Test lower level authentication
+		err = c1.Authenticate("admin", "admin")
+		So(err, ShouldBeNil)
+	})
+
+	Convey("Authorization Failed", t, func() {
+		f1, db1, c1, err := connect(t, "", "admin", "admin")
+		So(f1, ShouldNotBeBlank)
+		So(db1, ShouldNotBeNil)
+		So(c1, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer os.Remove(f1)
+
+		e, err := userExists(db1, "admin")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err := isAdmin(db1, "admin")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, true)
+		db1.Close()
+
+		// Perform Invalid Authentication when we connect
+		// to a database
+		f2, db2, c2, err := connect(t, f1, "admin", "invalid")
+		So(f2, ShouldNotBeBlank)
+		So(f1, ShouldEqual, f2)
+		So(db2, ShouldBeNil)
+		So(c2, ShouldBeNil)
+		So(err, ShouldEqual, ErrUnauthorized)
+	})
+
+	Convey("Authorization Failed (*SQLiteConn)", t, func() {
+		f1, db1, c1, err := connect(t, "", "admin", "admin")
+		So(f1, ShouldNotBeBlank)
+		So(db1, ShouldNotBeNil)
+		So(c1, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer os.Remove(f1)
+		defer db1.Close()
+
+		e, err := userExists(db1, "admin")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err := isAdmin(db1, "admin")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, true)
+
+		// Test lower level authentication
+		// We require a successful *SQLiteConn to test this.
+		err = c1.Authenticate("admin", "invalid")
+		So(err, ShouldNotBeNil)
+		So(err, ShouldEqual, ErrUnauthorized)
+	})
+}
+
+func TestUserAuthenticationAddUser(t *testing.T) {
+	Convey("Add Admin User", t, func() {
+		f, db, c, err := connect(t, "", "admin", "admin")
+		So(db, ShouldNotBeNil)
+		So(c, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer os.Remove(f)
+		defer db.Close()
+
+		e, err := userExists(db, "admin")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err := isAdmin(db, "admin")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, true)
+
+		// Add Admin User
+		rv, err := addUser(db, "admin2", "admin2", 1)
+		So(rv, ShouldEqual, 0) // 0 == C.SQLITE_OK
+		So(err, ShouldBeNil)
+
+		e, err = userExists(db, "admin2")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err = isAdmin(db, "admin2")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, true)
+	})
+
+	Convey("Add Admin User (*SQLiteConn)", t, func() {
+		f, db, c, err := connect(t, "", "admin", "admin")
+		So(db, ShouldNotBeNil)
+		So(c, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer os.Remove(f)
+		defer db.Close()
+
+		e, err := userExists(db, "admin")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err := isAdmin(db, "admin")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, true)
+
+		// Test lower level AuthUserAdd
+		err = c.AuthUserAdd("admin2", "admin2", true)
+		So(err, ShouldBeNil)
+
+		e, err = userExists(db, "admin2")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err = isAdmin(db, "admin2")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, true)
+	})
+
+	Convey("Add Normal User", t, func() {
+		f, db, c, err := connect(t, "", "admin", "admin")
+		So(db, ShouldNotBeNil)
+		So(c, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer os.Remove(f)
+		defer db.Close()
+
+		e, err := userExists(db, "admin")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err := isAdmin(db, "admin")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, true)
+
+		// Add Normal User
+		rv, err := addUser(db, "user", "user", 0)
+		So(rv, ShouldEqual, 0) // 0 == C.SQLITE_OK
+		So(err, ShouldBeNil)
+
+		e, err = userExists(db, "user")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err = isAdmin(db, "user")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, false)
+	})
+
+	Convey("Add Normal User (*SQLiteConn)", t, func() {
+		f, db, c, err := connect(t, "", "admin", "admin")
+		So(db, ShouldNotBeNil)
+		So(c, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer os.Remove(f)
+		defer db.Close()
+
+		e, err := userExists(db, "admin")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err := isAdmin(db, "admin")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, true)
+
+		// Test lower level AuthUserAdd
+		err = c.AuthUserAdd("user", "user", false)
+		So(err, ShouldBeNil)
+
+		e, err = userExists(db, "user")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err = isAdmin(db, "user")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, false)
+	})
+
+	Convey("Add Admin User Insufficient Privileges", t, func() {
+		f1, db1, c1, err := connect(t, "", "admin", "admin")
+		So(f1, ShouldNotBeBlank)
+		So(db1, ShouldNotBeNil)
+		So(c1, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer os.Remove(f1)
+
+		e, err := userExists(db1, "admin")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err := isAdmin(db1, "admin")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, true)
+
+		// Add Normal User
+		rv, err := addUser(db1, "user", "user", 0)
+		So(rv, ShouldEqual, 0) // 0 == C.SQLITE_OK
+		So(err, ShouldBeNil)
+
+		e, err = userExists(db1, "user")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err = isAdmin(db1, "user")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, false)
+		db1.Close()
+
+		// Reconnect as normal user
+		f2, db2, c2, err := connect(t, f1, "user", "user")
+		So(f2, ShouldNotBeBlank)
+		So(f1, ShouldEqual, f2)
+		So(db2, ShouldNotBeNil)
+		So(c2, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer db2.Close()
+
+		// Add Admin User
+		// Because 'user' is not admin
+		// Adding an admin user should now fail
+		// because we have insufficient privileges
+		rv, err = addUser(db2, "admin2", "admin2", 1)
+		So(rv, ShouldEqual, SQLITE_AUTH)
+		So(err, ShouldBeNil)
+	})
+
+	Convey("Add Admin User Insufficient Privileges (*SQLiteConn)", t, func() {
+		f1, db1, c1, err := connect(t, "", "admin", "admin")
+		So(f1, ShouldNotBeBlank)
+		So(db1, ShouldNotBeNil)
+		So(c1, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer os.Remove(f1)
+
+		e, err := userExists(db1, "admin")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err := isAdmin(db1, "admin")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, true)
+
+		// Add Normal User
+		rv, err := addUser(db1, "user", "user", 0)
+		So(rv, ShouldEqual, 0) // 0 == C.SQLITE_OK
+		So(err, ShouldBeNil)
+
+		e, err = userExists(db1, "user")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err = isAdmin(db1, "user")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, false)
+		db1.Close()
+
+		// Reconnect as normal user
+		f2, db2, c2, err := connect(t, f1, "user", "user")
+		So(f2, ShouldNotBeBlank)
+		So(f1, ShouldEqual, f2)
+		So(db2, ShouldNotBeNil)
+		So(c2, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer db2.Close()
+
+		// Add Admin User
+		// Because 'user' is not admin
+		// Adding an admin user should now fail
+		// because we have insufficient privileges
+		err = c2.AuthUserAdd("admin2", "admin2", true)
+		So(err, ShouldNotBeNil)
+		So(err, ShouldEqual, ErrAdminRequired)
+	})
+
+	Convey("Add Normal User Insufficient Privileges", t, func() {
+		f1, db1, c1, err := connect(t, "", "admin", "admin")
+		So(f1, ShouldNotBeBlank)
+		So(db1, ShouldNotBeNil)
+		So(c1, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer os.Remove(f1)
+
+		e, err := userExists(db1, "admin")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err := isAdmin(db1, "admin")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, true)
+
+		// Add Normal User
+		rv, err := addUser(db1, "user", "user", 0)
+		So(rv, ShouldEqual, 0) // 0 == C.SQLITE_OK
+		So(err, ShouldBeNil)
+
+		e, err = userExists(db1, "user")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err = isAdmin(db1, "user")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, false)
+		db1.Close()
+
+		// Reconnect as normal user
+		f2, db2, c2, err := connect(t, f1, "user", "user")
+		So(f2, ShouldNotBeBlank)
+		So(f1, ShouldEqual, f2)
+		So(db2, ShouldNotBeNil)
+		So(c2, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer db2.Close()
+
+		// Add Normal User
+		// Because 'user' is not admin
+		// Adding an normal user should now fail
+		// because we have insufficient privileges
+		rv, err = addUser(db2, "user2", "user2", 0)
+		So(rv, ShouldEqual, SQLITE_AUTH)
+		So(err, ShouldBeNil)
+	})
+
+	Convey("Add Normal User Insufficient Privileges (*SQLiteConn)", t, func() {
+		f1, db1, c1, err := connect(t, "", "admin", "admin")
+		So(f1, ShouldNotBeBlank)
+		So(db1, ShouldNotBeNil)
+		So(c1, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer os.Remove(f1)
+
+		e, err := userExists(db1, "admin")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err := isAdmin(db1, "admin")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, true)
+
+		// Add Normal User
+		rv, err := addUser(db1, "user", "user", 0)
+		So(rv, ShouldEqual, 0) // 0 == C.SQLITE_OK
+		So(err, ShouldBeNil)
+
+		e, err = userExists(db1, "user")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err = isAdmin(db1, "user")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, false)
+		db1.Close()
+
+		// Reconnect as normal user
+		f2, db2, c2, err := connect(t, f1, "user", "user")
+		So(f2, ShouldNotBeBlank)
+		So(f1, ShouldEqual, f2)
+		So(db2, ShouldNotBeNil)
+		So(c2, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer db2.Close()
+
+		// Add Normal User
+		// Because 'user' is not admin
+		// Adding an normal user should now fail
+		// because we have insufficient privileges
+		// Test lower level AuthUserAdd
+		err = c2.AuthUserAdd("user2", "user2", false)
+		So(err, ShouldNotBeNil)
+		So(err, ShouldEqual, ErrAdminRequired)
+	})
+}
+
+func TestUserAuthenticationModifyUser(t *testing.T) {
+	Convey("Modify Current Connection Password", t, func() {
+		f1, db1, c1, err := connect(t, "", "admin", "admin")
+		So(f1, ShouldNotBeBlank)
+		So(db1, ShouldNotBeNil)
+		So(c1, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer os.Remove(f1)
+
+		e, err := userExists(db1, "admin")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err := isAdmin(db1, "admin")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, true)
+
+		// Modify Password
+		rv, err := modifyUser(db1, "admin", "admin2", 1)
+		So(err, ShouldBeNil)
+		So(rv, ShouldEqual, 0)
+		db1.Close()
+
+		// Reconnect with new password
+		f2, db2, c2, err := connect(t, f1, "admin", "admin2")
+		So(f2, ShouldNotBeBlank)
+		So(f1, ShouldEqual, f2)
+		So(db2, ShouldNotBeNil)
+		So(c2, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer db2.Close()
+	})
+
+	Convey("Modify Current Connection Password (*SQLiteConn)", t, func() {
+		f1, db1, c1, err := connect(t, "", "admin", "admin")
+		So(f1, ShouldNotBeBlank)
+		So(db1, ShouldNotBeNil)
+		So(c1, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer os.Remove(f1)
+		defer db1.Close()
+
+		e, err := userExists(db1, "admin")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err := isAdmin(db1, "admin")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, true)
+
+		// Modify password through (*SQLiteConn)
+		err = c1.AuthUserChange("admin", "admin2", true)
+		So(err, ShouldBeNil)
+
+		// Reconnect with new password
+		f2, db2, c2, err := connect(t, f1, "admin", "admin2")
+		So(f2, ShouldNotBeBlank)
+		So(f1, ShouldEqual, f2)
+		So(db2, ShouldNotBeNil)
+		So(c2, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer db2.Close()
+	})
+
+	Convey("Modify Current Connection Admin Flag", t, func() {
+		f1, db1, c1, err := connect(t, "", "admin", "admin")
+		So(f1, ShouldNotBeBlank)
+		So(db1, ShouldNotBeNil)
+		So(c1, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer os.Remove(f1)
+		defer db1.Close()
+
+		e, err := userExists(db1, "admin")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err := isAdmin(db1, "admin")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, true)
+
+		// Modify Administrator Flag
+		// Because we are current logged in as 'admin'
+		// Changing our own admin flag should fail.
+		rv, err := modifyUser(db1, "admin", "admin", 0)
+		So(err, ShouldBeNil)
+		So(rv, ShouldEqual, SQLITE_AUTH)
+	})
+
+	Convey("Modify Current Connection Admin Flag (*SQLiteConn)", t, func() {
+		f1, db1, c1, err := connect(t, "", "admin", "admin")
+		So(f1, ShouldNotBeBlank)
+		So(db1, ShouldNotBeNil)
+		So(c1, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer os.Remove(f1)
+		defer db1.Close()
+
+		e, err := userExists(db1, "admin")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err := isAdmin(db1, "admin")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, true)
+
+		// Modify admin flag through (*SQLiteConn)
+		// Because we are current logged in as 'admin'
+		// Changing our own admin flag should fail.
+		err = c1.AuthUserChange("admin", "admin", false)
+		So(err, ShouldNotBeNil)
+		So(err, ShouldEqual, ErrAdminRequired)
+	})
+
+	Convey("Modify Other User Password", t, func() {
+		f1, db1, c1, err := connect(t, "", "admin", "admin")
+		So(f1, ShouldNotBeBlank)
+		So(db1, ShouldNotBeNil)
+		So(c1, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer os.Remove(f1)
+
+		e, err := userExists(db1, "admin")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err := isAdmin(db1, "admin")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, true)
+
+		// Add Normal User
+		rv, err := addUser(db1, "user", "user", 0)
+		So(rv, ShouldEqual, 0) // 0 == C.SQLITE_OK
+		So(err, ShouldBeNil)
+
+		// Verify User
+		e, err = userExists(db1, "user")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err = isAdmin(db1, "user")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, false)
+
+		// Modify Password for user
+		rv, err = modifyUser(db1, "user", "user2", 0)
+		So(err, ShouldBeNil)
+		So(rv, ShouldEqual, 0)
+		db1.Close()
+
+		// Reconnect as normal user with new password
+		f2, db2, c2, err := connect(t, f1, "user", "user2")
+		So(f2, ShouldNotBeBlank)
+		So(f1, ShouldEqual, f2)
+		So(db2, ShouldNotBeNil)
+		So(c2, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer db2.Close()
+	})
+
+	Convey("Modify Other User Password (*SQLiteConn)", t, func() {
+		f1, db1, c1, err := connect(t, "", "admin", "admin")
+		So(f1, ShouldNotBeBlank)
+		So(db1, ShouldNotBeNil)
+		So(c1, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer os.Remove(f1)
+
+		e, err := userExists(db1, "admin")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err := isAdmin(db1, "admin")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, true)
+
+		// Add Normal User
+		rv, err := addUser(db1, "user", "user", 0)
+		So(rv, ShouldEqual, 0) // 0 == C.SQLITE_OK
+		So(err, ShouldBeNil)
+
+		// Verify User
+		e, err = userExists(db1, "user")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err = isAdmin(db1, "user")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, false)
+
+		// Modify user password through (*SQLiteConn)
+		// Because we are still logged in as admin
+		// this should succeed.
+		err = c1.AuthUserChange("admin", "admin", false)
+		So(err, ShouldNotBeNil)
+	})
+
+	Convey("Modify Other User Admin Flag", t, func() {
+		f1, db1, c1, err := connect(t, "", "admin", "admin")
+		So(f1, ShouldNotBeBlank)
+		So(db1, ShouldNotBeNil)
+		So(c1, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer os.Remove(f1)
+
+		e, err := userExists(db1, "admin")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err := isAdmin(db1, "admin")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, true)
+
+		// Add Normal User
+		rv, err := addUser(db1, "user", "user", 0)
+		So(rv, ShouldEqual, 0) // 0 == C.SQLITE_OK
+		So(err, ShouldBeNil)
+
+		// Verify User
+		e, err = userExists(db1, "user")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err = isAdmin(db1, "user")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, false)
+
+		// Modify Password for user
+		// Because we are logged in as admin
+		// This call should succeed.
+		rv, err = modifyUser(db1, "user", "user", 1)
+		So(err, ShouldBeNil)
+		So(rv, ShouldEqual, 0)
+		db1.Close()
+
+		// Reconnect as normal user with new password
+		f2, db2, c2, err := connect(t, f1, "user", "user")
+		So(f2, ShouldNotBeBlank)
+		So(f1, ShouldEqual, f2)
+		So(db2, ShouldNotBeNil)
+		So(c2, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer db2.Close()
+	})
+
+	Convey("Modify Other User Admin Flag (*SQLiteConn)", t, func() {
+		f1, db1, c1, err := connect(t, "", "admin", "admin")
+		So(f1, ShouldNotBeBlank)
+		So(db1, ShouldNotBeNil)
+		So(c1, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer os.Remove(f1)
+
+		e, err := userExists(db1, "admin")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err := isAdmin(db1, "admin")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, true)
+
+		// Add Normal User
+		rv, err := addUser(db1, "user", "user", 0)
+		So(rv, ShouldEqual, 0) // 0 == C.SQLITE_OK
+		So(err, ShouldBeNil)
+
+		// Verify User
+		e, err = userExists(db1, "user")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err = isAdmin(db1, "user")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, false)
+
+		// Modify user password through (*SQLiteConn)
+		// Because we are still logged in as admin
+		// this should succeed.
+		err = c1.AuthUserChange("user", "user", true)
+		So(err, ShouldBeNil)
+	})
+
+	Convey("Modify Other User Password as Non-Admin", t, func() {
+		f1, db1, c1, err := connect(t, "", "admin", "admin")
+		So(f1, ShouldNotBeBlank)
+		So(db1, ShouldNotBeNil)
+		So(c1, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer os.Remove(f1)
+
+		e, err := userExists(db1, "admin")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err := isAdmin(db1, "admin")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, true)
+
+		// Add Normal User
+		rv, err := addUser(db1, "user", "user", 0)
+		So(rv, ShouldEqual, 0) // 0 == C.SQLITE_OK
+		So(err, ShouldBeNil)
+
+		// Add Normal User
+		rv, err = addUser(db1, "user2", "user2", 0)
+		So(rv, ShouldEqual, 0) // 0 == C.SQLITE_OK
+		So(err, ShouldBeNil)
+
+		// Verify 'user'
+		e, err = userExists(db1, "user")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err = isAdmin(db1, "user")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, false)
+
+		// Verify 'user2'
+		e, err = userExists(db1, "user2")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err = isAdmin(db1, "user2")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, false)
+		db1.Close()
+
+		// Reconnect as normal user
+		f2, db2, c2, err := connect(t, f1, "user", "user")
+		So(f2, ShouldNotBeBlank)
+		So(f1, ShouldEqual, f2)
+		So(db2, ShouldNotBeNil)
+		So(c2, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer db2.Close()
+
+		// Modify password for user as normal user
+		// Because 'user' is not admin
+		// Modifying password as a normal user should now fail
+		// because we have insufficient privileges
+		rv, err = modifyUser(db2, "user2", "invalid", 0)
+		So(err, ShouldBeNil)
+		So(rv, ShouldEqual, SQLITE_AUTH)
+	})
+
+	Convey("Modify Other User Password as Non-Admin", t, func() {
+		f1, db1, c1, err := connect(t, "", "admin", "admin")
+		So(f1, ShouldNotBeBlank)
+		So(db1, ShouldNotBeNil)
+		So(c1, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer os.Remove(f1)
+
+		e, err := userExists(db1, "admin")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err := isAdmin(db1, "admin")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, true)
+
+		// Add Normal User
+		rv, err := addUser(db1, "user", "user", 0)
+		So(rv, ShouldEqual, 0) // 0 == C.SQLITE_OK
+		So(err, ShouldBeNil)
+
+		// Add Normal User
+		rv, err = addUser(db1, "user2", "user2", 0)
+		So(rv, ShouldEqual, 0) // 0 == C.SQLITE_OK
+		So(err, ShouldBeNil)
+
+		// Verify 'user'
+		e, err = userExists(db1, "user")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err = isAdmin(db1, "user")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, false)
+
+		// Verify 'user2'
+		e, err = userExists(db1, "user2")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err = isAdmin(db1, "user2")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, false)
+		db1.Close()
+
+		// Reconnect as normal user
+		f2, db2, c2, err := connect(t, f1, "user", "user")
+		So(f2, ShouldNotBeBlank)
+		So(f1, ShouldEqual, f2)
+		So(db2, ShouldNotBeNil)
+		So(c2, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer db2.Close()
+
+		// Modify user password through (*SQLiteConn)
+		// for 'user2'
+		// Because 'user' is not admin
+		// Modifying password as a normal user should now fail
+		// because we have insufficient privileges
+		err = c2.AuthUserChange("user2", "invalid", false)
+		So(err, ShouldNotBeNil)
+		So(err, ShouldEqual, ErrAdminRequired)
+	})
+}
+
+func TestUserAuthenticationDeleteUser(t *testing.T) {
+	Convey("Delete User as Admin", t, func() {
+		f1, db1, c1, err := connect(t, "", "admin", "admin")
+		So(f1, ShouldNotBeBlank)
+		So(db1, ShouldNotBeNil)
+		So(c1, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer os.Remove(f1)
+
+		e, err := userExists(db1, "admin")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err := isAdmin(db1, "admin")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, true)
+
+		// Add Normal User
+		rv, err := addUser(db1, "user", "user", 0)
+		So(rv, ShouldEqual, 0) // 0 == C.SQLITE_OK
+		So(err, ShouldBeNil)
+
+		// Verify 'user'
+		e, err = userExists(db1, "user")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err = isAdmin(db1, "user")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, false)
+
+		rv, err = deleteUser(db1, "user")
+		So(err, ShouldBeNil)
+		So(rv, ShouldEqual, 0)
+
+		e, err = userExists(db1, "user")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 0)
+	})
+
+	Convey("Delete User as Admin (*SQLiteConn)", t, func() {
+		f1, db1, c1, err := connect(t, "", "admin", "admin")
+		So(f1, ShouldNotBeBlank)
+		So(db1, ShouldNotBeNil)
+		So(c1, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer os.Remove(f1)
+
+		e, err := userExists(db1, "admin")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err := isAdmin(db1, "admin")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, true)
+
+		// Add Normal User
+		rv, err := addUser(db1, "user", "user", 0)
+		So(rv, ShouldEqual, 0) // 0 == C.SQLITE_OK
+		So(err, ShouldBeNil)
+
+		// Verify 'user'
+		e, err = userExists(db1, "user")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err = isAdmin(db1, "user")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, false)
+
+		err = c1.AuthUserDelete("user")
+		So(err, ShouldBeNil)
+
+		e, err = userExists(db1, "user")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 0)
+	})
+
+	Convey("Delete User as Non-Admin", t, func() {
+		f1, db1, c1, err := connect(t, "", "admin", "admin")
+		So(f1, ShouldNotBeBlank)
+		So(db1, ShouldNotBeNil)
+		So(c1, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer os.Remove(f1)
+
+		e, err := userExists(db1, "admin")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err := isAdmin(db1, "admin")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, true)
+
+		// Add Normal User
+		rv, err := addUser(db1, "user", "user", 0)
+		So(rv, ShouldEqual, 0) // 0 == C.SQLITE_OK
+		So(err, ShouldBeNil)
+
+		// Add Normal User
+		rv, err = addUser(db1, "user2", "user2", 0)
+		So(rv, ShouldEqual, 0) // 0 == C.SQLITE_OK
+		So(err, ShouldBeNil)
+
+		// Verify 'user'
+		e, err = userExists(db1, "user")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err = isAdmin(db1, "user")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, false)
+
+		// Verify 'user2'
+		e, err = userExists(db1, "user2")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err = isAdmin(db1, "user2")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, false)
+		db1.Close()
+
+		// Reconnect as normal user
+		f2, db2, c2, err := connect(t, f1, "user", "user")
+		So(f2, ShouldNotBeBlank)
+		So(f1, ShouldEqual, f2)
+		So(db2, ShouldNotBeNil)
+		So(c2, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer db2.Close()
+
+		rv, err = deleteUser(db2, "user2")
+		So(err, ShouldBeNil)
+		So(rv, ShouldEqual, SQLITE_AUTH)
+	})
+
+	Convey("Delete User as Non-Admin (*SQLiteConn)", t, func() {
+		f1, db1, c1, err := connect(t, "", "admin", "admin")
+		So(f1, ShouldNotBeBlank)
+		So(db1, ShouldNotBeNil)
+		So(c1, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer os.Remove(f1)
+
+		e, err := userExists(db1, "admin")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err := isAdmin(db1, "admin")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, true)
+
+		// Add Normal User
+		rv, err := addUser(db1, "user", "user", 0)
+		So(rv, ShouldEqual, 0) // 0 == C.SQLITE_OK
+		So(err, ShouldBeNil)
+
+		// Add Normal User
+		rv, err = addUser(db1, "user2", "user2", 0)
+		So(rv, ShouldEqual, 0) // 0 == C.SQLITE_OK
+		So(err, ShouldBeNil)
+
+		// Verify 'user'
+		e, err = userExists(db1, "user")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err = isAdmin(db1, "user")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, false)
+
+		// Verify 'user2'
+		e, err = userExists(db1, "user2")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err = isAdmin(db1, "user2")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, false)
+		db1.Close()
+
+		// Reconnect as normal user
+		f2, db2, c2, err := connect(t, f1, "user", "user")
+		So(f2, ShouldNotBeBlank)
+		So(f1, ShouldEqual, f2)
+		So(db2, ShouldNotBeNil)
+		So(c2, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer db2.Close()
+
+		err = c2.AuthUserDelete("user2")
+		So(err, ShouldNotBeNil)
+		So(err, ShouldEqual, ErrAdminRequired)
+	})
+}
+
+func TestUserAuthenticationEncoder(t *testing.T) {
+	connectWithCrypt := func(t *testing.T, f string, username, password string, crypt string, salt string) (file string, db *sql.DB, c *SQLiteConn, err error) {
+		conn = nil // Clear connection
+		file = f   // Copy provided file (f) => file
+		if file == "" {
+			// Create dummy file
+			file = TempFilename(t)
+		}
+
+		db, err = sql.Open("sqlite3_with_conn", "file:"+file+fmt.Sprintf("?_auth&_auth_user=%s&_auth_pass=%s&_auth_crypt=%s&_auth_salt=%s", username, password, crypt, salt))
+		if err != nil {
+			defer os.Remove(file)
+			return file, nil, nil, err
+		}
+
+		// Dummy query to force connection and database creation
+		// Will return ErrUnauthorized (SQLITE_AUTH) if user authentication fails
+		if _, err = db.Exec("SELECT 1;"); err != nil {
+			defer os.Remove(file)
+			defer db.Close()
+			return file, nil, nil, err
+		}
+		c = conn
+
+		return
+	}
+
+	Convey("SHA1 Encoder", t, func() {
+		f1, db1, c1, err := connectWithCrypt(t, "", "admin", "admin", "sha1", "")
+		So(f1, ShouldNotBeBlank)
+		So(db1, ShouldNotBeNil)
+		So(c1, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer os.Remove(f1)
+
+		e, err := userExists(db1, "admin")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err := isAdmin(db1, "admin")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, true)
+		db1.Close()
+
+		// Preform authentication
+		f2, db2, c2, err := connectWithCrypt(t, f1, "admin", "admin", "sha1", "")
+		So(f2, ShouldNotBeBlank)
+		So(f1, ShouldEqual, f2)
+		So(db2, ShouldNotBeNil)
+		So(c2, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer db2.Close()
+	})
+
+	Convey("SSHA1 Encoder", t, func() {
+		f1, db1, c1, err := connectWithCrypt(t, "", "admin", "admin", "ssha1", "salted")
+		So(f1, ShouldNotBeBlank)
+		So(db1, ShouldNotBeNil)
+		So(c1, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer os.Remove(f1)
+
+		e, err := userExists(db1, "admin")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err := isAdmin(db1, "admin")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, true)
+		db1.Close()
+
+		// Preform authentication
+		f2, db2, c2, err := connectWithCrypt(t, f1, "admin", "admin", "ssha1", "salted")
+		So(f2, ShouldNotBeBlank)
+		So(f1, ShouldEqual, f2)
+		So(db2, ShouldNotBeNil)
+		So(c2, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer db2.Close()
+	})
+
+	Convey("SHA256 Encoder", t, func() {
+		f1, db1, c1, err := connectWithCrypt(t, "", "admin", "admin", "sha256", "")
+		So(f1, ShouldNotBeBlank)
+		So(db1, ShouldNotBeNil)
+		So(c1, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer os.Remove(f1)
+
+		e, err := userExists(db1, "admin")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err := isAdmin(db1, "admin")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, true)
+		db1.Close()
+
+		// Preform authentication
+		f2, db2, c2, err := connectWithCrypt(t, f1, "admin", "admin", "sha256", "")
+		So(f2, ShouldNotBeBlank)
+		So(f1, ShouldEqual, f2)
+		So(db2, ShouldNotBeNil)
+		So(c2, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer db2.Close()
+	})
+
+	Convey("SSHA256 Encoder", t, func() {
+		f1, db1, c1, err := connectWithCrypt(t, "", "admin", "admin", "ssha256", "salted")
+		So(f1, ShouldNotBeBlank)
+		So(db1, ShouldNotBeNil)
+		So(c1, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer os.Remove(f1)
+
+		e, err := userExists(db1, "admin")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err := isAdmin(db1, "admin")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, true)
+		db1.Close()
+
+		// Preform authentication
+		f2, db2, c2, err := connectWithCrypt(t, f1, "admin", "admin", "ssha256", "salted")
+		So(f2, ShouldNotBeBlank)
+		So(f1, ShouldEqual, f2)
+		So(db2, ShouldNotBeNil)
+		So(c2, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer db2.Close()
+	})
+
+	Convey("SHA384 Encoder", t, func() {
+		f1, db1, c1, err := connectWithCrypt(t, "", "admin", "admin", "sha384", "")
+		So(f1, ShouldNotBeBlank)
+		So(db1, ShouldNotBeNil)
+		So(c1, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer os.Remove(f1)
+
+		e, err := userExists(db1, "admin")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err := isAdmin(db1, "admin")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, true)
+		db1.Close()
+
+		// Preform authentication
+		f2, db2, c2, err := connectWithCrypt(t, f1, "admin", "admin", "sha384", "")
+		So(f2, ShouldNotBeBlank)
+		So(f1, ShouldEqual, f2)
+		So(db2, ShouldNotBeNil)
+		So(c2, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer db2.Close()
+	})
+
+	Convey("SSHA384 Encoder", t, func() {
+		f1, db1, c1, err := connectWithCrypt(t, "", "admin", "admin", "ssha384", "salted")
+		So(f1, ShouldNotBeBlank)
+		So(db1, ShouldNotBeNil)
+		So(c1, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer os.Remove(f1)
+
+		e, err := userExists(db1, "admin")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err := isAdmin(db1, "admin")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, true)
+		db1.Close()
+
+		// Preform authentication
+		f2, db2, c2, err := connectWithCrypt(t, f1, "admin", "admin", "ssha384", "salted")
+		So(f2, ShouldNotBeBlank)
+		So(f1, ShouldEqual, f2)
+		So(db2, ShouldNotBeNil)
+		So(c2, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer db2.Close()
+	})
+
+	Convey("SHA512 Encoder", t, func() {
+		f1, db1, c1, err := connectWithCrypt(t, "", "admin", "admin", "sha512", "")
+		So(f1, ShouldNotBeBlank)
+		So(db1, ShouldNotBeNil)
+		So(c1, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer os.Remove(f1)
+
+		e, err := userExists(db1, "admin")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err := isAdmin(db1, "admin")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, true)
+		db1.Close()
+
+		// Preform authentication
+		f2, db2, c2, err := connectWithCrypt(t, f1, "admin", "admin", "sha512", "")
+		So(f2, ShouldNotBeBlank)
+		So(f1, ShouldEqual, f2)
+		So(db2, ShouldNotBeNil)
+		So(c2, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer db2.Close()
+	})
+
+	Convey("SSHA512 Encoder", t, func() {
+		f1, db1, c1, err := connectWithCrypt(t, "", "admin", "admin", "ssha512", "salted")
+		So(f1, ShouldNotBeBlank)
+		So(db1, ShouldNotBeNil)
+		So(c1, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer os.Remove(f1)
+
+		e, err := userExists(db1, "admin")
+		So(err, ShouldBeNil)
+		So(e, ShouldEqual, 1)
+
+		a, err := isAdmin(db1, "admin")
+		So(err, ShouldBeNil)
+		So(a, ShouldEqual, true)
+		db1.Close()
+
+		// Preform authentication
+		f2, db2, c2, err := connectWithCrypt(t, f1, "admin", "admin", "ssha512", "salted")
+		So(f2, ShouldNotBeBlank)
+		So(f1, ShouldEqual, f2)
+		So(db2, ShouldNotBeNil)
+		So(c2, ShouldNotBeNil)
+		So(err, ShouldBeNil)
+		defer db2.Close()
+	})
+}
diff --git a/sqlite3_vtable_test.go b/sqlite3_opt_vtable_test.go
similarity index 99%
rename from sqlite3_vtable_test.go
rename to sqlite3_opt_vtable_test.go
index ce6a48c..d196c75 100644
--- a/sqlite3_vtable_test.go
+++ b/sqlite3_opt_vtable_test.go
@@ -2,7 +2,8 @@
 //
 // Use of this source code is governed by an MIT-style
 // license that can be found in the LICENSE file.
-// +build vtable
+
+// +build sqlite_vtable vtable
 
 package sqlite3
 
diff --git a/sqlite3_test.go b/sqlite3_test.go
index 782f57c..eadcf4a 100644
--- a/sqlite3_test.go
+++ b/sqlite3_test.go
@@ -1580,6 +1580,25 @@ func TestNilAndEmptyBytes(t *testing.T) {
 	}
 }
 
+func TestInsertNilByteSlice(t *testing.T) {
+	db, err := sql.Open("sqlite3", ":memory:")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer db.Close()
+	if _, err := db.Exec("create table blob_not_null (b blob not null)"); err != nil {
+		t.Fatal(err)
+	}
+	var nilSlice []byte
+	if _, err := db.Exec("insert into blob_not_null (b) values (?)", nilSlice); err == nil {
+		t.Fatal("didn't expect INSERT to 'not null' column with a nil []byte slice to work")
+	}
+	zeroLenSlice := []byte{}
+	if _, err := db.Exec("insert into blob_not_null (b) values (?)", zeroLenSlice); err != nil {
+		t.Fatal("failed to insert zero-length slice")
+	}
+}
+
 var customFunctionOnce sync.Once
 
 func BenchmarkCustomFunctions(b *testing.B) {