2shady4u / godot-sqlite

GDExtension wrapper for SQLite (Godot 4.x+)
MIT License
895 stars 79 forks source link

Unexpected behaviour: query_with_bindings puts value in 'single quotes' #118

Closed feefladder closed 1 year ago

feefladder commented 1 year ago

Issue description:

When using the update_rows function to increment values like so, e.g. I want to increase coolness of all non-cool values in my database, this doesn't work:

db.create_table("table_name",
    {"id":
        {"data_type":"int",
        "primary_key":true,
        "auto_increment":true},
    "coolness":
        {"data_type":"int",
        "not_null":true,
        "default":4
}})
db.insert_rows([{"coolness":6},{"coolness":3},{},{}])
db.update_rows("table_name","coolness<=5",{"coolness":"coolness+1"})

because the coolness+1 is inserted into the query like 'coolness+1' and then we get a string literal in an int column. What is a current workaround is to do a direct query like so:

db.query("UPDATE table_name SET coolness=coolness+1 WHERE coolness<=5")

This is unexpected behaviour, because FAQ 1 says you should manually put single quotes whenever needed. Steps to reproduce:

run the reproduction project.

Minimal reproduction project:

bug.zip

Well, uploading doesn't seem to work...

  1. Create an empty project and install the GDSQLite addon
  2. create a node with the following script:
extends Node

const SQLite := preload("res://addons/godot-sqlite/bin/gdsqlite.gdns")

var db_name = "res://db.db"
var db: SQLite  

func _ready():
    db = SQLite.new()
    db.path = db_name
    db.verbosity_level = 2
    db.open_db()
    db.create_table("table_name",
    {"id":
        {"data_type":"int",
        "primary_key":true,
        "auto_increment":true},
    "coolness":
        {"data_type":"int",
        "not_null":true,
        "default":4
    }})
    db.insert_rows("table_name",[{"coolness":6},{"coolness":5},{"coolness":4},{"coolness":1}])
    # works
    db.query("UPDATE table_name SET coolness=coolness+1 WHERE coolness<=5")
    #doesn't work
    db.query_with_bindings("UPDATE table_name SET coolness=? WHERE coolness<=5",["coolness+1"])
    #doesn't work
    db.update_rows("table_name","coolness<=5",{"coolness":"coolness+1"})
    print_debug(db.select_rows("table_name","1=1",["coolness"]))
    db.drop_table("table_name")
    db.close_db()
  1. run the scene and it should print:
    [{coolness:6}, {coolness:6}, {coolness:coolness+1}, {coolness:coolness+1}]

Additional context

feefladder commented 1 year ago

This also seems to be in general with query_with_bindings and string values. I think the line in question is here:

            sqlite3_bind_text(stmt, i + 1, (binding_value.operator String()).alloc_c_string(), -1, SQLITE_TRANSIENT);

But I do not see anywhere that there are single quotes placed around them or something...

feefladder commented 1 year ago

Ah, I guess this cannot be done, since it references a column name ("coolness"), which cannot be bound, also not in update statements... Apparently, this prevents SQL injection attacks, which is fair to want to do. Then, this should be a documentation thing

2shady4u commented 1 year ago

Hi @feefladder

Feel free to open a PR with your changes to the README.md :)

feefladder commented 1 year ago

Did so! Actually, I think this is still a (SQLite) bug of some sorts, since it puts a TEXT value into an INTEGER column.

feefladder commented 1 year ago

documentation updated