<commit_before>const mysql = require('mysql');
const { formatSchemaQueryResults } = require('../utils');

const id = 'mysql';
const name = 'MySQL';

function getSchemaSql(database) {
  const whereSql = database
    ? `WHERE t.table_schema = '${database}'`
    : `WHERE t.table_schema NOT IN (
        'mysql', 
        'performance_schema', 
        'information_schema'
      )`;
  return `
    SELECT 
      t.table_schema, 
      t.table_name, 
      c.column_name, 
      c.data_type
    FROM 
      INFORMATION_SCHEMA.TABLES t 
      JOIN INFORMATION_SCHEMA.COLUMNS c ON t.table_schema = c.table_schema AND t.table_name = c.table_name 
    ${whereSql}
    ORDER BY 
      t.table_schema, 
      t.table_name, 
      c.ordinal_position
  `;
}

/**
 * Run query for connection
 * Should return { rows, incomplete }
 * @param {string} query
 * @param {object} connection
 */
function runQuery(query, connection) {
  const myConnection = mysql.createConnection({
    multipleStatements: true,
    host: connection.host,
    port: connection.port ? connection.port : 3306,
    user: connection.username,
    password: <PASSWORD>,
    database: connection.database,
    insecureAuth: connection.mysqlInsecureAuth,
    timezone: 'Z',
    supportBigNumbers: true
  });

  return new Promise((resolve, reject) => {
    let incomplete = false;
    const rows = [];

    myConnection.connect(err => {
      if (err) {
        return reject(err);
      }
      let queryError;
      let resultsSent = false;

      function continueOn() {
        if (!resultsSent) {
          resultsSent = true;
          if (queryError) {
            return reject(queryError);
          }
          return resolve({ rows, incomplete });
        }
      }

      const myQuery = myConnection.query(query);
      myQuery
        .on('error', function(err) {
          // Handle error,
          // an 'end' event will be emitted after this as well
          // so we'll call the callback there.
          queryError = err;
        })
        .on('result', function(row) {
          // If we haven't hit the max yet add row to results
          if (rows.length < connection.maxRows) {
            return rows.push(row);
          }

          // Too many rows
          incomplete = true;

          // Stop the query stream
          myConnection.pause();

          // Destroy the underlying connection
          // Calling end() will wait and eventually time out
          myConnection.destroy();
          continueOn();
        })
        .on('end', function() {
          // all rows have been received
          // This will not fire if we end the connection early
          // myConnection.end()
          // myConnection.destroy()
          myConnection.end(error => {
            if (error) {
              console.error('Error ending MySQL connection', error);
            }
            continueOn();
          });
        });
    });
  });
}

/**
 * Test connectivity of connection
 * @param {*} connection
 */
function testConnection(connection) {
  const query = "SELECT 'success' AS TestQuery;";
  return runQuery(query, connection);
}

/**
 * Get schema for connection
 * @param {*} connection
 */
function getSchema(connection) {
  const schemaSql = getSchemaSql(connection.database);
  return runQuery(schemaSql, connection).then(queryResult =>
    formatSchemaQueryResults(queryResult)
  );
}

const fields = [
  {
    key: 'host',
    formType: 'TEXT',
    label: 'Host/Server/IP Address'
  },
  {
    key: 'port',
    formType: 'TEXT',
    label: 'Port (optional)'
  },
  {
    key: 'database',
    formType: 'TEXT',
    label: 'Database'
  },
  {
    key: 'username',
    formType: 'TEXT',
    label: 'Database Username'
  },
  {
    key: 'password',
    formType: 'PASSWORD',
    label: 'Database Password'
  },
  {
    key: 'mysqlInsecureAuth',
    formType: 'CHECKBOX',
    label: 'Use old/insecure pre 4.1 Auth System'
  }
];

module.exports = {
  id,
  name,
  fields,
  getSchema,
  runQuery,
  testConnection
};<commit_msg>Added SSL support for MySQL/MariaDB<commit_after>const fs = require('fs');
const mysql = require('mysql');
const { formatSchemaQueryResults } = require('../utils');

const id = 'mysql';
const name = 'MySQL';

function getSchemaSql(database) {
  const whereSql = database
    ? `WHERE t.table_schema = '${database}'`
    : `WHERE t.table_schema NOT IN (
        'mysql', 
        'performance_schema', 
        'information_schema'
      )`;
  return `
    SELECT 
      t.table_schema, 
      t.table_name, 
      c.column_name, 
      c.data_type
    FROM 
      INFORMATION_SCHEMA.TABLES t 
      JOIN INFORMATION_SCHEMA.COLUMNS c ON t.table_schema = c.table_schema AND t.table_name = c.table_name 
    ${whereSql}
    ORDER BY 
      t.table_schema, 
      t.table_name, 
      c.ordinal_position
  `;
}

/**
 * Run query for connection
 * Should return { rows, incomplete }
 * @param {string} query
 * @param {object} connection
 */
function runQuery(query, connection) {
  const myConfig = {
    multipleStatements: true,
    host: connection.host,
    port: connection.port ? connection.port : 3306,
    user: connection.username,
    password: <PASSWORD>,
    database: connection.database,
    insecureAuth: connection.mysqlInsecureAuth,
    timezone: 'Z',
    supportBigNumbers: true,
    ssl: connection.mysqlSsl
  };
  // TODO cache key/cert values
  if (connection.mysqlKey && connection.mysqlCert) {
    myConfig.ssl = {
      key: fs.readFileSync(connection.mysqlKey),
      cert: fs.readFileSync(connection.mysqlCert)
    };
    if (connection.mysqlCA) {
      myConfig.ssl['ca'] = fs.readFileSync(connection.mysqlCA);
    }
  }

  return new Promise((resolve, reject) => {
    const connection = mysql.createConnection(myConfig);
    let incomplete = false;
    const rows = [];

    connection.connect(err => {
      if (err) {
        return reject(err);
      }
      let queryError;
      let resultsSent = false;

      function continueOn() {
        if (!resultsSent) {
          resultsSent = true;
          if (queryError) {
            return reject(queryError);
          }
          return resolve({ rows, incomplete });
        }
      }

      const myQuery = connection.query(query);
      myQuery
        .on('error', function(err) {
          // Handle error,
          // an 'end' event will be emitted after this as well
          // so we'll call the callback there.
          queryError = err;
        })
        .on('result', function(row) {
          // If we haven't hit the max yet add row to results
          if (rows.length < connection.maxRows) {
            return rows.push(row);
          }

          // Too many rows
          incomplete = true;

          // Stop the query stream
          connection.pause();

          // Destroy the underlying connection
          // Calling end() will wait and eventually time out
          connection.destroy();
          continueOn();
        })
        .on('end', function() {
          // all rows have been received
          // This will not fire if we end the connection early
          // myConnection.end()
          // myConnection.destroy()
          connection.end(error => {
            if (error) {
              console.error('Error ending MySQL connection', error);
            }
            continueOn();
          });
        });
    });
  });
}

/**
 * Test connectivity of connection
 * @param {*} connection
 */
function testConnection(connection) {
  const query = "SELECT 'success' AS TestQuery;";
  return runQuery(query, connection);
}

/**
 * Get schema for connection
 * @param {*} connection
 */
function getSchema(connection) {
  const schemaSql = getSchemaSql(connection.database);
  return runQuery(schemaSql, connection).then(queryResult =>
    formatSchemaQueryResults(queryResult)
  );
}

const fields = [
  {
    key: 'host',
    formType: 'TEXT',
    label: 'Host/Server/IP Address'
  },
  {
    key: 'port',
    formType: 'TEXT',
    label: 'Port (optional)'
  },
  {
    key: 'database',
    formType: 'TEXT',
    label: 'Database'
  },
  {
    key: 'username',
    formType: 'TEXT',
    label: 'Database Username'
  },
  {
    key: 'password',
    formType: 'PASSWORD',
    label: 'Database Password'
  },
  {
    key: '<KEY>',
    formType: 'CHECKBOX',
    label: 'Use SSL'
  },
  {
    key: 'mysqlCert',
    formType: 'TEXT',
    label: 'Database Certificate Path'
  },
  {
    key: '<KEY>',
    formType: 'TEXT',
    label: 'Database Key Path'
  },
  {
    key: 'mysqlCA',
    formType: 'TEXT',
    label: 'Database CA Path'
  },
  {
    key: 'mysqlInsecureAuth',
    formType: 'CHECKBOX',
    label: 'Use old/insecure pre 4.1 Auth System'
  }
];

module.exports = {
  id,
  name,
  fields,
  getSchema,
  runQuery,
  testConnection
};
{'fraction_non_alphanumeric': 0.12289677125966349, 'fraction_numerical': 0.001364256480218281, 'mean_word_length': 2.18731884057971, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4912838', 'n_tokens_mistral': 2654, 'n_tokens_neox': 2573, 'n_words': 670}