async runQuery(query) {
    const connection = this.connection;
    const driver = this.driver;
    const user = this.user;
    const connectionName = connection.name;
    const startTime = new Date();

    const queryContext = {
      executionId: uuidv4(),
      connectionClientId: this.id,
      driver: connection.driver,
      userId: user && user.id,
      userEmail: user && user.email,
      connectionId: connection.id,
      connectionName,
      query,
      startTime,
    };

    appLog.info(queryContext, 'Running query');

    let results;
    try {
      // If client is connected use that connection,
      // otherwise use driver.runQuery to run query with fresh one-off connection
      if (this.isConnected()) {
        // uses pre-existing connection to run query, and keeps connection open
        // lastActivityAt is updated both before and after query (the query could take a while)
        this.lastActivityAt = new Date();
        results = await this.client.runQuery(query);
        this.lastActivityAt = new Date();
      } else {
        // Opens a new connection to db, runs query, then closes connection
        results = await driver.runQuery(query, connection);
      }
    } catch (error) {
      // It is logged INFO because it isn't necessarily a server/application error
      // It could just be a bad query
      appLog.info(
        { ...queryContext, error: error.toString() },
        'Error running query'
      );

      // Rethrow the error
      // The error here is something expected and should be shown to user
      throw error;
    }

    let { rows, incomplete } = results;

    if (!Array.isArray(rows)) {
      appLog.warn(
        {
          driver: connection.driver,
          connectionId: connection.id,
          connectionName,
          query,
        },
        'Expected rows to be an array but received %s.',
        typeof rows
      );
      rows = [];
    }

    const columns = getColumns(rows);
    const stopTime = new Date();
    const queryRunTime = stopTime - startTime;

    const queryResult = {
      rows,
      columns,
      incomplete: Boolean(incomplete),
    };

    appLog.info(
      {
        ...queryContext,
        stopTime,
        queryRunTime,
        rowCount: rows.length,
        incomplete: Boolean(incomplete),
      },
      'Query finished'
    );

    return queryResult;
  }
Run query
If the connectionClient supports persistent database connections and is connected,
it'll use the database connection already established.
If not connected or does not support persistent connection,
it uses the driver.runQuery() implementation that will open a connection, run query, then close.
@param {string} query
@returns {Promise}