All files / server/executors mysql-executor.ts

93.93% Statements 31/33
90.9% Branches 30/33
100% Functions 7/7
96.42% Lines 27/28

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91                        6x   1x 1x 1x         5x 2x   3x 3x 2x   1x             3x   3x 3x     4x 3x   1x     3x               14x     12x     10x   10x 6x       4x 2x         2x       1x                     12x  
/**
 * MySQL database executor
 * Works with mysql2 driver
 */
 
import type { SQL } from 'drizzle-orm'
import type { DrizzleDatabase } from '../types'
import { BaseDatabaseExecutor } from './base-executor'
 
export class MySQLExecutor extends BaseDatabaseExecutor {
  async execute<T = any[]>(query: SQL | any, numericFields?: string[]): Promise<T> {
    // Handle Drizzle query objects directly
    if (query && typeof query === 'object' && typeof query.execute === 'function') {
      // This is a Drizzle query object, execute it directly
      const result = await query.execute()
      Eif (Array.isArray(result)) {
        return result.map(row => this.convertNumericFields(row, numericFields)) as T
      }
      return result as T
    }
    
    if (!this.db.execute) {
      throw new Error('MySQL database instance must have an execute method')
    }
    const result = await this.db.execute(query)
    if (Array.isArray(result)) {
      return result.map(row => this.convertNumericFields(row, numericFields)) as T
    }
    return result as T
  }
 
  /**
   * Convert numeric string fields to numbers (measure fields + numeric dimensions)
   */
  private convertNumericFields(row: any, numericFields?: string[]): any {
    Iif (!row || typeof row !== 'object') return row
    
    const converted: any = {}
    for (const [key, value] of Object.entries(row)) {
      // Only convert specified numeric fields (measures + numeric dimensions)
      // Other dimensions and time dimensions keep their original types
      if (numericFields && numericFields.includes(key)) {
        converted[key] = this.coerceToNumber(value)
      } else {
        converted[key] = value
      }
    }
    return converted
  }
 
  /**
   * Coerce a value to a number if it represents a numeric type
   */
  private coerceToNumber(value: any): any {
    // Handle null/undefined - preserve null values for aggregations
    if (value == null) return value
    
    // Already a number
    if (typeof value === 'number') return value
    
    // Handle string representations of numbers
    Eif (typeof value === 'string') {
      // Check for exact numeric strings
      if (/^-?\d+(\.\d+)?$/.test(value)) {
        return value.includes('.') ? parseFloat(value) : parseInt(value, 10)
      }
      
      // Check for other numeric formats (scientific notation, etc.)
      if (!isNaN(parseFloat(value)) && isFinite(parseFloat(value))) {
        return parseFloat(value)
      }
    }
    
    // Return as-is for non-numeric values
    return value
  }
 
  getEngineType(): 'mysql' | 'singlestore' {
    return 'mysql'
  }
}
 
/**
 * Factory function for creating MySQL executors
 */
export function createMySQLExecutor(
  db: DrizzleDatabase,
  schema?: any
): MySQLExecutor {
  return new MySQLExecutor(db, schema, 'mysql')
}