├── .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 | 20 | 22 | 31 | 35 | 39 | 43 | 44 | 54 | 64 | 65 | 85 | 88 | 89 | 91 | 92 | 94 | image/svg+xml 95 | 97 | 98 | 99 | 100 | 101 | 106 | 113 | 120 | 127 | 134 | S 148 | L 162 | 169 | 176 | 180 | 185 | 189 | 195 | 201 | 206 | 207 | 213 | Q 226 | 227 | 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 | 20 | 22 | 31 | 35 | 39 | 43 | 44 | 54 | 64 | 65 | 96 | 101 | 102 | 104 | 105 | 107 | image/svg+xml 108 | 110 | 111 | 112 | 113 | 114 | 119 | 125 | 128 | 135 | 142 | 148 | 153 | 154 | 160 | 165 | 166 | 172 | 177 | 178 | 184 | 189 | 190 | 196 | 201 | 202 | 209 | 216 | S 230 | L 244 | Q 257 | 264 | 271 | 277 | 283 | 284 | 291 | Godot SQLiteGDNative wrapper for Godot 3.2+ 307 | 319 | 323 | 324 | 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 | 22 | 24 | 33 | 37 | 41 | 45 | 46 | 56 | 66 | 67 | 89 | 91 | 92 | 94 | image/svg+xml 95 | 97 | 98 | 99 | 100 | 101 | 106 | 109 | 116 | 123 | 129 | 134 | 135 | 141 | 146 | 147 | 153 | 158 | 159 | 165 | 170 | 171 | 177 | 182 | 183 | 190 | 197 | S 211 | L 225 | Q 238 | 245 | 252 | 258 | 264 | 265 | Godot SQLiteGDNative wrapper for Godot 3.1+ 281 | 293 | 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 | 20 | 22 | 32 | 41 | 45 | 49 | 53 | 54 | 64 | 74 | 75 | 95 | 100 | 101 | 103 | 104 | 106 | image/svg+xml 107 | 109 | 110 | 111 | 112 | 117 | 122 | 129 | 136 | 142 | 147 | 148 | 154 | 159 | 160 | 166 | 171 | 172 | 178 | 183 | 184 | 190 | 195 | 196 | 202 | 208 | 215 | 222 | SQLite 232 | 233 | 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 | 20 | 22 | 31 | 35 | 39 | 43 | 44 | 54 | 64 | 65 | 84 | 86 | 87 | 89 | image/svg+xml 90 | 92 | 93 | 94 | 95 | 96 | 101 | 108 | 115 | 121 | 126 | 127 | 133 | 138 | 139 | 145 | 150 | 151 | 157 | 162 | 163 | 169 | 174 | 175 | 181 | 186 | 187 | 194 | 201 | S 215 | L 229 | Q 242 | 249 | 256 | 262 | 268 | 269 | 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 | 22 | 24 | 33 | 37 | 41 | 45 | 46 | 56 | 66 | 67 | 85 | 87 | 88 | 90 | image/svg+xml 91 | 93 | 94 | 95 | 96 | 97 | 102 | 109 | 116 | 122 | 127 | 128 | 134 | 139 | 140 | 146 | 151 | 152 | 158 | 163 | 164 | 170 | 175 | 176 | 183 | 190 | S 204 | L 218 | Q 231 | 238 | 245 | 251 | 257 | 258 | 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 --------------------------------------------------------------------------------