Repository: knex/knex
Pull request #5538 (2023-04-10 09:58:45)

Bump typescript from 5.0.3 to 5.0.4
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 5.0.3 to 5.0.4.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/Microsoft/TypeScript/releases">typescript's releases</a>.</em></p>
<blockquote>
<h2>TypeScript 5.0.4</h2>
<p>For release notes, check out the <a href="https://devblogs.microsoft.com/typescript/announcing-typescript-5-0/">release announcement</a>.</p>
<p>For the complete list of fixed issues, check out the</p>
<ul>
<li><a href="https://github.com/Microsoft/TypeScript/issues?utf8=%E2%9C%93&amp;q=milestone%3A%22TypeScript+5.0.0%22+is%3Aclosed+">fixed issues query for Typescript v5.0.0 (Beta)</a>.</li>
<li><a href="https://github.com/Microsoft/TypeScript/issues?utf8=%E2%9C%93&amp;q=milestone%3A%22TypeScript+5.0.1%22+is%3Aclosed+">fixed issues query for Typescript v5.0.1 (RC)</a>.</li>
<li><a href="https://github.com/Microsoft/TypeScript/issues?utf8=%E2%9C%93&amp;q=milestone%3A%22TypeScript+5.0.2%22+is%3Aclosed+">fixed issues query for Typescript v5.0.2 (Stable)</a>.</li>
<li><a href="https://github.com/Microsoft/TypeScript/issues?utf8=%E2%9C%93&amp;q=milestone%3A%22TypeScript+5.0.3%22+is%3Aclosed+">fixed issues query for Typescript v5.0.3 (Stable)</a>.</li>
<li><a href="https://github.com/Microsoft/TypeScript/issues?utf8=%E2%9C%93&amp;q=milestone%3A%22TypeScript+5.0.4%22+is%3Aclosed+">fixed issues query for Typescript v5.0.4 (Stable)</a>.</li>
</ul>
<p>Downloads are available on:</p>
<ul>
<li><a href="https://www.npmjs.com/package/typescript">npm</a></li>
<li><a href="https://www.nuget.org/packages/Microsoft.TypeScript.MSBuild">NuGet package</a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/microsoft/TypeScript/commit/3b45f4db12bbae97d10f62ec0e2d94858252c5ab"><code>3b45f4d</code></a> Bump version to 5.0.4 and LKG</li>
<li><a href="https://github.com/microsoft/TypeScript/commit/dc7e5a738a88f1dd72391d388d982df6ef082e90"><code>dc7e5a7</code></a> 🤖 Pick PR <a href="https://redirect.github.com/Microsoft/TypeScript/issues/53599">#53599</a> (Ignore <code>allowImportingTsExtensions</code>...) into release-5.0 (#...</li>
<li><a href="https://github.com/microsoft/TypeScript/commit/233b4b0540b90f1998374a5d06e80e380c9968fd"><code>233b4b0</code></a> 🤖 Pick PR <a href="https://redirect.github.com/Microsoft/TypeScript/issues/53611">#53611</a> (Remove error on redundant isolatedM...) into release-5.0 (#...</li>
<li><a href="https://github.com/microsoft/TypeScript/commit/365cb5815a891268c3c288000378c77172ba43a1"><code>365cb58</code></a> 🤖 Pick PR <a href="https://redirect.github.com/Microsoft/TypeScript/issues/53613">#53613</a> (Fix Go To Source Definition in `--m...) into release-5.0 (#...</li>
<li><a href="https://github.com/microsoft/TypeScript/commit/97dac8a261a93c0627330569aec131007b72d098"><code>97dac8a</code></a> 🤖 Pick PR <a href="https://redirect.github.com/Microsoft/TypeScript/issues/53665">#53665</a> (Fix crash when private id in array ...) into release-5.0 (#...</li>
<li><a href="https://github.com/microsoft/TypeScript/commit/b2d5d9b13f11d2a83f9694a2aed44caf34ff7249"><code>b2d5d9b</code></a> 🤖 Pick PR <a href="https://redirect.github.com/Microsoft/TypeScript/issues/53666">#53666</a> (Disable JSX recovery hack when in u...) into release-5.0 (#...</li>
<li>See full diff in <a href="https://github.com/Microsoft/TypeScript/compare/v5.0.3...v5.0.4">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=typescript&package-manager=npm_and_yarn&previous-version=5.0.3&new-version=5.0.4)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>

coveralls: 
[![Coverage Status](https://coveralls.io/builds/58714206/badge)](https://coveralls.io/builds/58714206)

Coverage: 92.838%. Remained the same when pulling **003ded33f06b849c6916cc704a2b57ac3d2f5e89 on dependabot/npm_and_yarn/typescript-5.0.4** into **ce56f4002092f2f4757c56e0b61caea72f3cdebd on master**.


Patch:
diff --git a/lib/dialects/mssql/query/mssql-querycompiler.js b/lib/dialects/mssql/query/mssql-querycompiler.js
index 4dbe18de..74c9b8ae 100644
--- a/lib/dialects/mssql/query/mssql-querycompiler.js
+++ b/lib/dialects/mssql/query/mssql-querycompiler.js
@@ -11,6 +11,7 @@ const {
 } = require('../../../formatter/wrappingFormatter');
 
 const components = [
+  'comments',
   'columns',
   'join',
   'lock',
diff --git a/lib/dialects/oracle/query/oracle-querycompiler.js b/lib/dialects/oracle/query/oracle-querycompiler.js
index 032281e7..ee934cb4 100644
--- a/lib/dialects/oracle/query/oracle-querycompiler.js
+++ b/lib/dialects/oracle/query/oracle-querycompiler.js
@@ -12,6 +12,7 @@ const { ReturningHelper } = require('../utils');
 const { isString } = require('../../../util/is');
 
 const components = [
+  'comments',
   'columns',
   'join',
   'where',
diff --git a/lib/query/querybuilder.js b/lib/query/querybuilder.js
index 547a6040..87a8cc3b 100644
--- a/lib/query/querybuilder.js
+++ b/lib/query/querybuilder.js
@@ -61,6 +61,7 @@ class Builder extends EventEmitter {
     this.client = client;
     this.and = this;
     this._single = {};
+    this._comments = [];
     this._statements = [];
     this._method = 'select';
     if (client.config) {
@@ -88,6 +89,7 @@ class Builder extends EventEmitter {
     const cloned = new this.constructor(this.client);
     cloned._method = this._method;
     cloned._single = clone(this._single);
+    cloned._comments = clone(this._comments);
     cloned._statements = clone(this._statements);
     cloned._debug = this._debug;
 
@@ -235,6 +237,21 @@ class Builder extends EventEmitter {
     return this;
   }
 
+  // Adds a comment to the query
+  comment(txt) {
+    if (!isString(txt)) {
+      throw new Error('Comment must be a string');
+    }
+    const forbiddenChars = ['/*', '*/', '?'];
+    if (forbiddenChars.some((chars) => txt.includes(chars))) {
+      throw new Error(`Cannot include ${forbiddenChars.join(', ')} in comment`);
+    }
+    this._comments.push({
+      comment: txt,
+    });
+    return this;
+  }
+
   // Allow for a sub-select to be explicitly aliased as a column,
   // without needing to compile the query in a where.
   as(column) {
diff --git a/lib/query/querycompiler.js b/lib/query/querycompiler.js
index a4a09d27..ca79dac9 100644
--- a/lib/query/querycompiler.js
+++ b/lib/query/querycompiler.js
@@ -28,6 +28,7 @@ const {
 const debugBindings = debug('knex:bindings');
 
 const components = [
+  'comments',
   'columns',
   'join',
   'where',
@@ -50,6 +51,7 @@ class QueryCompiler {
     this.method = builder._method || 'select';
     this.options = builder._options;
     this.single = builder._single;
+    this.queryComments = builder._comments;
     this.timeout = builder._timeout || false;
     this.cancelOnTimeout = builder._cancelOnTimeout || false;
     this.grouped = groupBy(builder._statements, 'grouping');
@@ -138,6 +140,7 @@ class QueryCompiler {
         case 'union':
           unionStatement = statement;
           break;
+        case 'comments':
         case 'columns':
         case 'join':
         case 'where':
@@ -317,6 +320,14 @@ class QueryCompiler {
     );
   }
 
+  // Add comments to the query
+  comments() {
+    if (!this.queryComments.length) return '';
+    return this.queryComments
+      .map((comment) => `/* ${comment.comment} */`)
+      .join(' ');
+  }
+
   _aggregate(stmt, { aliasSeparator = ' as ', distinctParentheses } = {}) {
     const value = stmt.value;
     const method = stmt.method;
diff --git a/package.json b/package.json
index bbceb466..b814701c 100644
--- a/package.json
+++ b/package.json
@@ -144,7 +144,7 @@
     "toxiproxy-node-client": "^2.0.6",
     "ts-node": "^10.9.1",
     "tsd": "^0.28.1",
-    "typescript": "5.0.3"
+    "typescript": "5.0.4"
   },
   "buildDependencies": [
     "rimraf"
diff --git a/types/index.d.ts b/types/index.d.ts
index c5ce16e1..a9f54ea0 100644
--- a/types/index.d.ts
+++ b/types/index.d.ts
@@ -597,6 +597,7 @@ export declare namespace Knex {
     as: As<TRecord, TResult>;
     columns: Select<TRecord, TResult>;
     column: Select<TRecord, TResult>;
+    comment: Comment<TRecord, TResult>;
     hintComment: HintComment<TRecord, TResult>;
     from: Table<TRecord, TResult>;
     fromRaw: Table<TRecord, TResult>;
@@ -1359,6 +1360,10 @@ export declare namespace Knex {
     ): QueryBuilder<TRecord, TResult>;
   }
 
+  interface Comment<TRecord extends {} = any, TResult = any> {
+    (comment: string): QueryBuilder<TRecord, TResult>;
+  }
+
   interface HintComment<TRecord extends {} = any, TResult = any> {
     (hint: string): QueryBuilder<TRecord, TResult>;
     (hints: readonly string[]): QueryBuilder<TRecord, TResult>;


Test patch:
diff --git a/test/integration2/query/select/selects.spec.js b/test/integration2/query/select/selects.spec.js
index ee4212be..dc7a2d28 100644
--- a/test/integration2/query/select/selects.spec.js
+++ b/test/integration2/query/select/selects.spec.js
@@ -863,6 +863,16 @@ describe('Selects', function () {
         });
       });
 
+      it('#1982 - Allow comments in db', async function () {
+        await knex('accounts')
+          .comment('Integration Comment')
+          .select('first_name', 'email')
+          .limit(1)
+          .then(function(results) {
+            expect(results).to.have.length(1);
+          });
+      });
+
       describe('recursive CTE support', function () {
         before(async function () {
           await knex.schema.dropTableIfExists('rcte');
diff --git a/test/unit/query/builder.js b/test/unit/query/builder.js
index 5fc31e81..c1e86f84 100644
--- a/test/unit/query/builder.js
+++ b/test/unit/query/builder.js
@@ -9440,6 +9440,156 @@ describe('QueryBuilder', () => {
     );
   });
 
+  it('#1982 - should allow query comments in querybuilder', () => {
+    testsql(
+      qb()
+        .from('testtable')
+        .comment('Added comment 1')
+        .comment('Added comment 2'),
+      {
+        mysql: {
+          sql:
+            '/* Added comment 1 */ /* Added comment 2 */ select * from `testtable`',
+          bindings: [],
+        },
+        oracledb: {
+          sql:
+            '/* Added comment 1 */ /* Added comment 2 */ select * from "testtable"',
+          bindings: [],
+        },
+        mssql: {
+          sql:
+            '/* Added comment 1 */ /* Added comment 2 */ select * from [testtable]',
+          bindings: [],
+        },
+        pg: {
+          sql:
+            '/* Added comment 1 */ /* Added comment 2 */ select * from "testtable"',
+          bindings: [],
+        },
+        'pg-redshift': {
+          sql:
+            '/* Added comment 1 */ /* Added comment 2 */ select * from "testtable"',
+          bindings: [],
+        },
+      }
+    );
+  });
+
+  it('#1982 (2) - should throw error on non string', () => {
+    try {
+      testsql(
+        qb()
+          .from('testtable')
+          .comment({ prop: 'val' }),
+        {
+          mysql: {
+            sql: '',
+            bindings: [],
+          },
+          oracledb: {
+            sql: '',
+            bindings: [],
+          },
+          mssql: {
+            sql: '',
+            bindings: [],
+          },
+          pg: {
+            sql: '',
+            bindings: [],
+          },
+          'pg-redshift': {
+            sql: '',
+            bindings: [],
+          },
+        }
+      );
+      expect(true).to.equal(
+        false,
+        'Expected to throw error in compilation about non-string'
+      );
+    } catch (error) {
+      expect(error.message).to.contain('Comment must be a string');
+    }
+  });
+
+  it('#1982 (3) - should throw error when there is subcomments', () => {
+    try {
+      testsql(
+        qb()
+          .from('testtable')
+          .comment('/* Hello world'),
+        {
+          mysql: {
+            sql: '',
+            bindings: [],
+          },
+          oracledb: {
+            sql: '',
+            bindings: [],
+          },
+          mssql: {
+            sql: '',
+            bindings: [],
+          },
+          pg: {
+            sql: '',
+            bindings: [],
+          },
+          'pg-redshift': {
+            sql: '',
+            bindings: [],
+          },
+        }
+      );
+      expect(true).to.equal(
+        false,
+        'Expected to throw error in compilation about non-string'
+      );
+    } catch (error) {
+      expect(error.message).to.contain('Cannot include /*, */, ? in comment');
+    }
+  });
+
+  it('#1982 (4) - should throw error when there is question mark', () => {
+    try {
+      testsql(
+        qb()
+          .from('testtable')
+          .comment('?'),
+        {
+          mysql: {
+            sql: '',
+            bindings: [],
+          },
+          oracledb: {
+            sql: '',
+            bindings: [],
+          },
+          mssql: {
+            sql: '',
+            bindings: [],
+          },
+          pg: {
+            sql: '',
+            bindings: [],
+          },
+          'pg-redshift': {
+            sql: '',
+            bindings: [],
+          },
+        }
+      );
+      expect(true).to.equal(
+        false,
+        'Expected to throw error in compilation about non-string'
+      );
+    } catch (error) {
+      expect(error.message).to.contain('Cannot include /*, */, ? in comment');
+    }
+  });
+
   it('#4199 - allows hint comments in subqueries', () => {
     testsql(
       qb()