codenotary / immudb

immudb - immutable database based on zero trust, SQL/Key-Value/Document model, tamperproof, data change history
https://immudb.io
Other
8.62k stars 343 forks source link

feat: automatically convert uuid strings and byte slices to uuid values #1870

Closed tauu closed 11 months ago

tauu commented 11 months ago

This PR adds support for supplying UUID values in SQL queries as strings or byte slices by implicitly casting these to UUID values if they are used for UUID columns. This also improves the usability of for working with uuids using the stdlib.

The reason for this change is:

  1. Implicitly casting a string/binary slice representing a valid UUID to a UUID will never destroy information
  2. Catching type errors seems to provide little benefit in this case. That would require a user to provide a value that is a valid uuid, but was not intended as uuid. Considering the specific format of uuids, that sounds unlikely to happen.
  3. It is more intuitive to work with uuids for the user, see details below.

Currently the following queries and code snippets are returning an error, stating that a non uuid value was encountered.

CREATE TABLE test (id UUID, PRIMARY KEY id);
INSERT INTO test( id ) VALUES( "00010203-0440-0680-0809-0a0b0c0d0e0f" );
db.ExecContext(context.Background(), "CREATE TABLE test (id UUID, PRIMARY KEY id)") 
id := uuid.UUID([16]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x40, 0x06, 0x80, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f})
_, err = db.ExecContext(context.Background(), "INSERT INTO test (id) VALUES (?)", id)

The reason for this is, that the engine expects a raw uuid.UUID values for UUID columns, but raw UUIDs cannot be sent directly to the engine as it is not a supported type of the protocol. Consequently the UUID will be either transmitted as byte slice or as a string. As the sql.Valuer interface implementation of uuid.UUID converts it to a string, the id is always send as a string when the stdlib is used.

Currently the way to insert a UUID value is to add an explicit cast for the string:

_, err = db.ExecContext(context.Background(), "INSERT INTO test (id) VALUES (?::UUID)", id)

This is not super intuitive, especially as it works already seamlessly when querying. A uuid is automatically converted to a string value in a query response. Furthermore the stdlib will also automatically parse it into a raw uuid.UUID value.

To make working with uuids more intuitive, this PR changes the handling for uuid columns to always convert strings and byte slices to uuids if they are valid uuids.

Test for passing uuids as strings or byte slices to the engine or the stdlib have been included.

jeroiraz commented 11 months ago

thanks @tauu !