diff --git a/package-lock.json b/package-lock.json index d998c738..77e74572 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,7 +22,7 @@ "@swc/core": "^1.15.18", "@swc/helpers": "^0.5.19", "@types/mocha": "^10.0.10", - "@types/node": "^25.4.0", + "@types/node": "^25.5.0", "@types/sinon": "^21.0.0", "c8": "^11.0.0", "globals": "^17.4.0", @@ -1716,9 +1716,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "25.4.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.4.0.tgz", - "integrity": "sha512-9wLpoeWuBlcbBpOY3XmzSTG3oscB6xjBEEtn+pYXTfhyXhIxC5FsBer2KTopBlvKEiW9l13po9fq+SJY/5lkhw==", + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz", + "integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==", "dev": true, "license": "MIT", "dependencies": { @@ -8484,7 +8484,7 @@ }, "packages/builder": { "name": "@sqb/builder", - "version": "4.23.1", + "version": "4.23.4", "license": "Apache-2.0", "dependencies": { "debug": "^4.4.3", @@ -8504,7 +8504,7 @@ }, "packages/connect": { "name": "@sqb/connect", - "version": "4.23.1", + "version": "4.23.4", "license": "Apache-2.0", "dependencies": { "@jsopen/objects": "^2.2.0", @@ -8533,13 +8533,13 @@ "node": ">=20.0" }, "peerDependencies": { - "@sqb/builder": "^4.23.1", + "@sqb/builder": "^4.23.4", "reflect-metadata": "^0.2.2" } }, "packages/migrator": { "name": "@sqb/migrator", - "version": "4.23.1", + "version": "4.23.4", "license": "Apache-2.0", "dependencies": { "cross-dirname": "^0.1.0", @@ -8556,14 +8556,14 @@ "node": ">=20.0" }, "peerDependencies": { - "@sqb/builder": "^4.23.1", - "@sqb/connect": "^4.23.1", - "@sqb/postgres": "^4.23.1" + "@sqb/builder": "^4.23.4", + "@sqb/connect": "^4.23.4", + "@sqb/postgres": "^4.23.4" } }, "packages/mssql-dialect": { "name": "@sqb/mssql-dialect", - "version": "4.23.1", + "version": "4.23.4", "license": "Apache-2.0", "dependencies": { "tslib": "^2.8.1" @@ -8575,12 +8575,12 @@ "node": ">=20.0" }, "peerDependencies": { - "@sqb/builder": "^4.23.1" + "@sqb/builder": "^4.23.4" } }, "packages/nestjs": { "name": "@sqb/nestjs", - "version": "4.23.1", + "version": "4.23.4", "license": "Apache-2.0", "dependencies": { "@jsopen/objects": "^2.2.0", @@ -8604,19 +8604,18 @@ "peerDependencies": { "@nestjs/common": ">=7.4.0", "@nestjs/core": ">=7.4.0", - "@sqb/builder": "^4.23.1", - "@sqb/connect": "^4.23.1", + "@sqb/builder": "^4.23.4", + "@sqb/connect": "^4.23.4", "reflect-metadata": "^0.2.2", "rxjs": ">=6.6.0" } }, "packages/oracle": { "name": "@sqb/oracle", - "version": "4.23.1", + "version": "4.23.4", "license": "Apache-2.0", "dependencies": { - "tslib": "^2.8.1", - "valgen": "^5.19.5" + "tslib": "^2.8.1" }, "devDependencies": { "@types/oracledb": "^6.10.1", @@ -8627,18 +8626,19 @@ "node": ">=20.0" }, "peerDependencies": { - "@sqb/builder": "^4.23.1", - "@sqb/connect": "^4.23.1", - "@sqb/oracle-dialect": "^4.23.1", + "@sqb/builder": "^4.23.4", + "@sqb/connect": "^4.23.4", + "@sqb/oracle-dialect": "^4.23.4", "oracledb": ">= 6.4.0" } }, "packages/oracle-dialect": { "name": "@sqb/oracle-dialect", - "version": "4.23.1", + "version": "4.23.4", "license": "Apache-2.0", "dependencies": { - "tslib": "^2.8.1" + "tslib": "^2.8.1", + "valgen": "^5.19.5" }, "devDependencies": { "expect": "^30.3.0" @@ -8647,12 +8647,12 @@ "node": ">=20.0" }, "peerDependencies": { - "@sqb/builder": "^4.23.1" + "@sqb/builder": "^4.23.4" } }, "packages/postgres": { "name": "@sqb/postgres", - "version": "4.23.1", + "version": "4.23.4", "license": "Apache-2.0", "dependencies": { "putil-promisify": "^1.10.1", @@ -8666,15 +8666,15 @@ "node": ">=20.0" }, "peerDependencies": { - "@sqb/builder": "^4.23.1", - "@sqb/connect": "^4.23.1", - "@sqb/postgres-dialect": "^4.23.1", + "@sqb/builder": "^4.23.4", + "@sqb/connect": "^4.23.4", + "@sqb/postgres-dialect": "^4.23.4", "postgrejs": ">=2.15.1 <3.0.0" } }, "packages/postgres-dialect": { "name": "@sqb/postgres-dialect", - "version": "4.23.1", + "version": "4.23.4", "license": "Apache-2.0", "dependencies": { "tslib": "^2.8.1" @@ -8686,12 +8686,12 @@ "node": ">=20.0" }, "peerDependencies": { - "@sqb/builder": "^4.23.1" + "@sqb/builder": "^4.23.4" } }, "packages/sqlite-dialect": { "name": "@sqb/sqlite-dialect", - "version": "4.23.1", + "version": "4.23.4", "license": "Apache-2.0", "dependencies": { "tslib": "^2.8.1" @@ -8703,12 +8703,12 @@ "node": ">=20.0" }, "peerDependencies": { - "@sqb/builder": "^4.23.1" + "@sqb/builder": "^4.23.4" } }, "packages/sqljs": { "name": "@sqb/sqljs", - "version": "4.23.1", + "version": "4.23.4", "license": "Apache-2.0", "dependencies": { "putil-promisify": "^1.10.1", @@ -8723,9 +8723,9 @@ "node": ">=20.0" }, "peerDependencies": { - "@sqb/builder": "^4.23.1", - "@sqb/connect": "^4.23.1", - "@sqb/sqlite-dialect": "^4.23.1", + "@sqb/builder": "^4.23.4", + "@sqb/connect": "^4.23.4", + "@sqb/sqlite-dialect": "^4.23.4", "sql.js": "^1.14.1" } } diff --git a/package.json b/package.json index e8672dc8..28764f8f 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "@swc/core": "^1.15.18", "@swc/helpers": "^0.5.19", "@types/mocha": "^10.0.10", - "@types/node": "^25.4.0", + "@types/node": "^25.5.0", "@types/sinon": "^21.0.0", "c8": "^11.0.0", "globals": "^17.4.0", diff --git a/packages/builder/package.json b/packages/builder/package.json index 8d7d6429..9c8ed415 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -1,7 +1,7 @@ { "name": "@sqb/builder", "description": "Extensible multi-dialect SQL query builder written with TypeScript", - "version": "4.23.3", + "version": "4.24.0", "author": "Panates", "private": false, "license": "Apache-2.0", diff --git a/packages/builder/src/query/query.ts b/packages/builder/src/query/query.ts index bf551c1c..f83e029d 100644 --- a/packages/builder/src/query/query.ts +++ b/packages/builder/src/query/query.ts @@ -8,6 +8,8 @@ import { GenerateOptions, GenerateResult } from '../types.js'; export declare interface Query extends EventEmitter {} export abstract class Query extends Serializable { + protected _comment?: string; + protected _commentDialect?: string[]; protected _params?: Record; constructor() { @@ -19,14 +21,24 @@ export abstract class Query extends Serializable { * Generates Sql script */ generate(options?: GenerateOptions): GenerateResult { - const ctx = new SerializeContext(options); + const ctx = new SerializeContext(this, options); if (this._params) ctx.params = { ...ctx.params, ...this._params }; ctx.serializeHooks = this.listeners('serialize'); /* generate output */ - const sql = this._serialize(ctx); + let sql = this._serialize(ctx); + sql = flattenText(sql, { noWrap: !ctx.prettyPrint }); + if ( + this._comment && + (!ctx.dialect || + !this._commentDialect || + this._commentDialect.includes(ctx.dialect)) + ) { + const lines = '-- ' + this._comment.split('\n').join('\n-- ') + '\n'; + sql = lines + sql; + } return { - sql: flattenText(sql, { noWrap: !ctx.prettyPrint }), + sql, params: ctx.preparedParams, paramOptions: ctx.paramOptions, returningFields: ctx.returningFields, @@ -39,6 +51,12 @@ export abstract class Query extends Serializable { this._params = obj; return this; } + + comment(text: string, dialect?: string[]): this { + this._comment = text; + this._commentDialect = dialect; + return this; + } } merge(Query.prototype, EventEmitter.prototype, { descriptor: true }); diff --git a/packages/builder/src/serialize-context.ts b/packages/builder/src/serialize-context.ts index 42f27e5e..53557c8b 100644 --- a/packages/builder/src/serialize-context.ts +++ b/packages/builder/src/serialize-context.ts @@ -1,5 +1,6 @@ import { SerializationType } from './enums.js'; import { SerializerRegistry } from './extensions.js'; +import type { Query } from './query/query.js'; import { Serializable } from './serializable.js'; import { isLogicalOperator, isQuery, isSerializable } from './typeguards.js'; import { @@ -79,7 +80,10 @@ export class SerializeContext implements GenerateOptions { returningFields?: { field: string; alias?: string }[]; strictParamGenId?: number; - constructor(opts?: GenerateOptions) { + constructor( + readonly rootQuery: Query, + opts?: GenerateOptions, + ) { if (opts) Object.assign(this, opts); } diff --git a/packages/builder/test/query-objects/select-query.spec.ts b/packages/builder/test/query-objects/select-query.spec.ts index c451f0e9..386245b3 100644 --- a/packages/builder/test/query-objects/select-query.spec.ts +++ b/packages/builder/test/query-objects/select-query.spec.ts @@ -235,4 +235,18 @@ describe('builder:serialize "SelectQuery"', () => { query.generate(); }).toThrow('Alias required for sub-select in "from"'); }); + + it('should serialize comment', () => { + const query = Select().comment('This is a comment'); + const result = query.generate(options); + expect(result.sql).toStrictEqual('-- This is a comment\nselect *'); + }); + + it('should serialize multiline comment', () => { + const query = Select().comment('This is a comment\nline 2\nline 3'); + const result = query.generate(options); + expect(result.sql).toStrictEqual( + '-- This is a comment\n-- line 2\n-- line 3\nselect *', + ); + }); }); diff --git a/packages/connect/package.json b/packages/connect/package.json index df450e54..3288adb5 100644 --- a/packages/connect/package.json +++ b/packages/connect/package.json @@ -1,7 +1,7 @@ { "name": "@sqb/connect", "description": "Multi-dialect database connection framework written with TypeScript", - "version": "4.23.3", + "version": "4.24.0", "author": "Panates", "license": "Apache-2.0", "scripts": { @@ -44,7 +44,7 @@ "postgrejs": "^2.22.9" }, "peerDependencies": { - "@sqb/builder": "^4.23.3", + "@sqb/builder": "^4.24.0", "reflect-metadata": "^0.2.2" }, "type": "module", diff --git a/packages/connect/src/orm/commands/count.command.ts b/packages/connect/src/orm/commands/count.command.ts index 3066f2fc..8f39aa94 100644 --- a/packages/connect/src/orm/commands/count.command.ts +++ b/packages/connect/src/orm/commands/count.command.ts @@ -23,6 +23,7 @@ export class CountCommand { await prepareFilter(entity, filter, where, 'T'); } const query = Select(Count()).from(entity.tableName + ' T'); + if (args.comment) query.comment(args.comment, args.commentDialect); if (where) query.where(where); // Execute query const resp = await connection.execute(query, { diff --git a/packages/connect/src/orm/commands/create.command.ts b/packages/connect/src/orm/commands/create.command.ts index df582c06..2027e657 100644 --- a/packages/connect/src/orm/commands/create.command.ts +++ b/packages/connect/src/orm/commands/create.command.ts @@ -1,6 +1,7 @@ import { Insert, Param } from '@sqb/builder'; import type { SqbConnection } from '../../client/sqb-connection.js'; import { EntityMetadata } from '../model/entity-metadata.js'; +import { Repository } from '../repository.class.js'; import { checkEnumValue, isColumnField, @@ -13,7 +14,7 @@ export type CreateCommandArgs = { connection: SqbConnection; values: any; returning?: boolean; -}; +} & Repository.CreateOptions; type CreateCommandContext = { entity: EntityMetadata; @@ -50,6 +51,7 @@ export class CreateCommand { throw new Error('No field given to create new entity instance'); const query = Insert(tableName, ctx.queryValues); + if (args.comment) query.comment(args.comment, args.commentDialect); if (args.returning) { const primaryIndexColumns = EntityMetadata.getPrimaryIndexColumns(entity); if (primaryIndexColumns.length) diff --git a/packages/connect/src/orm/commands/delete.command.ts b/packages/connect/src/orm/commands/delete.command.ts index ef81a9f8..231b2899 100644 --- a/packages/connect/src/orm/commands/delete.command.ts +++ b/packages/connect/src/orm/commands/delete.command.ts @@ -24,6 +24,7 @@ export class DeleteCommand { await prepareFilter(entity, filter, where); } const query = Delete(entity.tableName + ' T'); + if (args.comment) query.comment(args.comment, args.commentDialect); if (where) query.where(...where._items); // Execute query const resp = await connection.execute(query, { diff --git a/packages/connect/src/orm/commands/find.command.ts b/packages/connect/src/orm/commands/find.command.ts index 8f2ca395..91fcb428 100644 --- a/packages/connect/src/orm/commands/find.command.ts +++ b/packages/connect/src/orm/commands/find.command.ts @@ -336,6 +336,8 @@ export class FindCommand { | 'params' | 'onTransformRow' | 'prettyPrint' + | 'comment' + | 'commentDialect' >, ): Promise { // Generate select query @@ -348,6 +350,8 @@ export class FindCommand { this.mainEntity.tableName + ' as ' + this.mainAlias, ); + if (args.comment) query.comment(args.comment, args.commentDialect); + if (args.distinct) query.distinct(); query.where(...this._filter._items); diff --git a/packages/connect/src/orm/commands/update.command.ts b/packages/connect/src/orm/commands/update.command.ts index 8229a732..7ab14e8c 100644 --- a/packages/connect/src/orm/commands/update.command.ts +++ b/packages/connect/src/orm/commands/update.command.ts @@ -55,6 +55,7 @@ export class UpdateCommand { const query = Update(tableName + ' as T', ctx.queryValues).where( ...ctx.queryFilter, ); + if (args.comment) query.comment(args.comment, args.commentDialect); const qr = await args.connection.execute(query, { params: args.params ? [...args.params, ctx.queryParams] : ctx.queryParams, objectRows: false, diff --git a/packages/connect/src/orm/repository.class.ts b/packages/connect/src/orm/repository.class.ts index eaf43ca9..a72dbfc4 100644 --- a/packages/connect/src/orm/repository.class.ts +++ b/packages/connect/src/orm/repository.class.ts @@ -32,6 +32,8 @@ export namespace Repository { export interface CommandOptions { connection?: SqbConnection; prettyPrint?: boolean; + comment?: string; + commentDialect?: string[]; } export interface CreateOptions extends CommandOptions, Projection {} diff --git a/packages/migrator/package.json b/packages/migrator/package.json index 878d8f29..a6fe4b9b 100644 --- a/packages/migrator/package.json +++ b/packages/migrator/package.json @@ -1,7 +1,7 @@ { "name": "@sqb/migrator", "description": "Database migrator for SQB", - "version": "4.23.3", + "version": "4.24.0", "author": "Panates", "license": "Apache-2.0", "scripts": { @@ -28,9 +28,9 @@ "tslib": "^2.8.1" }, "peerDependencies": { - "@sqb/builder": "^4.23.3", - "@sqb/connect": "^4.23.3", - "@sqb/postgres": "^4.23.3" + "@sqb/builder": "^4.24.0", + "@sqb/connect": "^4.24.0", + "@sqb/postgres": "^4.24.0" }, "devDependencies": { "expect": "^30.3.0", diff --git a/packages/mssql-dialect/package.json b/packages/mssql-dialect/package.json index 851a77c8..561b27fc 100644 --- a/packages/mssql-dialect/package.json +++ b/packages/mssql-dialect/package.json @@ -1,7 +1,7 @@ { "name": "@sqb/mssql-dialect", "description": "SQB serialization extension for MS-SQL database", - "version": "4.23.3", + "version": "4.24.0", "author": "Panates", "license": "Apache-2.0", "scripts": { @@ -24,7 +24,7 @@ "tslib": "^2.8.1" }, "peerDependencies": { - "@sqb/builder": "^4.23.3" + "@sqb/builder": "^4.24.0" }, "devDependencies": { "expect": "^30.3.0" diff --git a/packages/nestjs/package.json b/packages/nestjs/package.json index 941dbb01..863972ab 100644 --- a/packages/nestjs/package.json +++ b/packages/nestjs/package.json @@ -1,7 +1,7 @@ { "name": "@sqb/nestjs", "description": "Nestjs module for data connection using SQB", - "version": "4.23.3", + "version": "4.24.0", "author": "Panates", "license": "Apache-2.0", "scripts": { @@ -39,8 +39,8 @@ "peerDependencies": { "@nestjs/common": ">=7.4.0", "@nestjs/core": ">=7.4.0", - "@sqb/builder": "^4.23.3", - "@sqb/connect": "^4.23.3", + "@sqb/builder": "^4.24.0", + "@sqb/connect": "^4.24.0", "reflect-metadata": "^0.2.2", "rxjs": ">=6.6.0" }, diff --git a/packages/oracle-dialect/package.json b/packages/oracle-dialect/package.json index e0194d01..6d361f84 100644 --- a/packages/oracle-dialect/package.json +++ b/packages/oracle-dialect/package.json @@ -1,7 +1,7 @@ { "name": "@sqb/oracle-dialect", "description": "SQB serialization extension for Oracle database", - "version": "4.23.3", + "version": "4.24.0", "author": "Panates", "license": "Apache-2.0", "scripts": { @@ -25,7 +25,7 @@ "tslib": "^2.8.1" }, "peerDependencies": { - "@sqb/builder": "^4.23.3" + "@sqb/builder": "^4.24.0" }, "devDependencies": { "expect": "^30.3.0" @@ -65,4 +65,4 @@ "publishConfig": { "access": "public" } -} +} \ No newline at end of file diff --git a/packages/oracle-dialect/src/oracle-serializer.ts b/packages/oracle-dialect/src/oracle-serializer.ts index ead2ec5a..0f3685c5 100644 --- a/packages/oracle-dialect/src/oracle-serializer.ts +++ b/packages/oracle-dialect/src/oracle-serializer.ts @@ -230,11 +230,16 @@ export class OracleSerializer implements SerializerExtension { o: any, defFn: DefaultSerializeFunction, ): string { - const v = ctx.params?.[o.name]; - if (v instanceof Date) { - ctx.preparedParams = ctx.preparedParams || {}; - ctx.preparedParams[o.name] = toDateString(v).replace('T', ' '); - return `TO_DATE(:${o.name}, 'yyyy-mm-dd hh24:mi:ss.SSSSS')`; + if ( + ctx.rootQuery._type === SerializationType.SELECT_QUERY || + ctx.rootQuery._type === SerializationType.DELETE_QUERY + ) { + const v = ctx.params?.[o.name]; + if (v instanceof Date) { + ctx.preparedParams = ctx.preparedParams || {}; + ctx.preparedParams[o.name] = toDateString(v).replace('T', ' '); + return `TO_DATE(:${o.name}, 'yyyy-mm-dd hh24:mi:ss.SSSSS')`; + } } return defFn(ctx, o); } diff --git a/packages/oracle/package.json b/packages/oracle/package.json index 615da65b..0cb30417 100644 --- a/packages/oracle/package.json +++ b/packages/oracle/package.json @@ -1,7 +1,7 @@ { "name": "@sqb/oracle", "description": "SQB serialization extension for Oracle database", - "version": "4.23.3", + "version": "4.24.0", "author": "Panates", "license": "Apache-2.0", "scripts": { @@ -24,9 +24,9 @@ "tslib": "^2.8.1" }, "peerDependencies": { - "@sqb/builder": "^4.23.3", - "@sqb/connect": "^4.23.3", - "@sqb/oracle-dialect": "^4.23.3", + "@sqb/builder": "^4.24.0", + "@sqb/connect": "^4.24.0", + "@sqb/oracle-dialect": "^4.24.0", "oracledb": ">= 6.4.0" }, "devDependencies": { @@ -71,4 +71,4 @@ "publishConfig": { "access": "public" } -} +} \ No newline at end of file diff --git a/packages/postgres-dialect/package.json b/packages/postgres-dialect/package.json index c151cc47..e715a1f6 100644 --- a/packages/postgres-dialect/package.json +++ b/packages/postgres-dialect/package.json @@ -1,7 +1,7 @@ { "name": "@sqb/postgres-dialect", "description": "SQB serialization extension for PostgreSQL database", - "version": "4.23.3", + "version": "4.24.0", "author": "Panates", "license": "Apache-2.0", "scripts": { @@ -24,7 +24,7 @@ "tslib": "^2.8.1" }, "peerDependencies": { - "@sqb/builder": "^4.23.3" + "@sqb/builder": "^4.24.0" }, "devDependencies": { "expect": "^30.3.0" diff --git a/packages/postgres/package.json b/packages/postgres/package.json index c8ebf446..10800ca8 100644 --- a/packages/postgres/package.json +++ b/packages/postgres/package.json @@ -1,7 +1,7 @@ { "name": "@sqb/postgres", "description": "SQB serialization extension for PostgreSQL database", - "version": "4.23.3", + "version": "4.24.0", "author": "Panates", "license": "Apache-2.0", "scripts": { @@ -29,9 +29,9 @@ "postgrejs": "^2.22.2" }, "peerDependencies": { - "@sqb/builder": "^4.23.3", - "@sqb/connect": "^4.23.3", - "@sqb/postgres-dialect": "^4.23.3", + "@sqb/builder": "^4.24.0", + "@sqb/connect": "^4.24.0", + "@sqb/postgres-dialect": "^4.24.0", "postgrejs": ">=2.15.1 <3.0.0" }, "type": "module", diff --git a/packages/sqlite-dialect/package.json b/packages/sqlite-dialect/package.json index ca0c7ce8..518192ca 100644 --- a/packages/sqlite-dialect/package.json +++ b/packages/sqlite-dialect/package.json @@ -1,7 +1,7 @@ { "name": "@sqb/sqlite-dialect", "description": "SQB serialization extension for SQLite database", - "version": "4.23.3", + "version": "4.24.0", "author": "Panates", "license": "Apache-2.0", "scripts": { @@ -24,7 +24,7 @@ "tslib": "^2.8.1" }, "peerDependencies": { - "@sqb/builder": "^4.23.3" + "@sqb/builder": "^4.24.0" }, "devDependencies": { "expect": "^30.3.0" diff --git a/packages/sqljs/package.json b/packages/sqljs/package.json index b9fa89bc..89d919ee 100644 --- a/packages/sqljs/package.json +++ b/packages/sqljs/package.json @@ -1,7 +1,7 @@ { "name": "@sqb/sqljs", "description": "SQB serialization extension for sql.js driver", - "version": "4.23.3", + "version": "4.24.0", "author": "Panates", "license": "Apache-2.0", "scripts": { @@ -30,9 +30,9 @@ "sql.js": "^1.14.1" }, "peerDependencies": { - "@sqb/builder": "^4.23.3", - "@sqb/connect": "^4.23.3", - "@sqb/sqlite-dialect": "^4.23.3", + "@sqb/builder": "^4.24.0", + "@sqb/connect": "^4.24.0", + "@sqb/sqlite-dialect": "^4.24.0", "sql.js": "^1.14.1" }, "type": "module",