From 89f53844014fb384d80e8a3d0878f81fbc948fc3 Mon Sep 17 00:00:00 2001 From: Isak Date: Wed, 21 May 2025 16:10:07 +0200 Subject: [PATCH 1/5] strips serverVersion for zero-pad. Modifies minsuppertedversion for cypher 25 --- packages/language-support/src/version.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/language-support/src/version.ts b/packages/language-support/src/version.ts index 5400f6e0d..f0038a4d7 100644 --- a/packages/language-support/src/version.ts +++ b/packages/language-support/src/version.ts @@ -1,17 +1,21 @@ import semver from 'semver'; export function cypher25Supported(serverVersion: string) { - const minSupportedVersion = '5.27.0-2025040'; + const minSupportedVersion = '2025.8.0'; //TODO: set to correct value when cypher 25 is actually released const minSupported = semver.coerce(minSupportedVersion, { includePrerelease: false, }); - const current = semver.coerce(serverVersion, { includePrerelease: false }); + + const cleanedServerVersion = serverVersion.replace(/"(\.0+)(?=\d)"/g, '.'); + const current = semver.coerce(cleanedServerVersion, { + includePrerelease: false, + }); if (minSupported && current) { const comparison = semver.compare(minSupported, current); if (comparison === 0) { const minPrelease = semver.prerelease(minSupportedVersion)?.at(0); - const currentPrelease = semver.prerelease(serverVersion)?.at(0); + const currentPrelease = semver.prerelease(cleanedServerVersion)?.at(0); return ( typeof minPrelease === 'number' && From c33ebb9ddde1810b32aafde5665735625b3bb70e Mon Sep 17 00:00:00 2001 From: Isak Date: Thu, 22 May 2025 09:45:03 +0200 Subject: [PATCH 2/5] adapts cypher 25 check to the new dbms.components(), removing unused files/methods --- packages/language-support/src/index.ts | 1 - .../src/tests/version.test.ts | 23 -------------- packages/language-support/src/version.ts | 31 ------------------- packages/query-tools/src/metadataPoller.ts | 6 ++-- packages/query-tools/src/queries/version.ts | 22 +++++++------ packages/query-tools/src/schemaPoller.ts | 8 ++--- 6 files changed, 18 insertions(+), 73 deletions(-) delete mode 100644 packages/language-support/src/tests/version.test.ts delete mode 100644 packages/language-support/src/version.ts diff --git a/packages/language-support/src/index.ts b/packages/language-support/src/index.ts index c4ab87def..ce896dbdb 100644 --- a/packages/language-support/src/index.ts +++ b/packages/language-support/src/index.ts @@ -31,7 +31,6 @@ export type { Neo4jFunction, Neo4jProcedure, } from './types'; -export { cypher25Supported } from './version'; export { CypherLexer, CypherParser, CypherParserListener, CypherParserVisitor }; import CypherLexer from './generated-parser/CypherCmdLexer'; diff --git a/packages/language-support/src/tests/version.test.ts b/packages/language-support/src/tests/version.test.ts deleted file mode 100644 index 589526424..000000000 --- a/packages/language-support/src/tests/version.test.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { cypher25Supported } from '../version'; - -describe('version tests', () => { - test('cypher 25 should not be supported in 5.27.0', () => { - expect(cypher25Supported('5.27.0')).toBe(false); - }); - - test('cypher 25 should not be supported in 5.27.0-2025030', () => { - expect(cypher25Supported('5.27.0-2025030')).toBe(false); - }); - - test('cypher 25 should be supported 5.27.0-2025040 onwards', () => { - expect(cypher25Supported('5.27.0-2025040')).toBe(true); - expect(cypher25Supported('5.27.0-2025050')).toBe(true); - expect(cypher25Supported('5.27.0-2025060')).toBe(true); - expect(cypher25Supported('5.28')).toBe(true); - expect(cypher25Supported('5.28.0')).toBe(true); - expect(cypher25Supported('6.1.0')).toBe(true); - expect(cypher25Supported('5.27.0-beta')).toBe(false); - expect(cypher25Supported('5.27.0-alpha.2025050')).toBe(false); - expect(cypher25Supported('6.1.0-preRelease.3')).toBe(true); - }); -}); diff --git a/packages/language-support/src/version.ts b/packages/language-support/src/version.ts deleted file mode 100644 index f0038a4d7..000000000 --- a/packages/language-support/src/version.ts +++ /dev/null @@ -1,31 +0,0 @@ -import semver from 'semver'; - -export function cypher25Supported(serverVersion: string) { - const minSupportedVersion = '2025.8.0'; //TODO: set to correct value when cypher 25 is actually released - const minSupported = semver.coerce(minSupportedVersion, { - includePrerelease: false, - }); - - const cleanedServerVersion = serverVersion.replace(/"(\.0+)(?=\d)"/g, '.'); - const current = semver.coerce(cleanedServerVersion, { - includePrerelease: false, - }); - - if (minSupported && current) { - const comparison = semver.compare(minSupported, current); - if (comparison === 0) { - const minPrelease = semver.prerelease(minSupportedVersion)?.at(0); - const currentPrelease = semver.prerelease(cleanedServerVersion)?.at(0); - - return ( - typeof minPrelease === 'number' && - typeof currentPrelease === 'number' && - minPrelease <= currentPrelease - ); - } else { - return comparison < 0; - } - } - - return false; -} diff --git a/packages/query-tools/src/metadataPoller.ts b/packages/query-tools/src/metadataPoller.ts index 33a391e6e..89dcf1266 100644 --- a/packages/query-tools/src/metadataPoller.ts +++ b/packages/query-tools/src/metadataPoller.ts @@ -1,7 +1,6 @@ import type { CypherVersion } from '@neo4j-cypher/language-support'; import { _internalFeatureFlags, - cypher25Supported, cypherVersions, DbSchema, Neo4jFunction, @@ -115,14 +114,13 @@ export class ConnectedMetadataPoller extends MetadataPoller { constructor( databases: Database[], parameters: Record, - serverVersion: string | undefined, + languageVersions: string[] | undefined, private readonly connection: Neo4jConnection, private readonly events: EventEmitter, ) { super(); const supportsCypherAnnotation = - _internalFeatureFlags.cypher25 || - (serverVersion && cypher25Supported(serverVersion)); + _internalFeatureFlags.cypher25 || languageVersions?.includes('25'); this.dbSchema.parameters = parameters; diff --git a/packages/query-tools/src/queries/version.ts b/packages/query-tools/src/queries/version.ts index 0eda27287..b5e70ccd2 100644 --- a/packages/query-tools/src/queries/version.ts +++ b/packages/query-tools/src/queries/version.ts @@ -1,23 +1,25 @@ import { resultTransformers } from 'neo4j-driver'; import { ExecuteQueryArgs } from '../types/sdkTypes'; -/** - * Get dbms version - */ -export function getVersion(): ExecuteQueryArgs<{ - serverVersion: string | undefined; +export function getCypherVersions(): ExecuteQueryArgs<{ + languageVersions: string[] | undefined; }> { - const query = 'CALL dbms.components() YIELD versions'; + const query = 'CALL dbms.components() YIELD name, versions'; const resultTransformer = resultTransformers.mappedResultTransformer({ map(record) { const obj = record.toObject(); + const name = obj.name as string; const versions = obj.versions as string[]; - const version = versions?.at(0); - return version; + return { name, versions }; }, - collect(versions, summary) { - return { serverVersion: versions.at(0), summary }; + collect(rows, summary) { + rows.forEach((row) => { + if (row.name === 'Cypher') { + return { languageVersions: row.versions, summary }; + } + }); + return { languageVersions: ['5'], summary }; }, }); diff --git a/packages/query-tools/src/schemaPoller.ts b/packages/query-tools/src/schemaPoller.ts index 801813066..d8fb2fe29 100644 --- a/packages/query-tools/src/schemaPoller.ts +++ b/packages/query-tools/src/schemaPoller.ts @@ -13,7 +13,7 @@ import { } from './metadataPoller'; import { Neo4jConnection } from './neo4jConnection'; import { listDatabases } from './queries/databases.js'; -import { getVersion } from './queries/version'; +import { getCypherVersions } from './queries/version'; export type ConnnectionResult = { success: boolean; @@ -181,8 +181,8 @@ export class Neo4jSchemaPoller { ); const { query: versionQuery, queryConfig: versionQueryConfig } = - getVersion(); - const { serverVersion } = await this.driver.executeQuery( + getCypherVersions(); + const { languageVersions } = await this.driver.executeQuery( versionQuery, {}, versionQueryConfig, @@ -191,7 +191,7 @@ export class Neo4jSchemaPoller { this.metadata = new ConnectedMetadataPoller( databases, this.parameters, - serverVersion, + languageVersions, this.connection, this.events, ); From ca4961a87bb411cd53af79f60a944583c49abce6 Mon Sep 17 00:00:00 2001 From: Isak Date: Thu, 22 May 2025 09:59:20 +0200 Subject: [PATCH 3/5] switches so we call it cypherVersions everywhere --- packages/query-tools/src/metadataPoller.ts | 4 ++-- packages/query-tools/src/queries/version.ts | 6 +++--- packages/query-tools/src/schemaPoller.ts | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/query-tools/src/metadataPoller.ts b/packages/query-tools/src/metadataPoller.ts index 89dcf1266..d7a45f482 100644 --- a/packages/query-tools/src/metadataPoller.ts +++ b/packages/query-tools/src/metadataPoller.ts @@ -114,13 +114,13 @@ export class ConnectedMetadataPoller extends MetadataPoller { constructor( databases: Database[], parameters: Record, - languageVersions: string[] | undefined, + cypherVersions: string[] | undefined, private readonly connection: Neo4jConnection, private readonly events: EventEmitter, ) { super(); const supportsCypherAnnotation = - _internalFeatureFlags.cypher25 || languageVersions?.includes('25'); + _internalFeatureFlags.cypher25 || cypherVersions?.includes('25'); this.dbSchema.parameters = parameters; diff --git a/packages/query-tools/src/queries/version.ts b/packages/query-tools/src/queries/version.ts index b5e70ccd2..d0946c87b 100644 --- a/packages/query-tools/src/queries/version.ts +++ b/packages/query-tools/src/queries/version.ts @@ -2,7 +2,7 @@ import { resultTransformers } from 'neo4j-driver'; import { ExecuteQueryArgs } from '../types/sdkTypes'; export function getCypherVersions(): ExecuteQueryArgs<{ - languageVersions: string[] | undefined; + cypherVersions: string[] | undefined; }> { const query = 'CALL dbms.components() YIELD name, versions'; @@ -16,10 +16,10 @@ export function getCypherVersions(): ExecuteQueryArgs<{ collect(rows, summary) { rows.forEach((row) => { if (row.name === 'Cypher') { - return { languageVersions: row.versions, summary }; + return { cypherVersions: row.versions, summary }; } }); - return { languageVersions: ['5'], summary }; + return { cypherVersions: ['5'], summary }; }, }); diff --git a/packages/query-tools/src/schemaPoller.ts b/packages/query-tools/src/schemaPoller.ts index d8fb2fe29..b3ffbc4a3 100644 --- a/packages/query-tools/src/schemaPoller.ts +++ b/packages/query-tools/src/schemaPoller.ts @@ -182,7 +182,7 @@ export class Neo4jSchemaPoller { const { query: versionQuery, queryConfig: versionQueryConfig } = getCypherVersions(); - const { languageVersions } = await this.driver.executeQuery( + const { cypherVersions } = await this.driver.executeQuery( versionQuery, {}, versionQueryConfig, @@ -191,7 +191,7 @@ export class Neo4jSchemaPoller { this.metadata = new ConnectedMetadataPoller( databases, this.parameters, - languageVersions, + cypherVersions, this.connection, this.events, ); From 9fecd3b1cf3fb04a82146d2ea8f496e192857e4f Mon Sep 17 00:00:00 2001 From: Isak Date: Thu, 22 May 2025 10:15:41 +0200 Subject: [PATCH 4/5] switches array of all cypherversions to allCypherVersions to fix collision --- packages/language-support/src/index.ts | 2 +- packages/language-support/src/parserWrapper.ts | 8 ++++++-- packages/language-support/src/types.ts | 4 ++-- packages/query-tools/src/metadataPoller.ts | 4 ++-- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/packages/language-support/src/index.ts b/packages/language-support/src/index.ts index ce896dbdb..6fcf0c959 100644 --- a/packages/language-support/src/index.ts +++ b/packages/language-support/src/index.ts @@ -24,7 +24,7 @@ export { lintCypherQuery } from './syntaxValidation/syntaxValidation'; export type { SyntaxDiagnostic } from './syntaxValidation/syntaxValidation'; export { testData } from './tests/testData'; export { textMateGrammar } from './textMateGrammar'; -export { cypherVersions } from './types'; +export { allCypherVersions } from './types'; export type { CompletionItem, CypherVersion, diff --git a/packages/language-support/src/parserWrapper.ts b/packages/language-support/src/parserWrapper.ts index 3373d7ad8..8b35ed4e9 100644 --- a/packages/language-support/src/parserWrapper.ts +++ b/packages/language-support/src/parserWrapper.ts @@ -31,7 +31,11 @@ import { } from './helpers'; import { SyntaxDiagnostic } from './syntaxValidation/syntaxValidation'; import { SyntaxErrorsListener } from './syntaxValidation/syntaxValidationHelpers'; -import { CypherVersion, cypherVersionNumbers, cypherVersions } from './types'; +import { + CypherVersion, + cypherVersionNumbers, + allCypherVersions, +} from './types'; export interface ParsedStatement { command: ParsedCommand; @@ -494,7 +498,7 @@ class CypherVersionCollector extends ParseTreeListener { exitEveryRule(ctx: unknown) { if (ctx instanceof CypherVersionContext) { const parsedVersion = 'CYPHER ' + ctx.getText(); - cypherVersions.forEach((validVersion) => { + allCypherVersions.forEach((validVersion) => { if (parsedVersion === validVersion) { this.cypherVersion = parsedVersion; } diff --git a/packages/language-support/src/types.ts b/packages/language-support/src/types.ts index 512e5e937..0844f204b 100644 --- a/packages/language-support/src/types.ts +++ b/packages/language-support/src/types.ts @@ -7,9 +7,9 @@ export type ReturnDescription = { isDeprecated: boolean; }; -export const cypherVersions = ['CYPHER 5', 'CYPHER 25']; +export const allCypherVersions = ['CYPHER 5', 'CYPHER 25']; export const cypherVersionNumbers: string[] = ['5', '25']; -export type CypherVersion = (typeof cypherVersions)[number]; +export type CypherVersion = (typeof allCypherVersions)[number]; // we could parse this string for better types in the future export type Neo4jStringType = string; diff --git a/packages/query-tools/src/metadataPoller.ts b/packages/query-tools/src/metadataPoller.ts index d7a45f482..7ea4f3422 100644 --- a/packages/query-tools/src/metadataPoller.ts +++ b/packages/query-tools/src/metadataPoller.ts @@ -1,7 +1,7 @@ import type { CypherVersion } from '@neo4j-cypher/language-support'; import { _internalFeatureFlags, - cypherVersions, + allCypherVersions, DbSchema, Neo4jFunction, Neo4jProcedure, @@ -181,7 +181,7 @@ export class ConnectedMetadataPoller extends MetadataPoller { }); const versions: (CypherVersion | undefined)[] = supportsCypherAnnotation - ? cypherVersions + ? allCypherVersions : [undefined]; versions.forEach((cypherVersion) => { From 844c65d2fb47c1b732d46467774efa5eb9ee85ac Mon Sep 17 00:00:00 2001 From: Isak Date: Thu, 22 May 2025 13:54:37 +0200 Subject: [PATCH 5/5] review fix --- packages/query-tools/src/metadataPoller.ts | 4 +-- packages/query-tools/src/queries/version.ts | 38 +++++++++++++++++++-- packages/query-tools/src/schemaPoller.ts | 10 +++--- 3 files changed, 42 insertions(+), 10 deletions(-) diff --git a/packages/query-tools/src/metadataPoller.ts b/packages/query-tools/src/metadataPoller.ts index 7ea4f3422..b990743cc 100644 --- a/packages/query-tools/src/metadataPoller.ts +++ b/packages/query-tools/src/metadataPoller.ts @@ -114,13 +114,13 @@ export class ConnectedMetadataPoller extends MetadataPoller { constructor( databases: Database[], parameters: Record, - cypherVersions: string[] | undefined, + serverCypherVersions: string[] | undefined, private readonly connection: Neo4jConnection, private readonly events: EventEmitter, ) { super(); const supportsCypherAnnotation = - _internalFeatureFlags.cypher25 || cypherVersions?.includes('25'); + _internalFeatureFlags.cypher25 || serverCypherVersions?.includes('25'); this.dbSchema.parameters = parameters; diff --git a/packages/query-tools/src/queries/version.ts b/packages/query-tools/src/queries/version.ts index d0946c87b..79b9dde6c 100644 --- a/packages/query-tools/src/queries/version.ts +++ b/packages/query-tools/src/queries/version.ts @@ -1,8 +1,40 @@ import { resultTransformers } from 'neo4j-driver'; import { ExecuteQueryArgs } from '../types/sdkTypes'; +/** + * Get dbms version + */ +export function getVersion(): ExecuteQueryArgs<{ + serverVersion: string | undefined; +}> { + const query = 'CALL dbms.components() YIELD versions'; + + const resultTransformer = resultTransformers.mappedResultTransformer({ + map(record) { + const obj = record.toObject(); + const name = obj.name as string; + const versions = obj.versions as string[]; + return { name, versions }; + }, + collect(rows, summary) { + rows.forEach((row) => { + if (row.name === 'Neo4j Kernel') { + return { serverVersion: row.versions, summary }; + } + }); + //We should not reach this unless the "name" field changes + return { serverVersion: undefined, summary }; + }, + }); + + return { + query, + queryConfig: { resultTransformer, routing: 'READ', database: 'system' }, + }; +} + export function getCypherVersions(): ExecuteQueryArgs<{ - cypherVersions: string[] | undefined; + serverCypherVersions: string[] | undefined; }> { const query = 'CALL dbms.components() YIELD name, versions'; @@ -16,10 +48,10 @@ export function getCypherVersions(): ExecuteQueryArgs<{ collect(rows, summary) { rows.forEach((row) => { if (row.name === 'Cypher') { - return { cypherVersions: row.versions, summary }; + return { serverCypherVersions: row.versions, summary }; } }); - return { cypherVersions: ['5'], summary }; + return { serverCypherVersions: ['5'], summary }; }, }); diff --git a/packages/query-tools/src/schemaPoller.ts b/packages/query-tools/src/schemaPoller.ts index b3ffbc4a3..760450b42 100644 --- a/packages/query-tools/src/schemaPoller.ts +++ b/packages/query-tools/src/schemaPoller.ts @@ -180,18 +180,18 @@ export class Neo4jSchemaPoller { database, ); - const { query: versionQuery, queryConfig: versionQueryConfig } = + const { query: cypherVersionQuery, queryConfig: cypherVersionQueryConfig } = getCypherVersions(); - const { cypherVersions } = await this.driver.executeQuery( - versionQuery, + const { serverCypherVersions } = await this.driver.executeQuery( + cypherVersionQuery, {}, - versionQueryConfig, + cypherVersionQueryConfig, ); this.metadata = new ConnectedMetadataPoller( databases, this.parameters, - cypherVersions, + serverCypherVersions, this.connection, this.events, );