<commit_before>/*-------------------------------------------------------------------------
*
* Copyright (c) 2003-2014, PostgreSQL Global Development Group
* Copyright (c) 2004, Open Cloud Limited.
*
*
*-------------------------------------------------------------------------
*/

package org.postgresql.core.v3;

import org.postgresql.PGProperty;
import org.postgresql.core.ConnectionFactory;
import org.postgresql.core.Encoding;
import org.postgresql.core.Logger;
import org.postgresql.core.PGStream;
import org.postgresql.core.ProtocolConnection;
import org.postgresql.core.ServerVersion;
import org.postgresql.core.SetupQueryRunner;
import org.postgresql.core.Utils;
import org.postgresql.core.v2.SocketFactoryFactory;
import org.postgresql.hostchooser.GlobalHostStatusTracker;
import org.postgresql.hostchooser.HostChooser;
import org.postgresql.hostchooser.HostChooserFactory;
import org.postgresql.hostchooser.HostRequirement;
import org.postgresql.hostchooser.HostStatus;
import org.postgresql.sspi.SSPIClient;
import org.postgresql.util.GT;
import org.postgresql.util.HostSpec;
import org.postgresql.util.MD5Digest;
import org.postgresql.util.PSQLException;
import org.postgresql.util.PSQLState;
import org.postgresql.util.PSQLWarning;
import org.postgresql.util.ServerErrorMessage;
import org.postgresql.util.UnixCrypt;

import java.io.IOException;
import java.net.ConnectException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.TimeZone;

import javax.net.SocketFactory;

/**
 * ConnectionFactory implementation for version 3 (7.4+) connections.
 *
 * @author <NAME> (<EMAIL>), based on the previous implementation
 */
public class ConnectionFactoryImpl extends ConnectionFactory {
  private static final int AUTH_REQ_OK = 0;
  private static final int AUTH_REQ_KRB4 = 1;
  private static final int AUTH_REQ_KRB5 = 2;
  private static final int AUTH_REQ_PASSWORD = 3;
  private static final int AUTH_REQ_CRYPT = 4;
  private static final int AUTH_REQ_MD5 = 5;
  private static final int AUTH_REQ_SCM = 6;
  private static final int AUTH_REQ_GSS = 7;
  private static final int AUTH_REQ_GSS_CONTINUE = 8;
  private static final int AUTH_REQ_SSPI = 9;

  /**
   * Marker exception; thrown when we want to fall back to using V2.
   */
  private static class UnsupportedProtocolException extends IOException {
  }

  public ProtocolConnection openConnectionImpl(HostSpec[] hostSpecs, String user, String database,
      Properties info, Logger logger) throws SQLException {
    // Extract interesting values from the info properties:
    // - the SSL setting
    boolean requireSSL;
    boolean trySSL;
    String sslmode = PGProperty.SSL_MODE.get(info);
    if (sslmode == null) { // Fall back to the ssl property
      // assume "true" if the property is set but empty
      requireSSL = trySSL = PGProperty.SSL.getBoolean(info) || "".equals(PGProperty.SSL.get(info));
    } else {
      if ("disable".equals(sslmode)) {
        requireSSL = trySSL = false;
      } else if ("require".equals(sslmode) || "verify-ca".equals(sslmode)
          || "verify-full".equals(sslmode)) {
        requireSSL = trySSL = true;
      } else {
        throw new PSQLException(GT.tr("Invalid sslmode value: {0}", sslmode),
            PSQLState.CONNECTION_UNABLE_TO_CONNECT);
      }
    }

    // - the TCP keep alive setting
    boolean requireTCPKeepAlive = PGProperty.TCP_KEEP_ALIVE.getBoolean(info);

    // NOTE: To simplify this code, it is assumed that if we are
    // using the V3 protocol, then the database is at least 7.4. That
    // eliminates the need to check database versions and maintain
    // backward-compatible code here.
    //
    // Change by <NAME> <<EMAIL>>

    int connectTimeout = PGProperty.CONNECT_TIMEOUT.getInt(info) * 1000;

    // - the targetServerType setting
    HostRequirement targetServerType;
    try {
      targetServerType =
          HostRequirement.valueOf(info.getProperty("targetServerType", HostRequirement.any.name()));
    } catch (IllegalArgumentException ex) {
      throw new PSQLException(
          GT.tr("Invalid targetServerType value: {0}", info.getProperty("targetServerType")),
          PSQLState.CONNECTION_UNABLE_TO_CONNECT);
    }

    SocketFactory socketFactory = SocketFactoryFactory.getSocketFactory(info);

    HostChooser hostChooser =
        HostChooserFactory.createHostChooser(hostSpecs, targetServerType, info);
    Iterator<HostSpec> hostIter = hostChooser.iterator();
    while (hostIter.hasNext()) {
      HostSpec hostSpec = hostIter.next();

      if (logger.logDebug()) {
        logger.debug("Trying to establish a protocol version 3 connection to " + hostSpec);
      }

      //
      // Establish a connection.
      //

      PGStream newStream = null;
      try {
        newStream = new PGStream(socketFactory, hostSpec, connectTimeout);

        // Construct and send an ssl startup packet if requested.
        if (trySSL) {
          newStream = enableSSL(newStream, requireSSL, info, logger, connectTimeout);
        }

        // Set the socket timeout if the "socketTimeout" property has been set.
        int socketTimeout = PGProperty.SOCKET_TIMEOUT.getInt(info);
        if (socketTimeout > 0) {
          newStream.getSocket().setSoTimeout(socketTimeout * 1000);
        }

        // Enable TCP keep-alive probe if required.
        newStream.getSocket().setKeepAlive(requireTCPKeepAlive);

        // Try to set SO_SNDBUF and SO_RECVBUF socket options, if requested.
        // If receiveBufferSize and send_buffer_size are set to a value greater
        // than 0, adjust. -1 means use the system default, 0 is ignored since not
        // supported.

        // Set SO_RECVBUF read buffer size
        int receiveBufferSize = PGProperty.RECEIVE_BUFFER_SIZE.getInt(info);
        if (receiveBufferSize > -1) {
          // value of 0 not a valid buffer size value
          if (receiveBufferSize > 0) {
            newStream.getSocket().setReceiveBufferSize(receiveBufferSize);
          } else {
            logger.info("Ignore invalid value for receiveBufferSize: " + receiveBufferSize);
          }
        }

        // Set SO_SNDBUF write buffer size
        int sendBufferSize = PGProperty.SEND_BUFFER_SIZE.getInt(info);
        if (sendBufferSize > -1) {
          if (sendBufferSize > 0) {
            newStream.getSocket().setSendBufferSize(sendBufferSize);
          } else {
            logger.info("Ignore invalid value for sendBufferSize: " + sendBufferSize);
          }
        }

        logger.info("Receive Buffer Size is " + newStream.getSocket().getReceiveBufferSize());
        logger.info("Send Buffer Size is " + newStream.getSocket().getSendBufferSize());

        List<String[]> paramList = new ArrayList<String[]>();
        paramList.add(new String[]{"user", user});
        paramList.add(new String[]{"database", database});
        paramList.add(new String[]{"client_encoding", "UTF8"});
        paramList.add(new String[]{"DateStyle", "ISO"});
        paramList.add(new String[]{"TimeZone", createPostgresTimeZone()});
        String assumeMinServerVersion = PGProperty.ASSUME_MIN_SERVER_VERSION.get(info);
        if (Utils.parseServerVersionStr(assumeMinServerVersion)
            >= ServerVersion.v9_0.getVersionNum()) {
          // User is explicitly telling us this is a 9.0+ server so set properties here:
          paramList.add(new String[]{"extra_float_digits", "3"});
          String appName = PGProperty.APPLICATION_NAME.get(info);
          if (appName != null) {
            paramList.add(new String[]{"application_name", appName});
          }
        } else {
          // User has not explicitly told us that this is a 9.0+ server so stick to old default:
          paramList.add(new String[]{"extra_float_digits", "2"});
        }

        String currentSchema = PGProperty.CURRENT_SCHEMA.get(info);
        if (currentSchema != null) {
          paramList.add(new String[]{"search_path", currentSchema});
        }

        String[][] params = paramList.toArray(new String[][]{});

        sendStartupPacket(newStream, params, logger);

        // Do authentication (until AuthenticationOk).
        doAuthentication(newStream, hostSpec.getHost(), user, info, logger);

        // Do final startup.
        ProtocolConnectionImpl protoConnection =
            new ProtocolConnectionImpl(newStream, user, database, info, logger, connectTimeout);
        readStartupMessages(newStream, protoConnection, logger);

        // Check Master or Slave
        HostStatus hostStatus = HostStatus.ConnectOK;
        if (targetServerType != HostRequirement.any) {
          hostStatus = isMaster(protoConnection, logger) ? HostStatus.Master : HostStatus.Slave;
        }
        GlobalHostStatusTracker.reportHostStatus(hostSpec, hostStatus);
        if (!targetServerType.allowConnectingTo(hostStatus)) {
          protoConnection.close();
          if (hostIter.hasNext()) {
            // still more addresses to try
            continue;
          }
          throw new PSQLException(GT
              .tr("Could not find a server with specified targetServerType: {0}", targetServerType),
              PSQLState.CONNECTION_UNABLE_TO_CONNECT);
        }

        runInitialQueries(protoConnection, info, logger);

        // And we're done.
        return protoConnection;
      } catch (UnsupportedProtocolException upe) {
        // Swallow this and return null so ConnectionFactory tries the next protocol.
        if (logger.logDebug()) {
          logger.debug("Protocol not supported, abandoning connection.");
        }
        closeStream(newStream);
        return null;
      } catch (ConnectException cex) {
        // Added by <NAME> <<EMAIL>>
        // ConnectException is thrown when the connection cannot be made.
        // we trap this an return a more meaningful message for the end user
        GlobalHostStatusTracker.reportHostStatus(hostSpec, HostStatus.ConnectFail);
        if (hostIter.hasNext()) {
          // still more addresses to try
          continue;
        }
        throw new PSQLException(GT.tr(
            "Connection to {0} refused. Check that the hostname and port are correct and that the postmaster is accepting TCP/IP connections.",
            hostSpec), PSQLState.CONNECTION_UNABLE_TO_CONNECT, cex);
      } catch (IOException ioe) {
        closeStream(newStream);
        GlobalHostStatusTracker.reportHostStatus(hostSpec, HostStatus.ConnectFail);
        if (hostIter.hasNext()) {
          // still more addresses to try
          continue;
        }
        throw new PSQLException(GT.tr("The connection attempt failed."),
            PSQLState.CONNECTION_UNABLE_TO_CONNECT, ioe);
      } catch (SQLException se) {
        closeStream(newStream);
        if (hostIter.hasNext()) {
          // still more addresses to try
          continue;
        }
        throw se;
      }
    }
    throw new PSQLException(GT.tr("The connection url is invalid."),
        PSQLState.CONNECTION_UNABLE_TO_CONNECT);
  }

  /**
   * Convert Java time zone to postgres time zone. All others stay the same except that GMT+nn
   * changes to GMT-nn and vise versa.
   *
   * @return The current JVM time zone in postgresql format.
   */
  private static String createPostgresTimeZone() {
    String tz = TimeZone.getDefault().getID();
    if (tz.length() <= 3 || !tz.startsWith("GMT")) {
      return tz;
    }
    char sign = tz.charAt(3);
    String start;
    if (sign == '+') {
      start = "GMT-";
    } else if (sign == '-') {
      start = "GMT+";
    } else {
      // unknown type
      return tz;
    }

    return start + tz.substring(4);
  }

  private PGStream enableSSL(PGStream pgStream, boolean requireSSL, Properties info, Logger logger,
      int connectTimeout) throws IOException, SQLException {
    if (logger.logDebug()) {
      logger.debug(" FE=> SSLRequest");
    }

    // Send SSL request packet
    pgStream.SendInteger4(8);
    pgStream.SendInteger2(1234);
    pgStream.SendInteger2(5679);
    pgStream.flush();

    // Now get the response from the backend, one of N, E, S.
    int beresp = pgStream.ReceiveChar();
    switch (beresp) {
      case 'E':
        if (logger.logDebug()) {
          logger.debug(" <=BE SSLError");
        }

        // Server doesn't even know about the SSL handshake protocol
        if (requireSSL) {
          throw new PSQLException(GT.tr("The server does not support SSL."),
              PSQLState.CONNECTION_REJECTED);
        }

        // We have to reconnect to continue.
        pgStream.close();
        return new PGStream(pgStream.getSocketFactory(), pgStream.getHostSpec(), connectTimeout);

      case 'N':
        if (logger.logDebug()) {
          logger.debug(" <=BE SSLRefused");
        }

        // Server does not support ssl
        if (requireSSL) {
          throw new PSQLException(GT.tr("The server does not support SSL."),
              PSQLState.CONNECTION_REJECTED);
        }

        return pgStream;

      case 'S':
        if (logger.logDebug()) {
          logger.debug(" <=BE SSLOk");
        }

        // Server supports ssl
        org.postgresql.ssl.MakeSSL.convert(pgStream, info, logger);
        return pgStream;

      default:
        throw new PSQLException(GT.tr("An error occurred while setting up the SSL connection."),
            PSQLState.PROTOCOL_VIOLATION);
    }
  }

  private void sendStartupPacket(PGStream pgStream, String[][] params, Logger logger)
      throws IOException {
    if (logger.logDebug()) {
      StringBuilder details = new StringBuilder();
      for (int i = 0; i < params.length; ++i) {
        if (i != 0) {
          details.append(", ");
        }
        details.append(params[i][0]);
        details.append("=");
        details.append(params[i][1]);
      }
      logger.debug(" FE=> StartupPacket(" + details + ")");
    }

    // Precalculate message length and encode params.
    int length = 4 + 4;
    byte[][] encodedParams = new byte[params.length * 2][];
    for (int i = 0; i < params.length; ++i) {
      encodedParams[i * 2] = params[i][0].getBytes("UTF-8");
      encodedParams[i * 2 + 1] = params[i][1].getBytes("UTF-8");
      length += encodedParams[i * 2].length + 1 + encodedParams[i * 2 + 1].length + 1;
    }

    length += 1; // Terminating \0

    // Send the startup message.
    pgStream.SendInteger4(length);
    pgStream.SendInteger2(3); // protocol major<commit_msg>Refactored an unnecessary conversion of List<String[]> to String[][] when calling the sendStartupPacket. (#544)<commit_after>/*-------------------------------------------------------------------------
*
* Copyright (c) 2003-2014, PostgreSQL Global Development Group
* Copyright (c) 2004, Open Cloud Limited.
*
*
*-------------------------------------------------------------------------
*/

package org.postgresql.core.v3;

import org.postgresql.PGProperty;
import org.postgresql.core.ConnectionFactory;
import org.postgresql.core.Encoding;
import org.postgresql.core.Logger;
import org.postgresql.core.PGStream;
import org.postgresql.core.ProtocolConnection;
import org.postgresql.core.ServerVersion;
import org.postgresql.core.SetupQueryRunner;
import org.postgresql.core.Utils;
import org.postgresql.core.v2.SocketFactoryFactory;
import org.postgresql.hostchooser.GlobalHostStatusTracker;
import org.postgresql.hostchooser.HostChooser;
import org.postgresql.hostchooser.HostChooserFactory;
import org.postgresql.hostchooser.HostRequirement;
import org.postgresql.hostchooser.HostStatus;
import org.postgresql.sspi.SSPIClient;
import org.postgresql.util.GT;
import org.postgresql.util.HostSpec;
import org.postgresql.util.MD5Digest;
import org.postgresql.util.PSQLException;
import org.postgresql.util.PSQLState;
import org.postgresql.util.PSQLWarning;
import org.postgresql.util.ServerErrorMessage;
import org.postgresql.util.UnixCrypt;

import java.io.IOException;
import java.net.ConnectException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.TimeZone;

import javax.net.SocketFactory;

/**
 * ConnectionFactory implementation for version 3 (7.4+) connections.
 *
 * @author <NAME> (<EMAIL>), based on the previous implementation
 */
public class ConnectionFactoryImpl extends ConnectionFactory {
  private static final int AUTH_REQ_OK = 0;
  private static final int AUTH_REQ_KRB4 = 1;
  private static final int AUTH_REQ_KRB5 = 2;
  private static final int AUTH_REQ_PASSWORD = 3;
  private static final int AUTH_REQ_CRYPT = 4;
  private static final int AUTH_REQ_MD5 = 5;
  private static final int AUTH_REQ_SCM = 6;
  private static final int AUTH_REQ_GSS = 7;
  private static final int AUTH_REQ_GSS_CONTINUE = 8;
  private static final int AUTH_REQ_SSPI = 9;

  /**
   * Marker exception; thrown when we want to fall back to using V2.
   */
  private static class UnsupportedProtocolException extends IOException {
  }

  public ProtocolConnection openConnectionImpl(HostSpec[] hostSpecs, String user, String database,
      Properties info, Logger logger) throws SQLException {
    // Extract interesting values from the info properties:
    // - the SSL setting
    boolean requireSSL;
    boolean trySSL;
    String sslmode = PGProperty.SSL_MODE.get(info);
    if (sslmode == null) { // Fall back to the ssl property
      // assume "true" if the property is set but empty
      requireSSL = trySSL = PGProperty.SSL.getBoolean(info) || "".equals(PGProperty.SSL.get(info));
    } else {
      if ("disable".equals(sslmode)) {
        requireSSL = trySSL = false;
      } else if ("require".equals(sslmode) || "verify-ca".equals(sslmode)
          || "verify-full".equals(sslmode)) {
        requireSSL = trySSL = true;
      } else {
        throw new PSQLException(GT.tr("Invalid sslmode value: {0}", sslmode),
            PSQLState.CONNECTION_UNABLE_TO_CONNECT);
      }
    }

    // - the TCP keep alive setting
    boolean requireTCPKeepAlive = PGProperty.TCP_KEEP_ALIVE.getBoolean(info);

    // NOTE: To simplify this code, it is assumed that if we are
    // using the V3 protocol, then the database is at least 7.4. That
    // eliminates the need to check database versions and maintain
    // backward-compatible code here.
    //
    // Change by <NAME> <<EMAIL>>

    int connectTimeout = PGProperty.CONNECT_TIMEOUT.getInt(info) * 1000;

    // - the targetServerType setting
    HostRequirement targetServerType;
    try {
      targetServerType =
          HostRequirement.valueOf(info.getProperty("targetServerType", HostRequirement.any.name()));
    } catch (IllegalArgumentException ex) {
      throw new PSQLException(
          GT.tr("Invalid targetServerType value: {0}", info.getProperty("targetServerType")),
          PSQLState.CONNECTION_UNABLE_TO_CONNECT);
    }

    SocketFactory socketFactory = SocketFactoryFactory.getSocketFactory(info);

    HostChooser hostChooser =
        HostChooserFactory.createHostChooser(hostSpecs, targetServerType, info);
    Iterator<HostSpec> hostIter = hostChooser.iterator();
    while (hostIter.hasNext()) {
      HostSpec hostSpec = hostIter.next();

      if (logger.logDebug()) {
        logger.debug("Trying to establish a protocol version 3 connection to " + hostSpec);
      }

      //
      // Establish a connection.
      //

      PGStream newStream = null;
      try {
        newStream = new PGStream(socketFactory, hostSpec, connectTimeout);

        // Construct and send an ssl startup packet if requested.
        if (trySSL) {
          newStream = enableSSL(newStream, requireSSL, info, logger, connectTimeout);
        }

        // Set the socket timeout if the "socketTimeout" property has been set.
        int socketTimeout = PGProperty.SOCKET_TIMEOUT.getInt(info);
        if (socketTimeout > 0) {
          newStream.getSocket().setSoTimeout(socketTimeout * 1000);
        }

        // Enable TCP keep-alive probe if required.
        newStream.getSocket().setKeepAlive(requireTCPKeepAlive);

        // Try to set SO_SNDBUF and SO_RECVBUF socket options, if requested.
        // If receiveBufferSize and send_buffer_size are set to a value greater
        // than 0, adjust. -1 means use the system default, 0 is ignored since not
        // supported.

        // Set SO_RECVBUF read buffer size
        int receiveBufferSize = PGProperty.RECEIVE_BUFFER_SIZE.getInt(info);
        if (receiveBufferSize > -1) {
          // value of 0 not a valid buffer size value
          if (receiveBufferSize > 0) {
            newStream.getSocket().setReceiveBufferSize(receiveBufferSize);
          } else {
            logger.info("Ignore invalid value for receiveBufferSize: " + receiveBufferSize);
          }
        }

        // Set SO_SNDBUF write buffer size
        int sendBufferSize = PGProperty.SEND_BUFFER_SIZE.getInt(info);
        if (sendBufferSize > -1) {
          if (sendBufferSize > 0) {
            newStream.getSocket().setSendBufferSize(sendBufferSize);
          } else {
            logger.info("Ignore invalid value for sendBufferSize: " + sendBufferSize);
          }
        }

        logger.info("Receive Buffer Size is " + newStream.getSocket().getReceiveBufferSize());
        logger.info("Send Buffer Size is " + newStream.getSocket().getSendBufferSize());

        List<String[]> paramList = new ArrayList<String[]>();
        paramList.add(new String[]{"user", user});
        paramList.add(new String[]{"database", database});
        paramList.add(new String[]{"client_encoding", "UTF8"});
        paramList.add(new String[]{"DateStyle", "ISO"});
        paramList.add(new String[]{"TimeZone", createPostgresTimeZone()});
        String assumeMinServerVersion = PGProperty.ASSUME_MIN_SERVER_VERSION.get(info);
        if (Utils.parseServerVersionStr(assumeMinServerVersion)
            >= ServerVersion.v9_0.getVersionNum()) {
          // User is explicitly telling us this is a 9.0+ server so set properties here:
          paramList.add(new String[]{"extra_float_digits", "3"});
          String appName = PGProperty.APPLICATION_NAME.get(info);
          if (appName != null) {
            paramList.add(new String[]{"application_name", appName});
          }
        } else {
          // User has not explicitly told us that this is a 9.0+ server so stick to old default:
          paramList.add(new String[]{"extra_float_digits", "2"});
        }

        String currentSchema = PGProperty.CURRENT_SCHEMA.get(info);
        if (currentSchema != null) {
          paramList.add(new String[]{"search_path", currentSchema});
        }

        sendStartupPacket(newStream, paramList, logger);

        // Do authentication (until AuthenticationOk).
        doAuthentication(newStream, hostSpec.getHost(), user, info, logger);

        // Do final startup.
        ProtocolConnectionImpl protoConnection =
            new ProtocolConnectionImpl(newStream, user, database, info, logger, connectTimeout);
        readStartupMessages(newStream, protoConnection, logger);

        // Check Master or Slave
        HostStatus hostStatus = HostStatus.ConnectOK;
        if (targetServerType != HostRequirement.any) {
          hostStatus = isMaster(protoConnection, logger) ? HostStatus.Master : HostStatus.Slave;
        }
        GlobalHostStatusTracker.reportHostStatus(hostSpec, hostStatus);
        if (!targetServerType.allowConnectingTo(hostStatus)) {
          protoConnection.close();
          if (hostIter.hasNext()) {
            // still more addresses to try
            continue;
          }
          throw new PSQLException(GT
              .tr("Could not find a server with specified targetServerType: {0}", targetServerType),
              PSQLState.CONNECTION_UNABLE_TO_CONNECT);
        }

        runInitialQueries(protoConnection, info, logger);

        // And we're done.
        return protoConnection;
      } catch (UnsupportedProtocolException upe) {
        // Swallow this and return null so ConnectionFactory tries the next protocol.
        if (logger.logDebug()) {
          logger.debug("Protocol not supported, abandoning connection.");
        }
        closeStream(newStream);
        return null;
      } catch (ConnectException cex) {
        // Added by <NAME> <<EMAIL>>
        // ConnectException is thrown when the connection cannot be made.
        // we trap this an return a more meaningful message for the end user
        GlobalHostStatusTracker.reportHostStatus(hostSpec, HostStatus.ConnectFail);
        if (hostIter.hasNext()) {
          // still more addresses to try
          continue;
        }
        throw new PSQLException(GT.tr(
            "Connection to {0} refused. Check that the hostname and port are correct and that the postmaster is accepting TCP/IP connections.",
            hostSpec), PSQLState.CONNECTION_UNABLE_TO_CONNECT, cex);
      } catch (IOException ioe) {
        closeStream(newStream);
        GlobalHostStatusTracker.reportHostStatus(hostSpec, HostStatus.ConnectFail);
        if (hostIter.hasNext()) {
          // still more addresses to try
          continue;
        }
        throw new PSQLException(GT.tr("The connection attempt failed."),
            PSQLState.CONNECTION_UNABLE_TO_CONNECT, ioe);
      } catch (SQLException se) {
        closeStream(newStream);
        if (hostIter.hasNext()) {
          // still more addresses to try
          continue;
        }
        throw se;
      }
    }
    throw new PSQLException(GT.tr("The connection url is invalid."),
        PSQLState.CONNECTION_UNABLE_TO_CONNECT);
  }

  /**
   * Convert Java time zone to postgres time zone. All others stay the same except that GMT+nn
   * changes to GMT-nn and vise versa.
   *
   * @return The current JVM time zone in postgresql format.
   */
  private static String createPostgresTimeZone() {
    String tz = TimeZone.getDefault().getID();
    if (tz.length() <= 3 || !tz.startsWith("GMT")) {
      return tz;
    }
    char sign = tz.charAt(3);
    String start;
    if (sign == '+') {
      start = "GMT-";
    } else if (sign == '-') {
      start = "GMT+";
    } else {
      // unknown type
      return tz;
    }

    return start + tz.substring(4);
  }

  private PGStream enableSSL(PGStream pgStream, boolean requireSSL, Properties info, Logger logger,
      int connectTimeout) throws IOException, SQLException {
    if (logger.logDebug()) {
      logger.debug(" FE=> SSLRequest");
    }

    // Send SSL request packet
    pgStream.SendInteger4(8);
    pgStream.SendInteger2(1234);
    pgStream.SendInteger2(5679);
    pgStream.flush();

    // Now get the response from the backend, one of N, E, S.
    int beresp = pgStream.ReceiveChar();
    switch (beresp) {
      case 'E':
        if (logger.logDebug()) {
          logger.debug(" <=BE SSLError");
        }

        // Server doesn't even know about the SSL handshake protocol
        if (requireSSL) {
          throw new PSQLException(GT.tr("The server does not support SSL."),
              PSQLState.CONNECTION_REJECTED);
        }

        // We have to reconnect to continue.
        pgStream.close();
        return new PGStream(pgStream.getSocketFactory(), pgStream.getHostSpec(), connectTimeout);

      case 'N':
        if (logger.logDebug()) {
          logger.debug(" <=BE SSLRefused");
        }

        // Server does not support ssl
        if (requireSSL) {
          throw new PSQLException(GT.tr("The server does not support SSL."),
              PSQLState.CONNECTION_REJECTED);
        }

        return pgStream;

      case 'S':
        if (logger.logDebug()) {
          logger.debug(" <=BE SSLOk");
        }

        // Server supports ssl
        org.postgresql.ssl.MakeSSL.convert(pgStream, info, logger);
        return pgStream;

      default:
        throw new PSQLException(GT.tr("An error occurred while setting up the SSL connection."),
            PSQLState.PROTOCOL_VIOLATION);
    }
  }

  private void sendStartupPacket(PGStream pgStream, List<String[]> params, Logger logger)
      throws IOException {
    if (logger.logDebug()) {
      StringBuilder details = new StringBuilder();
      for (int i = 0; i < params.size(); ++i) {
        if (i != 0) {
          details.append(", ");
        }
        details.append(params.get(i)[0]);
        details.append("=");
        details.append(params.get(i)[1]);
      }
      logger.debug(" FE=> StartupPacket(" + details + ")");
    }

    // Precalculate message length and encode params.
    int length = 4 + 4;
    byte[][] encodedParams = new byte[params.size() * 2][];
    for (int i = 0; i < params.size(); ++i) {
      encodedParams[i * 2] = params.get(i)[0].getBytes("UTF-8");
      encodedParams[i * 2 + 1] = params.get(i)[1].getBytes("UTF-8");
      length += encodedParams[i * 2].length + 1 + encodedParams[i * 2 + 1].length + 1;
    }

    length += 1; // Terminating \0

    // Send the startup message.
    pgStream.SendInteger4(length);
    pgStream.SendInteger2(3); // protocol major
{'fraction_non_alphanumeric': 0.11452427584785074, 'fraction_numerical': 0.007387554547641137, 'mean_word_length': 2.651235729519508, 'pattern_counts': {'":': 0, '<': 39, '<?xml version=': 0, '>': 43, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '140180', 'n_tokens_mistral': 8295, 'n_tokens_neox': 8052, 'n_words': 2191}