Compare commits

...

342 Commits

Author SHA1 Message Date
Nuno Cruces
36583542e1 Updated dependencies. 2024-03-17 16:34:02 +00:00
Nuno Cruces
fd3a3a3499 Locking improvements. 2024-03-17 16:17:48 +00:00
dependabot[bot]
ec96c77715 Bump github.com/tetratelabs/wazero from 1.7.0-pre.1 to 1.7.0 (#65)
Bumps [github.com/tetratelabs/wazero](https://github.com/tetratelabs/wazero) from 1.7.0-pre.1 to 1.7.0.
- [Release notes](https://github.com/tetratelabs/wazero/releases)
- [Commits](https://github.com/tetratelabs/wazero/compare/v1.7.0-pre.1...v1.7.0)

---
updated-dependencies:
- dependency-name: github.com/tetratelabs/wazero
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-15 23:23:02 +00:00
Nuno Cruces
c61f7b90f6 Locking improvements. 2024-03-15 16:14:49 +00:00
Nuno Cruces
7bd31c3443 Use strchrnul. 2024-03-15 14:15:12 +00:00
Nuno Cruces
f2f698b78a Remove clear. 2024-03-15 14:13:00 +00:00
Nuno Cruces
846b95d2d4 Persistent WAL. 2024-03-14 14:31:16 +00:00
Nuno Cruces
b9453aefb6 SQLite 3.45.2.
Also, remove FTS3/4.
2024-03-12 14:24:11 +00:00
Nuno Cruces
28b6fedef0 wazero v1.7.0-pre.1. (#60)
This enables the wazevo next gen compiler.
2024-03-09 10:11:50 +00:00
Nuno Cruces
fed9ce6e1c Backport date from 3.46. 2024-03-07 03:04:28 -08:00
dependabot[bot]
0ec08c2e74 Bump golang.org/x/crypto from 0.20.0 to 0.21.0 (#63)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.20.0 to 0.21.0.
- [Commits](https://github.com/golang/crypto/compare/v0.20.0...v0.21.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-04 23:10:29 +00:00
Nuno Cruces
4439cd302c binaryen-version_117. 2024-02-28 12:48:05 +00:00
dependabot[bot]
705eab456a Bump golang.org/x/crypto from 0.19.0 to 0.20.0 (#62)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.19.0 to 0.20.0.
- [Commits](https://github.com/golang/crypto/compare/v0.19.0...v0.20.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-26 23:04:54 +00:00
Nuno Cruces
d1d5e355c4 CI testing. 2024-02-24 00:22:40 +00:00
Nuno Cruces
d3da8cc4f3 BSD. 2024-02-23 23:56:54 +00:00
Nuno Cruces
6def6f735c Warn about Git LFS. 2024-02-20 10:08:42 +00:00
Nuno Cruces
e02c5b5db0 BSD. 2024-02-19 00:19:36 +00:00
Nuno Cruces
52d42e4b21 Naming. 2024-02-10 10:09:53 +00:00
Nuno Cruces
396e6537b4 GORM v1.25.7. (#59) 2024-02-10 10:03:12 +00:00
Nuno Cruces
78cb9abefd Lebesgue/Morton order. 2024-02-08 00:39:39 +00:00
dependabot[bot]
b76cb33e62 Bump golang.org/x/crypto from 0.18.0 to 0.19.0 (#57)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.18.0 to 0.19.0.
- [Commits](https://github.com/golang/crypto/compare/v0.18.0...v0.19.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-07 23:21:55 +00:00
Nuno Cruces
c7eea620a3 Tweaks. 2024-02-05 12:47:55 +00:00
Nuno Cruces
76e2733fef Updated dependencies. 2024-02-03 02:12:35 +00:00
Nuno Cruces
58df08329d SQLite 3.45.1. 2024-02-03 02:00:45 +00:00
Nuno Cruces
cdab468a92 Flaky tests. 2024-02-03 02:00:40 +00:00
Nuno Cruces
7438fdb664 Busy handlers. 2024-02-03 00:34:15 +00:00
Nuno Cruces
da0e98f17e BSD tests. 2024-02-02 19:10:56 +00:00
Nuno Cruces
bb0c77c6fa Test on M1. 2024-02-02 17:49:46 +00:00
Nuno Cruces
ea8894162b Update dependencies. 2024-02-02 17:43:24 +00:00
Nuno Cruces
9898fbfffa Rename. 2024-02-02 17:40:55 +00:00
Nuno Cruces
031087327d Update, authorizer callbacks. 2024-01-27 10:57:46 +00:00
Nuno Cruces
c9cc893ed7 Commit callback. 2024-01-27 10:05:31 +00:00
Nuno Cruces
99ad7ff766 Collation callback. 2024-01-26 23:52:45 +00:00
Nuno Cruces
019c71fb55 Towards callbacks. 2024-01-26 15:41:36 +00:00
Nuno Cruces
88cf845651 JSON stats. 2024-01-25 11:03:33 +00:00
Nuno Cruces
354242e528 Locking tweaks. 2024-01-24 09:58:05 +00:00
Nuno Cruces
3d906d47dd Avoid allocations. 2024-01-23 17:50:11 +00:00
Nuno Cruces
9df3488964 Backport ISO week from 3.46. 2024-01-22 11:21:30 +00:00
Nuno Cruces
d998b5f36c Limits, and tweaks. 2024-01-18 15:53:00 +00:00
Nuno Cruces
9f58a5d669 Rename. 2024-01-18 15:11:04 +00:00
Nuno Cruces
7d52cb259b Updated dependencies. 2024-01-17 16:36:26 +00:00
Nuno Cruces
35bbd8a0b0 New APIs. 2024-01-17 15:39:13 +00:00
Nuno Cruces
bce66299ab Remove flag. 2024-01-17 10:52:47 +00:00
Nuno Cruces
bc840dcefb SQLite 3.45.0. 2024-01-16 15:53:47 +00:00
Nuno Cruces
c822fa95c7 Batch column scans. (#52) 2024-01-16 15:18:14 +00:00
Nuno Cruces
1b2c267b2b Optimize interrupts. 2024-01-16 15:08:26 +00:00
Nuno Cruces
3d99af86bf Ensure arena alignment. 2024-01-15 10:43:36 +00:00
Nuno Cruces
145bc228af Avoid allocation. 2024-01-12 13:35:21 +00:00
Nuno Cruces
6b0c2c0554 Optimize. (#51) 2024-01-11 02:18:12 +00:00
Nuno Cruces
97f2b73701 Optimize. 2024-01-10 16:53:18 +00:00
Nuno Cruces
cb1e33a32d Benchmarks. 2024-01-10 12:27:19 +00:00
Nuno Cruces
ee48dd5c96 More stats. 2024-01-10 11:39:26 +00:00
Nuno Cruces
af42af2978 More stats. 2024-01-09 03:20:59 +00:00
dependabot[bot]
d48a92fcdf Bump golang.org/x/crypto from 0.17.0 to 0.18.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.17.0 to 0.18.0.
- [Commits](https://github.com/golang/crypto/compare/v0.17.0...v0.18.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-08 23:05:11 +00:00
Nuno Cruces
69937fbee5 More vtab API. 2024-01-08 19:23:56 +00:00
dependabot[bot]
2fb325b223 Bump golang.org/x/sync from 0.5.0 to 0.6.0
Bumps [golang.org/x/sync](https://github.com/golang/sync) from 0.5.0 to 0.6.0.
- [Commits](https://github.com/golang/sync/compare/v0.5.0...v0.6.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sync
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-05 02:16:24 +00:00
dependabot[bot]
f0c583a581 Bump golang.org/x/sys from 0.15.0 to 0.16.0
Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.15.0 to 0.16.0.
- [Commits](https://github.com/golang/sys/compare/v0.15.0...v0.16.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-05 02:00:28 +00:00
Nuno Cruces
17ce949c55 Create osutil. 2024-01-03 12:54:26 +00:00
Nuno Cruces
ae850191c8 Refactor extensions. 2024-01-03 12:43:03 +00:00
Nuno Cruces
fab70ddbec IEEE754 extension. 2023-12-30 10:50:35 +00:00
Nuno Cruces
a3c5f47d79 Update README.md 2023-12-30 00:47:16 +00:00
Nuno Cruces
16b5d80ef7 Internal JSON and pointer wrappers. 2023-12-29 23:42:37 +00:00
Nuno Cruces
7e5a143214 Hash functions. 2023-12-29 23:42:30 +00:00
dependabot[bot]
92d75f7446 Bump cross-platform-actions/action from 0.21.1 to 0.22.0
Bumps [cross-platform-actions/action](https://github.com/cross-platform-actions/action) from 0.21.1 to 0.22.0.
- [Release notes](https://github.com/cross-platform-actions/action/releases)
- [Changelog](https://github.com/cross-platform-actions/action/blob/master/changelog.md)
- [Commits](https://github.com/cross-platform-actions/action/compare/v0.21.1...v0.22.0)

---
updated-dependencies:
- dependency-name: cross-platform-actions/action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-28 00:16:27 +00:00
Nuno Cruces
d56ee4ac2c Error logging. 2023-12-27 14:16:00 +00:00
Nuno Cruces
e944d5d8e7 Config. 2023-12-23 14:53:15 +00:00
Nuno Cruces
fde2277b4a wazero v1.6.0. 2023-12-23 13:19:33 +00:00
Nuno Cruces
1ebdeed565 Documentation, issue #45. 2023-12-22 02:45:26 +00:00
Nuno Cruces
89202629ec Increase various limits, fix #45. 2023-12-21 15:08:19 +00:00
Nuno Cruces
cb62771a45 Examples. 2023-12-20 16:59:16 +00:00
Nuno Cruces
0bb1cd5e2e Rework error messages, see #45. 2023-12-20 16:10:50 +00:00
Danlock
7bbd4f1e3c Fix regex link typo 2023-12-19 16:01:58 +00:00
Nuno Cruces
ed4a3a894b Extension API tweaks. 2023-12-19 15:24:54 +00:00
Nuno Cruces
f1b00a9944 wasi-sdk-21. 2023-12-19 00:33:04 +00:00
Nuno Cruces
9281948f57 Extension API tweaks. 2023-12-19 00:13:51 +00:00
Nuno Cruces
b0b27439b5 Fix macOS osAllocate.
Mozilla is just wrong.
https://searchfox.org/mozilla-central/source/xpcom/glue/FileUtils.cpp
2023-12-17 05:19:27 +00:00
Nuno Cruces
c938577763 Update README.md 2023-12-15 11:05:53 +00:00
Nuno Cruces
ebbb969cd7 Tweaks. 2023-12-15 00:46:12 +00:00
Nuno Cruces
0171743e88 Blob IO extension. 2023-12-14 23:04:18 +00:00
Nuno Cruces
c68413bd53 Optimize interrupts. 2023-12-14 17:23:46 +00:00
Nuno Cruces
3f8b480ba0 Optimize declared types. 2023-12-14 17:23:46 +00:00
Nuno Cruces
9866067701 Improve function cache.
Assume interned strings.
2023-12-14 17:22:49 +00:00
Nuno Cruces
964a42c76d Improve function cache.
Implement a 4x larger, PLRU bit cache.
2023-12-14 11:32:43 +00:00
Nuno Cruces
0b093b7c0e More tests. 2023-12-12 16:55:17 +00:00
Nuno Cruces
32a824cb6c Tests. 2023-12-12 14:06:54 +00:00
Nuno Cruces
2e1c65147a BSD tests. 2023-12-12 12:03:16 +00:00
Nuno Cruces
86cc08e4d6 Fix BSD tests. 2023-12-12 02:48:44 +00:00
dependabot[bot]
05077b8845 Bump actions/setup-go from 4 to 5
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 4 to 5.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-12 01:04:25 +00:00
Nuno Cruces
6e8d5e5be6 More fileio. 2023-12-12 01:00:13 +00:00
Nuno Cruces
c99fbcea6f Towards fileio extension. 2023-12-11 14:48:15 +00:00
Nuno Cruces
831a34a4c4 Updated dependencies. 2023-12-07 14:00:08 +00:00
Nuno Cruces
7c820ede3c Driver time formatting. 2023-12-07 13:49:33 +00:00
Nuno Cruces
089a0c0670 Pivot virtual table. 2023-12-06 17:49:48 +00:00
Nuno Cruces
8b45cac16b Improved error handling. 2023-12-05 18:17:33 +00:00
Nuno Cruces
06d2ff6752 Optimize VFS find. 2023-12-05 14:11:20 +00:00
Nuno Cruces
987f0f13a2 Test CPUs. 2023-12-04 14:01:25 +00:00
Nuno Cruces
cd40213898 Reuse statement, API. 2023-12-04 13:46:48 +00:00
Nuno Cruces
8a0baedc10 Tests, fixes. 2023-12-02 12:17:18 +00:00
Nuno Cruces
c667a1f469 Declared type. 2023-12-02 12:17:18 +00:00
Nuno Cruces
9c562f5d8b Cache functions. 2023-12-02 12:17:18 +00:00
Nuno Cruces
d862f47d95 Deoptimize. 2023-12-02 12:17:18 +00:00
Nuno Cruces
a9e32fd3f0 Fix compiler crash. 2023-12-02 12:17:18 +00:00
Nuno Cruces
b262f5cd01 Statement virtual table. 2023-12-02 12:17:18 +00:00
Nuno Cruces
4160b9a4bb Simplify tails. 2023-11-30 18:18:27 +00:00
Nuno Cruces
dbaf2d99cd Unprotected values. 2023-11-30 00:29:41 +00:00
Nuno Cruces
3f05115cd7 Virtual table API. 2023-11-29 10:46:11 +00:00
Nuno Cruces
9bf14becaf Reentrant arenas. 2023-11-29 10:46:02 +00:00
Nuno Cruces
997e197f54 VFS tweaks. 2023-11-28 16:38:02 +00:00
Nuno Cruces
b81fe284b6 memdb WAL. 2023-11-28 11:40:04 +00:00
dependabot[bot]
269306c5c8 Bump golang.org/x/sys from 0.14.0 to 0.15.0
Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.14.0 to 0.15.0.
- [Commits](https://github.com/golang/sys/compare/v0.14.0...v0.15.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-28 02:00:06 +00:00
Nuno Cruces
8a18243830 Reproducible builds. 2023-11-28 01:55:26 +00:00
Nuno Cruces
fcd6cc91d8 Skip BOM. 2023-11-27 23:35:43 +00:00
Nuno Cruces
c1838fc0bc Fix encoding issues. 2023-11-27 15:37:53 +00:00
Nuno Cruces
dc0d8236bf Updated dependencies. 2023-11-25 00:39:20 +00:00
Nuno Cruces
ba18facb0d SQLite 3.44.2. 2023-11-25 00:10:38 +00:00
Nuno Cruces
5653efa70e Limits. 2023-11-25 00:10:32 +00:00
Nuno Cruces
1acb95917a Simplify aggregate context. 2023-11-24 15:33:17 +00:00
Nuno Cruces
e31a42fb22 Lines virtual table. 2023-11-23 15:59:24 +00:00
Nuno Cruces
aec69acca9 Update README.md 2023-11-23 13:03:38 +00:00
Nuno Cruces
f2d6bdb8b7 Tests. 2023-11-23 12:53:28 +00:00
Nuno Cruces
9bb01d1f8b CSV virtual table. 2023-11-23 03:36:54 +00:00
Nuno Cruces
83c15f2ddc Virtual table API. 2023-11-22 15:06:39 +00:00
Nuno Cruces
97d4248176 Array extension. 2023-11-21 13:40:55 +00:00
Nuno Cruces
22d1ae0068 Tweaks. 2023-11-21 09:45:47 +00:00
Nuno Cruces
ae1d696cf3 Virtual tables fixes. 2023-11-20 18:12:56 +00:00
Nuno Cruces
5992403052 Virtual tables. 2023-11-20 00:27:12 +00:00
Nuno Cruces
f212b6712d Error handling. 2023-11-18 10:15:18 +00:00
Nuno Cruces
a49cc084f3 Towards virtual tables. 2023-11-17 22:39:00 +00:00
Nuno Cruces
2c2f825bc5 Towards virtual tables. 2023-11-17 19:06:10 +00:00
Nuno Cruces
787086b8c1 Towards virtual tables. 2023-11-17 02:23:10 +00:00
Nuno Cruces
314098addb Towards virtual tables. 2023-11-15 10:57:19 +00:00
Nuno Cruces
4bf8a79c1d Towards virtual tables. 2023-11-14 13:56:27 +00:00
Nuno Cruces
90628ab8aa Text as byte slices. 2023-11-10 13:42:20 +00:00
Nuno Cruces
aa02c14430 Towards virtual tables. 2023-11-10 13:23:14 +00:00
Nuno Cruces
1dc06bff49 Simplify URLs. 2023-11-09 16:35:45 +00:00
Nuno Cruces
4e9173661b Foreign keys activation. 2023-11-09 16:20:44 +00:00
Nuno Cruces
591480cd39 Fix JSON. 2023-11-09 16:16:48 +00:00
Nuno Cruces
828788912e JSON example. 2023-11-09 12:11:36 +00:00
Nuno Cruces
6f8645cd2e Tests. 2023-11-08 07:28:48 +00:00
Nuno Cruces
c00927e8bb Driver savepoints. 2023-11-07 15:19:40 +00:00
dependabot[bot]
6b28be6d0e Bump golang.org/x/sys from 0.13.0 to 0.14.0 (#36)
Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.13.0 to 0.14.0.
- [Commits](https://github.com/golang/sys/compare/v0.13.0...v0.14.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-07 15:00:00 +00:00
dependabot[bot]
310b4ff29d Bump golang.org/x/sync from 0.4.0 to 0.5.0 (#35)
Bumps [golang.org/x/sync](https://github.com/golang/sync) from 0.4.0 to 0.5.0.
- [Commits](https://github.com/golang/sync/compare/v0.4.0...v0.5.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sync
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-07 12:38:12 +00:00
dependabot[bot]
e82cf16b11 Bump golang.org/x/text from 0.13.0 to 0.14.0 (#34)
Bumps [golang.org/x/text](https://github.com/golang/text) from 0.13.0 to 0.14.0.
- [Release notes](https://github.com/golang/text/releases)
- [Commits](https://github.com/golang/text/compare/v0.13.0...v0.14.0)

---
updated-dependencies:
- dependency-name: golang.org/x/text
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-07 00:57:28 +00:00
Nuno Cruces
24c9b57c56 Pointer-passing interfaces. 2023-11-07 00:50:43 +00:00
Nuno Cruces
24b965ac7e Refactor. 2023-11-06 18:29:28 +00:00
Nuno Cruces
446168c572 Update workflows. 2023-11-04 11:21:31 +00:00
Nuno Cruces
a9e2cbbfc5 Quote values, identifiers. 2023-11-04 01:18:25 +00:00
Nuno Cruces
a7c00eb150 SQLite 3.44.0. 2023-11-03 03:43:14 -07:00
Nuno Cruces
0bcdb712ba SQL json_time function. 2023-11-03 03:40:46 -07:00
Nuno Cruces
2157d0f325 Interrupts: avoid goroutine. 2023-10-25 14:12:21 +01:00
Nuno Cruces
6353160619 Improve benchmark repeatability. 2023-10-25 13:17:37 +01:00
Nuno Cruces
501d157279 Update BSD test. 2023-10-24 23:27:10 +01:00
Nuno Cruces
4db18a7b9a JSON encoding fix. 2023-10-19 16:46:58 +01:00
Nuno Cruces
a9dddaa86c Optimize VFS search. 2023-10-19 16:43:54 +01:00
Nuno Cruces
b25936dbec Unix formats return UTC. 2023-10-19 12:07:03 +01:00
dependabot[bot]
bf23041e46 Bump github.com/ncruces/julianday from 0.1.5 to 1.0.0 (#33)
Bumps [github.com/ncruces/julianday](https://github.com/ncruces/julianday) from 0.1.5 to 1.0.0.
- [Commits](https://github.com/ncruces/julianday/compare/v0.1.5...v1.0.0)

---
updated-dependencies:
- dependency-name: github.com/ncruces/julianday
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-19 00:34:35 +01:00
Nuno Cruces
d60fceac92 JSON support. 2023-10-18 23:14:46 +01:00
Nuno Cruces
61da30f44a Allow configuring wazero. 2023-10-18 15:06:32 +01:00
Nuno Cruces
d4ff605983 Time scanner. 2023-10-18 15:06:12 +01:00
Nuno Cruces
8d0c654178 Cross compilation. 2023-10-17 15:30:08 +01:00
Nuno Cruces
728e59951b Test BSD. 2023-10-16 12:51:49 +01:00
Nuno Cruces
f7b16bad5c Patch flaky tests. 2023-10-16 12:26:25 +01:00
Nuno Cruces
db3e6da31a BSD locks. 2023-10-16 02:11:20 +01:00
Nuno Cruces
3f443b2ecc API change. 2023-10-13 18:53:37 +01:00
Nuno Cruces
eec45ea684 Towards JSON. 2023-10-13 17:06:05 +01:00
Nuno Cruces
f6d77f3cf4 GORM v1.25.5. 2023-10-13 00:42:06 +01:00
Nuno Cruces
d5d7cd1f2d SQLite 3.43.2. 2023-10-12 10:52:48 +01:00
dependabot[bot]
a33a187d48 Bump golang.org/x/sys from 0.12.0 to 0.13.0 (#30)
Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.12.0 to 0.13.0.
- [Commits](https://github.com/golang/sys/compare/v0.12.0...v0.13.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-05 23:53:11 +01:00
dependabot[bot]
70c6ee15c6 Bump golang.org/x/sync from 0.3.0 to 0.4.0 (#29)
Bumps [golang.org/x/sync](https://github.com/golang/sync) from 0.3.0 to 0.4.0.
- [Commits](https://github.com/golang/sync/compare/v0.3.0...v0.4.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sync
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-05 23:43:11 +01:00
Nuno Cruces
994d9b1812 Updated dependencies. 2023-10-02 10:09:26 +01:00
Nuno Cruces
b19bd28ed3 Simplify lock timeouts. 2023-10-02 10:06:09 +01:00
Nuno Cruces
e66bd51845 More VFS API. 2023-09-21 02:43:45 +01:00
Nuno Cruces
f5614bc2ed Tweaks. 2023-09-20 15:07:07 +01:00
Nuno Cruces
d9fcf60b7d Driver API. 2023-09-20 02:41:09 +01:00
Nuno Cruces
ac6dd1aa5f Updated dependencies. 2023-09-18 15:22:11 +01:00
Nuno Cruces
b1495bd6cb Build tags, docs. 2023-09-18 15:11:05 +01:00
Nuno Cruces
2d91760295 Portability. 2023-09-18 12:44:18 +01:00
Nuno Cruces
38d4254bc4 Update README.md 2023-09-15 15:37:57 +01:00
Nuno Cruces
c0aa734786 binaryen-version_116. 2023-09-15 15:10:08 +01:00
Nuno Cruces
fa845dbd3d Run test in all platforms. 2023-09-12 15:30:43 +01:00
Nuno Cruces
fed315ab79 Update go.yml 2023-09-12 15:28:11 +01:00
Nuno Cruces
726d7316f7 Update README.md 2023-09-12 00:00:32 +01:00
Nuno Cruces
ddb387b021 Updated dependencies. 2023-09-11 23:54:22 +01:00
Nuno Cruces
d0f19507f5 SQLite 3.43.1. 2023-09-11 23:48:38 +01:00
Nuno Cruces
9d997552ad Pearson correlation. 2023-09-02 00:48:55 +01:00
Nuno Cruces
9d75c39dcc Update README.md 2023-09-01 16:01:42 +01:00
Nuno Cruces
746a84965e Covariance. 2023-09-01 02:38:57 +01:00
Nuno Cruces
312d3b58f2 Statistics functions. 2023-09-01 01:23:25 +01:00
Nuno Cruces
b71cd295c2 Updated dependencies. 2023-08-25 09:56:09 +01:00
Nuno Cruces
5b3b61a304 SQLite 3.43.0. 2023-08-24 18:56:23 +01:00
Nuno Cruces
d661d15723 wazero v1.5.0. 2023-08-24 18:56:10 +01:00
Nuno Cruces
1e38165ad0 Timer resolution. 2023-08-20 03:12:55 +01:00
Nuno Cruces
58a32d7c9d Update GORM. 2023-08-20 00:56:08 +01:00
Nuno Cruces
6765e883c1 Register collation. 2023-08-10 13:39:52 +01:00
Nuno Cruces
18fc608433 Embed database as string. 2023-08-10 13:23:54 +01:00
Nuno Cruces
77f37893b9 Driver connector. 2023-08-10 13:18:13 +01:00
Nuno Cruces
f1e36e2581 Updated dependencies. 2023-08-09 16:30:32 +01:00
Nuno Cruces
772b9153c7 Use clear builtin. 2023-08-09 16:16:45 +01:00
Nuno Cruces
4b280a3a7e Updated dependencies. 2023-08-09 15:22:48 +01:00
Nuno Cruces
19b6098bf6 Update go.yml (#28) 2023-08-05 01:12:16 +01:00
dependabot[bot]
2aa685320f Bump golang.org/x/text from 0.11.0 to 0.12.0 (#26)
Bumps [golang.org/x/text](https://github.com/golang/text) from 0.11.0 to 0.12.0.
- [Release notes](https://github.com/golang/text/releases)
- [Commits](https://github.com/golang/text/compare/v0.11.0...v0.12.0)

---
updated-dependencies:
- dependency-name: golang.org/x/text
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-05 00:36:56 +01:00
dependabot[bot]
9941be05c2 Bump golang.org/x/sys from 0.10.0 to 0.11.0 (#27)
Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.10.0 to 0.11.0.
- [Commits](https://github.com/golang/sys/compare/v0.10.0...v0.11.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-05 00:35:11 +01:00
Nuno Cruces
a0a9ab7737 Avoid unnecessary alloc. 2023-08-04 14:12:36 +01:00
Nuno Cruces
a77727a1ce Port script. 2023-07-31 15:27:10 +01:00
Nuno Cruces
47fe032078 Updated dependencies. 2023-07-26 12:42:18 +01:00
Nuno Cruces
bdfe279444 Soundex. 2023-07-26 02:02:39 +01:00
dependabot[bot]
a86937a54e Bump github.com/tetratelabs/wazero from 1.3.0 to 1.3.1
Bumps [github.com/tetratelabs/wazero](https://github.com/tetratelabs/wazero) from 1.3.0 to 1.3.1.
- [Release notes](https://github.com/tetratelabs/wazero/releases)
- [Commits](https://github.com/tetratelabs/wazero/compare/v1.3.0...v1.3.1)

---
updated-dependencies:
- dependency-name: github.com/tetratelabs/wazero
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-25 08:02:20 +01:00
Nuno Cruces
6ef422fbde Unicode tests. 2023-07-13 12:19:32 +01:00
Nuno Cruces
ff0cb6fb88 Unicode tests, fixes. 2023-07-12 13:39:07 +01:00
Nuno Cruces
72db90efdf Unicode. 2023-07-11 16:34:15 +01:00
Nuno Cruces
5a3fdef3c5 wazero v1.3.0. 2023-07-11 12:30:39 +01:00
dependabot[bot]
ff34b0cae1 Bump golang.org/x/text from 0.10.0 to 0.11.0
Bumps [golang.org/x/text](https://github.com/golang/text) from 0.10.0 to 0.11.0.
- [Release notes](https://github.com/golang/text/releases)
- [Commits](https://github.com/golang/text/compare/v0.10.0...v0.11.0)

---
updated-dependencies:
- dependency-name: golang.org/x/text
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-04 23:55:17 +01:00
Nuno Cruces
f064492bb1 Updated dependencies. 2023-07-04 19:55:11 +01:00
Nuno Cruces
1427d30541 Updated dependencies. 2023-07-04 19:48:55 +01:00
Nuno Cruces
d3730341f0 Unknown collations. 2023-07-04 11:16:29 +01:00
Nuno Cruces
78ac2386f6 Refactor. 2023-07-04 02:29:38 +01:00
Nuno Cruces
632ea933b3 Function aux data. 2023-07-04 02:18:03 +01:00
Nuno Cruces
0f7fa6ebc9 Tests. 2023-07-03 18:28:46 +01:00
Nuno Cruces
6f7f776488 Refactor. 2023-07-03 17:42:53 +01:00
Nuno Cruces
f6d7c5e9c5 Refactor. 2023-07-03 17:08:16 +01:00
Nuno Cruces
1cc7ecfe8d Custom aggregate functions. 2023-07-03 15:45:16 +01:00
Nuno Cruces
3844e81404 Custom aggregate functions. 2023-07-01 15:19:45 +01:00
Nuno Cruces
fec1f8d32a Custom scalar functions. 2023-07-01 00:16:42 +01:00
Nuno Cruces
31572e6095 Fix nil/zero handles. 2023-06-30 17:09:01 +01:00
Nuno Cruces
4aee38b957 Error handling. 2023-06-30 12:25:07 +01:00
Nuno Cruces
232a7705b5 Wrap context. 2023-06-30 11:48:54 +01:00
Nuno Cruces
a6c2fccd74 Wrap value. 2023-06-30 10:45:16 +01:00
Nuno Cruces
6a982559cd Custom collating sequences. 2023-06-30 02:49:21 +01:00
Nuno Cruces
c7904d30de Refactor file handles. 2023-06-30 01:52:18 +01:00
Nuno Cruces
ce4386604d GORM v1.25.1. 2023-06-29 20:06:56 +01:00
Nuno Cruces
26b62c520d Towards SQL functions. 2023-06-29 14:21:59 +01:00
Nuno Cruces
738714bf32 Fix WAL. 2023-06-26 13:31:42 +01:00
Nuno Cruces
41b020bafc go-sqlite3 v0.8.0. 2023-06-16 17:21:50 +01:00
Nuno Cruces
d0e720272b Optimization flags. 2023-06-15 15:57:39 +01:00
Nuno Cruces
76171da12b go-sqlite3 v0.7.3. 2023-06-15 03:56:02 +01:00
Nuno Cruces
dcc845d684 wazero v1.2.1. 2023-06-15 03:43:25 +01:00
dependabot[bot]
f1b42c26d5 Bump golang.org/x/sync from 0.2.0 to 0.3.0
Bumps [golang.org/x/sync](https://github.com/golang/sync) from 0.2.0 to 0.3.0.
- [Commits](https://github.com/golang/sync/compare/v0.2.0...v0.3.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sync
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-15 00:13:43 +01:00
dependabot[bot]
1e94407ae7 Bump golang.org/x/sys from 0.8.0 to 0.9.0
Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.8.0 to 0.9.0.
- [Commits](https://github.com/golang/sys/compare/v0.8.0...v0.9.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-13 00:31:02 +01:00
Nuno Cruces
eb8d9b95fd Consistent lock timeouts. 2023-06-12 13:04:37 +01:00
Nuno Cruces
04037a75ed GORM driver sync. 2023-06-12 10:56:03 +01:00
Nuno Cruces
2472ceb0a0 Fix GORM module name. 2023-06-07 12:40:18 +01:00
Nuno Cruces
bfe9bfde2e Make GORM driver its own module. 2023-06-07 12:00:46 +01:00
Nuno Cruces
f07e82e361 GORM driver. 2023-06-06 12:37:54 +01:00
Nuno Cruces
fbbbe5a631 Fix plain files. 2023-06-06 03:47:02 +01:00
Nuno Cruces
5ea603ed78 Readers should not close. 2023-06-02 15:00:12 +01:00
Nuno Cruces
401cb77e38 binaryen-version_113. 2023-06-02 14:23:12 +01:00
Nuno Cruces
6511175011 wazero v1.2.0. 2023-06-02 14:11:20 +01:00
Nuno Cruces
f7d987fdf1 Commit phase-two API. 2023-06-02 13:40:08 +01:00
Nuno Cruces
00ba681bb5 Batch atomic writes API. 2023-06-02 11:14:34 +01:00
Nuno Cruces
d4d4533a41 Docs. 2023-06-02 03:38:26 +01:00
Nuno Cruces
ec9533b13f Implement modeof. 2023-06-02 03:38:26 +01:00
Nuno Cruces
8fe77a065c Remove wzprof. 2023-06-02 03:38:26 +01:00
Nuno Cruces
7bf5312bd4 Rename. 2023-06-02 03:38:26 +01:00
Nuno Cruces
ae7b74d858 Upgrade wzprof. 2023-06-01 16:09:18 +01:00
Nuno Cruces
9a8de3ad13 Enable memdb on speedtest1. 2023-06-01 15:41:20 +01:00
Nuno Cruces
05737e6025 Refactor reader VFS API. 2023-05-31 19:24:41 +01:00
Nuno Cruces
ac2836bb82 Refactor memdb API. 2023-05-31 16:27:31 +01:00
Nuno Cruces
d0d4b0e1a2 MemoryVFS mutexes. 2023-05-31 12:57:18 +01:00
Nuno Cruces
dc3dc6853d MemoryVFS journal. 2023-05-31 11:56:48 +01:00
dependabot[bot]
830240c368 Bump github.com/stealthrocket/wzprof from 0.1.3 to 0.1.4
Bumps [github.com/stealthrocket/wzprof](https://github.com/stealthrocket/wzprof) from 0.1.3 to 0.1.4.
- [Release notes](https://github.com/stealthrocket/wzprof/releases)
- [Changelog](https://github.com/stealthrocket/wzprof/blob/main/.goreleaser.yml)
- [Commits](https://github.com/stealthrocket/wzprof/compare/v0.1.3...v0.1.4)

---
updated-dependencies:
- dependency-name: github.com/stealthrocket/wzprof
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-30 13:40:52 +01:00
Nuno Cruces
dedec8682b Driver improvements. 2023-05-30 13:39:34 +01:00
Nuno Cruces
a33b828e13 Examples, tests, max size. 2023-05-30 11:21:14 +01:00
Nuno Cruces
8b2e96dedc Tests, fixes, docs. 2023-05-29 16:52:43 +01:00
Nuno Cruces
f1c46db512 VFS locking. 2023-05-27 23:36:39 +01:00
Nuno Cruces
7ca9d79424 MemoryVFS. 2023-05-27 23:36:39 +01:00
Nuno Cruces
254d473546 VFS URI parameters. 2023-05-27 23:36:39 +01:00
Nuno Cruces
5639fc1ff8 Update wzprof. 2023-05-27 23:36:39 +01:00
Nuno Cruces
ae4954d09b Profile with wzprof. 2023-05-27 23:36:39 +01:00
Nuno Cruces
45937d9749 Use wazerotest. 2023-05-27 23:36:39 +01:00
Nuno Cruces
eee71e06aa Tweak calling convention. 2023-05-25 17:03:40 +01:00
Nuno Cruces
9e7b6bb8ea Improve connection setup. 2023-05-25 11:14:18 +01:00
Nuno Cruces
597178f80d Backup fix, tests. 2023-05-24 02:47:18 +01:00
Nuno Cruces
cc2d16ac83 ReaderVFS. 2023-05-23 16:34:09 +01:00
Nuno Cruces
cfb69e4ce7 Reorg. 2023-05-23 14:47:39 +01:00
Nuno Cruces
e6969432e3 Rename. 2023-05-23 14:47:38 +01:00
Nuno Cruces
2b3da350cc Improved error handling. 2023-05-23 14:47:38 +01:00
Nuno Cruces
336ba87d56 Documentation. 2023-05-19 19:47:43 +01:00
Nuno Cruces
dd4823ebf0 Documentation, tests. 2023-05-19 14:45:40 +01:00
Nuno Cruces
663b23ff3b Documentation. 2023-05-19 13:47:37 +01:00
Nuno Cruces
4e2ce6c635 Refactor VFS. 2023-05-19 03:04:07 +01:00
Nuno Cruces
66effb4249 Rename. 2023-05-19 02:28:30 +01:00
Nuno Cruces
e1cce83f71 More VFS API. 2023-05-19 02:00:16 +01:00
Nuno Cruces
df953b31c2 Refactor VFS. 2023-05-18 16:00:34 +01:00
Nuno Cruces
67cc3d35d5 More VFS API. 2023-05-18 01:34:54 +01:00
Nuno Cruces
6846b72b31 Add SetInterrupt to DriverConn. 2023-05-17 14:38:47 +01:00
Nuno Cruces
c94cdaf720 More VFS API. 2023-05-17 14:38:47 +01:00
Nuno Cruces
f6a887dd1c Allow manual runs. 2023-05-17 14:38:35 +01:00
Nuno Cruces
2a010a2022 Towards VFS API. 2023-05-17 01:00:08 +01:00
Nuno Cruces
c86b06b048 Refactor. 2023-05-16 17:52:37 +01:00
Nuno Cruces
a44a13a506 Rename. 2023-05-16 15:40:08 +01:00
Nuno Cruces
4604719966 SQLite 3.42.0. 2023-05-16 14:56:47 +01:00
Nuno Cruces
03168d5d34 Build scripts. 2023-05-16 12:14:34 +01:00
Nuno Cruces
be4b6304f9 Documentation. 2023-05-16 12:14:23 +01:00
Nuno Cruces
b5e678a40a Inline host calls. 2023-05-09 14:41:24 +01:00
Nuno Cruces
2fc4698ddc Avoid some allocs. 2023-05-08 10:47:19 +01:00
dependabot[bot]
bd86539577 Bump golang.org/x/sync from 0.1.0 to 0.2.0
Bumps [golang.org/x/sync](https://github.com/golang/sync) from 0.1.0 to 0.2.0.
- [Commits](https://github.com/golang/sync/compare/v0.1.0...v0.2.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sync
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-05 07:52:12 +01:00
dependabot[bot]
7a785d9aec Bump golang.org/x/sys from 0.7.0 to 0.8.0
Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.7.0 to 0.8.0.
- [Commits](https://github.com/golang/sys/compare/v0.7.0...v0.8.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-05 07:51:01 +01:00
Nuno Cruces
59f79e8e74 Optimize calls. 2023-05-02 01:08:04 +01:00
dependabot[bot]
40457721d7 Bump github.com/tetratelabs/wazero from 1.0.3 to 1.1.0 (#11)
Bumps [github.com/tetratelabs/wazero](https://github.com/tetratelabs/wazero) from 1.0.3 to 1.1.0.
- [Release notes](https://github.com/tetratelabs/wazero/releases)
- [Commits](https://github.com/tetratelabs/wazero/compare/v1.0.3...v1.1.0)

---
updated-dependencies:
- dependency-name: github.com/tetratelabs/wazero
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-02 01:03:42 +01:00
Nuno Cruces
18eeb85783 Improve mock. 2023-04-28 13:50:50 +01:00
Nuno Cruces
b36536979b Fix reopen. 2023-04-28 13:50:32 +01:00
Nuno Cruces
a6226c3b31 wazero v1.0.3. 2023-04-22 10:06:48 +01:00
Nuno Cruces
bed2ee7674 Refactor. 2023-04-22 00:15:44 +01:00
Nuno Cruces
7e6d178122 Fix. 2023-04-21 13:33:24 +01:00
Nuno Cruces
f360c77a78 Optimize blobs. (#10) 2023-04-21 13:31:45 +01:00
Nuno Cruces
759b11a05d wazero 1.0.2. 2023-04-18 23:33:56 +01:00
Nuno Cruces
93ce586139 Optimize time. 2023-04-18 01:00:59 +01:00
Nuno Cruces
2e5082c616 Query pragmas at startup. 2023-04-17 00:29:20 +01:00
Nuno Cruces
34acc28af8 Fix CI. 2023-04-14 15:48:20 +01:00
Nuno Cruces
c1a640f7d8 Build using wasi-sdk. 2023-04-14 15:31:17 +01:00
Nuno Cruces
005b15610a Memory optimizations. 2023-04-11 15:33:38 +01:00
Nuno Cruces
23ee4ccb0b Refactor. 2023-04-10 19:55:44 +01:00
Nuno Cruces
3a8cfd036d Dependencies. 2023-04-10 14:24:06 +01:00
Nuno Cruces
c38382fd8e Refactor. 2023-03-31 14:33:24 +01:00
Nuno Cruces
8509e0b6c8 Test coverage. 2023-03-31 13:42:31 +01:00
Nuno Cruces
9c07e57252 Refactor. 2023-03-29 15:06:22 +01:00
Nuno Cruces
80039385d3 Read only files. 2023-03-25 11:46:13 +00:00
Nuno Cruces
89f4327b2b Sync journal directories. 2023-03-25 11:16:51 +00:00
Nuno Cruces
37a3ff37e8 wazero 1.0. 2023-03-24 21:17:30 +00:00
Nuno Cruces
d880d6842c Refactor VFS. 2023-03-23 13:29:26 +00:00
Nuno Cruces
bef46e7954 Locking improvements (windows). 2023-03-23 12:40:55 +00:00
Nuno Cruces
4e72b4d117 Locking fix. 2023-03-23 11:26:19 +00:00
Nuno Cruces
3b08d02a83 Lock refactoring. 2023-03-23 01:55:54 +00:00
Nuno Cruces
b19c12c4c7 SQLite 3.41.2, prefer speed over size. 2023-03-23 00:44:43 +00:00
Nuno Cruces
859a21ef4e CI improvements. 2023-03-22 12:08:33 +00:00
Nuno Cruces
8ff0ee752f Use flock. 2023-03-22 03:15:54 +00:00
Nuno Cruces
589ad86f76 Extensions. 2023-03-21 00:13:12 +00:00
Nuno Cruces
1a3a1be1f6 Fix test. 2023-03-20 14:26:25 +00:00
Nuno Cruces
222c217bc8 Scripts. 2023-03-20 13:06:31 +00:00
Nuno Cruces
c1dc716391 VFS performance. 2023-03-20 11:02:34 +00:00
Nuno Cruces
71e1e5a8ee Avoid some copies. 2023-03-20 02:16:42 +00:00
Nuno Cruces
e4efb20c71 Generate coverage chart. 2023-03-18 03:51:05 +00:00
Nuno Cruces
2c9459d907 Add SQLite speedtest1. 2023-03-18 03:03:11 +00:00
Nuno Cruces
d0875e5fab Lock timeouts. 2023-03-18 01:13:31 +00:00
Nuno Cruces
15dec13f15 FCNTL_SIZE_HINT, refactor. 2023-03-17 17:13:03 +00:00
Nuno Cruces
f38e36109a FCNTL_HAS_MOVED. 2023-03-17 14:11:09 +00:00
Nuno Cruces
4cb65ccbd9 xFileControl, xDeviceCharacteristics, PSOW. 2023-03-17 13:39:19 +00:00
Nuno Cruces
f789c2fb8b OPEN_NOFOLLOW. 2023-03-16 12:27:44 +00:00
Nuno Cruces
c6a2617dfc Locking fixes. 2023-03-16 02:52:22 +00:00
Nuno Cruces
6fc0afcd12 Towards lock timeouts. 2023-03-15 13:58:16 +00:00
Nuno Cruces
77088962f5 SQLite 3.41.1. 2023-03-15 13:29:09 +00:00
Nuno Cruces
71da34861b Fix time collation. 2023-03-13 04:19:58 +00:00
234 changed files with 19052 additions and 4032 deletions

2
.github/FUNDING.yml vendored
View File

@@ -1 +1 @@
custom: https://www.paypal.com/donate/buttons/manage/33P59ELZWGMK6
custom: https://www.paypal.com/donate?hosted_button_id=33P59ELZWGMK6

View File

@@ -9,3 +9,7 @@ updates:
directory: "/" # Location of package manifests
schedule:
interval: "daily"
- package-ecosystem: "github-actions" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "daily"

10
.github/workflows/bsd.sh vendored Executable file
View File

@@ -0,0 +1,10 @@
#!/usr/bin/env bash
echo 'set -euo pipefail' > test.sh
for p in $(go list ./...); do
dir=".${p#github.com/ncruces/go-sqlite3}"
name="$(basename "$p").test"
(cd ${dir}; GOOS=freebsd go test -c)
[ -f "${dir}/${name}" ] && echo "(cd ${dir}; ./${name} -test.v)" >> test.sh
done

View File

@@ -1,76 +0,0 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ "main" ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ "main" ]
schedule:
- cron: '15 18 * * 6'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'go' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Use only 'java' to analyze code written in Java, Kotlin or both
# Use only 'javascript' to analyze code written in JavaScript, TypeScript or both
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
steps:
- name: Checkout repository
uses: actions/checkout@v3
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
# Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
# If the Autobuild fails above, remove it and uncomment the following three lines.
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
# - run: |
# echo "Run, Build Application using script"
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
with:
category: "/language:${{matrix.language}}"

23
.github/workflows/cross.sh vendored Executable file
View File

@@ -0,0 +1,23 @@
#!/usr/bin/env bash
set -euo pipefail
echo android ; GOOS=android GOARCH=amd64 go build .
echo darwin ; GOOS=darwin GOARCH=amd64 go build .
echo dragonfly ; GOOS=dragonfly GOARCH=amd64 go build .
echo freebsd ; GOOS=freebsd GOARCH=amd64 go build .
echo illumos ; GOOS=illumos GOARCH=amd64 go build .
echo ios ; GOOS=ios GOARCH=amd64 go build .
echo linux ; GOOS=linux GOARCH=amd64 go build .
echo netbsd ; GOOS=netbsd GOARCH=amd64 go build .
echo openbsd ; GOOS=openbsd GOARCH=amd64 go build .
echo plan9 ; GOOS=plan9 GOARCH=amd64 go build .
echo solaris ; GOOS=solaris GOARCH=amd64 go build .
echo windows ; GOOS=windows GOARCH=amd64 go build .
echo aix ; GOOS=aix GOARCH=ppc64 go build .
echo js ; GOOS=js GOARCH=wasm go build .
echo wasip1 ; GOOS=wasip1 GOARCH=wasm go build .
echo darwin-flock ; GOOS=darwin GOARCH=amd64 go build -tags sqlite3_flock .
echo darwin-nosys ; GOOS=darwin GOARCH=amd64 go build -tags sqlite3_nosys .
echo linux-nosys ; GOOS=linux GOARCH=amd64 go build -tags sqlite3_nosys .
echo windows-nosys ; GOOS=windows GOARCH=amd64 go build -tags sqlite3_nosys .
echo freebsd-nosys ; GOOS=freebsd GOARCH=amd64 go build -tags sqlite3_nosys .

18
.github/workflows/cross.yml vendored Normal file
View File

@@ -0,0 +1,18 @@
name: Cross compile
on:
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: stable
- name: Build
run: .github/workflows/cross.sh

View File

@@ -1,42 +0,0 @@
name: Go
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
test:
strategy:
matrix:
os: [macos-latest, ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
with:
lfs: 'true'
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: stable
cache: true
- name: Build
run: go build -v ./...
- name: Test
run: go test -v ./...
- name: Test data races
run: go test -v -race ./...
if: matrix.os == 'ubuntu-latest'
- name: Update coverage report
uses: ncruces/go-coverage-report@main
if: |
matrix.os == 'ubuntu-latest' &&
github.event_name == 'push'
continue-on-error: true

23
.github/workflows/repro.sh vendored Executable file
View File

@@ -0,0 +1,23 @@
#!/usr/bin/env bash
set -euo pipefail
if [[ "$OSTYPE" == "linux"* ]]; then
WASI_SDK="https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-21/wasi-sdk-21.0-linux.tar.gz"
BINARYEN="https://github.com/WebAssembly/binaryen/releases/download/version_117/binaryen-version_117-x86_64-linux.tar.gz"
elif [[ "$OSTYPE" == "darwin"* ]]; then
WASI_SDK="https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-21/wasi-sdk-21.0-macos.tar.gz"
BINARYEN="https://github.com/WebAssembly/binaryen/releases/download/version_117/binaryen-version_117-x86_64-macos.tar.gz"
elif [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" ]]; then
WASI_SDK="https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-21/wasi-sdk-21.0.m-mingw.tar.gz"
BINARYEN="https://github.com/WebAssembly/binaryen/releases/download/version_117/binaryen-version_117-x86_64-windows.tar.gz"
fi
# Download tools
mkdir -p tools
[ -d "tools/wasi-sdk"* ] || curl -#L "$WASI_SDK" | tar xzC tools &
[ -d "tools/binaryen-version"* ] || curl -#L "$BINARYEN" | tar xzC tools &
wait
sqlite3/download.sh # Download SQLite
embed/build.sh # Build WASM
git diff --exit-code # Check diffs

23
.github/workflows/repro.yml vendored Normal file
View File

@@ -0,0 +1,23 @@
name: Reproducible build
on:
workflow_dispatch:
jobs:
build:
strategy:
matrix:
os: [macos-latest, ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
with:
lfs: 'true'
- uses: actions/setup-go@v5
with:
go-version: stable
- name: Build
run: .github/workflows/repro.sh

129
.github/workflows/test.yml vendored Normal file
View File

@@ -0,0 +1,129 @@
name: Test
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
workflow_dispatch:
jobs:
test:
strategy:
matrix:
os: [macos-latest, ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
with: { lfs: 'true' }
- uses: actions/setup-go@v5
with: { go-version: stable }
- name: Format
run: gofmt -s -w . && git diff --exit-code
if: matrix.os != 'windows-latest'
- name: Tidy
run: go mod tidy && git diff --exit-code
- name: Download
run: go mod download
- name: Verify
run: go mod verify
- name: Vet
run: go vet ./...
- name: Build
run: go build -v ./...
- name: Test
run: go test -v ./...
- name: Test BSD locks
run: go test -v -tags sqlite3_flock ./...
if: matrix.os == 'macos-latest'
- name: Test no locks
run: go test -v -tags sqlite3_nosys ./tests -run TestDB_nolock
- name: Test GORM
run: gormlite/test.sh
- uses: ncruces/go-coverage-report@v0
with:
chart: true
amend: true
if: |
github.event_name == 'push' &&
matrix.os == 'ubuntu-latest'
test-bsd:
runs-on: ubuntu-latest
needs: test
steps:
- uses: actions/checkout@v4
with: { lfs: 'true' }
- uses: actions/setup-go@v5
with: { go-version: stable }
- name: Build
run: .github/workflows/bsd.sh
- name: Test
uses: cross-platform-actions/action@v0.23.0
with:
operating_system: freebsd
version: '14.0'
shell: bash
run: source test.sh
sync_files: runner-to-vm
test-m1:
runs-on: macos-14
needs: test
steps:
- uses: actions/checkout@v4
with: { lfs: 'true' }
- uses: actions/setup-go@v5
with: { go-version: stable }
- name: Test
run: go test -v ./...
test-386:
runs-on: ubuntu-latest
needs: test
steps:
- uses: actions/checkout@v4
with: { lfs: 'true' }
- uses: actions/setup-go@v5
with: { go-version: stable }
- name: Test
run: GOARCH=386 go test -v -short ./...
test-arm:
runs-on: ubuntu-latest
needs: test
steps:
- uses: actions/checkout@v4
with: { lfs: 'true' }
- uses: actions/setup-go@v5
with: { go-version: stable }
- uses: docker/setup-qemu-action@v3
- name: Test
run: GOARCH=arm64 go test -v -short ./...

159
README.md
View File

@@ -4,78 +4,139 @@
[![Go Report](https://goreportcard.com/badge/github.com/ncruces/go-sqlite3)](https://goreportcard.com/report/github.com/ncruces/go-sqlite3)
[![Go Coverage](https://github.com/ncruces/go-sqlite3/wiki/coverage.svg)](https://github.com/ncruces/go-sqlite3/wiki/Test-coverage-report)
Go module `github.com/ncruces/go-sqlite3` wraps a [WASM](https://webassembly.org/) build of [SQLite](https://sqlite.org/),
and uses [wazero](https://wazero.io/) to provide `cgo`-free SQLite bindings.
Go module `github.com/ncruces/go-sqlite3` is `cgo`-free [SQLite](https://sqlite.org/) wrapper.\
It provides a [`database/sql`](https://pkg.go.dev/database/sql) compatible driver,
as well as direct access to most of the [C SQLite API](https://sqlite.org/cintro.html).
- Package [`github.com/ncruces/go-sqlite3`](https://pkg.go.dev/github.com/ncruces/go-sqlite3)
wraps the [C SQLite API](https://www.sqlite.org/cintro.html)
([example usage](https://pkg.go.dev/github.com/ncruces/go-sqlite3#example-package)).
- Package [`github.com/ncruces/go-sqlite3/driver`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/driver)
provides a [`database/sql`](https://pkg.go.dev/database/sql) driver
([example usage](https://pkg.go.dev/github.com/ncruces/go-sqlite3/driver#example-package)).
- Package [`github.com/ncruces/go-sqlite3/embed`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/embed)
embeds a build of SQLite into your application.
It wraps a [WASM](https://webassembly.org/) build of SQLite, and uses [wazero](https://wazero.io/) as the runtime.\
Go, wazero and [`x/sys`](https://pkg.go.dev/golang.org/x/sys) are the _only_ runtime dependencies.
### Packages
- [`github.com/ncruces/go-sqlite3`](https://pkg.go.dev/github.com/ncruces/go-sqlite3)
wraps the [C SQLite API](https://sqlite.org/cintro.html)
([example usage](https://pkg.go.dev/github.com/ncruces/go-sqlite3#example-package)).
- [`github.com/ncruces/go-sqlite3/driver`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/driver)
provides a [`database/sql`](https://pkg.go.dev/database/sql) driver
([example usage](https://pkg.go.dev/github.com/ncruces/go-sqlite3/driver#example-package)).
- [`github.com/ncruces/go-sqlite3/embed`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/embed)
embeds a build of SQLite into your application.
- [`github.com/ncruces/go-sqlite3/vfs`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/vfs)
wraps the [C SQLite VFS API](https://sqlite.org/vfs.html) and provides a pure Go implementation.
- [`github.com/ncruces/go-sqlite3/gormlite`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/gormlite)
provides a [GORM](https://gorm.io) driver.
### Extensions
- [`github.com/ncruces/go-sqlite3/ext/array`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/array)
provides the [`array`](https://sqlite.org/carray.html) table-valued function.
- [`github.com/ncruces/go-sqlite3/ext/blobio`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/blobio)
simplifies [incremental BLOB I/O](https://sqlite.org/c3ref/blob_open.html).
- [`github.com/ncruces/go-sqlite3/ext/csv`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/csv)
reads [comma-separated values](https://sqlite.org/csv.html).
- [`github.com/ncruces/go-sqlite3/ext/fileio`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/fileio)
reads, writes and lists files.
- [`github.com/ncruces/go-sqlite3/ext/hash`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/hash)
provides cryptographic hash functions.
- [`github.com/ncruces/go-sqlite3/ext/lines`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/lines)
reads data [line-by-line](https://github.com/asg017/sqlite-lines).
- [`github.com/ncruces/go-sqlite3/ext/pivot`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/pivot)
creates [pivot tables](https://github.com/jakethaw/pivot_vtab).
- [`github.com/ncruces/go-sqlite3/ext/statement`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/statement)
creates [parameterized views](https://github.com/0x09/sqlite-statement-vtab).
- [`github.com/ncruces/go-sqlite3/ext/stats`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/stats)
provides [statistics](https://www.oreilly.com/library/view/sql-in-a/9780596155322/ch04s02.html) functions.
- [`github.com/ncruces/go-sqlite3/ext/unicode`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/unicode)
provides [Unicode aware](https://sqlite.org/src/dir/ext/icu) functions.
- [`github.com/ncruces/go-sqlite3/ext/zorder`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/zorder)
maps multidimensional data to one dimension.
- [`github.com/ncruces/go-sqlite3/vfs/memdb`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/vfs/memdb)
implements an in-memory VFS.
- [`github.com/ncruces/go-sqlite3/vfs/readervfs`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/vfs/readervfs)
implements a VFS for immutable databases.
### Advanced features
- [incremental BLOB I/O](https://sqlite.org/c3ref/blob_open.html)
- [nested transactions](https://sqlite.org/lang_savepoint.html)
- [custom functions](https://sqlite.org/c3ref/create_function.html)
- [virtual tables](https://sqlite.org/vtab.html)
- [custom VFSes](https://sqlite.org/vfs.html)
- [online backup](https://sqlite.org/backup.html)
- [JSON support](https://sqlite.org/json1.html)
- [math functions](https://sqlite.org/lang_mathfunc.html)
- [full-text search](https://sqlite.org/fts5.html)
- [geospatial search](https://sqlite.org/geopoly.html)
- [and more…](embed/README.md)
### Caveats
This module replaces the SQLite [OS Interface](https://www.sqlite.org/vfs.html) (aka VFS)
with a pure Go implementation.
This has numerous benefits, but also comes with some caveats.
This module replaces the SQLite [OS Interface](https://sqlite.org/vfs.html)
(aka VFS) with a [pure Go](vfs/) implementation.
This has benefits, but also comes with some drawbacks.
#### Write-Ahead Logging
Because WASM does not support shared memory,
[WAL](https://www.sqlite.org/wal.html) support is [limited](https://www.sqlite.org/wal.html#noshm).
[WAL](https://sqlite.org/wal.html) support is [limited](https://sqlite.org/wal.html#noshm).
To work around this limitation, SQLite is compiled with
[`SQLITE_DEFAULT_LOCKING_MODE=1`](https://www.sqlite.org/compile.html#default_locking_mode),
making `EXCLUSIVE` the default locking mode.
For non-WAL databases, `NORMAL` locking mode can be activated with
[`PRAGMA locking_mode=NORMAL`](https://www.sqlite.org/pragma.html#pragma_locking_mode).
To work around this limitation, SQLite is [patched](sqlite3/locking_mode.patch)
to always use `EXCLUSIVE` locking mode for WAL databases.
Because connection pooling is incompatible with `EXCLUSIVE` locking mode,
the `database/sql` driver defaults to `NORMAL` locking mode.
To open WAL databases, or use `EXCLUSIVE` locking mode,
disable connection pooling by calling
to use the [`database/sql`](https://pkg.go.dev/database/sql) driver
with WAL mode databases you should disable connection pooling by calling
[`db.SetMaxOpenConns(1)`](https://pkg.go.dev/database/sql#DB.SetMaxOpenConns).
#### Open File Description Locks
#### File Locking
On Unix, this module uses [OFD locks](https://www.gnu.org/software/libc/manual/html_node/Open-File-Description-Locks.html)
POSIX advisory locks, which SQLite uses on Unix, are
[broken by design](https://sqlite.org/src/artifact/2e8b12?ln=1073-1161).
On Linux, macOS and illumos, this module uses
[OFD locks](https://www.gnu.org/software/libc/manual/html_node/Open-File-Description-Locks.html)
to synchronize access to database files.
OFD locks are fully compatible with POSIX advisory locks.
POSIX advisory locks, which SQLite uses, are [broken by design](https://www.sqlite.org/src/artifact/90c4fa?ln=1073-1161).
OFD locks are fully compatible with process-associated POSIX advisory locks,
and are supported on Linux, macOS and illumos.
As a work around for other Unixes, you can use [`nolock=1`](https://www.sqlite.org/uri.html).
On BSD Unixes, this module uses
[BSD locks](https://man.freebsd.org/cgi/man.cgi?query=flock&sektion=2).
On BSD Unixes, BSD locks are fully compatible with POSIX advisory locks.
#### Testing
On Windows, this module uses `LockFileEx` and `UnlockFileEx`,
like SQLite.
The pure Go VFS is stress tested by running an unmodified build of SQLite's
On all other platforms, file locking is not supported, and you must use
[`nolock=1`](https://sqlite.org/uri.html#urinolock)
(or [`immutable=1`](https://sqlite.org/uri.html#uriimmutable))
to open database files.
You can use [`vfs.SupportsFileLocking`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/vfs#SupportsFileLocking)
to check if your platform supports file locking.
To use the [`database/sql`](https://pkg.go.dev/database/sql) driver
with `nolock=1` you must disable connection pooling by calling
[`db.SetMaxOpenConns(1)`](https://pkg.go.dev/database/sql#DB.SetMaxOpenConns).
### Testing
This project aims for [high test coverage](https://github.com/ncruces/go-sqlite3/wiki/Test-coverage-report).
It also benefits greatly from [SQLite's](https://sqlite.org/testing.html) and
[wazero's](https://tetrate.io/blog/introducing-wazero-from-tetrate/#:~:text=Rock%2Dsolid%20test%20approach) thorough testing.
The pure Go VFS is tested by running SQLite's
[mptest](https://github.com/sqlite/sqlite/blob/master/mptest/mptest.c)
on Linux, macOS and Windows.
on Linux, macOS, Windows and FreeBSD.
### Roadmap
### Performance
Perfomance of the [`database/sql`](https://pkg.go.dev/database/sql) driver is
[competitive](https://github.com/cvilsmeier/go-sqlite-bench) with alternatives.
The WASM and VFS layers are also tested by running SQLite's
[speedtest1](https://github.com/sqlite/sqlite/blob/master/test/speedtest1.c).
- [ ] advanced SQLite features
- [x] nested transactions
- [x] incremental BLOB I/O
- [x] online backup
- [ ] snapshots
- [ ] session extension
- [ ] resumable bulk update
- [ ] shared-cache mode
- [ ] unlock-notify
- [ ] custom SQL functions
- [ ] custom VFSes
- [ ] read-only VFS, wrapping an [`io.ReaderAt`](https://pkg.go.dev/io#ReaderAt)
- [ ] in-memory VFS, wrapping a [`bytes.Buffer`](https://pkg.go.dev/bytes#Buffer)
- [ ] cloud-based VFS, based on [Cloud Backed SQLite](https://sqlite.org/cloudsqlite/doc/trunk/www/index.wiki)
- [ ] custom VFS API
### Alternatives
- [`modernc.org/sqlite`](https://pkg.go.dev/modernc.org/sqlite)
- [`crawshaw.io/sqlite`](https://pkg.go.dev/crawshaw.io/sqlite)
- [`github.com/mattn/go-sqlite3`](https://pkg.go.dev/github.com/mattn/go-sqlite3)
- [`github.com/zombiezen/go-sqlite`](https://pkg.go.dev/github.com/zombiezen/go-sqlite)
- [`github.com/zombiezen/go-sqlite`](https://pkg.go.dev/github.com/zombiezen/go-sqlite)

View File

@@ -1,8 +1,8 @@
package sqlite3
// Backup is a handle to an open BLOB.
// Backup is an handle to an ongoing online backup operation.
//
// https://www.sqlite.org/c3ref/backup.html
// https://sqlite.org/c3ref/backup.html
type Backup struct {
c *Conn
handle uint32
@@ -11,11 +11,11 @@ type Backup struct {
// Backup backs up srcDB on the src connection to the "main" database in dstURI.
//
// Backup calls [Open] to open the SQLite database file dstURI,
// Backup opens the SQLite database file dstURI,
// and blocks until the entire backup is complete.
// Use [Conn.BackupInit] for incremental backup.
//
// https://www.sqlite.org/backup.html
// https://sqlite.org/backup.html
func (src *Conn) Backup(srcDB, dstURI string) error {
b, err := src.BackupInit(srcDB, dstURI)
if err != nil {
@@ -28,12 +28,12 @@ func (src *Conn) Backup(srcDB, dstURI string) error {
// Restore restores dstDB on the dst connection from the "main" database in srcURI.
//
// Restore calls [Open] to open the SQLite database file srcURI,
// Restore opens the SQLite database file srcURI,
// and blocks until the entire restore is complete.
//
// https://www.sqlite.org/backup.html
// https://sqlite.org/backup.html
func (dst *Conn) Restore(dstDB, srcURI string) error {
src, err := dst.openDB(srcURI, OPEN_READWRITE|OPEN_CREATE|OPEN_URI)
src, err := dst.openDB(srcURI, OPEN_READONLY|OPEN_URI)
if err != nil {
return err
}
@@ -48,11 +48,11 @@ func (dst *Conn) Restore(dstDB, srcURI string) error {
// BackupInit initializes a backup operation to copy the content of one database into another.
//
// BackupInit calls [Open] to open the SQLite database file dstURI,
// BackupInit opens the SQLite database file dstURI,
// then initializes a backup that copies the contents of srcDB on the src connection
// to the "main" database in dstURI.
//
// https://www.sqlite.org/c3ref/backup_finish.html#sqlite3backupinit
// https://sqlite.org/c3ref/backup_finish.html#sqlite3backupinit
func (src *Conn) BackupInit(srcDB, dstURI string) (*Backup, error) {
dst, err := src.openDB(dstURI, OPEN_READWRITE|OPEN_CREATE|OPEN_URI)
if err != nil {
@@ -62,7 +62,7 @@ func (src *Conn) BackupInit(srcDB, dstURI string) (*Backup, error) {
}
func (c *Conn) backupInit(dst uint32, dstName string, src uint32, srcName string) (*Backup, error) {
defer c.arena.reset()
defer c.arena.mark()()
dstPtr := c.arena.string(dstName)
srcPtr := c.arena.string(srcName)
@@ -71,19 +71,19 @@ func (c *Conn) backupInit(dst uint32, dstName string, src uint32, srcName string
other = src
}
r := c.call(c.api.backupInit,
r := c.call("sqlite3_backup_init",
uint64(dst), uint64(dstPtr),
uint64(src), uint64(srcPtr))
if r[0] == 0 {
if r == 0 {
defer c.closeDB(other)
r = c.call(c.api.errcode, uint64(dst))
return nil, c.module.error(r[0], dst)
r = c.call("sqlite3_errcode", uint64(dst))
return nil, c.sqlite.error(r, dst)
}
return &Backup{
c: c,
otherc: other,
handle: uint32(r[0]),
handle: uint32(r),
}, nil
}
@@ -91,44 +91,44 @@ func (c *Conn) backupInit(dst uint32, dstName string, src uint32, srcName string
//
// It is safe to close a nil, zero or closed Backup.
//
// https://www.sqlite.org/c3ref/backup_finish.html#sqlite3backupfinish
// https://sqlite.org/c3ref/backup_finish.html#sqlite3backupfinish
func (b *Backup) Close() error {
if b == nil || b.handle == 0 {
return nil
}
r := b.c.call(b.c.api.backupFinish, uint64(b.handle))
r := b.c.call("sqlite3_backup_finish", uint64(b.handle))
b.c.closeDB(b.otherc)
b.handle = 0
return b.c.error(r[0])
return b.c.error(r)
}
// Step copies up to nPage pages between the source and destination databases.
// If nPage is negative, all remaining source pages are copied.
//
// https://www.sqlite.org/c3ref/backup_finish.html#sqlite3backupstep
// https://sqlite.org/c3ref/backup_finish.html#sqlite3backupstep
func (b *Backup) Step(nPage int) (done bool, err error) {
r := b.c.call(b.c.api.backupStep, uint64(b.handle), uint64(nPage))
if r[0] == _DONE {
r := b.c.call("sqlite3_backup_step", uint64(b.handle), uint64(nPage))
if r == _DONE {
return true, nil
}
return false, b.c.error(r[0])
return false, b.c.error(r)
}
// Remaining returns the number of pages still to be backed up
// at the conclusion of the most recent [Backup.Step].
//
// https://www.sqlite.org/c3ref/backup_finish.html#sqlite3backupremaining
// https://sqlite.org/c3ref/backup_finish.html#sqlite3backupremaining
func (b *Backup) Remaining() int {
r := b.c.call(b.c.api.backupRemaining, uint64(b.handle))
return int(r[0])
r := b.c.call("sqlite3_backup_remaining", uint64(b.handle))
return int(int32(r))
}
// PageCount returns the total number of pages in the source database
// at the conclusion of the most recent [Backup.Step].
//
// https://www.sqlite.org/c3ref/backup_finish.html#sqlite3backuppagecount
// https://sqlite.org/c3ref/backup_finish.html#sqlite3backuppagecount
func (b *Backup) PageCount() int {
r := b.c.call(b.c.api.backupFinish, uint64(b.handle))
return int(r[0])
r := b.c.call("sqlite3_backup_pagecount", uint64(b.handle))
return int(int32(r))
}

183
blob.go
View File

@@ -1,32 +1,36 @@
package sqlite3
import "io"
import (
"io"
"github.com/ncruces/go-sqlite3/internal/util"
)
// ZeroBlob represents a zero-filled, length n BLOB
// that can be used as an argument to
// [database/sql.DB.Exec] and similar methods.
type ZeroBlob int64
// Blob is a handle to an open BLOB.
// Blob is an handle to an open BLOB.
//
// It implements [io.ReadWriteSeeker] for incremental BLOB I/O.
//
// https://www.sqlite.org/c3ref/blob.html
// https://sqlite.org/c3ref/blob.html
type Blob struct {
c *Conn
handle uint32
bytes int64
offset int64
handle uint32
}
var _ io.ReadWriteSeeker = &Blob{}
// OpenBlob opens a BLOB for incremental I/O.
//
// https://www.sqlite.org/c3ref/blob_open.html
// https://sqlite.org/c3ref/blob_open.html
func (c *Conn) OpenBlob(db, table, column string, row int64, write bool) (*Blob, error) {
c.checkInterrupt()
defer c.arena.reset()
defer c.arena.mark()()
blobPtr := c.arena.new(ptrlen)
dbPtr := c.arena.string(db)
tablePtr := c.arena.string(table)
@@ -37,17 +41,17 @@ func (c *Conn) OpenBlob(db, table, column string, row int64, write bool) (*Blob,
flags = 1
}
r := c.call(c.api.blobOpen, uint64(c.handle),
r := c.call("sqlite3_blob_open", uint64(c.handle),
uint64(dbPtr), uint64(tablePtr), uint64(columnPtr),
uint64(row), flags, uint64(blobPtr))
if err := c.error(r[0]); err != nil {
if err := c.error(r); err != nil {
return nil, err
}
blob := Blob{c: c}
blob.handle = c.mem.readUint32(blobPtr)
blob.bytes = int64(c.call(c.api.blobBytes, uint64(blob.handle))[0])
blob.handle = util.ReadUint32(c.mod, blobPtr)
blob.bytes = int64(c.call("sqlite3_blob_bytes", uint64(blob.handle)))
return &blob, nil
}
@@ -55,73 +59,111 @@ func (c *Conn) OpenBlob(db, table, column string, row int64, write bool) (*Blob,
//
// It is safe to close a nil, zero or closed Blob.
//
// https://www.sqlite.org/c3ref/blob_close.html
// https://sqlite.org/c3ref/blob_close.html
func (b *Blob) Close() error {
if b == nil || b.handle == 0 {
return nil
}
r := b.c.call(b.c.api.blobClose, uint64(b.handle))
r := b.c.call("sqlite3_blob_close", uint64(b.handle))
b.handle = 0
return b.c.error(r[0])
return b.c.error(r)
}
// Size returns the size of the BLOB in bytes.
//
// https://www.sqlite.org/c3ref/blob_bytes.html
// https://sqlite.org/c3ref/blob_bytes.html
func (b *Blob) Size() int64 {
return b.bytes
}
// Read implements the [io.Reader] interface.
//
// https://www.sqlite.org/c3ref/blob_read.html
// https://sqlite.org/c3ref/blob_read.html
func (b *Blob) Read(p []byte) (n int, err error) {
if b.offset >= b.bytes {
return 0, io.EOF
}
avail := b.bytes - b.offset
want := int64(len(p))
if want > avail {
want = avail
}
defer b.c.arena.mark()()
ptr := b.c.arena.new(uint64(want))
r := b.c.call("sqlite3_blob_read", uint64(b.handle),
uint64(ptr), uint64(want), uint64(b.offset))
err = b.c.error(r)
if err != nil {
return 0, err
}
b.offset += want
if b.offset >= b.bytes {
err = io.EOF
}
copy(p, util.View(b.c.mod, ptr, uint64(want)))
return int(want), err
}
// WriteTo implements the [io.WriterTo] interface.
//
// https://sqlite.org/c3ref/blob_read.html
func (b *Blob) WriteTo(w io.Writer) (n int64, err error) {
if b.offset >= b.bytes {
return 0, nil
}
want := int64(1024 * 1024)
avail := b.bytes - b.offset
if want > avail {
want = avail
}
ptr := b.c.new(uint64(want))
defer b.c.free(ptr)
defer b.c.arena.mark()()
ptr := b.c.arena.new(uint64(want))
r := b.c.call(b.c.api.blobRead, uint64(b.handle),
uint64(ptr), uint64(want), uint64(b.offset))
err = b.c.error(r[0])
if err != nil {
return 0, err
}
for want > 0 {
r := b.c.call("sqlite3_blob_read", uint64(b.handle),
uint64(ptr), uint64(want), uint64(b.offset))
err = b.c.error(r)
if err != nil {
return n, err
}
mem := b.c.mem.view(ptr, uint64(want))
copy(p, mem)
b.offset += want
if b.offset >= b.bytes {
err = io.EOF
mem := util.View(b.c.mod, ptr, uint64(want))
m, err := w.Write(mem[:want])
b.offset += int64(m)
n += int64(m)
if err != nil {
return n, err
}
if int64(m) != want {
return n, io.ErrShortWrite
}
avail = b.bytes - b.offset
if want > avail {
want = avail
}
}
return int(want), err
return n, nil
}
// Write implements the [io.Writer] interface.
//
// https://www.sqlite.org/c3ref/blob_write.html
// https://sqlite.org/c3ref/blob_write.html
func (b *Blob) Write(p []byte) (n int, err error) {
offset := b.offset
if offset > b.bytes {
offset = b.bytes
}
defer b.c.arena.mark()()
ptr := b.c.arena.bytes(p)
ptr := b.c.newBytes(p)
defer b.c.free(ptr)
r := b.c.call(b.c.api.blobWrite, uint64(b.handle),
uint64(ptr), uint64(len(p)), uint64(offset))
err = b.c.error(r[0])
r := b.c.call("sqlite3_blob_write", uint64(b.handle),
uint64(ptr), uint64(len(p)), uint64(b.offset))
err = b.c.error(r)
if err != nil {
return 0, err
}
@@ -129,11 +171,60 @@ func (b *Blob) Write(p []byte) (n int, err error) {
return len(p), nil
}
// ReadFrom implements the [io.ReaderFrom] interface.
//
// https://sqlite.org/c3ref/blob_write.html
func (b *Blob) ReadFrom(r io.Reader) (n int64, err error) {
want := int64(1024 * 1024)
avail := b.bytes - b.offset
if l, ok := r.(*io.LimitedReader); ok && want > l.N {
want = l.N
}
if want > avail {
want = avail
}
if want < 1 {
want = 1
}
defer b.c.arena.mark()()
ptr := b.c.arena.new(uint64(want))
for {
mem := util.View(b.c.mod, ptr, uint64(want))
m, err := r.Read(mem[:want])
if m > 0 {
r := b.c.call("sqlite3_blob_write", uint64(b.handle),
uint64(ptr), uint64(m), uint64(b.offset))
err := b.c.error(r)
if err != nil {
return n, err
}
b.offset += int64(m)
n += int64(m)
}
if err == io.EOF {
return n, nil
}
if err != nil {
return n, err
}
avail = b.bytes - b.offset
if want > avail {
want = avail
}
if want < 1 {
want = 1
}
}
}
// Seek implements the [io.Seeker] interface.
func (b *Blob) Seek(offset int64, whence int) (int64, error) {
switch whence {
default:
return 0, whenceErr
return 0, util.WhenceErr
case io.SeekStart:
break
case io.SeekCurrent:
@@ -142,7 +233,7 @@ func (b *Blob) Seek(offset int64, whence int) (int64, error) {
offset += b.bytes
}
if offset < 0 {
return 0, offsetErr
return 0, util.OffsetErr
}
b.offset = offset
return offset, nil
@@ -150,10 +241,10 @@ func (b *Blob) Seek(offset int64, whence int) (int64, error) {
// Reopen moves a BLOB handle to a new row of the same database table.
//
// https://www.sqlite.org/c3ref/blob_reopen.html
// https://sqlite.org/c3ref/blob_reopen.html
func (b *Blob) Reopen(row int64) error {
r := b.c.call(b.c.api.blobReopen, uint64(b.handle), uint64(row))
b.bytes = int64(b.c.call(b.c.api.blobBytes, uint64(b.handle))[0])
err := b.c.error(b.c.call("sqlite3_blob_reopen", uint64(b.handle), uint64(row)))
b.bytes = int64(b.c.call("sqlite3_blob_bytes", uint64(b.handle)))
b.offset = 0
return b.c.error(r[0])
return err
}

103
config.go Normal file
View File

@@ -0,0 +1,103 @@
package sqlite3
import (
"context"
"github.com/ncruces/go-sqlite3/internal/util"
"github.com/tetratelabs/wazero/api"
)
// Config makes configuration changes to a database connection.
// Only boolean configuration options are supported.
// Called with no arg reads the current configuration value,
// called with one arg sets and returns the new value.
//
// https://sqlite.org/c3ref/db_config.html
func (c *Conn) Config(op DBConfig, arg ...bool) (bool, error) {
defer c.arena.mark()()
argsPtr := c.arena.new(2 * ptrlen)
var flag int
switch {
case len(arg) == 0:
flag = -1
case arg[0]:
flag = 1
}
util.WriteUint32(c.mod, argsPtr+0*ptrlen, uint32(flag))
util.WriteUint32(c.mod, argsPtr+1*ptrlen, argsPtr)
r := c.call("sqlite3_db_config", uint64(c.handle),
uint64(op), uint64(argsPtr))
return util.ReadUint32(c.mod, argsPtr) != 0, c.error(r)
}
// ConfigLog sets up the error logging callback for the connection.
//
// https://sqlite.org/errlog.html
func (c *Conn) ConfigLog(cb func(code ExtendedErrorCode, msg string)) error {
var enable uint64
if cb != nil {
enable = 1
}
r := c.call("sqlite3_config_log_go", enable)
if err := c.error(r); err != nil {
return err
}
c.log = cb
return nil
}
func logCallback(ctx context.Context, mod api.Module, _, iCode, zMsg uint32) {
if c, ok := ctx.Value(connKey{}).(*Conn); ok && c.log != nil {
msg := util.ReadString(mod, zMsg, _MAX_LENGTH)
c.log(xErrorCode(iCode), msg)
}
}
// Limit allows the size of various constructs to be
// limited on a connection by connection basis.
//
// https://sqlite.org/c3ref/limit.html
func (c *Conn) Limit(id LimitCategory, value int) int {
r := c.call("sqlite3_limit", uint64(c.handle), uint64(id), uint64(value))
return int(int32(r))
}
// SetAuthorizer registers an authorizer callback with the database connection.
//
// https://sqlite.org/c3ref/set_authorizer.html
func (c *Conn) SetAuthorizer(cb func(action AuthorizerActionCode, name3rd, name4th, schema, nameInner string) AuthorizerReturnCode) error {
var enable uint64
if cb != nil {
enable = 1
}
r := c.call("sqlite3_set_authorizer_go", uint64(c.handle), enable)
if err := c.error(r); err != nil {
return err
}
c.authorizer = cb
return nil
}
func authorizerCallback(ctx context.Context, mod api.Module, pDB uint32, action AuthorizerActionCode, zName3rd, zName4th, zSchema, zNameInner uint32) AuthorizerReturnCode {
if c, ok := ctx.Value(connKey{}).(*Conn); ok && c.handle == pDB && c.authorizer != nil {
var name3rd, name4th, schema, nameInner string
if zName3rd != 0 {
name3rd = util.ReadString(mod, zName3rd, _MAX_NAME)
}
if zName4th != 0 {
name4th = util.ReadString(mod, zName4th, _MAX_NAME)
}
if zSchema != 0 {
schema = util.ReadString(mod, zSchema, _MAX_NAME)
}
if zNameInner != 0 {
nameInner = util.ReadString(mod, zNameInner, _MAX_NAME)
}
return c.authorizer(action, name3rd, name4th, schema, nameInner)
}
return AUTH_OK
}

309
conn.go
View File

@@ -2,61 +2,74 @@ package sqlite3
import (
"context"
"database/sql/driver"
"errors"
"fmt"
"math"
"net/url"
"runtime"
"strings"
"sync/atomic"
"unsafe"
"time"
"github.com/ncruces/go-sqlite3/internal/util"
"github.com/tetratelabs/wazero/api"
)
// Conn is a database connection handle.
// A Conn is not safe for concurrent use by multiple goroutines.
//
// https://www.sqlite.org/c3ref/sqlite3.html
// https://sqlite.org/c3ref/sqlite3.html
type Conn struct {
*module
*sqlite
handle uint32
arena arena
interrupt context.Context
waiter chan struct{}
pending *Stmt
interrupt context.Context
pending *Stmt
busy func(int) bool
log func(xErrorCode, string)
collation func(*Conn, string)
authorizer func(AuthorizerActionCode, string, string, string, string) AuthorizerReturnCode
update func(AuthorizerActionCode, string, string, int64)
commit func() bool
rollback func()
arena arena
handle uint32
}
// Open calls [OpenFlags] with [OPEN_READWRITE], [OPEN_CREATE] and [OPEN_URI].
// Open calls [OpenFlags] with [OPEN_READWRITE], [OPEN_CREATE], [OPEN_URI] and [OPEN_NOFOLLOW].
func Open(filename string) (*Conn, error) {
return newConn(filename, OPEN_READWRITE|OPEN_CREATE|OPEN_URI)
return newConn(filename, OPEN_READWRITE|OPEN_CREATE|OPEN_URI|OPEN_NOFOLLOW)
}
// OpenFlags opens an SQLite database file as specified by the filename argument.
//
// If none of the required flags is used, a combination of [OPEN_READWRITE] and [OPEN_CREATE] is used.
// If a URI filename is used, PRAGMA statements to execute can be specified using "_pragma":
//
// sqlite3.Open("file:demo.db?_pragma=busy_timeout(10000)&_pragma=locking_mode(normal)")
// sqlite3.Open("file:demo.db?_pragma=busy_timeout(10000)")
//
// https://www.sqlite.org/c3ref/open.html
// https://sqlite.org/c3ref/open.html
func OpenFlags(filename string, flags OpenFlag) (*Conn, error) {
if flags&(OPEN_READONLY|OPEN_READWRITE|OPEN_CREATE) == 0 {
flags |= OPEN_READWRITE | OPEN_CREATE
}
return newConn(filename, flags)
}
type connKey struct{}
func newConn(filename string, flags OpenFlag) (conn *Conn, err error) {
mod, err := instantiateModule()
sqlite, err := instantiateSQLite()
if err != nil {
return nil, err
}
defer func() {
if conn == nil {
mod.close()
} else {
runtime.SetFinalizer(conn, finalizer[Conn](3))
sqlite.close()
}
}()
c := &Conn{module: mod}
c := &Conn{sqlite: sqlite}
c.arena = c.newArena(1024)
c.ctx = context.WithValue(c.ctx, connKey{}, c)
c.handle, err = c.openDB(filename, flags)
if err != nil {
return nil, err
@@ -65,14 +78,15 @@ func newConn(filename string, flags OpenFlag) (conn *Conn, err error) {
}
func (c *Conn) openDB(filename string, flags OpenFlag) (uint32, error) {
defer c.arena.reset()
defer c.arena.mark()()
connPtr := c.arena.new(ptrlen)
namePtr := c.arena.string(filename)
r := c.call(c.api.open, uint64(namePtr), uint64(connPtr), uint64(flags), 0)
flags |= OPEN_EXRESCODE
r := c.call("sqlite3_open_v2", uint64(namePtr), uint64(connPtr), uint64(flags), 0)
handle := c.mem.readUint32(connPtr)
if err := c.module.error(r[0], handle); err != nil {
handle := util.ReadUint32(c.mod, connPtr)
if err := c.sqlite.error(r, handle); err != nil {
c.closeDB(handle)
return 0, err
}
@@ -84,14 +98,13 @@ func (c *Conn) openDB(filename string, flags OpenFlag) (uint32, error) {
for _, p := range query["_pragma"] {
pragmas.WriteString(`PRAGMA `)
pragmas.WriteString(p)
pragmas.WriteByte(';')
pragmas.WriteString(`;`)
}
}
c.arena.reset()
pragmaPtr := c.arena.string(pragmas.String())
r := c.call(c.api.exec, uint64(handle), uint64(pragmaPtr), 0, 0, 0)
if err := c.module.error(r[0], handle, pragmas.String()); err != nil {
r := c.call("sqlite3_exec", uint64(handle), uint64(pragmaPtr), 0, 0, 0)
if err := c.sqlite.error(r, handle, pragmas.String()); err != nil {
if errors.Is(err, ERROR) {
err = fmt.Errorf("sqlite3: invalid _pragma: %w", err)
}
@@ -99,14 +112,13 @@ func (c *Conn) openDB(filename string, flags OpenFlag) (uint32, error) {
return 0, err
}
}
c.call(c.api.timeCollation, uint64(handle))
c.call("sqlite3_progress_handler_go", uint64(handle), 100)
return handle, nil
}
func (c *Conn) closeDB(handle uint32) {
r := c.call(c.api.closeZombie, uint64(handle))
if err := c.module.error(r[0], handle); err != nil {
r := c.call("sqlite3_close_v2", uint64(handle))
if err := c.sqlite.error(r, handle); err != nil {
panic(err)
}
}
@@ -119,37 +131,35 @@ func (c *Conn) closeDB(handle uint32) {
//
// It is safe to close a nil, zero or closed Conn.
//
// https://www.sqlite.org/c3ref/close.html
// https://sqlite.org/c3ref/close.html
func (c *Conn) Close() error {
if c == nil || c.handle == 0 {
return nil
}
c.SetInterrupt(context.Background())
c.pending.Close()
c.pending = nil
r := c.call(c.api.close, uint64(c.handle))
if err := c.error(r[0]); err != nil {
r := c.call("sqlite3_close", uint64(c.handle))
if err := c.error(r); err != nil {
return err
}
c.handle = 0
runtime.SetFinalizer(c, nil)
return c.module.close()
return c.close()
}
// Exec is a convenience function that allows an application to run
// multiple statements of SQL without having to use a lot of code.
//
// https://www.sqlite.org/c3ref/exec.html
// https://sqlite.org/c3ref/exec.html
func (c *Conn) Exec(sql string) error {
c.checkInterrupt()
defer c.arena.reset()
defer c.arena.mark()()
sqlPtr := c.arena.string(sql)
r := c.call(c.api.exec, uint64(c.handle), uint64(sqlPtr), 0, 0, 0)
return c.error(r[0])
r := c.call("sqlite3_exec", uint64(c.handle), uint64(sqlPtr), 0, 0, 0)
return c.error(r, sql)
}
// Prepare calls [Conn.PrepareFlags] with no flags.
@@ -162,60 +172,113 @@ func (c *Conn) Prepare(sql string) (stmt *Stmt, tail string, err error) {
// If the input text contains no SQL (if the input is an empty string or a comment),
// both stmt and err will be nil.
//
// https://www.sqlite.org/c3ref/prepare.html
// https://sqlite.org/c3ref/prepare.html
func (c *Conn) PrepareFlags(sql string, flags PrepareFlag) (stmt *Stmt, tail string, err error) {
if emptyStatement(sql) {
return nil, "", nil
if len(sql) > _MAX_LENGTH {
return nil, "", TOOBIG
}
defer c.arena.reset()
defer c.arena.mark()()
stmtPtr := c.arena.new(ptrlen)
tailPtr := c.arena.new(ptrlen)
sqlPtr := c.arena.string(sql)
r := c.call(c.api.prepare, uint64(c.handle),
r := c.call("sqlite3_prepare_v3", uint64(c.handle),
uint64(sqlPtr), uint64(len(sql)+1), uint64(flags),
uint64(stmtPtr), uint64(tailPtr))
stmt = &Stmt{c: c}
stmt.handle = c.mem.readUint32(stmtPtr)
i := c.mem.readUint32(tailPtr)
tail = sql[i-sqlPtr:]
stmt.handle = util.ReadUint32(c.mod, stmtPtr)
if sql := sql[util.ReadUint32(c.mod, tailPtr)-sqlPtr:]; sql != "" {
tail = sql
}
if err := c.error(r[0], sql); err != nil {
if err := c.error(r, sql); err != nil {
return nil, "", err
}
if stmt.handle == 0 {
return nil, "", nil
}
return
return stmt, tail, nil
}
// DBName returns the schema name for n-th database on the database connection.
//
// https://sqlite.org/c3ref/db_name.html
func (c *Conn) DBName(n int) string {
r := c.call("sqlite3_db_name", uint64(c.handle), uint64(n))
ptr := uint32(r)
if ptr == 0 {
return ""
}
return util.ReadString(c.mod, ptr, _MAX_NAME)
}
// ReadOnly determines if a database is read-only.
//
// https://sqlite.org/c3ref/db_readonly.html
func (c *Conn) ReadOnly(schema string) (ro bool, ok bool) {
var ptr uint32
if schema != "" {
defer c.arena.mark()()
ptr = c.arena.string(schema)
}
r := c.call("sqlite3_db_readonly", uint64(c.handle), uint64(ptr))
return int32(r) > 0, int32(r) < 0
}
// GetAutocommit tests the connection for auto-commit mode.
//
// https://www.sqlite.org/c3ref/get_autocommit.html
// https://sqlite.org/c3ref/get_autocommit.html
func (c *Conn) GetAutocommit() bool {
r := c.call(c.api.autocommit, uint64(c.handle))
return r[0] != 0
r := c.call("sqlite3_get_autocommit", uint64(c.handle))
return r != 0
}
// LastInsertRowID returns the rowid of the most recent successful INSERT
// on the database connection.
//
// https://www.sqlite.org/c3ref/last_insert_rowid.html
// https://sqlite.org/c3ref/last_insert_rowid.html
func (c *Conn) LastInsertRowID() int64 {
r := c.call(c.api.lastRowid, uint64(c.handle))
return int64(r[0])
r := c.call("sqlite3_last_insert_rowid", uint64(c.handle))
return int64(r)
}
// SetLastInsertRowID allows the application to set the value returned by
// [Conn.LastInsertRowID].
//
// https://sqlite.org/c3ref/set_last_insert_rowid.html
func (c *Conn) SetLastInsertRowID(id int64) {
c.call("sqlite3_set_last_insert_rowid", uint64(c.handle), uint64(id))
}
// Changes returns the number of rows modified, inserted or deleted
// by the most recently completed INSERT, UPDATE or DELETE statement
// on the database connection.
//
// https://www.sqlite.org/c3ref/changes.html
// https://sqlite.org/c3ref/changes.html
func (c *Conn) Changes() int64 {
r := c.call(c.api.changes, uint64(c.handle))
return int64(r[0])
r := c.call("sqlite3_changes64", uint64(c.handle))
return int64(r)
}
// TotalChanges returns the number of rows modified, inserted or deleted
// by all INSERT, UPDATE or DELETE statements completed
// since the database connection was opened.
//
// https://sqlite.org/c3ref/total_changes.html
func (c *Conn) TotalChanges() int64 {
r := c.call("sqlite3_total_changes64", uint64(c.handle))
return int64(r)
}
// ReleaseMemory frees memory used by a database connection.
//
// https://sqlite.org/c3ref/db_release_memory.html
func (c *Conn) ReleaseMemory() error {
r := c.call("sqlite3_db_release_memory", uint64(c.handle))
return c.error(r)
}
// SetInterrupt interrupts a long-running query when a context is done.
@@ -231,70 +294,81 @@ func (c *Conn) Changes() int64 {
//
// SetInterrupt returns the old context assigned to the connection.
//
// https://www.sqlite.org/c3ref/interrupt.html
// https://sqlite.org/c3ref/interrupt.html
func (c *Conn) SetInterrupt(ctx context.Context) (old context.Context) {
// Is a waiter running?
if c.waiter != nil {
c.waiter <- struct{}{} // Cancel the waiter.
<-c.waiter // Wait for it to finish.
c.waiter = nil
// Is it the same context?
if ctx == c.interrupt {
return ctx
}
// Reset the pending statement.
if c.pending != nil {
c.pending.Reset()
// A busy SQL statement prevents SQLite from ignoring an interrupt
// that comes before any other statements are started.
if c.pending == nil {
c.pending, _, _ = c.Prepare(`WITH RECURSIVE c(x) AS (VALUES(0) UNION ALL SELECT x FROM c) SELECT x FROM c`)
}
old = c.interrupt
c.interrupt = ctx
if ctx == nil || ctx.Done() == nil {
return old
if old != nil && old.Done() != nil && (ctx == nil || ctx.Err() == nil) {
c.pending.Reset()
}
// Creating an uncompleted SQL statement prevents SQLite from ignoring
// an interrupt that comes before any other statements are started.
if c.pending == nil {
c.pending, _, _ = c.Prepare(`SELECT 1 UNION ALL SELECT 2`)
if ctx != nil && ctx.Done() != nil {
c.pending.Step()
}
c.pending.Step()
// Don't create the goroutine if we're already interrupted.
// This happens frequently while restoring to a previously interrupted state.
if c.checkInterrupt() {
return old
}
waiter := make(chan struct{})
c.waiter = waiter
go func() {
select {
case <-waiter: // Waiter was cancelled.
break
case <-ctx.Done(): // Done was closed.
buf := c.mem.view(c.handle+c.api.interrupt, 4)
(*atomic.Uint32)(unsafe.Pointer(&buf[0])).Store(1)
// Wait for the next call to SetInterrupt.
<-waiter
}
// Signal that the waiter has finished.
waiter <- struct{}{}
}()
return old
}
func (c *Conn) checkInterrupt() bool {
if c.interrupt == nil || c.interrupt.Err() == nil {
return false
func (c *Conn) checkInterrupt() {
if c.interrupt != nil && c.interrupt.Err() != nil {
c.call("sqlite3_interrupt", uint64(c.handle))
}
buf := c.mem.view(c.handle+c.api.interrupt, 4)
(*atomic.Uint32)(unsafe.Pointer(&buf[0])).Store(1)
return true
}
// Pragma executes a PRAGMA statement and returns any results.
func progressCallback(ctx context.Context, mod api.Module, pDB uint32) uint32 {
if c, ok := ctx.Value(connKey{}).(*Conn); ok && c.handle == pDB && c.commit != nil {
if c.interrupt != nil && c.interrupt.Err() != nil {
return 1
}
}
return 0
}
// BusyTimeout sets a busy timeout.
//
// https://www.sqlite.org/pragma.html
// https://sqlite.org/c3ref/busy_timeout.html
func (c *Conn) BusyTimeout(timeout time.Duration) error {
ms := min((timeout+time.Millisecond-1)/time.Millisecond, math.MaxInt32)
r := c.call("sqlite3_busy_timeout", uint64(c.handle), uint64(ms))
return c.error(r)
}
// BusyHandler registers a callback to handle [BUSY] errors.
//
// https://sqlite.org/c3ref/busy_handler.html
func (c *Conn) BusyHandler(cb func(count int) (retry bool)) error {
var enable uint64
if cb != nil {
enable = 1
}
r := c.call("sqlite3_busy_handler_go", uint64(c.handle), enable)
if err := c.error(r); err != nil {
return err
}
c.busy = cb
return nil
}
func busyCallback(ctx context.Context, mod api.Module, pDB, count uint32) uint32 {
if c, ok := ctx.Value(connKey{}).(*Conn); ok && c.handle == pDB && c.busy != nil {
if retry := c.busy(int(count)); retry {
return 1
}
}
return 0
}
// Deprecated: executes a PRAGMA statement and returns results.
func (c *Conn) Pragma(str string) ([]string, error) {
stmt, _, err := c.Prepare(`PRAGMA ` + str)
if err != nil {
@@ -310,21 +384,14 @@ func (c *Conn) Pragma(str string) ([]string, error) {
}
func (c *Conn) error(rc uint64, sql ...string) error {
return c.module.error(rc, c.handle, sql...)
return c.sqlite.error(rc, c.handle, sql...)
}
// DriverConn is implemented by the SQLite [database/sql] driver connection.
//
// It can be used to access advanced SQLite features like
// [savepoints] and [incremental BLOB I/O].
// It can be used to access SQLite features like [online backup].
//
// [savepoints]: https://www.sqlite.org/lang_savepoint.html
// [incremental BLOB I/O]: https://www.sqlite.org/c3ref/blob_open.html
// [online backup]: https://sqlite.org/backup.html
type DriverConn interface {
driver.ConnBeginTx
driver.ExecerContext
driver.ConnPrepareContext
Savepoint() Savepoint
OpenBlob(db, table, column string, row int64, write bool) (*Blob, error)
Raw() *Conn
}

223
const.go
View File

@@ -9,17 +9,18 @@ const (
_UTF8 = 1
_MAX_STRING = 512 // Used for short strings: names, error messages…
_MAX_PATHNAME = 512
_MAX_NAME = 1e6 // Self-imposed limit for most NUL terminated strings.
_MAX_LENGTH = 1e9
_MAX_SQL_LENGTH = 1e9
_MAX_ALLOCATION_SIZE = 0x7ffffeff
_MAX_FUNCTION_ARG = 100
ptrlen = 4
)
// ErrorCode is a result code that [Error.Code] might return.
//
// https://www.sqlite.org/rescode.html
// https://sqlite.org/rescode.html
type ErrorCode uint8
const (
@@ -55,7 +56,7 @@ const (
// ExtendedErrorCode is a result code that [Error.ExtendedCode] might return.
//
// https://www.sqlite.org/rescode.html
// https://sqlite.org/rescode.html
type (
ExtendedErrorCode uint16
xErrorCode = ExtendedErrorCode
@@ -98,6 +99,7 @@ const (
IOERR_ROLLBACK_ATOMIC ExtendedErrorCode = xErrorCode(IOERR) | (31 << 8)
IOERR_DATA ExtendedErrorCode = xErrorCode(IOERR) | (32 << 8)
IOERR_CORRUPTFS ExtendedErrorCode = xErrorCode(IOERR) | (33 << 8)
IOERR_IN_PAGE ExtendedErrorCode = xErrorCode(IOERR) | (34 << 8)
LOCKED_SHAREDCACHE ExtendedErrorCode = xErrorCode(LOCKED) | (1 << 8)
LOCKED_VTAB ExtendedErrorCode = xErrorCode(LOCKED) | (2 << 8)
BUSY_RECOVERY ExtendedErrorCode = xErrorCode(BUSY) | (1 << 8)
@@ -138,39 +140,28 @@ const (
AUTH_USER ExtendedErrorCode = xErrorCode(AUTH) | (1 << 8)
)
// OpenFlag is a flag for a file open operation.
// OpenFlag is a flag for the [OpenFlags] function.
//
// https://www.sqlite.org/c3ref/c_open_autoproxy.html
// https://sqlite.org/c3ref/c_open_autoproxy.html
type OpenFlag uint32
const (
OPEN_READONLY OpenFlag = 0x00000001 /* Ok for sqlite3_open_v2() */
OPEN_READWRITE OpenFlag = 0x00000002 /* Ok for sqlite3_open_v2() */
OPEN_CREATE OpenFlag = 0x00000004 /* Ok for sqlite3_open_v2() */
OPEN_DELETEONCLOSE OpenFlag = 0x00000008 /* VFS only */
OPEN_EXCLUSIVE OpenFlag = 0x00000010 /* VFS only */
OPEN_AUTOPROXY OpenFlag = 0x00000020 /* VFS only */
OPEN_URI OpenFlag = 0x00000040 /* Ok for sqlite3_open_v2() */
OPEN_MEMORY OpenFlag = 0x00000080 /* Ok for sqlite3_open_v2() */
OPEN_MAIN_DB OpenFlag = 0x00000100 /* VFS only */
OPEN_TEMP_DB OpenFlag = 0x00000200 /* VFS only */
OPEN_TRANSIENT_DB OpenFlag = 0x00000400 /* VFS only */
OPEN_MAIN_JOURNAL OpenFlag = 0x00000800 /* VFS only */
OPEN_TEMP_JOURNAL OpenFlag = 0x00001000 /* VFS only */
OPEN_SUBJOURNAL OpenFlag = 0x00002000 /* VFS only */
OPEN_SUPER_JOURNAL OpenFlag = 0x00004000 /* VFS only */
OPEN_NOMUTEX OpenFlag = 0x00008000 /* Ok for sqlite3_open_v2() */
OPEN_FULLMUTEX OpenFlag = 0x00010000 /* Ok for sqlite3_open_v2() */
OPEN_SHAREDCACHE OpenFlag = 0x00020000 /* Ok for sqlite3_open_v2() */
OPEN_PRIVATECACHE OpenFlag = 0x00040000 /* Ok for sqlite3_open_v2() */
OPEN_WAL OpenFlag = 0x00080000 /* VFS only */
OPEN_NOFOLLOW OpenFlag = 0x01000000 /* Ok for sqlite3_open_v2() */
OPEN_EXRESCODE OpenFlag = 0x02000000 /* Extended result codes */
OPEN_READONLY OpenFlag = 0x00000001 /* Ok for sqlite3_open_v2() */
OPEN_READWRITE OpenFlag = 0x00000002 /* Ok for sqlite3_open_v2() */
OPEN_CREATE OpenFlag = 0x00000004 /* Ok for sqlite3_open_v2() */
OPEN_URI OpenFlag = 0x00000040 /* Ok for sqlite3_open_v2() */
OPEN_MEMORY OpenFlag = 0x00000080 /* Ok for sqlite3_open_v2() */
OPEN_NOMUTEX OpenFlag = 0x00008000 /* Ok for sqlite3_open_v2() */
OPEN_FULLMUTEX OpenFlag = 0x00010000 /* Ok for sqlite3_open_v2() */
OPEN_SHAREDCACHE OpenFlag = 0x00020000 /* Ok for sqlite3_open_v2() */
OPEN_PRIVATECACHE OpenFlag = 0x00040000 /* Ok for sqlite3_open_v2() */
OPEN_NOFOLLOW OpenFlag = 0x01000000 /* Ok for sqlite3_open_v2() */
OPEN_EXRESCODE OpenFlag = 0x02000000 /* Extended result codes */
)
// PrepareFlag is a flag that can be passed to [Conn.PrepareFlags].
//
// https://www.sqlite.org/c3ref/c_prepare_normalize.html
// https://sqlite.org/c3ref/c_prepare_normalize.html
type PrepareFlag uint32
const (
@@ -179,9 +170,155 @@ const (
PREPARE_NO_VTAB PrepareFlag = 0x04
)
// FunctionFlag is a flag that can be passed to
// [Conn.CreateFunction] and [Conn.CreateWindowFunction].
//
// https://sqlite.org/c3ref/c_deterministic.html
type FunctionFlag uint32
const (
DETERMINISTIC FunctionFlag = 0x000000800
DIRECTONLY FunctionFlag = 0x000080000
SUBTYPE FunctionFlag = 0x000100000
INNOCUOUS FunctionFlag = 0x000200000
RESULT_SUBTYPE FunctionFlag = 0x001000000
)
// StmtStatus name counter values associated with the [Stmt.Status] method.
//
// https://sqlite.org/c3ref/c_stmtstatus_counter.html
type StmtStatus uint32
const (
STMTSTATUS_FULLSCAN_STEP StmtStatus = 1
STMTSTATUS_SORT StmtStatus = 2
STMTSTATUS_AUTOINDEX StmtStatus = 3
STMTSTATUS_VM_STEP StmtStatus = 4
STMTSTATUS_REPREPARE StmtStatus = 5
STMTSTATUS_RUN StmtStatus = 6
STMTSTATUS_FILTER_MISS StmtStatus = 7
STMTSTATUS_FILTER_HIT StmtStatus = 8
STMTSTATUS_MEMUSED StmtStatus = 99
)
// DBConfig are the available database connection configuration options.
//
// https://sqlite.org/c3ref/c_dbconfig_defensive.html
type DBConfig uint32
const (
// DBCONFIG_MAINDBNAME DBConfig = 1000
// DBCONFIG_LOOKASIDE DBConfig = 1001
DBCONFIG_ENABLE_FKEY DBConfig = 1002
DBCONFIG_ENABLE_TRIGGER DBConfig = 1003
DBCONFIG_ENABLE_FTS3_TOKENIZER DBConfig = 1004
DBCONFIG_ENABLE_LOAD_EXTENSION DBConfig = 1005
DBCONFIG_NO_CKPT_ON_CLOSE DBConfig = 1006
DBCONFIG_ENABLE_QPSG DBConfig = 1007
DBCONFIG_TRIGGER_EQP DBConfig = 1008
DBCONFIG_RESET_DATABASE DBConfig = 1009
DBCONFIG_DEFENSIVE DBConfig = 1010
DBCONFIG_WRITABLE_SCHEMA DBConfig = 1011
DBCONFIG_LEGACY_ALTER_TABLE DBConfig = 1012
DBCONFIG_DQS_DML DBConfig = 1013
DBCONFIG_DQS_DDL DBConfig = 1014
DBCONFIG_ENABLE_VIEW DBConfig = 1015
DBCONFIG_LEGACY_FILE_FORMAT DBConfig = 1016
DBCONFIG_TRUSTED_SCHEMA DBConfig = 1017
DBCONFIG_STMT_SCANSTATUS DBConfig = 1018
DBCONFIG_REVERSE_SCANORDER DBConfig = 1019
)
// LimitCategory are the available run-time limit categories.
//
// https://sqlite.org/c3ref/c_limit_attached.html
type LimitCategory uint32
const (
LIMIT_LENGTH LimitCategory = 0
LIMIT_SQL_LENGTH LimitCategory = 1
LIMIT_COLUMN LimitCategory = 2
LIMIT_EXPR_DEPTH LimitCategory = 3
LIMIT_COMPOUND_SELECT LimitCategory = 4
LIMIT_VDBE_OP LimitCategory = 5
LIMIT_FUNCTION_ARG LimitCategory = 6
LIMIT_ATTACHED LimitCategory = 7
LIMIT_LIKE_PATTERN_LENGTH LimitCategory = 8
LIMIT_VARIABLE_NUMBER LimitCategory = 9
LIMIT_TRIGGER_DEPTH LimitCategory = 10
LIMIT_WORKER_THREADS LimitCategory = 11
)
// AuthorizerActionCode are the integer action codes
// that the authorizer callback may be passed.
//
// https://sqlite.org/c3ref/c_alter_table.html
type AuthorizerActionCode uint32
const (
/************************************************ 3rd ************ 4th ***********/
CREATE_INDEX AuthorizerActionCode = 1 /* Index Name Table Name */
CREATE_TABLE AuthorizerActionCode = 2 /* Table Name NULL */
CREATE_TEMP_INDEX AuthorizerActionCode = 3 /* Index Name Table Name */
CREATE_TEMP_TABLE AuthorizerActionCode = 4 /* Table Name NULL */
CREATE_TEMP_TRIGGER AuthorizerActionCode = 5 /* Trigger Name Table Name */
CREATE_TEMP_VIEW AuthorizerActionCode = 6 /* View Name NULL */
CREATE_TRIGGER AuthorizerActionCode = 7 /* Trigger Name Table Name */
CREATE_VIEW AuthorizerActionCode = 8 /* View Name NULL */
DELETE AuthorizerActionCode = 9 /* Table Name NULL */
DROP_INDEX AuthorizerActionCode = 10 /* Index Name Table Name */
DROP_TABLE AuthorizerActionCode = 11 /* Table Name NULL */
DROP_TEMP_INDEX AuthorizerActionCode = 12 /* Index Name Table Name */
DROP_TEMP_TABLE AuthorizerActionCode = 13 /* Table Name NULL */
DROP_TEMP_TRIGGER AuthorizerActionCode = 14 /* Trigger Name Table Name */
DROP_TEMP_VIEW AuthorizerActionCode = 15 /* View Name NULL */
DROP_TRIGGER AuthorizerActionCode = 16 /* Trigger Name Table Name */
DROP_VIEW AuthorizerActionCode = 17 /* View Name NULL */
INSERT AuthorizerActionCode = 18 /* Table Name NULL */
PRAGMA AuthorizerActionCode = 19 /* Pragma Name 1st arg or NULL */
READ AuthorizerActionCode = 20 /* Table Name Column Name */
SELECT AuthorizerActionCode = 21 /* NULL NULL */
TRANSACTION AuthorizerActionCode = 22 /* Operation NULL */
UPDATE AuthorizerActionCode = 23 /* Table Name Column Name */
ATTACH AuthorizerActionCode = 24 /* Filename NULL */
DETACH AuthorizerActionCode = 25 /* Database Name NULL */
ALTER_TABLE AuthorizerActionCode = 26 /* Database Name Table Name */
REINDEX AuthorizerActionCode = 27 /* Index Name NULL */
ANALYZE AuthorizerActionCode = 28 /* Table Name NULL */
CREATE_VTABLE AuthorizerActionCode = 29 /* Table Name Module Name */
DROP_VTABLE AuthorizerActionCode = 30 /* Table Name Module Name */
FUNCTION AuthorizerActionCode = 31 /* NULL Function Name */
SAVEPOINT AuthorizerActionCode = 32 /* Operation Savepoint Name */
COPY AuthorizerActionCode = 0 /* No longer used */
RECURSIVE AuthorizerActionCode = 33 /* NULL NULL */
)
// AuthorizerReturnCode are the integer codes
// that the authorizer callback may return.
//
// https://sqlite.org/c3ref/c_deny.html
type AuthorizerReturnCode uint32
const (
AUTH_OK AuthorizerReturnCode = 0
AUTH_DENY AuthorizerReturnCode = 1 /* Abort the SQL statement with an error */
AUTH_IGNORE AuthorizerReturnCode = 2 /* Don't allow access, but don't generate an error */
)
// TxnState are the allowed return values from [Conn.TxnState].
//
// https://sqlite.org/c3ref/c_txn_none.html
type TxnState uint32
const (
TXN_NONE TxnState = 0
TXN_READ TxnState = 1
TXN_WRITE TxnState = 2
)
// Datatype is a fundamental datatype of SQLite.
//
// https://www.sqlite.org/c3ref/c_blob.html
// https://sqlite.org/c3ref/c_blob.html
type Datatype uint32
const (
@@ -194,34 +331,18 @@ const (
// String implements the [fmt.Stringer] interface.
func (t Datatype) String() string {
const name = "INTEGERFLOATTEXTBLOBNULL"
const name = "INTEGERFLOATEXTBLOBNULL"
switch t {
case INTEGER:
return name[0:7]
case FLOAT:
return name[7:12]
case TEXT:
return name[12:16]
return name[11:15]
case BLOB:
return name[16:20]
return name[15:19]
case NULL:
return name[20:24]
return name[19:23]
}
return strconv.FormatUint(uint64(t), 10)
}
type _AccessFlag uint32
const (
_ACCESS_EXISTS _AccessFlag = 0
_ACCESS_READWRITE _AccessFlag = 1 /* Used by PRAGMA temp_store_directory */
_ACCESS_READ _AccessFlag = 2 /* Unused */
)
type _SyncFlag uint32
const (
_SYNC_NORMAL _SyncFlag = 0x00002
_SYNC_FULL _SyncFlag = 0x00003
_SYNC_DATAONLY _SyncFlag = 0x00010
)

229
context.go Normal file
View File

@@ -0,0 +1,229 @@
package sqlite3
import (
"encoding/json"
"errors"
"math"
"time"
"github.com/ncruces/go-sqlite3/internal/util"
)
// Context is the context in which an SQL function executes.
// An SQLite [Context] is in no way related to a Go [context.Context].
//
// https://sqlite.org/c3ref/context.html
type Context struct {
c *Conn
handle uint32
}
// Conn returns the database connection of the
// [Conn.CreateFunction] or [Conn.CreateWindowFunction]
// routines that originally registered the application defined function.
//
// https://sqlite.org/c3ref/context_db_handle.html
func (ctx Context) Conn() *Conn {
return ctx.c
}
// SetAuxData saves metadata for argument n of the function.
//
// https://sqlite.org/c3ref/get_auxdata.html
func (ctx Context) SetAuxData(n int, data any) {
ptr := util.AddHandle(ctx.c.ctx, data)
ctx.c.call("sqlite3_set_auxdata_go", uint64(ctx.handle), uint64(n), uint64(ptr))
}
// GetAuxData returns metadata for argument n of the function.
//
// https://sqlite.org/c3ref/get_auxdata.html
func (ctx Context) GetAuxData(n int) any {
ptr := uint32(ctx.c.call("sqlite3_get_auxdata", uint64(ctx.handle), uint64(n)))
return util.GetHandle(ctx.c.ctx, ptr)
}
// ResultBool sets the result of the function to a bool.
// SQLite does not have a separate boolean storage class.
// Instead, boolean values are stored as integers 0 (false) and 1 (true).
//
// https://sqlite.org/c3ref/result_blob.html
func (ctx Context) ResultBool(value bool) {
var i int64
if value {
i = 1
}
ctx.ResultInt64(i)
}
// ResultInt sets the result of the function to an int.
//
// https://sqlite.org/c3ref/result_blob.html
func (ctx Context) ResultInt(value int) {
ctx.ResultInt64(int64(value))
}
// ResultInt64 sets the result of the function to an int64.
//
// https://sqlite.org/c3ref/result_blob.html
func (ctx Context) ResultInt64(value int64) {
ctx.c.call("sqlite3_result_int64",
uint64(ctx.handle), uint64(value))
}
// ResultFloat sets the result of the function to a float64.
//
// https://sqlite.org/c3ref/result_blob.html
func (ctx Context) ResultFloat(value float64) {
ctx.c.call("sqlite3_result_double",
uint64(ctx.handle), math.Float64bits(value))
}
// ResultText sets the result of the function to a string.
//
// https://sqlite.org/c3ref/result_blob.html
func (ctx Context) ResultText(value string) {
ptr := ctx.c.newString(value)
ctx.c.call("sqlite3_result_text64",
uint64(ctx.handle), uint64(ptr), uint64(len(value)),
uint64(ctx.c.freer), _UTF8)
}
// ResultRawText sets the text result of the function to a []byte.
//
// https://sqlite.org/c3ref/result_blob.html
func (ctx Context) ResultRawText(value []byte) {
ptr := ctx.c.newBytes(value)
ctx.c.call("sqlite3_result_text64",
uint64(ctx.handle), uint64(ptr), uint64(len(value)),
uint64(ctx.c.freer), _UTF8)
}
// ResultBlob sets the result of the function to a []byte.
// Returning a nil slice is the same as calling [Context.ResultNull].
//
// https://sqlite.org/c3ref/result_blob.html
func (ctx Context) ResultBlob(value []byte) {
ptr := ctx.c.newBytes(value)
ctx.c.call("sqlite3_result_blob64",
uint64(ctx.handle), uint64(ptr), uint64(len(value)),
uint64(ctx.c.freer))
}
// ResultZeroBlob sets the result of the function to a zero-filled, length n BLOB.
//
// https://sqlite.org/c3ref/result_blob.html
func (ctx Context) ResultZeroBlob(n int64) {
ctx.c.call("sqlite3_result_zeroblob64",
uint64(ctx.handle), uint64(n))
}
// ResultNull sets the result of the function to NULL.
//
// https://sqlite.org/c3ref/result_blob.html
func (ctx Context) ResultNull() {
ctx.c.call("sqlite3_result_null",
uint64(ctx.handle))
}
// ResultTime sets the result of the function to a [time.Time].
//
// https://sqlite.org/c3ref/result_blob.html
func (ctx Context) ResultTime(value time.Time, format TimeFormat) {
if format == TimeFormatDefault {
ctx.resultRFC3339Nano(value)
return
}
switch v := format.Encode(value).(type) {
case string:
ctx.ResultText(v)
case int64:
ctx.ResultInt64(v)
case float64:
ctx.ResultFloat(v)
default:
panic(util.AssertErr())
}
}
func (ctx Context) resultRFC3339Nano(value time.Time) {
const maxlen = uint64(len(time.RFC3339Nano)) + 5
ptr := ctx.c.new(maxlen)
buf := util.View(ctx.c.mod, ptr, maxlen)
buf = value.AppendFormat(buf[:0], time.RFC3339Nano)
ctx.c.call("sqlite3_result_text64",
uint64(ctx.handle), uint64(ptr), uint64(len(buf)),
uint64(ctx.c.freer), _UTF8)
}
// ResultPointer sets the result of the function to NULL, just like [Context.ResultNull],
// except that it also associates ptr with that NULL value such that it can be retrieved
// within an application-defined SQL function using [Value.Pointer].
//
// https://sqlite.org/c3ref/result_blob.html
func (ctx Context) ResultPointer(ptr any) {
valPtr := util.AddHandle(ctx.c.ctx, ptr)
ctx.c.call("sqlite3_result_pointer_go", uint64(valPtr))
}
// ResultJSON sets the result of the function to the JSON encoding of value.
//
// https://sqlite.org/c3ref/result_blob.html
func (ctx Context) ResultJSON(value any) {
data, err := json.Marshal(value)
if err != nil {
ctx.ResultError(err)
return
}
ctx.ResultRawText(data)
}
// ResultValue sets the result of the function to a copy of [Value].
//
// https://sqlite.org/c3ref/result_blob.html
func (ctx Context) ResultValue(value Value) {
if value.c != ctx.c {
ctx.ResultError(MISUSE)
return
}
ctx.c.call("sqlite3_result_value",
uint64(ctx.handle), uint64(value.handle))
}
// ResultError sets the result of the function an error.
//
// https://sqlite.org/c3ref/result_blob.html
func (ctx Context) ResultError(err error) {
if errors.Is(err, NOMEM) {
ctx.c.call("sqlite3_result_error_nomem", uint64(ctx.handle))
return
}
if errors.Is(err, TOOBIG) {
ctx.c.call("sqlite3_result_error_toobig", uint64(ctx.handle))
return
}
msg, code := errorCode(err, _OK)
if msg != "" {
defer ctx.c.arena.mark()()
ptr := ctx.c.arena.string(msg)
ctx.c.call("sqlite3_result_error",
uint64(ctx.handle), uint64(ptr), uint64(len(msg)))
}
if code != _OK {
ctx.c.call("sqlite3_result_error_code",
uint64(ctx.handle), uint64(code))
}
}
// VTabNoChange may return true if a column is being fetched as part
// of an update during which the column value will not change.
//
// https://sqlite.org/c3ref/vtab_nochange.html
func (ctx Context) VTabNoChange() bool {
r := ctx.c.call("sqlite3_vtab_nochange", uint64(ctx.handle))
return r != 0
}

View File

@@ -12,235 +12,334 @@
//
// sql.Open("sqlite3", "file:demo.db?_txlock=immediate")
//
// Possible values are: "deferred", "immediate", "exclusive".
// A [read-only] transaction is always "deferred", regardless of "_txlock".
//
// The time encoding/decoding format can be specified using "_timefmt":
//
// sql.Open("sqlite3", "file:demo.db?_timefmt=sqlite")
//
// Possible values are: "auto" (the default), "sqlite", "rfc3339";
// "auto" encodes as RFC 3339 and decodes any [format] supported by SQLite;
// "sqlite" encodes as SQLite and decodes any [format] supported by SQLite;
// "rfc3339" encodes and decodes RFC 3339 only.
//
// [PRAGMA] statements can be specified using "_pragma":
//
// sql.Open("sqlite3", "file:demo.db?_pragma=busy_timeout(10000)&_pragma=locking_mode(normal)")
// sql.Open("sqlite3", "file:demo.db?_pragma=busy_timeout(10000)")
//
// If no PRAGMAs are specifed, a busy timeout of 1 minute
// and normal locking mode are used.
// If no PRAGMAs are specified, a busy timeout of 1 minute is set.
//
// [URI]: https://www.sqlite.org/uri.html
// [PRAGMA]: https://www.sqlite.org/pragma.html
// [TRANSACTION]: https://www.sqlite.org/lang_transaction.html#deferred_immediate_and_exclusive_transactions
// Order matters:
// busy timeout and locking mode should be the first PRAGMAs set, in that order.
//
// [URI]: https://sqlite.org/uri.html
// [PRAGMA]: https://sqlite.org/pragma.html
// [format]: https://sqlite.org/lang_datefunc.html#time_values
// [TRANSACTION]: https://sqlite.org/lang_transaction.html#deferred_immediate_and_exclusive_transactions
// [read-only]: https://pkg.go.dev/database/sql#TxOptions
package driver
import (
"context"
"database/sql"
"database/sql/driver"
"errors"
"fmt"
"io"
"net/url"
"strings"
"time"
"unsafe"
"github.com/ncruces/go-sqlite3"
"github.com/ncruces/go-sqlite3/internal/util"
)
// This variable can be replaced with -ldflags:
//
// go build -ldflags="-X github.com/ncruces/go-sqlite3/driver.driverName=sqlite"
var driverName = "sqlite3"
func init() {
sql.Register("sqlite3", sqlite{})
if driverName != "" {
sql.Register(driverName, sqlite{})
}
}
// Open opens the SQLite database specified by dataSourceName as a [database/sql.DB].
//
// The init function is called by the driver on new connections.
// The conn can be used to execute queries, register functions, etc.
// Any error return closes the conn and passes the error to [database/sql].
func Open(dataSourceName string, init func(*sqlite3.Conn) error) (*sql.DB, error) {
c, err := newConnector(dataSourceName, init)
if err != nil {
return nil, err
}
return sql.OpenDB(c), nil
}
type sqlite struct{}
func (sqlite) Open(name string) (_ driver.Conn, err error) {
c, err := sqlite3.OpenFlags(name, sqlite3.OPEN_READWRITE|sqlite3.OPEN_CREATE|sqlite3.OPEN_URI|sqlite3.OPEN_EXRESCODE)
func (sqlite) Open(name string) (driver.Conn, error) {
c, err := newConnector(name, nil)
if err != nil {
return nil, err
}
return c.Connect(context.Background())
}
var txBegin string
var pragmas []string
func (sqlite) OpenConnector(name string) (driver.Connector, error) {
return newConnector(name, nil)
}
func newConnector(name string, init func(*sqlite3.Conn) error) (*connector, error) {
c := connector{name: name, init: init}
var txlock, timefmt string
if strings.HasPrefix(name, "file:") {
if _, after, ok := strings.Cut(name, "?"); ok {
query, _ := url.ParseQuery(after)
switch s := query.Get("_txlock"); s {
case "":
txBegin = "BEGIN"
case "deferred", "immediate", "exclusive":
txBegin = "BEGIN " + s
default:
c.Close()
return nil, fmt.Errorf("sqlite3: invalid _txlock: %s", s)
query, err := url.ParseQuery(after)
if err != nil {
return nil, err
}
pragmas = query["_pragma"]
txlock = query.Get("_txlock")
timefmt = query.Get("_timefmt")
c.pragmas = query.Has("_pragma")
}
}
if len(pragmas) == 0 {
err := c.Exec(`
PRAGMA busy_timeout=60000;
PRAGMA locking_mode=normal;
`)
switch txlock {
case "":
c.txBegin = "BEGIN"
case "deferred", "immediate", "exclusive":
c.txBegin = "BEGIN " + txlock
default:
return nil, fmt.Errorf("sqlite3: invalid _txlock: %s", txlock)
}
switch timefmt {
case "":
c.tmRead = sqlite3.TimeFormatAuto
c.tmWrite = sqlite3.TimeFormatDefault
case "sqlite":
c.tmRead = sqlite3.TimeFormatAuto
c.tmWrite = sqlite3.TimeFormat3
case "rfc3339":
c.tmRead = sqlite3.TimeFormatDefault
c.tmWrite = sqlite3.TimeFormatDefault
default:
c.tmRead = sqlite3.TimeFormat(timefmt)
c.tmWrite = sqlite3.TimeFormat(timefmt)
}
return &c, nil
}
type connector struct {
init func(*sqlite3.Conn) error
name string
txBegin string
tmRead sqlite3.TimeFormat
tmWrite sqlite3.TimeFormat
pragmas bool
}
func (n *connector) Driver() driver.Driver {
return sqlite{}
}
func (n *connector) Connect(ctx context.Context) (_ driver.Conn, err error) {
c := &conn{
txBegin: n.txBegin,
tmRead: n.tmRead,
tmWrite: n.tmWrite,
}
c.Conn, err = sqlite3.Open(n.name)
if err != nil {
return nil, err
}
defer func() {
if err != nil {
c.Close()
}
}()
old := c.Conn.SetInterrupt(ctx)
defer c.Conn.SetInterrupt(old)
if !n.pragmas {
err = c.Conn.BusyTimeout(60 * time.Second)
if err != nil {
return nil, err
}
}
return conn{
conn: c,
txBegin: txBegin,
}, nil
if n.init != nil {
err = n.init(c.Conn)
if err != nil {
return nil, err
}
}
if n.pragmas || n.init != nil {
s, _, err := c.Conn.Prepare(`PRAGMA query_only`)
if err != nil {
return nil, err
}
if s.Step() && s.ColumnBool(0) {
c.readOnly = '1'
} else {
c.readOnly = '0'
}
err = s.Close()
if err != nil {
return nil, err
}
}
return c, nil
}
type conn struct {
conn *sqlite3.Conn
*sqlite3.Conn
txBegin string
txCommit string
txRollback string
tmRead sqlite3.TimeFormat
tmWrite sqlite3.TimeFormat
readOnly byte
}
var (
// Ensure these interfaces are implemented:
_ driver.ExecerContext = conn{}
_ driver.ConnBeginTx = conn{}
_ driver.Validator = conn{}
_ sqlite3.DriverConn = conn{}
_ driver.ConnPrepareContext = &conn{}
_ driver.ExecerContext = &conn{}
_ driver.ConnBeginTx = &conn{}
_ sqlite3.DriverConn = &conn{}
)
func (c conn) Close() error {
return c.conn.Close()
func (c *conn) Raw() *sqlite3.Conn {
return c.Conn
}
func (c conn) IsValid() (valid bool) {
r, err := c.conn.Pragma("locking_mode")
return err == nil && len(r) == 1 && r[0] == "normal"
}
func (c conn) Begin() (driver.Tx, error) {
func (c *conn) Begin() (driver.Tx, error) {
return c.BeginTx(context.Background(), driver.TxOptions{})
}
func (c conn) BeginTx(_ context.Context, opts driver.TxOptions) (driver.Tx, error) {
func (c *conn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) {
txBegin := c.txBegin
c.txCommit = `COMMIT`
c.txRollback = `ROLLBACK`
if opts.ReadOnly {
query_only, err := c.conn.Pragma("query_only")
if err != nil {
return nil, err
}
txBegin = `
BEGIN deferred;
PRAGMA query_only=on`
c.txCommit = `
c.txRollback = `
ROLLBACK;
PRAGMA query_only=` + query_only[0]
c.txRollback = c.txCommit
PRAGMA query_only=` + string(c.readOnly)
c.txCommit = c.txRollback
}
switch opts.Isolation {
default:
return nil, isolationErr
return nil, util.IsolationErr
case
driver.IsolationLevel(sql.LevelDefault),
driver.IsolationLevel(sql.LevelSerializable):
break
case driver.IsolationLevel(sql.LevelReadUncommitted):
read_uncommitted, err := c.conn.Pragma("read_uncommitted")
if err != nil {
return nil, err
}
txBegin += `; PRAGMA read_uncommitted=on`
c.txCommit += `; PRAGMA read_uncommitted=` + read_uncommitted[0]
c.txRollback += `; PRAGMA read_uncommitted=` + read_uncommitted[0]
}
err := c.conn.Exec(txBegin)
old := c.Conn.SetInterrupt(ctx)
defer c.Conn.SetInterrupt(old)
err := c.Conn.Exec(txBegin)
if err != nil {
return nil, err
}
return c, nil
}
func (c conn) Commit() error {
err := c.conn.Exec(c.txCommit)
if err != nil && !c.conn.GetAutocommit() {
func (c *conn) Commit() error {
err := c.Conn.Exec(c.txCommit)
if err != nil && !c.Conn.GetAutocommit() {
c.Rollback()
}
return err
}
func (c conn) Rollback() error {
return c.conn.Exec(c.txRollback)
func (c *conn) Rollback() error {
err := c.Conn.Exec(c.txRollback)
if errors.Is(err, sqlite3.INTERRUPT) {
old := c.Conn.SetInterrupt(context.Background())
defer c.Conn.SetInterrupt(old)
err = c.Conn.Exec(c.txRollback)
}
return err
}
func (c conn) Prepare(query string) (driver.Stmt, error) {
s, tail, err := c.conn.Prepare(query)
func (c *conn) Prepare(query string) (driver.Stmt, error) {
return c.PrepareContext(context.Background(), query)
}
func (c *conn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) {
old := c.Conn.SetInterrupt(ctx)
defer c.Conn.SetInterrupt(old)
s, tail, err := c.Conn.Prepare(query)
if err != nil {
return nil, err
}
if tail != "" {
// Check if the tail contains any SQL.
st, _, err := c.conn.Prepare(tail)
if err != nil {
s.Close()
return nil, err
}
if st != nil {
s.Close()
st.Close()
return nil, tailErr
}
s.Close()
return nil, util.TailErr
}
return stmt{s, c.conn}, nil
return &stmt{Stmt: s, tmRead: c.tmRead, tmWrite: c.tmWrite}, nil
}
func (c conn) PrepareContext(_ context.Context, query string) (driver.Stmt, error) {
return c.Prepare(query)
}
func (c conn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) {
func (c *conn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) {
if len(args) != 0 {
// Slow path.
return nil, driver.ErrSkip
}
old := c.conn.SetInterrupt(ctx)
defer c.conn.SetInterrupt(old)
if savept, ok := ctx.(*saveptCtx); ok {
// Called from driver.Savepoint.
savept.Savepoint = c.Conn.Savepoint()
return resultRowsAffected(0), nil
}
err := c.conn.Exec(query)
old := c.Conn.SetInterrupt(ctx)
defer c.Conn.SetInterrupt(old)
err := c.Conn.Exec(query)
if err != nil {
return nil, err
}
return result{
c.conn.LastInsertRowID(),
c.conn.Changes(),
}, nil
return newResult(c.Conn), nil
}
func (c conn) Savepoint() sqlite3.Savepoint {
return c.conn.Savepoint()
}
func (c conn) OpenBlob(db, table, column string, row int64, write bool) (*sqlite3.Blob, error) {
return c.conn.OpenBlob(db, table, column, row, write)
func (*conn) CheckNamedValue(arg *driver.NamedValue) error {
return nil
}
type stmt struct {
stmt *sqlite3.Stmt
conn *sqlite3.Conn
*sqlite3.Stmt
tmWrite sqlite3.TimeFormat
tmRead sqlite3.TimeFormat
}
var (
// Ensure these interfaces are implemented:
_ driver.StmtExecContext = stmt{}
_ driver.StmtQueryContext = stmt{}
_ driver.NamedValueChecker = stmt{}
_ driver.StmtExecContext = &stmt{}
_ driver.StmtQueryContext = &stmt{}
_ driver.NamedValueChecker = &stmt{}
)
func (s stmt) Close() error {
return s.stmt.Close()
}
func (s stmt) NumInput() int {
n := s.stmt.BindCount()
func (s *stmt) NumInput() int {
n := s.Stmt.BindCount()
for i := 1; i <= n; i++ {
if s.stmt.BindName(i) != "" {
if s.Stmt.BindName(i) != "" {
return -1
}
}
@@ -248,39 +347,45 @@ func (s stmt) NumInput() int {
}
// Deprecated: use ExecContext instead.
func (s stmt) Exec(args []driver.Value) (driver.Result, error) {
func (s *stmt) Exec(args []driver.Value) (driver.Result, error) {
return s.ExecContext(context.Background(), namedValues(args))
}
// Deprecated: use QueryContext instead.
func (s stmt) Query(args []driver.Value) (driver.Rows, error) {
func (s *stmt) Query(args []driver.Value) (driver.Rows, error) {
return s.QueryContext(context.Background(), namedValues(args))
}
func (s stmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) {
// Use QueryContext to setup bindings.
// No need to close rows: that simply resets the statement, exec does the same.
_, err := s.QueryContext(ctx, args)
func (s *stmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) {
err := s.setupBindings(args)
if err != nil {
return nil, err
}
err = s.stmt.Exec()
old := s.Stmt.Conn().SetInterrupt(ctx)
defer s.Stmt.Conn().SetInterrupt(old)
err = s.Stmt.Exec()
if err != nil {
return nil, err
}
return result{
int64(s.conn.LastInsertRowID()),
int64(s.conn.Changes()),
}, nil
return newResult(s.Stmt.Conn()), nil
}
func (s stmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) {
err := s.stmt.ClearBindings()
func (s *stmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) {
err := s.setupBindings(args)
if err != nil {
return nil, err
}
return &rows{ctx: ctx, stmt: s}, nil
}
func (s *stmt) setupBindings(args []driver.NamedValue) error {
err := s.Stmt.ClearBindings()
if err != nil {
return err
}
var ids [3]int
for _, arg := range args {
@@ -289,7 +394,7 @@ func (s stmt) QueryContext(ctx context.Context, args []driver.NamedValue) (drive
ids = append(ids, arg.Ordinal)
} else {
for _, prefix := range []string{":", "@", "$"} {
if id := s.stmt.BindIndex(prefix + arg.Name); id != 0 {
if id := s.Stmt.BindIndex(prefix + arg.Name); id != 0 {
ids = append(ids, id)
}
}
@@ -298,45 +403,61 @@ func (s stmt) QueryContext(ctx context.Context, args []driver.NamedValue) (drive
for _, id := range ids {
switch a := arg.Value.(type) {
case bool:
err = s.stmt.BindBool(id, a)
err = s.Stmt.BindBool(id, a)
case int:
err = s.stmt.BindInt(id, a)
err = s.Stmt.BindInt(id, a)
case int64:
err = s.stmt.BindInt64(id, a)
err = s.Stmt.BindInt64(id, a)
case float64:
err = s.stmt.BindFloat(id, a)
err = s.Stmt.BindFloat(id, a)
case string:
err = s.stmt.BindText(id, a)
err = s.Stmt.BindText(id, a)
case []byte:
err = s.stmt.BindBlob(id, a)
err = s.Stmt.BindBlob(id, a)
case sqlite3.ZeroBlob:
err = s.stmt.BindZeroBlob(id, int64(a))
err = s.Stmt.BindZeroBlob(id, int64(a))
case time.Time:
err = s.stmt.BindText(id, a.Format(time.RFC3339Nano))
err = s.Stmt.BindTime(id, a, s.tmWrite)
case util.JSON:
err = s.Stmt.BindJSON(id, a.Value)
case util.PointerUnwrap:
err = s.Stmt.BindPointer(id, util.UnwrapPointer(a))
case nil:
err = s.stmt.BindNull(id)
err = s.Stmt.BindNull(id)
default:
panic(assertErr)
panic(util.AssertErr())
}
}
if err != nil {
return nil, err
return err
}
}
return rows{ctx, s.stmt, s.conn}, nil
return nil
}
func (s stmt) CheckNamedValue(arg *driver.NamedValue) error {
func (s *stmt) CheckNamedValue(arg *driver.NamedValue) error {
switch arg.Value.(type) {
case bool, int, int64, float64, string, []byte,
sqlite3.ZeroBlob, time.Time, nil:
time.Time, sqlite3.ZeroBlob,
util.JSON, util.PointerUnwrap,
nil:
return nil
default:
return driver.ErrSkip
}
}
func newResult(c *sqlite3.Conn) driver.Result {
rows := c.Changes()
if rows != 0 {
id := c.LastInsertRowID()
if id != 0 {
return result{id, rows}
}
}
return resultRowsAffected(rows)
}
type result struct{ lastInsertId, rowsAffected int64 }
func (r result) LastInsertId() (int64, error) {
@@ -347,57 +468,104 @@ func (r result) RowsAffected() (int64, error) {
return r.rowsAffected, nil
}
type resultRowsAffected int64
func (r resultRowsAffected) LastInsertId() (int64, error) {
return 0, nil
}
func (r resultRowsAffected) RowsAffected() (int64, error) {
return int64(r), nil
}
type rows struct {
ctx context.Context
stmt *sqlite3.Stmt
conn *sqlite3.Conn
ctx context.Context
*stmt
names []string
types []string
}
func (r rows) Close() error {
return r.stmt.Reset()
func (r *rows) Close() error {
r.Stmt.ClearBindings()
return r.Stmt.Reset()
}
func (r rows) Columns() []string {
count := r.stmt.ColumnCount()
columns := make([]string, count)
for i := range columns {
columns[i] = r.stmt.ColumnName(i)
func (r *rows) Columns() []string {
if r.names == nil {
count := r.Stmt.ColumnCount()
r.names = make([]string, count)
for i := range r.names {
r.names[i] = r.Stmt.ColumnName(i)
}
}
return columns
return r.names
}
func (r rows) Next(dest []driver.Value) error {
old := r.conn.SetInterrupt(r.ctx)
defer r.conn.SetInterrupt(old)
func (r *rows) declType(index int) string {
if r.types == nil {
count := r.Stmt.ColumnCount()
r.types = make([]string, count)
for i := range r.types {
r.types[i] = strings.ToUpper(r.Stmt.ColumnDeclType(i))
}
}
return r.types[index]
}
if !r.stmt.Step() {
if err := r.stmt.Err(); err != nil {
func (r *rows) ColumnTypeDatabaseTypeName(index int) string {
decltype := r.declType(index)
if len := len(decltype); len > 0 && decltype[len-1] == ')' {
if i := strings.LastIndexByte(decltype, '('); i >= 0 {
decltype = decltype[:i]
}
}
return strings.TrimSpace(decltype)
}
func (r *rows) Next(dest []driver.Value) error {
old := r.Stmt.Conn().SetInterrupt(r.ctx)
defer r.Stmt.Conn().SetInterrupt(old)
if !r.Stmt.Step() {
if err := r.Stmt.Err(); err != nil {
return err
}
return io.EOF
}
data := unsafe.Slice((*any)(unsafe.SliceData(dest)), len(dest))
err := r.Stmt.Columns(data)
for i := range dest {
switch r.stmt.ColumnType(i) {
case sqlite3.INTEGER:
dest[i] = r.stmt.ColumnInt64(i)
case sqlite3.FLOAT:
dest[i] = r.stmt.ColumnFloat(i)
case sqlite3.TEXT:
dest[i] = maybeTime(r.stmt.ColumnText(i))
case sqlite3.BLOB:
buf, _ := dest[i].([]byte)
dest[i] = r.stmt.ColumnBlob(i, buf)
case sqlite3.NULL:
if buf, ok := dest[i].([]byte); ok {
dest[i] = buf[0:0]
} else {
dest[i] = nil
if t, ok := r.decodeTime(i, dest[i]); ok {
dest[i] = t
continue
}
if s, ok := dest[i].(string); ok {
t, ok := maybeTime(s)
if ok {
dest[i] = t
}
default:
panic(assertErr)
}
}
return r.stmt.Err()
return err
}
func (r *rows) decodeTime(i int, v any) (_ time.Time, _ bool) {
if r.tmRead == sqlite3.TimeFormatDefault {
return
}
switch r.declType(i) {
case "DATE", "TIME", "DATETIME", "TIMESTAMP":
// maybe
default:
return
}
switch v.(type) {
case int64, float64, string:
// maybe
default:
return
}
t, err := r.tmRead.Decode(v)
return t, err == nil
}

View File

@@ -6,11 +6,14 @@ import (
"database/sql"
"errors"
"math"
"net/url"
"path/filepath"
"testing"
"time"
"github.com/ncruces/go-sqlite3"
_ "github.com/ncruces/go-sqlite3/embed"
"github.com/ncruces/go-sqlite3/internal/util"
)
func Test_Open_dir(t *testing.T) {
@@ -112,13 +115,7 @@ func Test_Open_txLock(t *testing.T) {
func Test_Open_txLock_invalid(t *testing.T) {
t.Parallel()
db, err := sql.Open("sqlite3", "file::memory:?_txlock=xclusive")
if err != nil {
t.Fatal(err)
}
defer db.Close()
_, err = db.Conn(context.TODO())
_, err := sql.Open("sqlite3", "file::memory:?_txlock=xclusive")
if err == nil {
t.Fatal("want error")
}
@@ -142,20 +139,10 @@ func Test_BeginTx(t *testing.T) {
defer db.Close()
_, err = db.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelReadCommitted})
if err.Error() != string(isolationErr) {
if err.Error() != string(util.IsolationErr) {
t.Error("want isolationErr")
}
tx, err := db.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelReadUncommitted})
if err != nil {
t.Fatal(err)
}
err = tx.Rollback()
if err != nil {
t.Fatal(err)
}
tx1, err := db.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
if err != nil {
t.Fatal(err)
@@ -194,12 +181,6 @@ func Test_Prepare(t *testing.T) {
}
defer db.Close()
stmt, err := db.Prepare(`SELECT 1; -- HERE`)
if err != nil {
t.Error(err)
}
defer stmt.Close()
var serr *sqlite3.Error
_, err = db.Prepare(`SELECT`)
if err == nil {
@@ -215,22 +196,18 @@ func Test_Prepare(t *testing.T) {
t.Error("got message:", got)
}
_, err = db.Prepare(`SELECT 1; `)
if err.Error() != string(util.TailErr) {
t.Error("want tailErr")
}
_, err = db.Prepare(`SELECT 1; SELECT`)
if err == nil {
t.Error("want error")
}
if !errors.As(err, &serr) {
t.Fatalf("got %T, want sqlite3.Error", err)
}
if rc := serr.Code(); rc != sqlite3.ERROR {
t.Errorf("got %d, want sqlite3.ERROR", rc)
}
if got := err.Error(); got != `sqlite3: SQL logic error: incomplete input` {
t.Error("got message:", got)
if err.Error() != string(util.TailErr) {
t.Error("want tailErr")
}
_, err = db.Prepare(`SELECT 1; SELECT 2`)
if err.Error() != string(tailErr) {
if err.Error() != string(util.TailErr) {
t.Error("want tailErr")
}
}
@@ -305,10 +282,11 @@ func Test_QueryRow_blob_null(t *testing.T) {
if err != nil {
t.Fatal(err)
}
defer rows.Close()
want := [][]byte{nil, {0xca, 0xfe}, {0xba, 0xbe}, nil}
for i := 0; rows.Next(); i++ {
var buf []byte
var buf sql.RawBytes
err = rows.Scan(&buf)
if err != nil {
t.Fatal(err)
@@ -318,3 +296,39 @@ func Test_QueryRow_blob_null(t *testing.T) {
}
}
}
func Test_time(t *testing.T) {
t.Parallel()
for _, fmt := range []string{"auto", "sqlite", "rfc3339", time.ANSIC} {
t.Run(fmt, func(t *testing.T) {
db, err := sql.Open("sqlite3", "file::memory:?_timefmt="+url.QueryEscape(fmt))
if err != nil {
t.Fatal(err)
}
defer db.Close()
twosday := time.Date(2022, 2, 22, 22, 22, 22, 0, time.UTC)
_, err = db.Exec(`CREATE TABLE IF NOT EXISTS test (at DATETIME)`)
if err != nil {
t.Fatal(err)
}
_, err = db.Exec(`INSERT INTO test VALUES (?)`, twosday)
if err != nil {
t.Fatal(err)
}
var got time.Time
err = db.QueryRow(`SELECT * FROM test`).Scan(&got)
if err != nil {
t.Fatal(err)
}
if !got.Equal(twosday) {
t.Errorf("got: %v", got)
}
})
}
}

View File

@@ -1,11 +0,0 @@
package driver
type errorString string
func (e errorString) Error() string { return string(e) }
const (
assertErr = errorString("sqlite3: assertion failed")
tailErr = errorString("sqlite3: multiple statements")
isolationErr = errorString("sqlite3: unsupported isolation level")
)

65
driver/json_test.go Normal file
View File

@@ -0,0 +1,65 @@
package driver_test
import (
"fmt"
"log"
"github.com/ncruces/go-sqlite3"
"github.com/ncruces/go-sqlite3/driver"
_ "github.com/ncruces/go-sqlite3/embed"
_ "github.com/ncruces/go-sqlite3/vfs/memdb"
)
func Example_json() {
db, err := driver.Open("file:/test.db?vfs=memdb", nil)
if err != nil {
log.Fatal(err)
}
defer db.Close()
_, err = db.Exec(`
CREATE TABLE IF NOT EXISTS orders (
cart_id INTEGER PRIMARY KEY,
user_id INTEGER NOT NULL,
cart TEXT
);
`)
if err != nil {
log.Fatal(err)
}
type CartItem struct {
ItemID string `json:"id"`
Name string `json:"name"`
Quantity int `json:"quantity,omitempty"`
Price int `json:"price,omitempty"`
}
type Cart struct {
Items []CartItem `json:"items"`
}
_, err = db.Exec(`INSERT INTO orders (user_id, cart) VALUES (?, ?)`, 123, sqlite3.JSON(Cart{
[]CartItem{
{ItemID: "111", Name: "T-shirt", Quantity: 1, Price: 250},
{ItemID: "222", Name: "Trousers", Quantity: 1, Price: 600},
},
}))
if err != nil {
log.Fatal(err)
}
var total string
err = db.QueryRow(`
SELECT total(json_each.value -> 'price')
FROM orders, json_each(cart -> 'items')
WHERE cart_id = last_insert_rowid()
`).Scan(&total)
if err != nil {
log.Fatal(err)
}
fmt.Println("total:", total)
// Output:
// total: 850
}

27
driver/savepoint.go Normal file
View File

@@ -0,0 +1,27 @@
package driver
import (
"database/sql"
"time"
"github.com/ncruces/go-sqlite3"
)
// Savepoint establishes a new transaction savepoint.
//
// https://sqlite.org/lang_savepoint.html
func Savepoint(tx *sql.Tx) sqlite3.Savepoint {
var ctx saveptCtx
tx.ExecContext(&ctx, "")
return ctx.Savepoint
}
type saveptCtx struct{ sqlite3.Savepoint }
func (*saveptCtx) Deadline() (deadline time.Time, ok bool) { return }
func (*saveptCtx) Done() <-chan struct{} { return nil }
func (*saveptCtx) Err() error { return nil }
func (*saveptCtx) Value(key any) any { return nil }

88
driver/savepoint_test.go Normal file
View File

@@ -0,0 +1,88 @@
package driver_test
import (
"fmt"
"log"
"github.com/ncruces/go-sqlite3/driver"
_ "github.com/ncruces/go-sqlite3/embed"
_ "github.com/ncruces/go-sqlite3/vfs/memdb"
)
func ExampleSavepoint() {
db, err := driver.Open("file:/test.db?vfs=memdb", nil)
if err != nil {
log.Fatal(err)
}
defer db.Close()
_, err = db.Exec(`CREATE TABLE IF NOT EXISTS users (id INT, name VARCHAR(10))`)
if err != nil {
log.Fatal(err)
}
err = func() error {
tx, err := db.Begin()
if err != nil {
return err
}
defer tx.Rollback()
stmt, err := tx.Prepare(`INSERT INTO users (id, name) VALUES (?, ?)`)
if err != nil {
return err
}
defer stmt.Close()
_, err = stmt.Exec(0, "go")
if err != nil {
return err
}
_, err = stmt.Exec(1, "zig")
if err != nil {
return err
}
savept := driver.Savepoint(tx)
_, err = stmt.Exec(2, "whatever")
if err != nil {
return err
}
err = savept.Rollback()
if err != nil {
return err
}
_, err = stmt.Exec(3, "rust")
if err != nil {
return err
}
return tx.Commit()
}()
if err != nil {
log.Fatal(err)
}
rows, err := db.Query(`SELECT id, name FROM users`)
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
var id, name string
err = rows.Scan(&id, &name)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s %s\n", id, name)
}
// Output:
// 0 go
// 1 zig
// 3 rust
}

View File

@@ -1,7 +1,6 @@
package driver
import (
"database/sql/driver"
"time"
)
@@ -9,23 +8,24 @@ import (
// if it roundtrips back to the same string.
// This way times can be persisted to, and recovered from, the database,
// but if a string is needed, [database/sql] will recover the same string.
func maybeTime(text string) driver.Value {
func maybeTime(text string) (_ time.Time, _ bool) {
// Weed out (some) values that can't possibly be
// [time.RFC3339Nano] timestamps.
if len(text) < len("2006-01-02T15:04:05Z") {
return text
return
}
if len(text) > len(time.RFC3339Nano) {
return text
return
}
if text[4] != '-' || text[10] != 'T' || text[16] != ':' {
return text
return
}
// Slow path.
var buf [len(time.RFC3339Nano)]byte
date, err := time.Parse(time.RFC3339Nano, text)
if err == nil && date.Format(time.RFC3339Nano) == text {
return date
if err == nil && text == string(date.AppendFormat(buf[:0], time.RFC3339Nano)) {
return date, true
}
return text
return
}

View File

@@ -6,7 +6,7 @@ import (
)
// This checks that any string can be recovered as the same string.
func Fuzz_maybeTime_1(f *testing.F) {
func Fuzz_stringOrTime_1(f *testing.F) {
f.Add("")
f.Add(" ")
f.Add("SQLite")
@@ -22,65 +22,53 @@ func Fuzz_maybeTime_1(f *testing.F) {
f.Add("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.")
f.Fuzz(func(t *testing.T, str string) {
value := maybeTime(str)
switch v := value.(type) {
case time.Time:
v, ok := maybeTime(str)
if ok {
// Make sure times round-trip to the same string:
// https://pkg.go.dev/database/sql#Rows.Scan
if v.Format(time.RFC3339Nano) != str {
t.Fatalf("did not round-trip: %q", str)
}
case string:
if v != str {
t.Fatalf("did not round-trip: %q", str)
}
} else {
date, err := time.Parse(time.RFC3339Nano, str)
if err == nil && date.Format(time.RFC3339Nano) == str {
t.Fatalf("would round-trip: %q", str)
}
default:
t.Fatalf("invalid type %T: %q", v, str)
}
})
}
// This checks that any [time.Time] can be recovered as a [time.Time],
// with nanosecond accuracy, and preserving any timezone offset.
func Fuzz_maybeTime_2(f *testing.F) {
f.Add(0, 0)
f.Add(0, 1)
f.Add(0, -1)
f.Add(0, 999_999_999)
f.Add(0, 1_000_000_000)
f.Add(7956915742, 222_222_222) // twosday
f.Add(639095955742, 222_222_222) // twosday, year 22222AD
f.Add(-763421161058, 222_222_222) // twosday, year 22222BC
func Fuzz_stringOrTime_2(f *testing.F) {
f.Add(int64(0), int64(0))
f.Add(int64(0), int64(1))
f.Add(int64(0), int64(-1))
f.Add(int64(0), int64(999_999_999))
f.Add(int64(0), int64(1_000_000_000))
f.Add(int64(7956915742), int64(222_222_222)) // twosday
f.Add(int64(639095955742), int64(222_222_222)) // twosday, year 22222AD
f.Add(int64(-763421161058), int64(222_222_222)) // twosday, year 22222BC
checkTime := func(t *testing.T, date time.Time) {
value := maybeTime(date.Format(time.RFC3339Nano))
switch v := value.(type) {
case time.Time:
checkTime := func(t testing.TB, date time.Time) {
v, ok := maybeTime(date.Format(time.RFC3339Nano))
if ok {
// Make sure times round-trip to the same time:
if !v.Equal(date) {
t.Fatalf("did not round-trip: %v", date)
}
// Make with the same zone offset:
// With the same zone offset:
_, off1 := v.Zone()
_, off2 := date.Zone()
if off1 != off2 {
t.Fatalf("did not round-trip: %v", date)
}
case string:
} else {
t.Fatalf("was not recovered: %v", date)
default:
t.Fatalf("invalid type %T: %v", v, date)
}
}
f.Fuzz(func(t *testing.T, sec, nsec int) {
f.Fuzz(func(t *testing.T, sec, nsec int64) {
// Reduce the search space.
if 1e12 < sec || sec < -1e12 {
// Dates before 29000BC and after 33000AD; I think we're safe.
@@ -91,7 +79,7 @@ func Fuzz_maybeTime_2(f *testing.F) {
return
}
unix := time.Unix(int64(sec), int64(nsec))
unix := time.Unix(sec, nsec)
checkTime(t, unix)
checkTime(t, unix.UTC())
checkTime(t, unix.In(time.FixedZone("", -8*3600)))

View File

@@ -1,75 +0,0 @@
package sqlite3_test
import (
"context"
"database/sql"
"fmt"
"log"
"os"
"github.com/ncruces/go-sqlite3"
_ "github.com/ncruces/go-sqlite3/driver"
_ "github.com/ncruces/go-sqlite3/embed"
)
var db *sql.DB
func ExampleDriverConn() {
var err error
db, err = sql.Open("sqlite3", "demo.db")
if err != nil {
log.Fatal(err)
}
defer os.Remove("demo.db")
defer db.Close()
ctx := context.Background()
conn, err := db.Conn(ctx)
if err != nil {
log.Fatal(err)
}
defer conn.Close()
_, err = conn.ExecContext(ctx, `CREATE TABLE IF NOT EXISTS test (col)`)
if err != nil {
log.Fatal(err)
}
res, err := conn.ExecContext(ctx, `INSERT INTO test VALUES (?)`, sqlite3.ZeroBlob(11))
if err != nil {
log.Fatal(err)
}
id, err := res.LastInsertId()
if err != nil {
log.Fatal(err)
}
err = conn.Raw(func(driverConn any) error {
conn := driverConn.(sqlite3.DriverConn)
savept := conn.Savepoint()
defer savept.Release(&err)
blob, err := conn.OpenBlob("main", "test", "col", id, true)
if err != nil {
return err
}
defer blob.Close()
_, err = fmt.Fprint(blob, "Hello BLOB!")
return err
})
if err != nil {
log.Fatal(err)
}
var msg string
err = conn.QueryRowContext(ctx, `SELECT col FROM test`).Scan(&msg)
if err != nil {
log.Fatal(err)
}
fmt.Println(msg)
// Output:
// Hello BLOB!
}

View File

@@ -1,15 +1,26 @@
# Embeddable WASM build of SQLite
This folder includes an embeddable WASM build of SQLite 3.41.0 for use with
This folder includes an embeddable WASM build of SQLite 3.45.1 for use with
[`github.com/ncruces/go-sqlite3`](https://pkg.go.dev/github.com/ncruces/go-sqlite3).
The following optional features are compiled in:
- math functions
- FTS3/4/5
- JSON
- R*Tree
- GeoPoly
- [math functions](https://sqlite.org/lang_mathfunc.html)
- [FTS5](https://sqlite.org/fts5.html)
- [JSON](https://sqlite.org/json1.html)
- [R*Tree](https://sqlite.org/rtree.html)
- [GeoPoly](https://sqlite.org/geopoly.html)
- [soundex](https://sqlite.org/lang_corefunc.html#soundex)
- [base64](https://github.com/sqlite/sqlite/blob/master/ext/misc/base64.c)
- [decimal](https://github.com/sqlite/sqlite/blob/master/ext/misc/decimal.c)
- [ieee754](https://github.com/sqlite/sqlite/blob/master/ext/misc/ieee754.c)
- [regexp](https://github.com/sqlite/sqlite/blob/master/ext/misc/regexp.c)
- [series](https://github.com/sqlite/sqlite/blob/master/ext/misc/series.c)
- [uint](https://github.com/sqlite/sqlite/blob/master/ext/misc/uint.c)
- [uuid](https://github.com/sqlite/sqlite/blob/master/ext/misc/uuid.c)
- [time](../sqlite3/time.c)
See the [configuration options](../sqlite3/sqlite_cfg.h).
See the [configuration options](../sqlite3/sqlite_cfg.h),
and [patches](../sqlite3) applied.
Built using [`zig`](https://ziglang.org/) version 0.10.1.
Built using [`wasi-sdk`](https://github.com/WebAssembly/wasi-sdk),
and [`binaryen`](https://github.com/WebAssembly/binaryen).

View File

@@ -1,16 +1,31 @@
#!/usr/bin/env bash
set -eo pipefail
set -euo pipefail
cd -P -- "$(dirname -- "$0")"
# download SQLite
../sqlite3/download.sh
ROOT=../
BINARYEN="$ROOT/tools/binaryen-version_117/bin"
WASI_SDK="$ROOT/tools/wasi-sdk-21.0/bin"
# build SQLite
zig cc --target=wasm32-wasi -flto -g0 -Os \
-o sqlite3.wasm ../sqlite3/amalg.c \
-mmutable-globals \
"$WASI_SDK/clang" --target=wasm32-wasi -std=c17 -flto -g0 -O2 \
-Wall -Wextra -Wno-unused-parameter \
-o sqlite3.wasm "$ROOT/sqlite3/main.c" \
-I"$ROOT/sqlite3" \
-mexec-model=reactor \
-msimd128 -mmutable-globals \
-mbulk-memory -mreference-types \
-mnontrapping-fptoint -msign-ext \
-fno-stack-protector -fno-stack-clash-protection \
-Wl,--initial-memory=327680 \
-Wl,--stack-first \
-Wl,--import-undefined \
-D_HAVE_SQLITE_CONFIG_H \
$(awk '{print "-Wl,--export="$0}' exports.txt)
$(awk '{print "-Wl,--export="$0}' exports.txt)
trap 'rm -f sqlite3.tmp' EXIT
"$BINARYEN/wasm-ctor-eval" -g -c _initialize sqlite3.wasm -o sqlite3.tmp
"$BINARYEN/wasm-opt" -g --strip --strip-producers -c -O3 \
sqlite3.tmp -o sqlite3.wasm \
--enable-simd --enable-mutable-globals --enable-multivalue \
--enable-bulk-memory --enable-reference-types \
--enable-nontrapping-float-to-int --enable-sign-ext

View File

@@ -1,50 +1,117 @@
free
malloc
malloc_destructor
sqlite3_errcode
sqlite3_errstr
sqlite3_errmsg
sqlite3_error_offset
sqlite3_open_v2
sqlite3_close
sqlite3_close_v2
sqlite3_prepare_v3
sqlite3_finalize
sqlite3_reset
sqlite3_step
sqlite3_exec
sqlite3_clear_bindings
sqlite3_anycollseq_init
sqlite3_backup_finish
sqlite3_backup_init
sqlite3_backup_pagecount
sqlite3_backup_remaining
sqlite3_backup_step
sqlite3_bind_blob64
sqlite3_bind_double
sqlite3_bind_int64
sqlite3_bind_null
sqlite3_bind_parameter_count
sqlite3_bind_parameter_index
sqlite3_bind_parameter_name
sqlite3_bind_null
sqlite3_bind_int64
sqlite3_bind_double
sqlite3_bind_pointer_go
sqlite3_bind_text64
sqlite3_bind_blob64
sqlite3_bind_value
sqlite3_bind_zeroblob64
sqlite3_column_count
sqlite3_column_name
sqlite3_column_type
sqlite3_column_int64
sqlite3_column_double
sqlite3_column_text
sqlite3_blob_bytes
sqlite3_blob_close
sqlite3_blob_open
sqlite3_blob_read
sqlite3_blob_reopen
sqlite3_blob_write
sqlite3_busy_handler_go
sqlite3_busy_timeout
sqlite3_changes64
sqlite3_clear_bindings
sqlite3_close
sqlite3_close_v2
sqlite3_collation_needed_go
sqlite3_column_blob
sqlite3_column_bytes
sqlite3_blob_open
sqlite3_blob_close
sqlite3_blob_bytes
sqlite3_blob_read
sqlite3_blob_write
sqlite3_blob_reopen
sqlite3_column_count
sqlite3_column_decltype
sqlite3_column_double
sqlite3_column_int64
sqlite3_column_name
sqlite3_column_text
sqlite3_column_type
sqlite3_column_value
sqlite3_columns_go
sqlite3_commit_hook_go
sqlite3_config_log_go
sqlite3_create_aggregate_function_go
sqlite3_create_collation_go
sqlite3_create_function_go
sqlite3_create_module_go
sqlite3_create_window_function_go
sqlite3_db_config
sqlite3_db_name
sqlite3_db_readonly
sqlite3_db_release_memory
sqlite3_declare_vtab
sqlite3_errcode
sqlite3_errmsg
sqlite3_error_offset
sqlite3_errstr
sqlite3_exec
sqlite3_finalize
sqlite3_get_autocommit
sqlite3_get_auxdata
sqlite3_interrupt
sqlite3_last_insert_rowid
sqlite3_changes64
sqlite3_unlock_notify
sqlite3_backup_init
sqlite3_backup_step
sqlite3_backup_finish
sqlite3_backup_remaining
sqlite3_backup_pagecount
sqlite3_time_collation
sqlite3_interrupt_offset
sqlite3_limit
sqlite3_open_v2
sqlite3_overload_function
sqlite3_prepare_v3
sqlite3_progress_handler_go
sqlite3_reset
sqlite3_result_blob64
sqlite3_result_double
sqlite3_result_error
sqlite3_result_error_code
sqlite3_result_error_nomem
sqlite3_result_error_toobig
sqlite3_result_int64
sqlite3_result_null
sqlite3_result_pointer_go
sqlite3_result_text64
sqlite3_result_value
sqlite3_result_zeroblob64
sqlite3_rollback_hook_go
sqlite3_set_authorizer_go
sqlite3_set_auxdata_go
sqlite3_set_last_insert_rowid
sqlite3_step
sqlite3_stmt_busy
sqlite3_stmt_readonly
sqlite3_stmt_status
sqlite3_total_changes64
sqlite3_txn_state
sqlite3_update_hook_go
sqlite3_uri_key
sqlite3_uri_parameter
sqlite3_value_blob
sqlite3_value_bytes
sqlite3_value_double
sqlite3_value_dup
sqlite3_value_free
sqlite3_value_int64
sqlite3_value_nochange
sqlite3_value_numeric_type
sqlite3_value_pointer_go
sqlite3_value_text
sqlite3_value_type
sqlite3_vtab_collation
sqlite3_vtab_config_go
sqlite3_vtab_distinct
sqlite3_vtab_in
sqlite3_vtab_in_first
sqlite3_vtab_in_next
sqlite3_vtab_nochange
sqlite3_vtab_on_conflict
sqlite3_vtab_rhs_value

Binary file not shown.

166
error.go
View File

@@ -1,32 +1,33 @@
package sqlite3
import (
"fmt"
"runtime"
"errors"
"strconv"
"strings"
"github.com/ncruces/go-sqlite3/internal/util"
)
// Error wraps an SQLite Error Code.
//
// https://www.sqlite.org/c3ref/errcode.html
// https://sqlite.org/c3ref/errcode.html
type Error struct {
code uint64
str string
msg string
sql string
code uint64
}
// Code returns the primary error code for this error.
//
// https://www.sqlite.org/rescode.html
// https://sqlite.org/rescode.html
func (e *Error) Code() ErrorCode {
return ErrorCode(e.code)
}
// ExtendedCode returns the extended error code for this error.
//
// https://www.sqlite.org/rescode.html
// https://sqlite.org/rescode.html
func (e *Error) ExtendedCode() ExtendedErrorCode {
return ExtendedErrorCode(e.code)
}
@@ -43,8 +44,7 @@ func (e *Error) Error() string {
}
if e.msg != "" {
b.WriteByte(':')
b.WriteByte(' ')
b.WriteString(": ")
b.WriteString(e.msg)
}
@@ -68,6 +68,19 @@ func (e *Error) Is(err error) bool {
return false
}
// As converts this error to an [ErrorCode] or [ExtendedErrorCode].
func (e *Error) As(err any) bool {
switch c := err.(type) {
case *ErrorCode:
*c = e.Code()
return true
case *ExtendedErrorCode:
*c = e.ExtendedCode()
return true
}
return false
}
// Temporary returns true for [BUSY] errors.
func (e *Error) Temporary() bool {
return e.Code() == BUSY
@@ -85,72 +98,7 @@ func (e *Error) SQL() string {
// Error implements the error interface.
func (e ErrorCode) Error() string {
switch e {
case _OK:
return "sqlite3: not an error"
case _ROW:
return "sqlite3: another row available"
case _DONE:
return "sqlite3: no more rows available"
case ERROR:
return "sqlite3: SQL logic error"
case INTERNAL:
break
case PERM:
return "sqlite3: access permission denied"
case ABORT:
return "sqlite3: query aborted"
case BUSY:
return "sqlite3: database is locked"
case LOCKED:
return "sqlite3: database table is locked"
case NOMEM:
return "sqlite3: out of memory"
case READONLY:
return "sqlite3: attempt to write a readonly database"
case INTERRUPT:
return "sqlite3: interrupted"
case IOERR:
return "sqlite3: disk I/O error"
case CORRUPT:
return "sqlite3: database disk image is malformed"
case NOTFOUND:
return "sqlite3: unknown operation"
case FULL:
return "sqlite3: database or disk is full"
case CANTOPEN:
return "sqlite3: unable to open database file"
case PROTOCOL:
return "sqlite3: locking protocol"
case FORMAT:
break
case SCHEMA:
return "sqlite3: database schema has changed"
case TOOBIG:
return "sqlite3: string or blob too big"
case CONSTRAINT:
return "sqlite3: constraint failed"
case MISMATCH:
return "sqlite3: datatype mismatch"
case MISUSE:
return "sqlite3: bad parameter or other API misuse"
case NOLFS:
break
case AUTH:
return "sqlite3: authorization denied"
case EMPTY:
break
case RANGE:
return "sqlite3: column index out of range"
case NOTADB:
return "sqlite3: file is not a database"
case NOTICE:
return "sqlite3: notification message"
case WARNING:
return "sqlite3: warning message"
}
return "sqlite3: unknown error"
return util.ErrorCodeString(uint32(e))
}
// Temporary returns true for [BUSY] errors.
@@ -160,17 +108,7 @@ func (e ErrorCode) Temporary() bool {
// Error implements the error interface.
func (e ExtendedErrorCode) Error() string {
switch x := ErrorCode(e); {
case e == ABORT_ROLLBACK:
return "sqlite3: abort due to ROLLBACK"
case x < _ROW:
return x.Error()
case e == _ROW:
return "sqlite3: another row available"
case e == _DONE:
return "sqlite3: no more rows available"
}
return "sqlite3: unknown error"
return util.ErrorCodeString(uint32(e))
}
// Is tests whether this error matches a given [ErrorCode].
@@ -179,6 +117,15 @@ func (e ExtendedErrorCode) Is(err error) bool {
return ok && c == ErrorCode(e)
}
// As converts this error to an [ErrorCode].
func (e ExtendedErrorCode) As(err any) bool {
c, ok := err.(*ErrorCode)
if ok {
*c = ErrorCode(e)
}
return ok
}
// Temporary returns true for [BUSY] errors.
func (e ExtendedErrorCode) Temporary() bool {
return ErrorCode(e) == BUSY
@@ -189,36 +136,27 @@ func (e ExtendedErrorCode) Timeout() bool {
return e == BUSY_TIMEOUT
}
type errorString string
func (e errorString) Error() string { return string(e) }
const (
nilErr = errorString("sqlite3: invalid memory address or null pointer dereference")
oomErr = errorString("sqlite3: out of memory")
rangeErr = errorString("sqlite3: index out of range")
noNulErr = errorString("sqlite3: missing NUL terminator")
noGlobalErr = errorString("sqlite3: could not find global: ")
noFuncErr = errorString("sqlite3: could not find function: ")
binaryErr = errorString("sqlite3: no SQLite binary embed/set/loaded")
timeErr = errorString("sqlite3: invalid time value")
notImplErr = errorString("sqlite3: not implemented")
whenceErr = errorString("sqlite3: invalid whence")
offsetErr = errorString("sqlite3: invalid offset")
)
func assertErr() errorString {
msg := "sqlite3: assertion failed"
if _, file, line, ok := runtime.Caller(1); ok {
msg += " (" + file + ":" + strconv.Itoa(line) + ")"
func errorCode(err error, def ErrorCode) (msg string, code uint32) {
switch code := err.(type) {
case nil:
return "", _OK
case ErrorCode:
return "", uint32(code)
case xErrorCode:
return "", uint32(code)
case *Error:
return code.msg, uint32(code.code)
}
return errorString(msg)
}
func finalizer[T any](skip int) func(*T) {
msg := fmt.Sprintf("sqlite3: %T not closed", new(T))
if _, file, line, ok := runtime.Caller(skip + 1); ok && skip >= 0 {
msg += " (" + file + ":" + strconv.Itoa(line) + ")"
var ecode ErrorCode
var xcode xErrorCode
switch {
case errors.As(err, &xcode):
code = uint32(xcode)
case errors.As(err, &ecode):
code = uint32(ecode)
default:
code = uint32(def)
}
return func(*T) { panic(errorString(msg)) }
return err.Error(), code
}

View File

@@ -4,11 +4,13 @@ import (
"errors"
"strings"
"testing"
"github.com/ncruces/go-sqlite3/internal/util"
)
func Test_assertErr(t *testing.T) {
err := assertErr()
if s := err.Error(); !strings.HasPrefix(s, "sqlite3: assertion failed") || !strings.HasSuffix(s, "error_test.go:10)") {
err := util.AssertErr()
if s := err.Error(); !strings.HasPrefix(s, "sqlite3: assertion failed") || !strings.HasSuffix(s, "error_test.go:12)") {
t.Errorf("got %q", s)
}
}
@@ -16,22 +18,36 @@ func Test_assertErr(t *testing.T) {
func TestError(t *testing.T) {
t.Parallel()
err := Error{code: 0x8080}
if rc := err.Code(); rc != 0x80 {
t.Errorf("got %#x, want 0x80", rc)
var ecode ErrorCode
var xcode xErrorCode
err := &Error{code: 0x8080}
if !errors.As(err, &err) {
t.Fatal("want true")
}
if !errors.Is(&err, ErrorCode(0x80)) {
if ecode := err.Code(); ecode != 0x80 {
t.Errorf("got %#x, want 0x80", uint8(ecode))
}
if ok := errors.As(err, &ecode); !ok || ecode != ErrorCode(0x80) {
t.Errorf("got %#x, want 0x80", uint8(ecode))
}
if !errors.Is(err, ErrorCode(0x80)) {
t.Errorf("want true")
}
if rc := err.ExtendedCode(); rc != 0x8080 {
t.Errorf("got %#x, want 0x8080", rc)
if xcode := err.ExtendedCode(); xcode != 0x8080 {
t.Errorf("got %#x, want 0x8080", uint16(xcode))
}
if !errors.Is(&err, ExtendedErrorCode(0x8080)) {
if ok := errors.As(err, &xcode); !ok || xcode != xErrorCode(0x8080) {
t.Errorf("got %#x, want 0x8080", uint16(xcode))
}
if !errors.Is(err, xErrorCode(0x8080)) {
t.Errorf("want true")
}
if s := err.Error(); s != "sqlite3: 32896" {
t.Errorf("got %q", s)
}
if ok := errors.As(err.ExtendedCode(), &ecode); !ok || ecode != ErrorCode(0x80) {
t.Errorf("got %#x, want 0x80", uint8(ecode))
}
if !errors.Is(err.ExtendedCode(), ErrorCode(0x80)) {
t.Errorf("want true")
}
@@ -119,8 +135,8 @@ func Test_ErrorCode_Error(t *testing.T) {
// Test all error codes.
for i := 0; i == int(ErrorCode(i)); i++ {
want := "sqlite3: "
r := db.call(db.api.errstr, uint64(i))
want += db.mem.readString(uint32(r[0]), _MAX_STRING)
r := db.call("sqlite3_errstr", uint64(i))
want += util.ReadString(db.mod, uint32(r), _MAX_NAME)
got := ErrorCode(i).Error()
if got != want {
@@ -141,8 +157,8 @@ func Test_ExtendedErrorCode_Error(t *testing.T) {
// Test all extended error codes.
for i := 0; i == int(ExtendedErrorCode(i)); i++ {
want := "sqlite3: "
r := db.call(db.api.errstr, uint64(i))
want += db.mem.readString(uint32(r[0]), _MAX_STRING)
r := db.call("sqlite3_errstr", uint64(i))
want += util.ReadString(db.mod, uint32(r), _MAX_NAME)
got := ExtendedErrorCode(i).Error()
if got != want {

136
ext/array/array.go Normal file
View File

@@ -0,0 +1,136 @@
// Package array provides the array table-valued SQL function.
//
// https://sqlite.org/carray.html
package array
import (
"fmt"
"reflect"
"github.com/ncruces/go-sqlite3"
"github.com/ncruces/go-sqlite3/internal/util"
)
// Register registers the array single-argument, table-valued SQL function.
// The argument must be bound to a Go slice or array of
// ints, floats, bools, strings or byte slices,
// using [sqlite3.BindPointer] or [sqlite3.Pointer].
func Register(db *sqlite3.Conn) {
sqlite3.CreateModule[array](db, "array", nil,
func(db *sqlite3.Conn, _, _, _ string, _ ...string) (array, error) {
err := db.DeclareVTab(`CREATE TABLE x(value, array HIDDEN)`)
return array{}, err
})
}
type array struct{}
func (array) BestIndex(idx *sqlite3.IndexInfo) error {
for i, cst := range idx.Constraint {
if cst.Column == 1 && cst.Op == sqlite3.INDEX_CONSTRAINT_EQ && cst.Usable {
idx.ConstraintUsage[i] = sqlite3.IndexConstraintUsage{
Omit: true,
ArgvIndex: 1,
}
idx.EstimatedCost = 1
idx.EstimatedRows = 100
return nil
}
}
return sqlite3.CONSTRAINT
}
func (array) Open() (sqlite3.VTabCursor, error) {
return &cursor{}, nil
}
type cursor struct {
array reflect.Value
rowID int
}
func (c *cursor) EOF() bool {
return c.rowID >= c.array.Len()
}
func (c *cursor) Next() error {
c.rowID++
return nil
}
func (c *cursor) RowID() (int64, error) {
return int64(c.rowID), nil
}
func (c *cursor) Column(ctx *sqlite3.Context, n int) error {
if n != 0 {
return nil
}
v := c.array.Index(c.rowID)
k := v.Kind()
if k == reflect.Interface {
if v.IsNil() {
ctx.ResultNull()
return nil
}
v = v.Elem()
k = v.Kind()
}
switch {
case v.CanInt():
ctx.ResultInt64(v.Int())
case v.CanUint():
i64 := int64(v.Uint())
if i64 < 0 {
return fmt.Errorf("array: integer element overflow:%.0w %d", sqlite3.MISMATCH, v.Uint())
}
ctx.ResultInt64(i64)
case v.CanFloat():
ctx.ResultFloat(v.Float())
case k == reflect.Bool:
ctx.ResultBool(v.Bool())
case k == reflect.String:
ctx.ResultText(v.String())
case (k == reflect.Slice || k == reflect.Array && v.CanAddr()) &&
v.Type().Elem().Kind() == reflect.Uint8:
ctx.ResultBlob(v.Bytes())
default:
return fmt.Errorf("array: unsupported element:%.0w %v", sqlite3.MISMATCH, util.ReflectType(v))
}
return nil
}
func (c *cursor) Filter(idxNum int, idxStr string, arg ...sqlite3.Value) error {
array := reflect.ValueOf(arg[0].Pointer())
array, err := indexable(array)
if err != nil {
return err
}
c.array = array
c.rowID = 0
return nil
}
func indexable(v reflect.Value) (reflect.Value, error) {
switch v.Kind() {
case reflect.Slice:
return v, nil
case reflect.Array:
return v, nil
case reflect.Pointer:
if v := v.Elem(); v.Kind() == reflect.Array {
return v, nil
}
}
return v, fmt.Errorf("array: unsupported argument:%.0w %v", sqlite3.MISMATCH, util.ReflectType(v))
}

156
ext/array/array_test.go Normal file
View File

@@ -0,0 +1,156 @@
package array_test
import (
"fmt"
"log"
"math"
"reflect"
"testing"
"github.com/ncruces/go-sqlite3"
"github.com/ncruces/go-sqlite3/driver"
_ "github.com/ncruces/go-sqlite3/embed"
"github.com/ncruces/go-sqlite3/ext/array"
)
func Example_driver() {
db, err := driver.Open(":memory:", func(c *sqlite3.Conn) error {
array.Register(c)
return nil
})
if err != nil {
log.Fatal(err)
}
defer db.Close()
rows, err := db.Query(`
SELECT name
FROM pragma_function_list
WHERE name like 'geopoly%' AND narg IN array(?)`,
sqlite3.Pointer([]int{2, 3, 4}))
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
var name string
err := rows.Scan(&name)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s\n", name)
}
if err := rows.Err(); err != nil {
log.Fatal(err)
}
// Unordered output:
// geopoly_regular
// geopoly_overlap
// geopoly_contains_point
// geopoly_within
}
func Example() {
db, err := sqlite3.Open(":memory:")
if err != nil {
log.Fatal(err)
}
defer db.Close()
array.Register(db)
stmt, _, err := db.Prepare(`
SELECT name
FROM pragma_function_list
WHERE name like 'geopoly%' AND narg IN array(?)`)
if err != nil {
log.Fatal(err)
}
defer stmt.Close()
err = stmt.BindPointer(1, [...]int{2, 3, 4})
if err != nil {
log.Fatal(err)
}
for stmt.Step() {
fmt.Printf("%s\n", stmt.ColumnText(0))
}
if err := stmt.Err(); err != nil {
log.Fatal(err)
}
// Unordered output:
// geopoly_regular
// geopoly_overlap
// geopoly_contains_point
// geopoly_within
}
func Test_cursor_Column(t *testing.T) {
t.Parallel()
db, err := driver.Open(":memory:", func(c *sqlite3.Conn) error {
array.Register(c)
return nil
})
if err != nil {
t.Fatal(err)
}
defer db.Close()
rows, err := db.Query(`
SELECT rowid, value FROM array(?)`,
sqlite3.Pointer(&[...]any{nil, true, 1, uint(2), math.Pi, "text", []byte{1, 2, 3}}))
if err != nil {
t.Fatal(err)
}
defer rows.Close()
want := []string{"nil", "int64", "int64", "int64", "float64", "string", "[]uint8"}
for rows.Next() {
var id, val any
err := rows.Scan(&id, &val)
if err != nil {
t.Fatal(err)
}
if want := want[0]; val == nil {
if want != "nil" {
t.Errorf("got nil, want %s", want)
}
} else if got := reflect.TypeOf(val).String(); got != want {
t.Errorf("got %s, want %s", got, want)
}
want = want[1:]
}
if err := rows.Err(); err != nil {
log.Fatal(err)
}
}
func Test_array_errors(t *testing.T) {
t.Parallel()
db, err := sqlite3.Open(":memory:")
if err != nil {
t.Fatal(err)
}
defer db.Close()
array.Register(db)
err = db.Exec(`SELECT * FROM array()`)
if err == nil {
t.Fatal("want error")
} else {
t.Log(err)
}
err = db.Exec(`SELECT * FROM array(?)`)
if err == nil {
t.Fatal("want error")
} else {
t.Log(err)
}
}

139
ext/blobio/blob.go Normal file
View File

@@ -0,0 +1,139 @@
// Package blobio provides an SQL interface to incremental BLOB I/O.
package blobio
import (
"errors"
"io"
"github.com/ncruces/go-sqlite3"
"github.com/ncruces/go-sqlite3/internal/util"
)
// Register registers the SQL functions:
//
// readblob(schema, table, column, rowid, offset, n)
//
// Reads n bytes of a blob, starting at offset.
//
// writeblob(schema, table, column, rowid, offset, data)
//
// Writes data into a blob, at the given offset.
//
// openblob(schema, table, column, rowid, write, callback, args...)
//
// Opens blobs for reading or writing.
// The callback is invoked for each open blob,
// and must be bound to an [OpenCallback],
// using [sqlite3.BindPointer] or [sqlite3.Pointer].
// The optional args will be passed to the callback,
// along with the [sqlite3.Blob] handle.
//
// https://sqlite.org/c3ref/blob.html
func Register(db *sqlite3.Conn) {
db.CreateFunction("readblob", 6, 0, readblob)
db.CreateFunction("writeblob", 6, 0, writeblob)
db.CreateFunction("openblob", -1, 0, openblob)
}
// OpenCallback is the type for the openblob callback.
type OpenCallback func(*sqlite3.Blob, ...sqlite3.Value) error
func readblob(ctx sqlite3.Context, arg ...sqlite3.Value) {
blob, err := getAuxBlob(ctx, arg, false)
if err != nil {
ctx.ResultError(err)
return
}
_, err = blob.Seek(arg[4].Int64(), io.SeekStart)
if err != nil {
ctx.ResultError(err)
return
}
n := arg[5].Int64()
if n <= 0 {
return
}
buf := make([]byte, n)
_, err = io.ReadFull(blob, buf)
if err != nil {
ctx.ResultError(err)
return
}
ctx.ResultBlob(buf)
setAuxBlob(ctx, blob, false)
}
func writeblob(ctx sqlite3.Context, arg ...sqlite3.Value) {
blob, err := getAuxBlob(ctx, arg, true)
if err != nil {
ctx.ResultError(err)
return
}
_, err = blob.Seek(arg[4].Int64(), io.SeekStart)
if err != nil {
ctx.ResultError(err)
return
}
_, err = blob.Write(arg[5].RawBlob())
if err != nil {
ctx.ResultError(err)
return
}
setAuxBlob(ctx, blob, false)
}
func openblob(ctx sqlite3.Context, arg ...sqlite3.Value) {
if len(arg) < 6 {
ctx.ResultError(util.ErrorString("openblob: wrong number of arguments"))
return
}
blob, err := getAuxBlob(ctx, arg, arg[4].Bool())
if err != nil {
ctx.ResultError(err)
return
}
fn := arg[5].Pointer().(OpenCallback)
err = fn(blob, arg[6:]...)
if err != nil {
ctx.ResultError(err)
return
}
setAuxBlob(ctx, blob, true)
}
func getAuxBlob(ctx sqlite3.Context, arg []sqlite3.Value, write bool) (*sqlite3.Blob, error) {
row := arg[3].Int64()
if blob, ok := ctx.GetAuxData(0).(*sqlite3.Blob); ok {
if err := blob.Reopen(row); errors.Is(err, sqlite3.MISUSE) {
// Blob was closed (db, table, column or write changed).
} else {
return blob, err
}
}
db := arg[0].Text()
table := arg[1].Text()
column := arg[2].Text()
return ctx.Conn().OpenBlob(db, table, column, row, write)
}
func setAuxBlob(ctx sqlite3.Context, blob *sqlite3.Blob, open bool) {
// This ensures the blob is closed if db, table, column or write change.
ctx.SetAuxData(0, blob) // db
ctx.SetAuxData(1, blob) // table
ctx.SetAuxData(2, blob) // column
if open {
ctx.SetAuxData(4, blob) // write
}
}

184
ext/blobio/blob_test.go Normal file
View File

@@ -0,0 +1,184 @@
package blobio_test
import (
"io"
"log"
"os"
"reflect"
"testing"
"github.com/ncruces/go-sqlite3"
"github.com/ncruces/go-sqlite3/driver"
_ "github.com/ncruces/go-sqlite3/embed"
"github.com/ncruces/go-sqlite3/ext/array"
"github.com/ncruces/go-sqlite3/ext/blobio"
_ "github.com/ncruces/go-sqlite3/vfs/memdb"
)
func Example() {
// Open the database, registering the extension.
db, err := driver.Open("file:/test.db?vfs=memdb", func(conn *sqlite3.Conn) error {
blobio.Register(conn)
return nil
})
if err != nil {
log.Fatal(err)
}
defer db.Close()
_, err = db.Exec(`CREATE TABLE IF NOT EXISTS test (col)`)
if err != nil {
log.Fatal(err)
}
const message = "Hello BLOB!"
// Create the BLOB.
_, err = db.Exec(`INSERT INTO test VALUES (?)`, sqlite3.ZeroBlob(len(message)))
if err != nil {
log.Fatal(err)
}
// Write the BLOB.
_, err = db.Exec(`SELECT writeblob('main', 'test', 'col', last_insert_rowid(), 0, ?)`, message)
if err != nil {
log.Fatal(err)
}
// Read the BLOB.
_, err = db.Exec(`SELECT openblob('main', 'test', 'col', rowid, false, ?) FROM test`,
sqlite3.Pointer[blobio.OpenCallback](func(blob *sqlite3.Blob, _ ...sqlite3.Value) error {
_, err = io.Copy(os.Stdout, blob)
return err
}))
if err != nil {
log.Fatal(err)
}
// Output:
// Hello BLOB!
}
func Test_readblob(t *testing.T) {
t.Parallel()
db, err := sqlite3.Open(":memory:")
if err != nil {
t.Fatal(err)
}
defer db.Close()
blobio.Register(db)
array.Register(db)
err = db.Exec(`SELECT readblob()`)
if err == nil {
t.Fatal("want error")
} else {
t.Log(err)
}
err = db.Exec(`
CREATE TABLE IF NOT EXISTS test1 (col);
CREATE TABLE IF NOT EXISTS test2 (col);
INSERT INTO test1 VALUES (x'cafe');
INSERT INTO test2 VALUES (x'babe');
`)
if err != nil {
t.Fatal(err)
}
stmt, _, err := db.Prepare(`SELECT readblob('main', value, 'col', 1, 1, 1) FROM array(?)`)
if err != nil {
t.Fatal(err)
}
defer stmt.Close()
err = stmt.BindPointer(1, []string{"test1", "test2"})
if err != nil {
t.Fatal(err)
}
if stmt.Step() {
got := stmt.ColumnText(0)
if got != "\xfe" {
t.Errorf("got %q", got)
}
}
if stmt.Step() {
got := stmt.ColumnText(0)
if got != "\xbe" {
t.Errorf("got %q", got)
}
}
err = stmt.Err()
if err != nil {
t.Fatal(err)
}
}
func Test_openblob(t *testing.T) {
t.Parallel()
db, err := sqlite3.Open(":memory:")
if err != nil {
t.Fatal(err)
}
defer db.Close()
blobio.Register(db)
array.Register(db)
err = db.Exec(`SELECT openblob()`)
if err == nil {
t.Fatal("want error")
} else {
t.Log(err)
}
err = db.Exec(`
CREATE TABLE IF NOT EXISTS test1 (col);
CREATE TABLE IF NOT EXISTS test2 (col);
INSERT INTO test1 VALUES (x'cafe');
INSERT INTO test2 VALUES (x'babe');
`)
if err != nil {
t.Fatal(err)
}
stmt, _, err := db.Prepare(`SELECT openblob('main', value, 'col', 1, false, ?) FROM array(?)`)
if err != nil {
t.Fatal(err)
}
defer stmt.Close()
var got []string
err = stmt.BindPointer(1, blobio.OpenCallback(func(b *sqlite3.Blob, _ ...sqlite3.Value) error {
d, err := io.ReadAll(b)
if err != nil {
return err
}
got = append(got, string(d))
return nil
}))
if err != nil {
t.Fatal(err)
}
err = stmt.BindPointer(2, []string{"test1", "test2"})
if err != nil {
t.Fatal(err)
}
err = stmt.Exec()
if err != nil {
t.Fatal(err)
}
want := []string{"\xca\xfe", "\xba\xbe"}
if !reflect.DeepEqual(got, want) {
t.Errorf("got %v, want %v", got, want)
}
}

36
ext/csv/arg.go Normal file
View File

@@ -0,0 +1,36 @@
package csv
import (
"fmt"
"strconv"
"github.com/ncruces/go-sqlite3/internal/util"
"github.com/ncruces/go-sqlite3/util/vtabutil"
)
func uintArg(key, val string) (int, error) {
i, err := strconv.ParseUint(val, 10, 15)
if err != nil {
return 0, fmt.Errorf("csv: invalid %q parameter: %s", key, val)
}
return int(i), nil
}
func boolArg(key, val string) (bool, error) {
if val == "" {
return true, nil
}
b, ok := util.ParseBool(val)
if ok {
return b, nil
}
return false, fmt.Errorf("csv: invalid %q parameter: %s", key, val)
}
func runeArg(key, val string) (rune, error) {
r, _, tail, err := strconv.UnquoteChar(vtabutil.Unquote(val), 0)
if tail != "" || err != nil {
return 0, fmt.Errorf("csv: invalid %q parameter: %s", key, val)
}
return r, nil
}

108
ext/csv/arg_test.go Normal file
View File

@@ -0,0 +1,108 @@
package csv
import (
"testing"
"github.com/ncruces/go-sqlite3/util/vtabutil"
)
func Test_uintArg(t *testing.T) {
t.Parallel()
tests := []struct {
arg string
key string
val int
err bool
}{
{"columns 1", "columns 1", 0, true},
{"columns = 1", "columns", 1, false},
{"columns\t= 2", "columns", 2, false},
{" columns = 3", "columns", 3, false},
{" columns = -1", "columns", 0, true},
{" columns = 32768", "columns", 0, true},
}
for _, tt := range tests {
t.Run(tt.arg, func(t *testing.T) {
key, val := vtabutil.NamedArg(tt.arg)
if key != tt.key {
t.Errorf("NamedArg() %v, want err %v", key, tt.key)
}
got, err := uintArg(key, val)
if (err != nil) != tt.err {
t.Fatalf("uintArg() error = %v, want err %v", err, tt.err)
}
if got != tt.val {
t.Errorf("uintArg() = %v, want %v", got, tt.val)
}
})
}
}
func Test_boolArg(t *testing.T) {
tests := []struct {
arg string
key string
val bool
err bool
}{
{"header", "header", true, false},
{"header\t= 1", "header", true, false},
{" header = 0", "header", false, false},
{" header = TrUe", "header", true, false},
{" header = FaLsE", "header", false, false},
{" header = Yes", "header", true, false},
{" header = nO", "header", false, false},
{" header = On", "header", true, false},
{" header = Off", "header", false, false},
{" header = T", "header", false, true},
{" header = f", "header", false, true},
}
for _, tt := range tests {
t.Run(tt.arg, func(t *testing.T) {
key, val := vtabutil.NamedArg(tt.arg)
if key != tt.key {
t.Errorf("NamedArg() %v, want err %v", key, tt.key)
}
got, err := boolArg(key, val)
if (err != nil) != tt.err {
t.Fatalf("boolArg() error = %v, want err %v", err, tt.err)
}
if got != tt.val {
t.Errorf("boolArg() = %v, want %v", got, tt.val)
}
})
}
}
func Test_runeArg(t *testing.T) {
tests := []struct {
arg string
key string
val rune
err bool
}{
{"comma", "comma", 0, true},
{"comma\t= ,", "comma", ',', false},
{" comma = ;", "comma", ';', false},
{" comma = ;;", "comma", 0, true},
{` comma = '\t`, "comma", 0, true},
{` comma = '\t'`, "comma", '\t', false},
{` comma = "\t"`, "comma", '\t', false},
}
for _, tt := range tests {
t.Run(tt.arg, func(t *testing.T) {
key, val := vtabutil.NamedArg(tt.arg)
if key != tt.key {
t.Errorf("NamedArg() %v, want err %v", key, tt.key)
}
got, err := runeArg(key, val)
if (err != nil) != tt.err {
t.Fatalf("runeArg() error = %v, want err %v", err, tt.err)
}
if got != tt.val {
t.Errorf("runeArg() = %v, want %v", got, tt.val)
}
})
}
}

232
ext/csv/csv.go Normal file
View File

@@ -0,0 +1,232 @@
// Package csv provides a CSV virtual table.
//
// The CSV virtual table reads RFC 4180 formatted comma-separated values,
// and returns that content as if it were rows and columns of an SQL table.
//
// https://sqlite.org/csv.html
package csv
import (
"bufio"
"encoding/csv"
"fmt"
"io"
"io/fs"
"strings"
"github.com/ncruces/go-sqlite3"
"github.com/ncruces/go-sqlite3/util/osutil"
"github.com/ncruces/go-sqlite3/util/vtabutil"
)
// Register registers the CSV virtual table.
// If a filename is specified, [os.Open] is used to open the file.
func Register(db *sqlite3.Conn) {
RegisterFS(db, osutil.FS{})
}
// RegisterFS registers the CSV virtual table.
// If a filename is specified, fsys is used to open the file.
func RegisterFS(db *sqlite3.Conn, fsys fs.FS) {
declare := func(db *sqlite3.Conn, _, _, _ string, arg ...string) (_ *table, err error) {
var (
filename string
data string
schema string
header bool
columns int = -1
comma rune = ','
done = map[string]struct{}{}
)
for _, arg := range arg {
key, val := vtabutil.NamedArg(arg)
if _, ok := done[key]; ok {
return nil, fmt.Errorf("csv: more than one %q parameter", key)
}
switch key {
case "filename":
filename = vtabutil.Unquote(val)
case "data":
data = vtabutil.Unquote(val)
case "schema":
schema = vtabutil.Unquote(val)
case "header":
header, err = boolArg(key, val)
case "columns":
columns, err = uintArg(key, val)
case "comma":
comma, err = runeArg(key, val)
default:
return nil, fmt.Errorf("csv: unknown %q parameter", key)
}
if err != nil {
return nil, err
}
done[key] = struct{}{}
}
if (filename == "") == (data == "") {
return nil, fmt.Errorf(`csv: must specify either "filename" or "data" but not both`)
}
table := &table{
fsys: fsys,
name: filename,
data: data,
comma: comma,
header: header,
}
if schema == "" {
var row []string
if header || columns < 0 {
csv, c, err := table.newReader()
defer c.Close()
if err != nil {
return nil, err
}
row, err = csv.Read()
if err != nil {
return nil, err
}
}
schema = getSchema(header, columns, row)
}
err = db.DeclareVTab(schema)
if err != nil {
return nil, err
}
err = db.VTabConfig(sqlite3.VTAB_DIRECTONLY)
if err != nil {
return nil, err
}
return table, nil
}
sqlite3.CreateModule(db, "csv", declare, declare)
}
type table struct {
fsys fs.FS
name string
data string
comma rune
header bool
}
func (t *table) BestIndex(idx *sqlite3.IndexInfo) error {
idx.EstimatedCost = 1e6
return nil
}
func (t *table) Open() (sqlite3.VTabCursor, error) {
return &cursor{table: t}, nil
}
func (t *table) Rename(new string) error {
return nil
}
func (t *table) Integrity(schema, table string, flags int) error {
if flags&1 != 0 {
return nil
}
csv, c, err := t.newReader()
if err != nil {
return err
}
defer c.Close()
_, err = csv.ReadAll()
return err
}
func (t *table) newReader() (*csv.Reader, io.Closer, error) {
var r io.Reader
var c io.Closer
if t.name != "" {
f, err := t.fsys.Open(t.name)
if err != nil {
return nil, f, err
}
buf := bufio.NewReader(f)
bom, err := buf.Peek(3)
if err != nil {
return nil, f, err
}
if string(bom) == "\xEF\xBB\xBF" {
buf.Discard(3)
}
r = buf
c = f
} else {
r = strings.NewReader(t.data)
c = io.NopCloser(r)
}
csv := csv.NewReader(r)
csv.ReuseRecord = true
csv.Comma = t.comma
return csv, c, nil
}
type cursor struct {
table *table
closer io.Closer
csv *csv.Reader
row []string
rowID int64
}
func (c *cursor) Close() (err error) {
if c.closer != nil {
err = c.closer.Close()
c.closer = nil
}
return err
}
func (c *cursor) Filter(idxNum int, idxStr string, arg ...sqlite3.Value) error {
err := c.Close()
if err != nil {
return err
}
c.csv, c.closer, err = c.table.newReader()
if err != nil {
return err
}
if c.table.header {
c.Next() // skip header
}
c.rowID = 0
return c.Next()
}
func (c *cursor) Next() (err error) {
c.rowID++
c.row, err = c.csv.Read()
if err != io.EOF {
return err
}
return nil
}
func (c *cursor) EOF() bool {
return c.row == nil
}
func (c *cursor) RowID() (int64, error) {
return c.rowID, nil
}
func (c *cursor) Column(ctx *sqlite3.Context, col int) error {
if col < len(c.row) {
ctx.ResultText(c.row[col])
}
return nil
}

160
ext/csv/csv_test.go Normal file
View File

@@ -0,0 +1,160 @@
package csv_test
import (
"fmt"
"log"
"testing"
"github.com/ncruces/go-sqlite3"
_ "github.com/ncruces/go-sqlite3/embed"
"github.com/ncruces/go-sqlite3/ext/csv"
)
func Example() {
db, err := sqlite3.Open(":memory:")
if err != nil {
log.Fatal(err)
}
defer db.Close()
csv.Register(db)
err = db.Exec(`
CREATE VIRTUAL TABLE IF NOT EXISTS eurofxref USING csv(
filename = 'testdata/eurofxref.csv',
header = YES,
columns = 42,
)`)
if err != nil {
log.Fatal(err)
}
stmt, _, err := db.Prepare(`SELECT USD FROM eurofxref WHERE Date = '2022-02-22'`)
if err != nil {
log.Fatal(err)
}
defer stmt.Close()
if stmt.Step() {
fmt.Printf("On Twosday, 1€ = $%g", stmt.ColumnFloat(0))
}
if err := stmt.Reset(); err != nil {
log.Fatal(err)
}
err = db.Exec(`DROP TABLE eurofxref`)
if err != nil {
log.Fatal(err)
}
// Output:
// On Twosday, 1€ = $1.1342
}
func TestRegister(t *testing.T) {
t.Parallel()
db, err := sqlite3.Open(":memory:")
if err != nil {
t.Fatal(err)
}
defer db.Close()
csv.Register(db)
const data = `
"Rob" "Pike" rob
"Ken" Thompson ken
Robert "Griesemer" "gri"`
err = db.Exec(`
CREATE VIRTUAL TABLE temp.users USING csv(
data = ` + sqlite3.Quote(data) + `,
schema = 'CREATE TABLE x(first_name, last_name, username)',
comma = '\t'
)`)
if err != nil {
t.Fatal(err)
}
stmt, _, err := db.Prepare(`SELECT * FROM temp.users WHERE rowid = 1 ORDER BY username`)
if err != nil {
t.Fatal(err)
}
defer stmt.Close()
if !stmt.Step() {
t.Fatal("no rows")
}
if got := stmt.ColumnText(0); got != "Rob" {
t.Errorf("got %q want Rob", got)
}
if stmt.Step() {
t.Fatal("more rows")
}
err = db.Exec(`ALTER TABLE temp.users RENAME TO csv`)
if err != nil {
t.Fatal(err)
}
err = db.Exec(`PRAGMA integrity_check`)
if err != nil {
t.Error(err)
}
err = db.Exec(`PRAGMA quick_check`)
if err != nil {
t.Error(err)
}
err = db.Exec(`DROP TABLE temp.csv`)
if err != nil {
t.Error(err)
}
}
func TestRegister_errors(t *testing.T) {
t.Parallel()
db, err := sqlite3.Open(":memory:")
if err != nil {
t.Fatal(err)
}
defer db.Close()
csv.Register(db)
err = db.Exec(`CREATE VIRTUAL TABLE temp.users USING csv()`)
if err == nil {
t.Fatal("want error")
} else {
t.Log(err)
}
err = db.Exec(`CREATE VIRTUAL TABLE temp.users USING csv(data='abc', data='abc')`)
if err == nil {
t.Fatal("want error")
} else {
t.Log(err)
}
err = db.Exec(`CREATE VIRTUAL TABLE temp.users USING csv(data='abc', xpto='abc')`)
if err == nil {
t.Fatal("want error")
} else {
t.Log(err)
}
err = db.Exec(`CREATE VIRTUAL TABLE temp.users USING csv(data='abc', comma='"')`)
if err == nil {
t.Fatal("want error")
} else {
t.Log(err)
}
err = db.Exec(`CREATE VIRTUAL TABLE temp.users USING csv(data='abc', header=tru)`)
if err == nil {
t.Fatal("want error")
} else {
t.Log(err)
}
}

39
ext/csv/schema.go Normal file
View File

@@ -0,0 +1,39 @@
package csv
import (
"strconv"
"strings"
"github.com/ncruces/go-sqlite3"
)
func getSchema(header bool, columns int, row []string) string {
var sep string
var str strings.Builder
str.WriteString("CREATE TABLE x(")
if 0 <= columns && columns < len(row) {
row = row[:columns]
}
for i, f := range row {
str.WriteString(sep)
if header && f != "" {
str.WriteString(sqlite3.QuoteIdentifier(f))
} else {
str.WriteString("c")
str.WriteString(strconv.Itoa(i + 1))
}
str.WriteString(" TEXT")
sep = ","
}
for i := len(row); i < columns; i++ {
str.WriteString(sep)
str.WriteString("c")
str.WriteString(strconv.Itoa(i + 1))
str.WriteString(" TEXT")
sep = ","
}
str.WriteByte(')')
return str.String()
}

28
ext/csv/schema_test.go Normal file
View File

@@ -0,0 +1,28 @@
package csv
import "testing"
func Test_getSchema(t *testing.T) {
t.Parallel()
tests := []struct {
header bool
columns int
row []string
want string
}{
{true, 2, nil, `CREATE TABLE x(c1 TEXT,c2 TEXT)`},
{false, 2, nil, `CREATE TABLE x(c1 TEXT,c2 TEXT)`},
{false, -1, []string{"abc", ""}, `CREATE TABLE x(c1 TEXT,c2 TEXT)`},
{true, 3, []string{"abc", ""}, `CREATE TABLE x("abc" TEXT,c2 TEXT,c3 TEXT)`},
{true, -1, []string{"abc", "def"}, `CREATE TABLE x("abc" TEXT,"def" TEXT)`},
{true, 1, []string{"abc", "def"}, `CREATE TABLE x("abc" TEXT)`},
}
for _, tt := range tests {
t.Run(tt.want, func(t *testing.T) {
if got := getSchema(tt.header, tt.columns, tt.row); got != tt.want {
t.Errorf("getSchema() = %v, want %v", got, tt.want)
}
})
}
}

258
ext/csv/testdata/eurofxref.csv vendored Normal file
View File

@@ -0,0 +1,258 @@
Date,USD,JPY,BGN,CYP,CZK,DKK,EEK,GBP,HUF,LTL,LVL,MTL,PLN,ROL,RON,SEK,SIT,SKK,CHF,ISK,NOK,HRK,RUB,TRL,TRY,AUD,BRL,CAD,CNY,HKD,IDR,ILS,INR,KRW,MXN,MYR,NZD,PHP,SGD,THB,ZAR,
2022-12-30,1.0666,140.66,1.9558,N/A,24.116,7.4365,N/A,0.88693,400.87,N/A,N/A,N/A,4.6808,N/A,4.9495,11.1218,N/A,N/A,0.9847,151.5,10.5138,7.5365,N/A,N/A,19.9649,1.5693,5.6386,1.444,7.3582,8.3163,16519.82,3.7554,88.171,1344.09,20.856,4.6984,1.6798,59.32,1.43,36.835,18.0986,
2022-12-29,1.0649,142.24,1.9558,N/A,24.191,7.4365,N/A,0.88549,399.6,N/A,N/A,N/A,4.6855,N/A,4.9493,11.158,N/A,N/A,0.984,152.5,10.55,7.5365,N/A,N/A,19.934,1.5859,5.5351,1.4475,7.4151,8.2994,16680.38,3.7575,88.2295,1350.18,20.651,4.7106,1.6887,59.367,1.436,36.877,18.1967,
2022-12-28,1.064,142.21,1.9558,N/A,24.252,7.4365,N/A,0.88058,403.3,N/A,N/A,N/A,4.7008,N/A,4.946,11.1038,N/A,N/A,0.9863,151.9,10.4495,7.5365,N/A,N/A,19.9144,1.566,5.6109,1.4361,7.4224,8.2931,16765.93,3.7526,88.0943,1348.59,20.6856,4.7055,1.6772,59.613,1.4323,36.953,18.289,
2022-12-27,1.0624,141.68,1.9558,N/A,24.26,7.4366,N/A,0.88333,401.65,N/A,N/A,N/A,4.6683,N/A,4.927,11.1285,N/A,N/A,0.9885,152.3,10.4895,7.5375,N/A,N/A,19.8799,1.577,5.6035,1.4384,7.3994,8.2874,16620.58,3.7278,88.0808,1349.85,20.5515,4.699,1.6916,59.356,1.43,36.775,18.3181,
2022-12-23,1.0622,140.86,1.9558,N/A,24.247,7.4364,N/A,0.8803,400.68,N/A,N/A,N/A,4.6423,N/A,4.9056,11.1045,N/A,N/A,0.9867,152.3,10.4448,7.537,N/A,N/A,19.843,1.5857,5.4834,1.4433,7.4198,8.2878,16569.18,3.704,87.958,1359.5,20.7115,4.7002,1.6887,58.623,1.4337,36.842,18.1048,
2022-12-22,1.0633,140.42,1.9558,N/A,24.215,7.4367,N/A,0.88243,402.13,N/A,N/A,N/A,4.6443,N/A,4.8993,11.05,N/A,N/A,0.9852,153.3,10.4123,7.538,N/A,N/A,19.8553,1.5804,5.5386,1.4484,7.4229,8.2883,16525.91,3.6942,88.0365,1361.75,20.8485,4.7051,1.6918,58.705,1.4356,36.849,18.2238,
2022-12-21,1.0636,140.29,1.9558,N/A,24.218,7.438,N/A,0.87651,402.93,N/A,N/A,N/A,4.6665,N/A,4.8937,11.0623,N/A,N/A,0.9836,152.1,10.4309,7.5419,N/A,N/A,19.8541,1.5859,5.4913,1.4475,7.4219,8.2902,16573.77,3.6989,88.109,1367.63,20.9919,4.7197,1.685,58.556,1.4366,36.907,18.3529,
2022-12-20,1.0599,140.58,1.9558,N/A,24.181,7.4388,N/A,0.8753,403.88,N/A,N/A,N/A,4.6757,N/A,4.9125,11.0615,N/A,N/A,0.9854,151.5,10.5098,7.5471,N/A,N/A,19.7744,1.5972,5.6234,1.4451,7.39,8.2488,16537.09,3.6759,87.6649,1363.73,20.9355,4.6991,1.6816,58.549,1.4347,36.853,18.4239,
2022-12-19,1.0598,144.65,1.9558,N/A,24.233,7.4382,N/A,0.87118,403.18,N/A,N/A,N/A,4.6853,N/A,4.9107,11.0063,N/A,N/A,0.9884,151.9,10.5025,7.5395,N/A,N/A,19.7676,1.5794,5.6327,1.4472,7.3901,8.2428,16506.72,3.6551,87.5321,1377.17,20.9743,4.6912,1.6632,58.649,1.4378,36.923,18.3074,
2022-12-16,1.0619,145.53,1.9558,N/A,24.262,7.4379,N/A,0.87233,407.1,N/A,N/A,N/A,4.6925,N/A,4.9213,11.0153,N/A,N/A,0.9879,150.1,10.4833,7.5385,N/A,N/A,19.8039,1.5866,5.6233,1.4506,7.4037,8.2632,16575.47,3.6689,87.824,1389.7,21.0634,4.6984,1.6687,58.967,1.4413,37.145,18.6708,
2022-12-15,1.0621,145.07,1.9558,N/A,24.27,7.4387,N/A,0.86194,406.4,N/A,N/A,N/A,4.689,N/A,4.922,10.898,N/A,N/A,0.9862,150.9,10.4013,7.5395,N/A,N/A,19.806,1.5695,5.6247,1.4443,7.4007,8.2551,16591.4,3.6388,87.9355,1393.97,20.9431,4.6918,1.6628,59.297,1.4406,37.12,18.3599,
2022-12-14,1.0649,143.68,1.9558,N/A,24.276,7.4392,N/A,0.86118,406.63,N/A,N/A,N/A,4.681,N/A,4.9248,10.8638,N/A,N/A,0.9865,150.9,10.362,7.538,N/A,N/A,19.8579,1.551,5.6842,1.4441,7.4009,8.2751,16599.51,3.6327,87.8435,1379.99,20.8635,4.6765,1.6508,59.326,1.4349,36.851,18.2563,
2022-12-13,1.0545,144.85,1.9558,N/A,24.287,7.4391,N/A,0.85753,409.65,N/A,N/A,N/A,4.6938,N/A,4.9298,10.8965,N/A,N/A,0.9869,151.1,10.4679,7.5495,N/A,N/A,19.6649,1.5553,5.5784,1.4341,7.3637,8.2033,16521.81,3.6266,87.2965,1378.75,20.9435,4.6704,1.6464,58.852,1.4288,36.707,18.6855,
2022-12-12,1.0562,144.86,1.9558,N/A,24.307,7.4379,N/A,0.86006,416.78,N/A,N/A,N/A,4.6923,N/A,4.9318,10.9075,N/A,N/A,0.9855,150.7,10.5548,7.554,N/A,N/A,19.6913,1.5625,5.556,1.4428,7.367,8.2103,16524.63,3.6232,87.253,1377.93,20.9047,4.6652,1.6523,58.788,1.4284,36.708,18.4697,
2022-12-09,1.0559,143.3,1.9558,N/A,24.293,7.4379,N/A,0.8595,417.53,N/A,N/A,N/A,4.6869,N/A,4.9224,10.9188,N/A,N/A,0.9856,149.5,10.5345,7.555,N/A,N/A,19.6872,1.5553,5.5457,1.438,7.3475,8.2169,16453.46,3.6128,86.9535,1373.94,20.849,4.6512,1.6482,58.47,1.426,36.656,18.2358,
2022-12-08,1.0519,143.75,1.9558,N/A,24.324,7.4382,N/A,0.86258,417.66,N/A,N/A,N/A,4.6853,N/A,4.9131,10.906,N/A,N/A,0.9889,149.5,10.488,7.5553,N/A,N/A,19.6114,1.559,5.488,1.4307,7.3324,8.1889,16423.92,3.6206,86.6755,1387.06,20.6989,4.6257,1.6547,58.233,1.4256,36.559,18.0225,
2022-12-07,1.0529,144.44,1.9558,N/A,24.322,7.4382,N/A,0.86408,410.63,N/A,N/A,N/A,4.7003,N/A,4.918,10.919,N/A,N/A,0.9893,148.7,10.5255,7.5525,N/A,N/A,19.6256,1.5728,5.5023,1.4387,7.3476,8.1997,16465.17,3.626,86.692,1390.87,20.7534,4.6301,1.6573,58.432,1.4286,36.904,18.1353,
2022-12-06,1.0516,143.33,1.9558,N/A,24.316,7.438,N/A,0.8617,415.08,N/A,N/A,N/A,4.6975,N/A,4.913,10.889,N/A,N/A,0.9872,148.9,10.4408,7.5563,N/A,N/A,19.601,1.5625,5.5113,1.4326,7.3494,8.1813,16441.49,3.5831,86.6485,1386.39,20.6884,4.6223,1.6583,58.782,1.4263,36.827,18.2068,
2022-12-05,1.0587,143.07,1.9558,N/A,24.351,7.4369,N/A,0.86085,412.13,N/A,N/A,N/A,4.695,N/A,4.9215,10.8931,N/A,N/A,0.9893,148.9,10.3366,7.551,N/A,N/A,19.7326,1.5542,5.5491,1.4198,7.3573,8.2236,16332.36,3.5856,86.5249,1370.87,20.7295,4.6255,1.6498,59.245,1.4271,36.732,18.2038,
2022-12-02,1.0538,141.32,1.9558,N/A,24.377,7.4373,N/A,0.85855,410,N/A,N/A,N/A,4.684,N/A,4.9298,10.902,N/A,N/A,0.9834,148.7,10.2615,7.5503,N/A,N/A,19.6392,1.5457,5.4657,1.416,7.3971,8.2035,16179.78,3.5769,85.6435,1366.67,20.187,4.6241,1.6453,58.734,1.4227,36.614,18.2749,
2022-12-01,1.0454,142.48,1.9558,N/A,24.361,7.4373,N/A,0.85715,413.5,N/A,N/A,N/A,4.6998,N/A,4.9303,10.8984,N/A,N/A,0.9868,148.7,10.2495,7.55,N/A,N/A,19.4778,1.5377,5.4508,1.4059,7.3965,8.1371,16160.84,3.5642,84.933,1363.81,20.1472,4.605,1.6446,58.793,1.4195,36.526,18.5393,
2022-11-30,1.0376,144.28,1.9558,N/A,24.338,7.4366,N/A,0.86488,408.4,N/A,N/A,N/A,4.6635,N/A,4.9245,10.9345,N/A,N/A,0.9854,147.1,10.2648,7.549,N/A,N/A,19.3333,1.5425,5.5063,1.4021,7.3437,8.0944,16271.81,3.5691,84.4215,1365.14,20.0111,4.6147,1.6634,58.697,1.418,36.588,17.5768,
2022-11-29,1.0366,143.36,1.9558,N/A,24.334,7.4367,N/A,0.86218,406.5,N/A,N/A,N/A,4.673,N/A,4.9193,10.901,N/A,N/A,0.9862,147.1,10.3313,7.55,N/A,N/A,19.3181,1.5414,5.5126,1.4005,7.4289,8.0965,16301.58,3.5581,84.6548,1375.7,19.8075,4.6735,1.6639,58.64,1.4237,36.706,17.6027,
2022-11-28,1.0463,144.9,1.9558,N/A,24.348,7.4367,N/A,0.86606,408.87,N/A,N/A,N/A,4.6938,N/A,4.9246,10.8973,N/A,N/A,0.9872,146.7,10.364,7.5488,N/A,N/A,19.4844,1.5632,5.6354,1.4062,7.5326,8.1782,16440.45,3.6002,85.437,1396.56,20.22,4.6874,1.6827,59.21,1.4375,37.285,17.9376,
2022-11-25,1.0375,144.62,1.9558,N/A,24.367,7.4365,N/A,0.85885,411.33,N/A,N/A,N/A,4.6875,N/A,4.9255,10.8183,N/A,N/A,0.9836,146.5,10.2985,7.5473,N/A,N/A,19.3333,1.5404,5.5476,1.3864,7.4425,8.1084,16282.44,3.5551,84.7145,1383.2,20.1069,4.648,1.6651,58.795,1.4277,37.153,17.7677,
2022-11-24,1.0413,143.9,1.9558,N/A,24.392,7.4369,N/A,0.85933,413.33,N/A,N/A,N/A,4.6958,N/A,4.9205,10.8573,N/A,N/A,0.9818,146.5,10.3435,7.547,N/A,N/A,19.3969,1.5414,5.5504,1.3894,7.4442,8.1324,16295.35,3.5607,85.0295,1382.28,20.1501,4.6806,1.6598,58.992,1.4319,37.258,17.7246,
2022-11-23,1.0325,145.75,1.9558,N/A,24.356,7.437,N/A,0.86369,405.75,N/A,N/A,N/A,4.7033,N/A,4.937,10.8933,N/A,N/A,0.9795,146.7,10.3659,7.5435,N/A,N/A,19.2316,1.5522,5.565,1.3856,7.3982,8.0708,16189.81,3.5681,84.466,1397.42,20.001,4.7237,1.6718,58.914,1.4295,37.423,17.7103,
2022-11-22,1.0274,145.2,1.9558,N/A,24.351,7.4377,N/A,0.86358,408.23,N/A,N/A,N/A,4.7125,N/A,4.9269,10.9653,N/A,N/A,0.9791,145.7,10.4445,7.5438,N/A,N/A,19.1221,1.5473,5.4578,1.3765,7.3344,8.0313,16106.79,3.5615,83.8768,1392.68,20.0951,4.7055,1.6707,58.942,1.4167,37.11,17.7568,
2022-11-21,1.0246,145.33,1.9558,N/A,24.356,7.4377,N/A,0.86793,409.43,N/A,N/A,N/A,4.7075,N/A,4.9413,10.9873,N/A,N/A,0.9817,147.9,10.4898,7.5425,N/A,N/A,19.0822,1.5471,5.4401,1.377,7.3419,7.9989,16116.76,3.5515,83.7375,1395,19.9357,4.6927,1.6766,58.822,1.4162,37.121,17.7858,
2022-11-18,1.0366,145.12,1.9558,N/A,24.351,7.4385,N/A,0.87063,407.41,N/A,N/A,N/A,4.7033,N/A,4.9413,10.9805,N/A,N/A,0.9881,148.9,10.486,7.5415,N/A,N/A,19.3009,1.5433,5.547,1.3841,7.379,8.1092,16224.36,3.591,84.6875,1389.02,20.162,4.7202,1.6757,59.41,1.4229,37.069,17.908,
2022-11-17,1.0319,144.8,1.9558,N/A,24.399,7.4383,N/A,0.87475,415.6,N/A,N/A,N/A,4.7153,N/A,4.9254,10.9871,N/A,N/A,0.9818,148.9,10.498,7.541,N/A,N/A,19.2124,1.5526,5.6535,1.382,7.3859,8.077,16224.01,3.5796,84.394,1394.06,20.062,4.7122,1.6986,59.293,1.4221,37.123,18.0961,
2022-11-16,1.0412,145.29,1.9558,N/A,24.355,7.4386,N/A,0.87483,408.18,N/A,N/A,N/A,4.7065,N/A,4.9206,10.8754,N/A,N/A,0.9795,148.9,10.3675,7.5443,N/A,N/A,19.3783,1.54,5.5438,1.3801,7.372,8.1444,16248.37,3.5684,84.5905,1378.1,20.1227,4.7323,1.6897,59.678,1.425,37.103,18.0195,
2022-11-15,1.0404,144.84,1.9558,N/A,24.326,7.4388,N/A,0.87455,405.45,N/A,N/A,N/A,4.7073,N/A,4.9116,10.8081,N/A,N/A,0.979,149.9,10.357,7.5459,N/A,N/A,19.3608,1.5415,5.548,1.3816,7.3299,8.143,16164.78,3.5694,84.1304,1365.61,20.0795,4.7208,1.6897,59.532,1.4238,36.939,17.8822,
2022-11-14,1.0319,144.86,1.9558,N/A,24.289,7.4382,N/A,0.87513,407.28,N/A,N/A,N/A,4.6898,N/A,4.9043,10.7713,N/A,N/A,0.9751,150.3,10.3143,7.5465,N/A,N/A,19.1923,1.5427,5.4605,1.3706,7.2906,8.0852,16052.12,3.541,83.7779,1369.32,20.0985,4.7429,1.6957,59.04,1.4177,36.978,17.8393,
2022-11-11,1.0308,143.89,1.9558,N/A,24.278,7.4384,N/A,0.87538,402.08,N/A,N/A,N/A,4.6765,N/A,4.894,10.7241,N/A,N/A,0.9844,148.7,10.2635,7.5445,N/A,N/A,19.0987,1.5459,5.5147,1.3698,7.3267,8.0758,15979.45,3.5255,83.2253,1359.2,20.0239,4.77,1.702,59.106,1.4199,37.088,17.7944,
2022-11-10,0.9954,145.47,1.9558,N/A,24.361,7.4381,N/A,0.87298,400.95,N/A,N/A,N/A,4.706,N/A,4.8913,10.8743,N/A,N/A,0.9834,147.5,10.3615,7.5427,N/A,N/A,18.51,1.5525,5.286,1.3467,7.2184,7.8128,15615.6,3.5453,81.3058,1373.96,19.4562,4.6789,1.6984,57.793,1.3963,36.7,17.6882,
2022-11-09,1.0039,146.82,1.9558,N/A,24.337,7.4382,N/A,0.87774,403.53,N/A,N/A,N/A,4.701,N/A,4.9045,10.845,N/A,N/A,0.988,146.7,10.322,7.5425,N/A,N/A,18.6728,1.5538,5.1947,1.3501,7.2813,7.8801,15717.07,3.5621,81.6575,1369.73,19.6554,4.7098,1.7033,58.236,1.4061,36.999,17.877,
2022-11-08,0.9996,146.25,1.9558,N/A,24.326,7.4378,N/A,0.87378,400.75,N/A,N/A,N/A,4.6918,N/A,4.8978,10.8373,N/A,N/A,0.9911,146.3,10.2795,7.539,N/A,N/A,18.5991,1.5435,5.203,1.3489,7.2495,7.8468,15652.76,3.5436,81.518,1377.94,19.4495,4.7346,1.686,58.187,1.4022,37.22,17.8397,
2022-11-07,0.9993,146.18,1.9558,N/A,24.301,7.4393,N/A,0.87135,401.03,N/A,N/A,N/A,4.6865,N/A,4.8855,10.832,N/A,N/A,0.9874,145.9,10.2555,7.5375,N/A,N/A,18.5875,1.5428,5.07,1.3464,7.2189,7.8444,15648.95,3.5402,81.8407,1391.25,19.4395,4.7362,1.6834,58.361,1.4022,37.284,17.7583,
2022-11-04,0.9872,145.19,1.9558,N/A,24.422,7.4419,N/A,0.87478,401.15,N/A,N/A,N/A,4.6825,N/A,4.8893,10.8538,N/A,N/A,0.9863,145.5,10.2019,7.5353,N/A,N/A,18.3845,1.5311,4.9682,1.3351,7.0894,7.7493,15491.81,3.5065,81.02,1397.7,19.2611,4.6872,1.6769,57.672,1.3891,36.906,17.7983,
2022-11-03,0.9753,144.58,1.9558,N/A,24.539,7.4433,N/A,0.87228,407.87,N/A,N/A,N/A,4.709,N/A,4.9013,10.932,N/A,N/A,0.9889,144.9,10.3543,7.5375,N/A,N/A,18.1602,1.5517,5.0262,1.3452,7.1367,7.656,15400.2,3.4847,80.8845,1391.75,19.2363,4.6271,1.6957,57.463,1.3878,37.091,18.0173,
2022-11-02,0.9908,145.75,1.9558,N/A,24.506,7.4431,N/A,0.861,407,N/A,N/A,N/A,4.7035,N/A,4.912,10.9065,N/A,N/A,0.9861,143.7,10.2388,7.5335,N/A,N/A,18.4488,1.5426,5.0964,1.347,7.2156,7.7774,15492.57,3.4987,81.992,1402.01,19.4921,4.6944,1.6844,57.841,1.3983,37.314,17.9608,
2022-11-01,0.9947,146.35,1.9558,N/A,24.484,7.4438,N/A,0.86058,406.9,N/A,N/A,N/A,4.7053,N/A,4.9138,10.874,N/A,N/A,0.9878,143.3,10.1835,7.5342,N/A,N/A,18.5216,1.5409,5.1337,1.3469,7.2165,7.8079,15534.6,3.4922,82.084,1404.63,19.5984,4.7119,1.6876,57.786,1.4017,37.45,17.9802,
2022-10-31,0.9914,147.4,1.9558,N/A,24.488,7.4444,N/A,0.86115,409.65,N/A,N/A,N/A,4.7085,N/A,4.9143,10.901,N/A,N/A,0.9925,143.3,10.3028,7.531,N/A,N/A,18.4562,1.5529,5.2694,1.3553,7.238,7.7822,15489.55,3.4933,82.1035,1416.12,19.7122,4.6873,1.7099,57.8,1.4038,37.748,18.1736,
2022-10-28,0.9951,146.79,1.9558,N/A,24.465,7.4423,N/A,0.8612,411.7,N/A,N/A,N/A,4.7275,N/A,4.9189,10.9403,N/A,N/A,0.992,143.3,10.2695,7.532,N/A,N/A,18.5219,1.5511,5.327,1.3542,7.2159,7.8107,15481.88,3.5215,82.0565,1417.7,19.7718,4.6994,1.7151,57.739,1.4055,37.724,18.053,
2022-10-27,1.0037,147.37,1.9558,N/A,24.53,7.4387,N/A,0.86745,412.15,N/A,N/A,N/A,4.7585,N/A,4.8893,10.9583,N/A,N/A,0.9949,143.1,10.342,7.533,N/A,N/A,18.681,1.561,5.3889,1.3672,7.2552,7.8782,15629.06,3.5376,82.656,1428.57,20.015,4.7324,1.7316,58.441,1.4154,37.975,18.1521,
2022-10-26,1.0023,147.32,1.9558,N/A,24.535,7.4381,N/A,0.86603,408.09,N/A,N/A,N/A,4.7548,N/A,4.8806,10.953,N/A,N/A,0.9917,143.5,10.3408,7.532,N/A,N/A,18.6461,1.5466,5.2944,1.3568,7.1948,7.8678,15589.27,3.5145,82.206,1422.11,19.8501,4.7262,1.7249,58.493,1.4104,37.862,18.0212,
2022-10-25,0.9861,146.84,1.9558,N/A,24.472,7.4387,N/A,0.87143,413.7,N/A,N/A,N/A,4.777,N/A,4.9036,10.9728,N/A,N/A,0.9888,142.9,10.391,7.5315,N/A,N/A,18.3508,1.5599,5.2254,1.3537,7.2072,7.7407,15407.12,3.506,81.653,1417.5,19.6353,4.6697,1.7321,57.988,1.405,37.758,18.2211,
2022-10-24,0.9851,146.76,1.9558,N/A,24.482,7.4385,N/A,0.8707,411.88,N/A,N/A,N/A,4.7908,N/A,4.9128,11.0795,N/A,N/A,0.9856,142.5,10.392,7.5337,N/A,N/A,18.3298,1.5631,5.1461,1.3502,7.1544,7.7329,15362.63,3.4997,81.5451,1418.4,19.6514,4.6674,1.7343,58.021,1.4008,37.6,18.0625,
2022-10-21,0.973,147.59,1.9558,N/A,24.511,7.4382,N/A,0.87728,412.88,N/A,N/A,N/A,4.7885,N/A,4.9125,11.0868,N/A,N/A,0.9855,141.1,10.4315,7.5325,N/A,N/A,18.0988,1.5646,5.1117,1.3465,7.0504,7.6376,15199.12,3.4803,80.739,1404.32,19.5521,4.6101,1.7347,57.287,1.3917,37.349,18.0021,
2022-10-20,0.9811,146.99,1.9558,N/A,24.525,7.4389,N/A,0.87258,411.2,N/A,N/A,N/A,4.7728,N/A,4.9203,10.982,N/A,N/A,0.9836,141.1,10.402,7.5353,N/A,N/A,18.2257,1.5554,5.1387,1.3461,7.0858,7.7008,15250.05,3.4754,81.1755,1400.3,19.7005,4.6396,1.7206,57.742,1.3959,37.36,17.9106,
2022-10-19,0.9778,146.34,1.9558,N/A,24.563,7.439,N/A,0.86993,413.78,N/A,N/A,N/A,4.7878,N/A,4.9248,10.9448,N/A,N/A,0.981,141.1,10.3823,7.5325,N/A,N/A,18.1793,1.5568,5.1755,1.3479,7.0672,7.6757,15185.1,3.4628,81.1955,1398.35,19.6845,4.6152,1.7264,57.741,1.3931,37.469,17.8339,
2022-10-18,0.9835,146.65,1.9558,N/A,24.593,7.4393,N/A,0.86928,413.08,N/A,N/A,N/A,4.804,N/A,4.9359,10.906,N/A,N/A,0.9792,141.5,10.3528,7.5298,N/A,N/A,18.2813,1.5557,5.1795,1.3495,7.0805,7.72,15214.98,3.464,80.9195,1400.92,19.664,4.6382,1.7251,57.897,1.3963,37.422,17.7904,
2022-10-17,0.9739,145,1.9558,N/A,24.562,7.4379,N/A,0.8625,418.3,N/A,N/A,N/A,4.8143,N/A,4.937,10.9893,N/A,N/A,0.9762,140.9,10.342,7.5265,N/A,N/A,18.1043,1.5599,5.1497,1.3452,7.013,7.6448,15061.8,3.4486,80.128,1399.41,19.5,4.5934,1.7404,57.433,1.3896,37.169,17.6769,
2022-10-14,0.9717,143.63,1.9558,N/A,24.587,7.4378,N/A,0.86823,418.24,N/A,N/A,N/A,4.8328,N/A,4.9335,11.0035,N/A,N/A,0.9757,140.5,10.3323,7.5266,N/A,N/A,18.0614,1.5493,5.1177,1.3426,6.9952,7.6278,15031.5,3.444,79.9695,1398.5,19.5032,4.5689,1.7302,57.375,1.3852,37.109,17.6932,
2022-10-13,0.9739,142.94,1.9558,N/A,24.569,7.4385,N/A,0.86513,430.65,N/A,N/A,N/A,4.8303,N/A,4.9355,11.0098,N/A,N/A,0.9725,140.5,10.3525,7.531,N/A,N/A,18.1041,1.5495,5.1214,1.3443,6.9945,7.644,14952.86,3.4731,79.9981,1392.71,19.4442,4.5691,1.7314,57.352,1.3949,36.843,17.8173,
2022-10-12,0.9706,142.34,1.9558,N/A,24.561,7.4399,N/A,0.8784,429.65,N/A,N/A,N/A,4.8495,N/A,4.94,11.02,N/A,N/A,0.9664,140.1,10.4145,7.529,N/A,N/A,18.0427,1.5525,5.1378,1.3395,6.9603,7.6192,14907.04,3.4623,79.8955,1384.66,19.4522,4.5448,1.7372,57.148,1.3941,36.902,17.6876,
2022-10-11,0.9723,141.54,1.9558,N/A,24.535,7.439,N/A,0.87703,428.73,N/A,N/A,N/A,4.869,N/A,4.9394,11.0015,N/A,N/A,0.9675,140.7,10.4235,7.5293,N/A,N/A,18.0686,1.545,5.0456,1.3402,6.9669,7.6325,14930.83,3.4776,79.9555,1392.84,19.4115,4.5436,1.7323,57.243,1.3967,37.03,17.6153,
2022-10-10,0.9697,141.16,1.9558,N/A,24.521,7.4384,N/A,0.8773,428.2,N/A,N/A,N/A,4.8655,N/A,4.94,10.9502,N/A,N/A,0.968,139.9,10.3378,7.528,N/A,N/A,18.0131,1.536,5.0328,1.3312,6.9344,7.612,14872.51,3.4463,79.9678,1384.26,19.3588,4.5091,1.7369,57.197,1.3939,36.81,17.5866,
2022-10-07,0.9797,141.92,1.9558,N/A,24.517,7.4381,N/A,0.87383,423.85,N/A,N/A,N/A,4.8595,N/A,4.9415,10.8555,N/A,N/A,0.97,140.7,10.4498,7.527,N/A,N/A,18.209,1.5266,5.1075,1.3437,6.9715,7.6906,14933.14,3.4477,80.546,1381.42,19.643,4.5556,1.7328,57.747,1.3996,36.602,17.6222,
2022-10-06,0.986,142.68,1.9558,N/A,24.479,7.439,N/A,0.87583,422.59,N/A,N/A,N/A,4.8505,N/A,4.9364,10.8728,N/A,N/A,0.9709,139.9,10.4278,7.5288,N/A,N/A,18.3191,1.5263,5.1185,1.3475,7.0164,7.74,15021.24,3.4846,81.0615,1388.39,19.817,4.5726,1.728,57.949,1.4057,36.827,17.5769,
2022-10-05,0.9915,143.18,1.9558,N/A,24.524,7.4388,N/A,0.8734,423.6,N/A,N/A,N/A,4.791,N/A,4.9385,10.8376,N/A,N/A,0.9756,141.3,10.4858,7.5255,N/A,N/A,18.4201,1.538,5.1575,1.3493,7.0555,7.7831,15061.81,3.503,80.909,1406.71,19.8625,4.5911,1.7419,58.212,1.4124,37.102,17.6228,
2022-10-04,0.9891,143.3,1.9558,N/A,24.544,7.4374,N/A,0.87273,417.68,N/A,N/A,N/A,4.8193,N/A,4.9418,10.8166,N/A,N/A,0.9767,141.9,10.4915,7.523,N/A,N/A,18.3374,1.5318,5.0589,1.3503,7.0384,7.7644,15080.18,3.4885,80.6995,1412.2,19.777,4.5939,1.7368,58.104,1.4148,37.16,17.5437,
2022-10-03,0.9764,141.49,1.9558,N/A,24.527,7.4366,N/A,0.8707,424.86,N/A,N/A,N/A,4.832,N/A,4.9479,10.8743,N/A,N/A,0.9658,141.7,10.5655,7.5275,N/A,N/A,18.124,1.5128,5.178,1.3412,6.9481,7.6647,14969.79,3.498,79.898,1408.25,19.604,4.5383,1.7263,57.599,1.4015,37.181,17.5871,
2022-09-30,0.9748,141.01,1.9558,N/A,24.549,7.4365,N/A,0.883,422.18,N/A,N/A,N/A,4.8483,N/A,4.949,10.8993,N/A,N/A,0.9561,140.9,10.5838,7.524,N/A,N/A,18.0841,1.5076,5.2584,1.3401,6.9368,7.6521,14863.26,3.4759,79.425,1400.69,19.6393,4.5201,1.7177,57.276,1.4001,36.823,17.5353,
2022-09-29,0.9706,140.46,1.9558,N/A,24.687,7.4365,N/A,0.89485,421.93,N/A,N/A,N/A,4.857,N/A,4.9481,10.958,N/A,N/A,0.9538,140.1,10.4518,7.528,N/A,N/A,18,1.4982,5.2521,1.3294,6.9223,7.6192,14735.97,3.4422,79.314,1388.34,19.5779,4.4992,1.704,56.86,1.3961,36.946,17.4466,
2022-09-28,0.9565,138.39,1.9558,N/A,24.65,7.4368,N/A,0.90268,411.72,N/A,N/A,N/A,4.8043,N/A,4.9485,10.9194,N/A,N/A,0.9437,139.7,10.4576,7.5313,N/A,N/A,17.7311,1.4924,5.1728,1.3157,6.9199,7.5084,14622.96,3.3931,78.2655,1378.84,19.5294,4.4281,1.6998,56.528,1.3846,36.687,17.2916,
2022-09-27,0.9644,139.28,1.9558,N/A,24.661,7.4366,N/A,0.89275,406.65,N/A,N/A,N/A,4.764,N/A,4.9444,10.8533,N/A,N/A,0.9503,139.3,10.3473,7.528,N/A,N/A,17.824,1.4859,5.1235,1.3196,6.9156,7.5704,14604.52,3.3714,78.574,1370.06,19.5832,4.4466,1.6921,56.933,1.3838,36.565,17.2361,
2022-09-26,0.9646,139.07,1.9558,N/A,24.64,7.4365,N/A,0.89404,408.83,N/A,N/A,N/A,4.7608,N/A,4.9418,10.9275,N/A,N/A,0.9555,138.9,10.3585,7.5278,N/A,N/A,17.8001,1.4858,5.1504,1.3195,6.9075,7.572,14620.74,3.4069,78.704,1379.4,19.6066,4.4401,1.6886,56.908,1.3842,36.496,17.4247,
2022-09-23,0.9754,139.43,1.9558,N/A,24.658,7.4365,N/A,0.88201,406.3,N/A,N/A,N/A,4.7543,N/A,4.9433,10.9328,N/A,N/A,0.9565,139.9,10.2335,7.5228,N/A,N/A,17.9515,1.4828,5.0456,1.3177,6.9442,7.6567,14697.3,3.4152,79.0705,1381.97,19.5708,4.4659,1.6846,57.217,1.3897,36.636,17.3853,
2022-09-22,0.9884,139.18,1.9558,N/A,24.657,7.4365,N/A,0.87256,405.25,N/A,N/A,N/A,4.7592,N/A,4.9411,10.8724,N/A,N/A,0.9684,139.9,10.235,7.5235,N/A,N/A,18.1559,1.484,5.0677,1.3278,6.9804,7.7583,14824.28,3.4216,79.897,1384.79,19.6129,4.514,1.6832,57.721,1.3998,36.803,17.3514,
2022-09-21,0.9906,142.66,1.9558,N/A,24.637,7.4364,N/A,0.87335,405.1,N/A,N/A,N/A,4.7505,N/A,4.9443,10.9214,N/A,N/A,0.9549,140.3,10.2858,7.5205,N/A,N/A,18.149,1.4851,5.0924,1.3262,6.9821,7.7761,14866.28,3.4298,79.1555,1381.38,19.7847,4.5097,1.6844,57.285,1.4006,36.786,17.4879,
2022-09-20,0.9986,143.34,1.9558,N/A,24.556,7.4368,N/A,0.87395,398.58,N/A,N/A,N/A,4.7208,N/A,4.934,10.8338,N/A,N/A,0.9644,140.9,10.273,7.5198,N/A,N/A,18.2833,1.4893,5.2139,1.3268,7.003,7.8382,14997.82,3.4406,79.6095,1390.71,19.9667,4.5516,1.6908,57.497,1.4074,36.968,17.7261,
2022-09-19,0.999,143.42,1.9558,N/A,24.494,7.4373,N/A,0.87785,400.85,N/A,N/A,N/A,4.7058,N/A,4.93,10.7993,N/A,N/A,0.9658,139.9,10.2826,7.5215,N/A,N/A,18.2738,1.495,5.2886,1.3294,7.0066,7.8416,14975.43,3.446,79.653,1391.82,20.113,4.5455,1.6807,57.347,1.4082,36.983,17.7267,
2022-09-16,0.9954,142.53,1.9558,N/A,24.497,7.4366,N/A,0.874,403.98,N/A,N/A,N/A,4.7143,N/A,4.9238,10.7541,N/A,N/A,0.9579,138.3,10.1985,7.5235,N/A,N/A,18.1923,1.4894,5.2279,1.3226,6.9787,7.8133,14904.67,3.4267,79.3605,1383.58,20.0028,4.5141,1.6717,57.111,1.4025,36.8,17.6004,
2022-09-15,0.9992,143.43,1.9558,N/A,24.518,7.4366,N/A,0.86934,407.15,N/A,N/A,N/A,4.7273,N/A,4.9238,10.69,N/A,N/A,0.9572,138.7,10.1203,7.5258,N/A,N/A,18.2477,1.4853,5.1837,1.3172,6.9852,7.8423,14925.35,3.4384,79.7119,1397.18,20.0021,4.5314,1.6689,57.258,1.4062,36.816,17.5283,
2022-09-14,0.999,143.08,1.9558,N/A,24.527,7.4366,N/A,0.86498,402.9,N/A,N/A,N/A,4.7163,N/A,4.9297,10.675,N/A,N/A,0.9612,139.7,10.1125,7.5195,N/A,N/A,18.2397,1.4873,5.1827,1.3177,6.955,7.8405,14903.93,3.4339,79.422,1391.97,20.028,4.5225,1.6675,57.054,1.4039,36.608,17.4342,
2022-09-13,1.0175,144.5,1.9558,N/A,24.551,7.4366,N/A,0.86793,396.83,N/A,N/A,N/A,4.705,N/A,4.921,10.6108,N/A,N/A,0.9669,140.1,9.9988,7.5255,N/A,N/A,18.564,1.4736,5.1764,1.32,7.0467,7.9855,15099.17,3.4125,80.5453,1397.3,20.1615,4.5869,1.6555,57.665,1.4186,36.859,17.3112,
2022-09-12,1.0155,144.49,1.9558,N/A,24.546,7.4365,N/A,0.86778,395.73,N/A,N/A,N/A,4.6965,N/A,4.9135,10.6368,N/A,N/A,0.9667,140.9,9.9718,7.5195,N/A,N/A,18.5232,1.4749,5.1933,1.3194,7.0348,7.9709,15083.83,3.4346,80.692,1397.58,20.1025,4.5733,1.6499,57.701,1.4168,36.873,17.322,
2022-09-09,1.0049,143.3,1.9558,N/A,24.536,7.4365,N/A,0.8686,396.3,N/A,N/A,N/A,4.721,N/A,4.9019,10.6643,N/A,N/A,0.9657,140.9,9.9836,7.5245,N/A,N/A,18.3282,1.4704,5.2087,1.307,6.9543,7.8871,14905.33,3.4416,79.9685,1384.64,19.991,4.52,1.6463,57.098,1.4063,36.508,17.3753,
2022-09-08,1.0009,143.65,1.9558,N/A,24.543,7.4365,N/A,0.86656,395.48,N/A,N/A,N/A,4.7155,N/A,4.8756,10.7075,N/A,N/A,0.9739,140.3,10.0615,7.515,N/A,N/A,18.2546,1.4824,5.2042,1.3134,6.9564,7.8568,14891.86,3.429,79.7375,1381.7,20.013,4.5051,1.6491,57.031,1.4054,36.418,17.3797,
2022-09-07,0.9885,143.2,1.9558,N/A,24.631,7.4365,N/A,0.8651,401.83,N/A,N/A,N/A,4.729,N/A,4.8585,10.6888,N/A,N/A,0.975,141.3,9.9483,7.5143,N/A,N/A,18.0262,1.4748,5.1881,1.3037,6.8968,7.7596,14779.47,3.4053,79.028,1374.44,19.9225,4.4497,1.6459,56.532,1.3931,36.322,17.2582,
2022-09-06,0.9928,140.91,1.9558,N/A,24.55,7.4365,N/A,0.85743,402.65,N/A,N/A,N/A,4.7068,N/A,4.8424,10.6825,N/A,N/A,0.9745,141.9,9.8945,7.5133,N/A,N/A,18.0938,1.4651,5.13,1.3029,6.9091,7.7932,14783.94,3.39,79.2305,1366.65,19.8545,4.4676,1.6313,56.655,1.3947,36.242,17.0805,
2022-09-05,0.992,139.47,1.9558,N/A,24.622,7.4364,N/A,0.86358,403.9,N/A,N/A,N/A,4.736,N/A,4.8198,10.729,N/A,N/A,0.9747,142.7,9.9188,7.5173,N/A,N/A,18.0792,1.4616,5.1407,1.3043,6.8768,7.7867,14782.83,3.3826,79.2332,1359.98,19.8192,4.4563,1.6289,56.477,1.3932,36.263,17.088,
2022-09-02,0.9993,140.36,1.9558,N/A,24.481,7.437,N/A,0.86478,398.38,N/A,N/A,N/A,4.7063,N/A,4.8335,10.7498,N/A,N/A,0.9839,141.5,10.0035,7.5225,N/A,N/A,18.2072,1.4671,5.2153,1.3131,6.9031,7.8439,14895.92,3.396,79.8096,1360.84,20.1024,4.4809,1.6394,56.854,1.4013,36.624,17.2791,
2022-09-01,1.0004,139.34,1.9558,N/A,24.488,7.4372,N/A,0.86473,399.58,N/A,N/A,N/A,4.7128,N/A,4.8447,10.7415,N/A,N/A,0.9802,141.7,10.013,7.521,N/A,N/A,18.2149,1.4651,5.2239,1.3169,6.9017,7.8511,14878,3.3644,79.6195,1353.69,20.1954,4.4828,1.6389,56.609,1.4002,36.71,17.1524,
2022-08-31,1,138.72,1.9558,N/A,24.55,7.4371,N/A,0.86035,402.8,N/A,N/A,N/A,4.7283,N/A,4.8595,10.6788,N/A,N/A,0.9796,141.7,9.9388,7.5148,N/A,N/A,18.1849,1.4591,5.1482,1.3111,6.8947,7.8488,14849.93,3.3399,79.5465,1342.79,20.2044,4.4755,1.6322,56.153,1.3969,36.45,17.0667,
2022-08-30,1.0034,138.71,1.9558,N/A,24.577,7.4376,N/A,0.85645,406.38,N/A,N/A,N/A,4.7323,N/A,4.8657,10.65,N/A,N/A,0.9741,142.1,9.7553,7.5103,N/A,N/A,18.239,1.4472,5.0286,1.3047,6.9233,7.8751,14875,3.3168,79.8025,1350.92,20.0077,4.4907,1.6245,56.393,1.3997,36.494,16.8567,
2022-08-29,0.9986,138.49,1.9558,N/A,24.592,7.4379,N/A,0.8542,409.9,N/A,N/A,N/A,4.745,N/A,4.8699,10.628,N/A,N/A,0.967,141.1,9.7675,7.5119,N/A,N/A,18.1605,1.4529,5.0663,1.3026,6.9044,7.8368,14871.09,3.3146,79.8295,1347.47,19.9876,4.4837,1.6305,56.187,1.395,36.399,16.8891,
2022-08-26,1.0007,137.02,1.9558,N/A,24.635,7.4379,N/A,0.8459,410.48,N/A,N/A,N/A,4.7485,N/A,4.8729,10.5703,N/A,N/A,0.9642,140.3,9.667,7.5135,N/A,N/A,18.1923,1.4333,5.1069,1.2944,6.8671,7.8521,14825.57,3.2537,79.9025,1333.46,19.924,4.4706,1.6112,56.109,1.3906,36.035,16.7995,
2022-08-25,0.997,136.07,1.9558,N/A,24.648,7.4374,N/A,0.84293,408.93,N/A,N/A,N/A,4.7578,N/A,4.8758,10.5525,N/A,N/A,0.9616,140.3,9.64,7.514,N/A,N/A,18.112,1.4306,5.0879,1.2881,6.8317,7.8234,14753.15,3.2791,79.6555,1331.98,19.8132,4.4586,1.6006,55.842,1.3857,35.732,16.7903,
2022-08-24,0.9934,135.74,1.9558,N/A,24.629,7.4381,N/A,0.84283,410.93,N/A,N/A,N/A,4.7668,N/A,4.88,10.586,N/A,N/A,0.9576,139.5,9.636,7.5125,N/A,N/A,18.0362,1.4389,5.0606,1.2908,6.822,7.795,14757.6,3.2599,79.3006,1332.84,19.7781,4.4559,1.6065,55.7,1.3857,35.906,16.8976,
2022-08-23,0.9927,136.34,1.9558,N/A,24.658,7.4374,N/A,0.84343,410.55,N/A,N/A,N/A,4.7788,N/A,4.8839,10.6063,N/A,N/A,0.9602,140.1,9.7438,7.5128,N/A,N/A,17.983,1.4437,5.0984,1.2928,6.7952,7.79,14743.28,3.2598,79.2805,1332.1,19.9152,4.4557,1.6071,55.661,1.386,35.856,16.9568,
2022-08-22,1.0001,137.08,1.9558,N/A,24.651,7.437,N/A,0.84658,408,N/A,N/A,N/A,4.764,N/A,4.8853,10.65,N/A,N/A,0.958,140.9,9.7719,7.5113,N/A,N/A,18.1268,1.4478,5.1752,1.2989,6.8457,7.8468,14895.47,3.2778,79.8615,1344.21,20.1583,4.4854,1.6125,56.218,1.3958,36.099,17.0766,
2022-08-19,1.0054,137.67,1.9558,N/A,24.625,7.4373,N/A,0.84938,407.35,N/A,N/A,N/A,4.751,N/A,4.8811,10.6095,N/A,N/A,0.9616,140.5,9.8418,7.5155,N/A,N/A,18.2028,1.4584,5.2334,1.3062,6.8531,7.8877,14951.91,3.2879,80.2988,1343.39,20.3273,4.5007,1.6203,56.325,1.3979,35.988,17.1007,
2022-08-18,1.0178,137.17,1.9558,N/A,24.611,7.4389,N/A,0.84391,405.13,N/A,N/A,N/A,4.724,N/A,4.8808,10.5903,N/A,N/A,0.9683,140.5,9.8283,7.5215,N/A,N/A,18.4095,1.4617,5.2326,1.3118,6.906,7.9844,15092.62,3.2993,81.042,1344.81,20.313,4.5536,1.6145,56.776,1.4053,36.254,17.0004,
2022-08-17,1.0164,137.36,1.9558,N/A,24.566,7.4377,N/A,0.84208,404.28,N/A,N/A,N/A,4.7078,N/A,4.8825,10.5617,N/A,N/A,0.9686,140.3,9.8428,7.5071,N/A,N/A,18.2568,1.4655,5.2838,1.3117,6.8917,7.9705,15015.81,3.311,80.7555,1337.02,20.3825,4.5413,1.6165,56.772,1.4051,36.052,16.9125,
2022-08-16,1.0131,136.11,1.9558,N/A,24.54,7.4368,N/A,0.84218,406.2,N/A,N/A,N/A,4.7043,N/A,4.882,10.5365,N/A,N/A,0.9625,140.3,9.8428,7.51,N/A,N/A,18.1994,1.4463,5.1835,1.3076,6.8767,7.9449,14968.68,3.3087,80.3745,1329.66,20.1595,4.5245,1.6012,56.602,1.398,35.93,16.6556,
2022-08-15,1.0195,135.61,1.9558,N/A,24.46,7.4373,N/A,0.84375,398.6,N/A,N/A,N/A,4.6858,N/A,4.8849,10.498,N/A,N/A,0.9631,140.3,9.871,7.5028,N/A,N/A,18.3143,1.4508,5.2268,1.3167,6.905,7.9899,15042.36,3.3294,81.061,1336.35,20.3914,4.5465,1.6002,57.122,1.4036,36.218,16.7375,
2022-08-12,1.0285,137.47,1.9558,N/A,24.38,7.4395,N/A,0.84715,392.3,N/A,N/A,N/A,4.6773,N/A,4.8915,10.4515,N/A,N/A,0.9689,140.3,9.813,7.5138,N/A,N/A,18.4733,1.4496,5.3007,1.3148,6.9352,8.06,15104.2,3.345,81.9935,1342.59,20.4925,4.5709,1.5985,57.246,1.4106,36.393,16.7318,
2022-08-11,1.0338,136.57,1.9558,N/A,24.346,7.4395,N/A,0.84575,394.18,N/A,N/A,N/A,4.6828,N/A,4.9055,10.36,N/A,N/A,0.9712,139.7,9.804,7.515,N/A,N/A,18.5674,1.4532,5.2447,1.3202,6.9668,8.1118,15215.93,3.3505,82.2845,1344.62,20.6398,4.5952,1.6045,57.225,1.4151,36.343,16.7083,
2022-08-10,1.0252,138.16,1.9558,N/A,24.397,7.4397,N/A,0.84608,397.65,N/A,N/A,N/A,4.7063,N/A,4.9138,10.3773,N/A,N/A,0.9713,139.7,9.9118,7.5158,N/A,N/A,18.4099,1.4682,5.251,1.3207,6.9222,8.046,15218.87,3.3773,81.468,1344.16,20.713,4.5693,1.6211,57.061,1.4107,36.42,16.8788,
2022-08-09,1.0234,138.26,1.9558,N/A,24.532,7.4407,N/A,0.8452,397.35,N/A,N/A,N/A,4.7085,N/A,4.9038,10.3875,N/A,N/A,0.9763,140.1,9.9365,7.514,N/A,N/A,18.3342,1.4687,5.2478,1.3163,6.9106,8.0334,15197.09,3.3865,81.406,1336.74,20.7145,4.5592,1.6304,56.939,1.411,36.264,17.05,
2022-08-08,1.0199,137.62,1.9558,N/A,24.515,7.4405,N/A,0.84165,394.27,N/A,N/A,N/A,4.7043,N/A,4.9163,10.365,N/A,N/A,0.9763,140.1,9.9405,7.5123,N/A,N/A,18.3175,1.4607,5.238,1.3134,6.8931,8.0061,15147.65,3.3895,81.166,1329.93,20.681,4.5477,1.6202,56.554,1.4058,36.38,16.9694,
2022-08-05,1.0233,136.22,1.9558,N/A,24.581,7.4415,N/A,0.84268,393.78,N/A,N/A,N/A,4.7085,N/A,4.9251,10.3573,N/A,N/A,0.9776,138.9,9.982,7.5148,N/A,N/A,18.3853,1.4713,5.3348,1.3185,6.9068,8.0328,15236.2,3.4033,81.0469,1324.53,20.8367,4.5598,1.6248,56.524,1.4077,36.373,17.0342,
2022-08-04,1.0181,135.81,1.9558,N/A,24.659,7.4425,N/A,0.84231,395.98,N/A,N/A,N/A,4.7233,N/A,4.9261,10.374,N/A,N/A,0.9765,138.7,9.9065,7.515,N/A,N/A,18.2922,1.4607,5.3614,1.307,6.8769,7.9919,15200.94,3.4101,80.7715,1333.7,20.756,4.5387,1.6172,56.633,1.4037,36.606,17.0352,
2022-08-03,1.0194,136.18,1.9558,N/A,24.65,7.4427,N/A,0.83629,395.03,N/A,N/A,N/A,4.691,N/A,4.9245,10.3913,N/A,N/A,0.9773,139.3,9.8743,7.5178,N/A,N/A,18.3112,1.4681,5.3547,1.3085,6.883,8.0022,15184.35,3.4362,80.6895,1336.03,21.0788,4.5435,1.6266,56.855,1.4069,36.963,17.1283,
2022-08-02,1.0224,133.9,1.9558,N/A,24.644,7.4432,N/A,0.83665,396.82,N/A,N/A,N/A,4.7063,N/A,4.9298,10.3995,N/A,N/A,0.9744,139.3,9.9305,7.5195,N/A,N/A,18.3564,1.4745,5.3175,1.315,6.9117,8.0257,15204.85,3.4486,80.3243,1339.16,20.952,4.5531,1.6254,56.834,1.4103,36.914,16.982,
2022-08-01,1.0233,135.38,1.9558,N/A,24.628,7.4457,N/A,0.837,401.35,N/A,N/A,N/A,4.734,N/A,4.9283,10.3668,N/A,N/A,0.9717,138.7,9.8638,7.521,N/A,N/A,18.3475,1.4535,5.2723,1.3076,6.9105,8.0329,15203.21,3.4546,80.9335,1333.3,20.7635,4.5568,1.616,56.734,1.4087,36.977,16.8613,
2022-07-29,1.0198,136.42,1.9558,N/A,24.61,7.4438,N/A,0.8399,404.8,N/A,N/A,N/A,4.7375,N/A,4.9343,10.3875,N/A,N/A,0.9744,138.3,9.8773,7.518,N/A,N/A,18.2472,1.4646,5.2739,1.31,6.8705,8.0054,15155.56,3.4714,80.882,1329.4,20.6745,4.5386,1.6283,56.375,1.4088,36.978,16.8627,
2022-07-28,1.0122,137.26,1.9558,N/A,24.609,7.4442,N/A,0.83586,407.3,N/A,N/A,N/A,4.7908,N/A,4.9342,10.449,N/A,N/A,0.9745,138.7,9.8983,7.52,N/A,N/A,18.1417,1.4535,5.33,1.2986,6.8325,7.9456,15107.59,3.4701,80.6535,1320.45,20.676,4.5071,1.6172,56.592,1.4009,37.097,17.0011,
2022-07-27,1.0152,138.89,1.9558,N/A,24.575,7.4446,N/A,0.84138,404.67,N/A,N/A,N/A,4.7978,N/A,4.9334,10.4545,N/A,N/A,0.9768,139.1,9.9558,7.514,N/A,N/A,18.1859,1.462,5.4039,1.3049,6.8534,7.9692,15213.07,3.4855,81.135,1333.99,20.753,4.5263,1.6306,56.455,1.4088,37.4,17.1347,
2022-07-26,1.0124,138.35,1.9558,N/A,24.607,7.4449,N/A,0.84558,400.99,N/A,N/A,N/A,4.742,N/A,4.9324,10.4445,N/A,N/A,0.9765,139.1,10.0105,7.5145,N/A,N/A,18.0705,1.4605,5.4437,1.3035,6.8451,7.9466,15185.27,3.4891,80.805,1326.65,20.7845,4.5113,1.6235,56.16,1.4066,37.18,17.087,
2022-07-25,1.0236,139.84,1.9558,N/A,24.535,7.4449,N/A,0.84813,396.5,N/A,N/A,N/A,4.708,N/A,4.9339,10.3973,N/A,N/A,0.9869,139.5,10.0704,7.5195,N/A,N/A,18.2653,1.4707,5.5976,1.3168,6.9094,8.0345,15303.87,3.5201,81.6675,1341.25,20.9376,4.5586,1.6325,57.209,1.4176,37.525,17.1502,
2022-07-22,1.019,139.51,1.9558,N/A,24.514,7.4443,N/A,0.85141,398.3,N/A,N/A,N/A,4.7508,N/A,4.9321,10.4328,N/A,N/A,0.9832,139.5,10.1498,7.5234,N/A,N/A,18.094,1.4677,5.5821,1.3105,6.8852,7.9985,15275.69,3.5083,81.384,1335.66,20.9595,4.5366,1.6265,57.24,1.4151,37.392,17.2009,
2022-07-21,1.0199,141.46,1.9558,N/A,24.496,7.4446,N/A,0.85545,400.13,N/A,N/A,N/A,4.761,N/A,4.9391,10.426,N/A,N/A,0.9924,139.7,10.175,7.52,N/A,N/A,18.0327,1.4848,5.5777,1.3178,6.904,8.0056,15342.25,3.5203,81.451,1337.47,20.941,4.5457,1.6479,57.512,1.4218,37.66,17.5195,
2022-07-20,1.0199,140.92,1.9558,N/A,24.493,7.4452,N/A,0.85178,399.5,N/A,N/A,N/A,4.782,N/A,4.9396,10.4606,N/A,N/A,0.9896,139.5,10.1323,7.5143,N/A,N/A,17.9444,1.4767,5.5427,1.3132,6.8892,8.0062,15275.82,3.5147,81.599,1337.61,20.8967,4.5406,1.6308,57.398,1.4204,37.405,17.3924,
2022-07-19,1.0245,141.01,1.9558,N/A,24.555,7.4449,N/A,0.85303,397.45,N/A,N/A,N/A,4.7598,N/A,4.9395,10.4964,N/A,N/A,0.9918,138.9,10.176,7.5093,N/A,N/A,18.018,1.4869,5.5454,1.3264,6.9064,8.0423,15344.49,3.5295,81.898,1340.33,20.8552,4.559,1.6456,57.583,1.4269,37.492,17.457,
2022-07-18,1.0131,140.16,1.9558,N/A,24.508,7.4435,N/A,0.84708,402.05,N/A,N/A,N/A,4.776,N/A,4.9389,10.5265,N/A,N/A,0.9911,138.9,10.2553,7.513,N/A,N/A,17.7225,1.4839,5.4505,1.3151,6.8266,7.9528,15157.63,3.5004,81.034,1333.33,20.7095,4.5113,1.6432,57.056,1.4153,37.13,17.383,
2022-07-15,1.0059,139.49,1.9558,N/A,24.561,7.443,N/A,0.84988,403.73,N/A,N/A,N/A,4.7953,N/A,4.9407,10.5943,N/A,N/A,0.9849,138.9,10.2763,7.516,N/A,N/A,17.5451,1.4886,5.4434,1.3147,6.7943,7.8963,15081.58,3.5044,80.316,1333.79,20.9209,4.4752,1.6377,56.678,1.4113,36.866,17.2875,
2022-07-14,1.0005,139.04,1.9558,N/A,24.417,7.4425,N/A,0.8456,408.78,N/A,N/A,N/A,4.8146,N/A,4.942,10.6019,N/A,N/A,0.9841,138.89,10.2536,7.5122,N/A,N/A,17.494,1.4893,5.4586,1.3162,6.7618,7.8539,15119.56,3.4915,80.0752,1322.29,20.9585,4.4462,1.6437,56.565,1.4065,36.632,17.202,
2022-07-13,1.0067,138.02,1.9558,N/A,24.397,7.4416,N/A,0.84371,409.35,N/A,N/A,N/A,4.824,N/A,4.9414,10.602,N/A,N/A,0.9829,138.3,10.2428,7.5155,N/A,N/A,17.5629,1.4802,5.4533,1.3073,6.7722,7.9025,15117.08,3.4864,80.1285,1311.4,20.9029,4.4667,1.635,56.669,1.4134,36.377,17.0527,
2022-07-12,1.0042,137.31,1.9558,N/A,24.582,7.4408,N/A,0.84823,409.98,N/A,N/A,N/A,4.819,N/A,4.9413,10.629,N/A,N/A,0.9883,139.1,10.2754,7.517,N/A,N/A,17.4392,1.49,5.4009,1.3094,6.7518,7.8828,15054.05,3.4987,79.8965,1315.1,20.8883,4.4556,1.6395,56.648,1.4127,36.392,17.1509,
2022-07-11,1.0098,138.77,1.9558,N/A,24.592,7.4414,N/A,0.8454,408.22,N/A,N/A,N/A,4.7968,N/A,4.943,10.6943,N/A,N/A,0.9908,139.3,10.299,7.518,N/A,N/A,17.5447,1.491,5.3673,1.314,6.7793,7.9265,15131.92,3.5108,80.2435,1320.36,20.7845,4.4704,1.6466,56.574,1.4178,36.585,17.1634,
2022-07-08,1.0163,138.05,1.9558,N/A,24.614,7.4424,N/A,0.84585,402.45,N/A,N/A,N/A,4.763,N/A,4.9431,10.6665,N/A,N/A,0.9913,139.5,10.263,7.519,N/A,N/A,17.6026,1.4871,5.4345,1.3201,6.8095,7.9769,15210.73,3.5325,80.528,1321.61,20.8477,4.4992,1.6464,56.882,1.4228,36.602,17.1922,
2022-07-07,1.018,138.11,1.9558,N/A,24.779,7.4405,N/A,0.85105,410.04,N/A,N/A,N/A,4.7721,N/A,4.9448,10.723,N/A,N/A,0.9906,139.3,10.291,7.5193,N/A,N/A,17.5551,1.4883,5.4983,1.3227,6.823,7.9893,15265.27,3.5548,80.6,1324.66,20.9675,4.5077,1.6461,56.939,1.4255,36.74,17.0372,
2022-07-06,1.0177,137.71,1.9558,N/A,24.778,7.4403,N/A,0.85676,411.8,N/A,N/A,N/A,4.771,N/A,4.944,10.745,N/A,N/A,0.9896,138.5,10.2803,7.5198,N/A,N/A,17.5505,1.4961,5.5116,1.3274,6.8289,7.9864,15287.49,3.5884,80.5321,1331.69,21.0194,4.5028,1.6505,56.779,1.4305,36.78,17.0246,
2022-07-05,1.029,139.77,1.9558,N/A,24.751,7.4396,N/A,0.85845,407.38,N/A,N/A,N/A,4.7448,N/A,4.9438,10.8031,N/A,N/A,0.9932,139.1,10.285,7.5246,N/A,N/A,17.5049,1.518,5.5141,1.3364,6.9029,8.0748,15487.93,3.6343,81.673,1348.97,21.0171,4.5477,1.6772,57.009,1.4455,36.879,16.9143,
2022-07-04,1.0455,141.51,1.9558,N/A,24.745,7.4391,N/A,0.8596,401.52,N/A,N/A,N/A,4.71,N/A,4.944,10.7658,N/A,N/A,1.0037,139.3,10.2958,7.5301,N/A,N/A,17.5994,1.5205,5.5663,1.3435,6.9977,8.2033,15684.13,3.6655,82.5067,1353.4,21.1972,4.6138,1.6748,57.487,1.4587,37.298,17.0275,
2022-07-01,1.0425,141.05,1.9558,N/A,24.753,7.4391,N/A,0.86648,401.11,N/A,N/A,N/A,4.7168,N/A,4.9457,10.7783,N/A,N/A,1.0027,138.7,10.3651,7.531,N/A,N/A,17.4608,1.5382,5.5117,1.3492,6.987,8.1801,15621.64,3.6717,82.3747,1352.58,21.115,4.5943,1.6929,57.452,1.4565,37.186,17.1323,
2022-06-30,1.0387,141.54,1.9558,N/A,24.739,7.4392,N/A,0.8582,397.04,N/A,N/A,N/A,4.6904,N/A,4.9464,10.73,N/A,N/A,0.996,138.9,10.3485,7.5307,N/A,N/A,17.322,1.5099,5.4229,1.3425,6.9624,8.1493,15552,3.6392,82.113,1351.6,20.9641,4.5781,1.6705,57.15,1.4483,36.754,17.0143,
2022-06-29,1.0517,143.53,1.9558,N/A,24.739,7.4392,N/A,0.86461,394.28,N/A,N/A,N/A,4.6869,N/A,4.9419,10.6848,N/A,N/A,1.0005,139.9,10.3065,7.5285,N/A,N/A,17.4998,1.5256,5.5163,1.3513,7.0382,8.2532,15612.61,3.6344,83.037,1364.02,21.1375,4.6272,1.6871,57.773,1.4607,36.925,16.9295,
2022-06-28,1.0561,143.67,1.9558,N/A,24.726,7.4394,N/A,0.8635,398.55,N/A,N/A,N/A,4.6905,N/A,4.9443,10.6543,N/A,N/A,1.0101,139.5,10.337,7.532,N/A,N/A,17.5891,1.521,5.5308,1.3565,7.0775,8.288,15669.91,3.6267,83.408,1361.75,21.088,4.6432,1.6822,57.85,1.4645,37.154,16.9072,
2022-06-27,1.0572,143.25,1.9558,N/A,24.724,7.4408,N/A,0.862,402.62,N/A,N/A,N/A,4.699,N/A,4.944,10.6713,N/A,N/A,1.0143,139.7,10.408,7.5333,N/A,N/A,17.4794,1.5278,5.5446,1.3639,7.0737,8.2946,15635.36,3.6009,82.9325,1357.68,21.041,4.6559,1.6801,57.9,1.4641,37.361,16.7967,
2022-06-24,1.0524,142.19,1.9558,N/A,24.731,7.4398,N/A,0.85773,401.34,N/A,N/A,N/A,4.7023,N/A,4.9463,10.694,N/A,N/A,1.0072,139.7,10.4345,7.5295,N/A,N/A,18.2856,1.5248,5.4851,1.3657,7.0478,8.2609,15633.96,3.621,82.3985,1364.09,20.9901,4.6327,1.6731,57.83,1.462,37.36,16.7137,
2022-06-23,1.0493,142.11,1.9558,N/A,24.75,7.4388,N/A,0.85818,399.6,N/A,N/A,N/A,4.7085,N/A,4.9468,10.705,N/A,N/A,1.013,139.9,10.475,7.5286,N/A,N/A,18.2199,1.5212,5.4515,1.36,7.0367,8.236,15594.75,3.6192,82.1489,1367.21,21.0924,4.623,1.6713,57.44,1.4589,37.261,16.817,
2022-06-22,1.0521,143.11,1.9558,N/A,24.712,7.4387,N/A,0.85885,396,N/A,N/A,N/A,4.6905,N/A,4.9467,10.6688,N/A,N/A,1.0153,138.7,10.5045,7.5228,N/A,N/A,18.255,1.5254,5.4349,1.366,7.0604,8.2589,15618.98,3.6432,82.4075,1369.29,21.1491,4.6345,1.6835,57.174,1.4615,37.281,16.7985,
2022-06-21,1.055,143.75,1.9558,N/A,24.69,7.4393,N/A,0.8601,396.48,N/A,N/A,N/A,4.6435,N/A,4.9462,10.646,N/A,N/A,1.0214,138.7,10.3283,7.5205,N/A,N/A,18.3049,1.5177,5.442,1.366,7.068,8.2817,15639.62,3.6505,82.423,1365.09,21.2485,4.6399,1.6675,57.304,1.4612,37.294,16.7881,
2022-06-20,1.0517,141.94,1.9558,N/A,24.728,7.4387,N/A,0.85748,397.85,N/A,N/A,N/A,4.652,N/A,4.9453,10.6375,N/A,N/A,1.0162,137.3,10.4085,7.5175,N/A,N/A,18.2239,1.5061,5.4117,1.3662,7.0346,8.2558,15589.89,3.639,81.994,1357.54,21.3016,4.6291,1.6549,56.872,1.4589,37.157,16.8603,
2022-06-17,1.0486,141.21,1.9558,N/A,24.742,7.4384,N/A,0.855,400.53,N/A,N/A,N/A,4.7003,N/A,4.9469,10.6748,N/A,N/A,1.0105,137.7,10.4525,7.5155,N/A,N/A,18.1495,1.5039,5.3824,1.3631,7.0308,8.2314,15537.05,3.6112,81.871,1356.27,21.4474,4.6159,1.6601,56.371,1.4547,36.974,16.7133,
2022-06-16,1.04,138.24,1.9558,N/A,24.742,7.4386,N/A,0.8555,398.1,N/A,N/A,N/A,4.7138,N/A,4.9443,10.6942,N/A,N/A,1.0142,137.5,10.4588,7.5245,N/A,N/A,18.0126,1.4939,5.2559,1.3446,6.9844,8.1638,15427.31,3.5934,81.1945,1346.88,21.4115,4.5786,1.6608,55.7,1.4451,36.566,16.6052,
2022-06-15,1.0431,140.49,1.9558,N/A,24.703,7.4392,N/A,0.86328,397.96,N/A,N/A,N/A,4.669,N/A,4.9427,10.6278,N/A,N/A,1.0435,137.5,10.3868,7.5245,N/A,N/A,18.0465,1.5051,5.3164,1.3498,7.0013,8.1883,15361.97,3.6007,81.5142,1346.86,21.4763,4.6037,1.6706,55.627,1.4519,36.529,16.7111,
2022-06-14,1.0452,140.62,1.9558,N/A,24.749,7.4403,N/A,0.86578,398.68,N/A,N/A,N/A,4.6563,N/A,4.9443,10.622,N/A,N/A,1.0394,138.3,10.3945,7.5238,N/A,N/A,18.06,1.5174,5.3329,1.3522,7.0417,8.2048,15400.02,3.6208,81.559,1346.72,21.4832,4.6224,1.6755,55.669,1.4541,36.566,16.7959,
2022-06-13,1.0455,140.51,1.9558,N/A,24.724,7.4397,N/A,0.8585,399.3,N/A,N/A,N/A,4.6373,N/A,4.9459,10.616,N/A,N/A,1.0375,138.7,10.3222,7.5215,N/A,N/A,18.0495,1.4998,5.2785,1.3435,7.0434,8.2071,15376.17,3.5994,81.606,1349.93,21.2102,4.6195,1.6635,55.72,1.4538,36.425,16.807,
2022-06-10,1.0578,141.69,1.9558,N/A,24.705,7.4389,N/A,0.85048,398.48,N/A,N/A,N/A,4.6053,N/A,4.9442,10.5255,N/A,N/A,1.0404,137.7,10.1495,7.5225,N/A,N/A,18.0116,1.4845,5.1718,1.3484,7.0868,8.3031,15393.27,3.5626,82.3355,1344.25,20.8285,4.6564,1.6482,56.101,1.462,36.774,16.5209,
2022-06-09,1.0743,143.93,1.9558,N/A,24.689,7.4391,N/A,0.85653,396.45,N/A,N/A,N/A,4.5925,N/A,4.9453,10.5045,N/A,N/A,1.0495,138.7,10.1818,7.5223,N/A,N/A,18.5104,1.4985,5.2506,1.3506,7.1722,8.4317,15646.25,3.5859,83.526,1348.99,21.0248,4.7199,1.6673,56.872,1.4779,37.079,16.4132,
2022-06-08,1.0739,143.92,1.9558,N/A,24.622,7.4386,N/A,0.85575,391.25,N/A,N/A,N/A,4.5698,N/A,4.945,10.4938,N/A,N/A,1.0486,138.9,10.1395,7.5215,N/A,N/A,18.453,1.4917,5.2447,1.3467,7.1785,8.4275,15577.86,3.5848,83.414,1349.34,21.0458,4.7187,1.6644,56.799,1.4769,37.076,16.4626,
2022-06-07,1.0662,141.66,1.9558,N/A,24.739,7.4395,N/A,0.85365,389.33,N/A,N/A,N/A,4.5813,N/A,4.9426,10.5039,N/A,N/A,1.0423,138.9,10.1843,7.5244,N/A,N/A,17.8702,1.4884,5.1256,1.3437,7.1146,8.3656,15412.37,3.5661,82.873,1340.75,20.8435,4.6865,1.6582,56.421,1.4685,36.768,16.4059,
2022-06-06,1.0726,140.16,1.9558,N/A,24.715,7.439,N/A,0.85415,388.05,N/A,N/A,N/A,4.5808,N/A,4.9424,10.452,N/A,N/A,1.032,138.3,10.0853,7.5222,N/A,N/A,17.796,1.4842,5.0986,1.3463,7.1223,8.4154,15464.73,3.5697,83.245,1341.13,20.9078,4.7076,1.6428,56.67,1.4732,36.774,16.4142,
2022-06-03,1.073,139.59,1.9558,N/A,24.708,7.4388,N/A,0.8542,394.78,N/A,N/A,N/A,4.5955,N/A,4.9428,10.4589,N/A,N/A,1.0296,137.9,10.103,7.522,N/A,N/A,17.738,1.4805,5.1643,1.3484,7.1465,8.4167,15498.1,3.5751,83.273,1337.4,20.985,4.7094,1.6409,56.738,1.4741,36.777,16.6153,
2022-06-02,1.0692,138.72,1.9558,N/A,24.702,7.4391,N/A,0.85195,394.9,N/A,N/A,N/A,4.5787,N/A,4.9398,10.4705,N/A,N/A,1.0264,136.9,10.0845,7.5325,N/A,N/A,17.6175,1.4829,5.1335,1.352,7.135,8.3896,15481.86,3.5705,82.922,1334.06,20.9831,4.6949,1.6413,56.467,1.4701,36.754,16.6143,
2022-06-01,1.0712,138.68,1.9558,N/A,24.748,7.4393,N/A,0.85158,395.03,N/A,N/A,N/A,4.5913,N/A,4.9428,10.4758,N/A,N/A,1.0305,137.1,10.0438,7.5345,N/A,N/A,17.6223,1.4861,5.0646,1.3536,7.1586,8.4057,15574.33,3.5682,83.051,1331.44,21.0678,4.6951,1.6442,56.182,1.47,36.801,16.609,
2022-05-31,1.0713,137.36,1.9558,N/A,24.714,7.4394,N/A,0.85138,396.2,N/A,N/A,N/A,4.5805,N/A,4.9408,10.5053,N/A,N/A,1.0281,136.3,10.0983,7.541,N/A,N/A,17.5817,1.4933,5.0965,1.3573,7.1402,8.4063,15580.15,3.5746,83.231,1329.32,20.987,4.6907,1.6459,56.323,1.4687,36.751,16.745,
2022-05-30,1.0764,137.25,1.9558,N/A,24.712,7.4391,N/A,0.8515,392.18,N/A,N/A,N/A,4.5855,N/A,4.9441,10.518,N/A,N/A,1.0327,136.9,10.1256,7.5305,N/A,N/A,17.6416,1.4982,5.0629,1.3647,7.1735,8.449,15682.41,3.5722,83.475,1331.66,20.8994,4.6998,1.6439,56.322,1.4719,36.7,16.648,
2022-05-27,1.0722,136.05,1.9558,N/A,24.7,7.4392,N/A,0.84875,392.83,N/A,N/A,N/A,4.5858,N/A,4.9427,10.5293,N/A,N/A,1.0258,137.9,10.179,7.5379,N/A,N/A,17.582,1.4995,5.0959,1.3661,7.1831,8.4165,15583.97,3.597,83.1915,1343.63,21.136,4.6952,1.6426,56.02,1.4679,36.589,16.746,
2022-05-26,1.0697,135.95,1.9558,N/A,24.676,7.4409,N/A,0.85073,391.72,N/A,N/A,N/A,4.6083,N/A,4.9423,10.5983,N/A,N/A,1.0283,138.1,10.2715,7.5355,N/A,N/A,17.5588,1.511,5.1741,1.3715,7.2024,8.397,15628.91,3.5935,83.0065,1352.69,21.1935,4.7045,1.6541,55.975,1.4709,36.589,16.9312,
2022-05-25,1.0656,135.34,1.9558,N/A,24.648,7.4405,N/A,0.85295,388.25,N/A,N/A,N/A,4.621,N/A,4.9416,10.5419,N/A,N/A,1.0269,138.3,10.2704,7.5355,N/A,N/A,17.3954,1.5126,5.1736,1.372,7.1334,8.3647,15587.59,3.585,82.6666,1354.61,21.2213,4.6833,1.6539,55.787,1.4676,36.55,16.7628,
2022-05-24,1.072,136.49,1.9558,N/A,24.663,7.4411,N/A,0.8575,383.33,N/A,N/A,N/A,4.6015,N/A,4.9446,10.5013,N/A,N/A,1.0334,139.3,10.289,7.5285,N/A,N/A,17.2572,1.5152,5.1793,1.3714,7.1449,8.4143,15711.88,3.5848,83.185,1353.65,21.2456,4.7076,1.6656,56.152,1.4722,36.609,16.7814,
2022-05-23,1.0659,136.05,1.9558,N/A,24.594,7.4413,N/A,0.84783,381.65,N/A,N/A,N/A,4.621,N/A,4.947,10.4918,N/A,N/A,1.031,139.1,10.252,7.5275,N/A,N/A,16.8672,1.4982,5.1623,1.3626,7.085,8.3664,15609,3.5745,82.6795,1344.19,21.1273,4.6782,1.6463,55.686,1.4639,36.41,16.7437,
2022-05-20,1.0577,135.34,1.9558,N/A,24.67,7.4424,N/A,0.8482,382.93,N/A,N/A,N/A,4.6365,N/A,4.9477,10.4915,N/A,N/A,1.028,138.5,10.262,7.5335,N/A,N/A,16.8201,1.498,5.1989,1.3526,7.0638,8.2999,15501.99,3.533,82.1617,1340.58,21.0314,4.6422,1.6518,55.181,1.4588,36.284,16.7131,
2022-05-19,1.0525,134.46,1.9558,N/A,24.7,7.4423,N/A,0.84728,385.83,N/A,N/A,N/A,4.6423,N/A,4.9474,10.5098,N/A,N/A,1.0265,139.5,10.3102,7.5395,N/A,N/A,16.8037,1.5036,5.2094,1.349,7.1028,8.2594,15416.76,3.5623,81.7115,1343.21,21.0043,4.6363,1.6551,55.14,1.4576,36.343,16.8315,
2022-05-18,1.0523,135.76,1.9558,N/A,24.647,7.4419,N/A,0.8467,382.88,N/A,N/A,N/A,4.6443,N/A,4.9473,10.4675,N/A,N/A,1.0486,138.9,10.2125,7.535,N/A,N/A,16.7811,1.498,5.1974,1.3488,7.0972,8.2591,15446,3.526,81.6455,1332.76,20.9204,4.6254,1.6548,55.077,1.4598,36.399,16.7313,
2022-05-17,1.0541,136.32,1.9558,N/A,24.712,7.4414,N/A,0.844,386.3,N/A,N/A,N/A,4.6488,N/A,4.9478,10.4393,N/A,N/A,1.0457,138.7,10.178,7.5245,N/A,N/A,16.6027,1.4993,5.2621,1.3517,7.0899,8.2744,15433.23,3.5387,81.6515,1333.66,21.0273,4.6211,1.6561,55.137,1.4589,36.361,16.844,
2022-05-16,1.0422,135.01,1.9558,N/A,24.71,7.4418,N/A,0.85045,385.85,N/A,N/A,N/A,4.6675,N/A,4.9469,10.4978,N/A,N/A,1.0479,138.3,10.2188,7.5225,N/A,N/A,16.3121,1.5057,5.2819,1.3473,7.0786,8.1812,15294.29,3.5474,81.081,1337.9,20.9324,4.5836,1.6601,54.705,1.4531,36.274,16.9195,
2022-05-13,1.0385,133.91,1.9558,N/A,24.74,7.4412,N/A,0.85115,385,N/A,N/A,N/A,4.6883,N/A,4.9455,10.4905,N/A,N/A,1.0385,140.1,10.2043,7.52,N/A,N/A,16.0687,1.5067,5.3204,1.3505,7.0513,8.1522,15193.55,3.5586,80.4315,1330.83,20.988,4.5673,1.6633,54.449,1.45,36.109,16.7789,
2022-05-12,1.0408,133.85,1.9558,N/A,24.925,7.4413,N/A,0.85293,382.2,N/A,N/A,N/A,4.668,N/A,4.947,10.5648,N/A,N/A,1.0377,139.7,10.2898,7.5235,N/A,N/A,16.0132,1.5163,5.4161,1.3569,7.0691,8.1702,15255.73,3.5981,80.667,1341.98,21.2531,4.5725,1.6692,54.589,1.4529,36.15,16.8806,
2022-05-11,1.0553,137.07,1.9558,N/A,25.365,7.4393,N/A,0.85393,379.13,N/A,N/A,N/A,4.6575,N/A,4.947,10.526,N/A,N/A,1.0446,139.3,10.1793,7.5365,N/A,N/A,16.1851,1.5055,5.3859,1.3685,7.0893,8.2839,15308.87,3.6148,81.4935,1343.99,21.387,4.6185,1.6645,54.992,1.4622,36.492,16.9275,
2022-05-10,1.0554,137.38,1.9558,N/A,25.014,7.4386,N/A,0.85595,380.15,N/A,N/A,N/A,4.6763,N/A,4.9458,10.6075,N/A,N/A,1.0479,139.5,10.2315,7.5385,N/A,N/A,16.0883,1.5162,5.4232,1.3707,7.0967,8.2847,15349.06,3.6587,81.5425,1346.56,21.4716,4.6248,1.6707,55.289,1.4667,36.448,17.005,
2022-05-09,1.0559,138.1,1.9558,N/A,25.055,7.4385,N/A,0.85235,383.23,N/A,N/A,N/A,4.6985,N/A,4.9467,10.5818,N/A,N/A,1.0462,139.5,10.0583,7.5335,N/A,N/A,15.8941,1.5048,5.4321,1.3656,7.0886,8.2887,15367.67,3.6239,81.7415,1345.48,21.4341,4.6285,1.6584,55.67,1.4676,36.497,17.1332,
2022-05-06,1.057,137.9,1.9558,N/A,24.665,7.44,N/A,0.85625,381.47,N/A,N/A,N/A,4.7028,N/A,4.949,10.4686,N/A,N/A,1.0419,138.3,9.9808,7.5336,N/A,N/A,15.8078,1.4888,5.3183,1.356,7.0506,8.2969,15312.44,3.5965,81.298,1343.9,21.3555,4.6191,1.644,55.467,1.4642,36.303,16.9614,
2022-05-05,1.0568,137.18,1.9558,N/A,24.606,7.4405,N/A,0.8519,378.65,N/A,N/A,N/A,4.6673,N/A,4.9485,10.3748,N/A,N/A,1.0355,138,9.8463,7.5398,N/A,N/A,15.7051,1.4669,5.2192,1.3483,6.9944,8.2948,15250.92,3.5916,80.6185,1330.89,21.2069,4.5955,1.6273,55.364,1.4561,36.026,16.5862,
2022-05-04,1.0531,136.84,1.9558,N/A,24.644,7.4409,N/A,0.84194,377.55,N/A,N/A,N/A,4.6875,N/A,4.947,10.3968,N/A,N/A,1.0324,137.2,9.9042,7.5499,N/A,N/A,15.5769,1.478,5.24,1.3498,6.9594,8.2655,15201.44,3.5487,80.4035,1331.45,21.2947,4.5847,1.6333,55.26,1.4559,36.137,16.6489,
2022-05-03,1.0556,137.06,1.9558,N/A,24.662,7.4403,N/A,0.8413,382.15,N/A,N/A,N/A,4.6925,N/A,4.9475,10.3978,N/A,N/A,1.0272,137.6,9.909,7.5555,N/A,N/A,15.6941,1.4825,5.3143,1.357,6.9759,8.2838,15288.47,3.5597,80.842,1335.64,21.5025,4.5956,1.6366,55.455,1.4605,36.387,16.8303,
2022-05-02,1.0524,136.63,1.9558,N/A,24.671,7.4391,N/A,0.8381,378.51,N/A,N/A,N/A,4.685,N/A,4.9478,10.4035,N/A,N/A,1.0253,137.2,9.9248,7.561,N/A,N/A,15.6697,1.4913,5.248,1.356,6.9548,8.2581,15293.41,3.5277,80.497,1333.71,21.4758,4.5816,1.6362,55.229,1.4585,36.208,16.7383,
2022-04-29,1.054,137.01,1.9558,N/A,24.605,7.4415,N/A,0.83908,378.71,N/A,N/A,N/A,4.678,N/A,4.9479,10.2958,N/A,N/A,1.0229,137.8,9.7525,7.5667,N/A,N/A,15.6385,1.4699,5.1608,1.3426,6.9441,8.2703,15301.52,3.4993,80.638,1326.71,21.4181,4.5886,1.6119,55.2,1.4545,36.026,16.6473,
2022-04-28,1.0485,137.13,1.9558,N/A,24.526,7.4421,N/A,0.8435,377.06,N/A,N/A,N/A,4.6891,N/A,4.9479,10.3594,N/A,N/A,1.0216,137.8,9.899,7.5703,N/A,N/A,15.5362,1.4814,5.2465,1.3498,6.9381,8.2267,15222.05,3.5096,80.367,1337.82,21.4531,4.5741,1.6221,54.845,1.4556,36.152,16.7472,
2022-04-27,1.0583,135.57,1.9558,N/A,24.55,7.441,N/A,0.84215,379.74,N/A,N/A,N/A,4.7043,N/A,4.948,10.4035,N/A,N/A,1.0229,138.2,9.7838,7.565,N/A,N/A,15.6857,1.4828,5.3045,1.3572,6.9377,8.3045,15259.86,3.5178,81.0705,1341.98,21.6259,4.6142,1.6118,55.195,1.4602,36.331,16.8406,
2022-04-26,1.0674,136.15,1.9558,N/A,24.423,7.4393,N/A,0.84135,374.46,N/A,N/A,N/A,4.6466,N/A,4.9458,10.3935,N/A,N/A,1.0229,138.4,9.7943,7.5625,N/A,N/A,15.7944,1.4828,5.249,1.3613,6.9837,8.3735,15365.76,3.5147,81.7265,1340.02,21.6538,4.6485,1.6102,55.681,1.4666,36.596,16.7787,
2022-04-25,1.0746,137.73,1.9558,N/A,24.418,7.4391,N/A,0.8433,374.08,N/A,N/A,N/A,4.6398,N/A,4.9455,10.3476,N/A,N/A,1.0267,139.2,9.7018,7.562,N/A,N/A,15.864,1.4972,5.1953,1.3709,7.0398,8.4325,15533.84,3.5306,82.321,1344.49,21.8989,4.6815,1.624,56.259,1.4757,36.542,16.8549,
2022-04-22,1.0817,138.83,1.9558,N/A,24.32,7.4402,N/A,0.83925,370.35,N/A,N/A,N/A,4.6336,N/A,4.9455,10.278,N/A,N/A,1.0336,139.8,9.6255,7.5625,N/A,N/A,15.9446,1.4816,5.0926,1.3714,7.0332,8.4859,15603.47,3.5288,82.6943,1344.04,22.0034,4.6784,1.6193,56.721,1.4784,36.724,16.8652,
2022-04-21,1.0887,139.61,1.9558,N/A,24.38,7.4403,N/A,0.83523,370.6,N/A,N/A,N/A,4.63,N/A,4.945,10.2553,N/A,N/A,1.0335,139,9.5788,7.5635,N/A,N/A,15.9983,1.4653,5.0324,1.36,7.0228,8.5406,15624.02,3.5153,82.965,1348.33,21.8836,4.6716,1.6053,57.081,1.482,36.891,16.5996,
2022-04-20,1.083,138.53,1.9558,N/A,24.409,7.4405,N/A,0.82965,371.36,N/A,N/A,N/A,4.6338,N/A,4.9436,10.23,N/A,N/A,1.0254,139.2,9.5443,7.561,N/A,N/A,15.8892,1.4581,5.0481,1.3579,6.9448,8.494,15537.05,3.4908,82.6348,1337.89,21.6392,4.6415,1.595,56.747,1.4779,36.567,16.3019,
2022-04-19,1.0803,138.4,1.9558,N/A,24.424,7.4391,N/A,0.82955,374.12,N/A,N/A,N/A,4.6553,N/A,4.9411,10.3408,N/A,N/A,1.0208,139.8,9.5228,7.562,N/A,N/A,15.8416,1.4663,5.0261,1.3631,6.9008,8.4698,15498.35,3.5038,82.6038,1339.46,21.4725,4.5961,1.6016,56.683,1.4763,36.466,16.0401,
2022-04-14,1.0878,136.32,1.9558,N/A,24.42,7.4389,N/A,0.82908,376.57,N/A,N/A,N/A,4.6478,N/A,4.9459,10.3008,N/A,N/A,1.0189,140.4,9.5313,7.5587,N/A,N/A,15.9046,1.4612,5.1226,1.3663,6.932,8.5298,15621.3,3.4896,82.814,1334.71,21.5941,4.603,1.5957,56.759,1.4732,36.615,15.9331,
2022-04-13,1.0826,136.26,1.9558,N/A,24.45,7.4377,N/A,0.8328,378.45,N/A,N/A,N/A,4.6453,N/A,4.9415,10.3323,N/A,N/A,1.0116,140.2,9.5693,7.5538,N/A,N/A,15.7992,1.4603,5.0449,1.37,6.8939,8.4867,15549.1,3.4782,82.478,1328.47,21.417,4.5799,1.5991,56.446,1.4769,36.305,15.682,
2022-04-12,1.0861,136.29,1.9558,N/A,24.45,7.4379,N/A,0.83455,377.78,N/A,N/A,N/A,4.6552,N/A,4.9417,10.332,N/A,N/A,1.0131,139.6,9.5395,7.5513,N/A,N/A,15.9548,1.4599,5.0944,1.3724,6.9199,8.5112,15609.66,3.4972,82.7285,1335.49,21.5616,4.5972,1.5874,56.574,1.4803,36.531,15.849,
2022-04-11,1.09,137.01,1.9558,N/A,24.429,7.4375,N/A,0.83693,378.27,N/A,N/A,N/A,4.6456,N/A,4.9397,10.3128,N/A,N/A,1.018,140,9.5478,7.5519,N/A,N/A,16.0485,1.4654,5.155,1.3738,6.9405,8.544,15658.28,3.501,82.7085,1345.23,21.8653,4.6112,1.5938,56.753,1.4874,36.613,15.9127,
2022-04-08,1.0861,134.87,1.9558,N/A,24.479,7.4372,N/A,0.83355,375.66,N/A,N/A,N/A,4.6437,N/A,4.9425,10.2768,N/A,N/A,1.0155,139.6,9.508,7.549,N/A,N/A,16.0237,1.4552,5.1583,1.3675,6.9115,8.5134,15601.96,3.501,82.389,1333.12,21.8729,4.585,1.5849,55.99,1.4801,36.488,15.9968,
2022-04-07,1.0916,135.32,1.9558,N/A,24.512,7.4378,N/A,0.8345,379.26,N/A,N/A,N/A,4.637,N/A,4.9419,10.313,N/A,N/A,1.0185,141,9.5595,7.5562,N/A,N/A,16.0929,1.4578,5.146,1.3704,6.9448,8.5554,15692.35,3.5259,82.951,1330.92,21.9806,4.6046,1.5816,56.114,1.4848,36.541,16.052,
2022-04-06,1.0923,135.3,1.9558,N/A,24.441,7.4378,N/A,0.83473,377.77,N/A,N/A,N/A,4.6328,N/A,4.9433,10.2855,N/A,N/A,1.0187,141.4,9.5523,7.547,N/A,N/A,16.0998,1.4431,5.0996,1.3647,6.9498,8.5617,15683.36,3.5199,82.8343,1330.44,21.8759,4.604,1.5718,56.167,1.4844,36.701,15.9934,
2022-04-05,1.0969,134.76,1.9558,N/A,24.338,7.4378,N/A,0.8349,370.93,N/A,N/A,N/A,4.6265,N/A,4.9438,10.2593,N/A,N/A,1.0141,141.6,9.5398,7.5399,N/A,N/A,16.15,1.4374,5.0384,1.3647,6.9783,8.5917,15732.77,3.5152,82.635,1330.81,21.7474,4.6185,1.5657,56.194,1.4867,36.697,15.9529,
2022-04-04,1.1005,135.08,1.9558,N/A,24.32,7.4385,N/A,0.8389,369.15,N/A,N/A,N/A,4.6375,N/A,4.9432,10.3849,N/A,N/A,1.0203,141.8,9.5489,7.5455,N/A,N/A,16.183,1.4651,5.1162,1.3749,7.0026,8.6226,15783.89,3.5312,83.118,1338.41,21.82,4.643,1.586,56.521,1.4938,36.894,16.0957,
2022-04-01,1.1052,135.35,1.9558,N/A,24.376,7.4388,N/A,0.84145,368.12,N/A,N/A,N/A,4.6401,N/A,4.9452,10.332,N/A,N/A,1.0217,142,9.6628,7.5675,N/A,N/A,16.2411,1.4696,5.2188,1.3805,7.0311,8.6596,15887.5,3.5315,83.9847,1345.61,21.9087,4.6534,1.5911,57.084,1.4985,36.941,16.1685,
2022-03-31,1.1101,135.17,1.9558,N/A,24.375,7.4379,N/A,0.84595,369.77,N/A,N/A,N/A,4.6531,N/A,4.9463,10.337,N/A,N/A,1.0267,142,9.711,7.574,N/A,N/A,16.2823,1.4829,5.3009,1.3896,7.0403,8.6918,15947,3.5243,84.134,1347.37,22.0903,4.6677,1.6014,57.514,1.5028,36.911,16.1727,
2022-03-30,1.1126,135.47,1.9558,N/A,24.45,7.4391,N/A,0.84563,368.13,N/A,N/A,N/A,4.6679,N/A,4.9477,10.3498,N/A,N/A,1.0309,142.2,9.6398,7.572,N/A,N/A,16.3296,1.4809,5.2808,1.3891,7.0666,8.7081,15957.24,3.5399,84.38,1346.97,22.1557,4.6779,1.5947,57.906,1.5064,37.144,16.1288,
2022-03-29,1.1085,136.66,1.9558,N/A,24.464,7.4388,N/A,0.8444,369.8,N/A,N/A,N/A,4.6594,N/A,4.9478,10.329,N/A,N/A,1.0362,142.2,9.5995,7.5815,N/A,N/A,16.3275,1.4795,5.2434,1.387,7.055,8.6767,15896.31,3.5505,83.9685,1345.62,22.1561,4.6707,1.6054,57.602,1.5051,37.29,16.1804,
2022-03-28,1.0966,135.93,1.9558,N/A,24.65,7.4393,N/A,0.83643,374.13,N/A,N/A,N/A,4.718,N/A,4.9483,10.4225,N/A,N/A,1.0257,142.8,9.5123,7.5735,N/A,N/A,16.275,1.459,5.2133,1.3702,6.9862,8.5861,15737.77,3.5313,83.4825,1342.49,21.9841,4.6238,1.5838,57.08,1.4921,37.027,15.9925,
2022-03-25,1.1002,134.07,1.9558,N/A,24.645,7.4404,N/A,0.8338,373.81,N/A,N/A,N/A,4.7307,N/A,4.9487,10.3505,N/A,N/A,1.0207,142.2,9.5205,7.5754,N/A,N/A,16.3304,1.4624,5.2634,1.3781,7.0007,8.6117,15777.69,3.5351,83.8235,1343.32,21.9908,4.6324,1.5787,57.322,1.4919,36.906,16.0386,
2022-03-24,1.0978,133.71,1.9558,N/A,24.72,7.4397,N/A,0.83288,374.44,N/A,N/A,N/A,4.7421,N/A,4.9489,10.3555,N/A,N/A,1.0225,141.2,9.4923,7.5745,N/A,N/A,16.2917,1.4668,5.3057,1.3806,6.9933,8.5897,15778.31,3.5461,83.879,1342.53,22.177,4.6396,1.5812,57.426,1.4912,36.837,16.1478,
2022-03-23,1.0985,132.65,1.9558,N/A,24.605,7.4381,N/A,0.8328,372.25,N/A,N/A,N/A,4.7052,N/A,4.9463,10.4005,N/A,N/A,1.0269,141.4,9.6425,7.571,N/A,N/A,16.3108,1.4728,5.3903,1.384,7.0003,8.5948,15792.11,3.5407,83.9675,1337.76,22.1978,4.6401,1.5822,57.565,1.4919,36.965,16.2501,
2022-03-22,1.1024,132.96,1.9558,N/A,24.679,7.4402,N/A,0.83228,371.23,N/A,N/A,N/A,4.6851,N/A,4.9463,10.3822,N/A,N/A,1.0275,142.7,9.6233,7.575,N/A,N/A,16.3432,1.4802,5.4105,1.3867,7.0137,8.6285,15808.01,3.5521,83.9145,1343.81,22.3667,4.6483,1.586,57.749,1.4957,36.881,16.343,
2022-03-21,1.1038,131.57,1.9558,N/A,24.683,7.4411,N/A,0.83775,374.48,N/A,N/A,N/A,4.696,N/A,4.947,10.4088,N/A,N/A,1.0278,142.7,9.6575,7.5733,N/A,N/A,16.3773,1.4897,5.502,1.3898,7.0152,8.6387,15826.61,3.5637,84.1835,1342.62,22.48,4.6415,1.5997,57.817,1.496,37.038,16.4543,
2022-03-18,1.1008,131.4,1.9558,N/A,24.837,7.4423,N/A,0.83925,375.33,N/A,N/A,N/A,4.7135,N/A,4.9483,10.4303,N/A,N/A,1.0314,142.9,9.694,7.5685,N/A,N/A,16.3054,1.4945,5.5784,1.3911,7.0031,8.6101,15782.11,3.5761,83.7825,1337.31,22.5905,4.6157,1.6026,57.622,1.4952,36.745,16.5347,
2022-03-17,1.1051,131.27,1.9558,N/A,24.777,7.4439,N/A,0.84315,372.05,N/A,N/A,N/A,4.6889,N/A,4.9465,10.4503,N/A,N/A,1.0385,142.1,9.78,7.573,N/A,N/A,16.3123,1.5055,5.6339,1.3998,7.0176,8.6391,15835.97,3.5777,83.8435,1340.02,22.788,4.6364,1.613,57.69,1.498,36.767,16.5286,
2022-03-16,1.0994,130.05,1.9558,N/A,24.687,7.4412,N/A,0.83988,371.18,N/A,N/A,N/A,4.6765,N/A,4.9473,10.4205,N/A,N/A,1.0336,143.5,9.7988,7.5725,N/A,N/A,16.1783,1.5165,5.6523,1.3967,6.9817,8.5996,15690.36,3.5872,83.7805,1353.77,22.854,4.6147,1.6168,57.433,1.4966,36.681,16.5574,
2022-03-15,1.0991,129.67,1.9558,N/A,24.867,7.441,N/A,0.84053,371.41,N/A,N/A,N/A,4.7355,N/A,4.9482,10.526,N/A,N/A,1.0322,144.9,9.849,7.575,N/A,N/A,16.0968,1.5234,5.6385,1.4099,7.0117,8.6026,15710.44,3.6088,83.9555,1366.05,22.9352,4.6239,1.6216,57.536,1.4993,36.842,16.6249,
2022-03-14,1.096,129.3,1.9558,N/A,24.89,7.4405,N/A,0.83915,373.88,N/A,N/A,N/A,4.7218,N/A,4.949,10.5368,N/A,N/A,1.0249,145.1,9.8588,7.5745,N/A,N/A,16.2,1.5137,5.5286,1.3978,6.9738,8.5815,15678.53,3.586,83.931,1357.77,22.8311,4.6087,1.613,57.398,1.4947,36.59,16.5029,
2022-03-11,1.099,128.46,1.9558,N/A,25.213,7.4402,N/A,0.8397,380.92,N/A,N/A,N/A,4.782,N/A,4.949,10.646,N/A,N/A,1.023,144.9,9.8033,7.5713,N/A,N/A,16.2554,1.5017,5.5077,1.4024,6.9633,8.6007,15696.64,3.5683,83.9875,1354.04,22.9524,4.6098,1.6053,57.461,1.4949,36.542,16.4896,
2022-03-10,1.1084,128.54,1.9558,N/A,25.316,7.4401,N/A,0.84175,381.63,N/A,N/A,N/A,4.8239,N/A,4.9491,10.7073,N/A,N/A,1.027,145.5,9.919,7.5665,N/A,N/A,16.574,1.5109,5.5958,1.4189,7.0063,8.6688,15824.95,3.6219,84.607,1360.48,23.3153,4.6414,1.6185,57.825,1.5058,36.71,16.7264,
2022-03-09,1.0993,127.31,1.9558,N/A,25.364,7.444,N/A,0.8357,379.66,N/A,N/A,N/A,4.8196,N/A,4.9485,10.734,N/A,N/A,1.0198,145.3,9.798,7.5625,N/A,N/A,16.1323,1.4991,5.5201,1.4108,6.9454,8.5974,15710.06,3.5978,84.2025,1357.08,23.2145,4.6028,1.6055,57.259,1.4966,36.326,16.656,
2022-03-08,1.0892,126.03,1.9558,N/A,25.642,7.4441,N/A,0.83185,388.28,N/A,N/A,N/A,4.9103,N/A,4.9494,10.8803,N/A,N/A,1.0111,145.9,9.7925,7.5715,N/A,N/A,15.8183,1.4971,5.5346,1.3978,6.8805,8.5183,15639.76,3.6022,83.924,1344.71,23.2866,4.5556,1.5958,56.9,1.4856,36.156,16.7051,
2022-03-07,1.0895,125.55,1.9558,N/A,25.584,7.4406,N/A,0.82625,393.25,N/A,N/A,N/A,4.9525,N/A,4.9494,10.8573,N/A,N/A,1.0069,145.8,9.8325,7.56,N/A,N/A,15.6577,1.4751,5.5065,1.3864,6.8846,8.5154,15685.76,3.5653,83.8125,1338.45,23.0249,4.5426,1.5861,56.832,1.4831,35.866,16.6951,
2022-03-04,1.0929,126.17,1.9558,N/A,25.737,7.4394,N/A,0.82388,386.54,N/A,N/A,N/A,4.853,N/A,4.9495,10.7935,N/A,N/A,1.0056,144.2,9.8358,7.5584,N/A,N/A,15.5681,1.4872,5.5313,1.3937,6.9065,8.5411,15725.3,3.5603,83.4354,1332.23,22.7543,4.5661,1.6005,56.814,1.4872,35.776,16.8044,
2022-03-03,1.1076,128.18,1.9558,N/A,25.634,7.4399,N/A,0.82773,378.64,N/A,N/A,N/A,4.7691,N/A,4.9496,10.7688,N/A,N/A,1.0192,143.4,9.8418,7.57,N/A,N/A,15.6897,1.5139,5.6041,1.3992,6.9996,8.6547,15934.42,3.5872,84.174,1334.2,22.8945,4.637,1.6329,57.293,1.5042,36.063,16.8798,
2022-03-02,1.1106,128.08,1.9558,N/A,25.866,7.4387,N/A,0.83316,382.31,N/A,N/A,N/A,4.8021,N/A,4.9493,10.788,N/A,N/A,1.0216,143,9.8826,7.574,N/A,N/A,15.628,1.5272,5.7313,1.4088,7.0153,8.6785,15980.03,3.5954,84.1765,1338.99,22.999,4.6595,1.6404,57.206,1.5059,36.339,17.1904,
2022-03-01,1.1162,128.15,1.9558,N/A,25.465,7.4377,N/A,0.8329,379.6,N/A,N/A,N/A,4.7947,N/A,4.949,10.6893,N/A,N/A,1.0247,142,9.8598,7.567,117.201,N/A,15.5509,1.5365,5.7598,1.4158,7.0462,8.7234,16033.36,3.6152,84.5015,1342.6,22.8558,4.6802,1.6484,57.295,1.515,36.505,17.2145,
2022-02-28,1.1199,129.31,1.9558,N/A,24.997,7.4404,N/A,0.8355,369.72,N/A,N/A,N/A,4.6835,N/A,4.9484,10.6055,N/A,N/A,1.0336,141.8,9.9465,7.5655,115.4842,N/A,15.4532,1.5508,5.7828,1.4264,7.067,8.7514,16100.72,3.63,84.554,1347.62,22.9011,4.7019,1.6628,57.432,1.5201,36.593,17.2863,
2022-02-25,1.1216,129.64,1.9558,N/A,24.66,7.4418,N/A,0.8374,365.28,N/A,N/A,N/A,4.6369,N/A,4.9479,10.5848,N/A,N/A,1.0398,140.8,9.9756,7.5535,92.5673,N/A,15.4799,1.5541,5.738,1.4325,7.0828,8.7578,16088.71,3.6389,84.347,1346.44,22.9145,4.7107,1.6651,57.549,1.5176,36.441,17.0315,
2022-02-24,1.1163,128.28,1.9558,N/A,25.09,7.4405,N/A,0.83463,368.63,N/A,N/A,N/A,4.6554,N/A,4.9501,10.7338,N/A,N/A,1.032,142,10.0878,7.552,95.7175,N/A,16.0525,1.5593,5.6874,1.4316,7.0601,8.7178,16074,3.6618,84.296,1347.7,22.9355,4.6896,1.6692,57.45,1.5125,36.514,17.1634,
2022-02-23,1.1344,130.58,1.9558,N/A,24.473,7.4388,N/A,0.83463,357.25,N/A,N/A,N/A,4.5481,N/A,4.9468,10.5658,N/A,N/A,1.0431,141.2,10.0335,7.5362,90.8791,N/A,15.6871,1.5592,5.6808,1.4394,7.1669,8.8529,16270.12,3.6515,84.6135,1350.23,22.9079,4.748,1.6679,57.98,1.5253,36.601,17.0508,
2022-02-22,1.1342,130.54,1.9558,N/A,24.496,7.4392,N/A,0.83685,355.89,N/A,N/A,N/A,4.5447,N/A,4.9464,10.5996,N/A,N/A,1.0422,141.2,10.1018,7.537,89.8055,N/A,15.6959,1.5739,5.7677,1.4441,7.1771,8.8495,16293.92,3.6584,84.758,1353.34,23.033,4.7472,1.6848,58.208,1.5268,36.748,17.1768,
2022-02-21,1.1338,130.2,1.9558,N/A,24.345,7.4397,N/A,0.83298,357.54,N/A,N/A,N/A,4.5351,N/A,4.9448,10.6535,N/A,N/A,1.0387,141.4,10.1738,7.536,89.0866,N/A,15.4689,1.5751,5.8045,1.4454,7.1831,8.8443,16276.34,3.6433,84.677,1353.02,22.9951,4.7387,1.687,58.3,1.5265,36.588,17.1895,
2022-02-18,1.1354,130.59,1.9558,N/A,24.337,7.4382,N/A,0.83425,356.37,N/A,N/A,N/A,4.5201,N/A,4.9453,10.5796,N/A,N/A,1.0452,140.8,10.1465,7.5355,86.2815,N/A,15.4678,1.5754,5.8435,1.4424,7.184,8.8566,16304.49,3.6276,84.6525,1356.45,23.027,4.7528,1.6896,58.403,1.5255,36.435,17.0858,
2022-02-17,1.137,130.84,1.9558,N/A,24.383,7.4398,N/A,0.83493,356.08,N/A,N/A,N/A,4.5065,N/A,4.9432,10.593,N/A,N/A,1.0466,141.6,10.1225,7.533,86.388,N/A,15.4945,1.5786,5.8495,1.4439,7.206,8.8692,16291.53,3.6231,85.2935,1360.7,23.0367,4.7612,1.695,58.314,1.5278,36.537,16.9893,
2022-02-16,1.1372,131.56,1.9558,N/A,24.365,7.441,N/A,0.8394,355.65,N/A,N/A,N/A,4.4961,N/A,4.9436,10.5363,N/A,N/A,1.0516,141.4,10.1095,7.5295,85.3679,N/A,15.481,1.5859,5.8765,1.4416,7.2101,8.8712,16232.55,3.6256,85.3885,1361.31,23.1718,4.7595,1.7108,58.346,1.5291,36.771,17.214,
2022-02-15,1.1345,131.18,1.9558,N/A,24.419,7.4422,N/A,0.83765,355.33,N/A,N/A,N/A,4.5036,N/A,4.941,10.5739,N/A,N/A,1.0483,141.2,10.0893,7.529,85.5025,N/A,15.4716,1.5888,5.8977,1.4433,7.1969,8.8527,16187.89,3.6596,85.443,1357.5,23.1113,4.7493,1.7143,58.144,1.5265,36.724,17.1767,
2022-02-14,1.1316,130.6,1.9558,N/A,24.527,7.4411,N/A,0.8372,357.06,N/A,N/A,N/A,4.54,N/A,4.9457,10.6158,N/A,N/A,1.0472,142.6,10.0693,7.5293,86.348,N/A,15.351,1.5902,5.8965,1.4431,7.1937,8.8283,16190.53,3.6835,85.4715,1354.5,23.1331,4.742,1.7112,58.114,1.5247,36.8,17.131,
2022-02-11,1.1417,132.24,1.9558,N/A,24.405,7.44,N/A,0.83958,353.38,N/A,N/A,N/A,4.5204,N/A,4.9458,10.553,N/A,N/A,1.0557,141.8,10.0732,7.5312,85.855,N/A,15.4066,1.5927,5.9263,1.4498,7.2564,8.9054,16339.46,3.6958,85.8535,1363.68,23.3183,4.7832,1.7085,58.482,1.5339,37.282,17.2736,
2022-02-10,1.1439,132.42,1.9558,N/A,24.35,7.4404,N/A,0.84248,354.02,N/A,N/A,N/A,4.4921,N/A,4.9451,10.5275,N/A,N/A,1.0571,141.8,10.0693,7.5275,85.0187,N/A,15.4838,1.5894,5.9668,1.4498,7.2722,8.9142,16390.21,3.6796,85.9373,1367.52,23.3584,4.7855,1.7076,58.583,1.5345,37.331,17.3078,
2022-02-09,1.1435,132.04,1.9558,N/A,24.288,7.4437,N/A,0.84255,352.94,N/A,N/A,N/A,4.5135,N/A,4.9449,10.4075,N/A,N/A,1.0555,142.2,10.0585,7.5285,85.5289,N/A,15.551,1.5933,6.0198,1.4514,7.2759,8.9106,16390.99,3.6817,85.5765,1365.72,23.4719,4.7853,1.7107,58.603,1.5349,37.404,17.5281,
2022-02-08,1.1408,131.68,1.9558,N/A,24.259,7.4437,N/A,0.84363,353.09,N/A,N/A,N/A,4.5312,N/A,4.945,10.4433,N/A,N/A,1.0545,142.4,10.0758,7.5215,85.7797,N/A,15.5558,1.6025,6.0209,1.4505,7.2636,8.8923,16418.4,3.6753,85.2545,1367.88,23.5601,4.774,1.7196,58.715,1.5349,37.606,17.638,
2022-02-07,1.1447,131.59,1.9558,N/A,24.222,7.4443,N/A,0.84685,353.48,N/A,N/A,N/A,4.5432,N/A,4.9461,10.4483,N/A,N/A,1.0571,143.4,10.0658,7.52,86.5824,N/A,15.5235,1.6097,6.0541,1.4546,7.2807,8.9202,16478.97,3.6547,85.5345,1371.76,23.575,4.7909,1.7278,58.978,1.5389,37.735,17.7,
2022-02-04,1.1464,131.72,1.9558,N/A,24.36,7.4432,N/A,0.84593,352.92,N/A,N/A,N/A,4.5474,N/A,4.9466,10.4465,N/A,N/A,1.0567,142.8,10.0483,7.5275,87.3095,N/A,15.5072,1.6165,6.083,1.4583,7.2923,8.9286,16493.16,3.6741,85.6445,1374.04,23.5856,4.7914,1.7287,58.754,1.5419,37.797,17.5875,
2022-02-03,1.1286,129.63,1.9558,N/A,24.135,7.4388,N/A,0.83208,353.94,N/A,N/A,N/A,4.5315,N/A,4.9461,10.387,N/A,N/A,1.0407,142.4,9.9545,7.5295,86.1788,N/A,15.3047,1.5849,5.9843,1.4334,7.1795,8.7966,16243.28,3.5949,84.518,1358.42,23.2359,4.7215,1.7001,57.608,1.5212,37.424,17.2864,
2022-02-02,1.1323,129.37,1.9558,N/A,24.298,7.4383,N/A,0.83395,354.45,N/A,N/A,N/A,4.5449,N/A,4.9463,10.385,N/A,N/A,1.0399,143.2,9.9228,7.526,85.815,N/A,15.3011,1.5828,5.9677,1.433,7.2026,8.8245,16243.98,3.5777,84.613,1359.23,23.2134,4.7392,1.701,57.785,1.5254,37.575,17.3459,
2022-02-01,1.126,129.12,1.9558,N/A,24.335,7.4407,N/A,0.83498,356.38,N/A,N/A,N/A,4.5804,N/A,4.9465,10.4438,N/A,N/A,1.0374,143.8,9.9638,7.5275,86.3238,N/A,15.0644,1.5868,5.9572,1.4299,7.1625,8.7779,16126.29,3.566,84.197,1353.61,23.1296,4.7129,1.7032,57.516,1.5199,37.378,17.1633,
2022-01-31,1.1156,128.79,1.9558,N/A,24.372,7.4419,N/A,0.83153,357.19,N/A,N/A,N/A,4.5892,N/A,4.9475,10.489,N/A,N/A,1.0404,143.2,10.0085,7.5293,86.7251,N/A,14.931,1.582,6.003,1.4233,7.0963,8.6994,16036.76,3.564,83.3655,1349.08,23.1856,4.6693,1.6983,56.985,1.511,37.144,17.3734,
2022-01-28,1.1138,128.68,1.9558,N/A,24.443,7.4432,N/A,0.83178,358.42,N/A,N/A,N/A,4.5755,N/A,4.9463,10.552,N/A,N/A,1.0378,144,10.026,7.529,86.6113,N/A,15.1424,1.5971,6.0147,1.4239,7.0857,8.681,16047.76,3.5697,83.6015,1349.47,23.1854,4.6668,1.7031,57.028,1.5109,37.229,17.3844,
2022-01-27,1.116,128.74,1.9558,N/A,24.427,7.4428,N/A,0.83368,358.09,N/A,N/A,N/A,4.5592,N/A,4.9466,10.445,N/A,N/A,1.0391,145.2,9.9903,7.5328,87.139,N/A,15.1946,1.5771,6.0159,1.4161,7.1061,8.6951,16052.42,3.5695,83.7893,1344.5,23.143,4.6844,1.6872,57.295,1.5089,37.124,17.1112,
2022-01-26,1.1277,128.86,1.9558,N/A,24.531,7.442,N/A,0.83458,359.67,N/A,N/A,N/A,4.5864,N/A,4.9451,10.4493,N/A,N/A,1.0386,145.6,10.0115,7.529,89.265,N/A,15.2877,1.5727,6.1084,1.4173,7.1293,8.7791,16185.31,3.5834,84.4288,1350.03,23.1845,4.7268,1.6865,57.728,1.5162,37.169,17.0858,
2022-01-25,1.1268,128.49,1.9558,N/A,24.5,7.4437,N/A,0.83713,359.44,N/A,N/A,N/A,4.5751,N/A,4.9448,10.502,N/A,N/A,1.0364,146,10.1385,7.5295,88.7384,N/A,15.2723,1.5814,6.2049,1.4247,7.1325,8.7725,16169.54,3.5898,84.277,1350.71,23.2909,4.7213,1.6911,57.753,1.5157,37.28,17.2669,
2022-01-24,1.1304,128.62,1.9558,N/A,24.528,7.4431,N/A,0.83803,359.84,N/A,N/A,N/A,4.5572,N/A,4.9453,10.5038,N/A,N/A,1.0308,145.6,10.1638,7.529,88.649,N/A,15.1621,1.5866,6.1901,1.4269,7.1533,8.8003,16213.69,3.5846,84.3495,1352.41,23.2566,4.7352,1.6901,58.045,1.5216,37.382,17.2509,
2022-01-21,1.1348,129.14,1.9558,N/A,24.347,7.4431,N/A,0.83633,358.19,N/A,N/A,N/A,4.5318,N/A,4.9453,10.414,N/A,N/A,1.0353,145.6,10.0523,7.528,86.838,N/A,15.223,1.5774,6.2063,1.4211,7.1946,8.837,16244.2,3.5668,84.419,1351.89,23.2229,4.7508,1.6884,58.171,1.526,37.358,17.1546,
2022-01-20,1.1338,129.53,1.9558,N/A,24.263,7.4424,N/A,0.83265,355.81,N/A,N/A,N/A,4.5228,N/A,4.9453,10.3708,N/A,N/A,1.0382,145.4,9.9578,7.525,86.8952,N/A,15.2094,1.5662,6.1621,1.4158,7.1936,8.8274,16267.5,3.5564,84.362,1349.16,23.1684,4.7489,1.6731,58.285,1.5261,37.319,17.2531,
2022-01-19,1.1345,129.86,1.9558,N/A,24.313,7.4419,N/A,0.83168,355.88,N/A,N/A,N/A,4.5229,N/A,4.9449,10.3428,N/A,N/A,1.0383,145.2,9.9368,7.5238,86.48,N/A,15.4207,1.5709,6.2657,1.4144,7.2003,8.8392,16283.78,3.5529,84.4135,1347.1,23.0922,4.757,1.6684,58.429,1.5293,37.467,17.3889,
2022-01-18,1.1367,130.39,1.9558,N/A,24.426,7.4425,N/A,0.83673,357,N/A,N/A,N/A,4.526,N/A,4.9449,10.3185,N/A,N/A,1.0414,146,9.9638,7.5225,86.7325,N/A,15.4447,1.5833,6.2797,1.4228,7.2212,8.8576,16318.64,3.5557,84.813,1355.33,23.1443,4.7554,1.6798,58.553,1.5349,37.635,17.5398,
2022-01-17,1.1403,130.64,1.9558,N/A,24.467,7.4417,N/A,0.83573,356.09,N/A,N/A,N/A,4.5256,N/A,4.9443,10.305,N/A,N/A,1.0429,146.8,9.9623,7.5275,87.3907,N/A,15.2757,1.5811,6.2808,1.4287,7.2402,8.8844,16337.73,3.5479,84.7295,1359.89,23.1901,4.7704,1.6765,58.496,1.5374,37.761,17.601,
2022-01-14,1.1447,130.17,1.9558,N/A,24.493,7.4414,N/A,0.83508,356.1,N/A,N/A,N/A,4.5414,N/A,4.9429,10.2684,N/A,N/A,1.0429,147,9.9863,7.5205,88.0011,N/A,15.5256,1.5803,6.3361,1.433,7.2728,8.9119,16388.85,3.5565,84.9445,1361.27,23.2684,4.7831,1.6745,58.727,1.5418,38.033,17.6043,
2022-01-13,1.1463,130.98,1.9558,N/A,24.458,7.4409,N/A,0.83545,355.24,N/A,N/A,N/A,4.5361,N/A,4.944,10.238,N/A,N/A,1.0453,147,9.9333,7.5202,86.603,N/A,15.5744,1.5709,6.3518,1.4304,7.2913,8.9289,16379.02,3.5638,84.717,1359.24,23.3894,4.7875,1.6676,58.572,1.5427,38.08,17.6381,
2022-01-12,1.137,131.19,1.9558,N/A,24.423,7.4414,N/A,0.83338,355.98,N/A,N/A,N/A,4.5359,N/A,4.9453,10.264,N/A,N/A,1.0486,147,9.927,7.524,84.7559,N/A,15.5922,1.5762,6.3458,1.4261,7.2379,8.863,16295.93,3.5406,84.0285,1353.27,23.1852,4.7595,1.6775,58.08,1.5358,37.936,17.5645,
2022-01-11,1.1336,130.95,1.9558,N/A,24.412,7.4404,N/A,0.83475,357.45,N/A,N/A,N/A,4.5438,N/A,4.945,10.3075,N/A,N/A,1.0502,147,10.0165,7.5236,84.8663,N/A,15.696,1.5804,6.389,1.4329,7.2255,8.8384,16221.39,3.549,83.7481,1352.85,23.0888,4.7515,1.6772,57.979,1.5342,37.862,17.7094,
2022-01-10,1.1318,130.45,1.9558,N/A,24.357,7.4381,N/A,0.83398,358.4,N/A,N/A,N/A,4.5334,N/A,4.9449,10.3038,N/A,N/A,1.0446,146.2,10.0253,7.5278,84.9825,N/A,15.7183,1.5774,6.3969,1.4327,7.2128,8.8233,16181.9,3.5303,83.8,1355.68,23.0597,4.7536,1.6753,58.141,1.5344,38.074,17.6999,
2022-01-07,1.1298,130.9,1.9558,N/A,24.439,7.438,N/A,0.8343,358.68,N/A,N/A,N/A,4.5496,N/A,4.9451,10.2839,N/A,N/A,1.0422,146,10.0288,7.5214,85.298,N/A,15.7206,1.5804,6.4343,1.4374,7.206,8.8133,16188.43,3.5135,83.978,1359.96,23.1109,4.7553,1.6748,58.046,1.5356,38.074,17.6701,
2022-01-06,1.1315,131.05,1.9558,N/A,24.528,7.4393,N/A,0.83593,359.84,N/A,N/A,N/A,4.5614,N/A,4.9435,10.3265,N/A,N/A,1.0395,146.8,10.035,7.5197,86.5088,N/A,15.5504,1.5778,6.442,1.4451,7.2187,8.8272,16291.88,3.5247,84.2475,1362.06,23.2549,4.767,1.6752,57.943,1.5388,37.962,17.7932,
2022-01-05,1.1319,131.03,1.9558,N/A,24.581,7.4384,N/A,0.83546,362.15,N/A,N/A,N/A,4.5666,N/A,4.946,10.2545,N/A,N/A,1.0364,146.8,9.9672,7.519,85.7275,N/A,15.2446,1.56,6.4146,1.4399,7.2087,8.8227,16263.02,3.4989,84.161,1354.61,23.1422,4.7466,1.6597,57.713,1.534,37.607,17.9369,
2022-01-04,1.1279,131.17,1.9558,N/A,24.745,7.4378,N/A,0.83618,365.12,N/A,N/A,N/A,4.5667,N/A,4.9481,10.2808,N/A,N/A,1.0355,147.8,10.0138,7.5185,84.9202,N/A,15.1384,1.5682,6.4174,1.4382,7.1924,8.7919,16199.73,3.4909,84.2055,1352.91,23.1808,4.7214,1.6668,57.988,1.531,37.582,18.108,
2022-01-03,1.1355,130.56,1.9558,N/A,24.818,7.4382,N/A,0.84135,367.71,N/A,N/A,N/A,4.5895,N/A,4.9483,10.2958,N/A,N/A,1.0372,147.6,10.0013,7.519,84.5313,N/A,15.0777,1.5691,6.3539,1.442,7.2174,8.8541,16202.02,3.5139,84.3949,1354.4,23.2259,4.7379,1.6651,58.051,1.5333,37.665,17.9661,
1 Date USD JPY BGN CYP CZK DKK EEK GBP HUF LTL LVL MTL PLN ROL RON SEK SIT SKK CHF ISK NOK HRK RUB TRL TRY AUD BRL CAD CNY HKD IDR ILS INR KRW MXN MYR NZD PHP SGD THB ZAR
2 2022-12-30 1.0666 140.66 1.9558 N/A 24.116 7.4365 N/A 0.88693 400.87 N/A N/A N/A 4.6808 N/A 4.9495 11.1218 N/A N/A 0.9847 151.5 10.5138 7.5365 N/A N/A 19.9649 1.5693 5.6386 1.444 7.3582 8.3163 16519.82 3.7554 88.171 1344.09 20.856 4.6984 1.6798 59.32 1.43 36.835 18.0986
3 2022-12-29 1.0649 142.24 1.9558 N/A 24.191 7.4365 N/A 0.88549 399.6 N/A N/A N/A 4.6855 N/A 4.9493 11.158 N/A N/A 0.984 152.5 10.55 7.5365 N/A N/A 19.934 1.5859 5.5351 1.4475 7.4151 8.2994 16680.38 3.7575 88.2295 1350.18 20.651 4.7106 1.6887 59.367 1.436 36.877 18.1967
4 2022-12-28 1.064 142.21 1.9558 N/A 24.252 7.4365 N/A 0.88058 403.3 N/A N/A N/A 4.7008 N/A 4.946 11.1038 N/A N/A 0.9863 151.9 10.4495 7.5365 N/A N/A 19.9144 1.566 5.6109 1.4361 7.4224 8.2931 16765.93 3.7526 88.0943 1348.59 20.6856 4.7055 1.6772 59.613 1.4323 36.953 18.289
5 2022-12-27 1.0624 141.68 1.9558 N/A 24.26 7.4366 N/A 0.88333 401.65 N/A N/A N/A 4.6683 N/A 4.927 11.1285 N/A N/A 0.9885 152.3 10.4895 7.5375 N/A N/A 19.8799 1.577 5.6035 1.4384 7.3994 8.2874 16620.58 3.7278 88.0808 1349.85 20.5515 4.699 1.6916 59.356 1.43 36.775 18.3181
6 2022-12-23 1.0622 140.86 1.9558 N/A 24.247 7.4364 N/A 0.8803 400.68 N/A N/A N/A 4.6423 N/A 4.9056 11.1045 N/A N/A 0.9867 152.3 10.4448 7.537 N/A N/A 19.843 1.5857 5.4834 1.4433 7.4198 8.2878 16569.18 3.704 87.958 1359.5 20.7115 4.7002 1.6887 58.623 1.4337 36.842 18.1048
7 2022-12-22 1.0633 140.42 1.9558 N/A 24.215 7.4367 N/A 0.88243 402.13 N/A N/A N/A 4.6443 N/A 4.8993 11.05 N/A N/A 0.9852 153.3 10.4123 7.538 N/A N/A 19.8553 1.5804 5.5386 1.4484 7.4229 8.2883 16525.91 3.6942 88.0365 1361.75 20.8485 4.7051 1.6918 58.705 1.4356 36.849 18.2238
8 2022-12-21 1.0636 140.29 1.9558 N/A 24.218 7.438 N/A 0.87651 402.93 N/A N/A N/A 4.6665 N/A 4.8937 11.0623 N/A N/A 0.9836 152.1 10.4309 7.5419 N/A N/A 19.8541 1.5859 5.4913 1.4475 7.4219 8.2902 16573.77 3.6989 88.109 1367.63 20.9919 4.7197 1.685 58.556 1.4366 36.907 18.3529
9 2022-12-20 1.0599 140.58 1.9558 N/A 24.181 7.4388 N/A 0.8753 403.88 N/A N/A N/A 4.6757 N/A 4.9125 11.0615 N/A N/A 0.9854 151.5 10.5098 7.5471 N/A N/A 19.7744 1.5972 5.6234 1.4451 7.39 8.2488 16537.09 3.6759 87.6649 1363.73 20.9355 4.6991 1.6816 58.549 1.4347 36.853 18.4239
10 2022-12-19 1.0598 144.65 1.9558 N/A 24.233 7.4382 N/A 0.87118 403.18 N/A N/A N/A 4.6853 N/A 4.9107 11.0063 N/A N/A 0.9884 151.9 10.5025 7.5395 N/A N/A 19.7676 1.5794 5.6327 1.4472 7.3901 8.2428 16506.72 3.6551 87.5321 1377.17 20.9743 4.6912 1.6632 58.649 1.4378 36.923 18.3074
11 2022-12-16 1.0619 145.53 1.9558 N/A 24.262 7.4379 N/A 0.87233 407.1 N/A N/A N/A 4.6925 N/A 4.9213 11.0153 N/A N/A 0.9879 150.1 10.4833 7.5385 N/A N/A 19.8039 1.5866 5.6233 1.4506 7.4037 8.2632 16575.47 3.6689 87.824 1389.7 21.0634 4.6984 1.6687 58.967 1.4413 37.145 18.6708
12 2022-12-15 1.0621 145.07 1.9558 N/A 24.27 7.4387 N/A 0.86194 406.4 N/A N/A N/A 4.689 N/A 4.922 10.898 N/A N/A 0.9862 150.9 10.4013 7.5395 N/A N/A 19.806 1.5695 5.6247 1.4443 7.4007 8.2551 16591.4 3.6388 87.9355 1393.97 20.9431 4.6918 1.6628 59.297 1.4406 37.12 18.3599
13 2022-12-14 1.0649 143.68 1.9558 N/A 24.276 7.4392 N/A 0.86118 406.63 N/A N/A N/A 4.681 N/A 4.9248 10.8638 N/A N/A 0.9865 150.9 10.362 7.538 N/A N/A 19.8579 1.551 5.6842 1.4441 7.4009 8.2751 16599.51 3.6327 87.8435 1379.99 20.8635 4.6765 1.6508 59.326 1.4349 36.851 18.2563
14 2022-12-13 1.0545 144.85 1.9558 N/A 24.287 7.4391 N/A 0.85753 409.65 N/A N/A N/A 4.6938 N/A 4.9298 10.8965 N/A N/A 0.9869 151.1 10.4679 7.5495 N/A N/A 19.6649 1.5553 5.5784 1.4341 7.3637 8.2033 16521.81 3.6266 87.2965 1378.75 20.9435 4.6704 1.6464 58.852 1.4288 36.707 18.6855
15 2022-12-12 1.0562 144.86 1.9558 N/A 24.307 7.4379 N/A 0.86006 416.78 N/A N/A N/A 4.6923 N/A 4.9318 10.9075 N/A N/A 0.9855 150.7 10.5548 7.554 N/A N/A 19.6913 1.5625 5.556 1.4428 7.367 8.2103 16524.63 3.6232 87.253 1377.93 20.9047 4.6652 1.6523 58.788 1.4284 36.708 18.4697
16 2022-12-09 1.0559 143.3 1.9558 N/A 24.293 7.4379 N/A 0.8595 417.53 N/A N/A N/A 4.6869 N/A 4.9224 10.9188 N/A N/A 0.9856 149.5 10.5345 7.555 N/A N/A 19.6872 1.5553 5.5457 1.438 7.3475 8.2169 16453.46 3.6128 86.9535 1373.94 20.849 4.6512 1.6482 58.47 1.426 36.656 18.2358
17 2022-12-08 1.0519 143.75 1.9558 N/A 24.324 7.4382 N/A 0.86258 417.66 N/A N/A N/A 4.6853 N/A 4.9131 10.906 N/A N/A 0.9889 149.5 10.488 7.5553 N/A N/A 19.6114 1.559 5.488 1.4307 7.3324 8.1889 16423.92 3.6206 86.6755 1387.06 20.6989 4.6257 1.6547 58.233 1.4256 36.559 18.0225
18 2022-12-07 1.0529 144.44 1.9558 N/A 24.322 7.4382 N/A 0.86408 410.63 N/A N/A N/A 4.7003 N/A 4.918 10.919 N/A N/A 0.9893 148.7 10.5255 7.5525 N/A N/A 19.6256 1.5728 5.5023 1.4387 7.3476 8.1997 16465.17 3.626 86.692 1390.87 20.7534 4.6301 1.6573 58.432 1.4286 36.904 18.1353
19 2022-12-06 1.0516 143.33 1.9558 N/A 24.316 7.438 N/A 0.8617 415.08 N/A N/A N/A 4.6975 N/A 4.913 10.889 N/A N/A 0.9872 148.9 10.4408 7.5563 N/A N/A 19.601 1.5625 5.5113 1.4326 7.3494 8.1813 16441.49 3.5831 86.6485 1386.39 20.6884 4.6223 1.6583 58.782 1.4263 36.827 18.2068
20 2022-12-05 1.0587 143.07 1.9558 N/A 24.351 7.4369 N/A 0.86085 412.13 N/A N/A N/A 4.695 N/A 4.9215 10.8931 N/A N/A 0.9893 148.9 10.3366 7.551 N/A N/A 19.7326 1.5542 5.5491 1.4198 7.3573 8.2236 16332.36 3.5856 86.5249 1370.87 20.7295 4.6255 1.6498 59.245 1.4271 36.732 18.2038
21 2022-12-02 1.0538 141.32 1.9558 N/A 24.377 7.4373 N/A 0.85855 410 N/A N/A N/A 4.684 N/A 4.9298 10.902 N/A N/A 0.9834 148.7 10.2615 7.5503 N/A N/A 19.6392 1.5457 5.4657 1.416 7.3971 8.2035 16179.78 3.5769 85.6435 1366.67 20.187 4.6241 1.6453 58.734 1.4227 36.614 18.2749
22 2022-12-01 1.0454 142.48 1.9558 N/A 24.361 7.4373 N/A 0.85715 413.5 N/A N/A N/A 4.6998 N/A 4.9303 10.8984 N/A N/A 0.9868 148.7 10.2495 7.55 N/A N/A 19.4778 1.5377 5.4508 1.4059 7.3965 8.1371 16160.84 3.5642 84.933 1363.81 20.1472 4.605 1.6446 58.793 1.4195 36.526 18.5393
23 2022-11-30 1.0376 144.28 1.9558 N/A 24.338 7.4366 N/A 0.86488 408.4 N/A N/A N/A 4.6635 N/A 4.9245 10.9345 N/A N/A 0.9854 147.1 10.2648 7.549 N/A N/A 19.3333 1.5425 5.5063 1.4021 7.3437 8.0944 16271.81 3.5691 84.4215 1365.14 20.0111 4.6147 1.6634 58.697 1.418 36.588 17.5768
24 2022-11-29 1.0366 143.36 1.9558 N/A 24.334 7.4367 N/A 0.86218 406.5 N/A N/A N/A 4.673 N/A 4.9193 10.901 N/A N/A 0.9862 147.1 10.3313 7.55 N/A N/A 19.3181 1.5414 5.5126 1.4005 7.4289 8.0965 16301.58 3.5581 84.6548 1375.7 19.8075 4.6735 1.6639 58.64 1.4237 36.706 17.6027
25 2022-11-28 1.0463 144.9 1.9558 N/A 24.348 7.4367 N/A 0.86606 408.87 N/A N/A N/A 4.6938 N/A 4.9246 10.8973 N/A N/A 0.9872 146.7 10.364 7.5488 N/A N/A 19.4844 1.5632 5.6354 1.4062 7.5326 8.1782 16440.45 3.6002 85.437 1396.56 20.22 4.6874 1.6827 59.21 1.4375 37.285 17.9376
26 2022-11-25 1.0375 144.62 1.9558 N/A 24.367 7.4365 N/A 0.85885 411.33 N/A N/A N/A 4.6875 N/A 4.9255 10.8183 N/A N/A 0.9836 146.5 10.2985 7.5473 N/A N/A 19.3333 1.5404 5.5476 1.3864 7.4425 8.1084 16282.44 3.5551 84.7145 1383.2 20.1069 4.648 1.6651 58.795 1.4277 37.153 17.7677
27 2022-11-24 1.0413 143.9 1.9558 N/A 24.392 7.4369 N/A 0.85933 413.33 N/A N/A N/A 4.6958 N/A 4.9205 10.8573 N/A N/A 0.9818 146.5 10.3435 7.547 N/A N/A 19.3969 1.5414 5.5504 1.3894 7.4442 8.1324 16295.35 3.5607 85.0295 1382.28 20.1501 4.6806 1.6598 58.992 1.4319 37.258 17.7246
28 2022-11-23 1.0325 145.75 1.9558 N/A 24.356 7.437 N/A 0.86369 405.75 N/A N/A N/A 4.7033 N/A 4.937 10.8933 N/A N/A 0.9795 146.7 10.3659 7.5435 N/A N/A 19.2316 1.5522 5.565 1.3856 7.3982 8.0708 16189.81 3.5681 84.466 1397.42 20.001 4.7237 1.6718 58.914 1.4295 37.423 17.7103
29 2022-11-22 1.0274 145.2 1.9558 N/A 24.351 7.4377 N/A 0.86358 408.23 N/A N/A N/A 4.7125 N/A 4.9269 10.9653 N/A N/A 0.9791 145.7 10.4445 7.5438 N/A N/A 19.1221 1.5473 5.4578 1.3765 7.3344 8.0313 16106.79 3.5615 83.8768 1392.68 20.0951 4.7055 1.6707 58.942 1.4167 37.11 17.7568
30 2022-11-21 1.0246 145.33 1.9558 N/A 24.356 7.4377 N/A 0.86793 409.43 N/A N/A N/A 4.7075 N/A 4.9413 10.9873 N/A N/A 0.9817 147.9 10.4898 7.5425 N/A N/A 19.0822 1.5471 5.4401 1.377 7.3419 7.9989 16116.76 3.5515 83.7375 1395 19.9357 4.6927 1.6766 58.822 1.4162 37.121 17.7858
31 2022-11-18 1.0366 145.12 1.9558 N/A 24.351 7.4385 N/A 0.87063 407.41 N/A N/A N/A 4.7033 N/A 4.9413 10.9805 N/A N/A 0.9881 148.9 10.486 7.5415 N/A N/A 19.3009 1.5433 5.547 1.3841 7.379 8.1092 16224.36 3.591 84.6875 1389.02 20.162 4.7202 1.6757 59.41 1.4229 37.069 17.908
32 2022-11-17 1.0319 144.8 1.9558 N/A 24.399 7.4383 N/A 0.87475 415.6 N/A N/A N/A 4.7153 N/A 4.9254 10.9871 N/A N/A 0.9818 148.9 10.498 7.541 N/A N/A 19.2124 1.5526 5.6535 1.382 7.3859 8.077 16224.01 3.5796 84.394 1394.06 20.062 4.7122 1.6986 59.293 1.4221 37.123 18.0961
33 2022-11-16 1.0412 145.29 1.9558 N/A 24.355 7.4386 N/A 0.87483 408.18 N/A N/A N/A 4.7065 N/A 4.9206 10.8754 N/A N/A 0.9795 148.9 10.3675 7.5443 N/A N/A 19.3783 1.54 5.5438 1.3801 7.372 8.1444 16248.37 3.5684 84.5905 1378.1 20.1227 4.7323 1.6897 59.678 1.425 37.103 18.0195
34 2022-11-15 1.0404 144.84 1.9558 N/A 24.326 7.4388 N/A 0.87455 405.45 N/A N/A N/A 4.7073 N/A 4.9116 10.8081 N/A N/A 0.979 149.9 10.357 7.5459 N/A N/A 19.3608 1.5415 5.548 1.3816 7.3299 8.143 16164.78 3.5694 84.1304 1365.61 20.0795 4.7208 1.6897 59.532 1.4238 36.939 17.8822
35 2022-11-14 1.0319 144.86 1.9558 N/A 24.289 7.4382 N/A 0.87513 407.28 N/A N/A N/A 4.6898 N/A 4.9043 10.7713 N/A N/A 0.9751 150.3 10.3143 7.5465 N/A N/A 19.1923 1.5427 5.4605 1.3706 7.2906 8.0852 16052.12 3.541 83.7779 1369.32 20.0985 4.7429 1.6957 59.04 1.4177 36.978 17.8393
36 2022-11-11 1.0308 143.89 1.9558 N/A 24.278 7.4384 N/A 0.87538 402.08 N/A N/A N/A 4.6765 N/A 4.894 10.7241 N/A N/A 0.9844 148.7 10.2635 7.5445 N/A N/A 19.0987 1.5459 5.5147 1.3698 7.3267 8.0758 15979.45 3.5255 83.2253 1359.2 20.0239 4.77 1.702 59.106 1.4199 37.088 17.7944
37 2022-11-10 0.9954 145.47 1.9558 N/A 24.361 7.4381 N/A 0.87298 400.95 N/A N/A N/A 4.706 N/A 4.8913 10.8743 N/A N/A 0.9834 147.5 10.3615 7.5427 N/A N/A 18.51 1.5525 5.286 1.3467 7.2184 7.8128 15615.6 3.5453 81.3058 1373.96 19.4562 4.6789 1.6984 57.793 1.3963 36.7 17.6882
38 2022-11-09 1.0039 146.82 1.9558 N/A 24.337 7.4382 N/A 0.87774 403.53 N/A N/A N/A 4.701 N/A 4.9045 10.845 N/A N/A 0.988 146.7 10.322 7.5425 N/A N/A 18.6728 1.5538 5.1947 1.3501 7.2813 7.8801 15717.07 3.5621 81.6575 1369.73 19.6554 4.7098 1.7033 58.236 1.4061 36.999 17.877
39 2022-11-08 0.9996 146.25 1.9558 N/A 24.326 7.4378 N/A 0.87378 400.75 N/A N/A N/A 4.6918 N/A 4.8978 10.8373 N/A N/A 0.9911 146.3 10.2795 7.539 N/A N/A 18.5991 1.5435 5.203 1.3489 7.2495 7.8468 15652.76 3.5436 81.518 1377.94 19.4495 4.7346 1.686 58.187 1.4022 37.22 17.8397
40 2022-11-07 0.9993 146.18 1.9558 N/A 24.301 7.4393 N/A 0.87135 401.03 N/A N/A N/A 4.6865 N/A 4.8855 10.832 N/A N/A 0.9874 145.9 10.2555 7.5375 N/A N/A 18.5875 1.5428 5.07 1.3464 7.2189 7.8444 15648.95 3.5402 81.8407 1391.25 19.4395 4.7362 1.6834 58.361 1.4022 37.284 17.7583
41 2022-11-04 0.9872 145.19 1.9558 N/A 24.422 7.4419 N/A 0.87478 401.15 N/A N/A N/A 4.6825 N/A 4.8893 10.8538 N/A N/A 0.9863 145.5 10.2019 7.5353 N/A N/A 18.3845 1.5311 4.9682 1.3351 7.0894 7.7493 15491.81 3.5065 81.02 1397.7 19.2611 4.6872 1.6769 57.672 1.3891 36.906 17.7983
42 2022-11-03 0.9753 144.58 1.9558 N/A 24.539 7.4433 N/A 0.87228 407.87 N/A N/A N/A 4.709 N/A 4.9013 10.932 N/A N/A 0.9889 144.9 10.3543 7.5375 N/A N/A 18.1602 1.5517 5.0262 1.3452 7.1367 7.656 15400.2 3.4847 80.8845 1391.75 19.2363 4.6271 1.6957 57.463 1.3878 37.091 18.0173
43 2022-11-02 0.9908 145.75 1.9558 N/A 24.506 7.4431 N/A 0.861 407 N/A N/A N/A 4.7035 N/A 4.912 10.9065 N/A N/A 0.9861 143.7 10.2388 7.5335 N/A N/A 18.4488 1.5426 5.0964 1.347 7.2156 7.7774 15492.57 3.4987 81.992 1402.01 19.4921 4.6944 1.6844 57.841 1.3983 37.314 17.9608
44 2022-11-01 0.9947 146.35 1.9558 N/A 24.484 7.4438 N/A 0.86058 406.9 N/A N/A N/A 4.7053 N/A 4.9138 10.874 N/A N/A 0.9878 143.3 10.1835 7.5342 N/A N/A 18.5216 1.5409 5.1337 1.3469 7.2165 7.8079 15534.6 3.4922 82.084 1404.63 19.5984 4.7119 1.6876 57.786 1.4017 37.45 17.9802
45 2022-10-31 0.9914 147.4 1.9558 N/A 24.488 7.4444 N/A 0.86115 409.65 N/A N/A N/A 4.7085 N/A 4.9143 10.901 N/A N/A 0.9925 143.3 10.3028 7.531 N/A N/A 18.4562 1.5529 5.2694 1.3553 7.238 7.7822 15489.55 3.4933 82.1035 1416.12 19.7122 4.6873 1.7099 57.8 1.4038 37.748 18.1736
46 2022-10-28 0.9951 146.79 1.9558 N/A 24.465 7.4423 N/A 0.8612 411.7 N/A N/A N/A 4.7275 N/A 4.9189 10.9403 N/A N/A 0.992 143.3 10.2695 7.532 N/A N/A 18.5219 1.5511 5.327 1.3542 7.2159 7.8107 15481.88 3.5215 82.0565 1417.7 19.7718 4.6994 1.7151 57.739 1.4055 37.724 18.053
47 2022-10-27 1.0037 147.37 1.9558 N/A 24.53 7.4387 N/A 0.86745 412.15 N/A N/A N/A 4.7585 N/A 4.8893 10.9583 N/A N/A 0.9949 143.1 10.342 7.533 N/A N/A 18.681 1.561 5.3889 1.3672 7.2552 7.8782 15629.06 3.5376 82.656 1428.57 20.015 4.7324 1.7316 58.441 1.4154 37.975 18.1521
48 2022-10-26 1.0023 147.32 1.9558 N/A 24.535 7.4381 N/A 0.86603 408.09 N/A N/A N/A 4.7548 N/A 4.8806 10.953 N/A N/A 0.9917 143.5 10.3408 7.532 N/A N/A 18.6461 1.5466 5.2944 1.3568 7.1948 7.8678 15589.27 3.5145 82.206 1422.11 19.8501 4.7262 1.7249 58.493 1.4104 37.862 18.0212
49 2022-10-25 0.9861 146.84 1.9558 N/A 24.472 7.4387 N/A 0.87143 413.7 N/A N/A N/A 4.777 N/A 4.9036 10.9728 N/A N/A 0.9888 142.9 10.391 7.5315 N/A N/A 18.3508 1.5599 5.2254 1.3537 7.2072 7.7407 15407.12 3.506 81.653 1417.5 19.6353 4.6697 1.7321 57.988 1.405 37.758 18.2211
50 2022-10-24 0.9851 146.76 1.9558 N/A 24.482 7.4385 N/A 0.8707 411.88 N/A N/A N/A 4.7908 N/A 4.9128 11.0795 N/A N/A 0.9856 142.5 10.392 7.5337 N/A N/A 18.3298 1.5631 5.1461 1.3502 7.1544 7.7329 15362.63 3.4997 81.5451 1418.4 19.6514 4.6674 1.7343 58.021 1.4008 37.6 18.0625
51 2022-10-21 0.973 147.59 1.9558 N/A 24.511 7.4382 N/A 0.87728 412.88 N/A N/A N/A 4.7885 N/A 4.9125 11.0868 N/A N/A 0.9855 141.1 10.4315 7.5325 N/A N/A 18.0988 1.5646 5.1117 1.3465 7.0504 7.6376 15199.12 3.4803 80.739 1404.32 19.5521 4.6101 1.7347 57.287 1.3917 37.349 18.0021
52 2022-10-20 0.9811 146.99 1.9558 N/A 24.525 7.4389 N/A 0.87258 411.2 N/A N/A N/A 4.7728 N/A 4.9203 10.982 N/A N/A 0.9836 141.1 10.402 7.5353 N/A N/A 18.2257 1.5554 5.1387 1.3461 7.0858 7.7008 15250.05 3.4754 81.1755 1400.3 19.7005 4.6396 1.7206 57.742 1.3959 37.36 17.9106
53 2022-10-19 0.9778 146.34 1.9558 N/A 24.563 7.439 N/A 0.86993 413.78 N/A N/A N/A 4.7878 N/A 4.9248 10.9448 N/A N/A 0.981 141.1 10.3823 7.5325 N/A N/A 18.1793 1.5568 5.1755 1.3479 7.0672 7.6757 15185.1 3.4628 81.1955 1398.35 19.6845 4.6152 1.7264 57.741 1.3931 37.469 17.8339
54 2022-10-18 0.9835 146.65 1.9558 N/A 24.593 7.4393 N/A 0.86928 413.08 N/A N/A N/A 4.804 N/A 4.9359 10.906 N/A N/A 0.9792 141.5 10.3528 7.5298 N/A N/A 18.2813 1.5557 5.1795 1.3495 7.0805 7.72 15214.98 3.464 80.9195 1400.92 19.664 4.6382 1.7251 57.897 1.3963 37.422 17.7904
55 2022-10-17 0.9739 145 1.9558 N/A 24.562 7.4379 N/A 0.8625 418.3 N/A N/A N/A 4.8143 N/A 4.937 10.9893 N/A N/A 0.9762 140.9 10.342 7.5265 N/A N/A 18.1043 1.5599 5.1497 1.3452 7.013 7.6448 15061.8 3.4486 80.128 1399.41 19.5 4.5934 1.7404 57.433 1.3896 37.169 17.6769
56 2022-10-14 0.9717 143.63 1.9558 N/A 24.587 7.4378 N/A 0.86823 418.24 N/A N/A N/A 4.8328 N/A 4.9335 11.0035 N/A N/A 0.9757 140.5 10.3323 7.5266 N/A N/A 18.0614 1.5493 5.1177 1.3426 6.9952 7.6278 15031.5 3.444 79.9695 1398.5 19.5032 4.5689 1.7302 57.375 1.3852 37.109 17.6932
57 2022-10-13 0.9739 142.94 1.9558 N/A 24.569 7.4385 N/A 0.86513 430.65 N/A N/A N/A 4.8303 N/A 4.9355 11.0098 N/A N/A 0.9725 140.5 10.3525 7.531 N/A N/A 18.1041 1.5495 5.1214 1.3443 6.9945 7.644 14952.86 3.4731 79.9981 1392.71 19.4442 4.5691 1.7314 57.352 1.3949 36.843 17.8173
58 2022-10-12 0.9706 142.34 1.9558 N/A 24.561 7.4399 N/A 0.8784 429.65 N/A N/A N/A 4.8495 N/A 4.94 11.02 N/A N/A 0.9664 140.1 10.4145 7.529 N/A N/A 18.0427 1.5525 5.1378 1.3395 6.9603 7.6192 14907.04 3.4623 79.8955 1384.66 19.4522 4.5448 1.7372 57.148 1.3941 36.902 17.6876
59 2022-10-11 0.9723 141.54 1.9558 N/A 24.535 7.439 N/A 0.87703 428.73 N/A N/A N/A 4.869 N/A 4.9394 11.0015 N/A N/A 0.9675 140.7 10.4235 7.5293 N/A N/A 18.0686 1.545 5.0456 1.3402 6.9669 7.6325 14930.83 3.4776 79.9555 1392.84 19.4115 4.5436 1.7323 57.243 1.3967 37.03 17.6153
60 2022-10-10 0.9697 141.16 1.9558 N/A 24.521 7.4384 N/A 0.8773 428.2 N/A N/A N/A 4.8655 N/A 4.94 10.9502 N/A N/A 0.968 139.9 10.3378 7.528 N/A N/A 18.0131 1.536 5.0328 1.3312 6.9344 7.612 14872.51 3.4463 79.9678 1384.26 19.3588 4.5091 1.7369 57.197 1.3939 36.81 17.5866
61 2022-10-07 0.9797 141.92 1.9558 N/A 24.517 7.4381 N/A 0.87383 423.85 N/A N/A N/A 4.8595 N/A 4.9415 10.8555 N/A N/A 0.97 140.7 10.4498 7.527 N/A N/A 18.209 1.5266 5.1075 1.3437 6.9715 7.6906 14933.14 3.4477 80.546 1381.42 19.643 4.5556 1.7328 57.747 1.3996 36.602 17.6222
62 2022-10-06 0.986 142.68 1.9558 N/A 24.479 7.439 N/A 0.87583 422.59 N/A N/A N/A 4.8505 N/A 4.9364 10.8728 N/A N/A 0.9709 139.9 10.4278 7.5288 N/A N/A 18.3191 1.5263 5.1185 1.3475 7.0164 7.74 15021.24 3.4846 81.0615 1388.39 19.817 4.5726 1.728 57.949 1.4057 36.827 17.5769
63 2022-10-05 0.9915 143.18 1.9558 N/A 24.524 7.4388 N/A 0.8734 423.6 N/A N/A N/A 4.791 N/A 4.9385 10.8376 N/A N/A 0.9756 141.3 10.4858 7.5255 N/A N/A 18.4201 1.538 5.1575 1.3493 7.0555 7.7831 15061.81 3.503 80.909 1406.71 19.8625 4.5911 1.7419 58.212 1.4124 37.102 17.6228
64 2022-10-04 0.9891 143.3 1.9558 N/A 24.544 7.4374 N/A 0.87273 417.68 N/A N/A N/A 4.8193 N/A 4.9418 10.8166 N/A N/A 0.9767 141.9 10.4915 7.523 N/A N/A 18.3374 1.5318 5.0589 1.3503 7.0384 7.7644 15080.18 3.4885 80.6995 1412.2 19.777 4.5939 1.7368 58.104 1.4148 37.16 17.5437
65 2022-10-03 0.9764 141.49 1.9558 N/A 24.527 7.4366 N/A 0.8707 424.86 N/A N/A N/A 4.832 N/A 4.9479 10.8743 N/A N/A 0.9658 141.7 10.5655 7.5275 N/A N/A 18.124 1.5128 5.178 1.3412 6.9481 7.6647 14969.79 3.498 79.898 1408.25 19.604 4.5383 1.7263 57.599 1.4015 37.181 17.5871
66 2022-09-30 0.9748 141.01 1.9558 N/A 24.549 7.4365 N/A 0.883 422.18 N/A N/A N/A 4.8483 N/A 4.949 10.8993 N/A N/A 0.9561 140.9 10.5838 7.524 N/A N/A 18.0841 1.5076 5.2584 1.3401 6.9368 7.6521 14863.26 3.4759 79.425 1400.69 19.6393 4.5201 1.7177 57.276 1.4001 36.823 17.5353
67 2022-09-29 0.9706 140.46 1.9558 N/A 24.687 7.4365 N/A 0.89485 421.93 N/A N/A N/A 4.857 N/A 4.9481 10.958 N/A N/A 0.9538 140.1 10.4518 7.528 N/A N/A 18 1.4982 5.2521 1.3294 6.9223 7.6192 14735.97 3.4422 79.314 1388.34 19.5779 4.4992 1.704 56.86 1.3961 36.946 17.4466
68 2022-09-28 0.9565 138.39 1.9558 N/A 24.65 7.4368 N/A 0.90268 411.72 N/A N/A N/A 4.8043 N/A 4.9485 10.9194 N/A N/A 0.9437 139.7 10.4576 7.5313 N/A N/A 17.7311 1.4924 5.1728 1.3157 6.9199 7.5084 14622.96 3.3931 78.2655 1378.84 19.5294 4.4281 1.6998 56.528 1.3846 36.687 17.2916
69 2022-09-27 0.9644 139.28 1.9558 N/A 24.661 7.4366 N/A 0.89275 406.65 N/A N/A N/A 4.764 N/A 4.9444 10.8533 N/A N/A 0.9503 139.3 10.3473 7.528 N/A N/A 17.824 1.4859 5.1235 1.3196 6.9156 7.5704 14604.52 3.3714 78.574 1370.06 19.5832 4.4466 1.6921 56.933 1.3838 36.565 17.2361
70 2022-09-26 0.9646 139.07 1.9558 N/A 24.64 7.4365 N/A 0.89404 408.83 N/A N/A N/A 4.7608 N/A 4.9418 10.9275 N/A N/A 0.9555 138.9 10.3585 7.5278 N/A N/A 17.8001 1.4858 5.1504 1.3195 6.9075 7.572 14620.74 3.4069 78.704 1379.4 19.6066 4.4401 1.6886 56.908 1.3842 36.496 17.4247
71 2022-09-23 0.9754 139.43 1.9558 N/A 24.658 7.4365 N/A 0.88201 406.3 N/A N/A N/A 4.7543 N/A 4.9433 10.9328 N/A N/A 0.9565 139.9 10.2335 7.5228 N/A N/A 17.9515 1.4828 5.0456 1.3177 6.9442 7.6567 14697.3 3.4152 79.0705 1381.97 19.5708 4.4659 1.6846 57.217 1.3897 36.636 17.3853
72 2022-09-22 0.9884 139.18 1.9558 N/A 24.657 7.4365 N/A 0.87256 405.25 N/A N/A N/A 4.7592 N/A 4.9411 10.8724 N/A N/A 0.9684 139.9 10.235 7.5235 N/A N/A 18.1559 1.484 5.0677 1.3278 6.9804 7.7583 14824.28 3.4216 79.897 1384.79 19.6129 4.514 1.6832 57.721 1.3998 36.803 17.3514
73 2022-09-21 0.9906 142.66 1.9558 N/A 24.637 7.4364 N/A 0.87335 405.1 N/A N/A N/A 4.7505 N/A 4.9443 10.9214 N/A N/A 0.9549 140.3 10.2858 7.5205 N/A N/A 18.149 1.4851 5.0924 1.3262 6.9821 7.7761 14866.28 3.4298 79.1555 1381.38 19.7847 4.5097 1.6844 57.285 1.4006 36.786 17.4879
74 2022-09-20 0.9986 143.34 1.9558 N/A 24.556 7.4368 N/A 0.87395 398.58 N/A N/A N/A 4.7208 N/A 4.934 10.8338 N/A N/A 0.9644 140.9 10.273 7.5198 N/A N/A 18.2833 1.4893 5.2139 1.3268 7.003 7.8382 14997.82 3.4406 79.6095 1390.71 19.9667 4.5516 1.6908 57.497 1.4074 36.968 17.7261
75 2022-09-19 0.999 143.42 1.9558 N/A 24.494 7.4373 N/A 0.87785 400.85 N/A N/A N/A 4.7058 N/A 4.93 10.7993 N/A N/A 0.9658 139.9 10.2826 7.5215 N/A N/A 18.2738 1.495 5.2886 1.3294 7.0066 7.8416 14975.43 3.446 79.653 1391.82 20.113 4.5455 1.6807 57.347 1.4082 36.983 17.7267
76 2022-09-16 0.9954 142.53 1.9558 N/A 24.497 7.4366 N/A 0.874 403.98 N/A N/A N/A 4.7143 N/A 4.9238 10.7541 N/A N/A 0.9579 138.3 10.1985 7.5235 N/A N/A 18.1923 1.4894 5.2279 1.3226 6.9787 7.8133 14904.67 3.4267 79.3605 1383.58 20.0028 4.5141 1.6717 57.111 1.4025 36.8 17.6004
77 2022-09-15 0.9992 143.43 1.9558 N/A 24.518 7.4366 N/A 0.86934 407.15 N/A N/A N/A 4.7273 N/A 4.9238 10.69 N/A N/A 0.9572 138.7 10.1203 7.5258 N/A N/A 18.2477 1.4853 5.1837 1.3172 6.9852 7.8423 14925.35 3.4384 79.7119 1397.18 20.0021 4.5314 1.6689 57.258 1.4062 36.816 17.5283
78 2022-09-14 0.999 143.08 1.9558 N/A 24.527 7.4366 N/A 0.86498 402.9 N/A N/A N/A 4.7163 N/A 4.9297 10.675 N/A N/A 0.9612 139.7 10.1125 7.5195 N/A N/A 18.2397 1.4873 5.1827 1.3177 6.955 7.8405 14903.93 3.4339 79.422 1391.97 20.028 4.5225 1.6675 57.054 1.4039 36.608 17.4342
79 2022-09-13 1.0175 144.5 1.9558 N/A 24.551 7.4366 N/A 0.86793 396.83 N/A N/A N/A 4.705 N/A 4.921 10.6108 N/A N/A 0.9669 140.1 9.9988 7.5255 N/A N/A 18.564 1.4736 5.1764 1.32 7.0467 7.9855 15099.17 3.4125 80.5453 1397.3 20.1615 4.5869 1.6555 57.665 1.4186 36.859 17.3112
80 2022-09-12 1.0155 144.49 1.9558 N/A 24.546 7.4365 N/A 0.86778 395.73 N/A N/A N/A 4.6965 N/A 4.9135 10.6368 N/A N/A 0.9667 140.9 9.9718 7.5195 N/A N/A 18.5232 1.4749 5.1933 1.3194 7.0348 7.9709 15083.83 3.4346 80.692 1397.58 20.1025 4.5733 1.6499 57.701 1.4168 36.873 17.322
81 2022-09-09 1.0049 143.3 1.9558 N/A 24.536 7.4365 N/A 0.8686 396.3 N/A N/A N/A 4.721 N/A 4.9019 10.6643 N/A N/A 0.9657 140.9 9.9836 7.5245 N/A N/A 18.3282 1.4704 5.2087 1.307 6.9543 7.8871 14905.33 3.4416 79.9685 1384.64 19.991 4.52 1.6463 57.098 1.4063 36.508 17.3753
82 2022-09-08 1.0009 143.65 1.9558 N/A 24.543 7.4365 N/A 0.86656 395.48 N/A N/A N/A 4.7155 N/A 4.8756 10.7075 N/A N/A 0.9739 140.3 10.0615 7.515 N/A N/A 18.2546 1.4824 5.2042 1.3134 6.9564 7.8568 14891.86 3.429 79.7375 1381.7 20.013 4.5051 1.6491 57.031 1.4054 36.418 17.3797
83 2022-09-07 0.9885 143.2 1.9558 N/A 24.631 7.4365 N/A 0.8651 401.83 N/A N/A N/A 4.729 N/A 4.8585 10.6888 N/A N/A 0.975 141.3 9.9483 7.5143 N/A N/A 18.0262 1.4748 5.1881 1.3037 6.8968 7.7596 14779.47 3.4053 79.028 1374.44 19.9225 4.4497 1.6459 56.532 1.3931 36.322 17.2582
84 2022-09-06 0.9928 140.91 1.9558 N/A 24.55 7.4365 N/A 0.85743 402.65 N/A N/A N/A 4.7068 N/A 4.8424 10.6825 N/A N/A 0.9745 141.9 9.8945 7.5133 N/A N/A 18.0938 1.4651 5.13 1.3029 6.9091 7.7932 14783.94 3.39 79.2305 1366.65 19.8545 4.4676 1.6313 56.655 1.3947 36.242 17.0805
85 2022-09-05 0.992 139.47 1.9558 N/A 24.622 7.4364 N/A 0.86358 403.9 N/A N/A N/A 4.736 N/A 4.8198 10.729 N/A N/A 0.9747 142.7 9.9188 7.5173 N/A N/A 18.0792 1.4616 5.1407 1.3043 6.8768 7.7867 14782.83 3.3826 79.2332 1359.98 19.8192 4.4563 1.6289 56.477 1.3932 36.263 17.088
86 2022-09-02 0.9993 140.36 1.9558 N/A 24.481 7.437 N/A 0.86478 398.38 N/A N/A N/A 4.7063 N/A 4.8335 10.7498 N/A N/A 0.9839 141.5 10.0035 7.5225 N/A N/A 18.2072 1.4671 5.2153 1.3131 6.9031 7.8439 14895.92 3.396 79.8096 1360.84 20.1024 4.4809 1.6394 56.854 1.4013 36.624 17.2791
87 2022-09-01 1.0004 139.34 1.9558 N/A 24.488 7.4372 N/A 0.86473 399.58 N/A N/A N/A 4.7128 N/A 4.8447 10.7415 N/A N/A 0.9802 141.7 10.013 7.521 N/A N/A 18.2149 1.4651 5.2239 1.3169 6.9017 7.8511 14878 3.3644 79.6195 1353.69 20.1954 4.4828 1.6389 56.609 1.4002 36.71 17.1524
88 2022-08-31 1 138.72 1.9558 N/A 24.55 7.4371 N/A 0.86035 402.8 N/A N/A N/A 4.7283 N/A 4.8595 10.6788 N/A N/A 0.9796 141.7 9.9388 7.5148 N/A N/A 18.1849 1.4591 5.1482 1.3111 6.8947 7.8488 14849.93 3.3399 79.5465 1342.79 20.2044 4.4755 1.6322 56.153 1.3969 36.45 17.0667
89 2022-08-30 1.0034 138.71 1.9558 N/A 24.577 7.4376 N/A 0.85645 406.38 N/A N/A N/A 4.7323 N/A 4.8657 10.65 N/A N/A 0.9741 142.1 9.7553 7.5103 N/A N/A 18.239 1.4472 5.0286 1.3047 6.9233 7.8751 14875 3.3168 79.8025 1350.92 20.0077 4.4907 1.6245 56.393 1.3997 36.494 16.8567
90 2022-08-29 0.9986 138.49 1.9558 N/A 24.592 7.4379 N/A 0.8542 409.9 N/A N/A N/A 4.745 N/A 4.8699 10.628 N/A N/A 0.967 141.1 9.7675 7.5119 N/A N/A 18.1605 1.4529 5.0663 1.3026 6.9044 7.8368 14871.09 3.3146 79.8295 1347.47 19.9876 4.4837 1.6305 56.187 1.395 36.399 16.8891
91 2022-08-26 1.0007 137.02 1.9558 N/A 24.635 7.4379 N/A 0.8459 410.48 N/A N/A N/A 4.7485 N/A 4.8729 10.5703 N/A N/A 0.9642 140.3 9.667 7.5135 N/A N/A 18.1923 1.4333 5.1069 1.2944 6.8671 7.8521 14825.57 3.2537 79.9025 1333.46 19.924 4.4706 1.6112 56.109 1.3906 36.035 16.7995
92 2022-08-25 0.997 136.07 1.9558 N/A 24.648 7.4374 N/A 0.84293 408.93 N/A N/A N/A 4.7578 N/A 4.8758 10.5525 N/A N/A 0.9616 140.3 9.64 7.514 N/A N/A 18.112 1.4306 5.0879 1.2881 6.8317 7.8234 14753.15 3.2791 79.6555 1331.98 19.8132 4.4586 1.6006 55.842 1.3857 35.732 16.7903
93 2022-08-24 0.9934 135.74 1.9558 N/A 24.629 7.4381 N/A 0.84283 410.93 N/A N/A N/A 4.7668 N/A 4.88 10.586 N/A N/A 0.9576 139.5 9.636 7.5125 N/A N/A 18.0362 1.4389 5.0606 1.2908 6.822 7.795 14757.6 3.2599 79.3006 1332.84 19.7781 4.4559 1.6065 55.7 1.3857 35.906 16.8976
94 2022-08-23 0.9927 136.34 1.9558 N/A 24.658 7.4374 N/A 0.84343 410.55 N/A N/A N/A 4.7788 N/A 4.8839 10.6063 N/A N/A 0.9602 140.1 9.7438 7.5128 N/A N/A 17.983 1.4437 5.0984 1.2928 6.7952 7.79 14743.28 3.2598 79.2805 1332.1 19.9152 4.4557 1.6071 55.661 1.386 35.856 16.9568
95 2022-08-22 1.0001 137.08 1.9558 N/A 24.651 7.437 N/A 0.84658 408 N/A N/A N/A 4.764 N/A 4.8853 10.65 N/A N/A 0.958 140.9 9.7719 7.5113 N/A N/A 18.1268 1.4478 5.1752 1.2989 6.8457 7.8468 14895.47 3.2778 79.8615 1344.21 20.1583 4.4854 1.6125 56.218 1.3958 36.099 17.0766
96 2022-08-19 1.0054 137.67 1.9558 N/A 24.625 7.4373 N/A 0.84938 407.35 N/A N/A N/A 4.751 N/A 4.8811 10.6095 N/A N/A 0.9616 140.5 9.8418 7.5155 N/A N/A 18.2028 1.4584 5.2334 1.3062 6.8531 7.8877 14951.91 3.2879 80.2988 1343.39 20.3273 4.5007 1.6203 56.325 1.3979 35.988 17.1007
97 2022-08-18 1.0178 137.17 1.9558 N/A 24.611 7.4389 N/A 0.84391 405.13 N/A N/A N/A 4.724 N/A 4.8808 10.5903 N/A N/A 0.9683 140.5 9.8283 7.5215 N/A N/A 18.4095 1.4617 5.2326 1.3118 6.906 7.9844 15092.62 3.2993 81.042 1344.81 20.313 4.5536 1.6145 56.776 1.4053 36.254 17.0004
98 2022-08-17 1.0164 137.36 1.9558 N/A 24.566 7.4377 N/A 0.84208 404.28 N/A N/A N/A 4.7078 N/A 4.8825 10.5617 N/A N/A 0.9686 140.3 9.8428 7.5071 N/A N/A 18.2568 1.4655 5.2838 1.3117 6.8917 7.9705 15015.81 3.311 80.7555 1337.02 20.3825 4.5413 1.6165 56.772 1.4051 36.052 16.9125
99 2022-08-16 1.0131 136.11 1.9558 N/A 24.54 7.4368 N/A 0.84218 406.2 N/A N/A N/A 4.7043 N/A 4.882 10.5365 N/A N/A 0.9625 140.3 9.8428 7.51 N/A N/A 18.1994 1.4463 5.1835 1.3076 6.8767 7.9449 14968.68 3.3087 80.3745 1329.66 20.1595 4.5245 1.6012 56.602 1.398 35.93 16.6556
100 2022-08-15 1.0195 135.61 1.9558 N/A 24.46 7.4373 N/A 0.84375 398.6 N/A N/A N/A 4.6858 N/A 4.8849 10.498 N/A N/A 0.9631 140.3 9.871 7.5028 N/A N/A 18.3143 1.4508 5.2268 1.3167 6.905 7.9899 15042.36 3.3294 81.061 1336.35 20.3914 4.5465 1.6002 57.122 1.4036 36.218 16.7375
101 2022-08-12 1.0285 137.47 1.9558 N/A 24.38 7.4395 N/A 0.84715 392.3 N/A N/A N/A 4.6773 N/A 4.8915 10.4515 N/A N/A 0.9689 140.3 9.813 7.5138 N/A N/A 18.4733 1.4496 5.3007 1.3148 6.9352 8.06 15104.2 3.345 81.9935 1342.59 20.4925 4.5709 1.5985 57.246 1.4106 36.393 16.7318
102 2022-08-11 1.0338 136.57 1.9558 N/A 24.346 7.4395 N/A 0.84575 394.18 N/A N/A N/A 4.6828 N/A 4.9055 10.36 N/A N/A 0.9712 139.7 9.804 7.515 N/A N/A 18.5674 1.4532 5.2447 1.3202 6.9668 8.1118 15215.93 3.3505 82.2845 1344.62 20.6398 4.5952 1.6045 57.225 1.4151 36.343 16.7083
103 2022-08-10 1.0252 138.16 1.9558 N/A 24.397 7.4397 N/A 0.84608 397.65 N/A N/A N/A 4.7063 N/A 4.9138 10.3773 N/A N/A 0.9713 139.7 9.9118 7.5158 N/A N/A 18.4099 1.4682 5.251 1.3207 6.9222 8.046 15218.87 3.3773 81.468 1344.16 20.713 4.5693 1.6211 57.061 1.4107 36.42 16.8788
104 2022-08-09 1.0234 138.26 1.9558 N/A 24.532 7.4407 N/A 0.8452 397.35 N/A N/A N/A 4.7085 N/A 4.9038 10.3875 N/A N/A 0.9763 140.1 9.9365 7.514 N/A N/A 18.3342 1.4687 5.2478 1.3163 6.9106 8.0334 15197.09 3.3865 81.406 1336.74 20.7145 4.5592 1.6304 56.939 1.411 36.264 17.05
105 2022-08-08 1.0199 137.62 1.9558 N/A 24.515 7.4405 N/A 0.84165 394.27 N/A N/A N/A 4.7043 N/A 4.9163 10.365 N/A N/A 0.9763 140.1 9.9405 7.5123 N/A N/A 18.3175 1.4607 5.238 1.3134 6.8931 8.0061 15147.65 3.3895 81.166 1329.93 20.681 4.5477 1.6202 56.554 1.4058 36.38 16.9694
106 2022-08-05 1.0233 136.22 1.9558 N/A 24.581 7.4415 N/A 0.84268 393.78 N/A N/A N/A 4.7085 N/A 4.9251 10.3573 N/A N/A 0.9776 138.9 9.982 7.5148 N/A N/A 18.3853 1.4713 5.3348 1.3185 6.9068 8.0328 15236.2 3.4033 81.0469 1324.53 20.8367 4.5598 1.6248 56.524 1.4077 36.373 17.0342
107 2022-08-04 1.0181 135.81 1.9558 N/A 24.659 7.4425 N/A 0.84231 395.98 N/A N/A N/A 4.7233 N/A 4.9261 10.374 N/A N/A 0.9765 138.7 9.9065 7.515 N/A N/A 18.2922 1.4607 5.3614 1.307 6.8769 7.9919 15200.94 3.4101 80.7715 1333.7 20.756 4.5387 1.6172 56.633 1.4037 36.606 17.0352
108 2022-08-03 1.0194 136.18 1.9558 N/A 24.65 7.4427 N/A 0.83629 395.03 N/A N/A N/A 4.691 N/A 4.9245 10.3913 N/A N/A 0.9773 139.3 9.8743 7.5178 N/A N/A 18.3112 1.4681 5.3547 1.3085 6.883 8.0022 15184.35 3.4362 80.6895 1336.03 21.0788 4.5435 1.6266 56.855 1.4069 36.963 17.1283
109 2022-08-02 1.0224 133.9 1.9558 N/A 24.644 7.4432 N/A 0.83665 396.82 N/A N/A N/A 4.7063 N/A 4.9298 10.3995 N/A N/A 0.9744 139.3 9.9305 7.5195 N/A N/A 18.3564 1.4745 5.3175 1.315 6.9117 8.0257 15204.85 3.4486 80.3243 1339.16 20.952 4.5531 1.6254 56.834 1.4103 36.914 16.982
110 2022-08-01 1.0233 135.38 1.9558 N/A 24.628 7.4457 N/A 0.837 401.35 N/A N/A N/A 4.734 N/A 4.9283 10.3668 N/A N/A 0.9717 138.7 9.8638 7.521 N/A N/A 18.3475 1.4535 5.2723 1.3076 6.9105 8.0329 15203.21 3.4546 80.9335 1333.3 20.7635 4.5568 1.616 56.734 1.4087 36.977 16.8613
111 2022-07-29 1.0198 136.42 1.9558 N/A 24.61 7.4438 N/A 0.8399 404.8 N/A N/A N/A 4.7375 N/A 4.9343 10.3875 N/A N/A 0.9744 138.3 9.8773 7.518 N/A N/A 18.2472 1.4646 5.2739 1.31 6.8705 8.0054 15155.56 3.4714 80.882 1329.4 20.6745 4.5386 1.6283 56.375 1.4088 36.978 16.8627
112 2022-07-28 1.0122 137.26 1.9558 N/A 24.609 7.4442 N/A 0.83586 407.3 N/A N/A N/A 4.7908 N/A 4.9342 10.449 N/A N/A 0.9745 138.7 9.8983 7.52 N/A N/A 18.1417 1.4535 5.33 1.2986 6.8325 7.9456 15107.59 3.4701 80.6535 1320.45 20.676 4.5071 1.6172 56.592 1.4009 37.097 17.0011
113 2022-07-27 1.0152 138.89 1.9558 N/A 24.575 7.4446 N/A 0.84138 404.67 N/A N/A N/A 4.7978 N/A 4.9334 10.4545 N/A N/A 0.9768 139.1 9.9558 7.514 N/A N/A 18.1859 1.462 5.4039 1.3049 6.8534 7.9692 15213.07 3.4855 81.135 1333.99 20.753 4.5263 1.6306 56.455 1.4088 37.4 17.1347
114 2022-07-26 1.0124 138.35 1.9558 N/A 24.607 7.4449 N/A 0.84558 400.99 N/A N/A N/A 4.742 N/A 4.9324 10.4445 N/A N/A 0.9765 139.1 10.0105 7.5145 N/A N/A 18.0705 1.4605 5.4437 1.3035 6.8451 7.9466 15185.27 3.4891 80.805 1326.65 20.7845 4.5113 1.6235 56.16 1.4066 37.18 17.087
115 2022-07-25 1.0236 139.84 1.9558 N/A 24.535 7.4449 N/A 0.84813 396.5 N/A N/A N/A 4.708 N/A 4.9339 10.3973 N/A N/A 0.9869 139.5 10.0704 7.5195 N/A N/A 18.2653 1.4707 5.5976 1.3168 6.9094 8.0345 15303.87 3.5201 81.6675 1341.25 20.9376 4.5586 1.6325 57.209 1.4176 37.525 17.1502
116 2022-07-22 1.019 139.51 1.9558 N/A 24.514 7.4443 N/A 0.85141 398.3 N/A N/A N/A 4.7508 N/A 4.9321 10.4328 N/A N/A 0.9832 139.5 10.1498 7.5234 N/A N/A 18.094 1.4677 5.5821 1.3105 6.8852 7.9985 15275.69 3.5083 81.384 1335.66 20.9595 4.5366 1.6265 57.24 1.4151 37.392 17.2009
117 2022-07-21 1.0199 141.46 1.9558 N/A 24.496 7.4446 N/A 0.85545 400.13 N/A N/A N/A 4.761 N/A 4.9391 10.426 N/A N/A 0.9924 139.7 10.175 7.52 N/A N/A 18.0327 1.4848 5.5777 1.3178 6.904 8.0056 15342.25 3.5203 81.451 1337.47 20.941 4.5457 1.6479 57.512 1.4218 37.66 17.5195
118 2022-07-20 1.0199 140.92 1.9558 N/A 24.493 7.4452 N/A 0.85178 399.5 N/A N/A N/A 4.782 N/A 4.9396 10.4606 N/A N/A 0.9896 139.5 10.1323 7.5143 N/A N/A 17.9444 1.4767 5.5427 1.3132 6.8892 8.0062 15275.82 3.5147 81.599 1337.61 20.8967 4.5406 1.6308 57.398 1.4204 37.405 17.3924
119 2022-07-19 1.0245 141.01 1.9558 N/A 24.555 7.4449 N/A 0.85303 397.45 N/A N/A N/A 4.7598 N/A 4.9395 10.4964 N/A N/A 0.9918 138.9 10.176 7.5093 N/A N/A 18.018 1.4869 5.5454 1.3264 6.9064 8.0423 15344.49 3.5295 81.898 1340.33 20.8552 4.559 1.6456 57.583 1.4269 37.492 17.457
120 2022-07-18 1.0131 140.16 1.9558 N/A 24.508 7.4435 N/A 0.84708 402.05 N/A N/A N/A 4.776 N/A 4.9389 10.5265 N/A N/A 0.9911 138.9 10.2553 7.513 N/A N/A 17.7225 1.4839 5.4505 1.3151 6.8266 7.9528 15157.63 3.5004 81.034 1333.33 20.7095 4.5113 1.6432 57.056 1.4153 37.13 17.383
121 2022-07-15 1.0059 139.49 1.9558 N/A 24.561 7.443 N/A 0.84988 403.73 N/A N/A N/A 4.7953 N/A 4.9407 10.5943 N/A N/A 0.9849 138.9 10.2763 7.516 N/A N/A 17.5451 1.4886 5.4434 1.3147 6.7943 7.8963 15081.58 3.5044 80.316 1333.79 20.9209 4.4752 1.6377 56.678 1.4113 36.866 17.2875
122 2022-07-14 1.0005 139.04 1.9558 N/A 24.417 7.4425 N/A 0.8456 408.78 N/A N/A N/A 4.8146 N/A 4.942 10.6019 N/A N/A 0.9841 138.89 10.2536 7.5122 N/A N/A 17.494 1.4893 5.4586 1.3162 6.7618 7.8539 15119.56 3.4915 80.0752 1322.29 20.9585 4.4462 1.6437 56.565 1.4065 36.632 17.202
123 2022-07-13 1.0067 138.02 1.9558 N/A 24.397 7.4416 N/A 0.84371 409.35 N/A N/A N/A 4.824 N/A 4.9414 10.602 N/A N/A 0.9829 138.3 10.2428 7.5155 N/A N/A 17.5629 1.4802 5.4533 1.3073 6.7722 7.9025 15117.08 3.4864 80.1285 1311.4 20.9029 4.4667 1.635 56.669 1.4134 36.377 17.0527
124 2022-07-12 1.0042 137.31 1.9558 N/A 24.582 7.4408 N/A 0.84823 409.98 N/A N/A N/A 4.819 N/A 4.9413 10.629 N/A N/A 0.9883 139.1 10.2754 7.517 N/A N/A 17.4392 1.49 5.4009 1.3094 6.7518 7.8828 15054.05 3.4987 79.8965 1315.1 20.8883 4.4556 1.6395 56.648 1.4127 36.392 17.1509
125 2022-07-11 1.0098 138.77 1.9558 N/A 24.592 7.4414 N/A 0.8454 408.22 N/A N/A N/A 4.7968 N/A 4.943 10.6943 N/A N/A 0.9908 139.3 10.299 7.518 N/A N/A 17.5447 1.491 5.3673 1.314 6.7793 7.9265 15131.92 3.5108 80.2435 1320.36 20.7845 4.4704 1.6466 56.574 1.4178 36.585 17.1634
126 2022-07-08 1.0163 138.05 1.9558 N/A 24.614 7.4424 N/A 0.84585 402.45 N/A N/A N/A 4.763 N/A 4.9431 10.6665 N/A N/A 0.9913 139.5 10.263 7.519 N/A N/A 17.6026 1.4871 5.4345 1.3201 6.8095 7.9769 15210.73 3.5325 80.528 1321.61 20.8477 4.4992 1.6464 56.882 1.4228 36.602 17.1922
127 2022-07-07 1.018 138.11 1.9558 N/A 24.779 7.4405 N/A 0.85105 410.04 N/A N/A N/A 4.7721 N/A 4.9448 10.723 N/A N/A 0.9906 139.3 10.291 7.5193 N/A N/A 17.5551 1.4883 5.4983 1.3227 6.823 7.9893 15265.27 3.5548 80.6 1324.66 20.9675 4.5077 1.6461 56.939 1.4255 36.74 17.0372
128 2022-07-06 1.0177 137.71 1.9558 N/A 24.778 7.4403 N/A 0.85676 411.8 N/A N/A N/A 4.771 N/A 4.944 10.745 N/A N/A 0.9896 138.5 10.2803 7.5198 N/A N/A 17.5505 1.4961 5.5116 1.3274 6.8289 7.9864 15287.49 3.5884 80.5321 1331.69 21.0194 4.5028 1.6505 56.779 1.4305 36.78 17.0246
129 2022-07-05 1.029 139.77 1.9558 N/A 24.751 7.4396 N/A 0.85845 407.38 N/A N/A N/A 4.7448 N/A 4.9438 10.8031 N/A N/A 0.9932 139.1 10.285 7.5246 N/A N/A 17.5049 1.518 5.5141 1.3364 6.9029 8.0748 15487.93 3.6343 81.673 1348.97 21.0171 4.5477 1.6772 57.009 1.4455 36.879 16.9143
130 2022-07-04 1.0455 141.51 1.9558 N/A 24.745 7.4391 N/A 0.8596 401.52 N/A N/A N/A 4.71 N/A 4.944 10.7658 N/A N/A 1.0037 139.3 10.2958 7.5301 N/A N/A 17.5994 1.5205 5.5663 1.3435 6.9977 8.2033 15684.13 3.6655 82.5067 1353.4 21.1972 4.6138 1.6748 57.487 1.4587 37.298 17.0275
131 2022-07-01 1.0425 141.05 1.9558 N/A 24.753 7.4391 N/A 0.86648 401.11 N/A N/A N/A 4.7168 N/A 4.9457 10.7783 N/A N/A 1.0027 138.7 10.3651 7.531 N/A N/A 17.4608 1.5382 5.5117 1.3492 6.987 8.1801 15621.64 3.6717 82.3747 1352.58 21.115 4.5943 1.6929 57.452 1.4565 37.186 17.1323
132 2022-06-30 1.0387 141.54 1.9558 N/A 24.739 7.4392 N/A 0.8582 397.04 N/A N/A N/A 4.6904 N/A 4.9464 10.73 N/A N/A 0.996 138.9 10.3485 7.5307 N/A N/A 17.322 1.5099 5.4229 1.3425 6.9624 8.1493 15552 3.6392 82.113 1351.6 20.9641 4.5781 1.6705 57.15 1.4483 36.754 17.0143
133 2022-06-29 1.0517 143.53 1.9558 N/A 24.739 7.4392 N/A 0.86461 394.28 N/A N/A N/A 4.6869 N/A 4.9419 10.6848 N/A N/A 1.0005 139.9 10.3065 7.5285 N/A N/A 17.4998 1.5256 5.5163 1.3513 7.0382 8.2532 15612.61 3.6344 83.037 1364.02 21.1375 4.6272 1.6871 57.773 1.4607 36.925 16.9295
134 2022-06-28 1.0561 143.67 1.9558 N/A 24.726 7.4394 N/A 0.8635 398.55 N/A N/A N/A 4.6905 N/A 4.9443 10.6543 N/A N/A 1.0101 139.5 10.337 7.532 N/A N/A 17.5891 1.521 5.5308 1.3565 7.0775 8.288 15669.91 3.6267 83.408 1361.75 21.088 4.6432 1.6822 57.85 1.4645 37.154 16.9072
135 2022-06-27 1.0572 143.25 1.9558 N/A 24.724 7.4408 N/A 0.862 402.62 N/A N/A N/A 4.699 N/A 4.944 10.6713 N/A N/A 1.0143 139.7 10.408 7.5333 N/A N/A 17.4794 1.5278 5.5446 1.3639 7.0737 8.2946 15635.36 3.6009 82.9325 1357.68 21.041 4.6559 1.6801 57.9 1.4641 37.361 16.7967
136 2022-06-24 1.0524 142.19 1.9558 N/A 24.731 7.4398 N/A 0.85773 401.34 N/A N/A N/A 4.7023 N/A 4.9463 10.694 N/A N/A 1.0072 139.7 10.4345 7.5295 N/A N/A 18.2856 1.5248 5.4851 1.3657 7.0478 8.2609 15633.96 3.621 82.3985 1364.09 20.9901 4.6327 1.6731 57.83 1.462 37.36 16.7137
137 2022-06-23 1.0493 142.11 1.9558 N/A 24.75 7.4388 N/A 0.85818 399.6 N/A N/A N/A 4.7085 N/A 4.9468 10.705 N/A N/A 1.013 139.9 10.475 7.5286 N/A N/A 18.2199 1.5212 5.4515 1.36 7.0367 8.236 15594.75 3.6192 82.1489 1367.21 21.0924 4.623 1.6713 57.44 1.4589 37.261 16.817
138 2022-06-22 1.0521 143.11 1.9558 N/A 24.712 7.4387 N/A 0.85885 396 N/A N/A N/A 4.6905 N/A 4.9467 10.6688 N/A N/A 1.0153 138.7 10.5045 7.5228 N/A N/A 18.255 1.5254 5.4349 1.366 7.0604 8.2589 15618.98 3.6432 82.4075 1369.29 21.1491 4.6345 1.6835 57.174 1.4615 37.281 16.7985
139 2022-06-21 1.055 143.75 1.9558 N/A 24.69 7.4393 N/A 0.8601 396.48 N/A N/A N/A 4.6435 N/A 4.9462 10.646 N/A N/A 1.0214 138.7 10.3283 7.5205 N/A N/A 18.3049 1.5177 5.442 1.366 7.068 8.2817 15639.62 3.6505 82.423 1365.09 21.2485 4.6399 1.6675 57.304 1.4612 37.294 16.7881
140 2022-06-20 1.0517 141.94 1.9558 N/A 24.728 7.4387 N/A 0.85748 397.85 N/A N/A N/A 4.652 N/A 4.9453 10.6375 N/A N/A 1.0162 137.3 10.4085 7.5175 N/A N/A 18.2239 1.5061 5.4117 1.3662 7.0346 8.2558 15589.89 3.639 81.994 1357.54 21.3016 4.6291 1.6549 56.872 1.4589 37.157 16.8603
141 2022-06-17 1.0486 141.21 1.9558 N/A 24.742 7.4384 N/A 0.855 400.53 N/A N/A N/A 4.7003 N/A 4.9469 10.6748 N/A N/A 1.0105 137.7 10.4525 7.5155 N/A N/A 18.1495 1.5039 5.3824 1.3631 7.0308 8.2314 15537.05 3.6112 81.871 1356.27 21.4474 4.6159 1.6601 56.371 1.4547 36.974 16.7133
142 2022-06-16 1.04 138.24 1.9558 N/A 24.742 7.4386 N/A 0.8555 398.1 N/A N/A N/A 4.7138 N/A 4.9443 10.6942 N/A N/A 1.0142 137.5 10.4588 7.5245 N/A N/A 18.0126 1.4939 5.2559 1.3446 6.9844 8.1638 15427.31 3.5934 81.1945 1346.88 21.4115 4.5786 1.6608 55.7 1.4451 36.566 16.6052
143 2022-06-15 1.0431 140.49 1.9558 N/A 24.703 7.4392 N/A 0.86328 397.96 N/A N/A N/A 4.669 N/A 4.9427 10.6278 N/A N/A 1.0435 137.5 10.3868 7.5245 N/A N/A 18.0465 1.5051 5.3164 1.3498 7.0013 8.1883 15361.97 3.6007 81.5142 1346.86 21.4763 4.6037 1.6706 55.627 1.4519 36.529 16.7111
144 2022-06-14 1.0452 140.62 1.9558 N/A 24.749 7.4403 N/A 0.86578 398.68 N/A N/A N/A 4.6563 N/A 4.9443 10.622 N/A N/A 1.0394 138.3 10.3945 7.5238 N/A N/A 18.06 1.5174 5.3329 1.3522 7.0417 8.2048 15400.02 3.6208 81.559 1346.72 21.4832 4.6224 1.6755 55.669 1.4541 36.566 16.7959
145 2022-06-13 1.0455 140.51 1.9558 N/A 24.724 7.4397 N/A 0.8585 399.3 N/A N/A N/A 4.6373 N/A 4.9459 10.616 N/A N/A 1.0375 138.7 10.3222 7.5215 N/A N/A 18.0495 1.4998 5.2785 1.3435 7.0434 8.2071 15376.17 3.5994 81.606 1349.93 21.2102 4.6195 1.6635 55.72 1.4538 36.425 16.807
146 2022-06-10 1.0578 141.69 1.9558 N/A 24.705 7.4389 N/A 0.85048 398.48 N/A N/A N/A 4.6053 N/A 4.9442 10.5255 N/A N/A 1.0404 137.7 10.1495 7.5225 N/A N/A 18.0116 1.4845 5.1718 1.3484 7.0868 8.3031 15393.27 3.5626 82.3355 1344.25 20.8285 4.6564 1.6482 56.101 1.462 36.774 16.5209
147 2022-06-09 1.0743 143.93 1.9558 N/A 24.689 7.4391 N/A 0.85653 396.45 N/A N/A N/A 4.5925 N/A 4.9453 10.5045 N/A N/A 1.0495 138.7 10.1818 7.5223 N/A N/A 18.5104 1.4985 5.2506 1.3506 7.1722 8.4317 15646.25 3.5859 83.526 1348.99 21.0248 4.7199 1.6673 56.872 1.4779 37.079 16.4132
148 2022-06-08 1.0739 143.92 1.9558 N/A 24.622 7.4386 N/A 0.85575 391.25 N/A N/A N/A 4.5698 N/A 4.945 10.4938 N/A N/A 1.0486 138.9 10.1395 7.5215 N/A N/A 18.453 1.4917 5.2447 1.3467 7.1785 8.4275 15577.86 3.5848 83.414 1349.34 21.0458 4.7187 1.6644 56.799 1.4769 37.076 16.4626
149 2022-06-07 1.0662 141.66 1.9558 N/A 24.739 7.4395 N/A 0.85365 389.33 N/A N/A N/A 4.5813 N/A 4.9426 10.5039 N/A N/A 1.0423 138.9 10.1843 7.5244 N/A N/A 17.8702 1.4884 5.1256 1.3437 7.1146 8.3656 15412.37 3.5661 82.873 1340.75 20.8435 4.6865 1.6582 56.421 1.4685 36.768 16.4059
150 2022-06-06 1.0726 140.16 1.9558 N/A 24.715 7.439 N/A 0.85415 388.05 N/A N/A N/A 4.5808 N/A 4.9424 10.452 N/A N/A 1.032 138.3 10.0853 7.5222 N/A N/A 17.796 1.4842 5.0986 1.3463 7.1223 8.4154 15464.73 3.5697 83.245 1341.13 20.9078 4.7076 1.6428 56.67 1.4732 36.774 16.4142
151 2022-06-03 1.073 139.59 1.9558 N/A 24.708 7.4388 N/A 0.8542 394.78 N/A N/A N/A 4.5955 N/A 4.9428 10.4589 N/A N/A 1.0296 137.9 10.103 7.522 N/A N/A 17.738 1.4805 5.1643 1.3484 7.1465 8.4167 15498.1 3.5751 83.273 1337.4 20.985 4.7094 1.6409 56.738 1.4741 36.777 16.6153
152 2022-06-02 1.0692 138.72 1.9558 N/A 24.702 7.4391 N/A 0.85195 394.9 N/A N/A N/A 4.5787 N/A 4.9398 10.4705 N/A N/A 1.0264 136.9 10.0845 7.5325 N/A N/A 17.6175 1.4829 5.1335 1.352 7.135 8.3896 15481.86 3.5705 82.922 1334.06 20.9831 4.6949 1.6413 56.467 1.4701 36.754 16.6143
153 2022-06-01 1.0712 138.68 1.9558 N/A 24.748 7.4393 N/A 0.85158 395.03 N/A N/A N/A 4.5913 N/A 4.9428 10.4758 N/A N/A 1.0305 137.1 10.0438 7.5345 N/A N/A 17.6223 1.4861 5.0646 1.3536 7.1586 8.4057 15574.33 3.5682 83.051 1331.44 21.0678 4.6951 1.6442 56.182 1.47 36.801 16.609
154 2022-05-31 1.0713 137.36 1.9558 N/A 24.714 7.4394 N/A 0.85138 396.2 N/A N/A N/A 4.5805 N/A 4.9408 10.5053 N/A N/A 1.0281 136.3 10.0983 7.541 N/A N/A 17.5817 1.4933 5.0965 1.3573 7.1402 8.4063 15580.15 3.5746 83.231 1329.32 20.987 4.6907 1.6459 56.323 1.4687 36.751 16.745
155 2022-05-30 1.0764 137.25 1.9558 N/A 24.712 7.4391 N/A 0.8515 392.18 N/A N/A N/A 4.5855 N/A 4.9441 10.518 N/A N/A 1.0327 136.9 10.1256 7.5305 N/A N/A 17.6416 1.4982 5.0629 1.3647 7.1735 8.449 15682.41 3.5722 83.475 1331.66 20.8994 4.6998 1.6439 56.322 1.4719 36.7 16.648
156 2022-05-27 1.0722 136.05 1.9558 N/A 24.7 7.4392 N/A 0.84875 392.83 N/A N/A N/A 4.5858 N/A 4.9427 10.5293 N/A N/A 1.0258 137.9 10.179 7.5379 N/A N/A 17.582 1.4995 5.0959 1.3661 7.1831 8.4165 15583.97 3.597 83.1915 1343.63 21.136 4.6952 1.6426 56.02 1.4679 36.589 16.746
157 2022-05-26 1.0697 135.95 1.9558 N/A 24.676 7.4409 N/A 0.85073 391.72 N/A N/A N/A 4.6083 N/A 4.9423 10.5983 N/A N/A 1.0283 138.1 10.2715 7.5355 N/A N/A 17.5588 1.511 5.1741 1.3715 7.2024 8.397 15628.91 3.5935 83.0065 1352.69 21.1935 4.7045 1.6541 55.975 1.4709 36.589 16.9312
158 2022-05-25 1.0656 135.34 1.9558 N/A 24.648 7.4405 N/A 0.85295 388.25 N/A N/A N/A 4.621 N/A 4.9416 10.5419 N/A N/A 1.0269 138.3 10.2704 7.5355 N/A N/A 17.3954 1.5126 5.1736 1.372 7.1334 8.3647 15587.59 3.585 82.6666 1354.61 21.2213 4.6833 1.6539 55.787 1.4676 36.55 16.7628
159 2022-05-24 1.072 136.49 1.9558 N/A 24.663 7.4411 N/A 0.8575 383.33 N/A N/A N/A 4.6015 N/A 4.9446 10.5013 N/A N/A 1.0334 139.3 10.289 7.5285 N/A N/A 17.2572 1.5152 5.1793 1.3714 7.1449 8.4143 15711.88 3.5848 83.185 1353.65 21.2456 4.7076 1.6656 56.152 1.4722 36.609 16.7814
160 2022-05-23 1.0659 136.05 1.9558 N/A 24.594 7.4413 N/A 0.84783 381.65 N/A N/A N/A 4.621 N/A 4.947 10.4918 N/A N/A 1.031 139.1 10.252 7.5275 N/A N/A 16.8672 1.4982 5.1623 1.3626 7.085 8.3664 15609 3.5745 82.6795 1344.19 21.1273 4.6782 1.6463 55.686 1.4639 36.41 16.7437
161 2022-05-20 1.0577 135.34 1.9558 N/A 24.67 7.4424 N/A 0.8482 382.93 N/A N/A N/A 4.6365 N/A 4.9477 10.4915 N/A N/A 1.028 138.5 10.262 7.5335 N/A N/A 16.8201 1.498 5.1989 1.3526 7.0638 8.2999 15501.99 3.533 82.1617 1340.58 21.0314 4.6422 1.6518 55.181 1.4588 36.284 16.7131
162 2022-05-19 1.0525 134.46 1.9558 N/A 24.7 7.4423 N/A 0.84728 385.83 N/A N/A N/A 4.6423 N/A 4.9474 10.5098 N/A N/A 1.0265 139.5 10.3102 7.5395 N/A N/A 16.8037 1.5036 5.2094 1.349 7.1028 8.2594 15416.76 3.5623 81.7115 1343.21 21.0043 4.6363 1.6551 55.14 1.4576 36.343 16.8315
163 2022-05-18 1.0523 135.76 1.9558 N/A 24.647 7.4419 N/A 0.8467 382.88 N/A N/A N/A 4.6443 N/A 4.9473 10.4675 N/A N/A 1.0486 138.9 10.2125 7.535 N/A N/A 16.7811 1.498 5.1974 1.3488 7.0972 8.2591 15446 3.526 81.6455 1332.76 20.9204 4.6254 1.6548 55.077 1.4598 36.399 16.7313
164 2022-05-17 1.0541 136.32 1.9558 N/A 24.712 7.4414 N/A 0.844 386.3 N/A N/A N/A 4.6488 N/A 4.9478 10.4393 N/A N/A 1.0457 138.7 10.178 7.5245 N/A N/A 16.6027 1.4993 5.2621 1.3517 7.0899 8.2744 15433.23 3.5387 81.6515 1333.66 21.0273 4.6211 1.6561 55.137 1.4589 36.361 16.844
165 2022-05-16 1.0422 135.01 1.9558 N/A 24.71 7.4418 N/A 0.85045 385.85 N/A N/A N/A 4.6675 N/A 4.9469 10.4978 N/A N/A 1.0479 138.3 10.2188 7.5225 N/A N/A 16.3121 1.5057 5.2819 1.3473 7.0786 8.1812 15294.29 3.5474 81.081 1337.9 20.9324 4.5836 1.6601 54.705 1.4531 36.274 16.9195
166 2022-05-13 1.0385 133.91 1.9558 N/A 24.74 7.4412 N/A 0.85115 385 N/A N/A N/A 4.6883 N/A 4.9455 10.4905 N/A N/A 1.0385 140.1 10.2043 7.52 N/A N/A 16.0687 1.5067 5.3204 1.3505 7.0513 8.1522 15193.55 3.5586 80.4315 1330.83 20.988 4.5673 1.6633 54.449 1.45 36.109 16.7789
167 2022-05-12 1.0408 133.85 1.9558 N/A 24.925 7.4413 N/A 0.85293 382.2 N/A N/A N/A 4.668 N/A 4.947 10.5648 N/A N/A 1.0377 139.7 10.2898 7.5235 N/A N/A 16.0132 1.5163 5.4161 1.3569 7.0691 8.1702 15255.73 3.5981 80.667 1341.98 21.2531 4.5725 1.6692 54.589 1.4529 36.15 16.8806
168 2022-05-11 1.0553 137.07 1.9558 N/A 25.365 7.4393 N/A 0.85393 379.13 N/A N/A N/A 4.6575 N/A 4.947 10.526 N/A N/A 1.0446 139.3 10.1793 7.5365 N/A N/A 16.1851 1.5055 5.3859 1.3685 7.0893 8.2839 15308.87 3.6148 81.4935 1343.99 21.387 4.6185 1.6645 54.992 1.4622 36.492 16.9275
169 2022-05-10 1.0554 137.38 1.9558 N/A 25.014 7.4386 N/A 0.85595 380.15 N/A N/A N/A 4.6763 N/A 4.9458 10.6075 N/A N/A 1.0479 139.5 10.2315 7.5385 N/A N/A 16.0883 1.5162 5.4232 1.3707 7.0967 8.2847 15349.06 3.6587 81.5425 1346.56 21.4716 4.6248 1.6707 55.289 1.4667 36.448 17.005
170 2022-05-09 1.0559 138.1 1.9558 N/A 25.055 7.4385 N/A 0.85235 383.23 N/A N/A N/A 4.6985 N/A 4.9467 10.5818 N/A N/A 1.0462 139.5 10.0583 7.5335 N/A N/A 15.8941 1.5048 5.4321 1.3656 7.0886 8.2887 15367.67 3.6239 81.7415 1345.48 21.4341 4.6285 1.6584 55.67 1.4676 36.497 17.1332
171 2022-05-06 1.057 137.9 1.9558 N/A 24.665 7.44 N/A 0.85625 381.47 N/A N/A N/A 4.7028 N/A 4.949 10.4686 N/A N/A 1.0419 138.3 9.9808 7.5336 N/A N/A 15.8078 1.4888 5.3183 1.356 7.0506 8.2969 15312.44 3.5965 81.298 1343.9 21.3555 4.6191 1.644 55.467 1.4642 36.303 16.9614
172 2022-05-05 1.0568 137.18 1.9558 N/A 24.606 7.4405 N/A 0.8519 378.65 N/A N/A N/A 4.6673 N/A 4.9485 10.3748 N/A N/A 1.0355 138 9.8463 7.5398 N/A N/A 15.7051 1.4669 5.2192 1.3483 6.9944 8.2948 15250.92 3.5916 80.6185 1330.89 21.2069 4.5955 1.6273 55.364 1.4561 36.026 16.5862
173 2022-05-04 1.0531 136.84 1.9558 N/A 24.644 7.4409 N/A 0.84194 377.55 N/A N/A N/A 4.6875 N/A 4.947 10.3968 N/A N/A 1.0324 137.2 9.9042 7.5499 N/A N/A 15.5769 1.478 5.24 1.3498 6.9594 8.2655 15201.44 3.5487 80.4035 1331.45 21.2947 4.5847 1.6333 55.26 1.4559 36.137 16.6489
174 2022-05-03 1.0556 137.06 1.9558 N/A 24.662 7.4403 N/A 0.8413 382.15 N/A N/A N/A 4.6925 N/A 4.9475 10.3978 N/A N/A 1.0272 137.6 9.909 7.5555 N/A N/A 15.6941 1.4825 5.3143 1.357 6.9759 8.2838 15288.47 3.5597 80.842 1335.64 21.5025 4.5956 1.6366 55.455 1.4605 36.387 16.8303
175 2022-05-02 1.0524 136.63 1.9558 N/A 24.671 7.4391 N/A 0.8381 378.51 N/A N/A N/A 4.685 N/A 4.9478 10.4035 N/A N/A 1.0253 137.2 9.9248 7.561 N/A N/A 15.6697 1.4913 5.248 1.356 6.9548 8.2581 15293.41 3.5277 80.497 1333.71 21.4758 4.5816 1.6362 55.229 1.4585 36.208 16.7383
176 2022-04-29 1.054 137.01 1.9558 N/A 24.605 7.4415 N/A 0.83908 378.71 N/A N/A N/A 4.678 N/A 4.9479 10.2958 N/A N/A 1.0229 137.8 9.7525 7.5667 N/A N/A 15.6385 1.4699 5.1608 1.3426 6.9441 8.2703 15301.52 3.4993 80.638 1326.71 21.4181 4.5886 1.6119 55.2 1.4545 36.026 16.6473
177 2022-04-28 1.0485 137.13 1.9558 N/A 24.526 7.4421 N/A 0.8435 377.06 N/A N/A N/A 4.6891 N/A 4.9479 10.3594 N/A N/A 1.0216 137.8 9.899 7.5703 N/A N/A 15.5362 1.4814 5.2465 1.3498 6.9381 8.2267 15222.05 3.5096 80.367 1337.82 21.4531 4.5741 1.6221 54.845 1.4556 36.152 16.7472
178 2022-04-27 1.0583 135.57 1.9558 N/A 24.55 7.441 N/A 0.84215 379.74 N/A N/A N/A 4.7043 N/A 4.948 10.4035 N/A N/A 1.0229 138.2 9.7838 7.565 N/A N/A 15.6857 1.4828 5.3045 1.3572 6.9377 8.3045 15259.86 3.5178 81.0705 1341.98 21.6259 4.6142 1.6118 55.195 1.4602 36.331 16.8406
179 2022-04-26 1.0674 136.15 1.9558 N/A 24.423 7.4393 N/A 0.84135 374.46 N/A N/A N/A 4.6466 N/A 4.9458 10.3935 N/A N/A 1.0229 138.4 9.7943 7.5625 N/A N/A 15.7944 1.4828 5.249 1.3613 6.9837 8.3735 15365.76 3.5147 81.7265 1340.02 21.6538 4.6485 1.6102 55.681 1.4666 36.596 16.7787
180 2022-04-25 1.0746 137.73 1.9558 N/A 24.418 7.4391 N/A 0.8433 374.08 N/A N/A N/A 4.6398 N/A 4.9455 10.3476 N/A N/A 1.0267 139.2 9.7018 7.562 N/A N/A 15.864 1.4972 5.1953 1.3709 7.0398 8.4325 15533.84 3.5306 82.321 1344.49 21.8989 4.6815 1.624 56.259 1.4757 36.542 16.8549
181 2022-04-22 1.0817 138.83 1.9558 N/A 24.32 7.4402 N/A 0.83925 370.35 N/A N/A N/A 4.6336 N/A 4.9455 10.278 N/A N/A 1.0336 139.8 9.6255 7.5625 N/A N/A 15.9446 1.4816 5.0926 1.3714 7.0332 8.4859 15603.47 3.5288 82.6943 1344.04 22.0034 4.6784 1.6193 56.721 1.4784 36.724 16.8652
182 2022-04-21 1.0887 139.61 1.9558 N/A 24.38 7.4403 N/A 0.83523 370.6 N/A N/A N/A 4.63 N/A 4.945 10.2553 N/A N/A 1.0335 139 9.5788 7.5635 N/A N/A 15.9983 1.4653 5.0324 1.36 7.0228 8.5406 15624.02 3.5153 82.965 1348.33 21.8836 4.6716 1.6053 57.081 1.482 36.891 16.5996
183 2022-04-20 1.083 138.53 1.9558 N/A 24.409 7.4405 N/A 0.82965 371.36 N/A N/A N/A 4.6338 N/A 4.9436 10.23 N/A N/A 1.0254 139.2 9.5443 7.561 N/A N/A 15.8892 1.4581 5.0481 1.3579 6.9448 8.494 15537.05 3.4908 82.6348 1337.89 21.6392 4.6415 1.595 56.747 1.4779 36.567 16.3019
184 2022-04-19 1.0803 138.4 1.9558 N/A 24.424 7.4391 N/A 0.82955 374.12 N/A N/A N/A 4.6553 N/A 4.9411 10.3408 N/A N/A 1.0208 139.8 9.5228 7.562 N/A N/A 15.8416 1.4663 5.0261 1.3631 6.9008 8.4698 15498.35 3.5038 82.6038 1339.46 21.4725 4.5961 1.6016 56.683 1.4763 36.466 16.0401
185 2022-04-14 1.0878 136.32 1.9558 N/A 24.42 7.4389 N/A 0.82908 376.57 N/A N/A N/A 4.6478 N/A 4.9459 10.3008 N/A N/A 1.0189 140.4 9.5313 7.5587 N/A N/A 15.9046 1.4612 5.1226 1.3663 6.932 8.5298 15621.3 3.4896 82.814 1334.71 21.5941 4.603 1.5957 56.759 1.4732 36.615 15.9331
186 2022-04-13 1.0826 136.26 1.9558 N/A 24.45 7.4377 N/A 0.8328 378.45 N/A N/A N/A 4.6453 N/A 4.9415 10.3323 N/A N/A 1.0116 140.2 9.5693 7.5538 N/A N/A 15.7992 1.4603 5.0449 1.37 6.8939 8.4867 15549.1 3.4782 82.478 1328.47 21.417 4.5799 1.5991 56.446 1.4769 36.305 15.682
187 2022-04-12 1.0861 136.29 1.9558 N/A 24.45 7.4379 N/A 0.83455 377.78 N/A N/A N/A 4.6552 N/A 4.9417 10.332 N/A N/A 1.0131 139.6 9.5395 7.5513 N/A N/A 15.9548 1.4599 5.0944 1.3724 6.9199 8.5112 15609.66 3.4972 82.7285 1335.49 21.5616 4.5972 1.5874 56.574 1.4803 36.531 15.849
188 2022-04-11 1.09 137.01 1.9558 N/A 24.429 7.4375 N/A 0.83693 378.27 N/A N/A N/A 4.6456 N/A 4.9397 10.3128 N/A N/A 1.018 140 9.5478 7.5519 N/A N/A 16.0485 1.4654 5.155 1.3738 6.9405 8.544 15658.28 3.501 82.7085 1345.23 21.8653 4.6112 1.5938 56.753 1.4874 36.613 15.9127
189 2022-04-08 1.0861 134.87 1.9558 N/A 24.479 7.4372 N/A 0.83355 375.66 N/A N/A N/A 4.6437 N/A 4.9425 10.2768 N/A N/A 1.0155 139.6 9.508 7.549 N/A N/A 16.0237 1.4552 5.1583 1.3675 6.9115 8.5134 15601.96 3.501 82.389 1333.12 21.8729 4.585 1.5849 55.99 1.4801 36.488 15.9968
190 2022-04-07 1.0916 135.32 1.9558 N/A 24.512 7.4378 N/A 0.8345 379.26 N/A N/A N/A 4.637 N/A 4.9419 10.313 N/A N/A 1.0185 141 9.5595 7.5562 N/A N/A 16.0929 1.4578 5.146 1.3704 6.9448 8.5554 15692.35 3.5259 82.951 1330.92 21.9806 4.6046 1.5816 56.114 1.4848 36.541 16.052
191 2022-04-06 1.0923 135.3 1.9558 N/A 24.441 7.4378 N/A 0.83473 377.77 N/A N/A N/A 4.6328 N/A 4.9433 10.2855 N/A N/A 1.0187 141.4 9.5523 7.547 N/A N/A 16.0998 1.4431 5.0996 1.3647 6.9498 8.5617 15683.36 3.5199 82.8343 1330.44 21.8759 4.604 1.5718 56.167 1.4844 36.701 15.9934
192 2022-04-05 1.0969 134.76 1.9558 N/A 24.338 7.4378 N/A 0.8349 370.93 N/A N/A N/A 4.6265 N/A 4.9438 10.2593 N/A N/A 1.0141 141.6 9.5398 7.5399 N/A N/A 16.15 1.4374 5.0384 1.3647 6.9783 8.5917 15732.77 3.5152 82.635 1330.81 21.7474 4.6185 1.5657 56.194 1.4867 36.697 15.9529
193 2022-04-04 1.1005 135.08 1.9558 N/A 24.32 7.4385 N/A 0.8389 369.15 N/A N/A N/A 4.6375 N/A 4.9432 10.3849 N/A N/A 1.0203 141.8 9.5489 7.5455 N/A N/A 16.183 1.4651 5.1162 1.3749 7.0026 8.6226 15783.89 3.5312 83.118 1338.41 21.82 4.643 1.586 56.521 1.4938 36.894 16.0957
194 2022-04-01 1.1052 135.35 1.9558 N/A 24.376 7.4388 N/A 0.84145 368.12 N/A N/A N/A 4.6401 N/A 4.9452 10.332 N/A N/A 1.0217 142 9.6628 7.5675 N/A N/A 16.2411 1.4696 5.2188 1.3805 7.0311 8.6596 15887.5 3.5315 83.9847 1345.61 21.9087 4.6534 1.5911 57.084 1.4985 36.941 16.1685
195 2022-03-31 1.1101 135.17 1.9558 N/A 24.375 7.4379 N/A 0.84595 369.77 N/A N/A N/A 4.6531 N/A 4.9463 10.337 N/A N/A 1.0267 142 9.711 7.574 N/A N/A 16.2823 1.4829 5.3009 1.3896 7.0403 8.6918 15947 3.5243 84.134 1347.37 22.0903 4.6677 1.6014 57.514 1.5028 36.911 16.1727
196 2022-03-30 1.1126 135.47 1.9558 N/A 24.45 7.4391 N/A 0.84563 368.13 N/A N/A N/A 4.6679 N/A 4.9477 10.3498 N/A N/A 1.0309 142.2 9.6398 7.572 N/A N/A 16.3296 1.4809 5.2808 1.3891 7.0666 8.7081 15957.24 3.5399 84.38 1346.97 22.1557 4.6779 1.5947 57.906 1.5064 37.144 16.1288
197 2022-03-29 1.1085 136.66 1.9558 N/A 24.464 7.4388 N/A 0.8444 369.8 N/A N/A N/A 4.6594 N/A 4.9478 10.329 N/A N/A 1.0362 142.2 9.5995 7.5815 N/A N/A 16.3275 1.4795 5.2434 1.387 7.055 8.6767 15896.31 3.5505 83.9685 1345.62 22.1561 4.6707 1.6054 57.602 1.5051 37.29 16.1804
198 2022-03-28 1.0966 135.93 1.9558 N/A 24.65 7.4393 N/A 0.83643 374.13 N/A N/A N/A 4.718 N/A 4.9483 10.4225 N/A N/A 1.0257 142.8 9.5123 7.5735 N/A N/A 16.275 1.459 5.2133 1.3702 6.9862 8.5861 15737.77 3.5313 83.4825 1342.49 21.9841 4.6238 1.5838 57.08 1.4921 37.027 15.9925
199 2022-03-25 1.1002 134.07 1.9558 N/A 24.645 7.4404 N/A 0.8338 373.81 N/A N/A N/A 4.7307 N/A 4.9487 10.3505 N/A N/A 1.0207 142.2 9.5205 7.5754 N/A N/A 16.3304 1.4624 5.2634 1.3781 7.0007 8.6117 15777.69 3.5351 83.8235 1343.32 21.9908 4.6324 1.5787 57.322 1.4919 36.906 16.0386
200 2022-03-24 1.0978 133.71 1.9558 N/A 24.72 7.4397 N/A 0.83288 374.44 N/A N/A N/A 4.7421 N/A 4.9489 10.3555 N/A N/A 1.0225 141.2 9.4923 7.5745 N/A N/A 16.2917 1.4668 5.3057 1.3806 6.9933 8.5897 15778.31 3.5461 83.879 1342.53 22.177 4.6396 1.5812 57.426 1.4912 36.837 16.1478
201 2022-03-23 1.0985 132.65 1.9558 N/A 24.605 7.4381 N/A 0.8328 372.25 N/A N/A N/A 4.7052 N/A 4.9463 10.4005 N/A N/A 1.0269 141.4 9.6425 7.571 N/A N/A 16.3108 1.4728 5.3903 1.384 7.0003 8.5948 15792.11 3.5407 83.9675 1337.76 22.1978 4.6401 1.5822 57.565 1.4919 36.965 16.2501
202 2022-03-22 1.1024 132.96 1.9558 N/A 24.679 7.4402 N/A 0.83228 371.23 N/A N/A N/A 4.6851 N/A 4.9463 10.3822 N/A N/A 1.0275 142.7 9.6233 7.575 N/A N/A 16.3432 1.4802 5.4105 1.3867 7.0137 8.6285 15808.01 3.5521 83.9145 1343.81 22.3667 4.6483 1.586 57.749 1.4957 36.881 16.343
203 2022-03-21 1.1038 131.57 1.9558 N/A 24.683 7.4411 N/A 0.83775 374.48 N/A N/A N/A 4.696 N/A 4.947 10.4088 N/A N/A 1.0278 142.7 9.6575 7.5733 N/A N/A 16.3773 1.4897 5.502 1.3898 7.0152 8.6387 15826.61 3.5637 84.1835 1342.62 22.48 4.6415 1.5997 57.817 1.496 37.038 16.4543
204 2022-03-18 1.1008 131.4 1.9558 N/A 24.837 7.4423 N/A 0.83925 375.33 N/A N/A N/A 4.7135 N/A 4.9483 10.4303 N/A N/A 1.0314 142.9 9.694 7.5685 N/A N/A 16.3054 1.4945 5.5784 1.3911 7.0031 8.6101 15782.11 3.5761 83.7825 1337.31 22.5905 4.6157 1.6026 57.622 1.4952 36.745 16.5347
205 2022-03-17 1.1051 131.27 1.9558 N/A 24.777 7.4439 N/A 0.84315 372.05 N/A N/A N/A 4.6889 N/A 4.9465 10.4503 N/A N/A 1.0385 142.1 9.78 7.573 N/A N/A 16.3123 1.5055 5.6339 1.3998 7.0176 8.6391 15835.97 3.5777 83.8435 1340.02 22.788 4.6364 1.613 57.69 1.498 36.767 16.5286
206 2022-03-16 1.0994 130.05 1.9558 N/A 24.687 7.4412 N/A 0.83988 371.18 N/A N/A N/A 4.6765 N/A 4.9473 10.4205 N/A N/A 1.0336 143.5 9.7988 7.5725 N/A N/A 16.1783 1.5165 5.6523 1.3967 6.9817 8.5996 15690.36 3.5872 83.7805 1353.77 22.854 4.6147 1.6168 57.433 1.4966 36.681 16.5574
207 2022-03-15 1.0991 129.67 1.9558 N/A 24.867 7.441 N/A 0.84053 371.41 N/A N/A N/A 4.7355 N/A 4.9482 10.526 N/A N/A 1.0322 144.9 9.849 7.575 N/A N/A 16.0968 1.5234 5.6385 1.4099 7.0117 8.6026 15710.44 3.6088 83.9555 1366.05 22.9352 4.6239 1.6216 57.536 1.4993 36.842 16.6249
208 2022-03-14 1.096 129.3 1.9558 N/A 24.89 7.4405 N/A 0.83915 373.88 N/A N/A N/A 4.7218 N/A 4.949 10.5368 N/A N/A 1.0249 145.1 9.8588 7.5745 N/A N/A 16.2 1.5137 5.5286 1.3978 6.9738 8.5815 15678.53 3.586 83.931 1357.77 22.8311 4.6087 1.613 57.398 1.4947 36.59 16.5029
209 2022-03-11 1.099 128.46 1.9558 N/A 25.213 7.4402 N/A 0.8397 380.92 N/A N/A N/A 4.782 N/A 4.949 10.646 N/A N/A 1.023 144.9 9.8033 7.5713 N/A N/A 16.2554 1.5017 5.5077 1.4024 6.9633 8.6007 15696.64 3.5683 83.9875 1354.04 22.9524 4.6098 1.6053 57.461 1.4949 36.542 16.4896
210 2022-03-10 1.1084 128.54 1.9558 N/A 25.316 7.4401 N/A 0.84175 381.63 N/A N/A N/A 4.8239 N/A 4.9491 10.7073 N/A N/A 1.027 145.5 9.919 7.5665 N/A N/A 16.574 1.5109 5.5958 1.4189 7.0063 8.6688 15824.95 3.6219 84.607 1360.48 23.3153 4.6414 1.6185 57.825 1.5058 36.71 16.7264
211 2022-03-09 1.0993 127.31 1.9558 N/A 25.364 7.444 N/A 0.8357 379.66 N/A N/A N/A 4.8196 N/A 4.9485 10.734 N/A N/A 1.0198 145.3 9.798 7.5625 N/A N/A 16.1323 1.4991 5.5201 1.4108 6.9454 8.5974 15710.06 3.5978 84.2025 1357.08 23.2145 4.6028 1.6055 57.259 1.4966 36.326 16.656
212 2022-03-08 1.0892 126.03 1.9558 N/A 25.642 7.4441 N/A 0.83185 388.28 N/A N/A N/A 4.9103 N/A 4.9494 10.8803 N/A N/A 1.0111 145.9 9.7925 7.5715 N/A N/A 15.8183 1.4971 5.5346 1.3978 6.8805 8.5183 15639.76 3.6022 83.924 1344.71 23.2866 4.5556 1.5958 56.9 1.4856 36.156 16.7051
213 2022-03-07 1.0895 125.55 1.9558 N/A 25.584 7.4406 N/A 0.82625 393.25 N/A N/A N/A 4.9525 N/A 4.9494 10.8573 N/A N/A 1.0069 145.8 9.8325 7.56 N/A N/A 15.6577 1.4751 5.5065 1.3864 6.8846 8.5154 15685.76 3.5653 83.8125 1338.45 23.0249 4.5426 1.5861 56.832 1.4831 35.866 16.6951
214 2022-03-04 1.0929 126.17 1.9558 N/A 25.737 7.4394 N/A 0.82388 386.54 N/A N/A N/A 4.853 N/A 4.9495 10.7935 N/A N/A 1.0056 144.2 9.8358 7.5584 N/A N/A 15.5681 1.4872 5.5313 1.3937 6.9065 8.5411 15725.3 3.5603 83.4354 1332.23 22.7543 4.5661 1.6005 56.814 1.4872 35.776 16.8044
215 2022-03-03 1.1076 128.18 1.9558 N/A 25.634 7.4399 N/A 0.82773 378.64 N/A N/A N/A 4.7691 N/A 4.9496 10.7688 N/A N/A 1.0192 143.4 9.8418 7.57 N/A N/A 15.6897 1.5139 5.6041 1.3992 6.9996 8.6547 15934.42 3.5872 84.174 1334.2 22.8945 4.637 1.6329 57.293 1.5042 36.063 16.8798
216 2022-03-02 1.1106 128.08 1.9558 N/A 25.866 7.4387 N/A 0.83316 382.31 N/A N/A N/A 4.8021 N/A 4.9493 10.788 N/A N/A 1.0216 143 9.8826 7.574 N/A N/A 15.628 1.5272 5.7313 1.4088 7.0153 8.6785 15980.03 3.5954 84.1765 1338.99 22.999 4.6595 1.6404 57.206 1.5059 36.339 17.1904
217 2022-03-01 1.1162 128.15 1.9558 N/A 25.465 7.4377 N/A 0.8329 379.6 N/A N/A N/A 4.7947 N/A 4.949 10.6893 N/A N/A 1.0247 142 9.8598 7.567 117.201 N/A 15.5509 1.5365 5.7598 1.4158 7.0462 8.7234 16033.36 3.6152 84.5015 1342.6 22.8558 4.6802 1.6484 57.295 1.515 36.505 17.2145
218 2022-02-28 1.1199 129.31 1.9558 N/A 24.997 7.4404 N/A 0.8355 369.72 N/A N/A N/A 4.6835 N/A 4.9484 10.6055 N/A N/A 1.0336 141.8 9.9465 7.5655 115.4842 N/A 15.4532 1.5508 5.7828 1.4264 7.067 8.7514 16100.72 3.63 84.554 1347.62 22.9011 4.7019 1.6628 57.432 1.5201 36.593 17.2863
219 2022-02-25 1.1216 129.64 1.9558 N/A 24.66 7.4418 N/A 0.8374 365.28 N/A N/A N/A 4.6369 N/A 4.9479 10.5848 N/A N/A 1.0398 140.8 9.9756 7.5535 92.5673 N/A 15.4799 1.5541 5.738 1.4325 7.0828 8.7578 16088.71 3.6389 84.347 1346.44 22.9145 4.7107 1.6651 57.549 1.5176 36.441 17.0315
220 2022-02-24 1.1163 128.28 1.9558 N/A 25.09 7.4405 N/A 0.83463 368.63 N/A N/A N/A 4.6554 N/A 4.9501 10.7338 N/A N/A 1.032 142 10.0878 7.552 95.7175 N/A 16.0525 1.5593 5.6874 1.4316 7.0601 8.7178 16074 3.6618 84.296 1347.7 22.9355 4.6896 1.6692 57.45 1.5125 36.514 17.1634
221 2022-02-23 1.1344 130.58 1.9558 N/A 24.473 7.4388 N/A 0.83463 357.25 N/A N/A N/A 4.5481 N/A 4.9468 10.5658 N/A N/A 1.0431 141.2 10.0335 7.5362 90.8791 N/A 15.6871 1.5592 5.6808 1.4394 7.1669 8.8529 16270.12 3.6515 84.6135 1350.23 22.9079 4.748 1.6679 57.98 1.5253 36.601 17.0508
222 2022-02-22 1.1342 130.54 1.9558 N/A 24.496 7.4392 N/A 0.83685 355.89 N/A N/A N/A 4.5447 N/A 4.9464 10.5996 N/A N/A 1.0422 141.2 10.1018 7.537 89.8055 N/A 15.6959 1.5739 5.7677 1.4441 7.1771 8.8495 16293.92 3.6584 84.758 1353.34 23.033 4.7472 1.6848 58.208 1.5268 36.748 17.1768
223 2022-02-21 1.1338 130.2 1.9558 N/A 24.345 7.4397 N/A 0.83298 357.54 N/A N/A N/A 4.5351 N/A 4.9448 10.6535 N/A N/A 1.0387 141.4 10.1738 7.536 89.0866 N/A 15.4689 1.5751 5.8045 1.4454 7.1831 8.8443 16276.34 3.6433 84.677 1353.02 22.9951 4.7387 1.687 58.3 1.5265 36.588 17.1895
224 2022-02-18 1.1354 130.59 1.9558 N/A 24.337 7.4382 N/A 0.83425 356.37 N/A N/A N/A 4.5201 N/A 4.9453 10.5796 N/A N/A 1.0452 140.8 10.1465 7.5355 86.2815 N/A 15.4678 1.5754 5.8435 1.4424 7.184 8.8566 16304.49 3.6276 84.6525 1356.45 23.027 4.7528 1.6896 58.403 1.5255 36.435 17.0858
225 2022-02-17 1.137 130.84 1.9558 N/A 24.383 7.4398 N/A 0.83493 356.08 N/A N/A N/A 4.5065 N/A 4.9432 10.593 N/A N/A 1.0466 141.6 10.1225 7.533 86.388 N/A 15.4945 1.5786 5.8495 1.4439 7.206 8.8692 16291.53 3.6231 85.2935 1360.7 23.0367 4.7612 1.695 58.314 1.5278 36.537 16.9893
226 2022-02-16 1.1372 131.56 1.9558 N/A 24.365 7.441 N/A 0.8394 355.65 N/A N/A N/A 4.4961 N/A 4.9436 10.5363 N/A N/A 1.0516 141.4 10.1095 7.5295 85.3679 N/A 15.481 1.5859 5.8765 1.4416 7.2101 8.8712 16232.55 3.6256 85.3885 1361.31 23.1718 4.7595 1.7108 58.346 1.5291 36.771 17.214
227 2022-02-15 1.1345 131.18 1.9558 N/A 24.419 7.4422 N/A 0.83765 355.33 N/A N/A N/A 4.5036 N/A 4.941 10.5739 N/A N/A 1.0483 141.2 10.0893 7.529 85.5025 N/A 15.4716 1.5888 5.8977 1.4433 7.1969 8.8527 16187.89 3.6596 85.443 1357.5 23.1113 4.7493 1.7143 58.144 1.5265 36.724 17.1767
228 2022-02-14 1.1316 130.6 1.9558 N/A 24.527 7.4411 N/A 0.8372 357.06 N/A N/A N/A 4.54 N/A 4.9457 10.6158 N/A N/A 1.0472 142.6 10.0693 7.5293 86.348 N/A 15.351 1.5902 5.8965 1.4431 7.1937 8.8283 16190.53 3.6835 85.4715 1354.5 23.1331 4.742 1.7112 58.114 1.5247 36.8 17.131
229 2022-02-11 1.1417 132.24 1.9558 N/A 24.405 7.44 N/A 0.83958 353.38 N/A N/A N/A 4.5204 N/A 4.9458 10.553 N/A N/A 1.0557 141.8 10.0732 7.5312 85.855 N/A 15.4066 1.5927 5.9263 1.4498 7.2564 8.9054 16339.46 3.6958 85.8535 1363.68 23.3183 4.7832 1.7085 58.482 1.5339 37.282 17.2736
230 2022-02-10 1.1439 132.42 1.9558 N/A 24.35 7.4404 N/A 0.84248 354.02 N/A N/A N/A 4.4921 N/A 4.9451 10.5275 N/A N/A 1.0571 141.8 10.0693 7.5275 85.0187 N/A 15.4838 1.5894 5.9668 1.4498 7.2722 8.9142 16390.21 3.6796 85.9373 1367.52 23.3584 4.7855 1.7076 58.583 1.5345 37.331 17.3078
231 2022-02-09 1.1435 132.04 1.9558 N/A 24.288 7.4437 N/A 0.84255 352.94 N/A N/A N/A 4.5135 N/A 4.9449 10.4075 N/A N/A 1.0555 142.2 10.0585 7.5285 85.5289 N/A 15.551 1.5933 6.0198 1.4514 7.2759 8.9106 16390.99 3.6817 85.5765 1365.72 23.4719 4.7853 1.7107 58.603 1.5349 37.404 17.5281
232 2022-02-08 1.1408 131.68 1.9558 N/A 24.259 7.4437 N/A 0.84363 353.09 N/A N/A N/A 4.5312 N/A 4.945 10.4433 N/A N/A 1.0545 142.4 10.0758 7.5215 85.7797 N/A 15.5558 1.6025 6.0209 1.4505 7.2636 8.8923 16418.4 3.6753 85.2545 1367.88 23.5601 4.774 1.7196 58.715 1.5349 37.606 17.638
233 2022-02-07 1.1447 131.59 1.9558 N/A 24.222 7.4443 N/A 0.84685 353.48 N/A N/A N/A 4.5432 N/A 4.9461 10.4483 N/A N/A 1.0571 143.4 10.0658 7.52 86.5824 N/A 15.5235 1.6097 6.0541 1.4546 7.2807 8.9202 16478.97 3.6547 85.5345 1371.76 23.575 4.7909 1.7278 58.978 1.5389 37.735 17.7
234 2022-02-04 1.1464 131.72 1.9558 N/A 24.36 7.4432 N/A 0.84593 352.92 N/A N/A N/A 4.5474 N/A 4.9466 10.4465 N/A N/A 1.0567 142.8 10.0483 7.5275 87.3095 N/A 15.5072 1.6165 6.083 1.4583 7.2923 8.9286 16493.16 3.6741 85.6445 1374.04 23.5856 4.7914 1.7287 58.754 1.5419 37.797 17.5875
235 2022-02-03 1.1286 129.63 1.9558 N/A 24.135 7.4388 N/A 0.83208 353.94 N/A N/A N/A 4.5315 N/A 4.9461 10.387 N/A N/A 1.0407 142.4 9.9545 7.5295 86.1788 N/A 15.3047 1.5849 5.9843 1.4334 7.1795 8.7966 16243.28 3.5949 84.518 1358.42 23.2359 4.7215 1.7001 57.608 1.5212 37.424 17.2864
236 2022-02-02 1.1323 129.37 1.9558 N/A 24.298 7.4383 N/A 0.83395 354.45 N/A N/A N/A 4.5449 N/A 4.9463 10.385 N/A N/A 1.0399 143.2 9.9228 7.526 85.815 N/A 15.3011 1.5828 5.9677 1.433 7.2026 8.8245 16243.98 3.5777 84.613 1359.23 23.2134 4.7392 1.701 57.785 1.5254 37.575 17.3459
237 2022-02-01 1.126 129.12 1.9558 N/A 24.335 7.4407 N/A 0.83498 356.38 N/A N/A N/A 4.5804 N/A 4.9465 10.4438 N/A N/A 1.0374 143.8 9.9638 7.5275 86.3238 N/A 15.0644 1.5868 5.9572 1.4299 7.1625 8.7779 16126.29 3.566 84.197 1353.61 23.1296 4.7129 1.7032 57.516 1.5199 37.378 17.1633
238 2022-01-31 1.1156 128.79 1.9558 N/A 24.372 7.4419 N/A 0.83153 357.19 N/A N/A N/A 4.5892 N/A 4.9475 10.489 N/A N/A 1.0404 143.2 10.0085 7.5293 86.7251 N/A 14.931 1.582 6.003 1.4233 7.0963 8.6994 16036.76 3.564 83.3655 1349.08 23.1856 4.6693 1.6983 56.985 1.511 37.144 17.3734
239 2022-01-28 1.1138 128.68 1.9558 N/A 24.443 7.4432 N/A 0.83178 358.42 N/A N/A N/A 4.5755 N/A 4.9463 10.552 N/A N/A 1.0378 144 10.026 7.529 86.6113 N/A 15.1424 1.5971 6.0147 1.4239 7.0857 8.681 16047.76 3.5697 83.6015 1349.47 23.1854 4.6668 1.7031 57.028 1.5109 37.229 17.3844
240 2022-01-27 1.116 128.74 1.9558 N/A 24.427 7.4428 N/A 0.83368 358.09 N/A N/A N/A 4.5592 N/A 4.9466 10.445 N/A N/A 1.0391 145.2 9.9903 7.5328 87.139 N/A 15.1946 1.5771 6.0159 1.4161 7.1061 8.6951 16052.42 3.5695 83.7893 1344.5 23.143 4.6844 1.6872 57.295 1.5089 37.124 17.1112
241 2022-01-26 1.1277 128.86 1.9558 N/A 24.531 7.442 N/A 0.83458 359.67 N/A N/A N/A 4.5864 N/A 4.9451 10.4493 N/A N/A 1.0386 145.6 10.0115 7.529 89.265 N/A 15.2877 1.5727 6.1084 1.4173 7.1293 8.7791 16185.31 3.5834 84.4288 1350.03 23.1845 4.7268 1.6865 57.728 1.5162 37.169 17.0858
242 2022-01-25 1.1268 128.49 1.9558 N/A 24.5 7.4437 N/A 0.83713 359.44 N/A N/A N/A 4.5751 N/A 4.9448 10.502 N/A N/A 1.0364 146 10.1385 7.5295 88.7384 N/A 15.2723 1.5814 6.2049 1.4247 7.1325 8.7725 16169.54 3.5898 84.277 1350.71 23.2909 4.7213 1.6911 57.753 1.5157 37.28 17.2669
243 2022-01-24 1.1304 128.62 1.9558 N/A 24.528 7.4431 N/A 0.83803 359.84 N/A N/A N/A 4.5572 N/A 4.9453 10.5038 N/A N/A 1.0308 145.6 10.1638 7.529 88.649 N/A 15.1621 1.5866 6.1901 1.4269 7.1533 8.8003 16213.69 3.5846 84.3495 1352.41 23.2566 4.7352 1.6901 58.045 1.5216 37.382 17.2509
244 2022-01-21 1.1348 129.14 1.9558 N/A 24.347 7.4431 N/A 0.83633 358.19 N/A N/A N/A 4.5318 N/A 4.9453 10.414 N/A N/A 1.0353 145.6 10.0523 7.528 86.838 N/A 15.223 1.5774 6.2063 1.4211 7.1946 8.837 16244.2 3.5668 84.419 1351.89 23.2229 4.7508 1.6884 58.171 1.526 37.358 17.1546
245 2022-01-20 1.1338 129.53 1.9558 N/A 24.263 7.4424 N/A 0.83265 355.81 N/A N/A N/A 4.5228 N/A 4.9453 10.3708 N/A N/A 1.0382 145.4 9.9578 7.525 86.8952 N/A 15.2094 1.5662 6.1621 1.4158 7.1936 8.8274 16267.5 3.5564 84.362 1349.16 23.1684 4.7489 1.6731 58.285 1.5261 37.319 17.2531
246 2022-01-19 1.1345 129.86 1.9558 N/A 24.313 7.4419 N/A 0.83168 355.88 N/A N/A N/A 4.5229 N/A 4.9449 10.3428 N/A N/A 1.0383 145.2 9.9368 7.5238 86.48 N/A 15.4207 1.5709 6.2657 1.4144 7.2003 8.8392 16283.78 3.5529 84.4135 1347.1 23.0922 4.757 1.6684 58.429 1.5293 37.467 17.3889
247 2022-01-18 1.1367 130.39 1.9558 N/A 24.426 7.4425 N/A 0.83673 357 N/A N/A N/A 4.526 N/A 4.9449 10.3185 N/A N/A 1.0414 146 9.9638 7.5225 86.7325 N/A 15.4447 1.5833 6.2797 1.4228 7.2212 8.8576 16318.64 3.5557 84.813 1355.33 23.1443 4.7554 1.6798 58.553 1.5349 37.635 17.5398
248 2022-01-17 1.1403 130.64 1.9558 N/A 24.467 7.4417 N/A 0.83573 356.09 N/A N/A N/A 4.5256 N/A 4.9443 10.305 N/A N/A 1.0429 146.8 9.9623 7.5275 87.3907 N/A 15.2757 1.5811 6.2808 1.4287 7.2402 8.8844 16337.73 3.5479 84.7295 1359.89 23.1901 4.7704 1.6765 58.496 1.5374 37.761 17.601
249 2022-01-14 1.1447 130.17 1.9558 N/A 24.493 7.4414 N/A 0.83508 356.1 N/A N/A N/A 4.5414 N/A 4.9429 10.2684 N/A N/A 1.0429 147 9.9863 7.5205 88.0011 N/A 15.5256 1.5803 6.3361 1.433 7.2728 8.9119 16388.85 3.5565 84.9445 1361.27 23.2684 4.7831 1.6745 58.727 1.5418 38.033 17.6043
250 2022-01-13 1.1463 130.98 1.9558 N/A 24.458 7.4409 N/A 0.83545 355.24 N/A N/A N/A 4.5361 N/A 4.944 10.238 N/A N/A 1.0453 147 9.9333 7.5202 86.603 N/A 15.5744 1.5709 6.3518 1.4304 7.2913 8.9289 16379.02 3.5638 84.717 1359.24 23.3894 4.7875 1.6676 58.572 1.5427 38.08 17.6381
251 2022-01-12 1.137 131.19 1.9558 N/A 24.423 7.4414 N/A 0.83338 355.98 N/A N/A N/A 4.5359 N/A 4.9453 10.264 N/A N/A 1.0486 147 9.927 7.524 84.7559 N/A 15.5922 1.5762 6.3458 1.4261 7.2379 8.863 16295.93 3.5406 84.0285 1353.27 23.1852 4.7595 1.6775 58.08 1.5358 37.936 17.5645
252 2022-01-11 1.1336 130.95 1.9558 N/A 24.412 7.4404 N/A 0.83475 357.45 N/A N/A N/A 4.5438 N/A 4.945 10.3075 N/A N/A 1.0502 147 10.0165 7.5236 84.8663 N/A 15.696 1.5804 6.389 1.4329 7.2255 8.8384 16221.39 3.549 83.7481 1352.85 23.0888 4.7515 1.6772 57.979 1.5342 37.862 17.7094
253 2022-01-10 1.1318 130.45 1.9558 N/A 24.357 7.4381 N/A 0.83398 358.4 N/A N/A N/A 4.5334 N/A 4.9449 10.3038 N/A N/A 1.0446 146.2 10.0253 7.5278 84.9825 N/A 15.7183 1.5774 6.3969 1.4327 7.2128 8.8233 16181.9 3.5303 83.8 1355.68 23.0597 4.7536 1.6753 58.141 1.5344 38.074 17.6999
254 2022-01-07 1.1298 130.9 1.9558 N/A 24.439 7.438 N/A 0.8343 358.68 N/A N/A N/A 4.5496 N/A 4.9451 10.2839 N/A N/A 1.0422 146 10.0288 7.5214 85.298 N/A 15.7206 1.5804 6.4343 1.4374 7.206 8.8133 16188.43 3.5135 83.978 1359.96 23.1109 4.7553 1.6748 58.046 1.5356 38.074 17.6701
255 2022-01-06 1.1315 131.05 1.9558 N/A 24.528 7.4393 N/A 0.83593 359.84 N/A N/A N/A 4.5614 N/A 4.9435 10.3265 N/A N/A 1.0395 146.8 10.035 7.5197 86.5088 N/A 15.5504 1.5778 6.442 1.4451 7.2187 8.8272 16291.88 3.5247 84.2475 1362.06 23.2549 4.767 1.6752 57.943 1.5388 37.962 17.7932
256 2022-01-05 1.1319 131.03 1.9558 N/A 24.581 7.4384 N/A 0.83546 362.15 N/A N/A N/A 4.5666 N/A 4.946 10.2545 N/A N/A 1.0364 146.8 9.9672 7.519 85.7275 N/A 15.2446 1.56 6.4146 1.4399 7.2087 8.8227 16263.02 3.4989 84.161 1354.61 23.1422 4.7466 1.6597 57.713 1.534 37.607 17.9369
257 2022-01-04 1.1279 131.17 1.9558 N/A 24.745 7.4378 N/A 0.83618 365.12 N/A N/A N/A 4.5667 N/A 4.9481 10.2808 N/A N/A 1.0355 147.8 10.0138 7.5185 84.9202 N/A 15.1384 1.5682 6.4174 1.4382 7.1924 8.7919 16199.73 3.4909 84.2055 1352.91 23.1808 4.7214 1.6668 57.988 1.531 37.582 18.108
258 2022-01-03 1.1355 130.56 1.9558 N/A 24.818 7.4382 N/A 0.84135 367.71 N/A N/A N/A 4.5895 N/A 4.9483 10.2958 N/A N/A 1.0372 147.6 10.0013 7.519 84.5313 N/A 15.0777 1.5691 6.3539 1.442 7.2174 8.8541 16202.02 3.5139 84.3949 1354.4 23.2259 4.7379 1.6651 58.051 1.5333 37.665 17.9661

59
ext/fileio/fileio.go Normal file
View File

@@ -0,0 +1,59 @@
// Package fileio provides SQL functions to read, write and list files.
//
// https://sqlite.org/src/doc/tip/ext/misc/fileio.c
package fileio
import (
"errors"
"fmt"
"io/fs"
"os"
"github.com/ncruces/go-sqlite3"
)
// Register registers SQL functions readfile, writefile, lsmode,
// and the table-valued function fsdir.
func Register(db *sqlite3.Conn) {
RegisterFS(db, nil)
}
// Register registers SQL functions readfile, lsmode,
// and the table-valued function fsdir;
// fsys will be used to read files and list directories.
func RegisterFS(db *sqlite3.Conn, fsys fs.FS) {
db.CreateFunction("lsmode", 1, sqlite3.DETERMINISTIC, lsmode)
db.CreateFunction("readfile", 1, sqlite3.DIRECTONLY, readfile(fsys))
if fsys == nil {
db.CreateFunction("writefile", -1, sqlite3.DIRECTONLY, writefile)
}
sqlite3.CreateModule(db, "fsdir", nil, func(db *sqlite3.Conn, _, _, _ string, _ ...string) (fsdir, error) {
err := db.DeclareVTab(`CREATE TABLE x(name,mode,mtime TIMESTAMP,data,path HIDDEN,dir HIDDEN)`)
db.VTabConfig(sqlite3.VTAB_DIRECTONLY)
return fsdir{fsys}, err
})
}
func lsmode(ctx sqlite3.Context, arg ...sqlite3.Value) {
ctx.ResultText(fs.FileMode(arg[0].Int()).String())
}
func readfile(fsys fs.FS) func(ctx sqlite3.Context, arg ...sqlite3.Value) {
return func(ctx sqlite3.Context, arg ...sqlite3.Value) {
var err error
var data []byte
if fsys != nil {
data, err = fs.ReadFile(fsys, arg[0].Text())
} else {
data, err = os.ReadFile(arg[0].Text())
}
switch {
case err == nil:
ctx.ResultBlob(data)
case !errors.Is(err, fs.ErrNotExist):
ctx.ResultError(fmt.Errorf("readfile: %w", err))
}
}
}

80
ext/fileio/fileio_test.go Normal file
View File

@@ -0,0 +1,80 @@
package fileio_test
import (
"bytes"
"database/sql"
"io/fs"
"os"
"testing"
"github.com/ncruces/go-sqlite3"
"github.com/ncruces/go-sqlite3/driver"
_ "github.com/ncruces/go-sqlite3/embed"
"github.com/ncruces/go-sqlite3/ext/fileio"
)
func Test_lsmode(t *testing.T) {
t.Parallel()
db, err := driver.Open(":memory:", func(c *sqlite3.Conn) error {
fileio.Register(c)
return nil
})
if err != nil {
t.Fatal(err)
}
defer db.Close()
d, err := os.Getwd()
if err != nil {
t.Fatal(err)
}
s, err := os.Stat(d)
if err != nil {
t.Fatal(err)
}
var mode string
err = db.QueryRow(`SELECT lsmode(?)`, s.Mode()).Scan(&mode)
if err != nil {
t.Fatal(err)
}
if len(mode) != 10 || mode[0] != 'd' {
t.Errorf("got %s", mode)
} else {
t.Logf("got %s", mode)
}
}
func Test_readfile(t *testing.T) {
t.Parallel()
for _, fsys := range []fs.FS{nil, os.DirFS(".")} {
t.Run("", func(t *testing.T) {
db, err := driver.Open(":memory:", func(c *sqlite3.Conn) error {
fileio.RegisterFS(c, fsys)
return nil
})
if err != nil {
t.Fatal(err)
}
defer db.Close()
rows, err := db.Query(`SELECT readfile('fileio_test.go')`)
if err != nil {
t.Fatal(err)
}
if rows.Next() {
var data sql.RawBytes
rows.Scan(&data)
if !bytes.HasPrefix(data, []byte("package fileio_test")) {
t.Errorf("got %s", data[:min(64, len(data))])
}
}
})
}
}

186
ext/fileio/fsdir.go Normal file
View File

@@ -0,0 +1,186 @@
package fileio
import (
"io/fs"
"os"
"path"
"path/filepath"
"strings"
"github.com/ncruces/go-sqlite3"
)
type fsdir struct{ fsys fs.FS }
func (d fsdir) BestIndex(idx *sqlite3.IndexInfo) error {
var root, base bool
for i, cst := range idx.Constraint {
switch cst.Column {
case 4: // root
if !cst.Usable || cst.Op != sqlite3.INDEX_CONSTRAINT_EQ {
return sqlite3.CONSTRAINT
}
idx.ConstraintUsage[i] = sqlite3.IndexConstraintUsage{
Omit: true,
ArgvIndex: 1,
}
root = true
case 5: // base
if !cst.Usable || cst.Op != sqlite3.INDEX_CONSTRAINT_EQ {
return sqlite3.CONSTRAINT
}
idx.ConstraintUsage[i] = sqlite3.IndexConstraintUsage{
Omit: true,
ArgvIndex: 2,
}
base = true
}
}
if !root {
return sqlite3.CONSTRAINT
}
if base {
idx.EstimatedCost = 10
} else {
idx.EstimatedCost = 100
}
return nil
}
func (d fsdir) Open() (sqlite3.VTabCursor, error) {
return &cursor{fsdir: d}, nil
}
type cursor struct {
fsdir
curr entry
next chan entry
done chan struct{}
base string
rowID int64
eof bool
}
type entry struct {
fs.DirEntry
err error
path string
}
func (c *cursor) Close() error {
if c.done != nil {
close(c.done)
s := <-c.next
c.done = nil
c.next = nil
return s.err
}
return nil
}
func (c *cursor) Filter(idxNum int, idxStr string, arg ...sqlite3.Value) error {
if err := c.Close(); err != nil {
return err
}
root := arg[0].Text()
if len(arg) > 1 {
base := arg[1].Text()
if c.fsys != nil {
root = path.Join(base, root)
base = path.Clean(base) + "/"
} else {
root = filepath.Join(base, root)
base = filepath.Clean(base) + string(filepath.Separator)
}
c.base = base
}
c.rowID = 0
c.eof = false
c.next = make(chan entry)
c.done = make(chan struct{})
go c.WalkDir(root)
return c.Next()
}
func (c *cursor) Next() error {
curr, ok := <-c.next
c.curr = curr
c.eof = !ok
c.rowID++
return c.curr.err
}
func (c *cursor) EOF() bool {
return c.eof
}
func (c *cursor) RowID() (int64, error) {
return c.rowID, nil
}
func (c *cursor) Column(ctx *sqlite3.Context, n int) error {
switch n {
case 0: // name
name := strings.TrimPrefix(c.curr.path, c.base)
ctx.ResultText(name)
case 1: // mode
i, err := c.curr.Info()
if err != nil {
return err
}
ctx.ResultInt64(int64(i.Mode()))
case 2: // mtime
i, err := c.curr.Info()
if err != nil {
return err
}
ctx.ResultTime(i.ModTime(), sqlite3.TimeFormatUnixFrac)
case 3: // data
switch typ := c.curr.Type(); {
case typ.IsRegular():
var data []byte
var err error
if c.fsys != nil {
data, err = fs.ReadFile(c.fsys, c.curr.path)
} else {
data, err = os.ReadFile(c.curr.path)
}
if err != nil {
return err
}
ctx.ResultBlob(data)
case typ&fs.ModeSymlink != 0 && c.fsys == nil:
t, err := os.Readlink(c.curr.path)
if err != nil {
return err
}
ctx.ResultText(t)
}
}
return nil
}
func (c *cursor) WalkDir(path string) {
defer close(c.next)
if c.fsys != nil {
fs.WalkDir(c.fsys, path, c.WalkDirFunc)
} else {
filepath.WalkDir(path, c.WalkDirFunc)
}
}
func (c *cursor) WalkDirFunc(path string, d fs.DirEntry, err error) error {
select {
case <-c.done:
return fs.SkipAll
case c.next <- entry{d, err, path}:
return nil
}
}

78
ext/fileio/fsdir_test.go Normal file
View File

@@ -0,0 +1,78 @@
package fileio_test
import (
"bytes"
"database/sql"
"io/fs"
"os"
"testing"
"time"
"github.com/ncruces/go-sqlite3"
"github.com/ncruces/go-sqlite3/driver"
_ "github.com/ncruces/go-sqlite3/embed"
"github.com/ncruces/go-sqlite3/ext/fileio"
)
func Test_fsdir(t *testing.T) {
t.Parallel()
for _, fsys := range []fs.FS{nil, os.DirFS(".")} {
t.Run("", func(t *testing.T) {
db, err := driver.Open(":memory:", func(c *sqlite3.Conn) error {
fileio.RegisterFS(c, fsys)
return nil
})
if err != nil {
t.Fatal(err)
}
defer db.Close()
rows, err := db.Query(`SELECT * FROM fsdir('.', '.') LIMIT 4`)
if err != nil {
t.Fatal(err)
}
for rows.Next() {
var name string
var mode fs.FileMode
var mtime time.Time
var data sql.RawBytes
err := rows.Scan(&name, &mode, sqlite3.TimeFormatUnixFrac.Scanner(&mtime), &data)
if err != nil {
t.Fatal(err)
}
if mode.Perm() == 0 {
t.Errorf("got: %v", mode)
}
if mtime.Before(time.Unix(0, 0)) {
t.Errorf("got: %v", mtime)
}
if name == "fsdir_test.go" {
if !bytes.HasPrefix(data, []byte("package fileio_test")) {
t.Errorf("got: %s", data[:min(64, len(data))])
}
}
}
})
}
}
func Test_fsdir_errors(t *testing.T) {
t.Parallel()
db, err := sqlite3.Open(":memory:")
if err != nil {
t.Fatal(err)
}
defer db.Close()
fileio.Register(db)
err = db.Exec(`SELECT name FROM fsdir()`)
if err == nil {
t.Fatal("want error")
} else {
t.Log(err)
}
}

97
ext/fileio/write.go Normal file
View File

@@ -0,0 +1,97 @@
package fileio
import (
"errors"
"fmt"
"io/fs"
"os"
"path/filepath"
"time"
"github.com/ncruces/go-sqlite3"
"github.com/ncruces/go-sqlite3/internal/util"
"github.com/ncruces/go-sqlite3/util/fsutil"
)
func writefile(ctx sqlite3.Context, arg ...sqlite3.Value) {
if len(arg) < 2 || len(arg) > 4 {
ctx.ResultError(util.ErrorString("writefile: wrong number of arguments"))
return
}
file := arg[0].Text()
var mode fs.FileMode
if len(arg) > 2 {
mode = fsutil.FileModeFromValue(arg[2])
}
n, err := createFileAndDir(file, mode, arg[1])
if err != nil {
if len(arg) > 2 {
ctx.ResultError(fmt.Errorf("writefile: %w", err))
}
return
}
if mode&fs.ModeSymlink == 0 {
if len(arg) > 2 {
err := os.Chmod(file, mode.Perm())
if err != nil {
ctx.ResultError(fmt.Errorf("writefile: %w", err))
return
}
}
if len(arg) > 3 {
mtime := arg[3].Time(sqlite3.TimeFormatUnixFrac)
err := os.Chtimes(file, time.Time{}, mtime)
if err != nil {
ctx.ResultError(fmt.Errorf("writefile: %w", err))
return
}
}
}
if mode.IsRegular() {
ctx.ResultInt(n)
}
}
func createFileAndDir(path string, mode fs.FileMode, data sqlite3.Value) (int, error) {
n, err := createFile(path, mode, data)
if errors.Is(err, fs.ErrNotExist) {
if err := os.MkdirAll(filepath.Dir(path), 0777); err == nil {
return createFile(path, mode, data)
}
}
return n, err
}
func createFile(path string, mode fs.FileMode, data sqlite3.Value) (int, error) {
if mode.IsRegular() {
blob := data.RawBlob()
return len(blob), os.WriteFile(path, blob, fixPerm(mode, 0666))
}
if mode.IsDir() {
err := os.Mkdir(path, fixPerm(mode, 0777))
if errors.Is(err, fs.ErrExist) {
s, err := os.Lstat(path)
if err == nil && s.IsDir() {
return 0, nil
}
}
return 0, err
}
if mode&fs.ModeSymlink != 0 {
return 0, os.Symlink(data.Text(), path)
}
return 0, fmt.Errorf("invalid mode: %v", mode)
}
func fixPerm(mode fs.FileMode, def fs.FileMode) fs.FileMode {
if mode.Perm() == 0 {
return def
}
return mode.Perm()
}

92
ext/fileio/write_test.go Normal file
View File

@@ -0,0 +1,92 @@
package fileio
import (
"database/sql"
"io/fs"
"path/filepath"
"testing"
"time"
"github.com/ncruces/go-sqlite3"
"github.com/ncruces/go-sqlite3/driver"
_ "github.com/ncruces/go-sqlite3/embed"
)
func Test_writefile(t *testing.T) {
t.Parallel()
db, err := driver.Open(":memory:", func(c *sqlite3.Conn) error {
Register(c)
return nil
})
if err != nil {
t.Fatal(err)
}
defer db.Close()
dir := t.TempDir()
link := filepath.Join(dir, "link")
file := filepath.Join(dir, "test.txt")
nest := filepath.Join(dir, "tmp", "test.txt")
sock := filepath.Join(dir, "sock")
twosday := time.Date(2022, 2, 22, 22, 22, 22, 0, time.UTC)
_, err = db.Exec(`SELECT writefile(?, 'Hello world!')`, file)
if err != nil {
t.Fatal(err)
}
_, err = db.Exec(`SELECT writefile(?, ?, ?)`, link, "test.txt", fs.ModeSymlink)
if err != nil {
t.Fatal(err)
}
_, err = db.Exec(`SELECT writefile(?, ?, ?, ?)`, dir, nil, 0040700, twosday.Unix())
if err != nil {
t.Fatal(err)
}
rows, err := db.Query(`SELECT * FROM fsdir('.', ?)`, dir)
if err != nil {
t.Fatal(err)
}
for rows.Next() {
var name string
var mode fs.FileMode
var mtime time.Time
var data sql.NullString
err := rows.Scan(&name, &mode, &mtime, &data)
if err != nil {
t.Fatal(err)
}
if mode.IsDir() && !mtime.Equal(twosday) {
t.Errorf("got: %v", mtime)
}
if mode.IsRegular() && data.String != "Hello world!" {
t.Errorf("got: %v", data)
}
if mode&fs.ModeSymlink != 0 && data.String != "test.txt" {
t.Errorf("got: %v", data)
}
}
_, err = db.Exec(`SELECT writefile(?, 'Hello world!')`, nest)
if err != nil {
t.Fatal(err)
}
_, err = db.Exec(`SELECT writefile(?, ?, ?)`, sock, nil, fs.ModeSocket)
if err == nil {
t.Fatal("want error")
} else {
t.Log(err)
}
_, err = db.Exec(`SELECT writefile()`)
if err == nil {
t.Fatal("want error")
} else {
t.Log(err)
}
}

30
ext/hash/blake2.go Normal file
View File

@@ -0,0 +1,30 @@
package hash
import (
"crypto"
"github.com/ncruces/go-sqlite3"
"github.com/ncruces/go-sqlite3/internal/util"
)
func blake2sFunc(ctx sqlite3.Context, arg ...sqlite3.Value) {
hashFunc(ctx, arg[0], crypto.BLAKE2s_256)
}
func blake2bFunc(ctx sqlite3.Context, arg ...sqlite3.Value) {
size := 512
if len(arg) > 1 {
size = arg[1].Int()
}
switch size {
case 256:
hashFunc(ctx, arg[0], crypto.BLAKE2b_256)
case 384:
hashFunc(ctx, arg[0], crypto.BLAKE2b_384)
case 512:
hashFunc(ctx, arg[0], crypto.BLAKE2b_512)
default:
ctx.ResultError(util.ErrorString("blake2b: size must be 256, 384, 512"))
}
}

97
ext/hash/hash.go Normal file
View File

@@ -0,0 +1,97 @@
// Package hash provides cryptographic hash functions.
//
// Provided functions:
// - md4(data)
// - md5(data)
// - sha1(data)
// - sha3(data, size) (default size 256)
// - sha224(data)
// - sha256(data, size) (default size 256)
// - sha384(data)
// - sha512(data, size) (default size 512)
// - blake2s(data)
// - blake2b(data, size) (default size 512)
// - ripemd160(data)
//
// Each SQL function will only be registered if the corresponding
// [crypto.Hash] function is available.
// To ensure a specific hash function is available,
// import the implementing package.
package hash
import (
"crypto"
"github.com/ncruces/go-sqlite3"
)
// Register registers cryptographic hash functions for a database connection.
func Register(db *sqlite3.Conn) {
flags := sqlite3.DETERMINISTIC | sqlite3.INNOCUOUS
if crypto.MD4.Available() {
db.CreateFunction("md4", 1, flags, md4Func)
}
if crypto.MD5.Available() {
db.CreateFunction("md5", 1, flags, md5Func)
}
if crypto.SHA1.Available() {
db.CreateFunction("sha1", 1, flags, sha1Func)
}
if crypto.SHA3_512.Available() {
db.CreateFunction("sha3", 1, flags, sha3Func)
db.CreateFunction("sha3", 2, flags, sha3Func)
}
if crypto.SHA256.Available() {
db.CreateFunction("sha224", 1, flags, sha224Func)
db.CreateFunction("sha256", 1, flags, sha256Func)
db.CreateFunction("sha256", 2, flags, sha256Func)
}
if crypto.SHA512.Available() {
db.CreateFunction("sha384", 1, flags, sha384Func)
db.CreateFunction("sha512", 1, flags, sha512Func)
db.CreateFunction("sha512", 2, flags, sha512Func)
}
if crypto.BLAKE2s_256.Available() {
db.CreateFunction("blake2s", 1, flags, blake2sFunc)
}
if crypto.BLAKE2b_512.Available() {
db.CreateFunction("blake2b", 1, flags, blake2bFunc)
db.CreateFunction("blake2b", 2, flags, blake2bFunc)
}
if crypto.RIPEMD160.Available() {
db.CreateFunction("ripemd160", 1, flags, ripemd160Func)
}
}
func md4Func(ctx sqlite3.Context, arg ...sqlite3.Value) {
hashFunc(ctx, arg[0], crypto.MD4)
}
func md5Func(ctx sqlite3.Context, arg ...sqlite3.Value) {
hashFunc(ctx, arg[0], crypto.MD5)
}
func sha1Func(ctx sqlite3.Context, arg ...sqlite3.Value) {
hashFunc(ctx, arg[0], crypto.SHA1)
}
func ripemd160Func(ctx sqlite3.Context, arg ...sqlite3.Value) {
hashFunc(ctx, arg[0], crypto.RIPEMD160)
}
func hashFunc(ctx sqlite3.Context, arg sqlite3.Value, fn crypto.Hash) {
var data []byte
switch arg.Type() {
case sqlite3.NULL:
return
case sqlite3.BLOB:
data = arg.RawBlob()
default:
data = arg.RawText()
}
h := fn.New()
h.Write(data)
ctx.ResultBlob(h.Sum(nil))
}

98
ext/hash/hash_test.go Normal file
View File

@@ -0,0 +1,98 @@
package hash
import (
_ "crypto/md5"
_ "crypto/sha1"
_ "crypto/sha256"
_ "crypto/sha512"
"testing"
"github.com/ncruces/go-sqlite3"
"github.com/ncruces/go-sqlite3/driver"
_ "github.com/ncruces/go-sqlite3/embed"
_ "golang.org/x/crypto/blake2b"
_ "golang.org/x/crypto/blake2s"
_ "golang.org/x/crypto/md4"
_ "golang.org/x/crypto/ripemd160"
_ "golang.org/x/crypto/sha3"
)
func TestRegister(t *testing.T) {
t.Parallel()
tests := []struct {
name string
hash string
}{
{"md4(NULL)", ""},
{"md4(X'')", "31D6CFE0D16AE931B73C59D7E0C089C0"},
{"md4('The quick brown fox jumps over the lazy dog')", "1BEE69A46BA811185C194762ABAEAE90"},
{"md5('')", "D41D8CD98F00B204E9800998ECF8427E"},
{"sha1('')", "DA39A3EE5E6B4B0D3255BFEF95601890AFD80709"},
{"ripemd160('')", "9C1185A5C5E9FC54612808977EE8F548B2258D31"},
{"sha224('')", "D14A028C2A3A2BC9476102BB288234C415A2B01F828EA62AC5B3E42F"},
{"sha256('')", "E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855"},
{"sha256('', 224)", "D14A028C2A3A2BC9476102BB288234C415A2B01F828EA62AC5B3E42F"},
{"sha384('')", "38B060A751AC96384CD9327EB1B1E36A21FDB71114BE07434C0CC7BF63F6E1DA274EDEBFE76F65FBD51AD2F14898B95B"},
{"sha512('')", "CF83E1357EEFB8BDF1542850D66D8007D620E4050B5715DC83F4A921D36CE9CE47D0D13C5D85F2B0FF8318D2877EEC2F63B931BD47417A81A538327AF927DA3E"},
{"sha512('', 224)", "6ED0DD02806FA89E25DE060C19D3AC86CABB87D6A0DDD05C333B84F4"},
{"sha512('', 256)", "C672B8D1EF56ED28AB87C3622C5114069BDD3AD7B8F9737498D0C01ECEF0967A"},
{"sha512('', 384)", "38B060A751AC96384CD9327EB1B1E36A21FDB71114BE07434C0CC7BF63F6E1DA274EDEBFE76F65FBD51AD2F14898B95B"},
{"sha3('')", "A7FFC6F8BF1ED76651C14756A061D662F580FF4DE43B49FA82D80A4B80F8434A"},
{"sha3('', 224)", "6B4E03423667DBB73B6E15454F0EB1ABD4597F9A1B078E3F5B5A6BC7"},
{"sha3('', 384)", "0C63A75B845E4F7D01107D852E4C2485C51A50AAAA94FC61995E71BBEE983A2AC3713831264ADB47FB6BD1E058D5F004"},
{"sha3('', 512)", "A69F73CCA23A9AC5C8B567DC185A756E97C982164FE25859E0D1DCC1475C80A615B2123AF1F5F94C11E3E9402C3AC558F500199D95B6D3E301758586281DCD26"},
{"blake2s('')", "69217A3079908094E11121D042354A7C1F55B6482CA1A51E1B250DFD1ED0EEF9"},
{"blake2b('')", "786A02F742015903C6C6FD852552D272912F4740E15847618A86E217F71F5419D25E1031AFEE585313896444934EB04B903A685B1448B755D56F701AFE9BE2CE"},
{"blake2b('', 384)", "B32811423377F52D7862286EE1A72EE540524380FDA1724A6F25D7978C6FD3244A6CAF0498812673C5E05EF583825100"},
{"blake2b('', 256)", "0E5751C026E543B2E8AB2EB06099DAA1D1E5DF47778F7787FAAB45CDF12FE3A8"},
}
db, err := driver.Open(":memory:", func(c *sqlite3.Conn) error {
Register(c)
return nil
})
if err != nil {
t.Fatal(err)
}
defer db.Close()
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var hash string
err = db.QueryRow(`SELECT hex(` + tt.name + `)`).Scan(&hash)
if err != nil {
t.Fatal(err)
}
if hash != tt.hash {
t.Errorf("got %s, want %s", hash, tt.hash)
}
})
}
_, err = db.Exec(`SELECT sha256('', 255)`)
if err == nil {
t.Error("want error")
}
_, err = db.Exec(`SELECT sha512('', 255)`)
if err == nil {
t.Error("want error")
}
_, err = db.Exec(`SELECT sha3('', 255)`)
if err == nil {
t.Error("want error")
}
_, err = db.Exec(`SELECT blake2b('', 255)`)
if err == nil {
t.Error("want error")
}
}

53
ext/hash/sha2.go Normal file
View File

@@ -0,0 +1,53 @@
package hash
import (
"crypto"
"github.com/ncruces/go-sqlite3"
"github.com/ncruces/go-sqlite3/internal/util"
)
func sha224Func(ctx sqlite3.Context, arg ...sqlite3.Value) {
hashFunc(ctx, arg[0], crypto.SHA224)
}
func sha384Func(ctx sqlite3.Context, arg ...sqlite3.Value) {
hashFunc(ctx, arg[0], crypto.SHA384)
}
func sha256Func(ctx sqlite3.Context, arg ...sqlite3.Value) {
size := 256
if len(arg) > 1 {
size = arg[1].Int()
}
switch size {
case 224:
hashFunc(ctx, arg[0], crypto.SHA224)
case 256:
hashFunc(ctx, arg[0], crypto.SHA256)
default:
ctx.ResultError(util.ErrorString("sha256: size must be 224, 256"))
}
}
func sha512Func(ctx sqlite3.Context, arg ...sqlite3.Value) {
size := 512
if len(arg) > 1 {
size = arg[1].Int()
}
switch size {
case 224:
hashFunc(ctx, arg[0], crypto.SHA512_224)
case 256:
hashFunc(ctx, arg[0], crypto.SHA512_256)
case 384:
hashFunc(ctx, arg[0], crypto.SHA384)
case 512:
hashFunc(ctx, arg[0], crypto.SHA512)
default:
ctx.ResultError(util.ErrorString("sha512: size must be 224, 256, 384, 512"))
}
}

28
ext/hash/sha3.go Normal file
View File

@@ -0,0 +1,28 @@
package hash
import (
"crypto"
"github.com/ncruces/go-sqlite3"
"github.com/ncruces/go-sqlite3/internal/util"
)
func sha3Func(ctx sqlite3.Context, arg ...sqlite3.Value) {
size := 256
if len(arg) > 1 {
size = arg[1].Int()
}
switch size {
case 224:
hashFunc(ctx, arg[0], crypto.SHA3_224)
case 256:
hashFunc(ctx, arg[0], crypto.SHA3_256)
case 384:
hashFunc(ctx, arg[0], crypto.SHA3_384)
case 512:
hashFunc(ctx, arg[0], crypto.SHA3_512)
default:
ctx.ResultError(util.ErrorString("sha3: size must be 224, 256, 384, 512"))
}
}

193
ext/lines/lines.go Normal file
View File

@@ -0,0 +1,193 @@
// Package lines provides a virtual table to read data line-by-line.
//
// It is particularly useful for line-oriented datasets,
// like [ndjson] or [JSON Lines],
// when paired with SQLite's JSON support.
//
// https://github.com/asg017/sqlite-lines
//
// [ndjson]: https://ndjson.org/
// [JSON Lines]: https://jsonlines.org/
package lines
import (
"bufio"
"bytes"
"fmt"
"io"
"io/fs"
"github.com/ncruces/go-sqlite3"
"github.com/ncruces/go-sqlite3/util/osutil"
)
// Register registers the lines and lines_read table-valued functions.
// The lines function reads from a database blob or text.
// The lines_read function reads from a file or an [io.Reader].
// If a filename is specified, [os.Open] is used to open the file.
func Register(db *sqlite3.Conn) {
RegisterFS(db, osutil.FS{})
}
// RegisterFS registers the lines and lines_read table-valued functions.
// The lines function reads from a database blob or text.
// The lines_read function reads from a file or an [io.Reader].
// If a filename is specified, fsys is used to open the file.
func RegisterFS(db *sqlite3.Conn, fsys fs.FS) {
sqlite3.CreateModule[lines](db, "lines", nil,
func(db *sqlite3.Conn, _, _, _ string, _ ...string) (lines, error) {
err := db.DeclareVTab(`CREATE TABLE x(line TEXT, data HIDDEN)`)
db.VTabConfig(sqlite3.VTAB_INNOCUOUS)
return lines{}, err
})
sqlite3.CreateModule[lines](db, "lines_read", nil,
func(db *sqlite3.Conn, _, _, _ string, _ ...string) (lines, error) {
err := db.DeclareVTab(`CREATE TABLE x(line TEXT, data HIDDEN)`)
db.VTabConfig(sqlite3.VTAB_DIRECTONLY)
return lines{fsys}, err
})
}
type lines struct {
fsys fs.FS
}
func (l lines) BestIndex(idx *sqlite3.IndexInfo) error {
for i, cst := range idx.Constraint {
if cst.Column == 1 && cst.Op == sqlite3.INDEX_CONSTRAINT_EQ && cst.Usable {
idx.ConstraintUsage[i] = sqlite3.IndexConstraintUsage{
Omit: true,
ArgvIndex: 1,
}
idx.EstimatedCost = 1e6
idx.EstimatedRows = 100
return nil
}
}
return sqlite3.CONSTRAINT
}
func (l lines) Open() (sqlite3.VTabCursor, error) {
if l.fsys != nil {
return &reader{fsys: l.fsys}, nil
} else {
return &buffer{}, nil
}
}
type cursor struct {
line []byte
rowID int64
eof bool
}
func (c *cursor) EOF() bool {
return c.eof
}
func (c *cursor) RowID() (int64, error) {
return c.rowID, nil
}
func (c *cursor) Column(ctx *sqlite3.Context, n int) error {
if n == 0 {
ctx.ResultRawText(c.line)
}
return nil
}
type reader struct {
fsys fs.FS
reader *bufio.Reader
closer io.Closer
cursor
}
func (c *reader) Close() (err error) {
if c.closer != nil {
err = c.closer.Close()
c.closer = nil
}
return err
}
func (c *reader) Filter(idxNum int, idxStr string, arg ...sqlite3.Value) error {
if err := c.Close(); err != nil {
return err
}
var r io.Reader
typ := arg[0].Type()
switch typ {
case sqlite3.NULL:
if p, ok := arg[0].Pointer().(io.Reader); ok {
r = p
}
case sqlite3.TEXT:
f, err := c.fsys.Open(arg[0].Text())
if err != nil {
return err
}
r = f
}
if r == nil {
return fmt.Errorf("lines: unsupported argument:%.0w %v", sqlite3.MISMATCH, typ)
}
c.reader = bufio.NewReader(r)
c.closer, _ = r.(io.Closer)
c.rowID = 0
return c.Next()
}
func (c *reader) Next() (err error) {
c.line = c.line[:0]
for more := true; more; {
var line []byte
line, more, err = c.reader.ReadLine()
c.line = append(c.line, line...)
}
if err == io.EOF {
c.eof = true
err = nil
}
c.rowID++
return err
}
type buffer struct {
data []byte
cursor
}
func (c *buffer) Filter(idxNum int, idxStr string, arg ...sqlite3.Value) error {
typ := arg[0].Type()
switch typ {
case sqlite3.TEXT:
c.data = arg[0].RawText()
case sqlite3.BLOB:
c.data = arg[0].RawBlob()
default:
return fmt.Errorf("lines: unsupported argument:%.0w %v", sqlite3.MISMATCH, typ)
}
c.rowID = 0
return c.Next()
}
func (c *buffer) Next() error {
i := bytes.IndexByte(c.data, '\n')
j := i + 1
switch {
case i < 0:
i = len(c.data)
j = i
case i > 0 && c.data[i-1] == '\r':
i--
}
c.eof = len(c.data) == 0
c.line = c.data[:i]
c.data = c.data[j:]
c.rowID++
return nil
}

192
ext/lines/lines_test.go Normal file
View File

@@ -0,0 +1,192 @@
package lines_test
import (
"database/sql"
"errors"
"fmt"
"log"
"net/http"
"os"
"strings"
"testing"
"github.com/ncruces/go-sqlite3"
"github.com/ncruces/go-sqlite3/driver"
_ "github.com/ncruces/go-sqlite3/embed"
"github.com/ncruces/go-sqlite3/ext/lines"
)
func Example() {
db, err := driver.Open(":memory:", func(c *sqlite3.Conn) error {
lines.Register(c)
return nil
})
if err != nil {
log.Fatal(err)
}
defer db.Close()
res, err := http.Get("https://storage.googleapis.com/quickdraw_dataset/full/simplified/calendar.ndjson")
if err != nil {
log.Fatal(err)
}
defer res.Body.Close()
rows, err := db.Query(`
SELECT
line ->> '$.countrycode' as countrycode,
COUNT(*)
FROM lines_read(?)
GROUP BY 1
ORDER BY 2 DESC
LIMIT 5`,
sqlite3.Pointer(res.Body))
if err != nil {
log.Fatal(err)
}
defer rows.Close()
var countrycode sql.RawBytes
var count int
for rows.Next() {
err := rows.Scan(&countrycode, &count)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s: %d\n", countrycode, count)
}
if err := rows.Err(); err != nil {
log.Fatal(err)
}
// Output:
// US: 141001
// GB: 22560
// CA: 11759
// RU: 9250
// DE: 8748
}
func Test_lines(t *testing.T) {
t.Parallel()
db, err := driver.Open(":memory:", func(c *sqlite3.Conn) error {
lines.Register(c)
return nil
})
if err != nil {
log.Fatal(err)
}
defer db.Close()
const data = "line 1\nline 2\r\nline 3\n"
rows, err := db.Query(`SELECT rowid, line FROM lines(?)`, data)
if err != nil {
t.Fatal(err)
}
defer rows.Close()
for rows.Next() {
var id int64
var line string
err := rows.Scan(&id, &line)
if err != nil {
t.Fatal(err)
}
if want := fmt.Sprintf("line %d", id); line != want {
t.Errorf("got %q, want %q", line, want)
}
}
}
func Test_lines_error(t *testing.T) {
t.Parallel()
db, err := driver.Open(":memory:", func(c *sqlite3.Conn) error {
lines.Register(c)
return nil
})
if err != nil {
log.Fatal(err)
}
defer db.Close()
_, err = db.Exec(`SELECT rowid, line FROM lines(?)`, nil)
if err == nil {
t.Fatal("want error")
} else {
t.Log(err)
}
_, err = db.Exec(`SELECT rowid, line FROM lines_read(?)`, "xpto")
if err == nil {
t.Fatal("want error")
} else {
t.Log(err)
}
}
func Test_lines_read(t *testing.T) {
t.Parallel()
db, err := driver.Open(":memory:", func(c *sqlite3.Conn) error {
lines.Register(c)
return nil
})
if err != nil {
log.Fatal(err)
}
defer db.Close()
const data = "line 1\nline 2\r\nline 3\n"
rows, err := db.Query(`SELECT rowid, line FROM lines_read(?)`,
sqlite3.Pointer(strings.NewReader(data)))
if err != nil {
t.Fatal(err)
}
defer rows.Close()
for rows.Next() {
var id int64
var line string
err := rows.Scan(&id, &line)
if err != nil {
t.Fatal(err)
}
if want := fmt.Sprintf("line %d", id); line != want {
t.Errorf("got %q, want %q", line, want)
}
}
}
func Test_lines_test(t *testing.T) {
t.Parallel()
db, err := driver.Open(":memory:", func(c *sqlite3.Conn) error {
lines.Register(c)
return nil
})
if err != nil {
log.Fatal(err)
}
defer db.Close()
rows, err := db.Query(`SELECT rowid, line FROM lines_read(?)`, "lines_test.go")
if errors.Is(err, os.ErrNotExist) {
t.Skip(err)
}
if err != nil {
t.Fatal(err)
}
defer rows.Close()
for rows.Next() {
var id int64
var line string
err := rows.Scan(&id, &line)
if err != nil {
t.Fatal(err)
}
}
}

34
ext/pivot/op_test.go Normal file
View File

@@ -0,0 +1,34 @@
package pivot
import (
"testing"
"github.com/ncruces/go-sqlite3"
)
func Test_operator(t *testing.T) {
tests := []struct {
op sqlite3.IndexConstraintOp
want string
}{
{sqlite3.INDEX_CONSTRAINT_EQ, "="},
{sqlite3.INDEX_CONSTRAINT_LT, "<"},
{sqlite3.INDEX_CONSTRAINT_GT, ">"},
{sqlite3.INDEX_CONSTRAINT_LE, "<="},
{sqlite3.INDEX_CONSTRAINT_GE, ">="},
{sqlite3.INDEX_CONSTRAINT_NE, "<>"},
{sqlite3.INDEX_CONSTRAINT_IS, "IS"},
{sqlite3.INDEX_CONSTRAINT_ISNOT, "IS NOT"},
{sqlite3.INDEX_CONSTRAINT_REGEXP, "REGEXP"},
{sqlite3.INDEX_CONSTRAINT_MATCH, "MATCH"},
{sqlite3.INDEX_CONSTRAINT_GLOB, "GLOB"},
{sqlite3.INDEX_CONSTRAINT_LIKE, "LIKE"},
}
for _, tt := range tests {
t.Run(tt.want, func(t *testing.T) {
if got := operator(tt.op); got != tt.want {
t.Errorf("operator() = %v, want %v", got, tt.want)
}
})
}
}

274
ext/pivot/pivot.go Normal file
View File

@@ -0,0 +1,274 @@
// Package pivot implements a pivot virtual table.
//
// https://github.com/jakethaw/pivot_vtab
package pivot
import (
"errors"
"fmt"
"strings"
"github.com/ncruces/go-sqlite3"
)
// Register registers the pivot virtual table.
func Register(db *sqlite3.Conn) {
sqlite3.CreateModule(db, "pivot", declare, declare)
}
type table struct {
db *sqlite3.Conn
scan string
cell string
keys []string
cols []*sqlite3.Value
}
func declare(db *sqlite3.Conn, _, _, _ string, arg ...string) (_ *table, err error) {
if len(arg) != 3 {
return nil, fmt.Errorf("pivot: wrong number of arguments")
}
table := &table{db: db}
defer func() {
if err != nil {
table.Close()
}
}()
var sep string
var create strings.Builder
create.WriteString("CREATE TABLE x(")
// Row key query.
table.scan = "SELECT * FROM\n" + arg[0]
stmt, _, err := db.Prepare(table.scan)
if err != nil {
return nil, err
}
defer stmt.Close()
table.keys = make([]string, stmt.ColumnCount())
for i := range table.keys {
name := sqlite3.QuoteIdentifier(stmt.ColumnName(i))
table.keys[i] = name
create.WriteString(sep)
create.WriteString(name)
sep = ","
}
stmt.Close()
// Column definition query.
stmt, _, err = db.Prepare("SELECT * FROM\n" + arg[1])
if err != nil {
return nil, err
}
if stmt.ColumnCount() != 2 {
return nil, fmt.Errorf("pivot: column definition query expects 2 result columns")
}
for stmt.Step() {
name := sqlite3.QuoteIdentifier(stmt.ColumnText(1))
table.cols = append(table.cols, stmt.ColumnValue(0).Dup())
create.WriteString(",")
create.WriteString(name)
}
stmt.Close()
// Pivot cell query.
table.cell = "SELECT * FROM\n" + arg[2]
stmt, _, err = db.Prepare(table.cell)
if err != nil {
return nil, err
}
if stmt.ColumnCount() != 1 {
return nil, fmt.Errorf("pivot: cell query expects 1 result columns")
}
if stmt.BindCount() != len(table.keys)+1 {
return nil, fmt.Errorf("pivot: cell query expects %d bound parameters", len(table.keys)+1)
}
create.WriteByte(')')
err = db.DeclareVTab(create.String())
if err != nil {
return nil, err
}
return table, nil
}
func (t *table) Close() error {
for i := range t.cols {
t.cols[i].Close()
}
return nil
}
func (t *table) BestIndex(idx *sqlite3.IndexInfo) error {
var idxStr strings.Builder
idxStr.WriteString(t.scan)
argvIndex := 1
sep := " WHERE "
for i, cst := range idx.Constraint {
if !cst.Usable || !(0 <= cst.Column && cst.Column < len(t.keys)) {
continue
}
op := operator(cst.Op)
if op == "" {
continue
}
idxStr.WriteString(sep)
idxStr.WriteString(t.keys[cst.Column])
idxStr.WriteString(" ")
idxStr.WriteString(op)
idxStr.WriteString(" ?")
idx.ConstraintUsage[i] = sqlite3.IndexConstraintUsage{
ArgvIndex: argvIndex,
Omit: true,
}
sep = " AND "
argvIndex++
}
sep = " ORDER BY "
idx.OrderByConsumed = true
for _, ord := range idx.OrderBy {
if !(0 <= ord.Column && ord.Column < len(t.keys)) {
idx.OrderByConsumed = false
continue
}
idxStr.WriteString(sep)
idxStr.WriteString(t.keys[ord.Column])
idxStr.WriteString(" COLLATE ")
idxStr.WriteString(idx.Collation(ord.Column))
if ord.Desc {
idxStr.WriteString(" DESC")
}
sep = ","
}
idx.EstimatedCost = 1e9 / float64(argvIndex)
idx.IdxStr = idxStr.String()
return nil
}
func (t *table) Open() (sqlite3.VTabCursor, error) {
return &cursor{table: t}, nil
}
func (t *table) Rename(new string) error {
return nil
}
type cursor struct {
table *table
scan *sqlite3.Stmt
cell *sqlite3.Stmt
rowID int64
}
func (c *cursor) Close() error {
return errors.Join(c.scan.Close(), c.cell.Close())
}
func (c *cursor) Filter(idxNum int, idxStr string, arg ...sqlite3.Value) error {
err := c.scan.Close()
if err != nil {
return err
}
c.scan, _, err = c.table.db.Prepare(idxStr)
if err != nil {
return err
}
for i, arg := range arg {
err := c.scan.BindValue(i+1, arg)
if err != nil {
return err
}
}
if c.cell == nil {
c.cell, _, err = c.table.db.Prepare(c.table.cell)
if err != nil {
return err
}
}
c.rowID = 0
return c.Next()
}
func (c *cursor) Next() error {
if c.scan.Step() {
count := c.scan.ColumnCount()
for i := 0; i < count; i++ {
err := c.cell.BindValue(i+1, c.scan.ColumnValue(i))
if err != nil {
return err
}
}
c.rowID++
}
return c.scan.Err()
}
func (c *cursor) EOF() bool {
return !c.scan.Busy()
}
func (c *cursor) RowID() (int64, error) {
return c.rowID, nil
}
func (c *cursor) Column(ctx *sqlite3.Context, col int) error {
count := c.scan.ColumnCount()
if col < count {
ctx.ResultValue(c.scan.ColumnValue(col))
return nil
}
err := c.cell.BindValue(count+1, *c.table.cols[col-count])
if err != nil {
return err
}
if c.cell.Step() {
ctx.ResultValue(c.cell.ColumnValue(0))
}
return c.cell.Reset()
}
func operator(op sqlite3.IndexConstraintOp) string {
switch op {
case sqlite3.INDEX_CONSTRAINT_EQ:
return "="
case sqlite3.INDEX_CONSTRAINT_LT:
return "<"
case sqlite3.INDEX_CONSTRAINT_GT:
return ">"
case sqlite3.INDEX_CONSTRAINT_LE:
return "<="
case sqlite3.INDEX_CONSTRAINT_GE:
return ">="
case sqlite3.INDEX_CONSTRAINT_NE:
return "<>"
case sqlite3.INDEX_CONSTRAINT_MATCH:
return "MATCH"
case sqlite3.INDEX_CONSTRAINT_LIKE:
return "LIKE"
case sqlite3.INDEX_CONSTRAINT_GLOB:
return "GLOB"
case sqlite3.INDEX_CONSTRAINT_REGEXP:
return "REGEXP"
case sqlite3.INDEX_CONSTRAINT_IS, sqlite3.INDEX_CONSTRAINT_ISNULL:
return "IS"
case sqlite3.INDEX_CONSTRAINT_ISNOT, sqlite3.INDEX_CONSTRAINT_ISNOTNULL:
return "IS NOT"
default:
return ""
}
}

219
ext/pivot/pivot_test.go Normal file
View File

@@ -0,0 +1,219 @@
package pivot_test
import (
"fmt"
"log"
"strings"
"testing"
"github.com/ncruces/go-sqlite3"
_ "github.com/ncruces/go-sqlite3/embed"
"github.com/ncruces/go-sqlite3/ext/pivot"
)
// https://antonz.org/sqlite-pivot-table/
func Example() {
db, err := sqlite3.Open(":memory:")
if err != nil {
log.Fatal(err)
}
defer db.Close()
pivot.Register(db)
err = db.Exec(`
CREATE TABLE sales(product TEXT, year INT, income DECIMAL);
INSERT INTO sales(product, year, income) VALUES
('alpha', 2020, 100),
('alpha', 2021, 120),
('alpha', 2022, 130),
('alpha', 2023, 140),
('beta', 2020, 10),
('beta', 2021, 20),
('beta', 2022, 40),
('beta', 2023, 80),
('gamma', 2020, 80),
('gamma', 2021, 75),
('gamma', 2022, 78),
('gamma', 2023, 80);
`)
if err != nil {
log.Fatal(err)
}
err = db.Exec(`
CREATE VIRTUAL TABLE v_sales USING pivot(
-- rows
(SELECT DISTINCT product FROM sales),
-- columns
(SELECT DISTINCT year, year FROM sales),
-- cells
(SELECT sum(income) FROM sales WHERE product = ? AND year = ?)
)`)
if err != nil {
log.Fatal(err)
}
stmt, _, err := db.Prepare(`SELECT * FROM v_sales`)
if err != nil {
log.Fatal(err)
}
defer stmt.Close()
cols := make([]string, stmt.ColumnCount())
for i := range cols {
cols[i] = stmt.ColumnName(i)
}
fmt.Println(pretty(cols))
for stmt.Step() {
for i := range cols {
cols[i] = stmt.ColumnText(i)
}
fmt.Println(pretty(cols))
}
if err := stmt.Reset(); err != nil {
log.Fatal(err)
}
// Output:
// product 2020 2021 2022 2023
// alpha 100 120 130 140
// beta 10 20 40 80
// gamma 80 75 78 80
}
func TestRegister(t *testing.T) {
t.Parallel()
db, err := sqlite3.Open(":memory:")
if err != nil {
t.Fatal(err)
}
defer db.Close()
pivot.Register(db)
err = db.Exec(`
CREATE TABLE r AS
SELECT 1 id UNION SELECT 2 UNION SELECT 3;
CREATE TABLE c(
id INTEGER PRIMARY KEY,
name TEXT
);
INSERT INTO c (name) VALUES
('a'),('b'),('c'),('d');
CREATE TABLE x(
r_id INT,
c_id INT,
val TEXT
);
INSERT INTO x (r_id, c_id, val)
SELECT r.id, c.id, c.name || r.id
FROM c, r;
`)
if err != nil {
t.Fatal(err)
}
err = db.Exec(`
CREATE VIRTUAL TABLE v_x USING pivot(
-- rows
(SELECT id r_id FROM r),
-- columns
(SELECT id c_id, name FROM c),
-- cells
(SELECT val FROM x WHERE r_id = ?1 AND c_id = ?2)
)`)
if err != nil {
t.Fatal(err)
}
stmt, _, err := db.Prepare(`SELECT * FROM v_x WHERE rowid <> 0 AND r_id <> 1 ORDER BY rowid, r_id DESC LIMIT 1`)
if err != nil {
t.Fatal(err)
}
defer stmt.Close()
if stmt.Step() {
if got := stmt.ColumnInt(0); got != 3 {
t.Errorf("got %d, want 3", got)
}
}
}
func TestRegister_errors(t *testing.T) {
t.Parallel()
db, err := sqlite3.Open(":memory:")
if err != nil {
t.Fatal(err)
}
defer db.Close()
pivot.Register(db)
err = db.Exec(`CREATE VIRTUAL TABLE pivot USING pivot()`)
if err == nil {
t.Fatal("want error")
} else {
t.Log(err)
}
err = db.Exec(`CREATE VIRTUAL TABLE split_date USING pivot(SELECT 1, SELECT 2, SELECT 3)`)
if err == nil {
t.Fatal("want error")
} else {
t.Log(err)
}
err = db.Exec(`CREATE VIRTUAL TABLE split_date USING pivot((SELECT 1), SELECT 2, SELECT 3)`)
if err == nil {
t.Fatal("want error")
} else {
t.Log(err)
}
err = db.Exec(`CREATE VIRTUAL TABLE split_date USING pivot((SELECT 1), (SELECT 2), SELECT 3)`)
if err == nil {
t.Fatal("want error")
} else {
t.Log(err)
}
err = db.Exec(`CREATE VIRTUAL TABLE split_date USING pivot((SELECT 1), (SELECT 1, 2), SELECT 3)`)
if err == nil {
t.Fatal("want error")
} else {
t.Log(err)
}
err = db.Exec(`CREATE VIRTUAL TABLE split_date USING pivot((SELECT 1), (SELECT 1, 2), (SELECT 3, 4))`)
if err == nil {
t.Fatal("want error")
} else {
t.Log(err)
}
err = db.Exec(`CREATE VIRTUAL TABLE split_date USING pivot((SELECT 1), (SELECT 1, 2), (SELECT 3))`)
if err == nil {
t.Fatal("want error")
} else {
t.Log(err)
}
}
func pretty(cols []string) string {
var buf strings.Builder
for i, s := range cols {
if i != 0 {
buf.WriteByte(' ')
}
for buf.Len()%8 != 0 {
buf.WriteByte(' ')
}
buf.WriteString(s)
}
return buf.String()
}

213
ext/statement/stmt.go Normal file
View File

@@ -0,0 +1,213 @@
// Package statement defines table-valued functions using SQL.
//
// It can be used to create "parametrized views":
// pre-packaged queries that can be parametrized at query execution time.
//
// https://github.com/0x09/sqlite-statement-vtab
package statement
import (
"encoding/json"
"fmt"
"strconv"
"strings"
"unsafe"
"github.com/ncruces/go-sqlite3"
)
// Register registers the statement virtual table.
func Register(db *sqlite3.Conn) {
sqlite3.CreateModule(db, "statement", declare, declare)
}
type table struct {
stmt *sqlite3.Stmt
sql string
inuse bool
}
func declare(db *sqlite3.Conn, _, _, _ string, arg ...string) (*table, error) {
if len(arg) != 1 {
return nil, fmt.Errorf("statement: wrong number of arguments")
}
sql := "SELECT * FROM\n" + arg[0]
stmt, _, err := db.Prepare(sql)
if err != nil {
return nil, err
}
var sep string
var str strings.Builder
str.WriteString("CREATE TABLE x(")
outputs := stmt.ColumnCount()
for i := 0; i < outputs; i++ {
name := sqlite3.QuoteIdentifier(stmt.ColumnName(i))
str.WriteString(sep)
str.WriteString(name)
str.WriteString(" ")
str.WriteString(stmt.ColumnDeclType(i))
sep = ","
}
inputs := stmt.BindCount()
for i := 1; i <= inputs; i++ {
str.WriteString(sep)
name := stmt.BindName(i)
if name == "" {
str.WriteString("[")
str.WriteString(strconv.Itoa(i))
str.WriteString("] HIDDEN")
} else {
str.WriteString(sqlite3.QuoteIdentifier(name[1:]))
str.WriteString(" HIDDEN")
}
sep = ","
}
str.WriteByte(')')
err = db.DeclareVTab(str.String())
if err != nil {
stmt.Close()
return nil, err
}
return &table{sql: sql, stmt: stmt}, nil
}
func (t *table) Close() error {
return t.stmt.Close()
}
func (t *table) BestIndex(idx *sqlite3.IndexInfo) error {
idx.EstimatedCost = 1000
var argvIndex = 1
var needIndex bool
var listIndex []int
outputs := t.stmt.ColumnCount()
for i, cst := range idx.Constraint {
// Skip if this is a constraint on one of our output columns.
if cst.Column < outputs {
continue
}
// A given query plan is only usable if all provided input columns
// are usable and have equal constraints only.
if !cst.Usable || cst.Op != sqlite3.INDEX_CONSTRAINT_EQ {
return sqlite3.CONSTRAINT
}
// The non-zero argvIdx values must be contiguous.
// If they're not, build a list and serialize it through IdxStr.
nextIndex := cst.Column - outputs + 1
idx.ConstraintUsage[i] = sqlite3.IndexConstraintUsage{
ArgvIndex: argvIndex,
Omit: true,
}
if nextIndex != argvIndex {
needIndex = true
}
listIndex = append(listIndex, nextIndex)
argvIndex++
}
if needIndex {
buf, err := json.Marshal(listIndex)
if err != nil {
return err
}
idx.IdxStr = unsafe.String(&buf[0], len(buf))
}
return nil
}
func (t *table) Open() (sqlite3.VTabCursor, error) {
stmt := t.stmt
if !t.inuse {
t.inuse = true
} else {
var err error
stmt, _, err = t.stmt.Conn().Prepare(t.sql)
if err != nil {
return nil, err
}
}
return &cursor{table: t, stmt: stmt}, nil
}
func (t *table) Rename(new string) error {
return nil
}
type cursor struct {
table *table
stmt *sqlite3.Stmt
arg []sqlite3.Value
rowID int64
}
func (c *cursor) Close() error {
if c.stmt == c.table.stmt {
c.table.inuse = false
c.stmt.ClearBindings()
return c.stmt.Reset()
}
return c.stmt.Close()
}
func (c *cursor) Filter(idxNum int, idxStr string, arg ...sqlite3.Value) error {
c.arg = arg
c.rowID = 0
c.stmt.ClearBindings()
if err := c.stmt.Reset(); err != nil {
return err
}
var list []int
if idxStr != "" {
buf := unsafe.Slice(unsafe.StringData(idxStr), len(idxStr))
err := json.Unmarshal(buf, &list)
if err != nil {
return err
}
}
for i, arg := range arg {
param := i + 1
if list != nil {
param = list[i]
}
err := c.stmt.BindValue(param, arg)
if err != nil {
return err
}
}
return c.Next()
}
func (c *cursor) Next() error {
if c.stmt.Step() {
c.rowID++
}
return c.stmt.Err()
}
func (c *cursor) EOF() bool {
return !c.stmt.Busy()
}
func (c *cursor) RowID() (int64, error) {
return c.rowID, nil
}
func (c *cursor) Column(ctx *sqlite3.Context, col int) error {
switch outputs := c.stmt.ColumnCount(); {
case col < outputs:
ctx.ResultValue(c.stmt.ColumnValue(col))
case col-outputs < len(c.arg):
ctx.ResultValue(c.arg[col-outputs])
}
return nil
}

145
ext/statement/stmt_test.go Normal file
View File

@@ -0,0 +1,145 @@
package statement_test
import (
"fmt"
"log"
"testing"
"github.com/ncruces/go-sqlite3"
_ "github.com/ncruces/go-sqlite3/embed"
"github.com/ncruces/go-sqlite3/ext/statement"
)
func Example() {
db, err := sqlite3.Open(":memory:")
if err != nil {
log.Fatal(err)
}
defer db.Close()
statement.Register(db)
err = db.Exec(`
CREATE VIRTUAL TABLE split_date USING statement((
SELECT
strftime('%Y', :date) AS year,
strftime('%m', :date) AS month,
strftime('%d', :date) AS day
))`)
if err != nil {
log.Fatal(err)
}
stmt, _, err := db.Prepare(`SELECT * FROM split_date('2022-02-22')`)
if err != nil {
log.Fatal(err)
}
defer stmt.Close()
if stmt.Step() {
fmt.Printf("Twosday was %d-%d-%d", stmt.ColumnInt(0), stmt.ColumnInt(1), stmt.ColumnInt(2))
}
if err := stmt.Reset(); err != nil {
log.Fatal(err)
}
// Output:
// Twosday was 2022-2-22
}
func TestRegister(t *testing.T) {
t.Parallel()
db, err := sqlite3.Open(":memory:")
if err != nil {
t.Fatal(err)
}
defer db.Close()
statement.Register(db)
err = db.Exec(`
CREATE VIRTUAL TABLE arguments USING statement((SELECT ? AS a, ? AS b, ? AS c))
`)
if err != nil {
t.Fatal(err)
}
err = db.Exec(`
SELECT * from arguments WHERE [2] = 'y' AND [3] = 'z'
`)
if err != nil {
t.Fatal(err)
}
err = db.Exec(`
CREATE VIRTUAL TABLE hypot USING statement((SELECT sqrt(:x * :x + :y * :y) AS hypotenuse))
`)
if err != nil {
t.Fatal(err)
}
stmt, _, err := db.Prepare(`
SELECT x, y, * FROM hypot WHERE x = 3 AND y = 4
`)
if err != nil {
t.Fatal(err)
}
defer stmt.Close()
if stmt.Step() {
x := stmt.ColumnInt(0)
y := stmt.ColumnInt(1)
hypot := stmt.ColumnInt(2)
if x != 3 || y != 4 || hypot != 5 {
t.Errorf("hypot(%d, %d) = %d", x, y, hypot)
}
}
}
func TestRegister_errors(t *testing.T) {
t.Parallel()
db, err := sqlite3.Open(":memory:")
if err != nil {
t.Fatal(err)
}
defer db.Close()
statement.Register(db)
err = db.Exec(`CREATE VIRTUAL TABLE split_date USING statement()`)
if err == nil {
t.Fatal("want error")
} else {
t.Log(err)
}
err = db.Exec(`CREATE VIRTUAL TABLE split_date USING statement(SELECT 1, SELECT 2)`)
if err == nil {
t.Fatal("want error")
} else {
t.Log(err)
}
err = db.Exec(`CREATE VIRTUAL TABLE split_date USING statement((SELECT 1, SELECT 2))`)
if err == nil {
t.Fatal("want error")
} else {
t.Log(err)
}
err = db.Exec(`CREATE VIRTUAL TABLE split_date USING statement((SELECT 1; SELECT 2))`)
if err == nil {
t.Fatal("want error")
} else {
t.Log(err)
}
err = db.Exec(`CREATE VIRTUAL TABLE split_date USING statement((CREATE TABLE x(val)))`)
if err == nil {
t.Fatal("want error")
} else {
t.Log(err)
}
}

47
ext/stats/TODO.md Normal file
View File

@@ -0,0 +1,47 @@
# ANSI SQL Aggregate Functions
https://www.oreilly.com/library/view/sql-in-a/9780596155322/ch04s02.html
## Built in aggregates
- [x] `COUNT(*)`
- [x] `COUNT(expression)`
- [x] `SUM(expression)`
- [x] `AVG(expression)`
- [x] `MIN(expression)`
- [x] `MAX(expression)`
https://sqlite.org/lang_aggfunc.html
## Statistical aggregates
- [x] `STDDEV_POP(expression)`
- [x] `STDDEV_SAMP(expression)`
- [x] `VAR_POP(expression)`
- [x] `VAR_SAMP(expression)`
- [x] `COVAR_POP(dependent, independent)`
- [x] `COVAR_SAMP(dependent, independent)`
- [x] `CORR(dependent, independent)`
## Linear regression aggregates
- [X] `REGR_AVGX(dependent, independent)`
- [X] `REGR_AVGY(dependent, independent)`
- [X] `REGR_SXX(dependent, independent)`
- [X] `REGR_SYY(dependent, independent)`
- [X] `REGR_SXY(dependent, independent)`
- [X] `REGR_COUNT(dependent, independent)`
- [X] `REGR_SLOPE(dependent, independent)`
- [X] `REGR_INTERCEPT(dependent, independent)`
- [X] `REGR_R2(dependent, independent)`
## Set aggregates
- [X] `CUME_DIST() OVER window`
- [X] `RANK() OVER window`
- [X] `DENSE_RANK() OVER window`
- [X] `PERCENT_RANK() OVER window`
- [ ] `PERCENTILE_CONT(percentile) OVER window`
- [ ] `PERCENTILE_DISC(percentile) OVER window`
https://sqlite.org/windowfunctions.html#builtins

169
ext/stats/stats.go Normal file
View File

@@ -0,0 +1,169 @@
// Package stats provides aggregate functions for statistics.
//
// Provided functions:
// - stddev_pop: population standard deviation
// - stddev_samp: sample standard deviation
// - var_pop: population variance
// - var_samp: sample variance
// - covar_pop: population covariance
// - covar_samp: sample covariance
// - corr: correlation coefficient
// - regr_r2: correlation coefficient squared
// - regr_avgx: average of the independent variable
// - regr_avgy: average of the dependent variable
// - regr_sxx: sum of the squares of the independent variable
// - regr_syy: sum of the squares of the dependent variable
// - regr_sxy: sum of the products of each pair of variables
// - regr_count: count non-null pairs of variables
// - regr_slope: slope of the least-squares-fit linear equation
// - regr_intercept: y-intercept of the least-squares-fit linear equation
// - regr_json: all regr stats in a JSON object
//
// These join the [Built-in Aggregate Functions]:
// - count: count rows/values
// - sum: sum values
// - avg: average value
// - min: minimum value
// - max: maximum value
//
// See: [ANSI SQL Aggregate Functions]
//
// [Built-in Aggregate Functions]: https://sqlite.org/lang_aggfunc.html
// [ANSI SQL Aggregate Functions]: https://www.oreilly.com/library/view/sql-in-a/9780596155322/ch04s02.html
package stats
import "github.com/ncruces/go-sqlite3"
// Register registers statistics functions.
func Register(db *sqlite3.Conn) {
flags := sqlite3.DETERMINISTIC | sqlite3.INNOCUOUS
db.CreateWindowFunction("var_pop", 1, flags, newVariance(var_pop))
db.CreateWindowFunction("var_samp", 1, flags, newVariance(var_samp))
db.CreateWindowFunction("stddev_pop", 1, flags, newVariance(stddev_pop))
db.CreateWindowFunction("stddev_samp", 1, flags, newVariance(stddev_samp))
db.CreateWindowFunction("covar_pop", 2, flags, newCovariance(var_pop))
db.CreateWindowFunction("covar_samp", 2, flags, newCovariance(var_samp))
db.CreateWindowFunction("corr", 2, flags, newCovariance(corr))
db.CreateWindowFunction("regr_r2", 2, flags, newCovariance(regr_r2))
db.CreateWindowFunction("regr_sxx", 2, flags, newCovariance(regr_sxx))
db.CreateWindowFunction("regr_syy", 2, flags, newCovariance(regr_syy))
db.CreateWindowFunction("regr_sxy", 2, flags, newCovariance(regr_sxy))
db.CreateWindowFunction("regr_avgx", 2, flags, newCovariance(regr_avgx))
db.CreateWindowFunction("regr_avgy", 2, flags, newCovariance(regr_avgy))
db.CreateWindowFunction("regr_slope", 2, flags, newCovariance(regr_slope))
db.CreateWindowFunction("regr_intercept", 2, flags, newCovariance(regr_intercept))
db.CreateWindowFunction("regr_count", 2, flags, newCovariance(regr_count))
db.CreateWindowFunction("regr_json", 2, flags, newCovariance(regr_json))
}
const (
var_pop = iota
var_samp
stddev_pop
stddev_samp
corr
regr_r2
regr_sxx
regr_syy
regr_sxy
regr_avgx
regr_avgy
regr_slope
regr_intercept
regr_count
regr_json
)
func newVariance(kind int) func() sqlite3.AggregateFunction {
return func() sqlite3.AggregateFunction { return &variance{kind: kind} }
}
type variance struct {
kind int
welford
}
func (fn *variance) Value(ctx sqlite3.Context) {
var r float64
switch fn.kind {
case var_pop:
r = fn.var_pop()
case var_samp:
r = fn.var_samp()
case stddev_pop:
r = fn.stddev_pop()
case stddev_samp:
r = fn.stddev_samp()
}
ctx.ResultFloat(r)
}
func (fn *variance) Step(ctx sqlite3.Context, arg ...sqlite3.Value) {
if a := arg[0]; a.NumericType() != sqlite3.NULL {
fn.enqueue(a.Float())
}
}
func (fn *variance) Inverse(ctx sqlite3.Context, arg ...sqlite3.Value) {
if a := arg[0]; a.NumericType() != sqlite3.NULL {
fn.dequeue(a.Float())
}
}
func newCovariance(kind int) func() sqlite3.AggregateFunction {
return func() sqlite3.AggregateFunction { return &covariance{kind: kind} }
}
type covariance struct {
kind int
welford2
}
func (fn *covariance) Value(ctx sqlite3.Context) {
var r float64
switch fn.kind {
case var_pop:
r = fn.covar_pop()
case var_samp:
r = fn.covar_samp()
case corr:
r = fn.correlation()
case regr_r2:
r = fn.regr_r2()
case regr_sxx:
r = fn.regr_sxx()
case regr_syy:
r = fn.regr_syy()
case regr_sxy:
r = fn.regr_sxy()
case regr_avgx:
r = fn.regr_avgx()
case regr_avgy:
r = fn.regr_avgy()
case regr_slope:
r = fn.regr_slope()
case regr_intercept:
r = fn.regr_intercept()
case regr_count:
ctx.ResultInt64(fn.regr_count())
return
case regr_json:
ctx.ResultText(fn.regr_json())
return
}
ctx.ResultFloat(r)
}
func (fn *covariance) Step(ctx sqlite3.Context, arg ...sqlite3.Value) {
a, b := arg[0], arg[1]
if a.NumericType() != sqlite3.NULL && b.NumericType() != sqlite3.NULL {
fn.enqueue(a.Float(), b.Float())
}
}
func (fn *covariance) Inverse(ctx sqlite3.Context, arg ...sqlite3.Value) {
a, b := arg[0], arg[1]
if a.NumericType() != sqlite3.NULL && b.NumericType() != sqlite3.NULL {
fn.dequeue(a.Float(), b.Float())
}
}

243
ext/stats/stats_test.go Normal file
View File

@@ -0,0 +1,243 @@
package stats_test
import (
"math"
"testing"
"github.com/ncruces/go-sqlite3"
_ "github.com/ncruces/go-sqlite3/embed"
"github.com/ncruces/go-sqlite3/ext/stats"
)
func TestRegister_variance(t *testing.T) {
t.Parallel()
db, err := sqlite3.Open(":memory:")
if err != nil {
t.Fatal(err)
}
defer db.Close()
stats.Register(db)
err = db.Exec(`CREATE TABLE IF NOT EXISTS data (x)`)
if err != nil {
t.Fatal(err)
}
err = db.Exec(`INSERT INTO data (x) VALUES (4), (7.0), ('13'), (NULL), (16)`)
if err != nil {
t.Fatal(err)
}
stmt, _, err := db.Prepare(`
SELECT
sum(x), avg(x),
var_samp(x), var_pop(x),
stddev_samp(x), stddev_pop(x)
FROM data`)
if err != nil {
t.Fatal(err)
}
defer stmt.Close()
if stmt.Step() {
if got := stmt.ColumnFloat(0); got != 40 {
t.Errorf("got %v, want 40", got)
}
if got := stmt.ColumnFloat(1); got != 10 {
t.Errorf("got %v, want 10", got)
}
if got := stmt.ColumnFloat(2); got != 30 {
t.Errorf("got %v, want 30", got)
}
if got := stmt.ColumnFloat(3); got != 22.5 {
t.Errorf("got %v, want 22.5", got)
}
if got := stmt.ColumnFloat(4); got != math.Sqrt(30) {
t.Errorf("got %v, want √30", got)
}
if got := stmt.ColumnFloat(5); got != math.Sqrt(22.5) {
t.Errorf("got %v, want √22.5", got)
}
}
{
stmt, _, err := db.Prepare(`SELECT var_samp(x) OVER (ROWS 1 PRECEDING) FROM data`)
if err != nil {
t.Fatal(err)
}
defer stmt.Close()
want := [...]float64{0, 4.5, 18, 0, 0}
for i := 0; stmt.Step(); i++ {
if got := stmt.ColumnFloat(0); got != want[i] {
t.Errorf("got %v, want %v", got, want[i])
}
if got := stmt.ColumnType(0); (got == sqlite3.FLOAT) != (want[i] != 0) {
t.Errorf("got %v, want %v", got, want[i])
}
}
}
}
func TestRegister_covariance(t *testing.T) {
t.Parallel()
db, err := sqlite3.Open(":memory:")
if err != nil {
t.Fatal(err)
}
defer db.Close()
stats.Register(db)
err = db.Exec(`CREATE TABLE IF NOT EXISTS data (y, x)`)
if err != nil {
t.Fatal(err)
}
err = db.Exec(`INSERT INTO data (y, x) VALUES (3, 70), (5, 80), (2, 60), (7, 90), (4, 75)`)
if err != nil {
t.Fatal(err)
}
stmt, _, err := db.Prepare(`SELECT
corr(y, x), covar_samp(y, x), covar_pop(y, x),
regr_avgy(y, x), regr_avgx(y, x),
regr_syy(y, x), regr_sxx(y, x), regr_sxy(y, x),
regr_slope(y, x), regr_intercept(y, x), regr_r2(y, x),
regr_count(y, x), regr_json(y, x)
FROM data`)
if err != nil {
t.Fatal(err)
}
defer stmt.Close()
if stmt.Step() {
if got := stmt.ColumnFloat(0); got != 0.9881049293224639 {
t.Errorf("got %v, want 0.9881049293224639", got)
}
if got := stmt.ColumnFloat(1); got != 21.25 {
t.Errorf("got %v, want 21.25", got)
}
if got := stmt.ColumnFloat(2); got != 17 {
t.Errorf("got %v, want 17", got)
}
if got := stmt.ColumnFloat(3); got != 4.2 {
t.Errorf("got %v, want 4.2", got)
}
if got := stmt.ColumnFloat(4); got != 75 {
t.Errorf("got %v, want 75", got)
}
if got := stmt.ColumnFloat(5); got != 14.8 {
t.Errorf("got %v, want 14.8", got)
}
if got := stmt.ColumnFloat(6); got != 500 {
t.Errorf("got %v, want 500", got)
}
if got := stmt.ColumnFloat(7); got != 85 {
t.Errorf("got %v, want 85", got)
}
if got := stmt.ColumnFloat(8); got != 0.17 {
t.Errorf("got %v, want 0.17", got)
}
if got := stmt.ColumnFloat(9); got != -8.55 {
t.Errorf("got %v, want -8.55", got)
}
if got := stmt.ColumnFloat(10); got != 0.9763513513513513 {
t.Errorf("got %v, want 0.9763513513513513", got)
}
if got := stmt.ColumnInt(11); got != 5 {
t.Errorf("got %v, want 5", got)
}
var a map[string]float64
if err := stmt.ColumnJSON(12, &a); err != nil {
t.Error(err)
} else if got := a["count"]; got != 5 {
t.Errorf("got %v, want 5", got)
}
}
{
stmt, _, err := db.Prepare(`SELECT covar_samp(y, x) OVER (ROWS 1 PRECEDING) FROM data`)
if err != nil {
t.Fatal(err)
}
defer stmt.Close()
want := [...]float64{0, 10, 30, 75, 22.5}
for i := 0; stmt.Step(); i++ {
if got := stmt.ColumnFloat(0); got != want[i] {
t.Errorf("got %v, want %v", got, want[i])
}
if got := stmt.ColumnType(0); (got == sqlite3.FLOAT) != (want[i] != 0) {
t.Errorf("got %v, want %v", got, want[i])
}
}
}
}
func Benchmark_average(b *testing.B) {
db, err := sqlite3.Open(":memory:")
if err != nil {
b.Fatal(err)
}
defer db.Close()
stmt, _, err := db.Prepare(`SELECT avg(value) FROM generate_series(0, ?)`)
if err != nil {
b.Fatal(err)
}
defer stmt.Close()
err = stmt.BindInt(1, b.N)
if err != nil {
b.Fatal(err)
}
if stmt.Step() {
want := float64(b.N) / 2
if got := stmt.ColumnFloat(0); got != want {
b.Errorf("got %v, want %v", got, want)
}
}
err = stmt.Err()
if err != nil {
b.Error(err)
}
}
func Benchmark_variance(b *testing.B) {
db, err := sqlite3.Open(":memory:")
if err != nil {
b.Fatal(err)
}
defer db.Close()
stats.Register(db)
stmt, _, err := db.Prepare(`SELECT var_pop(value) FROM generate_series(0, ?)`)
if err != nil {
b.Fatal(err)
}
defer stmt.Close()
err = stmt.BindInt(1, b.N)
if err != nil {
b.Fatal(err)
}
if stmt.Step() && b.N > 100 {
want := float64(b.N*b.N) / 12
if got := stmt.ColumnFloat(0); want > (got-want)*float64(b.N) {
b.Errorf("got %v, want %v", got, want)
}
}
err = stmt.Err()
if err != nil {
b.Error(err)
}
}

181
ext/stats/welford.go Normal file
View File

@@ -0,0 +1,181 @@
package stats
import (
"math"
"strconv"
"strings"
)
// Welford's algorithm with Kahan summation:
// https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford's_online_algorithm
// https://en.wikipedia.org/wiki/Kahan_summation_algorithm
// See also:
// https://duckdb.org/docs/sql/aggregates.html#statistical-aggregates
type welford struct {
m1, m2 kahan
n int64
}
func (w welford) average() float64 {
return w.m1.hi
}
func (w welford) var_pop() float64 {
return w.m2.hi / float64(w.n)
}
func (w welford) var_samp() float64 {
return w.m2.hi / float64(w.n-1) // Bessel's correction
}
func (w welford) stddev_pop() float64 {
return math.Sqrt(w.var_pop())
}
func (w welford) stddev_samp() float64 {
return math.Sqrt(w.var_samp())
}
func (w *welford) enqueue(x float64) {
w.n++
d1 := x - w.m1.hi - w.m1.lo
w.m1.add(d1 / float64(w.n))
d2 := x - w.m1.hi - w.m1.lo
w.m2.add(d1 * d2)
}
func (w *welford) dequeue(x float64) {
w.n--
d1 := x - w.m1.hi - w.m1.lo
w.m1.sub(d1 / float64(w.n))
d2 := x - w.m1.hi - w.m1.lo
w.m2.sub(d1 * d2)
}
type welford2 struct {
m1y, m2y kahan
m1x, m2x kahan
cov kahan
n int64
}
func (w welford2) covar_pop() float64 {
return w.cov.hi / float64(w.n)
}
func (w welford2) covar_samp() float64 {
return w.cov.hi / float64(w.n-1) // Bessel's correction
}
func (w welford2) correlation() float64 {
return w.cov.hi / math.Sqrt(w.m2y.hi*w.m2x.hi)
}
func (w welford2) regr_avgy() float64 {
return w.m1y.hi
}
func (w welford2) regr_avgx() float64 {
return w.m1x.hi
}
func (w welford2) regr_syy() float64 {
return w.m2y.hi
}
func (w welford2) regr_sxx() float64 {
return w.m2x.hi
}
func (w welford2) regr_sxy() float64 {
return w.cov.hi
}
func (w welford2) regr_count() int64 {
return w.n
}
func (w welford2) regr_slope() float64 {
return w.cov.hi / w.m2x.hi
}
func (w welford2) regr_intercept() float64 {
slope := -w.regr_slope()
hi := math.FMA(slope, w.m1x.hi, w.m1y.hi)
lo := math.FMA(slope, w.m1x.lo, w.m1y.lo)
return hi + lo
}
func (w welford2) regr_r2() float64 {
return w.cov.hi * w.cov.hi / (w.m2y.hi * w.m2x.hi)
}
func (w welford2) regr_json() string {
var json strings.Builder
var num [32]byte
json.Grow(128)
json.WriteString(`{"count":`)
json.Write(strconv.AppendInt(num[:0], w.regr_count(), 10))
json.WriteString(`,"avgy":`)
json.Write(strconv.AppendFloat(num[:0], w.regr_avgy(), 'g', -1, 64))
json.WriteString(`,"avgx":`)
json.Write(strconv.AppendFloat(num[:0], w.regr_avgx(), 'g', -1, 64))
json.WriteString(`,"syy":`)
json.Write(strconv.AppendFloat(num[:0], w.regr_syy(), 'g', -1, 64))
json.WriteString(`,"sxx":`)
json.Write(strconv.AppendFloat(num[:0], w.regr_sxx(), 'g', -1, 64))
json.WriteString(`,"sxy":`)
json.Write(strconv.AppendFloat(num[:0], w.regr_sxy(), 'g', -1, 64))
json.WriteString(`,"slope":`)
json.Write(strconv.AppendFloat(num[:0], w.regr_slope(), 'g', -1, 64))
json.WriteString(`,"intercept":`)
json.Write(strconv.AppendFloat(num[:0], w.regr_intercept(), 'g', -1, 64))
json.WriteString(`,"r2":`)
json.Write(strconv.AppendFloat(num[:0], w.regr_r2(), 'g', -1, 64))
json.WriteByte('}')
return json.String()
}
func (w *welford2) enqueue(y, x float64) {
w.n++
d1y := y - w.m1y.hi - w.m1y.lo
d1x := x - w.m1x.hi - w.m1x.lo
w.m1y.add(d1y / float64(w.n))
w.m1x.add(d1x / float64(w.n))
d2y := y - w.m1y.hi - w.m1y.lo
d2x := x - w.m1x.hi - w.m1x.lo
w.m2y.add(d1y * d2y)
w.m2x.add(d1x * d2x)
w.cov.add(d1y * d2x)
}
func (w *welford2) dequeue(y, x float64) {
w.n--
d1y := y - w.m1y.hi - w.m1y.lo
d1x := x - w.m1x.hi - w.m1x.lo
w.m1y.sub(d1y / float64(w.n))
w.m1x.sub(d1x / float64(w.n))
d2y := y - w.m1y.hi - w.m1y.lo
d2x := x - w.m1x.hi - w.m1x.lo
w.m2y.sub(d1y * d2y)
w.m2x.sub(d1x * d2x)
w.cov.sub(d1y * d2x)
}
type kahan struct{ hi, lo float64 }
func (k *kahan) add(x float64) {
y := k.lo + x
t := k.hi + y
k.lo = y - (t - k.hi)
k.hi = t
}
func (k *kahan) sub(x float64) {
y := k.lo - x
t := k.hi + y
k.lo = y - (t - k.hi)
k.hi = t
}

81
ext/stats/welford_test.go Normal file
View File

@@ -0,0 +1,81 @@
package stats
import (
"math"
"testing"
)
func Test_welford(t *testing.T) {
t.Parallel()
var s1, s2 welford
s1.enqueue(4)
s1.enqueue(7)
s1.enqueue(13)
s1.enqueue(16)
if got := s1.average(); got != 10 {
t.Errorf("got %v, want 10", got)
}
if got := s1.var_samp(); got != 30 {
t.Errorf("got %v, want 30", got)
}
if got := s1.var_pop(); got != 22.5 {
t.Errorf("got %v, want 22.5", got)
}
if got := s1.stddev_samp(); got != math.Sqrt(30) {
t.Errorf("got %v, want √30", got)
}
if got := s1.stddev_pop(); got != math.Sqrt(22.5) {
t.Errorf("got %v, want √22.5", got)
}
s1.dequeue(4)
s2.enqueue(7)
s2.enqueue(13)
s2.enqueue(16)
if s1.var_pop() != s2.var_pop() {
t.Errorf("got %v, want %v", s1, s2)
}
}
func Test_covar(t *testing.T) {
t.Parallel()
var c1, c2 welford2
c1.enqueue(3, 70)
c1.enqueue(5, 80)
c1.enqueue(2, 60)
c1.enqueue(7, 90)
c1.enqueue(4, 75)
if got := c1.covar_samp(); got != 21.25 {
t.Errorf("got %v, want 21.25", got)
}
if got := c1.covar_pop(); got != 17 {
t.Errorf("got %v, want 17", got)
}
c1.dequeue(3, 70)
c2.enqueue(5, 80)
c2.enqueue(2, 60)
c2.enqueue(7, 90)
c2.enqueue(4, 75)
if c1.covar_pop() != c2.covar_pop() {
t.Errorf("got %v, want %v", c1.covar_pop(), c2.covar_pop())
}
}
func Test_correlation(t *testing.T) {
t.Parallel()
var c welford2
c.enqueue(1, 3)
c.enqueue(2, 2)
c.enqueue(3, 1)
if got := c.correlation(); got != -1 {
t.Errorf("got %v, want -1", got)
}
}

181
ext/unicode/unicode.go Normal file
View File

@@ -0,0 +1,181 @@
// Package unicode provides an alternative to the SQLite ICU extension.
//
// Like the [ICU extension], it provides Unicode aware:
// - upper() and lower() functions,
// - LIKE and REGEXP operators,
// - collation sequences.
//
// The implementation is not 100% compatible with the [ICU extension]:
// - upper() and lower() use [strings.ToUpper], [strings.ToLower] and [cases];
// - the LIKE operator follows [strings.EqualFold] rules;
// - the REGEXP operator uses Go [regexp/syntax];
// - collation sequences use [collate].
//
// Expect subtle differences (e.g.) in the handling of Turkish case folding.
//
// [ICU extension]: https://sqlite.org/src/dir/ext/icu
package unicode
import (
"bytes"
"regexp"
"strings"
"unicode/utf8"
"github.com/ncruces/go-sqlite3"
"github.com/ncruces/go-sqlite3/internal/util"
"golang.org/x/text/cases"
"golang.org/x/text/collate"
"golang.org/x/text/language"
)
// Register registers Unicode aware functions for a database connection.
func Register(db *sqlite3.Conn) {
flags := sqlite3.DETERMINISTIC | sqlite3.INNOCUOUS
db.CreateFunction("like", 2, flags, like)
db.CreateFunction("like", 3, flags, like)
db.CreateFunction("upper", 1, flags, upper)
db.CreateFunction("upper", 2, flags, upper)
db.CreateFunction("lower", 1, flags, lower)
db.CreateFunction("lower", 2, flags, lower)
db.CreateFunction("regexp", 2, flags, regex)
db.CreateFunction("icu_load_collation", 2, sqlite3.DIRECTONLY,
func(ctx sqlite3.Context, arg ...sqlite3.Value) {
name := arg[1].Text()
if name == "" {
return
}
err := RegisterCollation(db, arg[0].Text(), name)
if err != nil {
ctx.ResultError(err)
return
}
})
}
// RegisterCollation registers a Unicode collation sequence for a database connection.
func RegisterCollation(db *sqlite3.Conn, locale, name string) error {
tag, err := language.Parse(locale)
if err != nil {
return err
}
return db.CreateCollation(name, collate.New(tag).Compare)
}
func upper(ctx sqlite3.Context, arg ...sqlite3.Value) {
if len(arg) == 1 {
ctx.ResultRawText(bytes.ToUpper(arg[0].RawText()))
return
}
cs, ok := ctx.GetAuxData(1).(cases.Caser)
if !ok {
t, err := language.Parse(arg[1].Text())
if err != nil {
ctx.ResultError(err)
return
}
c := cases.Upper(t)
ctx.SetAuxData(1, c)
cs = c
}
ctx.ResultRawText(cs.Bytes(arg[0].RawText()))
}
func lower(ctx sqlite3.Context, arg ...sqlite3.Value) {
if len(arg) == 1 {
ctx.ResultRawText(bytes.ToLower(arg[0].RawText()))
return
}
cs, ok := ctx.GetAuxData(1).(cases.Caser)
if !ok {
t, err := language.Parse(arg[1].Text())
if err != nil {
ctx.ResultError(err)
return
}
c := cases.Lower(t)
ctx.SetAuxData(1, c)
cs = c
}
ctx.ResultRawText(cs.Bytes(arg[0].RawText()))
}
func regex(ctx sqlite3.Context, arg ...sqlite3.Value) {
re, ok := ctx.GetAuxData(0).(*regexp.Regexp)
if !ok {
r, err := regexp.Compile(arg[0].Text())
if err != nil {
ctx.ResultError(err)
return
}
re = r
ctx.SetAuxData(0, re)
}
ctx.ResultBool(re.Match(arg[1].RawText()))
}
func like(ctx sqlite3.Context, arg ...sqlite3.Value) {
escape := rune(-1)
if len(arg) == 3 {
var size int
b := arg[2].RawText()
escape, size = utf8.DecodeRune(b)
if size != len(b) {
ctx.ResultError(util.ErrorString("ESCAPE expression must be a single character"))
return
}
}
type likeData struct {
*regexp.Regexp
escape rune
}
re, ok := ctx.GetAuxData(0).(likeData)
if !ok || re.escape != escape {
re = likeData{
regexp.MustCompile(like2regex(arg[0].Text(), escape)),
escape,
}
ctx.SetAuxData(0, re)
}
ctx.ResultBool(re.Match(arg[1].RawText()))
}
func like2regex(pattern string, escape rune) string {
var re strings.Builder
start := 0
literal := false
re.Grow(len(pattern) + 10)
re.WriteString(`(?is)\A`) // case insensitive, . matches any character
for i, r := range pattern {
if start < 0 {
start = i
}
if literal {
literal = false
continue
}
var symbol string
switch r {
case '_':
symbol = `.`
case '%':
symbol = `.*`
case escape:
literal = true
default:
continue
}
re.WriteString(regexp.QuoteMeta(pattern[start:i]))
re.WriteString(symbol)
start = -1
}
if start >= 0 {
re.WriteString(regexp.QuoteMeta(pattern[start:]))
}
re.WriteString(`\z`)
return re.String()
}

217
ext/unicode/unicode_test.go Normal file
View File

@@ -0,0 +1,217 @@
package unicode
import (
"errors"
"reflect"
"testing"
"github.com/ncruces/go-sqlite3"
_ "github.com/ncruces/go-sqlite3/embed"
)
func TestRegister(t *testing.T) {
t.Parallel()
db, err := sqlite3.Open(":memory:")
if err != nil {
t.Fatal(err)
}
defer db.Close()
exec := func(fn string) string {
stmt, _, err := db.Prepare(`SELECT ` + fn)
if err != nil {
t.Fatal(err)
}
defer stmt.Close()
if stmt.Step() {
return stmt.ColumnText(0)
}
t.Fatal(stmt.Err())
return ""
}
Register(db)
tests := []struct {
test string
want string
}{
{`upper('hello')`, "HELLO"},
{`lower('HELLO')`, "hello"},
{`upper('привет')`, "ПРИВЕТ"},
{`lower('ПРИВЕТ')`, "привет"},
{`upper('istanbul')`, "ISTANBUL"},
{`upper('istanbul', 'tr-TR')`, "İSTANBUL"},
{`lower('Dünyanın İlk Borsası', 'tr-TR')`, "dünyanın ilk borsası"},
{`upper('Dünyanın İlk Borsası', 'tr-TR')`, "DÜNYANIN İLK BORSASI"},
{`'Hello' REGEXP 'ell'`, "1"},
{`'Hello' REGEXP 'el.'`, "1"},
{`'Hello' LIKE 'hel_'`, "0"},
{`'Hello' LIKE 'hel%'`, "1"},
{`'Hello' LIKE 'h_llo'`, "1"},
{`'Hello' LIKE 'hello'`, "1"},
{`'Привет' LIKE 'ПРИВЕТ'`, "1"},
{`'100%' LIKE '100|%' ESCAPE '|'`, "1"},
}
for _, tt := range tests {
t.Run(tt.test, func(t *testing.T) {
if got := exec(tt.test); got != tt.want {
t.Errorf("exec(%q) = %q, want %q", tt.test, got, tt.want)
}
})
}
err = db.Close()
if err != nil {
t.Fatal(err)
}
}
func TestRegister_collation(t *testing.T) {
t.Parallel()
db, err := sqlite3.Open(":memory:")
if err != nil {
t.Fatal(err)
}
defer db.Close()
Register(db)
err = db.Exec(`CREATE TABLE IF NOT EXISTS words (word VARCHAR(10))`)
if err != nil {
t.Fatal(err)
}
err = db.Exec(`INSERT INTO words (word) VALUES ('côte'), ('cote'), ('coter'), ('coté'), ('cotée'), ('côté')`)
if err != nil {
t.Fatal(err)
}
err = db.Exec(`SELECT icu_load_collation('fr_FR', 'french')`)
if err != nil {
t.Fatal(err)
}
stmt, _, err := db.Prepare(`SELECT word FROM words ORDER BY word COLLATE french`)
if err != nil {
t.Fatal(err)
}
defer stmt.Close()
got, want := []string{}, []string{"cote", "coté", "côte", "côté", "cotée", "coter"}
for stmt.Step() {
got = append(got, stmt.ColumnText(0))
}
if err := stmt.Err(); err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(got, want) {
t.Error("not equal")
}
err = stmt.Close()
if err != nil {
t.Fatal(err)
}
err = db.Close()
if err != nil {
t.Fatal(err)
}
}
func TestRegister_error(t *testing.T) {
t.Parallel()
db, err := sqlite3.Open(":memory:")
if err != nil {
t.Fatal(err)
}
defer db.Close()
Register(db)
err = db.Exec(`SELECT upper('hello', 'enUS')`)
if err == nil {
t.Error("want error")
}
if !errors.Is(err, sqlite3.ERROR) {
t.Errorf("got %v, want sqlite3.ERROR", err)
}
err = db.Exec(`SELECT lower('hello', 'enUS')`)
if err == nil {
t.Error("want error")
}
if !errors.Is(err, sqlite3.ERROR) {
t.Errorf("got %v, want sqlite3.ERROR", err)
}
err = db.Exec(`SELECT 'hello' REGEXP '\'`)
if err == nil {
t.Error("want error")
}
if !errors.Is(err, sqlite3.ERROR) {
t.Errorf("got %v, want sqlite3.ERROR", err)
}
err = db.Exec(`SELECT 'hello' LIKE 'HELLO' ESCAPE '\\'`)
if err == nil {
t.Error("want error")
}
if !errors.Is(err, sqlite3.ERROR) {
t.Errorf("got %v, want sqlite3.ERROR", err)
}
err = db.Exec(`SELECT icu_load_collation('enUS', 'error')`)
if err == nil {
t.Error("want error")
}
if !errors.Is(err, sqlite3.ERROR) {
t.Errorf("got %v, want sqlite3.ERROR", err)
}
err = db.Exec(`SELECT icu_load_collation('enUS', '')`)
if err != nil {
t.Error(err)
}
err = db.Close()
if err != nil {
t.Fatal(err)
}
}
func Test_like2regex(t *testing.T) {
t.Parallel()
const prefix = `(?is)\A`
const sufix = `\z`
tests := []struct {
pattern string
escape rune
want string
}{
{`a`, -1, `a`},
{`a.`, -1, `a\.`},
{`a%`, -1, `a.*`},
{`a\`, -1, `a\\`},
{`a_b`, -1, `a.b`},
{`a|b`, '|', `ab`},
{`a|_`, '|', `a_`},
}
for _, tt := range tests {
t.Run(tt.pattern, func(t *testing.T) {
want := prefix + tt.want + sufix
if got := like2regex(tt.pattern, tt.escape); got != want {
t.Errorf("like2regex() = %q, want %q", got, want)
}
})
}
}

58
ext/zorder/zorder.go Normal file
View File

@@ -0,0 +1,58 @@
// Package zorder provides functions for z-order transformations.
//
// https://sqlite.org/src/doc/tip/ext/misc/zorder.c
package zorder
import (
"github.com/ncruces/go-sqlite3"
"github.com/ncruces/go-sqlite3/internal/util"
)
// Register registers the zorder and unzorder SQL functions.
func Register(db *sqlite3.Conn) {
flags := sqlite3.DETERMINISTIC | sqlite3.INNOCUOUS
db.CreateFunction("zorder", -1, flags, zorder)
db.CreateFunction("unzorder", 3, flags, unzorder)
}
func zorder(ctx sqlite3.Context, arg ...sqlite3.Value) {
var x [63]int64
for i := range arg {
x[i] = arg[i].Int64()
}
if len(arg) > len(x) {
ctx.ResultError(util.ErrorString("zorder: too many parameters"))
return
}
var z int64
if len(arg) > 0 {
for i := 0; i < 63; i++ {
j := i % len(arg)
z |= (x[j] & 1) << i
x[j] >>= 1
}
}
for i := range arg {
if x[i] != 0 {
ctx.ResultError(util.ErrorString("zorder: parameter too large"))
return
}
}
ctx.ResultInt64(z)
}
func unzorder(ctx sqlite3.Context, arg ...sqlite3.Value) {
z := arg[0].Int64()
n := arg[1].Int64()
i := arg[2].Int64()
var k int
var x int64
for j := i; j < 63; j += n {
x |= ((z >> j) & 1) << k
k++
}
ctx.ResultInt64(x)
}

106
ext/zorder/zorder_test.go Normal file
View File

@@ -0,0 +1,106 @@
package zorder_test
import (
"testing"
"github.com/ncruces/go-sqlite3"
"github.com/ncruces/go-sqlite3/driver"
_ "github.com/ncruces/go-sqlite3/embed"
"github.com/ncruces/go-sqlite3/ext/zorder"
)
func TestRegister_zorder(t *testing.T) {
t.Parallel()
db, err := driver.Open(":memory:", func(c *sqlite3.Conn) error {
zorder.Register(c)
return nil
})
if err != nil {
t.Fatal(err)
}
defer db.Close()
var got int64
err = db.QueryRow(`SELECT zorder(2, 3)`).Scan(&got)
if err != nil {
t.Fatal(err)
}
if got != 14 {
t.Errorf("got %d, want 14", got)
}
err = db.QueryRow(`SELECT zorder(4, 5)`).Scan(&got)
if err != nil {
t.Fatal(err)
}
if got != 50 {
t.Errorf("got %d, want 14", got)
}
var check bool
err = db.QueryRow(`SELECT zorder(3, 4) BETWEEN zorder(2, 3) AND zorder(4, 5)`).Scan(&check)
if err != nil {
t.Fatal(err)
}
if !check {
t.Error("want true")
}
err = db.QueryRow(`SELECT zorder(2, 2) NOT BETWEEN zorder(2, 3) AND zorder(4, 5)`).Scan(&check)
if err != nil {
t.Fatal(err)
}
if !check {
t.Error("want true")
}
}
func TestRegister_unzorder(t *testing.T) {
t.Parallel()
db, err := driver.Open(":memory:", func(c *sqlite3.Conn) error {
zorder.Register(c)
return nil
})
if err != nil {
t.Fatal(err)
}
defer db.Close()
var got int64
err = db.QueryRow(`SELECT unzorder(zorder(3, 4), 2, 0)`).Scan(&got)
if err != nil {
t.Fatal(err)
}
if got != 3 {
t.Errorf("got %d, want 3", got)
}
err = db.QueryRow(`SELECT unzorder(zorder(3, 4), 2, 1)`).Scan(&got)
if err != nil {
t.Fatal(err)
}
if got != 4 {
t.Errorf("got %d, want 4", got)
}
}
func TestRegister_error(t *testing.T) {
t.Parallel()
db, err := driver.Open(":memory:", func(c *sqlite3.Conn) error {
zorder.Register(c)
return nil
})
if err != nil {
t.Fatal(err)
}
defer db.Close()
var got int64
err = db.QueryRow(`SELECT zorder(1, 2, 3, 100000)`).Scan(&got)
if err == nil {
t.Error("want error")
}
}

214
func.go Normal file
View File

@@ -0,0 +1,214 @@
package sqlite3
import (
"context"
"sync"
"github.com/ncruces/go-sqlite3/internal/util"
"github.com/tetratelabs/wazero/api"
)
// CollationNeeded registers a callback to be invoked
// whenever an unknown collation sequence is required.
//
// https://sqlite.org/c3ref/collation_needed.html
func (c *Conn) CollationNeeded(cb func(db *Conn, name string)) error {
var enable uint64
if cb != nil {
enable = 1
}
r := c.call("sqlite3_collation_needed_go", uint64(c.handle), enable)
if err := c.error(r); err != nil {
return err
}
c.collation = cb
return nil
}
// AnyCollationNeeded uses [Conn.CollationNeeded] to register
// a fake collating function for any unknown collating sequence.
// The fake collating function works like BINARY.
//
// This can be used to load schemas that contain
// one or more unknown collating sequences.
func (c *Conn) AnyCollationNeeded() {
c.call("sqlite3_anycollseq_init", uint64(c.handle), 0, 0)
}
// CreateCollation defines a new collating sequence.
//
// https://sqlite.org/c3ref/create_collation.html
func (c *Conn) CreateCollation(name string, fn func(a, b []byte) int) error {
defer c.arena.mark()()
namePtr := c.arena.string(name)
funcPtr := util.AddHandle(c.ctx, fn)
r := c.call("sqlite3_create_collation_go",
uint64(c.handle), uint64(namePtr), uint64(funcPtr))
return c.error(r)
}
// CreateFunction defines a new scalar SQL function.
//
// https://sqlite.org/c3ref/create_function.html
func (c *Conn) CreateFunction(name string, nArg int, flag FunctionFlag, fn ScalarFunction) error {
defer c.arena.mark()()
namePtr := c.arena.string(name)
funcPtr := util.AddHandle(c.ctx, fn)
r := c.call("sqlite3_create_function_go",
uint64(c.handle), uint64(namePtr), uint64(nArg),
uint64(flag), uint64(funcPtr))
return c.error(r)
}
// ScalarFunction is the type of a scalar SQL function.
// Implementations must not retain arg.
type ScalarFunction func(ctx Context, arg ...Value)
// CreateWindowFunction defines a new aggregate or aggregate window SQL function.
// If fn returns a [WindowFunction], then an aggregate window function is created.
// If fn returns an [io.Closer], it will be called to free resources.
//
// https://sqlite.org/c3ref/create_function.html
func (c *Conn) CreateWindowFunction(name string, nArg int, flag FunctionFlag, fn func() AggregateFunction) error {
defer c.arena.mark()()
call := "sqlite3_create_aggregate_function_go"
namePtr := c.arena.string(name)
funcPtr := util.AddHandle(c.ctx, fn)
if _, ok := fn().(WindowFunction); ok {
call = "sqlite3_create_window_function_go"
}
r := c.call(call,
uint64(c.handle), uint64(namePtr), uint64(nArg),
uint64(flag), uint64(funcPtr))
return c.error(r)
}
// AggregateFunction is the interface an aggregate function should implement.
//
// https://sqlite.org/appfunc.html
type AggregateFunction interface {
// Step is invoked to add a row to the current window.
// The function arguments, if any, corresponding to the row being added, are passed to Step.
// Implementations must not retain arg.
Step(ctx Context, arg ...Value)
// Value is invoked to return the current (or final) value of the aggregate.
Value(ctx Context)
}
// WindowFunction is the interface an aggregate window function should implement.
//
// https://sqlite.org/windowfunctions.html
type WindowFunction interface {
AggregateFunction
// Inverse is invoked to remove the oldest presently aggregated result of Step from the current window.
// The function arguments, if any, are those passed to Step for the row being removed.
// Implementations must not retain arg.
Inverse(ctx Context, arg ...Value)
}
// OverloadFunction overloads a function for a virtual table.
//
// https://sqlite.org/c3ref/overload_function.html
func (c *Conn) OverloadFunction(name string, nArg int) error {
defer c.arena.mark()()
namePtr := c.arena.string(name)
r := c.call("sqlite3_overload_function",
uint64(c.handle), uint64(namePtr), uint64(nArg))
return c.error(r)
}
func destroyCallback(ctx context.Context, mod api.Module, pApp uint32) {
util.DelHandle(ctx, pApp)
}
func collationCallback(ctx context.Context, mod api.Module, pArg, pDB, eTextRep, zName uint32) {
if c, ok := ctx.Value(connKey{}).(*Conn); ok && c.handle == pDB && c.collation != nil {
name := util.ReadString(mod, zName, _MAX_NAME)
c.collation(c, name)
}
}
func compareCallback(ctx context.Context, mod api.Module, pApp, nKey1, pKey1, nKey2, pKey2 uint32) uint32 {
fn := util.GetHandle(ctx, pApp).(func(a, b []byte) int)
return uint32(fn(util.View(mod, pKey1, uint64(nKey1)), util.View(mod, pKey2, uint64(nKey2))))
}
func funcCallback(ctx context.Context, mod api.Module, pCtx, pApp, nArg, pArg uint32) {
args := getFuncArgs()
defer putFuncArgs(args)
db := ctx.Value(connKey{}).(*Conn)
fn := util.GetHandle(db.ctx, pApp).(ScalarFunction)
callbackArgs(db, args[:nArg], pArg)
fn(Context{db, pCtx}, args[:nArg]...)
}
func stepCallback(ctx context.Context, mod api.Module, pCtx, pAgg, pApp, nArg, pArg uint32) {
args := getFuncArgs()
defer putFuncArgs(args)
db := ctx.Value(connKey{}).(*Conn)
callbackArgs(db, args[:nArg], pArg)
fn, _ := callbackAggregate(db, pAgg, pApp)
fn.Step(Context{db, pCtx}, args[:nArg]...)
}
func finalCallback(ctx context.Context, mod api.Module, pCtx, pAgg, pApp uint32) {
db := ctx.Value(connKey{}).(*Conn)
fn, handle := callbackAggregate(db, pAgg, pApp)
fn.Value(Context{db, pCtx})
util.DelHandle(ctx, handle)
}
func valueCallback(ctx context.Context, mod api.Module, pCtx, pAgg uint32) {
db := ctx.Value(connKey{}).(*Conn)
fn := util.GetHandle(db.ctx, pAgg).(AggregateFunction)
fn.Value(Context{db, pCtx})
}
func inverseCallback(ctx context.Context, mod api.Module, pCtx, pAgg, nArg, pArg uint32) {
args := getFuncArgs()
defer putFuncArgs(args)
db := ctx.Value(connKey{}).(*Conn)
callbackArgs(db, args[:nArg], pArg)
fn := util.GetHandle(db.ctx, pAgg).(WindowFunction)
fn.Inverse(Context{db, pCtx}, args[:nArg]...)
}
func callbackAggregate(db *Conn, pAgg, pApp uint32) (AggregateFunction, uint32) {
if pApp == 0 {
handle := util.ReadUint32(db.mod, pAgg)
return util.GetHandle(db.ctx, handle).(AggregateFunction), handle
}
// We need to create the aggregate.
fn := util.GetHandle(db.ctx, pApp).(func() AggregateFunction)()
handle := util.AddHandle(db.ctx, fn)
if pAgg != 0 {
util.WriteUint32(db.mod, pAgg, handle)
}
return fn, handle
}
func callbackArgs(db *Conn, arg []Value, pArg uint32) {
for i := range arg {
arg[i] = Value{
c: db,
handle: util.ReadUint32(db.mod, pArg+ptrlen*uint32(i)),
}
}
}
var funcArgsPool sync.Pool
func putFuncArgs(p *[_MAX_FUNCTION_ARG]Value) {
funcArgsPool.Put(p)
}
func getFuncArgs() *[_MAX_FUNCTION_ARG]Value {
if p := funcArgsPool.Get(); p == nil {
return new([_MAX_FUNCTION_ARG]Value)
} else {
return p.(*[_MAX_FUNCTION_ARG]Value)
}
}

157
func_test.go Normal file
View File

@@ -0,0 +1,157 @@
package sqlite3_test
import (
"bytes"
"fmt"
"log"
"regexp"
"github.com/ncruces/go-sqlite3"
_ "github.com/ncruces/go-sqlite3/embed"
"github.com/ncruces/go-sqlite3/ext/unicode"
)
func ExampleConn_CreateCollation() {
db, err := sqlite3.Open(":memory:")
if err != nil {
log.Fatal(err)
}
defer db.Close()
err = db.Exec(`CREATE TABLE IF NOT EXISTS words (word VARCHAR(10))`)
if err != nil {
log.Fatal(err)
}
err = db.Exec(`INSERT INTO words (word) VALUES ('côte'), ('cote'), ('coter'), ('coté'), ('cotée'), ('côté')`)
if err != nil {
log.Fatal(err)
}
err = db.CollationNeeded(func(db *sqlite3.Conn, name string) {
err := unicode.RegisterCollation(db, name, name)
if err != nil {
log.Fatal(err)
}
})
if err != nil {
log.Fatal(err)
}
stmt, _, err := db.Prepare(`SELECT word FROM words ORDER BY word COLLATE fr_FR`)
if err != nil {
log.Fatal(err)
}
defer stmt.Close()
for stmt.Step() {
fmt.Println(stmt.ColumnText(0))
}
if err := stmt.Err(); err != nil {
log.Fatal(err)
}
// Output:
// cote
// coté
// côte
// côté
// cotée
// coter
}
func ExampleConn_CreateFunction() {
db, err := sqlite3.Open(":memory:")
if err != nil {
log.Fatal(err)
}
defer db.Close()
err = db.Exec(`CREATE TABLE IF NOT EXISTS words (word VARCHAR(10))`)
if err != nil {
log.Fatal(err)
}
err = db.Exec(`INSERT INTO words (word) VALUES ('côte'), ('cote'), ('coter'), ('coté'), ('cotée'), ('côté')`)
if err != nil {
log.Fatal(err)
}
err = db.CreateFunction("upper", 1, sqlite3.DETERMINISTIC|sqlite3.INNOCUOUS, func(ctx sqlite3.Context, arg ...sqlite3.Value) {
ctx.ResultRawText(bytes.ToUpper(arg[0].RawText()))
})
if err != nil {
log.Fatal(err)
}
stmt, _, err := db.Prepare(`SELECT upper(word) FROM words`)
if err != nil {
log.Fatal(err)
}
defer stmt.Close()
for stmt.Step() {
fmt.Println(stmt.ColumnText(0))
}
if err := stmt.Err(); err != nil {
log.Fatal(err)
}
// Unordered output:
// COTE
// COTÉ
// CÔTE
// CÔTÉ
// COTÉE
// COTER
}
func ExampleContext_SetAuxData() {
db, err := sqlite3.Open(":memory:")
if err != nil {
log.Fatal(err)
}
defer db.Close()
err = db.Exec(`CREATE TABLE IF NOT EXISTS words (word VARCHAR(10))`)
if err != nil {
log.Fatal(err)
}
err = db.Exec(`INSERT INTO words (word) VALUES ('côte'), ('cote'), ('coter'), ('coté'), ('cotée'), ('côté')`)
if err != nil {
log.Fatal(err)
}
err = db.CreateFunction("regexp", 2, sqlite3.DETERMINISTIC|sqlite3.INNOCUOUS, func(ctx sqlite3.Context, arg ...sqlite3.Value) {
re, ok := ctx.GetAuxData(0).(*regexp.Regexp)
if !ok {
r, err := regexp.Compile(arg[0].Text())
if err != nil {
ctx.ResultError(err)
return
}
ctx.SetAuxData(0, r)
re = r
}
ctx.ResultBool(re.Match(arg[1].RawText()))
})
if err != nil {
log.Fatal(err)
}
stmt, _, err := db.Prepare(`SELECT word FROM words WHERE word REGEXP '^\p{L}+e$'`)
if err != nil {
log.Fatal(err)
}
defer stmt.Close()
for stmt.Step() {
fmt.Println(stmt.ColumnText(0))
}
if err := stmt.Err(); err != nil {
log.Fatal(err)
}
// Unordered output:
// cote
// côte
// cotée
}

87
func_win_test.go Normal file
View File

@@ -0,0 +1,87 @@
package sqlite3_test
import (
"fmt"
"log"
"unicode"
"github.com/ncruces/go-sqlite3"
_ "github.com/ncruces/go-sqlite3/embed"
)
func ExampleConn_CreateWindowFunction() {
db, err := sqlite3.Open(":memory:")
if err != nil {
log.Fatal(err)
}
defer db.Close()
err = db.Exec(`CREATE TABLE IF NOT EXISTS words (word VARCHAR(10))`)
if err != nil {
log.Fatal(err)
}
err = db.Exec(`INSERT INTO words (word) VALUES ('côte'), ('cote'), ('coter'), ('coté'), ('cotée'), ('côté')`)
if err != nil {
log.Fatal(err)
}
err = db.CreateWindowFunction("count_ascii", 1, sqlite3.DETERMINISTIC|sqlite3.INNOCUOUS, newASCIICounter)
if err != nil {
log.Fatal(err)
}
stmt, _, err := db.Prepare(`SELECT count_ascii(word) OVER (ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) FROM words`)
if err != nil {
log.Fatal(err)
}
defer stmt.Close()
for stmt.Step() {
fmt.Println(stmt.ColumnInt(0))
}
if err := stmt.Err(); err != nil {
log.Fatal(err)
}
// Output:
// 1
// 2
// 2
// 1
// 0
// 0
}
type countASCII struct{ result int }
func newASCIICounter() sqlite3.AggregateFunction {
return &countASCII{}
}
func (f *countASCII) Value(ctx sqlite3.Context) {
ctx.ResultInt(f.result)
}
func (f *countASCII) Step(ctx sqlite3.Context, arg ...sqlite3.Value) {
if f.isASCII(arg[0]) {
f.result++
}
}
func (f *countASCII) Inverse(ctx sqlite3.Context, arg ...sqlite3.Value) {
if f.isASCII(arg[0]) {
f.result--
}
}
func (f *countASCII) isASCII(arg sqlite3.Value) bool {
if arg.Type() != sqlite3.TEXT {
return false
}
for _, c := range arg.RawBlob() {
if c > unicode.MaxASCII {
return false
}
}
return true
}

13
go.mod
View File

@@ -1,12 +1,15 @@
module github.com/ncruces/go-sqlite3
go 1.19
go 1.21
require (
github.com/ncruces/julianday v0.1.5
github.com/tetratelabs/wazero v1.0.0-rc.1
golang.org/x/sync v0.1.0
golang.org/x/sys v0.6.0
github.com/ncruces/julianday v1.0.0
github.com/psanford/httpreadat v0.1.0
github.com/tetratelabs/wazero v1.7.0
golang.org/x/crypto v0.21.0
golang.org/x/sync v0.6.0
golang.org/x/sys v0.18.0
golang.org/x/text v0.14.0
)
retract v0.4.0 // tagged from the wrong branch

22
go.sum
View File

@@ -1,8 +1,14 @@
github.com/ncruces/julianday v0.1.5 h1:hDJ9ejiMp3DHsoZ5KW4c1lwfMjbARS7u/gbYcd0FBZk=
github.com/ncruces/julianday v0.1.5/go.mod h1:Dusn2KvZrrovOMJuOt0TNXL6tB7U2E8kvza5fFc9G7g=
github.com/tetratelabs/wazero v1.0.0-rc.1 h1:ytecMV5Ue0BwezjKh/cM5yv1Mo49ep2R2snSsQUyToc=
github.com/tetratelabs/wazero v1.0.0-rc.1/go.mod h1:wYx2gNRg8/WihJfSDxA1TIL8H+GkfLYm+bIfbblu9VQ=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
github.com/ncruces/julianday v1.0.0 h1:fH0OKwa7NWvniGQtxdJRxAgkBMolni2BjDHaWTxqt7M=
github.com/ncruces/julianday v1.0.0/go.mod h1:Dusn2KvZrrovOMJuOt0TNXL6tB7U2E8kvza5fFc9G7g=
github.com/psanford/httpreadat v0.1.0 h1:VleW1HS2zO7/4c7c7zNl33fO6oYACSagjJIyMIwZLUE=
github.com/psanford/httpreadat v0.1.0/go.mod h1:Zg7P+TlBm3bYbyHTKv/EdtSJZn3qwbPwpfZ/I9GKCRE=
github.com/tetratelabs/wazero v1.7.0 h1:jg5qPydno59wqjpGrHph81lbtHzTrWzwwtD4cD88+hQ=
github.com/tetratelabs/wazero v1.7.0/go.mod h1:ytl6Zuh20R/eROuyDaGPkp82O9C/DJfXAwJfQ3X6/7Y=
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=

6
go.work Normal file
View File

@@ -0,0 +1,6 @@
go 1.21
use (
.
./gormlite
)

4
go.work.sum Normal file
View File

@@ -0,0 +1,4 @@
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=

22
gormlite/LICENSE Normal file
View File

@@ -0,0 +1,22 @@
MIT License
Copyright (c) 2023 Nuno Cruces
Copyright (c) 2023 Jinzhu <wosmvp@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

17
gormlite/README.md Normal file
View File

@@ -0,0 +1,17 @@
# GORM SQLite Driver
[![Go Reference](https://pkg.go.dev/badge/image)](https://pkg.go.dev/github.com/ncruces/go-sqlite3/gormlite)
## Usage
```go
import (
_ "github.com/ncruces/go-sqlite3/embed"
"github.com/ncruces/go-sqlite3/gormlite"
"gorm.io/gorm"
)
db, err := gorm.Open(gormlite.Open("gorm.db"), &gorm.Config{})
```
Checkout [https://gorm.io](https://gorm.io) for details.

290
gormlite/ddlmod.go Normal file
View File

@@ -0,0 +1,290 @@
package gormlite
import (
"database/sql"
"errors"
"fmt"
"regexp"
"strconv"
"strings"
"gorm.io/gorm/migrator"
)
var (
sqliteSeparator = "`|\"|'|\t"
uniqueRegexp = regexp.MustCompile(fmt.Sprintf(`^CONSTRAINT [%v]?[\w-]+[%v]? UNIQUE (.*)$`, sqliteSeparator, sqliteSeparator))
indexRegexp = regexp.MustCompile(fmt.Sprintf(`(?is)CREATE(?: UNIQUE)? INDEX [%v]?[\w\d-]+[%v]?(?s:.*?)ON (.*)$`, sqliteSeparator, sqliteSeparator))
tableRegexp = regexp.MustCompile(fmt.Sprintf(`(?is)(CREATE TABLE [%v]?[\w\d-]+[%v]?)(?:\s*\((.*)\))?`, sqliteSeparator, sqliteSeparator))
separatorRegexp = regexp.MustCompile(fmt.Sprintf("[%v]", sqliteSeparator))
columnsRegexp = regexp.MustCompile(fmt.Sprintf(`[(,][%v]?(\w+)[%v]?`, sqliteSeparator, sqliteSeparator))
columnRegexp = regexp.MustCompile(fmt.Sprintf(`^[%v]?([\w\d]+)[%v]?\s+([\w\(\)\d]+)(.*)$`, sqliteSeparator, sqliteSeparator))
defaultValueRegexp = regexp.MustCompile(`(?i) DEFAULT \(?(.+)?\)?( |COLLATE|GENERATED|$)`)
regRealDataType = regexp.MustCompile(`[^\d](\d+)[^\d]?`)
)
func getAllColumns(s string) []string {
allMatches := columnsRegexp.FindAllStringSubmatch(s, -1)
columns := make([]string, 0, len(allMatches))
for _, matches := range allMatches {
if len(matches) > 1 {
columns = append(columns, matches[1])
}
}
return columns
}
type ddl struct {
head string
fields []string
columns []migrator.ColumnType
}
func parseDDL(strs ...string) (*ddl, error) {
var result ddl
for _, str := range strs {
if sections := tableRegexp.FindStringSubmatch(str); len(sections) > 0 {
var (
ddlBody = sections[2]
ddlBodyRunes = []rune(ddlBody)
bracketLevel int
quote rune
buf string
)
ddlBodyRunesLen := len(ddlBodyRunes)
result.head = sections[1]
for idx := 0; idx < ddlBodyRunesLen; idx++ {
var (
next rune = 0
c = ddlBodyRunes[idx]
)
if idx+1 < ddlBodyRunesLen {
next = ddlBodyRunes[idx+1]
}
if sc := string(c); separatorRegexp.MatchString(sc) {
if c == next {
buf += sc // Skip escaped quote
idx++
} else if quote > 0 {
quote = 0
} else {
quote = c
}
} else if quote == 0 {
if c == '(' {
bracketLevel++
} else if c == ')' {
bracketLevel--
} else if bracketLevel == 0 {
if c == ',' {
result.fields = append(result.fields, strings.TrimSpace(buf))
buf = ""
continue
}
}
}
if bracketLevel < 0 {
return nil, errors.New("invalid DDL, unbalanced brackets")
}
buf += string(c)
}
if bracketLevel != 0 {
return nil, errors.New("invalid DDL, unbalanced brackets")
}
if buf != "" {
result.fields = append(result.fields, strings.TrimSpace(buf))
}
for _, f := range result.fields {
fUpper := strings.ToUpper(f)
if strings.HasPrefix(fUpper, "CHECK") {
continue
}
if strings.HasPrefix(fUpper, "CONSTRAINT") {
matches := uniqueRegexp.FindStringSubmatch(f)
if len(matches) > 0 {
if columns := getAllColumns(matches[1]); len(columns) == 1 {
for idx, column := range result.columns {
if column.NameValue.String == columns[0] {
column.UniqueValue = sql.NullBool{Bool: true, Valid: true}
result.columns[idx] = column
break
}
}
}
}
continue
}
if strings.HasPrefix(fUpper, "PRIMARY KEY") {
for _, name := range getAllColumns(f) {
for idx, column := range result.columns {
if column.NameValue.String == name {
column.PrimaryKeyValue = sql.NullBool{Bool: true, Valid: true}
result.columns[idx] = column
break
}
}
}
} else if matches := columnRegexp.FindStringSubmatch(f); len(matches) > 0 {
columnType := migrator.ColumnType{
NameValue: sql.NullString{String: matches[1], Valid: true},
DataTypeValue: sql.NullString{String: matches[2], Valid: true},
ColumnTypeValue: sql.NullString{String: matches[2], Valid: true},
PrimaryKeyValue: sql.NullBool{Valid: true},
UniqueValue: sql.NullBool{Valid: true},
NullableValue: sql.NullBool{Bool: true, Valid: true},
DefaultValueValue: sql.NullString{Valid: false},
}
matchUpper := strings.ToUpper(matches[3])
if strings.Contains(matchUpper, " NOT NULL") {
columnType.NullableValue = sql.NullBool{Bool: false, Valid: true}
} else if strings.Contains(matchUpper, " NULL") {
columnType.NullableValue = sql.NullBool{Bool: true, Valid: true}
}
if strings.Contains(matchUpper, " UNIQUE") {
columnType.UniqueValue = sql.NullBool{Bool: true, Valid: true}
}
if strings.Contains(matchUpper, " PRIMARY") {
columnType.PrimaryKeyValue = sql.NullBool{Bool: true, Valid: true}
}
if defaultMatches := defaultValueRegexp.FindStringSubmatch(matches[3]); len(defaultMatches) > 1 {
if strings.ToLower(defaultMatches[1]) != "null" {
columnType.DefaultValueValue = sql.NullString{String: strings.Trim(defaultMatches[1], `"`), Valid: true}
}
}
// data type length
matches := regRealDataType.FindAllStringSubmatch(columnType.DataTypeValue.String, -1)
if len(matches) == 1 && len(matches[0]) == 2 {
size, _ := strconv.Atoi(matches[0][1])
columnType.LengthValue = sql.NullInt64{Valid: true, Int64: int64(size)}
columnType.DataTypeValue.String = strings.TrimSuffix(columnType.DataTypeValue.String, matches[0][0])
}
result.columns = append(result.columns, columnType)
}
}
} else if matches := indexRegexp.FindStringSubmatch(str); len(matches) > 0 {
// don't report Unique by UniqueIndex
} else {
return nil, errors.New("invalid DDL")
}
}
return &result, nil
}
func (d *ddl) clone() *ddl {
copied := new(ddl)
*copied = *d
copied.fields = make([]string, len(d.fields))
copy(copied.fields, d.fields)
copied.columns = make([]migrator.ColumnType, len(d.columns))
copy(copied.columns, d.columns)
return copied
}
func (d *ddl) compile() string {
if len(d.fields) == 0 {
return d.head
}
return fmt.Sprintf("%s (%s)", d.head, strings.Join(d.fields, ","))
}
func (d *ddl) renameTable(dst, src string) error {
tableReg, err := regexp.Compile("\\s*('|`|\")?\\b" + regexp.QuoteMeta(src) + "\\b('|`|\")?\\s*")
if err != nil {
return err
}
replaced := tableReg.ReplaceAllString(d.head, fmt.Sprintf(" `%s` ", dst))
if replaced == d.head {
return fmt.Errorf("failed to look up tablename `%s` from DDL head '%s'", src, d.head)
}
d.head = replaced
return nil
}
func (d *ddl) addConstraint(name string, sql string) {
reg := regexp.MustCompile("^CONSTRAINT [\"`]?" + regexp.QuoteMeta(name) + "[\"` ]")
for i := 0; i < len(d.fields); i++ {
if reg.MatchString(d.fields[i]) {
d.fields[i] = sql
return
}
}
d.fields = append(d.fields, sql)
}
func (d *ddl) removeConstraint(name string) bool {
reg := regexp.MustCompile("^CONSTRAINT [\"`]?" + regexp.QuoteMeta(name) + "[\"` ]")
for i := 0; i < len(d.fields); i++ {
if reg.MatchString(d.fields[i]) {
d.fields = append(d.fields[:i], d.fields[i+1:]...)
return true
}
}
return false
}
//lint:ignore U1000 ignore unused code.
func (d *ddl) hasConstraint(name string) bool {
reg := regexp.MustCompile("^CONSTRAINT [\"`]?" + regexp.QuoteMeta(name) + "[\"` ]")
for _, f := range d.fields {
if reg.MatchString(f) {
return true
}
}
return false
}
func (d *ddl) getColumns() []string {
res := []string{}
for _, f := range d.fields {
fUpper := strings.ToUpper(f)
if strings.HasPrefix(fUpper, "PRIMARY KEY") ||
strings.HasPrefix(fUpper, "CHECK") ||
strings.HasPrefix(fUpper, "CONSTRAINT") ||
strings.Contains(fUpper, "GENERATED ALWAYS AS") {
continue
}
reg := regexp.MustCompile("^[\"`']?([\\w\\d]+)[\"`']?")
match := reg.FindStringSubmatch(f)
if match != nil {
res = append(res, "`"+match[1]+"`")
}
}
return res
}
func (d *ddl) removeColumn(name string) bool {
reg := regexp.MustCompile("^(`|'|\"| )" + regexp.QuoteMeta(name) + "(`|'|\"| ) .*?$")
for i := 0; i < len(d.fields); i++ {
if reg.MatchString(d.fields[i]) {
d.fields = append(d.fields[:i], d.fields[i+1:]...)
return true
}
}
return false
}

397
gormlite/ddlmod_test.go Normal file
View File

@@ -0,0 +1,397 @@
package gormlite
import (
"database/sql"
"testing"
"gorm.io/gorm/migrator"
"gorm.io/gorm/utils/tests"
)
func TestParseDDL(t *testing.T) {
params := []struct {
name string
sql []string
nFields int
columns []migrator.ColumnType
}{
{"with_fk", []string{
"CREATE TABLE `notes` (" +
"`id` integer NOT NULL,`text` varchar(500) DEFAULT \"hello\",`age` integer DEFAULT 18,`user_id` integer,PRIMARY KEY (`id`),CONSTRAINT `fk_users_notes` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`))",
"CREATE UNIQUE INDEX `idx_profiles_refer` ON `profiles`(`text`)",
}, 6, []migrator.ColumnType{
{NameValue: sql.NullString{String: "id", Valid: true}, DataTypeValue: sql.NullString{String: "integer", Valid: true}, ColumnTypeValue: sql.NullString{String: "integer", Valid: true}, PrimaryKeyValue: sql.NullBool{Bool: true, Valid: true}, NullableValue: sql.NullBool{Valid: true}, UniqueValue: sql.NullBool{Valid: true}, DefaultValueValue: sql.NullString{Valid: false}},
{NameValue: sql.NullString{String: "text", Valid: true}, DataTypeValue: sql.NullString{String: "varchar", Valid: true}, LengthValue: sql.NullInt64{Int64: 500, Valid: true}, ColumnTypeValue: sql.NullString{String: "varchar(500)", Valid: true}, DefaultValueValue: sql.NullString{String: "hello", Valid: true}, NullableValue: sql.NullBool{Bool: true, Valid: true}, UniqueValue: sql.NullBool{Bool: false, Valid: true}, PrimaryKeyValue: sql.NullBool{Valid: true}},
{NameValue: sql.NullString{String: "age", Valid: true}, DataTypeValue: sql.NullString{String: "integer", Valid: true}, ColumnTypeValue: sql.NullString{String: "integer", Valid: true}, DefaultValueValue: sql.NullString{String: "18", Valid: true}, NullableValue: sql.NullBool{Bool: true, Valid: true}, UniqueValue: sql.NullBool{Valid: true}, PrimaryKeyValue: sql.NullBool{Valid: true}},
{NameValue: sql.NullString{String: "user_id", Valid: true}, DataTypeValue: sql.NullString{String: "integer", Valid: true}, ColumnTypeValue: sql.NullString{String: "integer", Valid: true}, DefaultValueValue: sql.NullString{Valid: false}, NullableValue: sql.NullBool{Bool: true, Valid: true}, UniqueValue: sql.NullBool{Valid: true}, PrimaryKeyValue: sql.NullBool{Valid: true}},
},
},
{"with_check", []string{"CREATE TABLE Persons (ID int NOT NULL,LastName varchar(255) NOT NULL,FirstName varchar(255),Age int,CHECK (Age>=18),CHECK (FirstName<>'John'))"}, 6, []migrator.ColumnType{
{NameValue: sql.NullString{String: "ID", Valid: true}, DataTypeValue: sql.NullString{String: "int", Valid: true}, ColumnTypeValue: sql.NullString{String: "int", Valid: true}, NullableValue: sql.NullBool{Valid: true}, DefaultValueValue: sql.NullString{Valid: false}, UniqueValue: sql.NullBool{Valid: true}, PrimaryKeyValue: sql.NullBool{Valid: true}},
{NameValue: sql.NullString{String: "LastName", Valid: true}, DataTypeValue: sql.NullString{String: "varchar", Valid: true}, LengthValue: sql.NullInt64{Int64: 255, Valid: true}, ColumnTypeValue: sql.NullString{String: "varchar(255)", Valid: true}, NullableValue: sql.NullBool{Bool: false, Valid: true}, DefaultValueValue: sql.NullString{Valid: false}, UniqueValue: sql.NullBool{Valid: true}, PrimaryKeyValue: sql.NullBool{Valid: true}},
{NameValue: sql.NullString{String: "FirstName", Valid: true}, DataTypeValue: sql.NullString{String: "varchar", Valid: true}, LengthValue: sql.NullInt64{Int64: 255, Valid: true}, ColumnTypeValue: sql.NullString{String: "varchar(255)", Valid: true}, DefaultValueValue: sql.NullString{Valid: false}, NullableValue: sql.NullBool{Bool: true, Valid: true}, UniqueValue: sql.NullBool{Valid: true}, PrimaryKeyValue: sql.NullBool{Valid: true}},
{NameValue: sql.NullString{String: "Age", Valid: true}, DataTypeValue: sql.NullString{String: "int", Valid: true}, ColumnTypeValue: sql.NullString{String: "int", Valid: true}, DefaultValueValue: sql.NullString{Valid: false}, NullableValue: sql.NullBool{Bool: true, Valid: true}, UniqueValue: sql.NullBool{Valid: true}, PrimaryKeyValue: sql.NullBool{Valid: true}},
}},
{"lowercase", []string{"create table test (ID int NOT NULL)"}, 1, []migrator.ColumnType{
{NameValue: sql.NullString{String: "ID", Valid: true}, DataTypeValue: sql.NullString{String: "int", Valid: true}, ColumnTypeValue: sql.NullString{String: "int", Valid: true}, NullableValue: sql.NullBool{Bool: false, Valid: true}, DefaultValueValue: sql.NullString{Valid: false}, UniqueValue: sql.NullBool{Valid: true}, PrimaryKeyValue: sql.NullBool{Valid: true}},
},
},
{"no brackets", []string{"create table test"}, 0, nil},
{"with_special_characters", []string{
"CREATE TABLE `test` (`text` varchar(10) DEFAULT \"测试, \")",
}, 1, []migrator.ColumnType{
{NameValue: sql.NullString{String: "text", Valid: true}, DataTypeValue: sql.NullString{String: "varchar", Valid: true}, LengthValue: sql.NullInt64{Int64: 10, Valid: true}, ColumnTypeValue: sql.NullString{String: "varchar(10)", Valid: true}, DefaultValueValue: sql.NullString{String: "测试, ", Valid: true}, NullableValue: sql.NullBool{Bool: true, Valid: true}, UniqueValue: sql.NullBool{Valid: true}, PrimaryKeyValue: sql.NullBool{Valid: true}},
},
},
{
"table_name_with_dash",
[]string{
"CREATE TABLE `test-a` (`id` int NOT NULL)",
"CREATE UNIQUE INDEX `idx_test-a_id` ON `test-a`(`id`)",
},
1,
[]migrator.ColumnType{
{
NameValue: sql.NullString{String: "id", Valid: true},
DataTypeValue: sql.NullString{String: "int", Valid: true},
ColumnTypeValue: sql.NullString{String: "int", Valid: true},
NullableValue: sql.NullBool{Bool: false, Valid: true},
DefaultValueValue: sql.NullString{Valid: false},
UniqueValue: sql.NullBool{Bool: false, Valid: true},
PrimaryKeyValue: sql.NullBool{Valid: true},
},
},
}, {
"unique index",
[]string{
"CREATE TABLE `test-b` (`field` integer NOT NULL)",
"CREATE UNIQUE INDEX `idx_uq` ON `test-b`(`field`) WHERE field = 0",
},
1,
[]migrator.ColumnType{{
NameValue: sql.NullString{String: "field", Valid: true},
DataTypeValue: sql.NullString{String: "integer", Valid: true},
ColumnTypeValue: sql.NullString{String: "integer", Valid: true},
PrimaryKeyValue: sql.NullBool{Bool: false, Valid: true},
UniqueValue: sql.NullBool{Bool: false, Valid: true},
NullableValue: sql.NullBool{Bool: false, Valid: true},
}},
}, {
"normal index",
[]string{
"CREATE TABLE `test-c` (`field` integer NOT NULL)",
"CREATE INDEX `idx_uq` ON `test-c`(`field`)",
},
1,
[]migrator.ColumnType{{
NameValue: sql.NullString{String: "field", Valid: true},
DataTypeValue: sql.NullString{String: "integer", Valid: true},
ColumnTypeValue: sql.NullString{String: "integer", Valid: true},
PrimaryKeyValue: sql.NullBool{Bool: false, Valid: true},
UniqueValue: sql.NullBool{Bool: false, Valid: true},
NullableValue: sql.NullBool{Bool: false, Valid: true},
}},
}, {
"unique constraint",
[]string{
"CREATE TABLE `unique_struct` (`name` text,CONSTRAINT `uni_unique_struct_name` UNIQUE (`name`))",
},
2,
[]migrator.ColumnType{{
NameValue: sql.NullString{String: "name", Valid: true},
DataTypeValue: sql.NullString{String: "text", Valid: true},
ColumnTypeValue: sql.NullString{String: "text", Valid: true},
PrimaryKeyValue: sql.NullBool{Bool: false, Valid: true},
UniqueValue: sql.NullBool{Bool: true, Valid: true},
NullableValue: sql.NullBool{Bool: true, Valid: true},
}},
},
{
"non-unique index",
[]string{
"CREATE TABLE `test-c` (`field` integer NOT NULL)",
"CREATE INDEX `idx_uq` ON `test-b`(`field`) WHERE field = 0",
},
1,
[]migrator.ColumnType{
{
NameValue: sql.NullString{String: "field", Valid: true},
DataTypeValue: sql.NullString{String: "integer", Valid: true},
ColumnTypeValue: sql.NullString{String: "integer", Valid: true},
PrimaryKeyValue: sql.NullBool{Bool: false, Valid: true},
UniqueValue: sql.NullBool{Bool: false, Valid: true},
NullableValue: sql.NullBool{Bool: false, Valid: true},
},
},
},
{
"index with \n from .schema sqlite",
[]string{
"CREATE TABLE `test-d` (`field` integer NOT NULL)",
"CREATE INDEX `idx_uq`\n ON `test-b`(`field`) WHERE field = 0",
},
1,
[]migrator.ColumnType{
{
NameValue: sql.NullString{String: "field", Valid: true},
DataTypeValue: sql.NullString{String: "integer", Valid: true},
ColumnTypeValue: sql.NullString{String: "integer", Valid: true},
PrimaryKeyValue: sql.NullBool{Bool: false, Valid: true},
UniqueValue: sql.NullBool{Bool: false, Valid: true},
NullableValue: sql.NullBool{Bool: false, Valid: true},
},
},
},
}
for _, p := range params {
t.Run(p.name, func(t *testing.T) {
ddl, err := parseDDL(p.sql...)
if err != nil {
panic(err.Error())
}
tests.AssertEqual(t, p.sql[0], ddl.compile())
if len(ddl.fields) != p.nFields {
t.Fatalf("fields length doesn't match: expect: %v, got %v", p.nFields, len(ddl.fields))
}
tests.AssertEqual(t, ddl.columns, p.columns)
})
}
}
func TestParseDDL_Whitespaces(t *testing.T) {
testColumns := []migrator.ColumnType{
{
NameValue: sql.NullString{String: "id", Valid: true},
DataTypeValue: sql.NullString{String: "integer", Valid: true},
ColumnTypeValue: sql.NullString{String: "integer", Valid: true},
NullableValue: sql.NullBool{Bool: true, Valid: true},
DefaultValueValue: sql.NullString{Valid: false},
UniqueValue: sql.NullBool{Bool: true, Valid: true},
PrimaryKeyValue: sql.NullBool{Bool: true, Valid: true},
},
{
NameValue: sql.NullString{String: "dark_mode", Valid: true},
DataTypeValue: sql.NullString{String: "numeric", Valid: true},
ColumnTypeValue: sql.NullString{String: "numeric", Valid: true},
NullableValue: sql.NullBool{Bool: true, Valid: true},
DefaultValueValue: sql.NullString{String: "true", Valid: true},
UniqueValue: sql.NullBool{Bool: false, Valid: true},
PrimaryKeyValue: sql.NullBool{Bool: false, Valid: true},
},
}
params := []struct {
name string
sql []string
nFields int
columns []migrator.ColumnType
}{
{
"with_newline",
[]string{"CREATE TABLE `users`\n(\nid integer primary key unique,\ndark_mode numeric DEFAULT true)"},
2,
testColumns,
},
{
"with_newline_2",
[]string{"CREATE TABLE `users` (\n\nid integer primary key unique,\ndark_mode numeric DEFAULT true)"},
2,
testColumns,
},
{
"with_missing_space",
[]string{"CREATE TABLE `users`(id integer primary key unique, dark_mode numeric DEFAULT true)"},
2,
testColumns,
},
{
"with_many_spaces",
[]string{"CREATE TABLE `users` (id integer primary key unique, dark_mode numeric DEFAULT true)"},
2,
testColumns,
},
}
for _, p := range params {
t.Run(p.name, func(t *testing.T) {
ddl, err := parseDDL(p.sql...)
if err != nil {
panic(err.Error())
}
if len(ddl.fields) != p.nFields {
t.Fatalf("fields length doesn't match: expect: %v, got %v", p.nFields, len(ddl.fields))
}
tests.AssertEqual(t, ddl.columns, p.columns)
})
}
}
func TestParseDDL_error(t *testing.T) {
params := []struct {
name string
sql string
}{
{"invalid_cmd", "CREATE TABLE"},
{"unbalanced_brackets", "CREATE TABLE test (ID int NOT NULL,Name varchar(255)"},
{"unbalanced_brackets2", "CREATE TABLE test (ID int NOT NULL,Name varchar(255)))"},
}
for _, p := range params {
t.Run(p.name, func(t *testing.T) {
_, err := parseDDL(p.sql)
if err == nil {
t.Fail()
}
})
}
}
func TestAddConstraint(t *testing.T) {
params := []struct {
name string
fields []string
cName string
sql string
expect []string
}{
{
name: "add_new",
fields: []string{"`id` integer NOT NULL"},
cName: "fk_users_notes",
sql: "CONSTRAINT `fk_users_notes` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`))",
expect: []string{"`id` integer NOT NULL", "CONSTRAINT `fk_users_notes` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`))"},
},
{
name: "update",
fields: []string{"`id` integer NOT NULL", "CONSTRAINT `fk_users_notes` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`))"},
cName: "fk_users_notes",
sql: "CONSTRAINT `fk_users_notes` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`)) ON UPDATE CASCADE ON DELETE CASCADE",
expect: []string{"`id` integer NOT NULL", "CONSTRAINT `fk_users_notes` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`)) ON UPDATE CASCADE ON DELETE CASCADE"},
},
{
name: "add_check",
fields: []string{"`id` integer NOT NULL"},
cName: "name_checker",
sql: "CONSTRAINT `name_checker` CHECK (`name` <> 'jinzhu')",
expect: []string{"`id` integer NOT NULL", "CONSTRAINT `name_checker` CHECK (`name` <> 'jinzhu')"},
},
{
name: "update_check",
fields: []string{"`id` integer NOT NULL", "CONSTRAINT `name_checker` CHECK (`name` <> 'thetadev')"},
cName: "name_checker",
sql: "CONSTRAINT `name_checker` CHECK (`name` <> 'jinzhu')",
expect: []string{"`id` integer NOT NULL", "CONSTRAINT `name_checker` CHECK (`name` <> 'jinzhu')"},
},
}
for _, p := range params {
t.Run(p.name, func(t *testing.T) {
testDDL := ddl{fields: p.fields}
testDDL.addConstraint(p.cName, p.sql)
tests.AssertEqual(t, p.expect, testDDL.fields)
})
}
}
func TestRemoveConstraint(t *testing.T) {
params := []struct {
name string
fields []string
cName string
success bool
expect []string
}{
{
name: "fk",
fields: []string{"`id` integer NOT NULL", "CONSTRAINT `fk_users_notes` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`))"},
cName: "fk_users_notes",
success: true,
expect: []string{"`id` integer NOT NULL"},
},
{
name: "check",
fields: []string{"CONSTRAINT `name_checker` CHECK (`name` <> 'thetadev')", "`id` integer NOT NULL"},
cName: "name_checker",
success: true,
expect: []string{"`id` integer NOT NULL"},
},
{
name: "none",
fields: []string{"CONSTRAINT `name_checker` CHECK (`name` <> 'thetadev')", "`id` integer NOT NULL"},
cName: "nothing",
success: false,
expect: []string{"CONSTRAINT `name_checker` CHECK (`name` <> 'thetadev')", "`id` integer NOT NULL"},
},
}
for _, p := range params {
t.Run(p.name, func(t *testing.T) {
testDDL := ddl{fields: p.fields}
success := testDDL.removeConstraint(p.cName)
tests.AssertEqual(t, p.success, success)
tests.AssertEqual(t, p.expect, testDDL.fields)
})
}
}
func TestGetColumns(t *testing.T) {
params := []struct {
name string
ddl string
columns []string
}{
{
name: "with_fk",
ddl: "CREATE TABLE `notes` (`id` integer NOT NULL,`text` varchar(500),`user_id` integer,PRIMARY KEY (`id`),CONSTRAINT `fk_users_notes` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`))",
columns: []string{"`id`", "`text`", "`user_id`"},
},
{
name: "with_check",
ddl: "CREATE TABLE Persons (ID int NOT NULL,LastName varchar(255) NOT NULL,FirstName varchar(255),Age int,CHECK (Age>=18),CHECK (FirstName!='John'))",
columns: []string{"`ID`", "`LastName`", "`FirstName`", "`Age`"},
},
{
name: "with_escaped_quote",
ddl: "CREATE TABLE Persons (ID int NOT NULL,LastName varchar(255) NOT NULL DEFAULT \"\",FirstName varchar(255))",
columns: []string{"`ID`", "`LastName`", "`FirstName`"},
},
{
name: "with_generated_column",
ddl: "CREATE TABLE Persons (ID int NOT NULL,LastName varchar(255) NOT NULL,FirstName varchar(255),FullName varchar(255) GENERATED ALWAYS AS (FirstName || ' ' || LastName))",
columns: []string{"`ID`", "`LastName`", "`FirstName`"},
},
{
name: "with_new_line",
ddl: `CREATE TABLE "tb_sys_role_menu__temp" (
"id" integer PRIMARY KEY AUTOINCREMENT,
"created_at" datetime NOT NULL,
"updated_at" datetime NOT NULL,
"created_by" integer NOT NULL DEFAULT 0,
"updated_by" integer NOT NULL DEFAULT 0,
"role_id" integer NOT NULL,
"menu_id" bigint NOT NULL
)`,
columns: []string{"`id`", "`created_at`", "`updated_at`", "`created_by`", "`updated_by`", "`role_id`", "`menu_id`"},
},
}
for _, p := range params {
t.Run(p.name, func(t *testing.T) {
testDDL, err := parseDDL(p.ddl)
if err != nil {
panic(err.Error())
}
cols := testDDL.getColumns()
tests.AssertEqual(t, p.columns, cols)
})
}
}

11
gormlite/download.sh Executable file
View File

@@ -0,0 +1,11 @@
#!/usr/bin/env bash
set -euo pipefail
cd -P -- "$(dirname -- "$0")"
curl -#OL "https://github.com/go-gorm/sqlite/raw/v1.5.5/ddlmod.go"
curl -#OL "https://github.com/go-gorm/sqlite/raw/v1.5.5/ddlmod_test.go"
curl -#OL "https://github.com/go-gorm/sqlite/raw/v1.5.5/error_translator.go"
curl -#OL "https://github.com/go-gorm/sqlite/raw/v1.5.5/migrator.go"
curl -#OL "https://github.com/go-gorm/sqlite/raw/v1.5.5/sqlite.go"
curl -#OL "https://github.com/go-gorm/sqlite/raw/v1.5.5/sqlite_test.go"

View File

@@ -0,0 +1,21 @@
package gormlite
import (
"errors"
"github.com/ncruces/go-sqlite3"
"gorm.io/gorm"
)
func (_Dialector) Translate(err error) error {
switch {
case
errors.Is(err, sqlite3.CONSTRAINT_UNIQUE),
errors.Is(err, sqlite3.CONSTRAINT_PRIMARYKEY):
return gorm.ErrDuplicatedKey
case
errors.Is(err, sqlite3.CONSTRAINT_FOREIGNKEY):
return gorm.ErrForeignKeyViolated
}
return err
}

16
gormlite/go.mod Normal file
View File

@@ -0,0 +1,16 @@
module github.com/ncruces/go-sqlite3/gormlite
go 1.21
require (
github.com/ncruces/go-sqlite3 v0.13.0
gorm.io/gorm v1.25.7
)
require (
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/ncruces/julianday v1.0.0 // indirect
github.com/tetratelabs/wazero v1.7.0 // indirect
golang.org/x/sys v0.18.0 // indirect
)

16
gormlite/go.sum Normal file
View File

@@ -0,0 +1,16 @@
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/ncruces/go-sqlite3 v0.13.0 h1:+N1VHVLnrCJasyXdAKQL9MNTKC3wHZa8pLMUuP8wb3k=
github.com/ncruces/go-sqlite3 v0.13.0/go.mod h1:y9zPUI+C42V9xuuJeN+tGhpOjr4gUHz2Pi2RLFVEdZg=
github.com/ncruces/julianday v1.0.0 h1:fH0OKwa7NWvniGQtxdJRxAgkBMolni2BjDHaWTxqt7M=
github.com/ncruces/julianday v1.0.0/go.mod h1:Dusn2KvZrrovOMJuOt0TNXL6tB7U2E8kvza5fFc9G7g=
github.com/tetratelabs/wazero v1.7.0 h1:jg5qPydno59wqjpGrHph81lbtHzTrWzwwtD4cD88+hQ=
github.com/tetratelabs/wazero v1.7.0/go.mod h1:ytl6Zuh20R/eROuyDaGPkp82O9C/DJfXAwJfQ3X6/7Y=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
gorm.io/gorm v1.25.7 h1:VsD6acwRjz2zFxGO50gPO6AkNs7KKnvfzUjHQhZDz/A=
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=

430
gormlite/migrator.go Normal file
View File

@@ -0,0 +1,430 @@
package gormlite
import (
"database/sql"
"fmt"
"strings"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"gorm.io/gorm/migrator"
"gorm.io/gorm/schema"
)
type _Migrator struct {
migrator.Migrator
}
func (m *_Migrator) RunWithoutForeignKey(fc func() error) error {
var enabled int
m.DB.Raw("PRAGMA foreign_keys").Scan(&enabled)
if enabled == 1 {
m.DB.Exec("PRAGMA foreign_keys = OFF")
defer m.DB.Exec("PRAGMA foreign_keys = ON")
}
return fc()
}
func (m _Migrator) HasTable(value interface{}) bool {
var count int
m.Migrator.RunWithValue(value, func(stmt *gorm.Statement) error {
return m.DB.Raw("SELECT count(*) FROM sqlite_master WHERE type='table' AND name=?", stmt.Table).Row().Scan(&count)
})
return count > 0
}
func (m _Migrator) DropTable(values ...interface{}) error {
return m.RunWithoutForeignKey(func() error {
values = m.ReorderModels(values, false)
tx := m.DB.Session(&gorm.Session{})
for i := len(values) - 1; i >= 0; i-- {
if err := m.RunWithValue(values[i], func(stmt *gorm.Statement) error {
return tx.Exec("DROP TABLE IF EXISTS ?", clause.Table{Name: stmt.Table}).Error
}); err != nil {
return err
}
}
return nil
})
}
func (m _Migrator) GetTables() (tableList []string, err error) {
return tableList, m.DB.Raw("SELECT name FROM sqlite_master where type=?", "table").Scan(&tableList).Error
}
func (m _Migrator) HasColumn(value interface{}, name string) bool {
var count int
m.Migrator.RunWithValue(value, func(stmt *gorm.Statement) error {
if stmt.Schema != nil {
if field := stmt.Schema.LookUpField(name); field != nil {
name = field.DBName
}
}
if name != "" {
m.DB.Raw(
"SELECT count(*) FROM sqlite_master WHERE type = ? AND tbl_name = ? AND (sql LIKE ? OR sql LIKE ? OR sql LIKE ? OR sql LIKE ? OR sql LIKE ?)",
"table", stmt.Table, `%"`+name+`" %`, `%`+name+` %`, "%`"+name+"`%", "%["+name+"]%", "%\t"+name+"\t%",
).Row().Scan(&count)
}
return nil
})
return count > 0
}
func (m _Migrator) AlterColumn(value interface{}, name string) error {
return m.RunWithoutForeignKey(func() error {
return m.recreateTable(value, nil, func(ddl *ddl, stmt *gorm.Statement) (*ddl, []interface{}, error) {
if field := stmt.Schema.LookUpField(name); field != nil {
var sqlArgs []interface{}
for i, f := range ddl.fields {
if matches := columnRegexp.FindStringSubmatch(f); len(matches) > 1 && matches[1] == field.DBName {
ddl.fields[i] = fmt.Sprintf("`%v` ?", field.DBName)
sqlArgs = []interface{}{m.FullDataTypeOf(field)}
// table created by old version might look like `CREATE TABLE ? (? varchar(10) UNIQUE)`.
// FullDataTypeOf doesn't contain UNIQUE, so we need to add unique constraint.
if strings.Contains(strings.ToUpper(matches[3]), " UNIQUE") {
uniName := m.DB.NamingStrategy.UniqueName(stmt.Table, field.DBName)
uni, _ := m.GuessConstraintInterfaceAndTable(stmt, uniName)
if uni != nil {
uniSQL, uniArgs := uni.Build()
ddl.addConstraint(uniName, uniSQL)
sqlArgs = append(sqlArgs, uniArgs...)
}
}
break
}
}
return ddl, sqlArgs, nil
}
return nil, nil, fmt.Errorf("failed to alter field with name %v", name)
})
})
}
// ColumnTypes return columnTypes []gorm.ColumnType and execErr error
func (m _Migrator) ColumnTypes(value interface{}) ([]gorm.ColumnType, error) {
columnTypes := make([]gorm.ColumnType, 0)
execErr := m.RunWithValue(value, func(stmt *gorm.Statement) (err error) {
var (
sqls []string
sqlDDL *ddl
)
if err := m.DB.Raw("SELECT sql FROM sqlite_master WHERE type IN ? AND tbl_name = ? AND sql IS NOT NULL order by type = ? desc", []string{"table", "index"}, stmt.Table, "table").Scan(&sqls).Error; err != nil {
return err
}
if sqlDDL, err = parseDDL(sqls...); err != nil {
return err
}
rows, err := m.DB.Session(&gorm.Session{}).Table(stmt.Table).Limit(1).Rows()
if err != nil {
return err
}
defer func() {
err = rows.Close()
}()
var rawColumnTypes []*sql.ColumnType
rawColumnTypes, err = rows.ColumnTypes()
if err != nil {
return err
}
for _, c := range rawColumnTypes {
columnType := migrator.ColumnType{SQLColumnType: c}
for _, column := range sqlDDL.columns {
if column.NameValue.String == c.Name() {
column.SQLColumnType = c
columnType = column
break
}
}
columnTypes = append(columnTypes, columnType)
}
return err
})
return columnTypes, execErr
}
func (m _Migrator) DropColumn(value interface{}, name string) error {
return m.recreateTable(value, nil, func(ddl *ddl, stmt *gorm.Statement) (*ddl, []interface{}, error) {
if field := stmt.Schema.LookUpField(name); field != nil {
name = field.DBName
}
ddl.removeColumn(name)
return ddl, nil, nil
})
}
func (m _Migrator) CreateConstraint(value interface{}, name string) error {
return m.RunWithValue(value, func(stmt *gorm.Statement) error {
constraint, table := m.GuessConstraintInterfaceAndTable(stmt, name)
return m.recreateTable(value, &table,
func(ddl *ddl, stmt *gorm.Statement) (*ddl, []interface{}, error) {
var (
constraintName string
constraintSql string
constraintValues []interface{}
)
if constraint != nil {
constraintName = constraint.GetName()
constraintSql, constraintValues = constraint.Build()
} else {
return nil, nil, nil
}
ddl.addConstraint(constraintName, constraintSql)
return ddl, constraintValues, nil
})
})
}
func (m _Migrator) DropConstraint(value interface{}, name string) error {
return m.RunWithValue(value, func(stmt *gorm.Statement) error {
constraint, table := m.GuessConstraintInterfaceAndTable(stmt, name)
if constraint != nil {
name = constraint.GetName()
}
return m.recreateTable(value, &table,
func(ddl *ddl, stmt *gorm.Statement) (*ddl, []interface{}, error) {
ddl.removeConstraint(name)
return ddl, nil, nil
})
})
}
func (m _Migrator) HasConstraint(value interface{}, name string) bool {
var count int64
m.RunWithValue(value, func(stmt *gorm.Statement) error {
constraint, table := m.GuessConstraintInterfaceAndTable(stmt, name)
if constraint != nil {
name = constraint.GetName()
}
m.DB.Raw(
"SELECT count(*) FROM sqlite_master WHERE type = ? AND tbl_name = ? AND (sql LIKE ? OR sql LIKE ? OR sql LIKE ? OR sql LIKE ? OR sql LIKE ?)",
"table", table, `%CONSTRAINT "`+name+`" %`, `%CONSTRAINT `+name+` %`, "%CONSTRAINT `"+name+"`%", "%CONSTRAINT ["+name+"]%", "%CONSTRAINT \t"+name+"\t%",
).Row().Scan(&count)
return nil
})
return count > 0
}
func (m _Migrator) CurrentDatabase() (name string) {
var null interface{}
m.DB.Raw("PRAGMA database_list").Row().Scan(&null, &name, &null)
return
}
func (m _Migrator) BuildIndexOptions(opts []schema.IndexOption, stmt *gorm.Statement) (results []interface{}) {
for _, opt := range opts {
str := stmt.Quote(opt.DBName)
if opt.Expression != "" {
str = opt.Expression
}
if opt.Collate != "" {
str += " COLLATE " + opt.Collate
}
if opt.Sort != "" {
str += " " + opt.Sort
}
results = append(results, clause.Expr{SQL: str})
}
return
}
func (m _Migrator) CreateIndex(value interface{}, name string) error {
return m.RunWithValue(value, func(stmt *gorm.Statement) error {
if stmt.Schema != nil {
if idx := stmt.Schema.LookIndex(name); idx != nil {
opts := m.BuildIndexOptions(idx.Fields, stmt)
values := []interface{}{clause.Column{Name: idx.Name}, clause.Table{Name: stmt.Table}, opts}
createIndexSQL := "CREATE "
if idx.Class != "" {
createIndexSQL += idx.Class + " "
}
createIndexSQL += "INDEX ?"
if idx.Type != "" {
createIndexSQL += " USING " + idx.Type
}
createIndexSQL += " ON ??"
if idx.Where != "" {
createIndexSQL += " WHERE " + idx.Where
}
return m.DB.Exec(createIndexSQL, values...).Error
}
}
return fmt.Errorf("failed to create index with name %v", name)
})
}
func (m _Migrator) HasIndex(value interface{}, name string) bool {
var count int
m.RunWithValue(value, func(stmt *gorm.Statement) error {
if stmt.Schema != nil {
if idx := stmt.Schema.LookIndex(name); idx != nil {
name = idx.Name
}
}
if name != "" {
m.DB.Raw(
"SELECT count(*) FROM sqlite_master WHERE type = ? AND tbl_name = ? AND name = ?", "index", stmt.Table, name,
).Row().Scan(&count)
}
return nil
})
return count > 0
}
func (m _Migrator) RenameIndex(value interface{}, oldName, newName string) error {
return m.RunWithValue(value, func(stmt *gorm.Statement) error {
var sql string
m.DB.Raw("SELECT sql FROM sqlite_master WHERE type = ? AND tbl_name = ? AND name = ?", "index", stmt.Table, oldName).Row().Scan(&sql)
if sql != "" {
if err := m.DropIndex(value, oldName); err != nil {
return err
}
return m.DB.Exec(strings.Replace(sql, oldName, newName, 1)).Error
}
return fmt.Errorf("failed to find index with name %v", oldName)
})
}
func (m _Migrator) DropIndex(value interface{}, name string) error {
return m.RunWithValue(value, func(stmt *gorm.Statement) error {
if stmt.Schema != nil {
if idx := stmt.Schema.LookIndex(name); idx != nil {
name = idx.Name
}
}
return m.DB.Exec("DROP INDEX ?", clause.Column{Name: name}).Error
})
}
type _Index struct {
Seq int
Name string
Unique bool
Origin string
Partial bool
}
// GetIndexes return Indexes []gorm.Index and execErr error,
// See the [doc]
//
// [doc]: https://www.sqlite.org/pragma.html#pragma_index_list
func (m _Migrator) GetIndexes(value interface{}) ([]gorm.Index, error) {
indexes := make([]gorm.Index, 0)
err := m.RunWithValue(value, func(stmt *gorm.Statement) error {
rst := make([]*_Index, 0)
if err := m.DB.Debug().Raw("SELECT * FROM PRAGMA_index_list(?)", stmt.Table).Scan(&rst).Error; err != nil { // alias `PRAGMA index_list(?)`
return err
}
for _, index := range rst {
if index.Origin == "u" { // skip the index was created by a UNIQUE constraint
continue
}
var columns []string
if err := m.DB.Raw("SELECT name FROM PRAGMA_index_info(?)", index.Name).Scan(&columns).Error; err != nil { // alias `PRAGMA index_info(?)`
return err
}
indexes = append(indexes, &migrator.Index{
TableName: stmt.Table,
NameValue: index.Name,
ColumnList: columns,
PrimaryKeyValue: sql.NullBool{Bool: index.Origin == "pk", Valid: true}, // The exceptions are INTEGER PRIMARY KEY
UniqueValue: sql.NullBool{Bool: index.Unique, Valid: true},
})
}
return nil
})
return indexes, err
}
func (m _Migrator) getRawDDL(table string) (string, error) {
var createSQL string
m.DB.Raw("SELECT sql FROM sqlite_master WHERE type = ? AND tbl_name = ? AND name = ?", "table", table, table).Row().Scan(&createSQL)
if m.DB.Error != nil {
return "", m.DB.Error
}
return createSQL, nil
}
func (m _Migrator) recreateTable(
value interface{}, tablePtr *string,
getCreateSQL func(ddl *ddl, stmt *gorm.Statement) (sql *ddl, sqlArgs []interface{}, err error),
) error {
return m.RunWithValue(value, func(stmt *gorm.Statement) error {
table := stmt.Table
if tablePtr != nil {
table = *tablePtr
}
rawDDL, err := m.getRawDDL(table)
if err != nil {
return err
}
originDDL, err := parseDDL(rawDDL)
if err != nil {
return err
}
createDDL, sqlArgs, err := getCreateSQL(originDDL.clone(), stmt)
if err != nil {
return err
}
if createDDL == nil {
return nil
}
newTableName := table + "__temp"
if err := createDDL.renameTable(newTableName, table); err != nil {
return err
}
columns := createDDL.getColumns()
createSQL := createDDL.compile()
return m.DB.Transaction(func(tx *gorm.DB) error {
if err := tx.Exec(createSQL, sqlArgs...).Error; err != nil {
return err
}
queries := []string{
fmt.Sprintf("INSERT INTO `%v`(%v) SELECT %v FROM `%v`", newTableName, strings.Join(columns, ","), strings.Join(columns, ","), table),
fmt.Sprintf("DROP TABLE `%v`", table),
fmt.Sprintf("ALTER TABLE `%v` RENAME TO `%v`", newTableName, table),
}
for _, query := range queries {
if err := tx.Exec(query).Error; err != nil {
return err
}
}
return nil
})
})
}

221
gormlite/sqlite.go Normal file
View File

@@ -0,0 +1,221 @@
// Package gormlite provides a GORM driver for SQLite.
package gormlite
import (
"database/sql"
"strconv"
"gorm.io/gorm"
"gorm.io/gorm/callbacks"
"gorm.io/gorm/clause"
"gorm.io/gorm/logger"
"gorm.io/gorm/migrator"
"gorm.io/gorm/schema"
"github.com/ncruces/go-sqlite3/driver"
)
// Open opens a GORM dialector from a data source name.
func Open(dsn string) gorm.Dialector {
return &_Dialector{DSN: dsn}
}
// Open opens a GORM dialector from a database handle.
func OpenDB(db *sql.DB) gorm.Dialector {
return &_Dialector{Conn: db}
}
type _Dialector struct {
DSN string
Conn gorm.ConnPool
}
func (dialector _Dialector) Name() string {
return "sqlite"
}
func (dialector _Dialector) Initialize(db *gorm.DB) (err error) {
if dialector.Conn != nil {
db.ConnPool = dialector.Conn
} else {
conn, err := driver.Open(dialector.DSN, nil)
if err != nil {
return err
}
db.ConnPool = conn
}
callbacks.RegisterDefaultCallbacks(db, &callbacks.Config{
CreateClauses: []string{"INSERT", "VALUES", "ON CONFLICT", "RETURNING"},
UpdateClauses: []string{"UPDATE", "SET", "FROM", "WHERE", "RETURNING"},
DeleteClauses: []string{"DELETE", "FROM", "WHERE", "RETURNING"},
LastInsertIDReversed: true,
})
for k, v := range dialector.ClauseBuilders() {
db.ClauseBuilders[k] = v
}
return
}
func (dialector _Dialector) ClauseBuilders() map[string]clause.ClauseBuilder {
return map[string]clause.ClauseBuilder{
"INSERT": func(c clause.Clause, builder clause.Builder) {
if insert, ok := c.Expression.(clause.Insert); ok {
if stmt, ok := builder.(*gorm.Statement); ok {
stmt.WriteString("INSERT ")
if insert.Modifier != "" {
stmt.WriteString(insert.Modifier)
stmt.WriteByte(' ')
}
stmt.WriteString("INTO ")
if insert.Table.Name == "" {
stmt.WriteQuoted(stmt.Table)
} else {
stmt.WriteQuoted(insert.Table)
}
return
}
}
c.Build(builder)
},
"LIMIT": func(c clause.Clause, builder clause.Builder) {
if limit, ok := c.Expression.(clause.Limit); ok {
var lmt = -1
if limit.Limit != nil && *limit.Limit >= 0 {
lmt = *limit.Limit
}
if lmt >= 0 || limit.Offset > 0 {
builder.WriteString("LIMIT ")
builder.WriteString(strconv.Itoa(lmt))
}
if limit.Offset > 0 {
builder.WriteString(" OFFSET ")
builder.WriteString(strconv.Itoa(limit.Offset))
}
}
},
"FOR": func(c clause.Clause, builder clause.Builder) {
if _, ok := c.Expression.(clause.Locking); ok {
// SQLite3 does not support row-level locking.
return
}
c.Build(builder)
},
}
}
func (dialector _Dialector) DefaultValueOf(field *schema.Field) clause.Expression {
if field.AutoIncrement {
return clause.Expr{SQL: "NULL"}
}
// doesn't work, will raise error
return clause.Expr{SQL: "DEFAULT"}
}
func (dialector _Dialector) Migrator(db *gorm.DB) gorm.Migrator {
return _Migrator{migrator.Migrator{Config: migrator.Config{
DB: db,
Dialector: dialector,
CreateIndexAfterCreateTable: true,
}}}
}
func (dialector _Dialector) BindVarTo(writer clause.Writer, stmt *gorm.Statement, v interface{}) {
writer.WriteByte('?')
}
func (dialector _Dialector) QuoteTo(writer clause.Writer, str string) {
var (
underQuoted, selfQuoted bool
continuousBacktick int8
shiftDelimiter int8
)
for _, v := range []byte(str) {
switch v {
case '`':
continuousBacktick++
if continuousBacktick == 2 {
writer.WriteString("``")
continuousBacktick = 0
}
case '.':
if continuousBacktick > 0 || !selfQuoted {
shiftDelimiter = 0
underQuoted = false
continuousBacktick = 0
writer.WriteString("`")
}
writer.WriteByte(v)
continue
default:
if shiftDelimiter-continuousBacktick <= 0 && !underQuoted {
writer.WriteString("`")
underQuoted = true
if selfQuoted = continuousBacktick > 0; selfQuoted {
continuousBacktick -= 1
}
}
for ; continuousBacktick > 0; continuousBacktick -= 1 {
writer.WriteString("``")
}
writer.WriteByte(v)
}
shiftDelimiter++
}
if continuousBacktick > 0 && !selfQuoted {
writer.WriteString("``")
}
writer.WriteString("`")
}
func (dialector _Dialector) Explain(sql string, vars ...interface{}) string {
return logger.ExplainSQL(sql, nil, `"`, vars...)
}
func (dialector _Dialector) DataTypeOf(field *schema.Field) string {
switch field.DataType {
case schema.Bool:
return "numeric"
case schema.Int, schema.Uint:
if field.AutoIncrement {
// doesn't check `PrimaryKey`, to keep backward compatibility
// https://sqlite.org/autoinc.html
return "integer PRIMARY KEY AUTOINCREMENT"
} else {
return "integer"
}
case schema.Float:
return "real"
case schema.String:
return "text"
case schema.Time:
// Distinguish between schema.Time and tag time
if val, ok := field.TagSettings["TYPE"]; ok {
return val
} else {
return "datetime"
}
case schema.Bytes:
return "blob"
}
return string(field.DataType)
}
func (dialectopr _Dialector) SavePoint(tx *gorm.DB, name string) error {
tx.Exec("SAVEPOINT " + name)
return nil
}
func (dialectopr _Dialector) RollbackTo(tx *gorm.DB, name string) error {
tx.Exec("ROLLBACK TO SAVEPOINT " + name)
return nil
}

96
gormlite/sqlite_test.go Normal file
View File

@@ -0,0 +1,96 @@
package gormlite
import (
"fmt"
"testing"
"gorm.io/gorm"
"github.com/ncruces/go-sqlite3"
"github.com/ncruces/go-sqlite3/driver"
_ "github.com/ncruces/go-sqlite3/embed"
)
func TestDialector(t *testing.T) {
// This is the DSN of the in-memory SQLite database for these tests.
const InMemoryDSN = "file:testdatabase?mode=memory&cache=shared"
// Custom connection with a custom function called "my_custom_function".
db, err := driver.Open(InMemoryDSN, func(conn *sqlite3.Conn) error {
return conn.CreateFunction("my_custom_function", 0, sqlite3.DETERMINISTIC,
func(ctx sqlite3.Context, arg ...sqlite3.Value) {
ctx.ResultText("my-result")
})
})
if err != nil {
t.Fatal(err)
}
rows := []struct {
description string
dialector gorm.Dialector
openSuccess bool
query string
querySuccess bool
}{
{
description: "Default driver",
dialector: Open(InMemoryDSN),
openSuccess: true,
query: "SELECT 1",
querySuccess: true,
},
{
description: "Custom function",
dialector: Open(InMemoryDSN),
openSuccess: true,
query: "SELECT my_custom_function()",
querySuccess: false,
},
{
description: "Custom connection",
dialector: OpenDB(db),
openSuccess: true,
query: "SELECT 1",
querySuccess: true,
},
{
description: "Custom connection, custom function",
dialector: OpenDB(db),
openSuccess: true,
query: "SELECT my_custom_function()",
querySuccess: true,
},
}
for rowIndex, row := range rows {
t.Run(fmt.Sprintf("%d/%s", rowIndex, row.description), func(t *testing.T) {
db, err := gorm.Open(row.dialector, &gorm.Config{})
if !row.openSuccess {
if err == nil {
t.Errorf("Expected Open to fail.")
}
return
}
if err != nil {
t.Errorf("Expected Open to succeed; got error: %v", err)
}
if db == nil {
t.Errorf("Expected db to be non-nil.")
}
if row.query != "" {
err = db.Exec(row.query).Error
if !row.querySuccess {
if err == nil {
t.Errorf("Expected query to fail.")
}
return
}
if err != nil {
t.Errorf("Expected query to succeed; got error: %v", err)
}
}
})
}
}

27
gormlite/test.sh Executable file
View File

@@ -0,0 +1,27 @@
#!/usr/bin/env bash
set -euo pipefail
cd -P -- "$(dirname -- "$0")"
rm -rf gorm/ tests/
go work use -r .
go test
git clone --branch v1.25.7 --filter=blob:none https://github.com/go-gorm/gorm.git
mv gorm/tests tests
rm -rf gorm/
patch -p1 -N < tests.patch
cd tests
go mod edit \
-require github.com/ncruces/go-sqlite3/gormlite@v0.0.0 \
-replace github.com/ncruces/go-sqlite3/gormlite=../ \
-replace github.com/ncruces/go-sqlite3=../../ \
-droprequire gorm.io/driver/sqlite \
-dropreplace gorm.io/gorm
go mod tidy && go work use . && go test
cd ..
rm -rf tests/
go work use -r .

22
gormlite/tests.patch Normal file
View File

@@ -0,0 +1,22 @@
diff --git a/tests/.gitignore b/tests/.gitignore
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -1 +1 @@
-go.sum
+*
diff --git a/tests/tests_test.go b/tests/tests_test.go
--- a/tests/tests_test.go
+++ b/tests/tests_test.go
@@ -7,9 +7,11 @@ import (
"path/filepath"
"time"
+ _ "github.com/ncruces/go-sqlite3/embed"
+ sqlite "github.com/ncruces/go-sqlite3/gormlite"
+
"gorm.io/driver/mysql"
"gorm.io/driver/postgres"
- "gorm.io/driver/sqlite"
"gorm.io/driver/sqlserver"
"gorm.io/gorm"
"gorm.io/gorm/logger"

22
internal/util/bool.go Normal file
View File

@@ -0,0 +1,22 @@
package util
import "strings"
func ParseBool(s string) (b, ok bool) {
if len(s) == 0 {
return false, false
}
if s[0] == '0' {
return false, true
}
if '1' <= s[0] && s[0] <= '9' {
return true, true
}
switch strings.ToLower(s) {
case "true", "yes", "on":
return true, true
case "false", "no", "off":
return false, true
}
return false, false
}

View File

@@ -0,0 +1,28 @@
package util
import "testing"
func TestParseBool(t *testing.T) {
tests := []struct {
str string
val bool
ok bool
}{
{"", false, false},
{"0", false, true},
{"1", true, true},
{"9", true, true},
{"T", false, false},
{"true", true, true},
{"FALSE", false, true},
{"false?", false, false},
}
for _, tt := range tests {
t.Run(tt.str, func(t *testing.T) {
gotVal, gotOK := ParseBool(tt.str)
if gotVal != tt.val || gotOK != tt.ok {
t.Errorf("ParseBool(%q) = (%v, %v) want (%v, %v)", tt.str, gotVal, gotOK, tt.val, tt.ok)
}
})
}
}

117
internal/util/const.go Normal file
View File

@@ -0,0 +1,117 @@
package util
// https://sqlite.com/matrix/rescode.html
const (
OK = 0 /* Successful result */
ERROR = 1 /* Generic error */
INTERNAL = 2 /* Internal logic error in SQLite */
PERM = 3 /* Access permission denied */
ABORT = 4 /* Callback routine requested an abort */
BUSY = 5 /* The database file is locked */
LOCKED = 6 /* A table in the database is locked */
NOMEM = 7 /* A malloc() failed */
READONLY = 8 /* Attempt to write a readonly database */
INTERRUPT = 9 /* Operation terminated by sqlite3_interrupt() */
IOERR = 10 /* Some kind of disk I/O error occurred */
CORRUPT = 11 /* The database disk image is malformed */
NOTFOUND = 12 /* Unknown opcode in sqlite3_file_control() */
FULL = 13 /* Insertion failed because database is full */
CANTOPEN = 14 /* Unable to open the database file */
PROTOCOL = 15 /* Database lock protocol error */
EMPTY = 16 /* Internal use only */
SCHEMA = 17 /* The database schema changed */
TOOBIG = 18 /* String or BLOB exceeds size limit */
CONSTRAINT = 19 /* Abort due to constraint violation */
MISMATCH = 20 /* Data type mismatch */
MISUSE = 21 /* Library used incorrectly */
NOLFS = 22 /* Uses OS features not supported on host */
AUTH = 23 /* Authorization denied */
FORMAT = 24 /* Not used */
RANGE = 25 /* 2nd parameter to sqlite3_bind out of range */
NOTADB = 26 /* File opened that is not a database file */
NOTICE = 27 /* Notifications from sqlite3_log() */
WARNING = 28 /* Warnings from sqlite3_log() */
ROW = 100 /* sqlite3_step() has another row ready */
DONE = 101 /* sqlite3_step() has finished executing */
ERROR_MISSING_COLLSEQ = ERROR | (1 << 8)
ERROR_RETRY = ERROR | (2 << 8)
ERROR_SNAPSHOT = ERROR | (3 << 8)
IOERR_READ = IOERR | (1 << 8)
IOERR_SHORT_READ = IOERR | (2 << 8)
IOERR_WRITE = IOERR | (3 << 8)
IOERR_FSYNC = IOERR | (4 << 8)
IOERR_DIR_FSYNC = IOERR | (5 << 8)
IOERR_TRUNCATE = IOERR | (6 << 8)
IOERR_FSTAT = IOERR | (7 << 8)
IOERR_UNLOCK = IOERR | (8 << 8)
IOERR_RDLOCK = IOERR | (9 << 8)
IOERR_DELETE = IOERR | (10 << 8)
IOERR_BLOCKED = IOERR | (11 << 8)
IOERR_NOMEM = IOERR | (12 << 8)
IOERR_ACCESS = IOERR | (13 << 8)
IOERR_CHECKRESERVEDLOCK = IOERR | (14 << 8)
IOERR_LOCK = IOERR | (15 << 8)
IOERR_CLOSE = IOERR | (16 << 8)
IOERR_DIR_CLOSE = IOERR | (17 << 8)
IOERR_SHMOPEN = IOERR | (18 << 8)
IOERR_SHMSIZE = IOERR | (19 << 8)
IOERR_SHMLOCK = IOERR | (20 << 8)
IOERR_SHMMAP = IOERR | (21 << 8)
IOERR_SEEK = IOERR | (22 << 8)
IOERR_DELETE_NOENT = IOERR | (23 << 8)
IOERR_MMAP = IOERR | (24 << 8)
IOERR_GETTEMPPATH = IOERR | (25 << 8)
IOERR_CONVPATH = IOERR | (26 << 8)
IOERR_VNODE = IOERR | (27 << 8)
IOERR_AUTH = IOERR | (28 << 8)
IOERR_BEGIN_ATOMIC = IOERR | (29 << 8)
IOERR_COMMIT_ATOMIC = IOERR | (30 << 8)
IOERR_ROLLBACK_ATOMIC = IOERR | (31 << 8)
IOERR_DATA = IOERR | (32 << 8)
IOERR_CORRUPTFS = IOERR | (33 << 8)
IOERR_IN_PAGE = IOERR | (34 << 8)
LOCKED_SHAREDCACHE = LOCKED | (1 << 8)
LOCKED_VTAB = LOCKED | (2 << 8)
BUSY_RECOVERY = BUSY | (1 << 8)
BUSY_SNAPSHOT = BUSY | (2 << 8)
BUSY_TIMEOUT = BUSY | (3 << 8)
CANTOPEN_NOTEMPDIR = CANTOPEN | (1 << 8)
CANTOPEN_ISDIR = CANTOPEN | (2 << 8)
CANTOPEN_FULLPATH = CANTOPEN | (3 << 8)
CANTOPEN_CONVPATH = CANTOPEN | (4 << 8)
CANTOPEN_DIRTYWAL = CANTOPEN | (5 << 8) /* Not Used */
CANTOPEN_SYMLINK = CANTOPEN | (6 << 8)
CORRUPT_VTAB = CORRUPT | (1 << 8)
CORRUPT_SEQUENCE = CORRUPT | (2 << 8)
CORRUPT_INDEX = CORRUPT | (3 << 8)
READONLY_RECOVERY = READONLY | (1 << 8)
READONLY_CANTLOCK = READONLY | (2 << 8)
READONLY_ROLLBACK = READONLY | (3 << 8)
READONLY_DBMOVED = READONLY | (4 << 8)
READONLY_CANTINIT = READONLY | (5 << 8)
READONLY_DIRECTORY = READONLY | (6 << 8)
ABORT_ROLLBACK = ABORT | (2 << 8)
CONSTRAINT_CHECK = CONSTRAINT | (1 << 8)
CONSTRAINT_COMMITHOOK = CONSTRAINT | (2 << 8)
CONSTRAINT_FOREIGNKEY = CONSTRAINT | (3 << 8)
CONSTRAINT_FUNCTION = CONSTRAINT | (4 << 8)
CONSTRAINT_NOTNULL = CONSTRAINT | (5 << 8)
CONSTRAINT_PRIMARYKEY = CONSTRAINT | (6 << 8)
CONSTRAINT_TRIGGER = CONSTRAINT | (7 << 8)
CONSTRAINT_UNIQUE = CONSTRAINT | (8 << 8)
CONSTRAINT_VTAB = CONSTRAINT | (9 << 8)
CONSTRAINT_ROWID = CONSTRAINT | (10 << 8)
CONSTRAINT_PINNED = CONSTRAINT | (11 << 8)
CONSTRAINT_DATATYPE = CONSTRAINT | (12 << 8)
NOTICE_RECOVER_WAL = NOTICE | (1 << 8)
NOTICE_RECOVER_ROLLBACK = NOTICE | (2 << 8)
NOTICE_RBU = NOTICE | (3 << 8)
WARNING_AUTOINDEX = WARNING | (1 << 8)
AUTH_USER = AUTH | (1 << 8)
OK_LOAD_PERMANENTLY = OK | (1 << 8)
OK_SYMLINK = OK | (2 << 8) /* internal use only */
)

106
internal/util/error.go Normal file
View File

@@ -0,0 +1,106 @@
package util
import (
"runtime"
"strconv"
)
type ErrorString string
func (e ErrorString) Error() string { return string(e) }
const (
NilErr = ErrorString("sqlite3: invalid memory address or null pointer dereference")
OOMErr = ErrorString("sqlite3: out of memory")
RangeErr = ErrorString("sqlite3: index out of range")
NoNulErr = ErrorString("sqlite3: missing NUL terminator")
NoBinaryErr = ErrorString("sqlite3: no SQLite binary embed/set/loaded")
BadBinaryErr = ErrorString("sqlite3: invalid SQLite binary embed/set/loaded")
TimeErr = ErrorString("sqlite3: invalid time value")
WhenceErr = ErrorString("sqlite3: invalid whence")
OffsetErr = ErrorString("sqlite3: invalid offset")
TailErr = ErrorString("sqlite3: multiple statements")
IsolationErr = ErrorString("sqlite3: unsupported isolation level")
ValueErr = ErrorString("sqlite3: unsupported value")
NoVFSErr = ErrorString("sqlite3: no such vfs: ")
)
func AssertErr() ErrorString {
msg := "sqlite3: assertion failed"
if _, file, line, ok := runtime.Caller(1); ok {
msg += " (" + file + ":" + strconv.Itoa(line) + ")"
}
return ErrorString(msg)
}
func ErrorCodeString(rc uint32) string {
switch rc {
case ABORT_ROLLBACK:
return "sqlite3: abort due to ROLLBACK"
case ROW:
return "sqlite3: another row available"
case DONE:
return "sqlite3: no more rows available"
}
switch rc & 0xff {
case OK:
return "sqlite3: not an error"
case ERROR:
return "sqlite3: SQL logic error"
case INTERNAL:
break
case PERM:
return "sqlite3: access permission denied"
case ABORT:
return "sqlite3: query aborted"
case BUSY:
return "sqlite3: database is locked"
case LOCKED:
return "sqlite3: database table is locked"
case NOMEM:
return "sqlite3: out of memory"
case READONLY:
return "sqlite3: attempt to write a readonly database"
case INTERRUPT:
return "sqlite3: interrupted"
case IOERR:
return "sqlite3: disk I/O error"
case CORRUPT:
return "sqlite3: database disk image is malformed"
case NOTFOUND:
return "sqlite3: unknown operation"
case FULL:
return "sqlite3: database or disk is full"
case CANTOPEN:
return "sqlite3: unable to open database file"
case PROTOCOL:
return "sqlite3: locking protocol"
case FORMAT:
break
case SCHEMA:
return "sqlite3: database schema has changed"
case TOOBIG:
return "sqlite3: string or blob too big"
case CONSTRAINT:
return "sqlite3: constraint failed"
case MISMATCH:
return "sqlite3: datatype mismatch"
case MISUSE:
return "sqlite3: bad parameter or other API misuse"
case NOLFS:
break
case AUTH:
return "sqlite3: authorization denied"
case EMPTY:
break
case RANGE:
return "sqlite3: column index out of range"
case NOTADB:
return "sqlite3: file is not a database"
case NOTICE:
return "sqlite3: notification message"
case WARNING:
return "sqlite3: warning message"
}
return "sqlite3: unknown error"
}

193
internal/util/func.go Normal file
View File

@@ -0,0 +1,193 @@
package util
import (
"context"
"github.com/tetratelabs/wazero"
"github.com/tetratelabs/wazero/api"
)
type i32 interface{ ~int32 | ~uint32 }
type i64 interface{ ~int64 | ~uint64 }
type funcVI[T0 i32] func(context.Context, api.Module, T0)
func (fn funcVI[T0]) Call(ctx context.Context, mod api.Module, stack []uint64) {
fn(ctx, mod, T0(stack[0]))
}
func ExportFuncVI[T0 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0)) {
mod.NewFunctionBuilder().
WithGoModuleFunction(funcVI[T0](fn),
[]api.ValueType{api.ValueTypeI32}, nil).
Export(name)
}
type funcVII[T0, T1 i32] func(context.Context, api.Module, T0, T1)
func (fn funcVII[T0, T1]) Call(ctx context.Context, mod api.Module, stack []uint64) {
fn(ctx, mod, T0(stack[0]), T1(stack[1]))
}
func ExportFuncVII[T0, T1 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1)) {
mod.NewFunctionBuilder().
WithGoModuleFunction(funcVII[T0, T1](fn),
[]api.ValueType{api.ValueTypeI32, api.ValueTypeI32}, nil).
Export(name)
}
type funcVIII[T0, T1, T2 i32] func(context.Context, api.Module, T0, T1, T2)
func (fn funcVIII[T0, T1, T2]) Call(ctx context.Context, mod api.Module, stack []uint64) {
fn(ctx, mod, T0(stack[0]), T1(stack[1]), T2(stack[2]))
}
func ExportFuncVIII[T0, T1, T2 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1, T2)) {
mod.NewFunctionBuilder().
WithGoModuleFunction(funcVIII[T0, T1, T2](fn),
[]api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32}, nil).
Export(name)
}
type funcVIIII[T0, T1, T2, T3 i32] func(context.Context, api.Module, T0, T1, T2, T3)
func (fn funcVIIII[T0, T1, T2, T3]) Call(ctx context.Context, mod api.Module, stack []uint64) {
fn(ctx, mod, T0(stack[0]), T1(stack[1]), T2(stack[2]), T3(stack[3]))
}
func ExportFuncVIIII[T0, T1, T2, T3 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1, T2, T3)) {
mod.NewFunctionBuilder().
WithGoModuleFunction(funcVIIII[T0, T1, T2, T3](fn),
[]api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32}, nil).
Export(name)
}
type funcVIIIII[T0, T1, T2, T3, T4 i32] func(context.Context, api.Module, T0, T1, T2, T3, T4)
func (fn funcVIIIII[T0, T1, T2, T3, T4]) Call(ctx context.Context, mod api.Module, stack []uint64) {
fn(ctx, mod, T0(stack[0]), T1(stack[1]), T2(stack[2]), T3(stack[3]), T4(stack[4]))
}
func ExportFuncVIIIII[T0, T1, T2, T3, T4 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1, T2, T3, T4)) {
mod.NewFunctionBuilder().
WithGoModuleFunction(funcVIIIII[T0, T1, T2, T3, T4](fn),
[]api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32}, nil).
Export(name)
}
type funcVIIIIJ[T0, T1, T2, T3 i32, T4 i64] func(context.Context, api.Module, T0, T1, T2, T3, T4)
func (fn funcVIIIIJ[T0, T1, T2, T3, T4]) Call(ctx context.Context, mod api.Module, stack []uint64) {
fn(ctx, mod, T0(stack[0]), T1(stack[1]), T2(stack[2]), T3(stack[3]), T4(stack[4]))
}
func ExportFuncVIIIIJ[T0, T1, T2, T3 i32, T4 i64](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1, T2, T3, T4)) {
mod.NewFunctionBuilder().
WithGoModuleFunction(funcVIIIIJ[T0, T1, T2, T3, T4](fn),
[]api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI64}, nil).
Export(name)
}
type funcII[TR, T0 i32] func(context.Context, api.Module, T0) TR
func (fn funcII[TR, T0]) Call(ctx context.Context, mod api.Module, stack []uint64) {
stack[0] = uint64(fn(ctx, mod, T0(stack[0])))
}
func ExportFuncII[TR, T0 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0) TR) {
mod.NewFunctionBuilder().
WithGoModuleFunction(funcII[TR, T0](fn),
[]api.ValueType{api.ValueTypeI32}, []api.ValueType{api.ValueTypeI32}).
Export(name)
}
type funcIII[TR, T0, T1 i32] func(context.Context, api.Module, T0, T1) TR
func (fn funcIII[TR, T0, T1]) Call(ctx context.Context, mod api.Module, stack []uint64) {
stack[0] = uint64(fn(ctx, mod, T0(stack[0]), T1(stack[1])))
}
func ExportFuncIII[TR, T0, T1 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1) TR) {
mod.NewFunctionBuilder().
WithGoModuleFunction(funcIII[TR, T0, T1](fn),
[]api.ValueType{api.ValueTypeI32, api.ValueTypeI32}, []api.ValueType{api.ValueTypeI32}).
Export(name)
}
type funcIIII[TR, T0, T1, T2 i32] func(context.Context, api.Module, T0, T1, T2) TR
func (fn funcIIII[TR, T0, T1, T2]) Call(ctx context.Context, mod api.Module, stack []uint64) {
stack[0] = uint64(fn(ctx, mod, T0(stack[0]), T1(stack[1]), T2(stack[2])))
}
func ExportFuncIIII[TR, T0, T1, T2 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1, T2) TR) {
mod.NewFunctionBuilder().
WithGoModuleFunction(funcIIII[TR, T0, T1, T2](fn),
[]api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32}, []api.ValueType{api.ValueTypeI32}).
Export(name)
}
type funcIIIII[TR, T0, T1, T2, T3 i32] func(context.Context, api.Module, T0, T1, T2, T3) TR
func (fn funcIIIII[TR, T0, T1, T2, T3]) Call(ctx context.Context, mod api.Module, stack []uint64) {
stack[0] = uint64(fn(ctx, mod, T0(stack[0]), T1(stack[1]), T2(stack[2]), T3(stack[3])))
}
func ExportFuncIIIII[TR, T0, T1, T2, T3 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1, T2, T3) TR) {
mod.NewFunctionBuilder().
WithGoModuleFunction(funcIIIII[TR, T0, T1, T2, T3](fn),
[]api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32}, []api.ValueType{api.ValueTypeI32}).
Export(name)
}
type funcIIIIII[TR, T0, T1, T2, T3, T4 i32] func(context.Context, api.Module, T0, T1, T2, T3, T4) TR
func (fn funcIIIIII[TR, T0, T1, T2, T3, T4]) Call(ctx context.Context, mod api.Module, stack []uint64) {
stack[0] = uint64(fn(ctx, mod, T0(stack[0]), T1(stack[1]), T2(stack[2]), T3(stack[3]), T4(stack[4])))
}
func ExportFuncIIIIII[TR, T0, T1, T2, T3, T4 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1, T2, T3, T4) TR) {
mod.NewFunctionBuilder().
WithGoModuleFunction(funcIIIIII[TR, T0, T1, T2, T3, T4](fn),
[]api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32}, []api.ValueType{api.ValueTypeI32}).
Export(name)
}
type funcIIIIIII[TR, T0, T1, T2, T3, T4, T5 i32] func(context.Context, api.Module, T0, T1, T2, T3, T4, T5) TR
func (fn funcIIIIIII[TR, T0, T1, T2, T3, T4, T5]) Call(ctx context.Context, mod api.Module, stack []uint64) {
stack[0] = uint64(fn(ctx, mod, T0(stack[0]), T1(stack[1]), T2(stack[2]), T3(stack[3]), T4(stack[4]), T5(stack[5])))
}
func ExportFuncIIIIIII[TR, T0, T1, T2, T3, T4, T5 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1, T2, T3, T4, T5) TR) {
mod.NewFunctionBuilder().
WithGoModuleFunction(funcIIIIIII[TR, T0, T1, T2, T3, T4, T5](fn),
[]api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32}, []api.ValueType{api.ValueTypeI32}).
Export(name)
}
type funcIIIIJ[TR, T0, T1, T2 i32, T3 i64] func(context.Context, api.Module, T0, T1, T2, T3) TR
func (fn funcIIIIJ[TR, T0, T1, T2, T3]) Call(ctx context.Context, mod api.Module, stack []uint64) {
stack[0] = uint64(fn(ctx, mod, T0(stack[0]), T1(stack[1]), T2(stack[2]), T3(stack[3])))
}
func ExportFuncIIIIJ[TR, T0, T1, T2 i32, T3 i64](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1, T2, T3) TR) {
mod.NewFunctionBuilder().
WithGoModuleFunction(funcIIIIJ[TR, T0, T1, T2, T3](fn),
[]api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI64}, []api.ValueType{api.ValueTypeI32}).
Export(name)
}
type funcIIJ[TR, T0 i32, T1 i64] func(context.Context, api.Module, T0, T1) TR
func (fn funcIIJ[TR, T0, T1]) Call(ctx context.Context, mod api.Module, stack []uint64) {
stack[0] = uint64(fn(ctx, mod, T0(stack[0]), T1(stack[1])))
}
func ExportFuncIIJ[TR, T0 i32, T1 i64](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1) TR) {
mod.NewFunctionBuilder().
WithGoModuleFunction(funcIIJ[TR, T0, T1](fn),
[]api.ValueType{api.ValueTypeI32, api.ValueTypeI64}, []api.ValueType{api.ValueTypeI32}).
Export(name)
}

75
internal/util/handle.go Normal file
View File

@@ -0,0 +1,75 @@
package util
import (
"context"
"io"
"github.com/tetratelabs/wazero/experimental"
)
type handleKey struct{}
type handleState struct {
handles []any
empty int
}
func NewContext(ctx context.Context) context.Context {
state := new(handleState)
ctx = experimental.WithCloseNotifier(ctx, state)
ctx = context.WithValue(ctx, handleKey{}, state)
return ctx
}
func (s *handleState) CloseNotify(ctx context.Context, exitCode uint32) {
for _, h := range s.handles {
if c, ok := h.(io.Closer); ok {
c.Close()
}
}
s.handles = nil
s.empty = 0
}
func GetHandle(ctx context.Context, id uint32) any {
if id == 0 {
return nil
}
s := ctx.Value(handleKey{}).(*handleState)
return s.handles[^id]
}
func DelHandle(ctx context.Context, id uint32) error {
if id == 0 {
return nil
}
s := ctx.Value(handleKey{}).(*handleState)
a := s.handles[^id]
s.handles[^id] = nil
s.empty++
if c, ok := a.(io.Closer); ok {
return c.Close()
}
return nil
}
func AddHandle(ctx context.Context, a any) (id uint32) {
if a == nil {
panic(NilErr)
}
s := ctx.Value(handleKey{}).(*handleState)
// Find an empty slot.
if s.empty > cap(s.handles)-len(s.handles) {
for id, h := range s.handles {
if h == nil {
s.empty--
s.handles[id] = a
return ^uint32(id)
}
}
}
// Add a new slot.
s.handles = append(s.handles, a)
return -uint32(len(s.handles))
}

35
internal/util/json.go Normal file
View File

@@ -0,0 +1,35 @@
package util
import (
"encoding/json"
"strconv"
"time"
"unsafe"
)
type JSON struct{ Value any }
func (j JSON) Scan(value any) error {
var buf []byte
switch v := value.(type) {
case []byte:
buf = v
case string:
buf = unsafe.Slice(unsafe.StringData(v), len(v))
case int64:
buf = strconv.AppendInt(nil, v, 10)
case float64:
buf = strconv.AppendFloat(nil, v, 'g', -1, 64)
case time.Time:
buf = append(buf, '"')
buf = v.AppendFormat(buf, time.RFC3339Nano)
buf = append(buf, '"')
case nil:
buf = append(buf, "null"...)
default:
panic(AssertErr())
}
return json.Unmarshal(buf, j.Value)
}

134
internal/util/mem.go Normal file
View File

@@ -0,0 +1,134 @@
package util
import (
"bytes"
"math"
"github.com/tetratelabs/wazero/api"
)
func View(mod api.Module, ptr uint32, size uint64) []byte {
if ptr == 0 {
panic(NilErr)
}
if size > math.MaxUint32 {
panic(RangeErr)
}
if size == 0 {
return nil
}
buf, ok := mod.Memory().Read(ptr, uint32(size))
if !ok {
panic(RangeErr)
}
return buf
}
func ReadUint8(mod api.Module, ptr uint32) uint8 {
if ptr == 0 {
panic(NilErr)
}
v, ok := mod.Memory().ReadByte(ptr)
if !ok {
panic(RangeErr)
}
return v
}
func ReadUint32(mod api.Module, ptr uint32) uint32 {
if ptr == 0 {
panic(NilErr)
}
v, ok := mod.Memory().ReadUint32Le(ptr)
if !ok {
panic(RangeErr)
}
return v
}
func WriteUint8(mod api.Module, ptr uint32, v uint8) {
if ptr == 0 {
panic(NilErr)
}
ok := mod.Memory().WriteByte(ptr, v)
if !ok {
panic(RangeErr)
}
}
func WriteUint32(mod api.Module, ptr uint32, v uint32) {
if ptr == 0 {
panic(NilErr)
}
ok := mod.Memory().WriteUint32Le(ptr, v)
if !ok {
panic(RangeErr)
}
}
func ReadUint64(mod api.Module, ptr uint32) uint64 {
if ptr == 0 {
panic(NilErr)
}
v, ok := mod.Memory().ReadUint64Le(ptr)
if !ok {
panic(RangeErr)
}
return v
}
func WriteUint64(mod api.Module, ptr uint32, v uint64) {
if ptr == 0 {
panic(NilErr)
}
ok := mod.Memory().WriteUint64Le(ptr, v)
if !ok {
panic(RangeErr)
}
}
func ReadFloat64(mod api.Module, ptr uint32) float64 {
return math.Float64frombits(ReadUint64(mod, ptr))
}
func WriteFloat64(mod api.Module, ptr uint32, v float64) {
WriteUint64(mod, ptr, math.Float64bits(v))
}
func ReadString(mod api.Module, ptr, maxlen uint32) string {
if ptr == 0 {
panic(NilErr)
}
switch maxlen {
case 0:
return ""
case math.MaxUint32:
// avoid overflow
default:
maxlen = maxlen + 1
}
mem := mod.Memory()
buf, ok := mem.Read(ptr, maxlen)
if !ok {
buf, ok = mem.Read(ptr, mem.Size()-ptr)
if !ok {
panic(RangeErr)
}
}
if i := bytes.IndexByte(buf, 0); i < 0 {
panic(NoNulErr)
} else {
return string(buf[:i])
}
}
func WriteBytes(mod api.Module, ptr uint32, b []byte) {
buf := View(mod, ptr, uint64(len(b)))
copy(buf, b)
}
func WriteString(mod api.Module, ptr uint32, s string) {
buf := View(mod, ptr, uint64(len(s)+1))
buf[len(s)] = 0
copy(buf, s)
}

120
internal/util/mem_test.go Normal file
View File

@@ -0,0 +1,120 @@
package util
import (
"math"
"testing"
"github.com/tetratelabs/wazero/experimental/wazerotest"
)
func TestView_nil(t *testing.T) {
defer func() { _ = recover() }()
mock := wazerotest.NewModule(wazerotest.NewFixedMemory(wazerotest.PageSize))
View(mock, 0, 8)
t.Error("want panic")
}
func TestView_range(t *testing.T) {
defer func() { _ = recover() }()
mock := wazerotest.NewModule(wazerotest.NewFixedMemory(wazerotest.PageSize))
View(mock, wazerotest.PageSize-2, 8)
t.Error("want panic")
}
func TestView_overflow(t *testing.T) {
defer func() { _ = recover() }()
mock := wazerotest.NewModule(wazerotest.NewFixedMemory(wazerotest.PageSize))
View(mock, 1, math.MaxInt64)
t.Error("want panic")
}
func TestReadUint8_nil(t *testing.T) {
defer func() { _ = recover() }()
mock := wazerotest.NewModule(wazerotest.NewFixedMemory(wazerotest.PageSize))
ReadUint8(mock, 0)
t.Error("want panic")
}
func TestReadUint8_range(t *testing.T) {
defer func() { _ = recover() }()
mock := wazerotest.NewModule(wazerotest.NewFixedMemory(wazerotest.PageSize))
ReadUint8(mock, wazerotest.PageSize)
t.Error("want panic")
}
func TestReadUint32_nil(t *testing.T) {
defer func() { _ = recover() }()
mock := wazerotest.NewModule(wazerotest.NewFixedMemory(wazerotest.PageSize))
ReadUint32(mock, 0)
t.Error("want panic")
}
func TestReadUint32_range(t *testing.T) {
defer func() { _ = recover() }()
mock := wazerotest.NewModule(wazerotest.NewFixedMemory(wazerotest.PageSize))
ReadUint32(mock, wazerotest.PageSize-2)
t.Error("want panic")
}
func TestReadUint64_nil(t *testing.T) {
defer func() { _ = recover() }()
mock := wazerotest.NewModule(wazerotest.NewFixedMemory(wazerotest.PageSize))
ReadUint64(mock, 0)
t.Error("want panic")
}
func TestReadUint64_range(t *testing.T) {
defer func() { _ = recover() }()
mock := wazerotest.NewModule(wazerotest.NewFixedMemory(wazerotest.PageSize))
ReadUint64(mock, wazerotest.PageSize-2)
t.Error("want panic")
}
func TestWriteUint8_nil(t *testing.T) {
defer func() { _ = recover() }()
mock := wazerotest.NewModule(wazerotest.NewFixedMemory(wazerotest.PageSize))
WriteUint8(mock, 0, 1)
t.Error("want panic")
}
func TestWriteUint8_range(t *testing.T) {
defer func() { _ = recover() }()
mock := wazerotest.NewModule(wazerotest.NewFixedMemory(wazerotest.PageSize))
WriteUint8(mock, wazerotest.PageSize, 1)
t.Error("want panic")
}
func TestWriteUint32_nil(t *testing.T) {
defer func() { _ = recover() }()
mock := wazerotest.NewModule(wazerotest.NewFixedMemory(wazerotest.PageSize))
WriteUint32(mock, 0, 1)
t.Error("want panic")
}
func TestWriteUint32_range(t *testing.T) {
defer func() { _ = recover() }()
mock := wazerotest.NewModule(wazerotest.NewFixedMemory(wazerotest.PageSize))
WriteUint32(mock, wazerotest.PageSize-2, 1)
t.Error("want panic")
}
func TestWriteUint64_nil(t *testing.T) {
defer func() { _ = recover() }()
mock := wazerotest.NewModule(wazerotest.NewFixedMemory(wazerotest.PageSize))
WriteUint64(mock, 0, 1)
t.Error("want panic")
}
func TestWriteUint64_range(t *testing.T) {
defer func() { _ = recover() }()
mock := wazerotest.NewModule(wazerotest.NewFixedMemory(wazerotest.PageSize))
WriteUint64(mock, wazerotest.PageSize-2, 1)
t.Error("want panic")
}
func TestReadString_range(t *testing.T) {
defer func() { _ = recover() }()
mock := wazerotest.NewModule(wazerotest.NewFixedMemory(wazerotest.PageSize))
ReadString(mock, wazerotest.PageSize+2, math.MaxUint32)
t.Error("want panic")
}

11
internal/util/pointer.go Normal file
View File

@@ -0,0 +1,11 @@
package util
type Pointer[T any] struct{ Value T }
func (p Pointer[T]) unwrap() any { return p.Value }
type PointerUnwrap interface{ unwrap() any }
func UnwrapPointer(p PointerUnwrap) any {
return p.unwrap()
}

Some files were not shown because too many files have changed in this diff Show More