├── .gitattributes
├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
└── workflows
│ ├── build.yml
│ ├── build_var.json
│ ├── create-build-matrix.ps1
│ ├── release_template.md
│ └── static_checks.yml
├── .gitignore
├── .gitmodules
├── CONTRIBUTORS.md
├── LICENSE.md
├── README.md
├── SConstruct
├── demo
├── Main.gd
├── Main.gd.uid
├── Main.tscn
├── addons
│ └── godot-sqlite
│ │ ├── bin
│ │ ├── binaries_here.txt
│ │ ├── libgdsqlite.macos.template_debug.framework
│ │ │ └── Resources
│ │ │ │ └── Info.plist
│ │ └── libgdsqlite.macos.template_release.framework
│ │ │ └── Resources
│ │ │ └── Info.plist
│ │ ├── gdsqlite.gdextension
│ │ ├── gdsqlite.gdextension.uid
│ │ ├── godot-sqlite.gd
│ │ ├── godot-sqlite.gd.uid
│ │ └── plugin.cfg
├── build.ps1
├── data
│ ├── test_backup_base64_old.json
│ └── test_backup_old.json
├── data_to_be_packaged.db
├── database.gd
├── database.gd.uid
├── default_env.tres
├── export_data.gd
├── export_data.gd.uid
├── export_presets.cfg
├── icon.png
├── icon.png.import
└── project.godot
├── doc_classes
└── SQLite.xml
├── icon
├── godot-sqleet-icon-256x256.png
├── godot-sqleet-icon.png
├── godot-sqleet-icon.svg
├── godot-sqlite-banner-4.x.png
├── godot-sqlite-banner-4.x.svg
├── godot-sqlite-banner-v2.png
├── godot-sqlite-banner-v2.svg
├── godot-sqlite-banner.png
├── godot-sqlite-banner.svg
├── godot-sqlite-icon-256x256-v2.png
├── godot-sqlite-icon-256x256.png
├── godot-sqlite-icon-4.x.png
├── godot-sqlite-icon-4.x.svg
├── godot-sqlite-icon-v2.png
├── godot-sqlite-icon-v2.svg
├── godot-sqlite-icon.png
├── godot-sqlite-icon.svg
├── godot-sqlite-social-preview-4.x.png
└── godot-sqlite-social-preview-4.x.svg
├── misc
└── scripts
│ └── clang_format.sh
└── src
├── gdsqlite.cpp
├── gdsqlite.h
├── register_types.cpp
├── register_types.h
├── sqlite
├── LICENSE.md
├── shell.c
├── sqlite3.c
├── sqlite3.h
└── sqlite3ext.h
└── vfs
├── gdsqlite_file.cpp
├── gdsqlite_file.h
├── gdsqlite_vfs.cpp
└── gdsqlite_vfs.h
/.gitattributes:
--------------------------------------------------------------------------------
1 | * linguist-vendored
2 | *.cpp linguist-vendored=false
3 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | ko_fi: 2shady4u
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Report a bug in this project
4 | title: ''
5 | labels: bug
6 | assignees: ''
7 |
8 | ---
9 |
12 |
13 | **Environment:**
14 | - OS:
15 | - Godot version:
16 | - _godot-sqlite_ version:
17 |
18 | **Issue description:**
19 |
20 |
21 | **Steps to reproduce:**
22 |
23 |
24 | **Minimal reproduction project:**
25 |
26 |
27 | **Additional context**
28 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: enhancement
6 | assignees: ''
7 |
8 | ---
9 |
12 |
13 | **Is your feature request related to a problem? Please describe.**
14 |
15 |
16 | **Describe the solution you'd like**
17 |
18 |
19 | **Describe alternatives you've considered**
20 |
21 |
22 | **Additional context**
23 |
24 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: 🌈 All Builds
2 | on:
3 | push:
4 | branches: [master]
5 | tags:
6 | - "v*"
7 |
8 | # Global Settings
9 | env:
10 | PROJECT_FOLDER: .
11 | TARGET_PATH: demo/addons/godot-sqlite/bin/
12 | TARGET_NAME: libgdsqlite
13 | VAR_PATH: .github/workflows/build_var.json
14 | SCONS_CACHE: ${{ github.workspace }}/.scons-cache/
15 | EM_VERSION: 3.1.64
16 | GODOT_VERSION: 4.4
17 | EXPORT_NAME: ./demo/
18 |
19 | jobs:
20 | matrix:
21 | name: Generate build matrix
22 | runs-on: ubuntu-latest
23 | outputs:
24 | matrix-json: ${{ steps.set-matrix.outputs.matrix }}
25 |
26 | steps:
27 | - uses: actions/checkout@v4
28 | - id: set-matrix
29 | shell: pwsh
30 | # Use a small PowerShell script to generate the build matrix
31 | run: "& .github/workflows/create-build-matrix.ps1"
32 |
33 | build:
34 | needs: [matrix]
35 | name: ${{ matrix.name }} - ${{ matrix.target == 'template_debug' && 'Debug' || 'Release' }}
36 | runs-on: ${{ matrix.os }}
37 | strategy:
38 | fail-fast: false
39 | matrix:
40 | include: ${{ fromJson(needs.matrix.outputs.matrix-json) }}
41 |
42 | steps:
43 | - name: Checkout
44 | uses: actions/checkout@v4
45 | with:
46 | lfs: true
47 | submodules: recursive
48 |
49 | - name: Restore Godot build cache
50 | uses: ./godot-cpp/.github/actions/godot-cache-restore
51 | with:
52 | cache-name: ${{ matrix.cache-name }}-${{ matrix.target }}
53 | continue-on-error: true
54 |
55 | # Use python 3.x release (works cross platform; best to keep self contained in it's own step)
56 | - name: Set up Python 3.x
57 | uses: actions/setup-python@v5
58 | with:
59 | # Semantic version range syntax or exact version of a Python version
60 | python-version: "3.x"
61 | # Optional - x64 or x86 architecture, defaults to x64
62 | architecture: "x64"
63 |
64 | - name: Android dependencies
65 | if: ${{ matrix.platform == 'android' }}
66 | uses: nttld/setup-ndk@v1
67 | with:
68 | ndk-version: r23c
69 | link-to-sdk: true
70 |
71 | - name: Web dependencies
72 | if: ${{ matrix.platform == 'web' }}
73 | uses: mymindstorm/setup-emsdk@v14
74 | with:
75 | version: ${{ env.EM_VERSION }}
76 | no-cache: true
77 |
78 | # Setup scons, print python version and scons version info, so if anything is broken it won't run the build.
79 | - name: Configuring Python packages
80 | run: |
81 | python -c "import sys; print(sys.version)"
82 | python -m pip install scons ${{ matrix.additional-python-packages }}
83 | python --version
84 | scons --version
85 |
86 | - name: Windows Compilation
87 | if: runner.os == 'Windows'
88 | run: |
89 | if(-Not (Test-Path -Path ${{ env.PROJECT_FOLDER }}\${{ env.TARGET_PATH }}))
90 | {
91 | mkdir ${{ env.PROJECT_FOLDER }}\${{ env.TARGET_PATH }}
92 | }
93 | cd ${{ env.PROJECT_FOLDER }}
94 | scons platform=windows target=${{ matrix.target }} target_path=${{ env.TARGET_PATH }} target_name=${{ env.TARGET_NAME }} -j6 ${{ matrix.flags }}
95 |
96 | - name: iOS Compilation
97 | if: matrix.platform == 'ios'
98 | run: |
99 | mkdir -v -p ${{ env.PROJECT_FOLDER }}/${{ env.TARGET_PATH }}
100 | cd ${{ env.PROJECT_FOLDER }}
101 | scons arch=universal ios_simulator=yes platform=ios target=${{ matrix.target }} target_path=${{ env.TARGET_PATH }} target_name=${{ env.TARGET_NAME }} -j6
102 | scons arch=arm64 ios_simulator=no platform=ios target=${{ matrix.target }} target_path=${{ env.TARGET_PATH }} target_name=${{ env.TARGET_NAME }} -j6
103 | xcodebuild -create-xcframework -library ${{ env.TARGET_PATH }}/${{ env.TARGET_NAME }}.ios.${{ matrix.target }}.a -library ${{ env.TARGET_PATH }}/${{ env.TARGET_NAME }}.ios.${{ matrix.target }}.simulator.a -output ${{ env.TARGET_PATH }}/${{ env.TARGET_NAME }}.ios.${{ matrix.target }}.xcframework
104 | xcodebuild -create-xcframework -library godot-cpp/bin/libgodot-cpp.ios.${{ matrix.target }}.arm64.a -library godot-cpp/bin/libgodot-cpp.ios.${{ matrix.target }}.universal.simulator.a -output ${{ env.TARGET_PATH }}/libgodot-cpp.ios.${{ matrix.target }}.xcframework
105 |
106 | - name: Not Windows or iOS Compilation
107 | if: runner.os != 'Windows' && matrix.platform != 'ios'
108 | run: |
109 | mkdir -v -p ${{ env.PROJECT_FOLDER }}/${{ env.TARGET_PATH }}
110 | cd ${{ env.PROJECT_FOLDER }}
111 | scons platform=${{ matrix.platform }} target=${{ matrix.target }} target_path=${{ env.TARGET_PATH }} target_name=${{ env.TARGET_NAME }} -j6 ${{ matrix.flags }}
112 |
113 | - name: Save Godot build cache
114 | uses: ./godot-cpp/.github/actions/godot-cache-save
115 | with:
116 | cache-name: ${{ matrix.cache-name }}-${{ matrix.target }}
117 | continue-on-error: true
118 |
119 | - name: Upload Artifact
120 | env:
121 | ARTIFACT_FOLDER: ${{ env.PROJECT_FOLDER }}/${{ env.TARGET_PATH }}
122 | uses: actions/upload-artifact@v4
123 | with:
124 | name: ${{ matrix.cache-name }}-${{ matrix.target }}
125 | path: ${{ env.ARTIFACT_FOLDER }}*.${{ matrix.artifact-extension }}
126 | if-no-files-found: error
127 |
128 | web-deploy:
129 | name: Web Deploy
130 | runs-on: ubuntu-latest
131 | needs: [build]
132 | if: github.ref == 'refs/heads/master'
133 | container:
134 | image: barichello/godot-ci:4.4 # Ideally this would be ${GODOT_VERSION}, but Github Actions doesn't allow this! :(
135 |
136 | steps:
137 | - name: Checkout
138 | uses: actions/checkout@v4
139 | with:
140 | lfs: true
141 |
142 | - name: Download Artifacts
143 | uses: actions/download-artifact@v4
144 |
145 | - name: Copy Libraries to Project Folder
146 | run: |
147 | mkdir -v -p ${{ env.PROJECT_FOLDER }}/${{ env.TARGET_PATH }}
148 | cp --verbose web-wasm32-*/*.wasm ${{ env.PROJECT_FOLDER }}/${{ env.TARGET_PATH }}
149 |
150 | - name: Setup
151 | run: |
152 | mkdir -v -p ~/.local/share/godot/export_templates
153 | mv /root/.local/share/godot/export_templates/${GODOT_VERSION}.stable ~/.local/share/godot/export_templates/${GODOT_VERSION}.stable
154 |
155 | - name: Web Build
156 | run: |
157 | mkdir -v -p build/web
158 | cd $EXPORT_NAME
159 | godot --headless --verbose --export-release "Web" ../build/web/index.html
160 |
161 | - name: Upload Artifact
162 | uses: actions/upload-artifact@v4
163 | with:
164 | name: web
165 | path: build/web
166 |
167 | # Installing rsync is needed in order to deploy to GitHub Pages. Without it, the build will fail.
168 | - name: Install rsync 📚
169 | run: |
170 | apt-get update && apt-get install -y rsync
171 |
172 | - name: Deploy to GitHub Pages 🚀
173 | uses: JamesIves/github-pages-deploy-action@releases/v4
174 | with:
175 | BRANCH: gh-pages # The branch the action should deploy to.
176 | FOLDER: build/web # The folder the action should deploy.
177 |
178 | release:
179 | name: Release
180 | runs-on: "ubuntu-22.04"
181 | needs: [build]
182 | if: github.event_name == 'push' && contains(github.ref, 'refs/tags/')
183 |
184 | steps:
185 | - name: Checkout
186 | uses: actions/checkout@v4
187 | with:
188 | lfs: true
189 | submodules: recursive
190 |
191 | - name: Download Artifacts
192 | id: download
193 | uses: actions/download-artifact@v4
194 | with:
195 | path: artifacts
196 |
197 | - name: Copy Artifacts to bin/-folder
198 | run: |
199 | mkdir -v -p ${{ env.PROJECT_FOLDER }}/${{ env.TARGET_PATH }}
200 | cd ${{ env.PROJECT_FOLDER }}
201 | cp -r ${{steps.download.outputs.download-path}}/**/* ${{ env.TARGET_PATH }}
202 | zip -r demo.zip demo/
203 | cd ${{ env.TARGET_PATH }}/..
204 | zip -r bin.zip bin/
205 |
206 | - name: Create Release
207 | uses: softprops/action-gh-release@v1
208 | with:
209 | body_path: ${{ env.PROJECT_FOLDER }}/.github/workflows/release_template.md
210 | files: |
211 | ${{ env.PROJECT_FOLDER }}/demo.zip
212 | ${{ env.PROJECT_FOLDER }}/${{ env.TARGET_PATH }}/../bin.zip
213 | draft: true
214 | prerelease: true
215 |
--------------------------------------------------------------------------------
/.github/workflows/build_var.json:
--------------------------------------------------------------------------------
1 | {
2 | "targets": [
3 | "template_debug",
4 | "template_release"
5 | ],
6 | "common_flags": "enable_fts5=no",
7 | "jobs": [
8 | {
9 | "name": "Windows (x86_64, MSVC)",
10 | "os": "windows-latest",
11 | "platform": "windows",
12 | "artifact-extension": "dll",
13 | "additional-python-packages": "pywin32",
14 | "cache-name": "win-x86_64-msvc",
15 | "skip": true
16 | },
17 | {
18 | "name": "Windows (x86_64, MinGW)",
19 | "os": "windows-latest",
20 | "platform": "windows",
21 | "artifact-extension": "dll",
22 | "flags": "use_mingw=yes",
23 | "cache-name": "win-x86_64-mingw"
24 | },
25 | {
26 | "name": "Windows (x86_64, MinGW, Double Precision)",
27 | "os": "windows-latest",
28 | "platform": "windows",
29 | "artifact-extension": "dll",
30 | "flags": "use_mingw=yes precision=double",
31 | "cache-name": "win-x86_64-mingw-f64",
32 | "skip": true
33 | },
34 | {
35 | "name": "Ubuntu (GCC)",
36 | "os": "ubuntu-22.04",
37 | "platform": "linux",
38 | "artifact-extension": "so",
39 | "cache-name": "linux-x86_64"
40 | },
41 | {
42 | "name": "Ubuntu (GCC, Double Precision)",
43 | "os": "ubuntu-22.04",
44 | "platform": "linux",
45 | "artifact-extension": "so",
46 | "flags": "precision=double",
47 | "cache-name": "linux-x86_64-f64",
48 | "skip": true
49 | },
50 | {
51 | "name": "MacOS (universal)",
52 | "os": "macos-latest",
53 | "platform": "macos",
54 | "artifact-extension": "framework",
55 | "flags": "arch=universal",
56 | "cache-name": "macos-universal"
57 | },
58 | {
59 | "name": "Android (x86_64)",
60 | "os": "ubuntu-22.04",
61 | "platform": "android",
62 | "artifact-extension": "so",
63 | "flags": "arch=x86_64",
64 | "cache-name": "android-x86_64"
65 | },
66 | {
67 | "name": "Android (arm64)",
68 | "os": "ubuntu-22.04",
69 | "platform": "android",
70 | "artifact-extension": "so",
71 | "flags": "arch=arm64",
72 | "cache-name": "android-arm64"
73 | },
74 | {
75 | "name": "iOS (arm64)",
76 | "os": "macos-latest",
77 | "platform": "ios",
78 | "artifact-extension": "xcframework",
79 | "cache-name": "ios-arm64"
80 | },
81 | {
82 | "name": "Web (wasm32)",
83 | "os": "ubuntu-22.04",
84 | "platform": "web",
85 | "artifact-extension": "wasm",
86 | "cache-name": "web-wasm32"
87 | },
88 | {
89 | "name": "Web (wasm32, No Threads)",
90 | "os": "ubuntu-22.04",
91 | "platform": "web",
92 | "artifact-extension": "nothreads.wasm",
93 | "flags": "threads=no",
94 | "cache-name": "web-wasm32-nothreads"
95 | }
96 | ]
97 | }
98 |
--------------------------------------------------------------------------------
/.github/workflows/create-build-matrix.ps1:
--------------------------------------------------------------------------------
1 | $RawMatrix = Get-Content -Raw -Path .github/workflows/build_var.json | ConvertFrom-Json
2 |
3 | $Targets = $RawMatrix.targets
4 | $CommonFlags = $RawMatrix.common_flags
5 | $Jobs = $RawMatrix.jobs
6 |
7 | $Matrix = @()
8 | foreach ($job in $Jobs) {
9 | if ($job.skip -eq $true) {
10 | continue
11 | }
12 | foreach ($target in $Targets) {
13 | $MatrixJob = $job.PsObject.Copy()
14 | $MatrixJob | Add-Member -MemberType NoteProperty -Name 'target' -Value $target
15 | # Add the common flags to the job-specific flags
16 | if ($null -ne $CommonFlags) {
17 | if ($null -eq $MatrixJob.flags) {
18 | $MatrixJob | Add-Member -MemberType NoteProperty -Name 'flags' -Value $CommonFlags
19 | }
20 | else {
21 | $MatrixJob.flags += " " + $CommonFlags
22 | }
23 | }
24 | $Matrix += $MatrixJob
25 | }
26 | }
27 |
28 | Write-Host (ConvertTo-JSON -InputObject $Matrix)
29 | Write-Output "matrix=$(ConvertTo-JSON -InputObject $Matrix -Compress)" >> $env:GITHUB_OUTPUT
--------------------------------------------------------------------------------
/.github/workflows/release_template.md:
--------------------------------------------------------------------------------
1 | Download the demo-project and/or the necessary binaries below.
2 |
3 | ### Dependencies Versions
4 |
5 | Godot vxx.xx.xx
6 | SQLite vxx.xx.xx
7 |
8 | ### Supported Operating Systems:
9 | - Mac OS X (universal)
10 | - Linux
11 | - Windows
12 | - Android (arm64 & x86_64)
13 | - iOS (arm64)
14 |
15 | #### What's new?
16 | - First new thing
17 | - Second new thing
--------------------------------------------------------------------------------
/.github/workflows/static_checks.yml:
--------------------------------------------------------------------------------
1 | # NOTE: The content of this file has been directly adapted from /godot-cpp/.workflows/static_checks.yml
2 | name: 📊 Static Checks
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | static-checks:
7 | name: Format (clang-format)
8 | runs-on: ubuntu-22.04
9 | steps:
10 | - name: Checkout
11 | uses: actions/checkout@v4
12 | with:
13 | lfs: true
14 | submodules: recursive
15 |
16 | # Azure repositories are not reliable, we need to prevent Azure giving us packages.
17 | - name: Make apt sources.list use the default Ubuntu repositories
18 | run: |
19 | sudo rm -f /etc/apt/sources.list.d/*
20 | sudo cp -f godot-cpp/misc/ci/sources.list /etc/apt/sources.list
21 | wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
22 | sudo apt-add-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-15 main"
23 | sudo apt-get update
24 |
25 | - name: Install dependencies
26 | run: |
27 | sudo apt-get install -qq clang-format-15
28 | sudo update-alternatives --remove-all clang-format || true
29 | sudo update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-15 100
30 |
31 | - name: Style checks via clang-format (clang_format.sh)
32 | run: |
33 | bash ./misc/scripts/clang_format.sh
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Godot-specific ignores
3 | *.gen.*
4 | .import/
5 | .godot/
6 | /gen/
7 |
8 | # Imported translations (automatically generated from CSV files)
9 | *.translation
10 |
11 | # Mono-specific ignores
12 | .mono/
13 | data_*/
14 | mono_crash.*.json
15 |
16 | # System/tool-specific ignores
17 | .directory
18 | *~
19 |
20 | # Logs and databases #
21 | ######################
22 | *.log
23 | *.sql
24 | *.sqlite
25 | *.db
26 | # Do not ignore the database that is to be opened in read-only mode!
27 | !data_to_be_packaged.db
28 |
29 | # Compiled source #
30 | ###################
31 | *.com
32 | *.class
33 | *.exe
34 | *.o
35 | *.os
36 | *.obj
37 | *.exp
38 | *.pdb
39 | *.lib
40 | *.bc
41 |
42 | # OS generated files #
43 | ######################
44 | .DS_Store
45 | .DS_Store?
46 | ._*
47 | .Spotlight-V100
48 | .Trashes
49 | ehthumbs.db
50 | Thumbs.db
51 |
52 | # Etc
53 | .sconsign.dblite
54 | demo/build
55 | test_backup_new.json
56 | test_backup_base64_new.json
57 |
58 | # VS
59 | vsproj/.vs/
60 | vsproj/Debug/
61 | vsproj/Release/
62 | vsproj/x64/
63 | vsproj/x86/
64 | vsproj/gdsqlite.vcxproj.*
65 |
66 | .vscode
67 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "godot-cpp"]
2 | path = godot-cpp
3 | url = https://github.com/GodotNativeTools/godot-cpp
4 |
--------------------------------------------------------------------------------
/CONTRIBUTORS.md:
--------------------------------------------------------------------------------
1 | # Contributors
2 |
3 | Here's a list of people who contributed to the plugin with source code changes.
4 | In chronological order based on the first contribution.
5 |
6 | ## Maintainer
7 |
8 | - [Piet Bronders (2shady4u)](https://github.com/2shady4u)
9 | - [Jeroen De Geeter (jdegeete)](https://github.com/jdegeete)
10 |
11 | ## Contributors
12 |
13 | - [flomar](https://github.com/flomar)
14 | - [Aaron Franke (aaronfranke)](https://github.com/aaronfranke)
15 | - [Akshay Sunil Masare (aksmas)](https://github.com/aksmas)
16 | - [Geoffrey Casper (Geo25rey)](https://github.com/Geo25rey)
17 | - [Chris Ridenour (cridenour)](https://github.com/cridenour)
18 | - [filkata](https://github.com/filkata)
19 | - [abcjjy](https://github.com/abcjjy)
20 | - [Markus Sauermann (Sauermann)](https://github.com/Sauermann)
21 | - [Bernardo Rodriguez (Soleyu)](https://github.com/Soleyu)
22 | - [ugursoy](https://github.com/ugursoy)
23 | - [youngminz](https://github.com/youngminz)
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019-2025 Piet Bronders & Jeroen De Geeter
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/SConstruct:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | target_path = ARGUMENTS.pop("target_path", "demo/addons/godot-sqlite/bin/")
4 | target_name = ARGUMENTS.pop("target_name", "libgdsqlite")
5 |
6 | env = SConscript("godot-cpp/SConstruct")
7 |
8 | env_vars = Variables()
9 | env_vars.Add(BoolVariable("enable_fts5", "Enable SQLite's FTS5 extension which provides full-test search functionality to database applications", False))
10 | env_vars.Update(env)
11 | Help(env_vars.GenerateHelpText(env))
12 |
13 | target = "{}{}".format(
14 | target_path, target_name
15 | )
16 |
17 | # For the reference:
18 | # - CCFLAGS are compilation flags shared between C and C++
19 | # - CFLAGS are for C-specific compilation flags
20 | # - CXXFLAGS are for C++-specific compilation flags
21 | # - CPPFLAGS are for pre-processor flags
22 | # - CPPDEFINES are for pre-processor defines
23 | # - LINKFLAGS are for linking flags
24 |
25 | # tweak this if you want to use different folders, or more folders, to store your source code in.
26 | env.Append(CPPPATH=["src/"])
27 | sources = [Glob('src/*.cpp'), Glob('src/vfs/*.cpp'), 'src/sqlite/sqlite3.c']
28 |
29 | if env["target"] in ["editor", "template_debug"]:
30 | doc_data = env.GodotCPPDocData("src/gen/doc_data.gen.cpp", source=Glob("doc_classes/*.xml"))
31 | sources.append(doc_data)
32 |
33 | if env["enable_fts5"]:
34 | print("FTS5 is enabled.")
35 | env.Append(CPPDEFINES=['SQLITE_ENABLE_FTS5'])
36 | else:
37 | print("FTS5 is disabled.")
38 |
39 | if env["platform"] == "macos":
40 | library = env.SharedLibrary(
41 | "{}.{}.{}.framework/{}.{}.{}".format(
42 | target,
43 | env["platform"],
44 | env["target"],
45 | target_name,
46 | env["platform"],
47 | env["target"]
48 | ),
49 | source=sources,
50 | )
51 | elif env["platform"] == "ios":
52 | if env["ios_simulator"]:
53 | library = env.StaticLibrary(
54 | "{}.{}.{}.simulator.a".format(
55 | target,
56 | env["platform"],
57 | env["target"]),
58 | source=sources,
59 | )
60 | else:
61 | library = env.StaticLibrary(
62 | "{}.{}.{}.a".format(
63 | target,
64 | env["platform"],
65 | env["target"]),
66 | source=sources,
67 | )
68 | else:
69 | library = env.SharedLibrary(
70 | "{}{}{}".format(
71 | target,
72 | env["suffix"],
73 | env["SHLIBSUFFIX"]
74 | ),
75 | source=sources,
76 | )
77 |
78 | Default(library)
--------------------------------------------------------------------------------
/demo/Main.gd:
--------------------------------------------------------------------------------
1 | extends Control
2 |
3 | func _enter_tree():
4 | var _error : int = $Database.connect("output_received", _on_output_received)
5 | _error = $Database.connect("texture_received", _on_texture_received)
6 |
7 | func _on_output_received(text : String) -> void:
8 | var label := Label.new()
9 | $MarginContainer/VBoxContainer/ScrollContainer/VBoxContainer.add_child(label)
10 |
11 | label.text = text
12 | label.set("theme_override_colors/font_color", Color.LIME_GREEN)
13 | label.autowrap_mode = TextServer.AUTOWRAP_WORD
14 |
15 | func _on_texture_received(texture : Texture) -> void:
16 | var texture_rect := TextureRect.new()
17 | $MarginContainer/VBoxContainer/ScrollContainer/VBoxContainer.add_child(texture_rect)
18 |
19 | texture_rect.texture = texture
20 | texture_rect.stretch_mode = TextureRect.STRETCH_KEEP
21 |
--------------------------------------------------------------------------------
/demo/Main.gd.uid:
--------------------------------------------------------------------------------
1 | uid://d1u6lbep66lom
2 |
--------------------------------------------------------------------------------
/demo/Main.tscn:
--------------------------------------------------------------------------------
1 | [gd_scene load_steps=4 format=3 uid="uid://dsba4ukrk2ymt"]
2 |
3 | [ext_resource type="Script" uid="uid://c2ckll61v16xo" path="res://database.gd" id="1"]
4 | [ext_resource type="Script" uid="uid://d1u6lbep66lom" path="res://Main.gd" id="2"]
5 |
6 | [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_45gfi"]
7 | content_margin_left = 24.0
8 | content_margin_top = 12.0
9 | content_margin_right = 24.0
10 | content_margin_bottom = 12.0
11 | bg_color = Color(0, 0, 0, 1)
12 |
13 | [node name="Main" type="Control"]
14 | layout_mode = 3
15 | anchors_preset = 15
16 | anchor_right = 1.0
17 | anchor_bottom = 1.0
18 | grow_horizontal = 2
19 | grow_vertical = 2
20 | script = ExtResource("2")
21 |
22 | [node name="Database" type="Node" parent="."]
23 | script = ExtResource("1")
24 |
25 | [node name="MarginContainer" type="MarginContainer" parent="."]
26 | layout_mode = 1
27 | anchors_preset = 15
28 | anchor_right = 1.0
29 | anchor_bottom = 1.0
30 | grow_horizontal = 2
31 | grow_vertical = 2
32 | theme_override_constants/margin_left = 24
33 | theme_override_constants/margin_top = 24
34 | theme_override_constants/margin_right = 24
35 | theme_override_constants/margin_bottom = 24
36 |
37 | [node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"]
38 | layout_mode = 2
39 | theme_override_constants/separation = 24
40 |
41 | [node name="Label" type="Label" parent="MarginContainer/VBoxContainer"]
42 | layout_mode = 2
43 | text = "Godot SQLite Demo"
44 | horizontal_alignment = 1
45 | vertical_alignment = 1
46 |
47 | [node name="ScrollContainer" type="ScrollContainer" parent="MarginContainer/VBoxContainer"]
48 | layout_mode = 2
49 | size_flags_horizontal = 3
50 | size_flags_vertical = 3
51 | theme_override_styles/panel = SubResource("StyleBoxFlat_45gfi")
52 |
53 | [node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer/VBoxContainer/ScrollContainer"]
54 | layout_mode = 2
55 | size_flags_horizontal = 3
56 |
--------------------------------------------------------------------------------
/demo/addons/godot-sqlite/bin/binaries_here.txt:
--------------------------------------------------------------------------------
1 | Download binaries and place them in this folder.
2 | Latest binaries: https://github.com/2shady4u/godot-sqlite/releases
--------------------------------------------------------------------------------
/demo/addons/godot-sqlite/bin/libgdsqlite.macos.template_debug.framework/Resources/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleExecutable
6 | libgdsqlite.template_debug
7 | CFBundleIdentifier
8 | org.godotengine.libgdsqlite
9 | CFBundleInfoDictionaryVersion
10 | 6.0
11 | CFBundleName
12 | libgdsqlite.macos.template_debug
13 | CFBundlePackageType
14 | FMWK
15 | CFBundleShortVersionString
16 | 1.0.0
17 | CFBundleSupportedPlatforms
18 |
19 | MacOSX
20 |
21 | CFBundleVersion
22 | 1.0.0
23 | LSMinimumSystemVersion
24 | 10.12
25 |
26 |
--------------------------------------------------------------------------------
/demo/addons/godot-sqlite/bin/libgdsqlite.macos.template_release.framework/Resources/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleExecutable
6 | libgdsqlite.template_release
7 | CFBundleIdentifier
8 | org.godotengine.libgdsqlite
9 | CFBundleInfoDictionaryVersion
10 | 6.0
11 | CFBundleName
12 | libgdsqlite.macos.template_release
13 | CFBundlePackageType
14 | FMWK
15 | CFBundleShortVersionString
16 | 1.0.0
17 | CFBundleSupportedPlatforms
18 |
19 | MacOSX
20 |
21 | CFBundleVersion
22 | 1.0.0
23 | LSMinimumSystemVersion
24 | 10.12
25 |
26 |
--------------------------------------------------------------------------------
/demo/addons/godot-sqlite/gdsqlite.gdextension:
--------------------------------------------------------------------------------
1 | [configuration]
2 |
3 | entry_symbol = "sqlite_library_init"
4 | compatibility_minimum = "4.4"
5 |
6 | [libraries]
7 |
8 | macos.debug = "res://addons/godot-sqlite/bin/libgdsqlite.macos.template_debug.framework"
9 | macos.release = "res://addons/godot-sqlite/bin/libgdsqlite.macos.template_release.framework"
10 | windows.debug.x86_64 = "res://addons/godot-sqlite/bin/libgdsqlite.windows.template_debug.x86_64.dll"
11 | windows.release.x86_64 = "res://addons/godot-sqlite/bin/libgdsqlite.windows.template_release.x86_64.dll"
12 | linux.debug.x86_64 = "res://addons/godot-sqlite/bin/libgdsqlite.linux.template_debug.x86_64.so"
13 | linux.release.x86_64 = "res://addons/godot-sqlite/bin/libgdsqlite.linux.template_release.x86_64.so"
14 | android.debug.arm64 = "res://addons/godot-sqlite/bin/libgdsqlite.android.template_debug.arm64.so"
15 | android.release.arm64 = "res://addons/godot-sqlite/bin/libgdsqlite.android.template_release.arm64.so"
16 | android.debug.x86_64 = "res://addons/godot-sqlite/bin/libgdsqlite.android.template_debug.x86_64.so"
17 | android.release.x86_64 = "res://addons/godot-sqlite/bin/libgdsqlite.android.template_release.x86_64.so"
18 | ios.debug = "res://addons/godot-sqlite/bin/libgdsqlite.ios.template_debug.xcframework"
19 | ios.release = "res://addons/godot-sqlite/bin/libgdsqlite.ios.template_release.xcframework"
20 | web.debug.threads.wasm32 = "res://addons/godot-sqlite/bin/libgdsqlite.web.template_debug.wasm32.wasm"
21 | web.release.threads.wasm32 = "res://addons/godot-sqlite/bin/libgdsqlite.web.template_release.wasm32.wasm"
22 | web.debug.wasm32 = "res://addons/godot-sqlite/bin/libgdsqlite.web.template_debug.wasm32.nothreads.wasm"
23 | web.release.wasm32 = "res://addons/godot-sqlite/bin/libgdsqlite.web.template_release.wasm32.nothreads.wasm"
24 |
25 | [dependencies]
26 |
27 | ios.debug = {
28 | "res://addons/godot-sqlite/bin/libgodot-cpp.ios.template_debug.xcframework": ""
29 | }
30 | ios.release = {
31 | "res://addons/godot-sqlite/bin/libgodot-cpp.ios.template_release.xcframework": ""
32 | }
--------------------------------------------------------------------------------
/demo/addons/godot-sqlite/gdsqlite.gdextension.uid:
--------------------------------------------------------------------------------
1 | uid://ca6ikrilfs4se
2 |
--------------------------------------------------------------------------------
/demo/addons/godot-sqlite/godot-sqlite.gd:
--------------------------------------------------------------------------------
1 | # ############################################################################ #
2 | # Copyright © 2019-2025 Piet Bronders & Jeroen De Geeter
3 | # Licensed under the MIT License.
4 | # See LICENSE in the project root for license information.
5 | # ############################################################################ #
6 |
7 | @tool
8 | extends EditorPlugin
9 |
10 | func _enter_tree():
11 | pass
12 |
13 | func _exit_tree():
14 | pass
15 |
--------------------------------------------------------------------------------
/demo/addons/godot-sqlite/godot-sqlite.gd.uid:
--------------------------------------------------------------------------------
1 | uid://cwsfv71x0jo4e
2 |
--------------------------------------------------------------------------------
/demo/addons/godot-sqlite/plugin.cfg:
--------------------------------------------------------------------------------
1 | [plugin]
2 |
3 | name="Godot SQLite"
4 | description="GDNative wrapper for SQLite (Godot 4.X+), making it possible to use SQLite databases as data storage in all your future games."
5 | author="Piet Bronders & Jeroen De Geeter"
6 | version="4.5"
7 | script="godot-sqlite.gd"
8 |
--------------------------------------------------------------------------------
/demo/build.ps1:
--------------------------------------------------------------------------------
1 | new-item -Name build -itemType directory
2 | godot -s export_data.gd
3 | godot -e --export-debug "Windows Desktop" build/godot-sqlite.exe
4 |
--------------------------------------------------------------------------------
/demo/data/test_backup_old.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "name": "company",
4 | "type": "table",
5 | "sql": "CREATE TABLE company (id int PRIMARY KEY NOT NULL,name text NOT NULL,age int NOT NULL,address char(50),salary real)",
6 | "row_array": [
7 | {
8 | "address": "California",
9 | "age": 31,
10 | "id": 1,
11 | "name": "Paul",
12 | "salary": 20000
13 | },
14 | {
15 | "address": "Columbia",
16 | "age": 65,
17 | "id": 2,
18 | "name": "James",
19 | "salary": 70000
20 | },
21 | {
22 | "address": "Richmond",
23 | "age": 24,
24 | "id": 3,
25 | "name": "Mark",
26 | "salary": 65000
27 | },
28 | {
29 | "address": "Texas",
30 | "age": 29,
31 | "id": 4,
32 | "name": "Robert",
33 | "salary": 65000
34 | },
35 | {
36 | "address": "Atlanta",
37 | "age": 62,
38 | "id": 5,
39 | "name": "Julia",
40 | "salary": 65000
41 | }
42 | ]
43 | },
44 | {
45 | "name": "office_supply",
46 | "type": "table",
47 | "sql": "CREATE TABLE office_supply (id int PRIMARY KEY NOT NULL,name text NOT NULL,amount int NOT NULL)",
48 | "row_array": [
49 | {
50 | "id": 1,
51 | "name": "stapler",
52 | "amount": 5
53 | },
54 | {
55 | "id": 2,
56 | "name": "binder",
57 | "amount": 56
58 | }
59 | ]
60 | }
61 | ]
--------------------------------------------------------------------------------
/demo/data_to_be_packaged.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/2shady4u/godot-sqlite/e1e2f9d8e7e075fb612ce9f436b587569efd4a7d/demo/data_to_be_packaged.db
--------------------------------------------------------------------------------
/demo/database.gd:
--------------------------------------------------------------------------------
1 | extends Node
2 |
3 | var db : SQLite = null
4 |
5 | const verbosity_level : int = SQLite.VERBOSE
6 |
7 | var db_name := "res://data/test"
8 | var packaged_db_name := "res://data_to_be_packaged"
9 | var peristent_db_name := "user://my_database"
10 | var json_name := "res://data/test_backup"
11 |
12 | var table_name := "company"
13 | var other_table_name := "expenses"
14 | var packaged_table_name = "creatures"
15 |
16 | var ids := [1,2,3,4,5,6,7]
17 | var names := ["Paul","Allen","Teddy","Mark","Robert","Julia","Amanda"]
18 | var ages := [32,25,23,25,30,63,13]
19 | var addresses := ["California","Texas","Baltimore","Richmond","Texas","Atlanta","New-York"]
20 | var salaries := [20000.00,15000.00,20000.00,65000.00,65000.00,65000.00,65000.00]
21 |
22 | var percentage_above_thirty := 0.05
23 | var percentage_below_thirty := 0.1
24 | var doomed_city := "Texas"
25 |
26 | signal output_received(text)
27 | signal texture_received(texture)
28 |
29 | func _ready():
30 | if OS.get_name() in ["Android", "iOS", "Web"]:
31 | copy_data_to_user()
32 | db_name = "user://data/test"
33 | json_name = "user://data/test_backup"
34 |
35 | # Enable/disable examples here:
36 | example_of_basic_database_querying()
37 | example_of_in_memory_and_foreign_key_support()
38 | example_of_call_external_functions()
39 | example_of_blob_io()
40 | example_of_read_only_database()
41 | example_of_database_persistency()
42 | example_of_fts5_usage()
43 |
44 | func cprint(text : String) -> void:
45 | print(text)
46 | output_received.emit(text)
47 |
48 | func copy_data_to_user() -> void:
49 | var data_path := "res://data"
50 | var copy_path := "user://data"
51 |
52 | DirAccess.make_dir_absolute(copy_path)
53 | var dir = DirAccess.open(data_path)
54 | if dir:
55 | dir.list_dir_begin();
56 | var file_name = dir.get_next()
57 | while (file_name != ""):
58 | if dir.current_is_dir():
59 | pass
60 | else:
61 | cprint("Copying " + file_name + " to /user-folder")
62 | dir.copy(data_path + "/" + file_name, copy_path + "/" + file_name)
63 | file_name = dir.get_next()
64 | else:
65 | cprint("An error occurred when trying to access the path.")
66 |
67 | # Basic example that goes over all the basic features available in the addon, such
68 | # as creating and dropping tables, inserting and deleting rows and doing more elementary
69 | # PRAGMA queries.
70 | func example_of_basic_database_querying():
71 |
72 | # Make a big table containing the variable types.
73 | var table_dict : Dictionary = Dictionary()
74 | table_dict["id"] = {"data_type":"int", "primary_key": true, "not_null": true}
75 | table_dict["name"] = {"data_type":"text", "not_null": true}
76 | table_dict["age"] = {"data_type":"int", "not_null": true}
77 | table_dict["address"] = {"data_type":"char(50)"}
78 | table_dict["salary"] = {"data_type":"real"}
79 |
80 | db = SQLite.new()
81 | db.path = db_name
82 | db.verbosity_level = verbosity_level
83 | # Open the database using the db_name found in the path variable
84 | db.open_db()
85 | # Throw away any table that was already present
86 | db.drop_table(table_name)
87 | # Create a table with the structure found in table_dict and add it to the database
88 | db.create_table(table_name, table_dict)
89 |
90 | var row_array : Array = []
91 | var row_dict : Dictionary = Dictionary()
92 | for i in range(0,ids.size()):
93 | row_dict["id"] = ids[i]
94 | row_dict["name"] = names[i]
95 | row_dict["age"] = ages[i]
96 | row_dict["address"] = addresses[i]
97 | row_dict["salary"] = salaries[i]
98 | row_array.append(row_dict.duplicate())
99 |
100 | # Insert a new row in the table
101 | db.insert_row(table_name, row_dict)
102 | row_dict.clear()
103 | #cprint(row_array)
104 |
105 | # Select the id and age of the employees that are older than 30
106 | var select_condition : String = "age > 30"
107 | var selected_array : Array = db.select_rows(table_name, select_condition, ["id", "age"])
108 | cprint("condition: " + select_condition)
109 | cprint("result: {0}".format([str(selected_array)]))
110 |
111 | # Change name of 'Amanda' to 'Olga' and her age to 30
112 | db.update_rows(table_name, "name = 'Amanda'", {"AGE":30, "NAME":"Olga"})
113 |
114 | # Select the employee with the name Olga and with age 30
115 | select_condition = "name = 'Olga' and age = 30"
116 | selected_array = db.select_rows(table_name, select_condition, ["*"])
117 | cprint("condition: " + select_condition)
118 | cprint("result: {0}".format([str(selected_array)]))
119 |
120 | # Delete the employee named Olga
121 | db.delete_rows(table_name, "name = 'Olga'")
122 |
123 | # Select all employees
124 | select_condition = ""
125 | selected_array = db.select_rows(table_name, select_condition, ["*"])
126 | cprint("condition: " + select_condition)
127 | cprint("result: {0}".format([str(selected_array)]))
128 | # Check the types of the values in the dictionary
129 | cprint("Types of selected columns:")
130 | cprint("salary: {0}".format([typeof(selected_array[0]["salary"])]))
131 | cprint("age: {0}".format([typeof(selected_array[0]["age"])]))
132 | cprint("name: {0}".format([typeof(selected_array[0]["name"])]))
133 |
134 | # Delete all employees
135 | db.delete_rows(table_name, "*")
136 |
137 | # Add all employees again
138 | db.insert_rows(table_name, row_array)
139 |
140 | # Do a normal query
141 | db.query("SELECT COUNT(*) AS 'number_of_employees' FROM " + table_name + ";")
142 | cprint("There are {0} employees in the company".format([db.query_result[0]["number_of_employees"]]))
143 |
144 | db.query("PRAGMA encoding;")
145 | cprint("Current database encoding is: {0}".format([db.query_result[0]["encoding"]]))
146 |
147 | # Create a TRIGGER and trigger it!
148 | db.query("CREATE TRIGGER increase_salary_after_employee_termination AFTER DELETE ON " + table_name + " BEGIN UPDATE " + table_name + " SET salary = salary + 100;END;")
149 |
150 | db.select_rows(table_name, "", ["name", "salary"])
151 | cprint("employees: {0}".format([str(db.query_result_by_reference)]))
152 |
153 | cprint("Firing that slacker Paul!")
154 | db.delete_rows(table_name, "name = 'Paul'")
155 |
156 | db.select_rows(table_name, "", ["name", "salary"])
157 | cprint("employees: {0}".format([str(db.query_result_by_reference)]))
158 |
159 | # Create a VIEW and use it!
160 | db.query("CREATE VIEW cheapest_employee AS SELECT id, name FROM " + table_name + " WHERE salary = (SELECT MIN(salary) FROM company) LIMIT 1;")
161 |
162 | # Fire the cheapest employee!
163 | cprint("Firing the cheapest employee!")
164 | db.delete_rows(table_name, "id = (SELECT id FROM cheapest_employee)")
165 |
166 | db.select_rows(table_name, "", ["name", "salary"])
167 | cprint("employees: {0}".format([str(db.query_result_by_reference)]))
168 |
169 | # Create an INDEX!
170 | db.query("CREATE INDEX idx_name ON " + table_name + "(name);")
171 |
172 | # Export the table to a json-file with a specified name
173 | db.export_to_json(json_name + "_new")
174 |
175 | # Close the current database
176 | db.close_db()
177 |
178 | # Import (and, consequently, open) a database from an old backup json-file
179 | cprint("Overwriting database content with old backup...")
180 | db.import_from_json(json_name + "_old")
181 |
182 | # Check which employees were present in this old json-file
183 | select_condition = ""
184 | selected_array = db.select_rows(table_name, select_condition, ["*"])
185 | cprint("condition: " + select_condition)
186 | cprint("result: {0}".format([str(selected_array)]))
187 | # Check the types of the values in the dictionary
188 | cprint("Types of selected columns:")
189 | cprint("salary: {0}".format([typeof(selected_array[0]["salary"])]))
190 | cprint("age: {0}".format([typeof(selected_array[0]["age"])]))
191 | cprint("name: {0}".format([typeof(selected_array[0]["name"])]))
192 |
193 | # Import the data (in a destructive manner) from the new backup json-file
194 | cprint("Overwriting database content again with latest backup...")
195 | db.import_from_json(json_name + "_new")
196 |
197 | db.query("SELECT * FROM sqlite_master;")
198 | cprint(str(db.query_result_by_reference))
199 |
200 | # Try to delete a non-existant table from the database.
201 | if not db.delete_rows(other_table_name, "*"):
202 | cprint("SQL error: " + db.error_message)
203 |
204 | # Close the imported database
205 | db.close_db()
206 |
207 | # This example demonstrates the in-memory and foreign key support. It's
208 | # rather contrived, but it gets the point across.
209 | func example_of_in_memory_and_foreign_key_support():
210 |
211 | # Create the database as usual.
212 | db = SQLite.new()
213 | # Enable in-memory storage.
214 | db.path = ":memory:"
215 | db.verbosity_level = verbosity_level
216 | # Enable foreign keys.
217 | db.foreign_keys = true
218 | # Open the database as usual.
219 | db.open_db()
220 |
221 | # Create a table for all your friends.
222 | db.create_table("friends", {
223 | "id": {"data_type": "int", "primary_key": true, "not_null": true},
224 | "name": {"data_type": "text", "not_null": true, "unique": true},
225 | "hobby": {"data_type": "int", "foreign_key": "hobbies.id", "not_null": true}
226 | })
227 |
228 | # Create a table for all your friends' hobbies.
229 | db.create_table("hobbies", {
230 | "id": {"data_type": "int", "primary_key": true, "not_null": true},
231 | "description": {"data_type": "text", "not_null": true, "unique": true}
232 | })
233 |
234 | # ATTENTION: The important thing to note about the "friends" table is the
235 | # definition of the foreign key "hobbies.id". This tells SQLITE to enforce
236 | # the foreign key constraint, and that the field "friends.hobby" is now
237 | # tied to the field "hobbies.id". Consequently, you are now required to
238 | # specify a valid hobby when adding a friend to the database, which in
239 | # turn means you first need to add some hobbies to the database before
240 | # you can add any of your friends and assign them a hobby.
241 |
242 | # This won't work! There is no valid hobby with id 23 yet!
243 | db.insert_rows("friends", [
244 | {"id": 1, "name": "John", "hobby": 23}
245 | ])
246 |
247 | # This will work! You create the hobby with id 23 first, then you can
248 | # create your friend referencing that hobby.
249 | db.insert_rows("hobbies", [
250 | {"id": 23, "description": "Extreme Relaxing"}
251 | ])
252 | db.insert_rows("friends", [
253 | {"id": 1, "name": "John", "hobby": 23}
254 | ])
255 |
256 | # Close the database.
257 | db.close_db()
258 |
259 | func should_employee_be_fired(address : String) -> bool:
260 | if address == doomed_city:
261 | return true
262 | else:
263 | return false
264 |
265 | func increase_wages(salary : float, age : int) -> float:
266 | if age > 30:
267 | return (1.0 + percentage_above_thirty)*salary
268 | else:
269 | return (1.0 + percentage_below_thirty)*salary
270 |
271 | func example_of_call_external_functions():
272 | # Make a big table containing the variable types.
273 | var table_dict : Dictionary = Dictionary()
274 | table_dict["id"] = {"data_type":"int", "primary_key": true, "not_null": true}
275 | table_dict["name"] = {"data_type":"text", "not_null": true}
276 | table_dict["age"] = {"data_type":"int", "not_null": true}
277 | table_dict["address"] = {"data_type":"char(50)"}
278 | table_dict["salary"] = {"data_type":"real"}
279 |
280 | db = SQLite.new()
281 | db.path = db_name
282 | db.verbosity_level = verbosity_level
283 | # Open the database using the db_name found in the path variable
284 | db.open_db()
285 | # Throw away any table that was already present
286 | db.drop_table(table_name)
287 | # Create a table with the structure found in table_dict and add it to the database
288 | db.create_table(table_name, table_dict)
289 |
290 | var row_array : Array = []
291 | var row_dict : Dictionary = Dictionary()
292 | for i in range(0,ids.size()):
293 | row_dict["id"] = ids[i]
294 | row_dict["name"] = names[i]
295 | row_dict["age"] = ages[i]
296 | row_dict["address"] = addresses[i]
297 | row_dict["salary"] = salaries[i]
298 | row_array.append(row_dict.duplicate())
299 |
300 | # Insert a new row in the table
301 | db.insert_row(table_name, row_dict)
302 | row_dict.clear()
303 |
304 | var callable := Callable(self, "should_employee_be_fired")
305 | db.create_function("should_employee_be_fired", callable, 1)
306 |
307 | callable = Callable(self, "increase_wages")
308 | db.create_function("increase_wages", callable, 2)
309 |
310 | db.query("UPDATE company SET salary = increase_wages(salary, age);")
311 | db.query("DELETE FROM company WHERE should_employee_be_fired(address);")
312 |
313 | var select_condition := ""
314 | var selected_array : Array = db.select_rows(table_name, select_condition, ["id", "salary", "name"])
315 | cprint("result: {0}".format([str(selected_array)]))
316 |
317 | # The BLOB-datatype is useful when lots of raw data has to be stored.
318 | # For example images fall into this category!
319 | func example_of_blob_io():
320 | # Make a big table containing the variable types.
321 | var table_dict : Dictionary = Dictionary()
322 | table_dict["id"] = {"data_type":"int", "primary_key": true, "not_null": true}
323 | table_dict["data"] = {"data_type":"blob", "not_null": true}
324 |
325 | var texture := preload("res://icon.png")
326 | texture_received.emit(texture)
327 | var tex_data : PackedByteArray = texture.get_image().save_png_to_buffer()
328 |
329 | db = SQLite.new()
330 | db.path = db_name
331 | db.verbosity_level = verbosity_level
332 | # Open the database using the db_name found in the path variable
333 | db.open_db()
334 | # Throw away any table that was already present
335 | db.drop_table(table_name)
336 | # Create a table with the structure found in table_dict and add it to the database
337 | db.create_table(table_name, table_dict)
338 |
339 | # Insert a new row in the table and bind the texture data to the data column.
340 | db.insert_row(table_name, {"id": 1, "data": tex_data})
341 |
342 | var selected_array : Array = db.select_rows(table_name, "", ["data"])
343 | for selected_row in selected_array:
344 | var selected_data = selected_row.get("data", PackedByteArray())
345 |
346 | var image := Image.new()
347 | var _error : int = image.load_png_from_buffer(selected_data)
348 | var loaded_texture := ImageTexture.create_from_image(image)
349 | texture_received.emit(loaded_texture)
350 |
351 | # Export the table to a json-file and automatically encode BLOB data to base64.
352 | db.export_to_json(json_name + "_base64_new")
353 |
354 | # Import again!
355 | db.import_from_json(json_name + "_base64_old")
356 |
357 | # Check out the 'old' icon stored in this backup file!
358 | selected_array = db.select_rows(table_name, "", ["data"])
359 | for selected_row in selected_array:
360 | var selected_data = selected_row.get("data", PackedByteArray())
361 |
362 | var image := Image.new()
363 | var _error : int = image.load_png_from_buffer(selected_data)
364 | var loaded_texture := ImageTexture.create_from_image(image)
365 | texture_received.emit(loaded_texture)
366 |
367 | # Close the current database
368 | db.close_db()
369 |
370 | func regexp(pattern : String, subject : String) -> bool:
371 | var regex = RegEx.new()
372 | regex.compile(pattern)
373 | var result = regex.search(subject)
374 | if result:
375 | return true
376 | else:
377 | return false
378 |
379 | # Example of accessing a packaged database by using the custom Virtual File System (VFS)
380 | # which allows packaged databases to be opened in read_only modus.
381 | # Databases used in this way can be added to the project's export filters
382 | # and will be readable, but not writable, by Godot.
383 | func example_of_read_only_database():
384 | db = SQLite.new()
385 | db.path = packaged_db_name
386 | db.verbosity_level = verbosity_level
387 | db.read_only = true
388 |
389 | db.open_db()
390 |
391 | var callable := Callable(self, "regexp")
392 | db.create_function("regexp", callable, 2)
393 |
394 | # Select all the creatures
395 | var select_condition : String = ""
396 | var selected_array : Array = db.select_rows(packaged_table_name, select_condition, ["*"])
397 | cprint("condition: " + select_condition)
398 | cprint("result: {0}".format([str(selected_array)]))
399 |
400 | # Select all the creatures that start with the letter 'b'
401 | select_condition = "name LIKE 'b%'"
402 | selected_array = db.select_rows(packaged_table_name, select_condition, ["name"])
403 | cprint("condition: " + select_condition)
404 | cprint("Following creatures start with the letter 'b':")
405 | for row in selected_array:
406 | cprint("* " + row["name"])
407 |
408 | # Do the same thing by using the REGEXP operator
409 | # This function has to be user-defined as discussed here:
410 | # https://www.sqlite.org/lang_expr.html#regexp
411 | select_condition = "name REGEXP '^s.*'"
412 | selected_array = db.select_rows(packaged_table_name, select_condition, ["name"])
413 | cprint("condition: " + select_condition)
414 | cprint("Following creatures start with the letter 's':")
415 | for row in selected_array:
416 | cprint("* " + row["name"])
417 |
418 | # Open another simultanous database connection in read-only mode.
419 | var other_db = SQLite.new()
420 | other_db.path = packaged_db_name
421 | other_db.verbosity_level = verbosity_level
422 | other_db.read_only = true
423 |
424 | other_db.open_db()
425 |
426 | # Get the experience you would get by kiling a mimic.
427 | select_condition = "name = 'mimic'"
428 | selected_array = other_db.select_rows(packaged_table_name, select_condition, ["experience"])
429 | cprint("Killing a mimic yields " + str(selected_array[0]["experience"]) + " experience points!")
430 |
431 | # Close the current database
432 | db.close_db()
433 |
434 | func example_of_database_persistency():
435 | var table_dict : Dictionary = Dictionary()
436 | table_dict["id"] = {"data_type":"int", "primary_key": true, "not_null": true}
437 | table_dict["count"] = {"data_type":"int", "not_null": true, "default": 0}
438 |
439 | db = SQLite.new()
440 | db.path = peristent_db_name
441 | db.verbosity_level = verbosity_level
442 | db.open_db()
443 | db.create_table(table_name, table_dict)
444 |
445 | # Does the row already exist?
446 | db.select_rows(table_name, "id = 1", ["count"])
447 | var query_result : Array = db.query_result
448 | var count : int = 0
449 | if query_result.is_empty():
450 | # It doesn't exist yet! Add it!
451 | db.insert_row(table_name, {"id": 1, "count": 0})
452 | else:
453 | var result : Dictionary = query_result[0]
454 | count = int(result.get("count", count))
455 |
456 | cprint("Count is: {0}".format([count]))
457 |
458 | # Increment the value for the next time!
459 | db.update_rows(table_name, "id = 1", {"count": count + 1 })
460 |
461 | # Close the current database
462 | db.close_db()
463 |
464 | var fts5_table_name := "posts"
465 |
466 | # Basic example that showcases seaching functionalities of FTS5...
467 | func example_of_fts5_usage():
468 | db = SQLite.new()
469 | if not db.compileoption_used("ENABLE_FTS5"):
470 | cprint("No support for FTS5 available in binaries (re-compile with compile option `enable_fts5=yes`)")
471 | return
472 |
473 | db.path = db_name
474 | db.verbosity_level = verbosity_level
475 | # Open the database using the db_name found in the path variable
476 | db.open_db()
477 | db.drop_table(fts5_table_name)
478 |
479 | db.query("CREATE VIRTUAL TABLE " + fts5_table_name + " USING FTS5(title, body);")
480 |
481 | var row_array := [
482 | {"title":'Learn SQlite FTS5', "body":'This tutorial teaches you how to perform full-text search in SQLite using FTS5'},
483 | {"title":'Advanced SQlite Full-text Search', "body":'Show you some advanced techniques in SQLite full-text searching'},
484 | {"title":'SQLite Tutorial', "body":'Help you learn SQLite quickly and effectively'},
485 | ]
486 |
487 | db.insert_rows(fts5_table_name, row_array)
488 |
489 | db.query("SELECT * FROM " + fts5_table_name + " WHERE posts MATCH 'fts5';")
490 | cprint("result: {0}".format([str(db.query_result)]))
491 |
492 | db.query("SELECT * FROM " + fts5_table_name + " WHERE posts MATCH 'learn SQLite';")
493 | cprint("result: {0}".format([str(db.query_result)]))
494 |
495 | # Close the current database
496 | db.close_db()
497 |
--------------------------------------------------------------------------------
/demo/database.gd.uid:
--------------------------------------------------------------------------------
1 | uid://c2ckll61v16xo
2 |
--------------------------------------------------------------------------------
/demo/default_env.tres:
--------------------------------------------------------------------------------
1 | [gd_resource type="Environment" load_steps=2 format=2]
2 |
3 | [sub_resource type="ProceduralSky" id=1]
4 |
5 | [resource]
6 | background_mode = 2
7 | background_sky = SubResource( 1 )
8 |
--------------------------------------------------------------------------------
/demo/export_data.gd:
--------------------------------------------------------------------------------
1 | extends SceneTree
2 |
3 | var data_path = "res://data"
4 | var copy_path = "res://build/data"
5 |
6 | func _init():
7 | var dir = DirAccess.open(data_path)
8 | if DirAccess.get_open_error() == OK:
9 | dir.make_dir_recursive(copy_path)
10 | dir.list_dir_begin();
11 | var file_name = dir.get_next()
12 | while (file_name != ""):
13 | if dir.current_is_dir():
14 | pass
15 | else:
16 | print("Copying " + file_name + " to /build-folder")
17 | dir.copy(data_path + "/" + file_name, copy_path + "/" + file_name)
18 | file_name = dir.get_next()
19 | else:
20 | print("An error occurred when trying to access the path.")
21 | quit()
22 |
--------------------------------------------------------------------------------
/demo/export_data.gd.uid:
--------------------------------------------------------------------------------
1 | uid://dmfwiq4ik3vos
2 |
--------------------------------------------------------------------------------
/demo/export_presets.cfg:
--------------------------------------------------------------------------------
1 | [preset.0]
2 |
3 | name="Windows Desktop"
4 | platform="Windows Desktop"
5 | runnable=true
6 | dedicated_server=false
7 | custom_features=""
8 | export_filter="all_resources"
9 | include_filter="data_to_be_packaged.db"
10 | exclude_filter="data/*"
11 | export_path=""
12 | encryption_include_filters=""
13 | encryption_exclude_filters=""
14 | encrypt_pck=false
15 | encrypt_directory=false
16 |
17 | [preset.0.options]
18 |
19 | custom_template/debug=""
20 | custom_template/release=""
21 | debug/export_console_wrapper=1
22 | binary_format/embed_pck=false
23 | texture_format/bptc=false
24 | texture_format/s3tc=true
25 | texture_format/etc=false
26 | texture_format/etc2=false
27 | binary_format/architecture="x86_64"
28 | codesign/enable=false
29 | codesign/timestamp=true
30 | codesign/timestamp_server_url=""
31 | codesign/digest_algorithm=1
32 | codesign/description=""
33 | codesign/custom_options=PackedStringArray()
34 | application/modify_resources=true
35 | application/icon=""
36 | application/console_wrapper_icon=""
37 | application/icon_interpolation=4
38 | application/file_version=""
39 | application/product_version=""
40 | application/company_name=""
41 | application/product_name=""
42 | application/file_description=""
43 | application/copyright=""
44 | application/trademarks=""
45 | application/export_angle=0
46 | ssh_remote_deploy/enabled=false
47 | ssh_remote_deploy/host="user@host_ip"
48 | ssh_remote_deploy/port="22"
49 | ssh_remote_deploy/extra_args_ssh=""
50 | ssh_remote_deploy/extra_args_scp=""
51 | ssh_remote_deploy/run_script="Expand-Archive -LiteralPath '{temp_dir}\\{archive_name}' -DestinationPath '{temp_dir}'
52 | $action = New-ScheduledTaskAction -Execute '{temp_dir}\\{exe_name}' -Argument '{cmd_args}'
53 | $trigger = New-ScheduledTaskTrigger -Once -At 00:00
54 | $settings = New-ScheduledTaskSettingsSet
55 | $task = New-ScheduledTask -Action $action -Trigger $trigger -Settings $settings
56 | Register-ScheduledTask godot_remote_debug -InputObject $task -Force:$true
57 | Start-ScheduledTask -TaskName godot_remote_debug
58 | while (Get-ScheduledTask -TaskName godot_remote_debug | ? State -eq running) { Start-Sleep -Milliseconds 100 }
59 | Unregister-ScheduledTask -TaskName godot_remote_debug -Confirm:$false -ErrorAction:SilentlyContinue"
60 | ssh_remote_deploy/cleanup_script="Stop-ScheduledTask -TaskName godot_remote_debug -ErrorAction:SilentlyContinue
61 | Unregister-ScheduledTask -TaskName godot_remote_debug -Confirm:$false -ErrorAction:SilentlyContinue
62 | Remove-Item -Recurse -Force '{temp_dir}'"
63 |
64 | [preset.1]
65 |
66 | name="Android"
67 | platform="Android"
68 | runnable=true
69 | dedicated_server=false
70 | custom_features=""
71 | export_filter="all_resources"
72 | include_filter="*.db, *.json"
73 | exclude_filter=""
74 | export_path=""
75 | encryption_include_filters=""
76 | encryption_exclude_filters=""
77 | encrypt_pck=false
78 | encrypt_directory=false
79 |
80 | [preset.1.options]
81 |
82 | custom_template/debug=""
83 | custom_template/release=""
84 | gradle_build/use_gradle_build=false
85 | gradle_build/export_format=0
86 | gradle_build/min_sdk=""
87 | gradle_build/target_sdk=""
88 | architectures/armeabi-v7a=false
89 | architectures/arm64-v8a=true
90 | architectures/x86=false
91 | architectures/x86_64=true
92 | version/code=1
93 | version/name="1.0"
94 | package/unique_name="org.godotengine.gdsqlite"
95 | package/name=""
96 | package/signed=true
97 | package/app_category=2
98 | package/retain_data_on_uninstall=false
99 | package/exclude_from_recents=false
100 | package/show_in_android_tv=false
101 | package/show_in_app_library=true
102 | package/show_as_launcher_app=false
103 | launcher_icons/main_192x192=""
104 | launcher_icons/adaptive_foreground_432x432=""
105 | launcher_icons/adaptive_background_432x432=""
106 | graphics/opengl_debug=false
107 | xr_features/xr_mode=0
108 | screen/immersive_mode=true
109 | screen/support_small=true
110 | screen/support_normal=true
111 | screen/support_large=true
112 | screen/support_xlarge=true
113 | user_data_backup/allow=false
114 | command_line/extra_args=""
115 | apk_expansion/enable=false
116 | apk_expansion/SALT=""
117 | apk_expansion/public_key=""
118 | permissions/custom_permissions=PackedStringArray()
119 | permissions/access_checkin_properties=false
120 | permissions/access_coarse_location=false
121 | permissions/access_fine_location=false
122 | permissions/access_location_extra_commands=false
123 | permissions/access_mock_location=false
124 | permissions/access_network_state=false
125 | permissions/access_surface_flinger=false
126 | permissions/access_wifi_state=false
127 | permissions/account_manager=false
128 | permissions/add_voicemail=false
129 | permissions/authenticate_accounts=false
130 | permissions/battery_stats=false
131 | permissions/bind_accessibility_service=false
132 | permissions/bind_appwidget=false
133 | permissions/bind_device_admin=false
134 | permissions/bind_input_method=false
135 | permissions/bind_nfc_service=false
136 | permissions/bind_notification_listener_service=false
137 | permissions/bind_print_service=false
138 | permissions/bind_remoteviews=false
139 | permissions/bind_text_service=false
140 | permissions/bind_vpn_service=false
141 | permissions/bind_wallpaper=false
142 | permissions/bluetooth=false
143 | permissions/bluetooth_admin=false
144 | permissions/bluetooth_privileged=false
145 | permissions/brick=false
146 | permissions/broadcast_package_removed=false
147 | permissions/broadcast_sms=false
148 | permissions/broadcast_sticky=false
149 | permissions/broadcast_wap_push=false
150 | permissions/call_phone=false
151 | permissions/call_privileged=false
152 | permissions/camera=false
153 | permissions/capture_audio_output=false
154 | permissions/capture_secure_video_output=false
155 | permissions/capture_video_output=false
156 | permissions/change_component_enabled_state=false
157 | permissions/change_configuration=false
158 | permissions/change_network_state=false
159 | permissions/change_wifi_multicast_state=false
160 | permissions/change_wifi_state=false
161 | permissions/clear_app_cache=false
162 | permissions/clear_app_user_data=false
163 | permissions/control_location_updates=false
164 | permissions/delete_cache_files=false
165 | permissions/delete_packages=false
166 | permissions/device_power=false
167 | permissions/diagnostic=false
168 | permissions/disable_keyguard=false
169 | permissions/dump=false
170 | permissions/expand_status_bar=false
171 | permissions/factory_test=false
172 | permissions/flashlight=false
173 | permissions/force_back=false
174 | permissions/get_accounts=false
175 | permissions/get_package_size=false
176 | permissions/get_tasks=false
177 | permissions/get_top_activity_info=false
178 | permissions/global_search=false
179 | permissions/hardware_test=false
180 | permissions/inject_events=false
181 | permissions/install_location_provider=false
182 | permissions/install_packages=false
183 | permissions/install_shortcut=false
184 | permissions/internal_system_window=false
185 | permissions/internet=false
186 | permissions/kill_background_processes=false
187 | permissions/location_hardware=false
188 | permissions/manage_accounts=false
189 | permissions/manage_app_tokens=false
190 | permissions/manage_documents=false
191 | permissions/manage_external_storage=false
192 | permissions/master_clear=false
193 | permissions/media_content_control=false
194 | permissions/modify_audio_settings=false
195 | permissions/modify_phone_state=false
196 | permissions/mount_format_filesystems=false
197 | permissions/mount_unmount_filesystems=false
198 | permissions/nfc=false
199 | permissions/persistent_activity=false
200 | permissions/post_notifications=false
201 | permissions/process_outgoing_calls=false
202 | permissions/read_calendar=false
203 | permissions/read_call_log=false
204 | permissions/read_contacts=false
205 | permissions/read_external_storage=false
206 | permissions/read_frame_buffer=false
207 | permissions/read_history_bookmarks=false
208 | permissions/read_input_state=false
209 | permissions/read_logs=false
210 | permissions/read_phone_state=false
211 | permissions/read_profile=false
212 | permissions/read_sms=false
213 | permissions/read_social_stream=false
214 | permissions/read_sync_settings=false
215 | permissions/read_sync_stats=false
216 | permissions/read_user_dictionary=false
217 | permissions/reboot=false
218 | permissions/receive_boot_completed=false
219 | permissions/receive_mms=false
220 | permissions/receive_sms=false
221 | permissions/receive_wap_push=false
222 | permissions/record_audio=false
223 | permissions/reorder_tasks=false
224 | permissions/restart_packages=false
225 | permissions/send_respond_via_message=false
226 | permissions/send_sms=false
227 | permissions/set_activity_watcher=false
228 | permissions/set_alarm=false
229 | permissions/set_always_finish=false
230 | permissions/set_animation_scale=false
231 | permissions/set_debug_app=false
232 | permissions/set_orientation=false
233 | permissions/set_pointer_speed=false
234 | permissions/set_preferred_applications=false
235 | permissions/set_process_limit=false
236 | permissions/set_time=false
237 | permissions/set_time_zone=false
238 | permissions/set_wallpaper=false
239 | permissions/set_wallpaper_hints=false
240 | permissions/signal_persistent_processes=false
241 | permissions/status_bar=false
242 | permissions/subscribed_feeds_read=false
243 | permissions/subscribed_feeds_write=false
244 | permissions/system_alert_window=false
245 | permissions/transmit_ir=false
246 | permissions/uninstall_shortcut=false
247 | permissions/update_device_stats=false
248 | permissions/use_credentials=false
249 | permissions/use_sip=false
250 | permissions/vibrate=false
251 | permissions/wake_lock=false
252 | permissions/write_apn_settings=false
253 | permissions/write_calendar=false
254 | permissions/write_call_log=false
255 | permissions/write_contacts=false
256 | permissions/write_external_storage=false
257 | permissions/write_gservices=false
258 | permissions/write_history_bookmarks=false
259 | permissions/write_profile=false
260 | permissions/write_secure_settings=false
261 | permissions/write_settings=false
262 | permissions/write_sms=false
263 | permissions/write_social_stream=false
264 | permissions/write_sync_settings=false
265 | permissions/write_user_dictionary=false
266 | xr_features/hand_tracking=0
267 | xr_features/hand_tracking_frequency=0
268 | xr_features/passthrough=0
269 |
270 | [preset.2]
271 |
272 | name="Web"
273 | platform="Web"
274 | runnable=true
275 | dedicated_server=false
276 | custom_features=""
277 | export_filter="all_resources"
278 | include_filter="*.db, *.json"
279 | exclude_filter=""
280 | export_path=""
281 | encryption_include_filters=""
282 | encryption_exclude_filters=""
283 | encrypt_pck=false
284 | encrypt_directory=false
285 |
286 | [preset.2.options]
287 |
288 | custom_template/debug=""
289 | custom_template/release=""
290 | variant/extensions_support=true
291 | vram_texture_compression/for_desktop=true
292 | vram_texture_compression/for_mobile=false
293 | html/export_icon=true
294 | html/custom_html_shell=""
295 | html/head_include=""
296 | html/canvas_resize_policy=2
297 | html/focus_canvas_on_start=true
298 | html/experimental_virtual_keyboard=false
299 | progressive_web_app/enabled=false
300 | progressive_web_app/offline_page=""
301 | progressive_web_app/display=1
302 | progressive_web_app/orientation=0
303 | progressive_web_app/icon_144x144=""
304 | progressive_web_app/icon_180x180=""
305 | progressive_web_app/icon_512x512=""
306 | progressive_web_app/background_color=Color(0, 0, 0, 1)
307 |
--------------------------------------------------------------------------------
/demo/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/2shady4u/godot-sqlite/e1e2f9d8e7e075fb612ce9f436b587569efd4a7d/demo/icon.png
--------------------------------------------------------------------------------
/demo/icon.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://ciimg3a4cvt6l"
6 | path="res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://icon.png"
14 | dest_files=["res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 |
--------------------------------------------------------------------------------
/demo/project.godot:
--------------------------------------------------------------------------------
1 | ; Engine configuration file.
2 | ; It's best edited using the editor UI and not directly,
3 | ; since the parameters that go here are not all obvious.
4 | ;
5 | ; Format:
6 | ; [section] ; section goes between []
7 | ; param=value ; assign values to parameters
8 |
9 | config_version=5
10 |
11 | [application]
12 |
13 | config/name="SQLite Demo"
14 | run/main_scene="res://Main.tscn"
15 | config/features=PackedStringArray("4.4")
16 | config/icon="res://icon.png"
17 |
18 | [debug]
19 |
20 | settings/stdout/verbose_stdout=true
21 |
22 | [editor_plugins]
23 |
24 | enabled=PackedStringArray()
25 |
26 | [rendering]
27 |
28 | textures/vram_compression/import_etc2_astc=true
29 | quality/driver/driver_name="GLES2"
30 | vram_compression/import_etc=true
31 | environment/default_environment="res://default_env.tres"
32 |
--------------------------------------------------------------------------------
/doc_classes/SQLite.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | A SQLite wrapper class implemented in GDExtension.
7 |
8 |
9 | [b]Example usage[/b]:
10 | [codeblock]
11 | extends Node
12 |
13 | var db = SQLite.new()
14 |
15 | func _ready():
16 | var table_name: String = "players"
17 | var table_dict: Dictionary = {
18 | "id": {"data_type":"int", "primary_key": true, "not_null": true, "auto_increment": true},
19 | "name": {"data_type":"text", "not_null": true},
20 | "portrait": {"data_type":"blob", "not_null": true}
21 | }
22 |
23 | db.path = "res://my_database"
24 | db.verbosity_level = SQLite.VerbosityLevel.NORMAL
25 | db.open_db()
26 |
27 | # Check if the table already exists or not.
28 | db.query_with_bindings("SELECT name FROM sqlite_master WHERE type='table' AND name=?;", [table_name])
29 | if not db.query_result.is_empty():
30 | db.drop_table(table_name)
31 | db.create_table(table_name, table_dict)
32 |
33 | var texture := preload("res://icon.png")
34 | var tex_data: PackedByteArray = texture.get_image().save_png_to_buffer()
35 | var row_dict: Dictionary = {
36 | "name": "Doomguy",
37 | "portrait": tex_data
38 | }
39 | db.insert_row(table_name, row_dict)
40 |
41 | db.select_rows(table_name, "name = 'Doomguy'", ["id", "name"])
42 | print(db.query_result)
43 | [/codeblock]
44 |
45 |
46 | https://github.com/2shady4u/godot-sqlite/blob/master/README.md
47 | https://github.com/2shady4u/godot-sqlite/blob/master/demo/database.gd
48 |
49 |
50 |
51 |
52 |
53 | Open a new database connection. Multiple concurrently open connections to the same database are possible.
54 |
55 |
56 |
57 |
58 |
59 | Close the current database connection.
60 |
61 |
62 |
63 |
64 |
65 | Query the database using the raw SQL statement defined in [code]query_string[/code].
66 |
67 |
68 |
69 |
70 |
71 | Binds the parameters contained in the [code]param_bindings[/code]-variable to the query. Using this function stops any possible attempts at SQL data injection as the parameters are sanitized. More information regarding parameter bindings can be found [url=https://www.sqlite.org/c3ref/bind_blob.html]here[/url].
72 | [b]Example usage[/b]:
73 | [codeblock]
74 | var column_name : String = "name"
75 | var query_string : String = "SELECT %s FROM company WHERE age < ?;" % [column_name]
76 | var param_bindings : Array = [24]
77 | var success = db.query_with_bindings(query_string, param_bindings)
78 | # Executes following query:
79 | # SELECT name FROM company WHERE age < 24;
80 | [/codeblock]
81 | Using bindings is optional, except for PackedByteArray (= raw binary data) which has to binded to allow the insertion and selection of BLOB data in the database.
82 | [i][b]NOTE:[/b] Binding column names is not possible due to SQLite restrictions. If dynamic column names are required, insert the column name directly into the [code]query_string[/code]-variable itself (see [url=https://github.com/2shady4u/godot-sqlite/issues/41]https://github.com/2shady4u/godot-sqlite/issues/41[/url]).[/i]
83 |
84 |
85 |
86 |
87 |
88 | Each key/value pair of the [code]table_dictionary[/code]-variable defines a column of the table. Each key defines the name of a column in the database, while the value is a dictionary that contains further column specifications.
89 | [b]Required fields[/b]:
90 | - [b]"data_type"[/b]: type of the column variable, following values are valid*:
91 | - "int" (SQLite: INTEGER, GODOT: [constant TYPE_INT])[br] - "real" (SQLite: REAL, GODOT: [constant TYPE_REAL])[br] - "text" (SQLite: TEXT, GODOT: [constant TYPE_STRING])[br] - "char(?)"** (SQLite: CHAR(?)**, GODOT: [constant TYPE_STRING])[br] - "blob" (SQLite: BLOB, GODOT: [constant TYPE_PACKED_BYTE_ARRAY])
92 | * [i]Data types not found in this list throw an error and end up finalizing the current SQLite statement.[/i][br] ** [i]with the question mark being replaced by the maximum amount of characters[/i]
93 | [b]Optional fields[/b]:
94 | - [b]"not_null"[/b] [i](default = false)[/i]: Is the NULL value an invalid value for this column?[br]- [b]"unique"[/b] [i](default = false)[/i]: Does the column have a unique constraint?[br]- [b]"default"[/b]: The default value of the column if not explicitly given.[br]- [b]"primary_key"[/b] [i](default = false)[/i]: Is this the primary key of this table?
95 | Multiple columns can be set as a primary key.
96 | - [b]"auto_increment"[/b] [i](default = false)[/i]: Automatically increment this column when no explicit value is given. This auto-generated value will be one more (+1) than the largest value currently in use.
97 | [i][b]NOTE[/b]: Auto-incrementing a column only works when this column is the primary key and no other columns are primary keys![/i]
98 | - [b]"foreign_key"[/b]: Enforce an "exist" relationship between tables by setting this variable to [code]foreign_table.foreign_column[/code]. In other words, when adding an additional row, the column value should be an existing value as found in the column with name [code]foreign_column[/code] of the table with name [code]foreign_table[/code].
99 | [i][b]NOTE[/b]: Availability of foreign keys has to be enabled by setting the [code]foreign_keys[/code]-variable to true BEFORE opening the database.[/i]
100 | [b]Example usage[/b]:
101 | [codeblock]
102 | # Add the row "id" to the table, which is an auto-incremented primary key.
103 | # When adding additional rows, this value can either by explicitely given or be unfilled.
104 | table_dictionary["id"] = {
105 | "data_type": "int",
106 | "primary_key": true,
107 | "auto_increment": true
108 | }
109 | [/codeblock]
110 | For more concrete usage examples see the [code]database.gd[/code]-file as found [url=https://github.com/2shady4u/godot-sqlite/blob/master/demo/database.gd]here[url].
111 |
112 |
113 |
114 |
115 |
116 | Drop the table with name [code]table_name[/code]. This method is equivalent to the following query:
117 | [codeblock]
118 | db.query("DROP TABLE "+ table_name + ";")
119 | [/codeblock]
120 |
121 |
122 |
123 |
124 |
125 | Each key/value pair of the [code]row_dictionary[/code]-variable defines the column values of a single row.
126 | Columns should adhere to the table schema as instantiated using the [code]table_dictionary[/code]-variable and are required if their corresponding [b]"not_null"[/b]-column value is set to [code]True[/code].
127 |
128 |
129 |
130 |
131 |
132 | Insert multiple rows into the given table. The [code]row_array[/code] input argument should be an array of dictionaries where each element is defined as in [method insert_row].
133 |
134 |
135 |
136 |
137 |
138 | Returns the results from the latest query [b]by value[/b]; meaning that this property does not get overwritten by any successive queries.
139 |
140 |
141 |
142 |
143 |
144 | With the [code]updated_row_dictionary[/code]-variable adhering to the same table schema & conditions as the [code]row_dictionary[/code]-variable defined previously.
145 |
146 |
147 |
148 |
149 |
150 | Delete all rows of the table that match the given conditions.
151 |
152 |
153 |
154 |
155 |
156 | Drops all database tables and imports the database structure and content present inside of [code]import_path.json[/code].
157 |
158 |
159 |
160 |
161 |
162 | Exports the database structure and content to [code]export_path.json[/code] as a backup or for ease of editing.
163 |
164 |
165 |
166 |
167 |
168 | Bind a [url=https://www.sqlite.org/appfunc.html]scalar SQL function[/url] to the database that can then be used in subsequent queries.
169 |
170 |
171 |
172 |
173 |
174 | Check if the given database connection is or is not in autocommit mode, see [url=https://sqlite.org/c3ref/get_autocommit.html]here[/url].
175 |
176 |
177 |
178 |
179 |
180 | Backup the current database to a path, see [url=https://www.sqlite.org/backup.html]here[/url]. This feature is useful if you are using a database as your save file and you want to easily implement a saving mechanic.
181 |
182 |
183 |
184 |
185 |
186 | Restore the current database from a path, see [url=https://www.sqlite.org/backup.html]here[/url]. This feature is useful if you are using a database as your save file and you want to easily implement a loading mechanic. Be warned that the original database will be overwritten entirely when restoring.
187 |
188 |
189 |
190 |
191 |
192 | Check if the binary was compiled using the specified option, see [url=https://sqlite.org/c3ref/compileoption_get.html]here[/url].
193 | Mostly relevant for checking if the [url=https://sqlite.org/fts5.html]SQLite FTS5 Extension[/url] is enabled, in which case the following lines can be used:
194 | [codeblock]
195 | db.compileoption_used("SQLITE_ENABLE_FTS5") # Returns '1' if enabled or '0' if disabled
196 | db.compileoption_used("ENABLE_FTS5") # The "SQLITE_"-prefix may be omitted.
197 | [/codeblock]
198 |
199 |
200 |
201 |
202 |
203 | [url=https://www.sqlite.org/c3ref/load_extension.html]Extension loading[/url] is disabled by default for security reasons. There are two ways to load an extension: C-API and SQL function. This method turns on both options.
204 | SQL function [code]load_extension()[/code] can only be used after enabling extension loading with this method. Preferably should be disabled after loading the extension to prevent SQL injections. Returns the SQLite return code.
205 |
206 | [codeblock]
207 | var module_path = ProjectSettings.globalize_path("res://addons/godot-sqlite/extensions/spellfix.dll")
208 | db.enable_load_extension(true)
209 | db.query_with_bindings(
210 | "select load_extension(?, ?);", [
211 | module_path,
212 | "sqlite3_spellfix_init"
213 | ])
214 | db.enable_load_extension(false)
215 | [/codeblock]
216 |
217 |
218 |
219 |
220 |
221 | Loads the extension in the given path. Does not require [method SQLite.enable_load_extension], as it only enables C-API during the call and disables it right after, utilizing the recommended extension loading method declared by the SQLite documentation ([url=https://www.sqlite.org/c3ref/load_extension.html]see[/url]). Returns the SQLite return code.
222 | - [b]extension_path:[/b] the path to the compiled binary of the extension
223 | - [b]entrypoint:[/b] the extension's entrypoint method (init function). It is defined in the .c file of the extension.
224 | Example for loading the spellfix module:
225 | [codeblock]
226 | db.load_extension("res://addons/godot-sqlite/extensions/spellfix.dll", "sqlite3_spellfix_init")
227 | [/codeblock]
228 |
229 |
230 |
231 |
232 |
233 | Path to the database, should be set before opening the database with [code]open_db()[/code]. If no database with this name exists, a new one at the supplied path will be created. Both [code]res://[/code] and [code]user://[/code] keywords can be used to define the path.
234 |
235 |
236 | Contains the zErrMsg returned by the SQLite query in human-readable form. An empty string corresponds with the case in which the query executed succesfully.
237 |
238 |
239 | Default extension that is automatically appended to the [code]path[/code]-variable whenever [b]no[/b] extension is detected/given.
240 | [i][b]NOTE:[/b] If database files without extension are desired, this variable has to be set to "" (= an empty string) as to skip this automatic procedure entirely.[/i]
241 |
242 |
243 | Enables or disables the availability of [url=https://www.sqlite.org/foreignkeys.html]foreign keys[/url] in the SQLite database.
244 |
245 |
246 | Enabling this property opens the database in read-only modus & allows databases to be packaged inside of the PCK. To make this possible, a custom [url=https://www.sqlite.org/vfs.html]VFS[/url] is employed which internally takes care of all the file handling using the Godot API.
247 |
248 |
249 | Contains the results from the latest query [b]by value[/b]; meaning that this property is safe to use when looping successive queries as it does not get overwritten by any future queries.
250 |
251 |
252 | Contains the results from the latest query [b]by reference[/b] and is, as a direct result, cleared and repopulated after every new query.
253 |
254 |
255 | Exposes the [code]sqlite3_last_insert_rowid()[/code]-method to Godot as described [url=https://www.sqlite.org/c3ref/last_insert_rowid.html]here[/url].
256 | Attempting to modify this variable directly is forbidden and throws an error.
257 |
258 |
259 | The verbosity_level determines the amount of logging to the Godot console that is handy for debugging your (possibly faulty) SQLite queries.
260 | [i][b]NOTE:[/b] [constant VERBOSE] and higher levels might considerably slow down your queries due to excessive logging.[/i]
261 |
262 |
263 |
264 |
265 |
266 |
267 | Don't print anything to the console.
268 |
269 |
270 | Print essential information to the console.
271 |
272 |
273 | Print additional information to the console.
274 |
275 |
276 | Same as [constant VERBOSE].
277 |
278 |
279 |
--------------------------------------------------------------------------------
/icon/godot-sqleet-icon-256x256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/2shady4u/godot-sqlite/e1e2f9d8e7e075fb612ce9f436b587569efd4a7d/icon/godot-sqleet-icon-256x256.png
--------------------------------------------------------------------------------
/icon/godot-sqleet-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/2shady4u/godot-sqlite/e1e2f9d8e7e075fb612ce9f436b587569efd4a7d/icon/godot-sqleet-icon.png
--------------------------------------------------------------------------------
/icon/godot-sqleet-icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
228 |
--------------------------------------------------------------------------------
/icon/godot-sqlite-banner-4.x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/2shady4u/godot-sqlite/e1e2f9d8e7e075fb612ce9f436b587569efd4a7d/icon/godot-sqlite-banner-4.x.png
--------------------------------------------------------------------------------
/icon/godot-sqlite-banner-v2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/2shady4u/godot-sqlite/e1e2f9d8e7e075fb612ce9f436b587569efd4a7d/icon/godot-sqlite-banner-v2.png
--------------------------------------------------------------------------------
/icon/godot-sqlite-banner-v2.svg:
--------------------------------------------------------------------------------
1 |
2 |
325 |
--------------------------------------------------------------------------------
/icon/godot-sqlite-banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/2shady4u/godot-sqlite/e1e2f9d8e7e075fb612ce9f436b587569efd4a7d/icon/godot-sqlite-banner.png
--------------------------------------------------------------------------------
/icon/godot-sqlite-banner.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
294 |
--------------------------------------------------------------------------------
/icon/godot-sqlite-icon-256x256-v2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/2shady4u/godot-sqlite/e1e2f9d8e7e075fb612ce9f436b587569efd4a7d/icon/godot-sqlite-icon-256x256-v2.png
--------------------------------------------------------------------------------
/icon/godot-sqlite-icon-256x256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/2shady4u/godot-sqlite/e1e2f9d8e7e075fb612ce9f436b587569efd4a7d/icon/godot-sqlite-icon-256x256.png
--------------------------------------------------------------------------------
/icon/godot-sqlite-icon-4.x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/2shady4u/godot-sqlite/e1e2f9d8e7e075fb612ce9f436b587569efd4a7d/icon/godot-sqlite-icon-4.x.png
--------------------------------------------------------------------------------
/icon/godot-sqlite-icon-4.x.svg:
--------------------------------------------------------------------------------
1 |
2 |
234 |
--------------------------------------------------------------------------------
/icon/godot-sqlite-icon-v2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/2shady4u/godot-sqlite/e1e2f9d8e7e075fb612ce9f436b587569efd4a7d/icon/godot-sqlite-icon-v2.png
--------------------------------------------------------------------------------
/icon/godot-sqlite-icon-v2.svg:
--------------------------------------------------------------------------------
1 |
2 |
270 |
--------------------------------------------------------------------------------
/icon/godot-sqlite-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/2shady4u/godot-sqlite/e1e2f9d8e7e075fb612ce9f436b587569efd4a7d/icon/godot-sqlite-icon.png
--------------------------------------------------------------------------------
/icon/godot-sqlite-icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
259 |
--------------------------------------------------------------------------------
/icon/godot-sqlite-social-preview-4.x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/2shady4u/godot-sqlite/e1e2f9d8e7e075fb612ce9f436b587569efd4a7d/icon/godot-sqlite-social-preview-4.x.png
--------------------------------------------------------------------------------
/misc/scripts/clang_format.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # This script runs clang-format on all relevant files in the repo.
4 | # This is the primary script responsible for fixing style violations.
5 |
6 | set -uo pipefail
7 |
8 | # Loops through all code files tracked by Git.
9 | git ls-files -- '*.c' '*.h' '*.cpp' '*.hpp' ':!:src/sqlite/*' |
10 | while read -r f; do
11 | # Run clang-format.
12 | clang-format --style=file:godot-cpp/.clang-format --Wno-error=unknown -i "$f"
13 | done
14 |
15 | diff=$(git diff --color)
16 |
17 | # If no patch has been generated all is OK, clean up, and exit.
18 | if [ -z "$diff" ] ; then
19 | printf "Files in this commit comply with the clang-tidy style rules.\n"
20 | exit 0
21 | fi
22 |
23 | # A patch has been created, notify the user, clean up, and exit.
24 | printf "\n*** The following changes have been made to comply with the formatting rules:\n\n"
25 | echo "$diff"
26 | printf "\n*** Please fix your commit(s) with 'git commit --amend' or 'git rebase -i '\n"
27 | exit 1
28 |
--------------------------------------------------------------------------------
/src/gdsqlite.h:
--------------------------------------------------------------------------------
1 | #ifndef SQLITE_CLASS_H
2 | #define SQLITE_CLASS_H
3 |
4 | #include
5 |
6 | #include
7 | #include
8 |
9 | #include
10 | #include
11 | #include
12 | #include
13 |
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include
19 | #include
20 | #include
21 |
22 | namespace godot {
23 | enum OBJECT_TYPE {
24 | TABLE,
25 | INDEX,
26 | VIEW,
27 | TRIGGER
28 | };
29 | struct object_struct {
30 | String name, sql;
31 | OBJECT_TYPE type;
32 | Array base64_columns, row_array;
33 | };
34 |
35 | class SQLite : public RefCounted {
36 | GDCLASS(SQLite, RefCounted)
37 |
38 | private:
39 | bool validate_json(const Array &import_json, std::vector &tables_to_import);
40 | bool validate_table_dict(const Dictionary &p_table_dict);
41 | int backup_database(sqlite3 *source_db, sqlite3 *destination_db);
42 | void remove_shadow_tables(Array &p_array);
43 |
44 | sqlite3 *db;
45 | std::vector> function_registry;
46 |
47 | int64_t verbosity_level = 1;
48 | bool foreign_keys = false;
49 | bool read_only = false;
50 | String path = "default";
51 | String error_message = "";
52 | String default_extension = "db";
53 | TypedArray query_result = TypedArray();
54 |
55 | protected:
56 | static void _bind_methods();
57 |
58 | public:
59 | // Constants.
60 | enum VerbosityLevel {
61 | QUIET = 0,
62 | NORMAL = 1,
63 | VERBOSE = 2,
64 | VERY_VERBOSE = 3
65 | };
66 |
67 | SQLite();
68 | ~SQLite();
69 |
70 | // Functions.
71 | bool open_db();
72 | bool close_db();
73 | bool query(const String &p_query);
74 | bool query_with_bindings(const String &p_query, Array param_bindings);
75 |
76 | bool create_table(const String &p_name, const Dictionary &p_table_dict);
77 | bool drop_table(const String &p_name);
78 |
79 | bool backup_to(String destination_path);
80 | bool restore_from(String source_path);
81 |
82 | bool insert_row(const String &p_name, const Dictionary &p_row_dict);
83 | bool insert_rows(const String &p_name, const Array &p_row_array);
84 |
85 | Array select_rows(const String &p_name, const String &p_conditions, const Array &p_columns_array);
86 | bool update_rows(const String &p_name, const String &p_conditions, const Dictionary &p_updated_row_dict);
87 | bool delete_rows(const String &p_name, const String &p_conditions);
88 |
89 | bool create_function(const String &p_name, const Callable &p_callable, int p_argc);
90 |
91 | bool import_from_json(String import_path);
92 | bool export_to_json(String export_path);
93 |
94 | int get_autocommit() const;
95 | int compileoption_used(const String &option_name) const;
96 |
97 | int load_extension(const String &p_path, const String &p_init_func_name);
98 | int enable_load_extension(const bool &p_onoff);
99 |
100 | // Properties.
101 | void set_last_insert_rowid(const int64_t &p_last_insert_rowid);
102 | int64_t get_last_insert_rowid() const;
103 |
104 | void set_verbosity_level(const int64_t &p_verbosity_level);
105 | int64_t get_verbosity_level() const;
106 |
107 | void set_foreign_keys(const bool &p_foreign_keys);
108 | bool get_foreign_keys() const;
109 |
110 | void set_read_only(const bool &p_read_only);
111 | bool get_read_only() const;
112 |
113 | void set_path(const String &p_path);
114 | String get_path() const;
115 |
116 | void set_error_message(const String &p_error_message);
117 | String get_error_message() const;
118 |
119 | void set_default_extension(const String &p_default_extension);
120 | String get_default_extension() const;
121 |
122 | void set_query_result(const TypedArray &p_query_result);
123 | TypedArray get_query_result() const;
124 |
125 | TypedArray get_query_result_by_reference() const;
126 | };
127 |
128 | } //namespace godot
129 |
130 | VARIANT_ENUM_CAST(SQLite::VerbosityLevel);
131 |
132 | #endif // ! SQLITE_CLASS_H
133 |
--------------------------------------------------------------------------------
/src/register_types.cpp:
--------------------------------------------------------------------------------
1 | #include "register_types.h"
2 |
3 | #include
4 |
5 | #include
6 | #include
7 | #include
8 |
9 | #include "gdsqlite.h"
10 |
11 | using namespace godot;
12 |
13 | void initialize_sqlite_module(ModuleInitializationLevel p_level) {
14 | if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
15 | return;
16 | }
17 |
18 | GDREGISTER_CLASS(SQLite);
19 | }
20 |
21 | void uninitialize_sqlite_module(ModuleInitializationLevel p_level) {
22 | if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
23 | return;
24 | }
25 | }
26 |
27 | extern "C" {
28 |
29 | // Initialization.
30 |
31 | GDExtensionBool GDE_EXPORT sqlite_library_init(GDExtensionInterfaceGetProcAddress p_get_proc_address, const GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization) {
32 | godot::GDExtensionBinding::InitObject init_obj(p_get_proc_address, p_library, r_initialization);
33 |
34 | init_obj.register_initializer(initialize_sqlite_module);
35 | init_obj.register_terminator(uninitialize_sqlite_module);
36 | init_obj.set_minimum_library_initialization_level(MODULE_INITIALIZATION_LEVEL_SCENE);
37 |
38 | return init_obj.init();
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/register_types.h:
--------------------------------------------------------------------------------
1 | #ifndef SQLITE_REGISTER_TYPES_H
2 | #define SQLITE_REGISTER_TYPES_H
3 |
4 | #include
5 | using namespace godot;
6 |
7 | void initialize_sqlite_module(ModuleInitializationLevel p_level);
8 | void uninitialize_sqlite_module(ModuleInitializationLevel p_level);
9 |
10 | #endif // ! SQLITE_REGISTER_TYPES_H
--------------------------------------------------------------------------------
/src/sqlite/LICENSE.md:
--------------------------------------------------------------------------------
1 | SQLite Is Public Domain
2 |
3 | All of the code and documentation in SQLite has been dedicated to the public domain by the authors. All code authors, and representatives of the companies they work for, have signed affidavits dedicating their contributions to the public domain and originals of those signed affidavits are stored in a firesafe at the main offices of Hwaci. Anyone is free to copy, modify, publish, use, compile, sell, or distribute the original SQLite code, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means.
4 |
5 | The previous paragraph applies to the deliverable code and documentation in SQLite - those parts of the SQLite library that you actually bundle and ship with a larger application. Some scripts used as part of the build process (for example the "configure" scripts generated by autoconf) might fall under other open-source licenses. Nothing from these build scripts ever reaches the final deliverable SQLite library, however, and so the licenses associated with those scripts should not be a factor in assessing your rights to copy and use the SQLite library.
6 |
7 | All of the deliverable code in SQLite has been written from scratch. No code has been taken from other projects or from the open internet. Every line of code can be traced back to its original author, and all of those authors have public domain dedications on file. So the SQLite code base is clean and is uncontaminated with licensed code from other projects.
8 |
--------------------------------------------------------------------------------
/src/vfs/gdsqlite_file.cpp:
--------------------------------------------------------------------------------
1 | #include "gdsqlite_file.h"
2 |
3 | using namespace godot;
4 |
5 | /*
6 | ** Close a file.
7 | */
8 | int gdsqlite_file::close(sqlite3_file *pFile) {
9 | gdsqlite_file *p = reinterpret_cast(pFile);
10 | ERR_FAIL_COND_V(!p->file->is_open(), SQLITE_IOERR_CLOSE);
11 |
12 | p->file->close();
13 | p->file.unref();
14 |
15 | return SQLITE_OK;
16 | }
17 |
18 | /*
19 | ** Read data from a file.
20 | */
21 | int gdsqlite_file::read(sqlite3_file *pFile, void *zBuf, int iAmt, sqlite_int64 iOfst) {
22 | gdsqlite_file *p = reinterpret_cast(pFile);
23 | ERR_FAIL_COND_V(!p->file->is_open(), SQLITE_IOERR_CLOSE);
24 |
25 | /* Seek the wanted position in the file */
26 | p->file->seek(iOfst);
27 | ERR_FAIL_COND_V(p->file->get_position() != iOfst, SQLITE_IOERR_READ);
28 |
29 | /* Read and populate the data */
30 | PackedByteArray arr = p->file->get_buffer(iAmt);
31 | memcpy(zBuf, arr.ptr(), iAmt);
32 |
33 | if (arr.size() == iAmt) {
34 | return SQLITE_OK;
35 | } else if (arr.size() >= 0) {
36 | return SQLITE_IOERR_SHORT_READ;
37 | }
38 |
39 | ERR_FAIL_V(SQLITE_IOERR_READ);
40 | }
41 |
42 | /*
43 | ** Write data to a file.
44 | */
45 | int gdsqlite_file::write(sqlite3_file *pFile, const void *zBuf, int iAmt, sqlite_int64 iOfst) {
46 | gdsqlite_file *p = reinterpret_cast(pFile);
47 | ERR_FAIL_COND_V(!p->file->is_open(), SQLITE_IOERR_CLOSE);
48 |
49 | /* Seek the wanted position in the file */
50 | p->file->seek(iOfst);
51 | ERR_FAIL_COND_V(p->file->get_position() != iOfst, SQLITE_IOERR_READ);
52 |
53 | /* Write the data to the file */
54 | PackedByteArray arr = PackedByteArray();
55 | arr.resize(iAmt);
56 | memcpy(arr.ptrw(), zBuf, iAmt);
57 | p->file->store_buffer(arr);
58 |
59 | /* Was the write succesful? */
60 | size_t bytes_written = p->file->get_position() - iOfst;
61 | ERR_FAIL_COND_V(bytes_written != iAmt, SQLITE_IOERR_WRITE);
62 |
63 | return SQLITE_OK;
64 | }
65 |
66 | /*
67 | ** Truncate a file. This is a no-op for this VFS.
68 | */
69 | int gdsqlite_file::truncate(sqlite3_file *pFile, sqlite_int64 size) {
70 | return SQLITE_OK;
71 | }
72 |
73 | /*
74 | ** Sync the contents of the file to the persistent media.
75 | */
76 | int gdsqlite_file::sync(sqlite3_file *pFile, int flags) {
77 | return SQLITE_OK;
78 | }
79 |
80 | /*
81 | ** Write the size of the file in bytes to *pSize.
82 | */
83 | int gdsqlite_file::fileSize(sqlite3_file *pFile, sqlite_int64 *pSize) {
84 | gdsqlite_file *p = reinterpret_cast(pFile);
85 | ERR_FAIL_COND_V(!p->file->is_open(), SQLITE_IOERR_CLOSE);
86 |
87 | *pSize = p->file->get_length();
88 |
89 | return SQLITE_OK;
90 | }
91 |
92 | /*
93 | ** Locking functions. The xLock() and xUnlock() methods are both no-ops.
94 | ** The xCheckReservedLock() always indicates that no other process holds
95 | ** a reserved lock on the database file. This ensures that if a hot-journal
96 | ** file is found in the file-system it is rolled back.
97 | */
98 | int gdsqlite_file::lock(sqlite3_file *pFile, int eLock) {
99 | return SQLITE_OK;
100 | }
101 | int gdsqlite_file::unlock(sqlite3_file *pFile, int eLock) {
102 | return SQLITE_OK;
103 | }
104 | int gdsqlite_file::checkReservedLock(sqlite3_file *pFile, int *pResOut) {
105 | *pResOut = 0;
106 | return SQLITE_OK;
107 | }
108 |
109 | /*
110 | ** No xFileControl() verbs are implemented by this VFS.
111 | */
112 | int gdsqlite_file::fileControl(sqlite3_file *pFile, int op, void *pArg) {
113 | return SQLITE_NOTFOUND;
114 | }
115 |
116 | /*
117 | ** The xSectorSize() and xDeviceCharacteristics() methods. These two
118 | ** may return special values allowing SQLite to optimize file-system
119 | ** access to some extent. But it is also safe to simply return 0.
120 | */
121 | int gdsqlite_file::sectorSize(sqlite3_file *pFile) {
122 | return 0;
123 | }
124 | int gdsqlite_file::deviceCharacteristics(sqlite3_file *pFile) {
125 | return 0;
126 | }
127 |
--------------------------------------------------------------------------------
/src/vfs/gdsqlite_file.h:
--------------------------------------------------------------------------------
1 | #ifndef GDSQLITE_FILE_H
2 | #define GDSQLITE_FILE_H
3 |
4 | #include
5 |
6 | #include "./sqlite/sqlite3.h"
7 | #include
8 |
9 | namespace godot {
10 | struct gdsqlite_file {
11 | sqlite3_file base; /* Base class. Must be first. */
12 | Ref file; /* File descriptor */
13 |
14 | static int close(sqlite3_file *pFile);
15 | static int read(sqlite3_file *pFile, void *zBuf, int iAmt, sqlite_int64 iOfst);
16 | static int write(sqlite3_file *pFile, const void *zBuf, int iAmt, sqlite_int64 iOfst);
17 | static int truncate(sqlite3_file *pFile, sqlite_int64 size);
18 | static int sync(sqlite3_file *pFile, int flags);
19 | static int fileSize(sqlite3_file *pFile, sqlite_int64 *pSize);
20 | static int lock(sqlite3_file *pFile, int eLock);
21 | static int unlock(sqlite3_file *pFile, int eLock);
22 | static int checkReservedLock(sqlite3_file *pFile, int *pResOut);
23 | static int fileControl(sqlite3_file *pFile, int op, void *pArg);
24 | static int sectorSize(sqlite3_file *pFile);
25 | static int deviceCharacteristics(sqlite3_file *pFile);
26 | };
27 |
28 | } //namespace godot
29 |
30 | #endif
--------------------------------------------------------------------------------
/src/vfs/gdsqlite_vfs.cpp:
--------------------------------------------------------------------------------
1 | #include "gdsqlite_vfs.h"
2 |
3 | using namespace godot;
4 |
5 | /*
6 | ** Open a file handle.
7 | */
8 | static int gdsqlite_vfs_open(sqlite3_vfs *pVfs, const char *zName, sqlite3_file *pFile, int flags, int *pOutFlags) {
9 | static const sqlite3_io_methods gdsqlite_file_io_methods = {
10 | 1, /* iVersion */
11 | gdsqlite_file::close, /* xClose */
12 | gdsqlite_file::read, /* xRead */
13 | gdsqlite_file::write, /* xWrite */
14 | gdsqlite_file::truncate, /* xTruncate */
15 | gdsqlite_file::sync, /* xSync */
16 | gdsqlite_file::fileSize, /* xFileSize */
17 | gdsqlite_file::lock, /* xLock */
18 | gdsqlite_file::unlock, /* xUnlock */
19 | gdsqlite_file::checkReservedLock, /* xCheckReservedLock */
20 | gdsqlite_file::fileControl, /* xFileControl */
21 | gdsqlite_file::sectorSize, /* xSectorSize */
22 | gdsqlite_file::deviceCharacteristics, /* xDeviceCharacteristics */
23 | };
24 | gdsqlite_file *p = reinterpret_cast(pFile);
25 | Ref file;
26 | FileAccess::ModeFlags godot_flags;
27 |
28 | ERR_FAIL_COND_V(zName == NULL, SQLITE_IOERR); /* How does this respond to :memory:? */
29 |
30 | /* TODO: Add/Support additional flags:
31 | ** - SQLITE_OPEN_DELETEONCLOSE
32 | ** - SQLITE_OPEN_EXCLUSIVE
33 | ** - ???
34 | */
35 |
36 | /* Convert SQLite's flags to something Godot might understand! */
37 | if (flags & SQLITE_OPEN_READONLY) {
38 | // UtilityFunctions::print("READ");
39 | godot_flags = FileAccess::READ;
40 | }
41 | // TODO: Figure out if checking for SQLITE_OPEN_READWRITE is necessary when the database is readonly?
42 | if (flags & SQLITE_OPEN_READWRITE) {
43 | if (flags & SQLITE_OPEN_CREATE) {
44 | if (file->file_exists(String(zName))) {
45 | // UtilityFunctions::print("READ WRITE");
46 | godot_flags = FileAccess::READ_WRITE;
47 | } else {
48 | // UtilityFunctions::print("WRITE READ");
49 | godot_flags = FileAccess::WRITE_READ;
50 | }
51 | } else {
52 | // UtilityFunctions::print("READ WRITE");
53 | godot_flags = FileAccess::READ_WRITE;
54 | }
55 | }
56 |
57 | /* Attempt to open the database or journal file using Godot's `open()`-function */
58 | file = FileAccess::open(String(zName), godot_flags);
59 | Error err_code = FileAccess::get_open_error();
60 | if (err_code != Error::OK) {
61 | /* File can't be opened! */
62 | /* In most cases this is caused by the fact that Godot opens files in a non-shareable way, as discussed here: */
63 | /* https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/fopen-s-wfopen-s?view=msvc-160 */
64 | /* Simply assuring that the file is closed in all other programs fixes this issue... */
65 | /* Multiple database connections are only possible if they are opened in SQLITE_OPEN_READONLY mode */
66 | ERR_PRINT("GDSQLITE_VFS Error: Could not open database! Is the file read/write locked by another program? (Error = " + String(std::to_string(static_cast(err_code)).c_str()) + ")");
67 | return SQLITE_CANTOPEN;
68 | }
69 |
70 | if (pOutFlags) {
71 | *pOutFlags = flags;
72 | }
73 | p->file = file;
74 | p->base.pMethods = &gdsqlite_file_io_methods;
75 | return SQLITE_OK;
76 | }
77 |
78 | /*
79 | ** Delete the file identified by argument zPath. If the dirSync parameter
80 | ** is non-zero, then ensure the file-system modification to delete the
81 | ** file has been synced to disk before returning.
82 | */
83 | static int gdsqlite_vfs_delete(sqlite3_vfs *pVfs, const char *zPath, int dirSync) {
84 | String base_dir = String(zPath).get_base_dir();
85 | Ref dir = DirAccess::open(base_dir);
86 | Error err_code = dir->remove(zPath);
87 | /* Probably we'll also need to check if the file exists and check the err_code! */
88 | return SQLITE_OK;
89 | }
90 |
91 | /*
92 | ** Query the file-system to see if the named file exists, is readable or
93 | ** is both readable and writable.
94 | */
95 | static int gdsqlite_vfs_access(sqlite3_vfs *pVfs, const char *zPath, int flags, int *pResOut) {
96 | Ref file;
97 | Error err_code = Error::OK;
98 |
99 | switch (flags) {
100 | case SQLITE_ACCESS_EXISTS:
101 | *pResOut = file->file_exists(zPath);
102 | break;
103 |
104 | case SQLITE_ACCESS_READWRITE:
105 | file = FileAccess::open(zPath, FileAccess::READ_WRITE);
106 | err_code = FileAccess::get_open_error();
107 | *pResOut = (err_code == Error::OK);
108 | break;
109 |
110 | case SQLITE_ACCESS_READ:
111 | file = FileAccess::open(zPath, FileAccess::READ);
112 | err_code = FileAccess::get_open_error();
113 | *pResOut = (err_code == Error::OK);
114 | break;
115 |
116 | default:
117 | /* Probably throw some kind of error here? */
118 | break;
119 | }
120 |
121 | return SQLITE_OK;
122 | }
123 |
124 | /*
125 | ** Argument zPath points to a nul-terminated string containing a file path.
126 | ** If zPath is an absolute path, then it is copied as is into the output
127 | ** buffer. Otherwise, if it is a relative path, then the equivalent full
128 | ** path is written to the output buffer.
129 | **
130 | ** This function assumes that paths are UNIX style. Specifically, that:
131 | **
132 | ** 1. Path components are separated by a '/'. and
133 | ** 2. Full paths begin with a '/' character.
134 | */
135 | static int gdsqlite_vfs_fullPathname(sqlite3_vfs *pVfs, const char *zPath, int nPathOut, char *zPathOut) {
136 | for (int i = 0; i < nPathOut; ++i) {
137 | zPathOut[i] = zPath[i];
138 | if (zPath[i] == '\0') {
139 | break;
140 | }
141 | }
142 |
143 | return SQLITE_OK;
144 | }
145 |
146 | /*
147 | ** The following four VFS methods:
148 | **
149 | ** xDlOpen
150 | ** xDlError
151 | ** xDlSym
152 | ** xDlClose
153 | **
154 | ** are supposed to implement the functionality needed by SQLite to load
155 | ** extensions compiled as shared objects. This simple VFS does not support
156 | ** this functionality, so the following functions are no-ops.
157 | */
158 | static void *gdsqlite_vfs_dlOpen(sqlite3_vfs *vfs, const char *filename) {
159 | return 0;
160 | }
161 | static void gdsqlite_vfs_dlError(sqlite3_vfs *vfs, int nBytes, char *errMsg) {
162 | sqlite3_snprintf(nBytes, errMsg, "Loadable extensions are not supported");
163 | errMsg[nBytes - 1] = '\0';
164 | }
165 | static void (*gdsqlite_vfs_dlSym(sqlite3_vfs *vfs, void *data, const char *symbol))(void) {
166 | return 0;
167 | }
168 | static void gdsqlite_vfs_dlClose(sqlite3_vfs *vfs, void *data) {
169 | return;
170 | }
171 |
172 | /*
173 | ** Parameter zByte points to a buffer nByte bytes in size. Populate this
174 | ** buffer with pseudo-random data.
175 | */
176 | static int gdsqlite_vfs_randomness(sqlite3_vfs *pVfs, int nByte, char *zByte) {
177 | srand(Time::get_singleton()->get_unix_time_from_system());
178 | for (int i = 0; i < nByte; ++i) {
179 | zByte[i] = rand();
180 | }
181 | return SQLITE_OK;
182 | }
183 |
184 | /*
185 | ** Sleep for at least nMicro microseconds. Return the (approximate) number
186 | ** of microseconds slept for.
187 | */
188 | static int gdsqlite_vfs_sleep(sqlite3_vfs *pVfs, int nMicro) {
189 | OS::get_singleton()->delay_usec(nMicro);
190 | return nMicro;
191 | }
192 |
193 | /*
194 | ** Set *pTime to the current UTC time expressed as a Julian day. Return
195 | ** SQLITE_OK if successful, or an error code otherwise.
196 | **
197 | ** http://en.wikipedia.org/wiki/Julian_day
198 | **
199 | ** This implementation is not very good. The current time is rounded to
200 | ** an integer number of seconds. Also, assuming time_t is a signed 32-bit
201 | ** value, it will stop working some time in the year 2038 AD (the so-called
202 | ** "year 2038" problem that afflicts systems that store time this way).
203 | */
204 | static int gdsqlite_vfs_currentTime(sqlite3_vfs *vfs, double *pTime) {
205 | uint64_t unix_time = Time::get_singleton()->get_unix_time_from_system();
206 | *pTime = unix_time / 86400.0 + 2440587.5;
207 | return SQLITE_OK;
208 | }
209 |
210 | static int gdsqlite_vfs_getLastError(sqlite3_vfs *vfs, int nBuf, char *buf) {
211 | // TODO: Implement properly
212 | return 0;
213 | }
214 |
215 | static int gdsqlite_vfs_currentTimeInt64(sqlite3_vfs *vfs, sqlite3_int64 *now) {
216 | uint64_t unix_time = Time::get_singleton()->get_unix_time_from_system();
217 | *now = unix_time + 210866760000; // Add the number of ms since julian time
218 | return SQLITE_OK;
219 | }
220 |
221 | /*
222 | ** This function returns a pointer to the VFS implemented in this file.
223 | ** To make the VFS available to SQLite:
224 | **
225 | ** sqlite3_vfs_register(gdsqlite_vfs(), 0);
226 | */
227 | sqlite3_vfs *godot::gdsqlite_vfs() {
228 | static sqlite3_vfs godot_vfs = {
229 | 3, /* iVersion */
230 | sizeof(gdsqlite_file), /* szOsFile */
231 | MAXPATHNAME, /* mxPathname */
232 | 0, /* pNext */
233 | "godot", /* zName */
234 | NULL, /* pAppData */
235 | gdsqlite_vfs_open, /* xOpen */
236 | gdsqlite_vfs_delete, /* xDelete */
237 | gdsqlite_vfs_access, /* xAccess */
238 | gdsqlite_vfs_fullPathname, /* xFullPathname */
239 | gdsqlite_vfs_dlOpen, /* xDlOpen */
240 | gdsqlite_vfs_dlError, /* xDlError */
241 | gdsqlite_vfs_dlSym, /* xDlSym */
242 | gdsqlite_vfs_dlClose, /* xDlClose */
243 | gdsqlite_vfs_randomness, /* xRandomness */
244 | gdsqlite_vfs_sleep, /* xSleep */
245 | gdsqlite_vfs_currentTime, /* xCurrentTime */
246 | gdsqlite_vfs_getLastError, /* xGetLastError */
247 | gdsqlite_vfs_currentTimeInt64, /* xCurrentTimeInt64 */
248 | NULL, /* xSetSystemCall */
249 | NULL, /* xGetSystemCall */
250 | NULL /* xNextSystemCall */
251 | };
252 | return &godot_vfs;
253 | }
254 |
--------------------------------------------------------------------------------
/src/vfs/gdsqlite_vfs.h:
--------------------------------------------------------------------------------
1 | #ifndef GDSQLITE_VFS_H
2 | #define GDSQLITE_VFS_H
3 |
4 | #include
5 |
6 | #include
7 | #include
8 | #include
9 |
10 | #include "./sqlite/sqlite3.h"
11 | #include "gdsqlite_file.h"
12 | #include
13 | #include
14 |
15 | /*
16 | ** The maximum pathname length supported by this VFS.
17 | */
18 | #define MAXPATHNAME 512
19 |
20 | namespace godot {
21 |
22 | sqlite3_vfs *gdsqlite_vfs();
23 |
24 | }
25 |
26 | #endif
--------------------------------------------------------------------------------