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: Add Cockroachdb support without transactions #16394
base: main
Are you sure you want to change the base?
feat: Add Cockroachdb support without transactions #16394
Conversation
…t and overrid postgres helpers for cockroachDB
…file, addressed some review comments
…hange in validtor-js package
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 just have a few more files that I need to check out in the cockroachdb folder but it looks very good to me. In the end it's not too many tests that add an id, but adding a util function for it in the future might be nice. Not for this PR imo.
@@ -233,6 +236,7 @@ We use a simple conventional commits convention: | |||
- `db2` | |||
- `ibmi` | |||
- `snowflake` | |||
- `cockroachdb` |
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.
This should be added to the scopes in .github/workflows/pr-title.yml as well
it(util.inspect(options, { depth: 2 }), () => { | ||
|
||
return expectsql( |
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.
it(util.inspect(options, { depth: 2 }), () => { | |
return expectsql( | |
it(util.inspect(options, { depth: 2 }), () => { | |
return expectsql( |
const dialectName = Support.getTestDialect(); | ||
const current = Support.sequelize; | ||
const sql = current.dialect.queryGenerator; | ||
const dialectName = Support.getTestDialect(); |
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.
const dialectName = Support.getTestDialect(); |
@@ -71,7 +71,7 @@ describe('json', () => { | |||
|
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 two tests above should produce the same result for cockroachdb as well right? You can add those already
default: `SELECT [user].*, [POSTS].[id] AS [POSTS.id], [POSTS].[title] AS [POSTS.title] FROM (SELECT [user].[id_user] AS [id], [user].[email], [user].[first_name] AS [firstName], [user].[last_name] AS [lastName] FROM [users] AS [user] ORDER BY [user].${TICK_LEFT}${TICK_LEFT}${TICK_LEFT}last_name${TICK_RIGHT}${TICK_RIGHT}${TICK_RIGHT} ASC${sql.addLimitAndOffset({ | ||
limit: 30, | ||
offset: 10, | ||
order: [['`user`.`last_name`', 'ASC']], | ||
}) | ||
}) AS [user] LEFT OUTER JOIN [post] AS [POSTS] ON [user].[id_user] = [POSTS].[user_id] ORDER BY [user].${TICK_LEFT}${TICK_LEFT}${TICK_LEFT}last_name${TICK_RIGHT}${TICK_RIGHT}${TICK_RIGHT} ASC;`, | ||
ibmi: `${`SELECT "user".*, "POSTS"."id" AS "POSTS.id", "POSTS"."title" AS "POSTS.title" FROM (SELECT "user"."id_user" AS "id", "user"."email", "user"."first_name" AS "firstName", "user"."last_name" AS "lastName" FROM "users" AS "user" ORDER BY "user"."""last_name""" ASC`}${ | ||
sql.addLimitAndOffset({ limit: 30, offset: 10, order: [['`user`.`last_name`', 'ASC']] }) | ||
ibmi: `${`SELECT "user".*, "POSTS"."id" AS "POSTS.id", "POSTS"."title" AS "POSTS.title" FROM (SELECT "user"."id_user" AS "id", "user"."email", "user"."first_name" AS "firstName", "user"."last_name" AS "lastName" FROM "users" AS "user" ORDER BY "user"."""last_name""" ASC`}${sql.addLimitAndOffset({ limit: 30, offset: 10, order: [['`user`.`last_name`', 'ASC']] }) | ||
}) AS "user" LEFT OUTER JOIN "post" AS "POSTS" ON "user"."id_user" = "POSTS"."user_id" ORDER BY "user"."""last_name""" ASC`, |
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.
Could you undo the changes to these lines?
@@ -849,7 +855,11 @@ describe(Support.getTestDialectTeaser('Model'), () => { | |||
await User.sync({ force: true }); | |||
const user = await User.create({}, { returning: true }); | |||
expect(user.get('id')).to.be.ok; | |||
expect(user.get('id')).to.equal(1); | |||
|
|||
// This test expects the ID to be 1 but in CockroachDB uses UUIDv4 to generate IDs. |
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.
Is there a different expectation we can add instead?
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 could manually add id = 1
for the record or fetch the record ID and compare if needed, but seemed redundant.
Let me know your thoughts @WikiRik
|
||
await userWithDefaults.sync({ force: true }); | ||
const user = await userWithDefaults.create({}); | ||
// uuid validation regex taken from http://stackoverflow.com/a/13653180/800016 |
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.
You can use the isUUID validator for this
expect(user2).to.not.be.null; | ||
await t.rollback(); | ||
}); | ||
|
||
// Disabled in sqlite because it only supports one connection at a time | ||
// Disabled in sqlite and cockroachdb because it only supports one connection at a time |
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.
// Disabled in sqlite and cockroachdb because it only supports one connection at a time | |
// Disabled in sqlite and partially for cockroachdb because it only supports one connection at a time |
|
||
const dialectName = Support.getTestDialect(); |
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.
const dialectName = Support.getTestDialect(); |
@@ -570,8 +576,8 @@ describe(Support.getTestDialectTeaser('QueryInterface'), () => { | |||
this.User = this.sequelize.define('users', { |
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'll review the tests below here after you've resolved the conflicts with the main branch
@WikiRik Hi! CockroachDB developer here - do you know if this PR is able to get reviewed? |
…:divye11/sequelize-cockroachdb into cockroachdb-support-without-transactions
…:divye11/sequelize-cockroachdb into cockroachdb-support-without-transactions
@divye11 are you able to resolve the merge conflicts so we can take another look at this? |
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.
Thank you for your work!
I have reviewed the source code part. I will be reviewing the tests next
ALTER RANGE default CONFIGURE ZONE USING "gc.ttlseconds" = 600; | ||
ALTER DATABASE system CONFIGURE ZONE USING "gc.ttlseconds" = 600; |
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.
ALTER RANGE default CONFIGURE ZONE USING "gc.ttlseconds" = 600; | |
ALTER DATABASE system CONFIGURE ZONE USING "gc.ttlseconds" = 600; | |
ALTER RANGE default CONFIGURE ZONE USING \"gc.ttlseconds\" = 600; | |
ALTER DATABASE system CONFIGURE ZONE USING \"gc.ttlseconds\" = 600; |
ALTER RANGE default CONFIGURE ZONE USING "gc.ttlseconds" = 600; | ||
ALTER DATABASE system CONFIGURE ZONE USING "gc.ttlseconds" = 600; |
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.
ALTER RANGE default CONFIGURE ZONE USING "gc.ttlseconds" = 600; | |
ALTER DATABASE system CONFIGURE ZONE USING "gc.ttlseconds" = 600; | |
ALTER RANGE default CONFIGURE ZONE USING \"gc.ttlseconds\" = 600; | |
ALTER DATABASE system CONFIGURE ZONE USING \"gc.ttlseconds\" = 600; |
|
||
export class INTEGER extends PostgresInteger { | ||
sanitize(value: number): unknown { | ||
if (value > Number.MAX_SAFE_INTEGER) { |
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.
If the value is > MAX_SAFE_INTEGER, it is already not reliable and should not be converted. This should throw an error
As that is already what BaseIntegerDataType
does, there are no changes needed here, you can just re-export PostgresInteger as INTEGER
import type { AbstractDataType, AcceptableTypeOf, ArrayOptions, BindParamOptions, DataType } from '../abstract/data-types'; | ||
import * as BaseTypes from '../abstract/data-types'; | ||
import { attributeTypeToSql } from '../abstract/data-types-utils'; | ||
import { ARRAY as PostgresArray, GEOGRAPHY as PostgresGeography, INTEGER as PostgresInteger } from '../postgres/data-types'; |
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.
Now that postgres has been moved to its own package, you will need to add an exports
config to postgres similar to what is done in core to access the internals (the _non-semver-use-at-your-own-risk_
export)
} else if (this.options.type instanceof BaseTypes.HSTORE) { | ||
ValidationErrorItem.throwDataTypeValidationError('cockroachdb does not support the HSTORE data type.\n See https://www.cockroachlabs.com/docs/stable/data-types/ for a list of supported data types.'); | ||
} else if (this.options.type instanceof BaseTypes.UUID) { | ||
ValidationErrorItem.throwDataTypeValidationError('cockroachdb does not support the UUID data type.\n See https://www.cockroachlabs.com/docs/stable/data-types/ for a list of supported data types.'); | ||
} else if (this.options.type instanceof BaseTypes.CITEXT) { | ||
ValidationErrorItem.throwDataTypeValidationError('cockroachdb does not support the CITEXT data type.\n See https://www.cockroachlabs.com/docs/stable/data-types/ for a list of supported data types.'); | ||
} |
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.
These error messages are redundant, as the types themselves will throw if they are not supported
The relevant support options to set to make them throw are:
supports.dataTypes.HSTORE
supports.dataTypes.CITEXT
Note that there is not one for UUID, as all dialects should support it (with a fallback to a binary field or text when not available). From the documentation you linked, it looks like cockroach does support UUID though
The error message about nested array is fine though, as it's an interaction between this type and its subtype
]); | ||
} | ||
|
||
getUuidV1FunctionCall(): string { |
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.
getUuidV4FunctionCall
is fine as it has a different implementation, but getUuidV1FunctionCall
is the same implementation so this method can be removed
try { | ||
await super.removeConstraint(tableName, constraintName, options); | ||
} catch (error: any) { | ||
if (error.message.includes('use DROP INDEX CASCADE instead')) { |
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 think the code should try to correct for this. It should alert the user, and the user should then use the cascade
option themselves, that option is already supported in RemoveConstraintQueryOptions
@@ -57,7 +58,7 @@ export class PostgresConnectionManager extends AbstractConnectionManager<PgConne | |||
#oidMap = new Map<number, TypeOids>(); | |||
#oidParserCache = new Map<number, TypeParser<any, any>>(); | |||
|
|||
constructor(dialect: PostgresDialect, sequelize: Sequelize) { | |||
constructor(dialect: PostgresDialect | CockroachDbDialect, sequelize: Sequelize) { |
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 postgres connection manager cannot have a direct reference to cockroachdb, it will create an package loop between @sequelize/postgres
and @sequelize/cockroachdb
.
After the merge of main
in this PR, we can look into how this can be solved. I think the solution would be to have CockroachDbDialect
extend PostgresDialect
, but that may require moving some init code somehow
@@ -857,7 +857,7 @@ ${associationOwner._getAssociationDebugList()}`); | |||
const missingIndexes = this.getIndexes() | |||
.filter(item1 => !existingIndexes.some(item2 => item1.name === item2.name)) | |||
.sort((index1, index2) => { | |||
if (this.sequelize.options.dialect === 'postgres') { | |||
if (this.sequelize.options.dialect === 'postgres' || this.sequelize.options.dialect === 'cockroachdb') { |
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.
Following merge of main in this PR, keep in mind that this.sequelize.options.dialect
should not be used anymore. Use this.sequelize.dialect.name
instead
import forOwn from 'lodash/forOwn'; | ||
import isEqual from 'lodash/isEqual'; | ||
|
||
const debug = logger.debugContext('sql:pg'); |
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.
const debug = logger.debugContext('sql:pg'); | |
const debug = logger.debugContext('sql:cockroachdb'); |
Pull Request Checklist
Description Of Change
Todos
...(dialect === 'cockroachdb' && { id: 1 })