Interactions-as-a-Service / d1-orm

A simple, strictly typed ORM, to assist you in using Cloudflare's D1 product
https://docs.interactions.rest/d1-orm/
Apache License 2.0
162 stars 11 forks source link

boolean default value use case not covered #51

Closed kingmesal closed 1 year ago

kingmesal commented 1 year ago

https://github.com/Interactions-as-a-Service/d1-orm/commit/b214e74a5c7c56fb593008b87bf819affb52fb47

Does not provide support for a boolean default value.

Skye-31 commented 1 year ago

Default values use the unknown type. There's no bug here?

kingmesal commented 1 year ago

the database cannot set a default value of "false" or "true" into a column which is coerced into an int 0 | 1.

Skye-31 commented 1 year ago

Just as it coerces types when inserting, it does so with default values. Please test these things first

kingmesal commented 1 year ago

Here is why this is an issue:

With this Model:

export function TestingTable(orm: D1Orm) {
    return new Model({ D1Orm: orm, tableName: "testing" }, {
        id: {
            type: DataTypes.INTEGER,
            primaryKey: true,
        },
        name: {
            type: DataTypes.STRING,
            notNull: true
        },
        a: {
            type: DataTypes.BOOLEAN,
            defaultValue: "0",
        },
        b: {
            type: DataTypes.BOOLEAN,
            defaultValue: "false",
        },
        c: {
            type: DataTypes.BOOLEAN,
            defaultValue: "1",
        },
        d: {
            type: DataTypes.BOOLEAN,
            defaultValue: "true",
        },
        e: {
            type: DataTypes.BOOLEAN,
            defaultValue: false,
        },
        f: {
            type: DataTypes.BOOLEAN,
            defaultValue: true,
        },
    });
}

Produces this create statement:

CREATE TABLE `testing` (
id integer, 
name text NOT NULL, 
a boolean DEFAULT "0", 
b boolean DEFAULT "false", 
c boolean DEFAULT "1", 
d boolean DEFAULT "true", 
e boolean DEFAULT "false", 
f boolean DEFAULT "true", 
PRIMARY KEY (id));

For the sake of this test, since pair B and E and pair D and F would produce the exact same results, I modified columns 'e' and 'f' in the database to be:

e boolean DEFAULT false, 
f boolean DEFAULT true, 

The reason I did that was to show that true and false without quotes are keywords in sqlite grammar that get automatically converted to 1 and 0. Which can be seen further down.

Inserting this record:

    const t = TestingTable(orm);
    const key = "test 5";
    const testRecord: ReturnType<typeof TestingTable> = {
        name: key,
    }
    const insert = await t.InsertOne(testRecord);

Fetching that same record:

    const record = await t.First({ where: { name: key } });
    console.log(record);
{
  id: 5,
  name: 'test 5',
  a: 0,
  b: 'false',
  c: 1,
  d: 'true',
  e: 0,
  f: 1
}

So, when evaluating those values from the data with this code:

    console.log(`Record a: ${Boolean(record?.a)}`);
    console.log(`Record b: ${Boolean(record?.b)}`);
    console.log(`Record c: ${Boolean(record?.c)}`);
    console.log(`Record d: ${Boolean(record?.d)}`);
    console.log(`Record e: ${Boolean(record?.e)}`);
    console.log(`Record f: ${Boolean(record?.f)}`);
Record a: false
Record b: true // THIS IS WRONG!
Record c: true
Record d: true
Record e: false
Record f: true
kingmesal commented 1 year ago

This was fixed in #55