MCPcopy Index your code
hub / github.com/bytebase/dbhub / executeReadOnly

Method executeReadOnly

src/connectors/sqlserver/index.ts:718–802  ·  view source on GitHub ↗

* Execute a query inside a transaction that always rolls back, preventing * any modifications from persisting. SQL Server has no native READ ONLY * transaction mode, so this is the defense-in-depth backstop behind the * keyword classifier. * * Because the rollback guard is application

(
    processedSQL: string,
    parameters?: any[],
  )

Source from the content-addressed store, hash-verified

716 * COMMIT/ROLLBACK inside string literals that stripCommentsAndStrings removes
717 */
718 private async executeReadOnly(
719 processedSQL: string,
720 parameters?: any[],
721 ): Promise<SQLResult> {
722 const cleaned = stripCommentsAndStrings(processedSQL, "sqlserver").toLowerCase();
723 if (/\b(?:commit|rollback)\b/.test(cleaned)) {
724 throw new Error(
725 "Read-only mode: transaction control statements (COMMIT, ROLLBACK) are not allowed",
726 );
727 }
728 if (/\b(?:exec|execute|sp_executesql|xp_cmdshell)\b/.test(cleaned)) {
729 throw new Error(
730 "Read-only mode: dynamic SQL execution (EXEC, EXECUTE, sp_executesql, xp_cmdshell) is not allowed",
731 );
732 }
733
734 const transaction = new sql.Transaction(this.connection!);
735 await transaction.begin();
736
737 const request = new sql.Request(transaction);
738 const messages: DatabaseMessage[] = [];
739 request.on(
740 'info',
741 (info: { message: string; number?: number; class?: number; lineNumber?: number }) => {
742 messages.push({
743 text: info.message,
744 severity: info.class !== undefined ? String(info.class) : undefined,
745 code: info.number,
746 line: info.lineNumber,
747 });
748 },
749 );
750
751 if (parameters && parameters.length > 0) {
752 parameters.forEach((param, index) => {
753 const paramName = `p${index + 1}`;
754 if (typeof param === 'string') {
755 request.input(paramName, sql.VarChar, param);
756 } else if (typeof param === 'number') {
757 if (Number.isInteger(param)) {
758 request.input(paramName, sql.Int, param);
759 } else {
760 request.input(paramName, sql.Float, param);
761 }
762 } else if (typeof param === 'boolean') {
763 request.input(paramName, sql.Bit, param);
764 } else if (param === null || param === undefined) {
765 request.input(paramName, sql.VarChar, param);
766 } else if (Array.isArray(param)) {
767 request.input(paramName, sql.VarChar, JSON.stringify(param));
768 } else {
769 request.input(paramName, sql.VarChar, JSON.stringify(param));
770 }
771 });
772 }
773
774 let result;
775 let queryFailed = false;

Callers 1

executeSQLMethod · 0.95

Calls 1

stripCommentsAndStringsFunction · 0.85

Tested by

no test coverage detected