Skip to content

Commit

Permalink
[typeid-sql] Fix typos and clarify usage of different encodings in RE…
Browse files Browse the repository at this point in the history
…ADME (#378)

## Summary
Fix typos and clarify usage of different encodings in README

## How was it tested?
N/A
  • Loading branch information
loreto authored Sep 6, 2024
1 parent 2c0da99 commit 455dbca
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 37 deletions.
2 changes: 1 addition & 1 deletion typeid/typeid-go/sql.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func (tid *TypeID[P]) Scan(src any) error {
return nil
}
return tid.UnmarshalText([]byte(obj))
// TODO: add supporte for []byte
// TODO: add support for []byte
// we don't just want to store the full string as a byte array. Instead
// we should encode using the UUID bytes. We could add support for
// Binary Marshalling and Unmarshalling at the same time.
Expand Down
102 changes: 66 additions & 36 deletions typeid/typeid-sql/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,52 +25,94 @@ for development and testing, but you do **not** need to use Supabase for this
implementation to work – simply use the Postgres instance of your choice.

## Usage

Once you've installed the TypeID types and functions in your Postgres instance,
you can use it as follows.
you have two options on how to encode TypeIDs in your database.

### 1. Text-based encoding
This encoding is more inefficient than the alternative, but it's very straight-forward
to understand, it's easy to debug by simply inspecting the contents of your tables, and
it works well with other tools you might be using to inspect your database.

To define a new type of typeid with a specific prefix use the `typeid\_
To use it:
+ Declare your `id` column using the `text` type.
+ Use the `typeid_generate_text` function to generate new default values.
+ Use the `typeid_check_text` to enforce all strings in the column are valid typeids.

Example:

```sql
-- Define a `users` table that uses `user_id` as its primary key.
-- We use the `typeid_generate` function to randomly generate a new typeid of the
-- We use the `typeid_generate_text` function to randomly generate a new typeid of the
-- correct type for each user.
-- We also recommend adding the check constraint to the column
CREATE TABLE users (
"id" user_id not null default typeid_generate('user'),
"id" text not null default typeid_generate_text('user') CHECK (typeid_check_text(id, 'user')),
"name" text,
"email" text
);

-- Now we can insert new uses and have the `id` column automatically generated.
-- Now we can insert new users and have the `id` column automatically generated.
INSERT INTO users ("name", "email") VALUES ('Alice P. Hacker', 'alice@hacker.net');
SELECT id FROM users;
-- Result:
-- "user_01hfs6amkdfem8sb6b1xmg7tq7"

-- Insert a user with a specific typeid that might have been generated elsewhere:
INSERT INTO users ("id", "name", "email")
VALUES ('user_01h455vb4pex5vsknk084sn02q', 'Ben Bitdiddle', 'ben@bitdiddle.com');

-- To retrieve the ids as encoded strings, just use the column:
SELECT id AS id, "name", "email" FROM users;

-- You can also use filter in a WHERE clause to filter by typeid:
SELECT typeid_print(id) AS id, "name", "email" FROM users
WHERE id = 'user_01h455vb4pex5vsknk084sn02q';
```

Or
### 2. UUID-based encoding using compound types
In this approach, we internally encode typeids as a `(prefix, uuid)` tuple. The
sql files in this library provide a predefined `typeid` type to represent
said tuples.

The advantage of this approach is that it is a more efficient encoding because we
store the uuid portion of the typeid using the native `uuid` type.

You can use the typeid_generate_text function to generate a new typeid as a string, and not use the typeid type
The disadvanage is that it is harder to work with and debug.

If performance is a primary concern of yours, also consider using the native
[postgres extension](https://github.com/blitss/typeid-postgres) for typeid,
which exposes typeids as a "built-in" type.

To define a new typeid using this encoding, you can use the `typeid_check` function:
```sql
-- Define a `user_id` type, which is a typeid with type prefix "user".
-- Using `user_id` throughout our schema, gives us type safety by guaranteeing
-- that the type prefix is always "user".
CREATE DOMAIN user_id AS typeid CHECK (typeid_check(value, 'user'));
```

You can now use the newly defined type in your tables. The `typeid_generate` function
makes it possible to automatically a new random typeid for each row:

```sql
-- Define a `users` table that uses `user_id` as its primary key.
-- We use the `typeid_generate_text` function to randomly generate a new typeid of the
-- We use the `typeid_generate` function to randomly generate a new typeid of the
-- correct type for each user.
-- You will need to manually add the check constraint to the column
CREATE TABLE users (
"id" text not null default typeid_generate_text('user') CHECK (typeid_check_text(id, 'user')),
"id" user_id not null default typeid_generate('user'),
"name" text,
"email" text
);

-- Now we can insert new uses and have the `id` column automatically generated.
-- Now we can insert new users and have the `id` column automatically generated.
INSERT INTO users ("name", "email") VALUES ('Alice P. Hacker', 'alice@hacker.net');
SELECT id FROM users;
-- Result:
-- "user_01hfs6amkdfem8sb6b1xmg7tq7"
```
#### Querying
To make it easy to query typeid tuples using the standard string representation, we
provide two convenience functions: `typeid_parse` and `typeid_print`, which convert
to and from the standard string representation.

Note that the database internally encodes typeids as a `(prefix, uuid)` tuple. Because
this is different than the standard string representation of typeids in other libraries,
we provide a `typeid_parse` and a `typeid_print` function that can be used to write
queries with the standard string representation of typeids:
Example:

```sql
-- Insert a user with a specific typeid that might have been generated elsewhere:
Expand All @@ -85,22 +127,7 @@ SELECT typeid_print(id) AS id, "name", "email" FROM users
WHERE id = typeid_parse('user_01h455vb4pex5vsknk084sn02q');
```

or for the text variant

```sql
-- Insert a user with a specific typeid that might have been generated elsewhere:
INSERT INTO users ("id", "name", "email")
VALUES ('user_01h455vb4pex5vsknk084sn02q', 'Ben Bitdiddle', 'ben@bitdiddle.com');

-- To retrieve the ids as encoded strings, just use the column:
SELECT id AS id, "name", "email" FROM users;

-- You can also use filter in a WHERE clause to filter by typeid:
SELECT typeid_print(id) AS id, "name", "email" FROM users
WHERE id = 'user_01h455vb4pex5vsknk084sn02q';
```

## (Optional) Operator overload
#### (Optional) Operator overload

If you'd like to be able to do the following:

Expand All @@ -112,7 +139,10 @@ SELECT * FROM users u WHERE u.id = 'user_01h455vb4pex5vsknk084sn02q';
-- "(user,018962e7-3a6d-7290-b088-5c4e3bdf918c)",Ben Bitdiddle,ben@bitdiddle.com
```

Then you can add in [the operator overload function for typeids](https://github.com/search?q=repo%3Ajetify-com%2Ftypeid-sql%20compare_type_id_equality&type=code):
Then you can add in [the operator overload functions for typeid](https://github.com/jetify-com/typeid-sql/blob/main/sql/04_operator.sql).

Some users have reported issues with the above operator when using Rails and ActiveRecord – we
recommend removing `COMMUTATOR` from the operator definition if you encounter issues.

## Future work (contributions welcome)

Expand Down

0 comments on commit 455dbca

Please sign in to comment.