/*
 * Microsoft JDBC Driver for SQL Server
 * 
 * Copyright(c) Microsoft Corporation All rights reserved.
 * 
 * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
 */
package com.microsoft.sqlserver.jdbc.bulkCopy;

import static org.junit.Assert.fail;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.Arrays;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.platform.runner.JUnitPlatform;
import org.junit.runner.RunWith;

import com.microsoft.sqlserver.jdbc.ISQLServerBulkRecord;
import com.microsoft.sqlserver.jdbc.SQLServerBulkCSVFileRecord;
import com.microsoft.sqlserver.jdbc.SQLServerBulkCopy;
import com.microsoft.sqlserver.testframework.AbstractTest;
import com.microsoft.sqlserver.testframework.DBConnection;
import com.microsoft.sqlserver.testframework.DBResultSet;
import com.microsoft.sqlserver.testframework.DBStatement;
import com.microsoft.sqlserver.testframework.DBTable;
import com.microsoft.sqlserver.testframework.sqlType.SqlType;

/**
 * Test bulkcopy with CSV file input
 * 
 * In the input csv, first row is comma separated datatype name of values to follow Precision and scale are separated by hyphen in csv to distinguish
 * between column and scale/precision ie decimal(18-6) in csv is decimal type with precision 18 and scale 6
 * 
 * Destination table contains one column for each datatype name in the csv header(first line of csv)
 */
@RunWith(JUnitPlatform.class)
@DisplayName("Test bulkCopy with CSV")
public class BulkCopyCSVTest extends AbstractTest {

    static String inputFile = "BulkCopyCSVTestInput.csv";
    static String encoding = "UTF-8";
    static String delimiter = ",";

    static DBConnection con = null;
    static DBStatement stmt = null;
    static String filePath = null;

    /**
     * Create connection, statement and generate path of resource file
     */
    @BeforeAll
    static void setUpConnection() {
        con = new DBConnection(connectionString);
        stmt = con.createStatement();
        filePath = getCurrentClassPath();
    }

    /**
     * test simple csv file for bulkcopy
     */
    @Test
    @DisplayName("Test SQLServerBulkCSVFileRecord")
    void testCSV() {
        DBTable destTable = null;
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(filePath + inputFile), encoding));
            // read the first line from csv and parse it to get datatypes to create destination column
            String[] columnTypes = br.readLine().substring(1)/* Skip the Byte order mark */.split(delimiter, -1);
            br.close();

            int numberOfColumns = columnTypes.length;
            destTable = new DBTable(false);
            SQLServerBulkCSVFileRecord fileRecord = new SQLServerBulkCSVFileRecord(filePath + inputFile, encoding, delimiter, true);
            SQLServerBulkCopy bulkCopy = new SQLServerBulkCopy((Connection) con.product());
            bulkCopy.setDestinationTableName(destTable.getEscapedTableName());

            // add a column in destTable for each datatype in csv
            for (int i = 0; i < numberOfColumns; i++) {
                SqlType sqlType = null;
                int precision = -1;
                int scale = -1;

                String columnType = columnTypes[i].trim().toLowerCase();
                int indexOpenParenthesis = columnType.lastIndexOf("(");
                // skip the parenthesis in case of precision and scale type
                if (-1 != indexOpenParenthesis) {
                    String precision_scale = columnType.substring(indexOpenParenthesis + 1, columnType.length() - 1);
                    columnType = columnType.substring(0, indexOpenParenthesis);
                    sqlType = SqlTypeMapping.valueOf(columnType.toUpperCase()).sqlType;

                    // add scale if exist
                    int indexPrecisionScaleSeparator = precision_scale.indexOf("-");
                    if (-1 != indexPrecisionScaleSeparator) {
                        scale = Integer.parseInt(precision_scale.substring(indexPrecisionScaleSeparator + 1));
                        sqlType.setScale(scale);
                        precision_scale = precision_scale.substring(0, indexPrecisionScaleSeparator);
                    }
                    // add precision
                    precision = Integer.parseInt(precision_scale);
                    sqlType.setPrecision(precision);
                }
                else {
                    sqlType = SqlTypeMapping.valueOf(columnType.toUpperCase()).sqlType;
                }

                destTable.addColumn(sqlType);
                fileRecord.addColumnMetadata(i + 1, "", sqlType.getJdbctype().getVendorTypeNumber(), (-1 == precision) ? 0 : precision,
                        (-1 == scale) ? 0 : scale);
            }
            stmt.createTable(destTable);
            bulkCopy.writeToServer((ISQLServerBulkRecord) fileRecord);
            bulkCopy.close();
            validateValuesFromCSV(destTable);
        }
        catch (Exception e) {
            fail(e.getMessage());
        }
        finally {
            if (null != destTable) {
                stmt.dropTable(destTable);
            }
        }
    }

    /**
     * 
     * @return location of resource file
     */
    static String getCurrentClassPath() {

        try {
            String className = new Object() {
            }.getClass().getEnclosingClass().getName();
            String location = Class.forName(className).getProtectionDomain().getCodeSource().getLocation().getPath();
            return location + "/";
        }
        catch (ClassNotFoundException e) {
            fail("Failed to get CSV file path. " + e.getMessage());
        }
        return null;
    }

    /**
     * validate value in csv and in destination table as string
     * 
     * @param destinationTable
     */
    static void validateValuesFromCSV(DBTable destinationTable) {
        BufferedReader br;
        try {
            br = new BufferedReader(new InputStreamReader(new FileInputStream(filePath + inputFile), encoding));
            br.readLine();   // skip first line as it is header

            DBResultSet dstResultSet = stmt.executeQuery("SELECT * FROM " + destinationTable.getEscapedTableName() + ";");
            ResultSetMetaData destMeta = ((ResultSet) dstResultSet.product()).getMetaData();
            int totalColumns = destMeta.getColumnCount();
            while (dstResultSet.next()) {
                String[] srcValues = br.readLine().split(delimiter);
                if ((0 == srcValues.length) && (srcValues.length != totalColumns)) {
                    srcValues = new String[totalColumns];
                    Arrays.fill(srcValues, null);
                }
                for (int i = 1; i <= totalColumns; i++) {
                    String srcValue = srcValues[i - 1];
                    String dstValue = dstResultSet.getString(i);
                    srcValue = (null != srcValue) ? srcValue.trim() : srcValue;
                    dstValue = (null != dstValue) ? dstValue.trim() : dstValue;

                    // get the value from csv as string and compare them
                    BulkCopyTestUtil.comapreSourceDest(java.sql.Types.VARCHAR, srcValue, dstValue);
                }
            }
        }
        catch (Exception e) {
            fail("CSV validation failed with " + e.getMessage());
        }
    }

    /**
     * drop source table after testing bulk copy
     * 
     * @throws SQLException
     */
    @AfterAll
    static void tearConnection() throws SQLException {
        stmt.close();
        con.close();
    }
}

@@ -415,32 +415,53 @@
 utStreamReader;%0A
+import java.net.URI;%0A
 import java.sql.
@@ -5918,24 +5918,29 @@
 ().getPath()
++ %22/%22
 ;%0A          
@@ -5945,29 +5945,80 @@
     
-return location + %22/%22
+URI uri = new URI(location.toString());%0A            return uri.getPath()
 ;%0A  
@@ -6044,21 +6044,8 @@
 ch (
-ClassNotFound
 Exce

Access test resource path as URI to avoid white space read as %20 in URL