From efc41bcd27b3db141336544453881d49f81253d4 Mon Sep 17 00:00:00 2001 From: Gert-Jan Timmer Date: Tue, 29 May 2018 11:33:43 +0200 Subject: [PATCH 01/24] Update Connection Options * Rewrite order of options * ADD: PRAGMA auto_vacuum * ADD: Multi Boolean values * UPD: README * FIX: Case-Sensitive values * Reduced code for: - foreign_keys - recursive_triggers --- README.md | 13 ++++-- sqlite3.go | 123 +++++++++++++++++++++++++++++++++++------------------ 2 files changed, 90 insertions(+), 46 deletions(-) diff --git a/README.md b/README.md index 1f51f29..006112c 100644 --- a/README.md +++ b/README.md @@ -68,15 +68,20 @@ Options can be given using the following format: `KEYWORD=VALUE` and multiple op 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 | |------|-----|----------|-------------| +| Auto Vacuum | _vacuum | | For more information see [PRAGMA auto_vacuum](https://www.sqlite.org/pragma.html#pragma_auto_vacuum) | +| Busy Timeout | _busy_timeout | `int` | Specify value for sqlite3_busy_timeout. | +| Foreign Keys | _foreign_keys | `boolean` | Enable or disable enforcement of foreign keys. | +| Mutex Locking | _mutex | | Specify mutex mode. | +| Recursive Triggers | _recursive_triggers | `boolean` | Enable or disable recursive triggers. | | Shared-Cache Mode | cache | | Set cache mode for more information see [sqlite.org](https://www.sqlite.org/sharedcache.html) | | Time Zone Location | _loc | auto | Specify location of time format. | -| Busy Timeout | _busy_timeout | `int` | Specify value for sqlite3_busy_timeout. | | Transaction Lock | _txlock | | Specify locking behavior for transactions. | -| Foreign Keys | _foreign_keys | | Enable or disable enforcement of foreign keys. | -| Recursive Triggers | _recursive_triggers | | Enable or disable recursive triggers. | -| Mutex Locking | _mutex | | Specify mutex mode. | ## DSN Examples diff --git a/sqlite3.go b/sqlite3.go index da464e2..5fac138 100644 --- a/sqlite3.go +++ b/sqlite3.go @@ -779,6 +779,14 @@ 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 @@ -787,28 +795,43 @@ func errorString(err Error) string { // 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". +// +// _busy_timeout=XXX +// Specify value for sqlite3_busy_timeout. +// // _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". +// +// _vacuum=X +// 0 | none - Auto Vacuum disabled +// 1 | full - Auto Vacuum FULL +// 2 | incremental - Auto Vacuum Incremental 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") } + // Options var loc *time.Location + mutex := C.int(C.SQLITE_OPEN_FULLMUTEX) txlock := "BEGIN" + + // PRAGMA's + autoVacuum := -1 busyTimeout := 5000 foreignKeys := -1 recursiveTriggers := -1 - mutex := C.int(C.SQLITE_OPEN_FULLMUTEX) + pos := strings.IndexRune(dsn, '?') if pos >= 1 { params, err := url.ParseQuery(dsn[pos+1:]) @@ -828,13 +851,16 @@ 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 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 @@ -851,13 +877,36 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { } } + // auto_vacuum + if val := params.Get("_vacuum"); 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 _vacuum: %v", val) + } + } + + // _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) + } + busyTimeout = int(iv) + } + // _foreign_keys if val := params.Get("_foreign_keys"); val != "" { - switch val { - case "1": - foreignKeys = 1 - case "0": + 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) } @@ -865,28 +914,16 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { // _recursive_triggers if val := params.Get("_recursive_triggers"); val != "" { - switch val { - case "1": - recursiveTriggers = 1 - case "0": + 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) } } - // _mutex - if val := params.Get("_mutex"); val != "" { - switch val { - case "no": - mutex = C.SQLITE_OPEN_NOMUTEX - case "full": - mutex = C.SQLITE_OPEN_FULLMUTEX - default: - return nil, fmt.Errorf("Invalid _mutex: %v", val) - } - } - if !strings.HasPrefix(dsn, "file:") { dsn = dsn[:pos] } @@ -920,24 +957,26 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { } return nil } - if foreignKeys == 0 { - if err := exec("PRAGMA foreign_keys = OFF;"); err != nil { - C.sqlite3_close_v2(db) - return nil, err - } - } else if foreignKeys == 1 { - if err := exec("PRAGMA foreign_keys = ON;"); err != nil { + + // Auto Vacuum + if autoVacuum > -1 { + if err := exec(fmt.Sprintf("PRAGMA auto_vacuum = %d;", autoVacuum)); 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 { + } + + // 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 } From 95237557d2984326ac85b7fcf29945f584f4d959 Mon Sep 17 00:00:00 2001 From: Gert-Jan Timmer Date: Tue, 29 May 2018 11:46:53 +0200 Subject: [PATCH 02/24] ADD: PRAGMA case_sensitive_like ADD: Comments UPD: README --- README.md | 7 ++++--- sqlite3.go | 55 ++++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 51 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 006112c..652d8e7 100644 --- a/README.md +++ b/README.md @@ -75,10 +75,11 @@ Boolean values can be one of: | Name | Key | Value(s) | Description | |------|-----|----------|-------------| | Auto Vacuum | _vacuum | | For more information see [PRAGMA auto_vacuum](https://www.sqlite.org/pragma.html#pragma_auto_vacuum) | -| Busy Timeout | _busy_timeout | `int` | Specify value for sqlite3_busy_timeout. | -| Foreign Keys | _foreign_keys | `boolean` | Enable or disable enforcement of foreign keys. | +| Busy Timeout | _busy_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 | _cslike | `boolean` | For more information see [PRAGMA case_sensitive_like](https://www.sqlite.org/pragma.html#pragma_case_sensitive_like) | +| Foreign Keys | _foreign_keys | `boolean` | For more information see [PRAGMA foreign_keys](https://www.sqlite.org/pragma.html#pragma_foreign_keys) | | Mutex Locking | _mutex | | Specify mutex mode. | -| Recursive Triggers | _recursive_triggers | `boolean` | Enable or disable recursive triggers. | +| Recursive Triggers | _recursive_triggers | `boolean` | For more information see [PRAGMA recursive_triggers](https://www.sqlite.org/pragma.html#pragma_recursive_triggers) | | Shared-Cache Mode | cache | | Set cache mode for more information see [sqlite.org](https://www.sqlite.org/sharedcache.html) | | Time Zone Location | _loc | auto | Specify location of time format. | | Transaction Lock | _txlock | | Specify locking behavior for transactions. | diff --git a/sqlite3.go b/sqlite3.go index 5fac138..d64411d 100644 --- a/sqlite3.go +++ b/sqlite3.go @@ -806,10 +806,14 @@ func errorString(err Error) string { // _busy_timeout=XXX // Specify value for sqlite3_busy_timeout. // -// _foreign_keys=X +// _cslike=Boolean +// Default or disabled the LIKE operation is case-insensitive. +// When enabling this options behaviour of LIKE will become case-sensitive. +// +// _foreign_keys=Boolean // Enable or disable enforcement of foreign keys. X can be 1 or 0. // -// _recursive_triggers=X +// _recursive_triggers=Boolean // Enable or disable recursive triggers. X can be 1 or 0. // // _vacuum=X @@ -829,6 +833,7 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { // PRAGMA's autoVacuum := -1 busyTimeout := 5000 + caseSensitiveLike := -1 foreignKeys := -1 recursiveTriggers := -1 @@ -877,7 +882,10 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { } } - // auto_vacuum + // Auto Vacuum (_vacuum) + // + // https://www.sqlite.org/pragma.html#pragma_auto_vacuum + // if val := params.Get("_vacuum"); val != "" { switch strings.ToLower(val) { case "0", "none": @@ -891,7 +899,10 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { } } - // _busy_timeout + // Busy Timeout (_busy_timeout) + // + // https://www.sqlite.org/pragma.html#pragma_busy_timeout + // if val := params.Get("_busy_timeout"); val != "" { iv, err := strconv.ParseInt(val, 10, 64) if err != nil { @@ -900,7 +911,25 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { busyTimeout = int(iv) } - // _foreign_keys + // Case Sensitive Like (_cslike) + // + // https://www.sqlite.org/pragma.html#pragma_case_sensitive_like + // + if val := params.Get("_cslike"); 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 _cslike: %v, expecting boolean value of '0 1 false true no yes off on'", val) + } + } + + // Foreign Keys (_foreign_keys) + // + // https://www.sqlite.org/pragma.html#pragma_foreign_keys + // if val := params.Get("_foreign_keys"); val != "" { switch strings.ToLower(val) { case "0", "no", "false", "off": @@ -908,11 +937,14 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { 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 + // Recursive Triggers (_recursive_triggers) + // + // https://www.sqlite.org/pragma.html#pragma_recursive_triggers + // if val := params.Get("_recursive_triggers"); val != "" { switch strings.ToLower(val) { case "0", "no", "false", "off": @@ -920,7 +952,7 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { 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) } } @@ -966,6 +998,13 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { } } + if caseSensitiveLike > -1 { + if err := exec(fmt.Sprintf("PRAGMA case_sensitive_like = %d;", caseSensitiveLike)); err != nil { + C.sqlite3_close_v2(db) + return nil, err + } + } + // Forgein Keys if foreignKeys > -1 { if err := exec(fmt.Sprintf("PRAGMA foreign_keys = %d;", foreignKeys)); err != nil { From f087cd79b247059a4d002baa6c3a1e3f574093e5 Mon Sep 17 00:00:00 2001 From: Gert-Jan Timmer Date: Tue, 29 May 2018 11:53:36 +0200 Subject: [PATCH 03/24] Update Busy Timeout PRAGMA ADD: multiple key --- README.md | 18 +++++++++--------- sqlite3.go | 11 ++++++++++- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 652d8e7..e890c0d 100644 --- a/README.md +++ b/README.md @@ -74,15 +74,15 @@ Boolean values can be one of: | Name | Key | Value(s) | Description | |------|-----|----------|-------------| -| Auto Vacuum | _vacuum |
  • `0` \| `none`
  • `1` \| `full`
  • `2` \| `incremental`
| For more information see [PRAGMA auto_vacuum](https://www.sqlite.org/pragma.html#pragma_auto_vacuum) | -| Busy Timeout | _busy_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 | _cslike | `boolean` | For more information see [PRAGMA case_sensitive_like](https://www.sqlite.org/pragma.html#pragma_case_sensitive_like) | -| Foreign Keys | _foreign_keys | `boolean` | For more information see [PRAGMA foreign_keys](https://www.sqlite.org/pragma.html#pragma_foreign_keys) | -| Mutex Locking | _mutex |
  • no
  • full
| Specify mutex mode. | -| Recursive Triggers | _recursive_triggers | `boolean` | For more information see [PRAGMA recursive_triggers](https://www.sqlite.org/pragma.html#pragma_recursive_triggers) | -| Shared-Cache Mode | cache |
  • shared
  • private
| Set cache mode for more information see [sqlite.org](https://www.sqlite.org/sharedcache.html) | -| Time Zone Location | _loc | auto | Specify location of time format. | -| Transaction Lock | _txlock |
  • immediate
  • deferred
  • exclusive
| Specify locking behavior for transactions. | +| Auto Vacuum | `_vacuum` |
  • `0` \| `none`
  • `1` \| `full`
  • `2` \| `incremental`
| 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 | `_cslike` | `boolean` | For more information see [PRAGMA case_sensitive_like](https://www.sqlite.org/pragma.html#pragma_case_sensitive_like) | +| Foreign Keys | `_foreign_keys` | `boolean` | For more information see [PRAGMA foreign_keys](https://www.sqlite.org/pragma.html#pragma_foreign_keys) | +| Mutex Locking | `_mutex` |
  • no
  • full
| Specify mutex mode. | +| Recursive Triggers | `_recursive_triggers` | `boolean` | For more information see [PRAGMA recursive_triggers](https://www.sqlite.org/pragma.html#pragma_recursive_triggers) | +| Shared-Cache Mode | `cache` |
  • shared
  • private
| Set cache mode for more information see [sqlite.org](https://www.sqlite.org/sharedcache.html) | +| Time Zone Location | `_loc` | auto | Specify location of time format. | +| Transaction Lock | `_txlock` |
  • immediate
  • deferred
  • exclusive
| Specify locking behavior for transactions. | ## DSN Examples diff --git a/sqlite3.go b/sqlite3.go index d64411d..aa45559 100644 --- a/sqlite3.go +++ b/sqlite3.go @@ -825,6 +825,8 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { return nil, errors.New("sqlite library was not compiled for thread-safe operation") } + var pkey string + // Options var loc *time.Location mutex := C.int(C.SQLITE_OPEN_FULLMUTEX) @@ -903,7 +905,13 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { // // https://www.sqlite.org/pragma.html#pragma_busy_timeout // - if val := params.Get("_busy_timeout"); val != "" { + 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) @@ -930,6 +938,7 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { // // https://www.sqlite.org/pragma.html#pragma_foreign_keys // + if val := params.Get("_foreign_keys"); val != "" { switch strings.ToLower(val) { case "0", "no", "false", "off": From afd179bd93c5ef77cc8d4695a02341b4bd85584f Mon Sep 17 00:00:00 2001 From: Gert-Jan Timmer Date: Tue, 29 May 2018 11:58:29 +0200 Subject: [PATCH 04/24] Update Foreign Keys PRAGMA ADD: Multiple key --- README.md | 2 +- sqlite3.go | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index e890c0d..eec00d8 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ Boolean values can be one of: | Auto Vacuum | `_vacuum` |
  • `0` \| `none`
  • `1` \| `full`
  • `2` \| `incremental`
| 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 | `_cslike` | `boolean` | For more information see [PRAGMA case_sensitive_like](https://www.sqlite.org/pragma.html#pragma_case_sensitive_like) | -| Foreign Keys | `_foreign_keys` | `boolean` | For more information see [PRAGMA foreign_keys](https://www.sqlite.org/pragma.html#pragma_foreign_keys) | +| Foreign Keys | `_foreign_keys` \| `_fk` | `boolean` | For more information see [PRAGMA foreign_keys](https://www.sqlite.org/pragma.html#pragma_foreign_keys) | | Mutex Locking | `_mutex` |
  • no
  • full
| Specify mutex mode. | | Recursive Triggers | `_recursive_triggers` | `boolean` | For more information see [PRAGMA recursive_triggers](https://www.sqlite.org/pragma.html#pragma_recursive_triggers) | | Shared-Cache Mode | `cache` |
  • shared
  • private
| Set cache mode for more information see [sqlite.org](https://www.sqlite.org/sharedcache.html) | diff --git a/sqlite3.go b/sqlite3.go index aa45559..d009358 100644 --- a/sqlite3.go +++ b/sqlite3.go @@ -803,14 +803,14 @@ func errorString(err Error) string { // Specify locking behavior for transactions. XXX can be "immediate", // "deferred", "exclusive". // -// _busy_timeout=XXX +// _busy_timeout=XXX"| _timeout=XXX // Specify value for sqlite3_busy_timeout. // // _cslike=Boolean // Default or disabled the LIKE operation is case-insensitive. // When enabling this options behaviour of LIKE will become case-sensitive. // -// _foreign_keys=Boolean +// _foreign_keys=Boolean | _fk=Boolean // Enable or disable enforcement of foreign keys. X can be 1 or 0. // // _recursive_triggers=Boolean @@ -938,8 +938,13 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { // // https://www.sqlite.org/pragma.html#pragma_foreign_keys // - - if val := params.Get("_foreign_keys"); val != "" { + 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 From bb42c28ba57b5f9850d4fe66ec3534135d80fb75 Mon Sep 17 00:00:00 2001 From: Gert-Jan Timmer Date: Tue, 29 May 2018 12:01:16 +0200 Subject: [PATCH 05/24] Fix: pkey Ensure pkey is empty for next condition --- sqlite3.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/sqlite3.go b/sqlite3.go index d009358..b932305 100644 --- a/sqlite3.go +++ b/sqlite3.go @@ -811,10 +811,10 @@ func errorString(err Error) string { // When enabling this options behaviour of LIKE will become case-sensitive. // // _foreign_keys=Boolean | _fk=Boolean -// Enable or disable enforcement of foreign keys. X can be 1 or 0. +// Enable or disable enforcement of foreign keys. // -// _recursive_triggers=Boolean -// Enable or disable recursive triggers. X can be 1 or 0. +// _recursive_triggers=Boolean | _rt=Boolean +// Enable or disable recursive triggers. // // _vacuum=X // 0 | none - Auto Vacuum disabled @@ -905,6 +905,7 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { // // https://www.sqlite.org/pragma.html#pragma_busy_timeout // + pkey = "" // Reset pkey if _, ok := params["_busy_timeout"]; ok { pkey = "_busy_timeout" } @@ -938,6 +939,7 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { // // https://www.sqlite.org/pragma.html#pragma_foreign_keys // + pkey = "" // Reset pkey if _, ok := params["_foreign_keys"]; ok { pkey = "_foreign_keys" } From 4ab48f193516239cd4d3fc42769d41c078990348 Mon Sep 17 00:00:00 2001 From: Gert-Jan Timmer Date: Tue, 29 May 2018 12:03:18 +0200 Subject: [PATCH 06/24] Update Recursive Triggers ADD: Multiple Key --- README.md | 2 +- sqlite3.go | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index eec00d8..3086586 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ Boolean values can be one of: | Case Sensitive LIKE | `_cslike` | `boolean` | For more information see [PRAGMA case_sensitive_like](https://www.sqlite.org/pragma.html#pragma_case_sensitive_like) | | Foreign Keys | `_foreign_keys` \| `_fk` | `boolean` | For more information see [PRAGMA foreign_keys](https://www.sqlite.org/pragma.html#pragma_foreign_keys) | | Mutex Locking | `_mutex` |
  • no
  • full
| Specify mutex mode. | -| Recursive Triggers | `_recursive_triggers` | `boolean` | For more information see [PRAGMA recursive_triggers](https://www.sqlite.org/pragma.html#pragma_recursive_triggers) | +| Recursive Triggers | `_recursive_triggers` \| `_rt` | `boolean` | For more information see [PRAGMA recursive_triggers](https://www.sqlite.org/pragma.html#pragma_recursive_triggers) | | Shared-Cache Mode | `cache` |
  • shared
  • private
| Set cache mode for more information see [sqlite.org](https://www.sqlite.org/sharedcache.html) | | Time Zone Location | `_loc` | auto | Specify location of time format. | | Transaction Lock | `_txlock` |
  • immediate
  • deferred
  • exclusive
| Specify locking behavior for transactions. | diff --git a/sqlite3.go b/sqlite3.go index b932305..4de18d6 100644 --- a/sqlite3.go +++ b/sqlite3.go @@ -961,7 +961,14 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { // // https://www.sqlite.org/pragma.html#pragma_recursive_triggers // - if val := params.Get("_recursive_triggers"); val != "" { + 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 From 7393095f073c6c46d86618b83f8867ee1a8da359 Mon Sep 17 00:00:00 2001 From: Gert-Jan Timmer Date: Tue, 29 May 2018 12:09:56 +0200 Subject: [PATCH 07/24] ADD: PRAGMA defer_foreign_keys --- README.md | 1 + sqlite3.go | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/README.md b/README.md index 3086586..6b96d67 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,7 @@ Boolean values can be one of: | Auto Vacuum | `_vacuum` |
  • `0` \| `none`
  • `1` \| `full`
  • `2` \| `incremental`
| 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 | `_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) | | Mutex Locking | `_mutex` |
  • no
  • full
| Specify mutex mode. | | Recursive Triggers | `_recursive_triggers` \| `_rt` | `boolean` | For more information see [PRAGMA recursive_triggers](https://www.sqlite.org/pragma.html#pragma_recursive_triggers) | diff --git a/sqlite3.go b/sqlite3.go index 4de18d6..18a1f51 100644 --- a/sqlite3.go +++ b/sqlite3.go @@ -810,6 +810,9 @@ func errorString(err Error) string { // 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. // @@ -836,6 +839,7 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { autoVacuum := -1 busyTimeout := 5000 caseSensitiveLike := -1 + deferForeignKeys := -1 foreignKeys := -1 recursiveTriggers := -1 @@ -935,6 +939,28 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { } } + // Defer Foreign Keys + // + // 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 _foreign_keys: %v, expecting boolean value of '0 1 false true no yes off on'", val) + } + } + // Foreign Keys (_foreign_keys) // // https://www.sqlite.org/pragma.html#pragma_foreign_keys @@ -1028,6 +1054,14 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { } } + // 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 + } + } + // Forgein Keys if foreignKeys > -1 { if err := exec(fmt.Sprintf("PRAGMA foreign_keys = %d;", foreignKeys)); err != nil { From 37d3ff3d8602f1cef8f8fa4087524fb1f504733a Mon Sep 17 00:00:00 2001 From: Gert-Jan Timmer Date: Tue, 29 May 2018 12:10:05 +0200 Subject: [PATCH 08/24] Update Comments --- sqlite3.go | 1 + 1 file changed, 1 insertion(+) diff --git a/sqlite3.go b/sqlite3.go index 18a1f51..56f26c1 100644 --- a/sqlite3.go +++ b/sqlite3.go @@ -1047,6 +1047,7 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { } } + // Case Sensitive LIKE if caseSensitiveLike > -1 { if err := exec(fmt.Sprintf("PRAGMA case_sensitive_like = %d;", caseSensitiveLike)); err != nil { C.sqlite3_close_v2(db) From a4b55e1a40f79778687211cbdc3804f1b639fe67 Mon Sep 17 00:00:00 2001 From: Gert-Jan Timmer Date: Tue, 29 May 2018 12:19:46 +0200 Subject: [PATCH 09/24] ADD: PRAGMA ignore_check_constraints --- README.md | 1 + sqlite3.go | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/README.md b/README.md index 6b96d67..0277dac 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,7 @@ Boolean values can be one of: | 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) | | Mutex Locking | `_mutex` |
  • no
  • full
| Specify mutex mode. | | Recursive Triggers | `_recursive_triggers` \| `_rt` | `boolean` | For more information see [PRAGMA recursive_triggers](https://www.sqlite.org/pragma.html#pragma_recursive_triggers) | | Shared-Cache Mode | `cache` |
  • shared
  • private
| Set cache mode for more information see [sqlite.org](https://www.sqlite.org/sharedcache.html) | diff --git a/sqlite3.go b/sqlite3.go index 56f26c1..dc8f373 100644 --- a/sqlite3.go +++ b/sqlite3.go @@ -816,6 +816,10 @@ func errorString(err Error) string { // _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. +// // _recursive_triggers=Boolean | _rt=Boolean // Enable or disable recursive triggers. // @@ -840,6 +844,7 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { busyTimeout := 5000 caseSensitiveLike := -1 deferForeignKeys := -1 + ignoreCheckConstraints := -1 foreignKeys := -1 recursiveTriggers := -1 @@ -983,6 +988,21 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { } } + // 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) + } + } + // Recursive Triggers (_recursive_triggers) // // https://www.sqlite.org/pragma.html#pragma_recursive_triggers @@ -1071,6 +1091,14 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { } } + // 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 + } + } + // Recursive Triggers if recursiveTriggers > -1 { if err := exec(fmt.Sprintf("PRAGMA recursive_triggers = %d;", recursiveTriggers)); err != nil { From cac1feb8c7c32492dae6f55b3f543004e817366c Mon Sep 17 00:00:00 2001 From: Gert-Jan Timmer Date: Tue, 29 May 2018 12:19:57 +0200 Subject: [PATCH 10/24] Update Comments --- sqlite3.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sqlite3.go b/sqlite3.go index dc8f373..2a98d18 100644 --- a/sqlite3.go +++ b/sqlite3.go @@ -944,7 +944,7 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { } } - // Defer Foreign Keys + // Defer Foreign Keys (_defer_foreign_keys | _defer_fk) // // https://www.sqlite.org/pragma.html#pragma_defer_foreign_keys // @@ -962,11 +962,11 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { case "1", "yes", "true", "on": deferForeignKeys = 1 default: - return nil, fmt.Errorf("Invalid _foreign_keys: %v, expecting boolean value of '0 1 false true no yes off on'", val) + 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) + // Foreign Keys (_foreign_keys | _fk) // // https://www.sqlite.org/pragma.html#pragma_foreign_keys // From a5150a8e0147d534d5e785e81bda595137a3e7dd Mon Sep 17 00:00:00 2001 From: Gert-Jan Timmer Date: Tue, 29 May 2018 12:20:11 +0200 Subject: [PATCH 11/24] ADD: Additional Copyright line --- sqlite3.go | 1 + 1 file changed, 1 insertion(+) diff --git a/sqlite3.go b/sqlite3.go index 2a98d18..b4d3f7e 100644 --- a/sqlite3.go +++ b/sqlite3.go @@ -1,4 +1,5 @@ // Copyright (C) 2014 Yasuhiro Matsumoto . +// Copyright (C) 2018 G.J.R. Timmer . // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. From 46fb6884f2e1455e94c70a6412df92c4ae03200a Mon Sep 17 00:00:00 2001 From: Gert-Jan Timmer Date: Tue, 29 May 2018 13:02:28 +0200 Subject: [PATCH 12/24] Update README ADD: Comments for SQLite Access Mode ADD: Comments for SQLite Shared-Cache Mode UPD: README * Add Access Mode in Connection String Table --- README.md | 1 + sqlite3.go | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/README.md b/README.md index 0277dac..db20536 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,7 @@ Boolean values can be one of: | 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) | +| Mode | `mode` |
  • ro
  • rw
  • rwc
  • memory
| Access Mode of the database. For more information see [SQLite Open](https://www.sqlite.org/c3ref/open.html) | | Mutex Locking | `_mutex` |
  • no
  • full
| Specify mutex mode. | | Recursive Triggers | `_recursive_triggers` \| `_rt` | `boolean` | For more information see [PRAGMA recursive_triggers](https://www.sqlite.org/pragma.html#pragma_recursive_triggers) | | Shared-Cache Mode | `cache` |
  • shared
  • private
| Set cache mode for more information see [sqlite.org](https://www.sqlite.org/sharedcache.html) | diff --git a/sqlite3.go b/sqlite3.go index b4d3f7e..8d915bf 100644 --- a/sqlite3.go +++ b/sqlite3.go @@ -793,6 +793,23 @@ func errorString(err Error) string { // 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 +// // 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". From a159b5d1ab8e8406bd113e1bdbd1fd115daeb1c6 Mon Sep 17 00:00:00 2001 From: Gert-Jan Timmer Date: Tue, 29 May 2018 13:06:07 +0200 Subject: [PATCH 13/24] Fix: String ToLower for PRAGMA's --- sqlite3.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/sqlite3.go b/sqlite3.go index 8d915bf..4b79aac 100644 --- a/sqlite3.go +++ b/sqlite3.go @@ -875,9 +875,10 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { // _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) @@ -887,7 +888,7 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { // _mutex if val := params.Get("_mutex"); val != "" { - switch val { + switch strings.ToLower(val) { case "no": mutex = C.SQLITE_OPEN_NOMUTEX case "full": @@ -899,7 +900,7 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { // _txlock if val := params.Get("_txlock"); val != "" { - switch val { + switch strings.ToLower(val) { case "immediate": txlock = "BEGIN IMMEDIATE" case "exclusive": From e02bbc03819ea6dfc61212b7dbd772bc0809ae7a Mon Sep 17 00:00:00 2001 From: Gert-Jan Timmer Date: Tue, 29 May 2018 13:13:38 +0200 Subject: [PATCH 14/24] ADD: PRAGMA journal_mode --- README.md | 1 + sqlite3.go | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/README.md b/README.md index db20536..19190e1 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,7 @@ Boolean values can be one of: | 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) | +| Journal Mode | `_journal` |
  • DELETE
  • TRUNCATE
  • PERSIST
  • MEMORY
  • WAL
  • OFF
| For more information see [PRAGMA journal_mode](https://www.sqlite.org/pragma.html#pragma_journal_mode) | | Mode | `mode` |
  • ro
  • rw
  • rwc
  • memory
| Access Mode of the database. For more information see [SQLite Open](https://www.sqlite.org/c3ref/open.html) | | Mutex Locking | `_mutex` |
  • no
  • full
| Specify mutex mode. | | Recursive Triggers | `_recursive_triggers` \| `_rt` | `boolean` | For more information see [PRAGMA recursive_triggers](https://www.sqlite.org/pragma.html#pragma_recursive_triggers) | diff --git a/sqlite3.go b/sqlite3.go index 4b79aac..3855882 100644 --- a/sqlite3.go +++ b/sqlite3.go @@ -838,6 +838,10 @@ func errorString(err Error) string { // 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 +// Set journal mode for the databases associated with the current connection. +// https://www.sqlite.org/pragma.html#pragma_journal_mode +// // _recursive_triggers=Boolean | _rt=Boolean // Enable or disable recursive triggers. // @@ -863,6 +867,7 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { caseSensitiveLike := -1 deferForeignKeys := -1 ignoreCheckConstraints := -1 + journalMode := "DELETE" foreignKeys := -1 recursiveTriggers := -1 @@ -1022,6 +1027,19 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { } } + // Journal Mode (_journal) + // + // https://www.sqlite.org/pragma.html#pragma_journal_mode + // + if val := params.Get("_journal"); val != "" { + switch strings.ToUpper(val) { + case "DELETE", "TRUNCATE", "PERSIST", "MEMORY", "WAL", "OFF": + journalMode = strings.ToUpper(val) + default: + return nil, fmt.Errorf("Invalid _journal: %v, expecting value of 'DELETE TRUNCATE PERSIST MEMORY WAL OFF'", val) + } + } + // Recursive Triggers (_recursive_triggers) // // https://www.sqlite.org/pragma.html#pragma_recursive_triggers @@ -1118,6 +1136,13 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { } } + // 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 + } + // Recursive Triggers if recursiveTriggers > -1 { if err := exec(fmt.Sprintf("PRAGMA recursive_triggers = %d;", recursiveTriggers)); err != nil { From f14a7566f95301542aab53506f6b304e89865f5d Mon Sep 17 00:00:00 2001 From: Gert-Jan Timmer Date: Tue, 29 May 2018 13:19:40 +0200 Subject: [PATCH 15/24] ADD: PRAGMA locking_mode --- README.md | 1 + sqlite3.go | 29 ++++++++++++++++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 19190e1..2e1312f 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,7 @@ Boolean values can be one of: | 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) | | Journal Mode | `_journal` |
  • DELETE
  • TRUNCATE
  • PERSIST
  • MEMORY
  • WAL
  • OFF
| For more information see [PRAGMA journal_mode](https://www.sqlite.org/pragma.html#pragma_journal_mode) | +| Locking Mode | `_locking` |
  • NORMAL
  • EXCLUSIVE
| For more information see [PRAGMA locking_mode](https://www.sqlite.org/pragma.html#pragma_locking_mode) | | Mode | `mode` |
  • ro
  • rw
  • rwc
  • memory
| Access Mode of the database. For more information see [SQLite Open](https://www.sqlite.org/c3ref/open.html) | | Mutex Locking | `_mutex` |
  • no
  • full
| Specify mutex mode. | | Recursive Triggers | `_recursive_triggers` \| `_rt` | `boolean` | For more information see [PRAGMA recursive_triggers](https://www.sqlite.org/pragma.html#pragma_recursive_triggers) | diff --git a/sqlite3.go b/sqlite3.go index 3855882..b0a330d 100644 --- a/sqlite3.go +++ b/sqlite3.go @@ -842,6 +842,11 @@ func errorString(err Error) string { // Set journal mode for the databases associated with the current connection. // https://www.sqlite.org/pragma.html#pragma_journal_mode // +// _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 +// // _recursive_triggers=Boolean | _rt=Boolean // Enable or disable recursive triggers. // @@ -866,9 +871,10 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { busyTimeout := 5000 caseSensitiveLike := -1 deferForeignKeys := -1 + foreignKeys := -1 ignoreCheckConstraints := -1 journalMode := "DELETE" - foreignKeys := -1 + lockingMode := "NORMAL" recursiveTriggers := -1 pos := strings.IndexRune(dsn, '?') @@ -1040,6 +1046,19 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { } } + // Locking Mode (_locking) + // + // https://www.sqlite.org/pragma.html#pragma_locking_mode + // + if val := params.Get("_locking"); val != "" { + switch strings.ToUpper(val) { + case "NORMAL", "EXCLUSIVE": + lockingMode = strings.ToUpper(val) + default: + return nil, fmt.Errorf("Invalid _locking: %v, expecting value of 'NORMAL EXCLUSIVE", val) + } + } + // Recursive Triggers (_recursive_triggers) // // https://www.sqlite.org/pragma.html#pragma_recursive_triggers @@ -1143,6 +1162,14 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { 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 + } + // Recursive Triggers if recursiveTriggers > -1 { if err := exec(fmt.Sprintf("PRAGMA recursive_triggers = %d;", recursiveTriggers)); err != nil { From 9e79299c09fe7a6786b163be2b0abdab03a39d8b Mon Sep 17 00:00:00 2001 From: Gert-Jan Timmer Date: Tue, 29 May 2018 13:23:39 +0200 Subject: [PATCH 16/24] Add: Documentation for opening as Immutable --- README.md | 1 + sqlite3.go | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/README.md b/README.md index 2e1312f..addd874 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,7 @@ Boolean values can be one of: | 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` |
  • DELETE
  • TRUNCATE
  • PERSIST
  • MEMORY
  • WAL
  • OFF
| For more information see [PRAGMA journal_mode](https://www.sqlite.org/pragma.html#pragma_journal_mode) | | Locking Mode | `_locking` |
  • NORMAL
  • EXCLUSIVE
| For more information see [PRAGMA locking_mode](https://www.sqlite.org/pragma.html#pragma_locking_mode) | | Mode | `mode` |
  • ro
  • rw
  • rwc
  • memory
| Access Mode of the database. For more information see [SQLite Open](https://www.sqlite.org/c3ref/open.html) | diff --git a/sqlite3.go b/sqlite3.go index b0a330d..3e2d2e3 100644 --- a/sqlite3.go +++ b/sqlite3.go @@ -810,6 +810,15 @@ func errorString(err Error) string { // - 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". From 764e391156b04640da0150bc4b1f11003b93d2a4 Mon Sep 17 00:00:00 2001 From: Gert-Jan Timmer Date: Tue, 29 May 2018 13:29:06 +0200 Subject: [PATCH 17/24] ADD: PRAGMA query_only --- README.md | 1 + sqlite3.go | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/README.md b/README.md index addd874..02e5b25 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,7 @@ Boolean values can be one of: | Locking Mode | `_locking` |
  • NORMAL
  • EXCLUSIVE
| For more information see [PRAGMA locking_mode](https://www.sqlite.org/pragma.html#pragma_locking_mode) | | Mode | `mode` |
  • ro
  • rw
  • rwc
  • memory
| Access Mode of the database. For more information see [SQLite Open](https://www.sqlite.org/c3ref/open.html) | | Mutex Locking | `_mutex` |
  • no
  • full
| 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) | | Shared-Cache Mode | `cache` |
  • shared
  • private
| Set cache mode for more information see [sqlite.org](https://www.sqlite.org/sharedcache.html) | | Time Zone Location | `_loc` | auto | Specify location of time format. | diff --git a/sqlite3.go b/sqlite3.go index 3e2d2e3..c9a6bfe 100644 --- a/sqlite3.go +++ b/sqlite3.go @@ -856,6 +856,9 @@ func errorString(err Error) string { // 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. // @@ -884,6 +887,7 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { ignoreCheckConstraints := -1 journalMode := "DELETE" lockingMode := "NORMAL" + queryOnly := -1 recursiveTriggers := -1 pos := strings.IndexRune(dsn, '?') @@ -1068,6 +1072,21 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { } } + // 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 @@ -1179,6 +1198,14 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { return nil, err } + // Query Only + if queryOnly > 0 { + 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 { From 6a80b70b7aa22a4c9e649f76a3381d8cc62a4a6d Mon Sep 17 00:00:00 2001 From: Gert-Jan Timmer Date: Tue, 29 May 2018 13:41:52 +0200 Subject: [PATCH 18/24] Add: PRAGMA secure_delete ADD: Connection PRAGMA ADD: Build tag for secure_delete mode: FAST --- README.md | 2 ++ sqlite3.go | 34 +++++++++++++++++++++++++++++++ sqlite3_opt_secure_delete.go | 3 ++- sqlite3_opt_secure_delete_fast.go | 15 ++++++++++++++ 4 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 sqlite3_opt_secure_delete_fast.go diff --git a/README.md b/README.md index 02e5b25..82b43f8 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,7 @@ Boolean values can be one of: | Mutex Locking | `_mutex` |
  • no
  • full
| 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` |
  • shared
  • private
| Set cache mode for more information see [sqlite.org](https://www.sqlite.org/sharedcache.html) | | Time Zone Location | `_loc` | auto | Specify location of time format. | | Transaction Lock | `_txlock` |
  • immediate
  • deferred
  • exclusive
| Specify locking behavior for transactions. | @@ -137,6 +138,7 @@ go build --tags "icu json1 fts5 secure_delete" | Introspect PRAGMAS | sqlite_introspect | This option adds some extra PRAGMA statements.
  • PRAGMA function_list
  • PRAGMA module_list
  • PRAGMA pragma_list
| | 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.

When this option is not used, secure_delete defaults to off. When this option is present, secure_delete defaults to on.

The secure_delete setting causes deleted content to be overwritten with zeros. There is a small performance penalty since additional I/O must occur.

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 | # Compilation diff --git a/sqlite3.go b/sqlite3.go index c9a6bfe..5783581 100644 --- a/sqlite3.go +++ b/sqlite3.go @@ -862,6 +862,10 @@ func errorString(err Error) string { // _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 +// // _vacuum=X // 0 | none - Auto Vacuum disabled // 1 | full - Auto Vacuum FULL @@ -889,6 +893,7 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { lockingMode := "NORMAL" queryOnly := -1 recursiveTriggers := -1 + secureDelete := "DEFAULT" pos := strings.IndexRune(dsn, '?') if pos >= 1 { @@ -1109,6 +1114,23 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { } } + // 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 _recursive_triggers: %v, expecting boolean value of '0 1 false true no yes off on'", val) + } + } + if !strings.HasPrefix(dsn, "file:") { dsn = dsn[:pos] } @@ -1214,6 +1236,18 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { } } + // 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 + } + } + conn := &SQLiteConn{db: db, loc: loc, txlock: txlock} if len(d.Extensions) > 0 { diff --git a/sqlite3_opt_secure_delete.go b/sqlite3_opt_secure_delete.go index d243297..0e2f280 100644 --- a/sqlite3_opt_secure_delete.go +++ b/sqlite3_opt_secure_delete.go @@ -1,4 +1,5 @@ // Copyright (C) 2014 Yasuhiro Matsumoto . +// Copyright (C) 2018 G.J.R. Timmer . // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. @@ -8,7 +9,7 @@ package sqlite3 /* -#cgo CFLAGS: -DSQLITE_SECURE_DELETE +#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 . +// Copyright (C) 2018 G.J.R. Timmer . +// +// 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" From 619236f55ecc3829901a8f8c7d16d5a1a105e3ec Mon Sep 17 00:00:00 2001 From: Gert-Jan Timmer Date: Tue, 29 May 2018 13:43:22 +0200 Subject: [PATCH 19/24] Add: Copyright for additional Features --- sqlite3_opt_allow_uri_authority.go | 1 + sqlite3_opt_app_armor.go | 3 ++- sqlite3_opt_foreign_keys.go | 1 + sqlite3_opt_introspect.go | 3 ++- sqlite3_opt_stat4.go | 1 + sqlite3_opt_vacuum_full.go | 1 + sqlite3_opt_vacuum_incr.go | 1 + 7 files changed, 9 insertions(+), 2 deletions(-) diff --git a/sqlite3_opt_allow_uri_authority.go b/sqlite3_opt_allow_uri_authority.go index 0b0ccde..c92e817 100644 --- a/sqlite3_opt_allow_uri_authority.go +++ b/sqlite3_opt_allow_uri_authority.go @@ -1,4 +1,5 @@ // Copyright (C) 2014 Yasuhiro Matsumoto . +// Copyright (C) 2018 G.J.R. Timmer . // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. diff --git a/sqlite3_opt_app_armor.go b/sqlite3_opt_app_armor.go index c395293..89947cd 100644 --- a/sqlite3_opt_app_armor.go +++ b/sqlite3_opt_app_armor.go @@ -1,5 +1,6 @@ // Copyright (C) 2014 Yasuhiro Matsumoto . -// +// Copyright (C) 2018 G.J.R. Timmer . + // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. diff --git a/sqlite3_opt_foreign_keys.go b/sqlite3_opt_foreign_keys.go index fd6d97d..fd4f5a7 100644 --- a/sqlite3_opt_foreign_keys.go +++ b/sqlite3_opt_foreign_keys.go @@ -1,4 +1,5 @@ // Copyright (C) 2014 Yasuhiro Matsumoto . +// Copyright (C) 2018 G.J.R. Timmer . // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. diff --git a/sqlite3_opt_introspect.go b/sqlite3_opt_introspect.go index 7819898..10d4433 100644 --- a/sqlite3_opt_introspect.go +++ b/sqlite3_opt_introspect.go @@ -1,5 +1,6 @@ // Copyright (C) 2014 Yasuhiro Matsumoto . -// +// Copyright (C) 2018 G.J.R. Timmer . + // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. diff --git a/sqlite3_opt_stat4.go b/sqlite3_opt_stat4.go index 37908fd..3a34c12 100644 --- a/sqlite3_opt_stat4.go +++ b/sqlite3_opt_stat4.go @@ -1,4 +1,5 @@ // Copyright (C) 2014 Yasuhiro Matsumoto . +// Copyright (C) 2018 G.J.R. Timmer . // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. diff --git a/sqlite3_opt_vacuum_full.go b/sqlite3_opt_vacuum_full.go index 3065931..a202c80 100644 --- a/sqlite3_opt_vacuum_full.go +++ b/sqlite3_opt_vacuum_full.go @@ -1,4 +1,5 @@ // Copyright (C) 2014 Yasuhiro Matsumoto . +// Copyright (C) 2018 G.J.R. Timmer . // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. diff --git a/sqlite3_opt_vacuum_incr.go b/sqlite3_opt_vacuum_incr.go index 2f51f09..62bbf50 100644 --- a/sqlite3_opt_vacuum_incr.go +++ b/sqlite3_opt_vacuum_incr.go @@ -1,4 +1,5 @@ // Copyright (C) 2014 Yasuhiro Matsumoto . +// Copyright (C) 2018 G.J.R. Timmer . // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. From cb041405c6e3989fea4ead50b90a4bc7e75d5ab5 Mon Sep 17 00:00:00 2001 From: Gert-Jan Timmer Date: Tue, 29 May 2018 13:55:31 +0200 Subject: [PATCH 20/24] ADD: PRAGMA synchronous --- README.md | 1 + sqlite3.go | 41 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 82b43f8..6e6abe7 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,7 @@ Boolean values can be one of: | 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` |
  • shared
  • private
| Set cache mode for more information see [sqlite.org](https://www.sqlite.org/sharedcache.html) | +| Synchronous | `_synchronous` \| `_sync` |
  • 0 \| OFF
  • 1 \| NORMAL
  • 2 \| FULL
  • 3 \| EXTRA
| 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` |
  • immediate
  • deferred
  • exclusive
| Specify locking behavior for transactions. | diff --git a/sqlite3.go b/sqlite3.go index 5783581..0ffdb8f 100644 --- a/sqlite3.go +++ b/sqlite3.go @@ -866,6 +866,10 @@ func errorString(err Error) string { // 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 +// // _vacuum=X // 0 | none - Auto Vacuum disabled // 1 | full - Auto Vacuum FULL @@ -894,6 +898,7 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { queryOnly := -1 recursiveTriggers := -1 secureDelete := "DEFAULT" + synchronousMode := "NORMAL" pos := strings.IndexRune(dsn, '?') if pos >= 1 { @@ -1057,8 +1062,14 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { // if val := params.Get("_journal"); val != "" { switch strings.ToUpper(val) { - case "DELETE", "TRUNCATE", "PERSIST", "MEMORY", "WAL", "OFF": + 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) } @@ -1131,6 +1142,26 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { } } + // 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) + } + } + if !strings.HasPrefix(dsn, "file:") { dsn = dsn[:pos] } @@ -1248,6 +1279,14 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { } } + // 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 + } + conn := &SQLiteConn{db: db, loc: loc, txlock: txlock} if len(d.Extensions) > 0 { From 24cbd402e45f54cc20dc47d81c9002a965679f2b Mon Sep 17 00:00:00 2001 From: Gert-Jan Timmer Date: Tue, 29 May 2018 14:01:33 +0200 Subject: [PATCH 21/24] ADD: PRAGMA writable_schema --- README.md | 1 + sqlite3.go | 39 +++++++++++++++++++++++++++++++++++---- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 6e6abe7..1d7d7c9 100644 --- a/README.md +++ b/README.md @@ -92,6 +92,7 @@ Boolean values can be one of: | Synchronous | `_synchronous` \| `_sync` |
  • 0 \| OFF
  • 1 \| NORMAL
  • 2 \| FULL
  • 3 \| EXTRA
| 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` |
  • immediate
  • deferred
  • exclusive
| 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 diff --git a/sqlite3.go b/sqlite3.go index 0ffdb8f..f0bfcee 100644 --- a/sqlite3.go +++ b/sqlite3.go @@ -830,6 +830,11 @@ func errorString(err Error) string { // Specify locking behavior for transactions. XXX can be "immediate", // "deferred", "exclusive". // +// _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. // @@ -870,10 +875,12 @@ func errorString(err Error) string { // Change the setting of the "synchronous" flag. // https://www.sqlite.org/pragma.html#pragma_synchronous // -// _vacuum=X -// 0 | none - Auto Vacuum disabled -// 1 | full - Auto Vacuum FULL -// 2 | incremental - Auto Vacuum Incremental +// _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") @@ -899,6 +906,7 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { recursiveTriggers := -1 secureDelete := "DEFAULT" synchronousMode := "NORMAL" + writableSchema := -1 pos := strings.IndexRune(dsn, '?') if pos >= 1 { @@ -1162,6 +1170,21 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { } } + // 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) + } + } + if !strings.HasPrefix(dsn, "file:") { dsn = dsn[:pos] } @@ -1287,6 +1310,14 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { 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 + } + } + conn := &SQLiteConn{db: db, loc: loc, txlock: txlock} if len(d.Extensions) > 0 { From 4857d602fe5180b3b2f0418ee286c1e462780ef3 Mon Sep 17 00:00:00 2001 From: Gert-Jan Timmer Date: Tue, 29 May 2018 14:11:49 +0200 Subject: [PATCH 22/24] Fix: Connection DSN Keys * Conform keys to match PRAGMA * UPD: README * Fix error of _auto_vacuum * Fix error of _case_sensitive_like * Fix error of _locking_mode * Fix error of _secure_delete --- README.md | 8 ++++---- sqlite3.go | 51 ++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 44 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 1d7d7c9..dc75958 100644 --- a/README.md +++ b/README.md @@ -74,15 +74,15 @@ Boolean values can be one of: | Name | Key | Value(s) | Description | |------|-----|----------|-------------| -| Auto Vacuum | `_vacuum` |
  • `0` \| `none`
  • `1` \| `full`
  • `2` \| `incremental`
| For more information see [PRAGMA auto_vacuum](https://www.sqlite.org/pragma.html#pragma_auto_vacuum) | +| Auto Vacuum | `_auto_vacuum` \| `_vacuum` |
  • `0` \| `none`
  • `1` \| `full`
  • `2` \| `incremental`
| 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 | `_cslike` | `boolean` | For more information see [PRAGMA case_sensitive_like](https://www.sqlite.org/pragma.html#pragma_case_sensitive_like) | +| 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` |
  • DELETE
  • TRUNCATE
  • PERSIST
  • MEMORY
  • WAL
  • OFF
| For more information see [PRAGMA journal_mode](https://www.sqlite.org/pragma.html#pragma_journal_mode) | -| Locking Mode | `_locking` |
  • NORMAL
  • EXCLUSIVE
| For more information see [PRAGMA locking_mode](https://www.sqlite.org/pragma.html#pragma_locking_mode) | +| Journal Mode | `_journal_mode` \| `_journal` |
  • DELETE
  • TRUNCATE
  • PERSIST
  • MEMORY
  • WAL
  • OFF
| For more information see [PRAGMA journal_mode](https://www.sqlite.org/pragma.html#pragma_journal_mode) | +| Locking Mode | `_locking_mode` \| `_locking` |
  • NORMAL
  • EXCLUSIVE
| For more information see [PRAGMA locking_mode](https://www.sqlite.org/pragma.html#pragma_locking_mode) | | Mode | `mode` |
  • ro
  • rw
  • rwc
  • memory
| Access Mode of the database. For more information see [SQLite Open](https://www.sqlite.org/c3ref/open.html) | | Mutex Locking | `_mutex` |
  • no
  • full
| Specify mutex mode. | | Query Only | `_query_only` | `boolean` | For more information see [PRAGMA query_only](https://www.sqlite.org/pragma.html#pragma_query_only) | diff --git a/sqlite3.go b/sqlite3.go index f0bfcee..e1b7809 100644 --- a/sqlite3.go +++ b/sqlite3.go @@ -838,7 +838,8 @@ func errorString(err Error) string { // _busy_timeout=XXX"| _timeout=XXX // Specify value for sqlite3_busy_timeout. // -// _cslike=Boolean +// _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. // @@ -852,11 +853,11 @@ func errorString(err Error) string { // 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 +// _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=X +// _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 @@ -958,7 +959,14 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { // // https://www.sqlite.org/pragma.html#pragma_auto_vacuum // - if val := params.Get("_vacuum"); val != "" { + 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 @@ -967,7 +975,7 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { case "2", "incremental": autoVacuum = 2 default: - return nil, fmt.Errorf("Invalid _vacuum: %v", val) + return nil, fmt.Errorf("Invalid _auto_vacuum: %v, expecting value of '0 NONE 1 FULL 2 INCREMENTAL'", val) } } @@ -994,14 +1002,21 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { // // https://www.sqlite.org/pragma.html#pragma_case_sensitive_like // - if val := params.Get("_cslike"); val != "" { + 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 _cslike: %v, expecting boolean value of '0 1 false true no yes off on'", val) + return nil, fmt.Errorf("Invalid _case_sensitive_like: %v, expecting boolean value of '0 1 false true no yes off on'", val) } } @@ -1064,11 +1079,18 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { } } - // Journal Mode (_journal) + // Journal Mode (_journal_mode | _journal) // // https://www.sqlite.org/pragma.html#pragma_journal_mode // - if val := params.Get("_journal"); val != "" { + 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) @@ -1087,12 +1109,19 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { // // 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: %v, expecting value of 'NORMAL EXCLUSIVE", val) + return nil, fmt.Errorf("Invalid _locking_mode: %v, expecting value of 'NORMAL EXCLUSIVE", val) } } @@ -1146,7 +1175,7 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { case "fast": secureDelete = "FAST" default: - return nil, fmt.Errorf("Invalid _recursive_triggers: %v, expecting boolean value of '0 1 false true no yes off on'", val) + return nil, fmt.Errorf("Invalid _secure_delete: %v, expecting boolean value of '0 1 false true no yes off on'", val) } } From d6b854186dafe2b2dbdc94fe7783fdd5e1544ede Mon Sep 17 00:00:00 2001 From: Gert-Jan Timmer Date: Tue, 29 May 2018 14:57:25 +0200 Subject: [PATCH 23/24] Fix: Condition of queryOnly Variable --- sqlite3.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sqlite3.go b/sqlite3.go index e1b7809..ab0dbc4 100644 --- a/sqlite3.go +++ b/sqlite3.go @@ -1304,7 +1304,7 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { } // Query Only - if queryOnly > 0 { + if queryOnly > -1 { if err := exec(fmt.Sprintf("PRAGMA query_only = %d;", queryOnly)); err != nil { C.sqlite3_close_v2(db) return nil, err From 42560ec6004740cdbaffe2afe0dfabda33f8a9ab Mon Sep 17 00:00:00 2001 From: Gert-Jan Timmer Date: Tue, 29 May 2018 14:57:40 +0200 Subject: [PATCH 24/24] Fix: Display of secure_delete error message --- sqlite3.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sqlite3.go b/sqlite3.go index ab0dbc4..75a5ee8 100644 --- a/sqlite3.go +++ b/sqlite3.go @@ -1175,7 +1175,7 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { case "fast": secureDelete = "FAST" default: - return nil, fmt.Errorf("Invalid _secure_delete: %v, expecting boolean value of '0 1 false true no yes off on'", val) + return nil, fmt.Errorf("Invalid _secure_delete: %v, expecting boolean value of '0 1 false true no yes off on fast'", val) } }