Skip to content
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

feat: migrate updateQuery to typescript #17063

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

lohart13
Copy link
Contributor

@lohart13 lohart13 commented Feb 7, 2024

Pull Request Checklist

  • Have you added new tests to prevent regressions?
  • If a documentation update is necessary, have you opened a PR to the documentation repository?
  • Did you update the typescript typings accordingly (if applicable)?
  • Does the description below contain a link to an existing issue (Closes #[issue]) or a description of the issue you are solving?
  • Does the name of your PR follow our conventions?

Description Of Change

This PR migrates updateQuery methods to typescript.

@lohart13 lohart13 force-pushed the update-ts branch 2 times, most recently from 6c58304 to 1c4e81d Compare February 8, 2024 09:55
@lohart13 lohart13 force-pushed the update-ts branch 2 times, most recently from 2cf0c07 to 91da630 Compare February 10, 2024 01:39
@lohart13 lohart13 changed the title feat: migrate updateQuery to typescript feat: migrate update to typescript Feb 10, 2024
Copy link
Member

@ephys ephys left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Preliminary review but the few comments I posted already request some pretty big changes to update

packages/core/src/dialects/abstract/index.ts Outdated Show resolved Hide resolved
@lohart13 lohart13 changed the title feat: migrate update to typescript feat: migrate updateQuery to typescript Feb 12, 2024
@lohart13
Copy link
Contributor Author

Looking into merging update and bulkUpdate it seems there is some work required to remove the Model instance building from query.js and only return the raw database values as the model.save method needs some work in order to work with raw values rather than an instance.

I'd propose to fix these methods once #16988 has been merged.

I'll update #16988 to exclude the changes to the queryInterface

Copy link
Member

@ephys ephys left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As always, thank you so much for your work!

I have yet to review sqlite & mssql but I already have a few comments so I'll take a break here

Comment on lines 330 to 346
const returnFields: string[] = [];

if (Array.isArray(options.returning)) {
returnFields.push(...options.returning.map(field => {
if (typeof field === 'string') {
return this.queryGenerator.quoteIdentifier(field);
} else if (field instanceof BaseSqlExpression) {
return this.queryGenerator.formatSqlExpression(field, options);
}

throw new Error(`Unsupported value in "returning" option: ${inspect(field)}. This option only accepts true, false, an array of strings, or a sql expression.`);
}));
} else if (modelDefinition) {
for (const [name, attribute] of modelDefinition.physicalAttributes) {
returnFields.push(this.queryGenerator.quoteIdentifier(attribute.columnName ?? name));
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small optimization, as this function is called often:

  • assigned returnFields to the result of options.returning.map to avoid creating an unused array
  • used .values() instead of iterating the physicalAttributes map directly, as columnName is guaranteed to be provided, and creating an array for each entry of the map is expensive
Suggested change
const returnFields: string[] = [];
if (Array.isArray(options.returning)) {
returnFields.push(...options.returning.map(field => {
if (typeof field === 'string') {
return this.queryGenerator.quoteIdentifier(field);
} else if (field instanceof BaseSqlExpression) {
return this.queryGenerator.formatSqlExpression(field, options);
}
throw new Error(`Unsupported value in "returning" option: ${inspect(field)}. This option only accepts true, false, an array of strings, or a sql expression.`);
}));
} else if (modelDefinition) {
for (const [name, attribute] of modelDefinition.physicalAttributes) {
returnFields.push(this.queryGenerator.quoteIdentifier(attribute.columnName ?? name));
}
}
let returnFields: string[];
if (Array.isArray(options.returning)) {
returnFields = options.returning.map(field => {
if (typeof field === 'string') {
return this.queryGenerator.quoteIdentifier(field);
} else if (field instanceof BaseSqlExpression) {
return this.queryGenerator.formatSqlExpression(field, options);
}
throw new Error(`Unsupported value in "returning" option: ${inspect(field)}. This option only accepts true, false, an array of strings, or a sql expression.`);
});
} else {
returnFields = [];
if (modelDefinition) {
for (const attribute of modelDefinition.physicalAttributes.values()) {
returnFields.push(this.queryGenerator.quoteIdentifier(attribute.columnName));
}
}
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated this now, LMK if it looks OK.

packages/core/src/dialects/abstract/index.ts Outdated Show resolved Hide resolved
}

const bind = Object.create(null);
const valueHash = removeNullishValuesFromHash(attrValueHash, this.options.omitNull ?? false);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to slowly phase out removeNullishValuesFromHash and the omitNull option

Could you move its use to the places that use updateQuery instead of keeping it here? That will make it possible to not include it in the model method rewrite that's in progress, while keeping it in the old model methods

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've moved the removeNullishValuesFromHash logic to the model methods.

} else if (field instanceof Literal) {
// Due to how the mssql query is built, using a literal would never result in a properly formed query.
// It's better to warn early.
throw new Error(`literal() cannot be used in the "returning" option array in ${this.dialect.name}. Use col(), or a string instead.`);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check is a bit outdated. The logic should accept instances of Col, and strings, but ban all other values, not just Literal

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the logic now

const returnFields: string[] = [];

if (Array.isArray(options.returning)) {
returnFields.push(...options.returning.map(field => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comments made on the parent method apply here too

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implementation update now.


generateOutputTableFragment(returnFields: string[], modelDefinition?: ModelDefinition | null): string {
if (!(modelDefinition instanceof ModelDefinition)) {
throw new Error('Using returning with triggers requires specifying a model or model definition.');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check is specific to where this method was used. modelDefinition should be made mandatory here, and this check should be moved to where this method is called

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated now.


const columnsToExclude = new Set<string>();
const attributes = map(filter(returnFields, field => field !== '*'), field => {
const unquotedColumn = field.replace(/^\[/, '').replace(/\]$/, '');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quoting then unquoting is a bad idea and will break for identifiers that require escaping

The issue is that getReturnFields handles both formatting returning, & setting its default value.

You need to first use a function called normalizeReturning which:

  • if returning is true: sets it to the list of model columns when a model is available, or * otherwise
  • if returning is false: sets it to EMPTY_ARRAY
  • if returning is an array: leaves it as-is

Then getReturnFields can become formatReturnField, which only handles serialization

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree and have updated the implementation now.

@WikiRik
Copy link
Member

WikiRik commented Feb 18, 2024

Heads up for these and your other open PRs; we've moved to prettier in #17101 which causes a lot of merge conflicts. When resolving you can just accept your changes first and then run yarn format to fix the differences

@ephys
Copy link
Member

ephys commented Feb 18, 2024

To add to this, very sorry for all the conflicts I've been introducing recently

@WikiRik
Copy link
Member

WikiRik commented Mar 22, 2024

@lohart13 @ephys just so that we are on the same page; is this order of when we want to review/merge PRs correct?

  1. fix: unify returning queries  #17157 and fix: make BaseSqlExpression a unique class #17158 (these are interchangeable from each other)
  2. feat: migrate updateQuery to typescript #17063
  3. feat: migrate bulkInsertQuery & insertQuery to ts #16988
  4. feat: migrate index to typescript #16227

Your other PRs are on hold while we try to focus efforts on releasing v7(-beta) soonish. Read more about our plans here; https://github.com/sequelize/meetings/wiki/Meeting-2024%E2%80%9003%E2%80%9022

@lohart13
Copy link
Contributor Author

@lohart13 @ephys just so that we are on the same page; is this order of when we want to review/merge PRs correct?

  1. fix: unify returning queries  #17157 and fix: make BaseSqlExpression a unique class #17158 (these are interchangeable from each other)
  2. feat: migrate updateQuery to typescript #17063
  3. feat: migrate bulkInsertQuery & insertQuery to ts #16988
  4. feat: migrate index to typescript #16227

Your other PRs are on hold while we try to focus efforts on releasing v7(-beta) soonish. Read more about our plans here; https://github.com/sequelize/meetings/wiki/Meeting-2024%E2%80%9003%E2%80%9022

@WikiRik, yes, this order is correct.

Out of interest, when would it be likely to have PR feat: add DataTypes.DATE.PLAIN #15565 reviewed/merged?

@lohart13
Copy link
Contributor Author

@WikiRik I've also add PR #17191 which can be merged at a similar time to #17158 and #17157

@sequelize-bot sequelize-bot bot added the conflicted This PR has merge conflicts and will not be present in the list of PRs to review label Apr 10, 2024
@sequelize-bot sequelize-bot bot removed the conflicted This PR has merge conflicts and will not be present in the list of PRs to review label Apr 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants