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

Method executeSQL

src/connectors/mysql/index.ts:606–686  ·  view source on GitHub ↗
(sql: string, options: ExecuteOptions, parameters?: any[])

Source from the content-addressed store, hash-verified

604 }
605
606 async executeSQL(sql: string, options: ExecuteOptions, parameters?: any[]): Promise<SQLResult> {
607 if (!this.pool) {
608 throw new Error("Not connected to database");
609 }
610
611 // Get a dedicated connection from the pool to ensure session consistency
612 // This is critical for session-specific features like LAST_INSERT_ID()
613 const conn = await this.pool.getConnection();
614 try {
615 // Engine-level read-only backstop: run the batch inside a READ ONLY
616 // transaction so MySQL rejects DML writes (INSERT/UPDATE/DELETE/REPLACE)
617 // that the keyword classifier missed (e.g. function-based writes). Note this
618 // does NOT stop DDL: statements like DROP/CREATE perform an implicit COMMIT
619 // that ends the read-only transaction first, so DDL escapes. Stacked-DDL
620 // payloads (e.g. `SELECT 1--1;DROP TABLE t`) are instead rejected upstream by
621 // the read-only classifier, which now splits `--`-hidden statements (see
622 // scanSingleLineCommentMySQL in sql-parser.ts).
623 if (options.readonly) {
624 await conn.query("START TRANSACTION READ ONLY");
625 }
626
627 // Apply maxRows limit to SELECT queries if specified
628 let processedSQL = sql;
629 if (options.maxRows) {
630 // Handle multi-statement SQL by processing each statement individually
631 const statements = splitSQLStatements(sql, "mysql");
632
633 const processedStatements = statements.map(statement =>
634 SQLRowLimiter.applyMaxRows(statement, options.maxRows)
635 );
636
637 processedSQL = processedStatements.join('; ');
638 if (sql.trim().endsWith(';')) {
639 processedSQL += ';';
640 }
641 }
642
643 // Use dedicated connection with multipleStatements: true support
644 // Pass parameters if provided, with optional query timeout
645 let results: any;
646 if (parameters && parameters.length > 0) {
647 try {
648 results = await conn.query({ sql: processedSQL, timeout: this.queryTimeoutMs }, parameters);
649 } catch (error) {
650 console.error(`[MySQL executeSQL] ERROR: ${(error as Error).message}`);
651 console.error(`[MySQL executeSQL] SQL: ${processedSQL}`);
652 console.error(`[MySQL executeSQL] Parameters: ${JSON.stringify(parameters)}`);
653 throw error;
654 }
655 } else {
656 results = await conn.query({ sql: processedSQL, timeout: this.queryTimeoutMs });
657 }
658
659 // MySQL2 returns results in format [rows, fields]
660 // Extract the first element which contains the actual row data
661 const [firstResult] = results;
662
663 // Parse results using shared utility that handles both single and multi-statement queries

Callers 1

createSSLTestsMethod · 0.95

Calls 4

splitSQLStatementsFunction · 0.85
parseQueryResultsFunction · 0.85
extractAffectedRowsFunction · 0.85
applyMaxRowsMethod · 0.80

Tested by 1

createSSLTestsMethod · 0.76