Skip to content

Commit 7dfafd6

Browse files
committed
Merge branch 'feature/add-doc-classes'
2 parents 37ce753 + d78df13 commit 7dfafd6

File tree

6 files changed

+270
-17
lines changed

6 files changed

+270
-17
lines changed

README.md

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ Additionally, a video tutorial by [Mitch McCollum (finepointcgi)](https://github
6161

6262
Default extension that is automatically appended to the `path`-variable whenever **no** extension is detected/given.
6363

64-
***NOTE:** If database files without extension are desired, this variable has to be set to "" (= an empty string) as to skip this automatic procedure entirely.*
64+
***NOTE**: If database files without extension are desired, this variable has to be set to "" (= an empty string) as to skip this automatic procedure entirely.*
6565

6666
- **foreign_keys** (Boolean, default=false)
6767

@@ -71,8 +71,6 @@ Additionally, a video tutorial by [Mitch McCollum (finepointcgi)](https://github
7171

7272
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 [VFS](https://www.sqlite.org/vfs.html) is employed which internally takes care of all the file handling using the Godot API.
7373

74-
***NOTE:** Godot opens files in a mode that is not shareable i.e. the database file cannot be open in any other program. Attempting to open a read-only database that is locked by another program fails and returns `ERR_FILE_CANT_OPEN` (`12`). However, multiple simultaneous read-only database connections are allowed.*
75-
7674
- **query_result** (Array, default=[])
7775

7876
Contains the results from the latest query **by value**; meaning that this property is safe to use when looping successive queries as it does not get overwritten by any future queries.
@@ -97,16 +95,22 @@ Additionally, a video tutorial by [Mitch McCollum (finepointcgi)](https://github
9795
| VERBOSE (2) | Print additional information to the console |
9896
| VERY_VERBOSE (3) | Same as VERBOSE |
9997

100-
***NOTE:** VERBOSE and higher levels might considerably slow down your queries due to excessive logging.*
98+
***NOTE**: VERBOSE and higher levels might considerably slow down your queries due to excessive logging.*
10199

102-
## Functions
100+
## Methods
103101

104102
- Boolean success = **open_db()**
105103

104+
Open a new database connection. Multiple concurrently open connections to the same database are possible.
105+
106106
- Boolean success = **close_db()**
107107

108+
Close the current database connection.
109+
108110
- Boolean success = **query(** String query_string **)**
109111

112+
Query the database using the raw SQL statement defined in `query_string`.
113+
110114
- Boolean success = **query_with_bindings(** String query_string, Array param_bindings **)**
111115

112116
Binds the parameters contained in the `param_bindings`-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 [here](https://www.sqlite.org/c3ref/bind_blob.html).
@@ -169,16 +173,21 @@ Additionally, a video tutorial by [Mitch McCollum (finepointcgi)](https://github
169173
# Add the row "id" to the table, which is an auto-incremented primary key.
170174
# When adding additional rows, this value can either by explicitely given or be unfilled.
171175
table_dictionary["id"] = {
172-
"data_type":"int",
176+
"data_type": "int",
173177
"primary_key": true,
174-
"auto_increment":true
178+
"auto_increment": true
175179
}
176180
```
177181
178182
For more concrete usage examples see the `database.gd`-file as found in this repository's demo project.
179183
180184
- Boolean success = **drop_table(** String table_name **)**
181185
186+
Drop the table with name `table_name`. This method is equivalent to the following query:
187+
```
188+
db.query("DROP TABLE "+ table_name + ";")
189+
```
190+
182191
- Boolean success = **insert_row(** String table_name, Dictionary row_dictionary **)**
183192
184193
Each key/value pair of the `row_dictionary`-variable defines the column values of a single row.
@@ -362,8 +371,6 @@ To enable this behaviour following conditions need to be met:
362371

363372
You can also open databases in read-only mode that are not packaged, albeit under some restrictions such as the fact that the database files have to copied manually to `user://`-folder on mobile platforms (Android & iOS) and for web builds.
364373

365-
One important additional constraint for read-only databases is that Godot's implementation of file handling does not allow files to opened in a shareable manner. Basically this means that opening a database connection fails whenever other programs have a read lock on the database file e.g. having the file open in [SQLiteStudio](https://sqlitestudio.pl/) for editing purposes. However, multiple simultaneous read-only database connections are allowed.
366-
367374
***NOTE**: The contents of your PCK file can be verified by using externally available tools as found [here](https://github.com/hhyyrylainen/GodotPckTool).*
368375

369376
## Read and write databases

SConstruct

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
#!/usr/bin/env python
2-
import os
3-
import sys
42

53
target_path = ARGUMENTS.pop("target_path", "demo/addons/godot-sqlite/bin/")
64
target_name = ARGUMENTS.pop("target_name", "libgdsqlite")
@@ -28,6 +26,10 @@ target = "{}{}".format(
2826
env.Append(CPPPATH=["src/"])
2927
sources = [Glob('src/*.cpp'), Glob('src/vfs/*.cpp'), 'src/sqlite/sqlite3.c']
3028

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+
3133
if env["platform"] == "macos":
3234
target = "{}.{}.{}.framework/{}.{}.{}".format(
3335
target,

demo/database.gd

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -388,10 +388,6 @@ func example_of_read_only_database():
388388
cprint("* " + row["name"])
389389

390390
# Open another simultanous database connection in read-only mode.
391-
# Having multiple database connections in read-only mode is allowed.
392-
# Having multiple database connections in read and write is NOT allowed!
393-
# This behaviour is hard-coded into Godot's file handling system and can't be
394-
# modified by this plugin.
395391
var other_db = SQLite.new()
396392
other_db.path = packaged_db_name
397393
other_db.verbosity_level = verbosity_level

demo/project.godot

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ config_version=5
1212

1313
config/name="SQLite Demo"
1414
run/main_scene="res://Main.tscn"
15-
config/features=PackedStringArray("4.2")
15+
config/features=PackedStringArray("4.3")
1616
config/icon="res://icon.png"
1717

1818
[debug]

doc_classes/SQLite.xml

Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<class name="SQLite" inherits="RefCounted"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/godotengine/godot/master/doc/class.xsd">
5+
<brief_description>
6+
A SQLite wrapper class implemented in GDExtension.
7+
</brief_description>
8+
<description>
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+
</description>
45+
<tutorials>
46+
<link title="Repository's README.md">https://github.com/2shady4u/godot-sqlite/blob/master/README.md</link>
47+
<link title="Script containing multiple usage examples">https://github.com/2shady4u/godot-sqlite/blob/master/demo/database.gd</link>
48+
</tutorials>
49+
<methods>
50+
<method name="open_db">
51+
<return type="bool" />
52+
<description>
53+
Open a new database connection. Multiple concurrently open connections to the same database are possible.
54+
</description>
55+
</method>
56+
<method name="close_db">
57+
<return type="bool" />
58+
<description>
59+
Close the current database connection.
60+
</description>
61+
</method>
62+
<method name="query">
63+
<return type="bool" />
64+
<description>
65+
Query the database using the raw SQL statement defined in [code]query_string[/code].
66+
</description>
67+
</method>
68+
<method name="query_with_bindings">
69+
<return type="bool" />
70+
<description>
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 query_string : String = "SELECT ? FROM company WHERE age &lt; ?;"
75+
var param_bindings : Array = ["name", 24]
76+
var success = db.query_with_bindings(query_string, param_bindings)
77+
# Executes following query:
78+
# SELECT name FROM company WHERE age &lt; 24;
79+
[/codeblock]
80+
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.
81+
[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]
82+
</description>
83+
</method>
84+
<method name="create_table">
85+
<return type="bool" />
86+
<description>
87+
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.
88+
[b]Required fields[/b]:
89+
- [b]"data_type"[/b]: type of the column variable, following values are valid*:
90+
- "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])
91+
* [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]
92+
[b]Optional fields[/b]:
93+
- [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?
94+
Evidently, only a single column can be set as the primary key.
95+
- [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.
96+
[i][b]NOTE[/b]: Auto-incrementing a column only works when this column is the primary key![/i]
97+
- [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].
98+
[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]
99+
[b]Example usage[/b]:
100+
[codeblock]
101+
# Add the row "id" to the table, which is an auto-incremented primary key.
102+
# When adding additional rows, this value can either by explicitely given or be unfilled.
103+
table_dictionary["id"] = {
104+
"data_type": "int",
105+
"primary_key": true,
106+
"auto_increment": true
107+
}
108+
[/codeblock]
109+
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].
110+
</description>
111+
</method>
112+
<method name="drop_table">
113+
<return type="bool" />
114+
<description>
115+
Drop the table with name [code]table_name[/code]. This method is equivalent to the following query:
116+
[codeblock]
117+
db.query("DROP TABLE "+ table_name + ";")
118+
[/codeblock]
119+
</description>
120+
</method>
121+
<method name="insert_row">
122+
<return type="bool" />
123+
<description>
124+
Each key/value pair of the [code]row_dictionary[/code]-variable defines the column values of a single row.
125+
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].
126+
</description>
127+
</method>
128+
<method name="insert_rows">
129+
<return type="bool" />
130+
<description>
131+
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].
132+
</description>
133+
</method>
134+
<method name="select_rows">
135+
<return type="Array" />
136+
<description>
137+
Returns the results from the latest query [b]by value[/b]; meaning that this property does not get overwritten by any successive queries.
138+
</description>
139+
</method>
140+
<method name="update_rows">
141+
<return type="bool" />
142+
<description>
143+
With the [code]updated_row_dictionary[/code]-variable adhering to the same table schema &amp; conditions as the [code]row_dictionary[/code]-variable defined previously.
144+
</description>
145+
</method>
146+
<method name="delete_rows">
147+
<return type="bool" />
148+
<description>
149+
Delete all rows of the table that match the given conditions.
150+
</description>
151+
</method>
152+
<method name="import_from_json">
153+
<return type="bool" />
154+
<description>
155+
Drops all database tables and imports the database structure and content present inside of [code]import_path.json[/code].
156+
</description>
157+
</method>
158+
<method name="export_to_json">
159+
<return type="bool" />
160+
<description>
161+
Exports the database structure and content to [code]export_path.json[/code] as a backup or for ease of editing.
162+
</description>
163+
</method>
164+
<method name="create_function">
165+
<return type="bool" />
166+
<description>
167+
Bind a [url=https://www.sqlite.org/appfunc.html]scalar SQL function[/url] to the database that can then be used in subsequent queries.
168+
</description>
169+
</method>
170+
<method name="get_autocommit">
171+
<return type="int" />
172+
<description>
173+
Check if the given database connection is or is not in autocommit mode, see [url=https://sqlite.org/c3ref/get_autocommit.html]here[/url].
174+
</description>
175+
</method>
176+
<method name="backup_to">
177+
<return type="bool" />
178+
<description>
179+
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.
180+
</description>
181+
</method>
182+
<method name="restore_from">
183+
<return type="bool" />
184+
<description>
185+
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.
186+
</description>
187+
</method>
188+
<method name="compileoption_used">
189+
<return type="bool" />
190+
<description>
191+
Check if the binary was compiled using the specified option, see [url=https://sqlite.org/c3ref/compileoption_get.html]here[/url].
192+
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:
193+
[codeblock]
194+
db.compileoption_used("SQLITE_ENABLE_FTS5") # Returns '1' if enabled or '0' if disabled
195+
db.compileoption_used("ENABLE_FTS5") # The "SQLITE_"-prefix may be omitted.
196+
[/codeblock]
197+
</description>
198+
</method>
199+
</methods>
200+
<members>
201+
<member name="path" type="String" default="default">
202+
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.
203+
</member>
204+
<member name="error_message" type="String" default="&quot;&quot;">
205+
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.
206+
</member>
207+
<member name="default_extension" type="String" default="db">
208+
Default extension that is automatically appended to the [code]path[/code]-variable whenever [b]no[/b] extension is detected/given.
209+
[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]
210+
</member>
211+
<member name="foreign_keys" type="bool" default="false">
212+
Enables or disables the availability of [url=https://www.sqlite.org/foreignkeys.html]foreign keys[/url] in the SQLite database.
213+
</member>
214+
<member name="read_only" type="bool" default="false">
215+
Enabling this property opens the database in read-only modus &amp; 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.
216+
</member>
217+
<member name="query_result" type="Array" default="[]">
218+
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.
219+
</member>
220+
<member name="query_result_by_reference" type="Array" default="[]">
221+
Contains the results from the latest query [b]by reference[/b] and is, as a direct result, cleared and repopulated after every new query.
222+
</member>
223+
<member name="last_insert_rowid" type="int" default="0">
224+
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].
225+
Attempting to modify this variable directly is forbidden and throws an error.
226+
</member>
227+
<member name="verbosity_level" type="int" default="1">
228+
The verbosity_level determines the amount of logging to the Godot console that is handy for debugging your (possibly faulty) SQLite queries.
229+
[i][b]NOTE:[/b] [constant VERBOSE] and higher levels might considerably slow down your queries due to excessive logging.[/i]
230+
</member>
231+
</members>
232+
<signals>
233+
</signals>
234+
<constants>
235+
<constant name="QUIET" value="0">
236+
Don't print anything to the console.
237+
</constant>
238+
<constant name="NORMAL" value="1">
239+
Print essential information to the console.
240+
</constant>
241+
<constant name="VERBOSE" value="2">
242+
Print additional information to the console.
243+
</constant>
244+
<constant name="VERY_VERBOSE" value="3">
245+
Same as [constant VERBOSE].
246+
</constant>
247+
</constants>
248+
</class>

0 commit comments

Comments
 (0)