-
Notifications
You must be signed in to change notification settings - Fork 181
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: allow string type attribute to be auto-generated in Postgres #404
Conversation
This is tricky issue, thank you @agnes512 for digging deep into this problem space. IIUC, there are two aspects we need to address:
Also IIUC, this issue is specific to SQL databases that typically use a numeric primary key when the value is generated by the database. For example, database-generated UUID primary keys are the default option in MongoDB. While I like the idea of introducing a new data type I find this situation similar to ObjectID issues we are facing in MongoDB - we have a string-like value that must be treated differently by the connector. I prefer to find a solution where the property type stays as @model()
export class Customer extends Entity {
@property({
type: 'string',
id: true,
useDefaultIdType: false,
postgresql: {
defaultFn: 'gen_random_uuid()'
}
})
custid: string;
} Or perhaps we can enhance
To be honest, I feel both options described above are adding too much complexity and not enough benefits for the majority of our users. Let's try to find something simpler. I like the following items in your original proposal:
I would like to propose a slightly different solution building on your ideas:
In other words, if the user defines an primary key as auto-generated, then LoopBack should use the most sensible option supported by the database (i.e. IMO, the migration should not abort the process by throwing an error, I am proposing to print a warning instead and continue with the migration process. I think that even if don't configure all auto-generation rules in the database, it is still very useful to have the rest of the schema created by the LoopBack migration tool. (We also don't want to abort migration in the middle, when some of the tables were already migrated/updated but some haven't.) We can also apply these new rules to connectors that don't support auto-generated numeric values (MongoDB and Cloudant), see e.g. loopbackio/loopback-next#2040. Because schema migration is usually not executed for NoSQL databases, this would require the check to be performed at runtime. To keep things simple, it may be best to leave this part out of scope of this pull request, just keep it in mind. Circling back to the proposal to add I am proposing to open a new feature request in loopback-next to implement |
@bajtos thank you for elaborating all your concern!
You're right, I didn't consider this part. Will just use the existing types.
I was thinking to have something similar to mongoDB:
so when Any preferences? |
That's a decent solution too!
Personally, I would try to keep this simple and easy to use for our users. If their model/property definition says that the PK/ |
@slnode test please |
@slnode test please |
1 similar comment
@slnode test please |
@slnode test please |
Good discussion. @agnes512 , this seems like a good feature. Great work! |
1ef89c2
to
e0e7f0d
Compare
@slnode test please |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor nitpicks and some questions
@@ -295,12 +296,33 @@ function mixinMigration(PostgreSQL) { | |||
const self = this; | |||
const modelDef = this.getModelDefinition(model); | |||
const prop = modelDef.properties[propName]; | |||
let result = self.columnDataType(model, propName); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The variable name here is a bit misleading, maybe use columnType
instead of result
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sorry never mind, I just realized the code is in a function called buildColumnDefinition
, so result
is better.
if (result === 'INTEGER') { | ||
return 'SERIAL'; | ||
} else if (postgType === 'UUID') { | ||
if (postgSettings && postgSettings.defaultFn && postgSettings.extension) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, how do we deal with the extension
? Is it a function or just a boolean value?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to 'install' the extension to use its functions. e.g, to use foo.bar(), we need to have foo installed first. In here extension is just a name that will be used in postgres query of creating table later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👏 Nice feature! Left a few comments.
@@ -481,7 +524,7 @@ function mixinMigration(PostgreSQL) { | |||
default: | |||
case 'String': | |||
case 'JSON': | |||
return 'TEXT'; | |||
case 'Uuid': |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, what's the difference between Uuid
and UUID
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am following the pattern with other type such as String
, Number
. But I think the case doesn't matter. I can change it to uuid
.
As for the UUID
below, it's modified to upper case so I use UUID
there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh I see, the code comparison gave me an impression that they are two cases in the same function, while actually not lol. 👍
assert.deepEqual(cols, { | ||
defaultcode: | ||
{column_name: 'defaultcode', | ||
column_default: 'uuid_generate_v4()', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIUC the column_default
here is uuid_generate_v4()
instead of uuid_generate_v1()
because extension
is missing in the property def, so I am thinking, maybe add a warning before line 318 to let people know it falls back to the default value due to missing extension?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct, it should use the default extension and function for this case. And I have uuid_generate_v4()
here 😄
We need tests to assert the behavior when |
I think |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👏
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know this area too well but I think the changes look reasonable to me 👍
Great work people. But still I think we have a problem.
So everything works well but when I try to do find on any model, I get id as NaN. Guessing because loopback is still treating my id as a number. This happens only when generated is true so i guess Also when i am doing realtions, the foreign key are being treated as numbers for some reason. Any update would be much apprecitated, thank you. |
fixes loopbackio/loopback-next#2398
please review it with whitespace hided
I went through several related issues:
loopbackio/loopback-next#2398 is partially fixed in loopbackio/loopback-datasource-juggler#1783.
Issue
The flag
useDefaultIdType: false
is supposed to allow to use custom type of auto-generated id property. But it only allows the default type, and it won't generated the custom id type itself as different DBs have different syntax/setup.For example, Postgres and MySQL default identity type is INT. And they can be auto-generated by
SERIAL
andAUTO_INCREAMENT
respectively.The model definition above won't work on Postgres and MySQL because these two connectors only support to auto-generated the default id type ( integer in this case).
From #3602 what suggests and real world usage, I am proposing to add
uuid
type to data type, and also add support for auto-generated string type id to Postgres connector:Proposal
uuid
and auto-generated, by default, useuuid-ossp
extension anduuid_generate_v4()
function to allow auto-generated string.when id type isstring
and auto-generated, treat it asuuid
. ( Should we?)However, it is user's responsibility to provide valid extension and function.
ALTER TABLE
to set up the table manually.If the above is okay, will fix MySQL as well.
Checklist
👉 Read and sign the CLA (Contributor License Agreement) 👈
npm test
passes on your machine