Exemple #1
0
        /// <summary>
        /// Method to import CSV file
        /// </summary>
        /// <param name="path">Path to csv file that should be imported</param>
        /// <param name="databaseName">Database that is used</param>
        /// <param name="blockSize">Block size of table, if not specified default value of database server configuration will be used</param>
        /// <param name="hasHeader">Specifies whether input csv has header, if not specified default value is true</param>
        /// <param name="columnSeparator">Char representing column separator, if not specified default value will be guessed</param>
        /// <param name="batchSize">Number of lines processed in one batch, if not specified default value is 65536</param>
        /// <param name="threadsCount">Number of threads for processing csv, if not specified number of cores of the client CPU will be used</param>
        /// <param name="encoding">Encoding of csv, if not specified it will be guessed</param>
        public void Import(string path, string databaseName,
                           string tableName = "", int blockSize = 0, bool hasHeader = true, char columnSeparator = '\0', int batchSize = 65536, int threadsCount = 0, Encoding encoding = null)
        {
            this.databaseName = databaseName;

            try
            {
                // Table name from path or from argument if present
                if (tableName == "")
                {
                    tableName = Path.GetFileNameWithoutExtension(path);
                }

                this.tableName = tableName;

                if (encoding == null)
                {
                    encoding = ParserCSV.GuessEncoding(path);
                }
                if (columnSeparator == '\0')
                {
                    columnSeparator = ParserCSV.GuessSeparator(path, encoding);
                }
                var types = ParserCSV.GuessTypes(path, hasHeader, columnSeparator, encoding);
                streamLength = ParserCSV.GetStreamLength(path);

                client.Connect();
                client.UseDatabase(databaseName);
                CreateTable(databaseName, tableName, types, blockSize);

                ParserCSV.Configuration configuration = new ParserCSV.Configuration(batchSize: batchSize, encoding: encoding, columnSeparator: columnSeparator);

                if (threadsCount <= 0)
                {
                    // Use more threads if file is bigger than 10MB
                    if (streamLength > 10000000)
                    {
                        threadsCount = Environment.ProcessorCount;
                    }
                    else
                    {
                        threadsCount = 1;
                    }
                }

                Console.WriteLine("Importing started with " + threadsCount + " threads.");
                var startTime = DateTime.Now;

                long streamThreadLength = streamLength / threadsCount;
                long lines  = 0;
                long errors = 0;
                linesImported = new long[threadsCount];
                bytesImported = new long[threadsCount];
                linesError    = new long[threadsCount];
                Thread[]    threads          = new Thread[threadsCount];
                Exception[] threadExceptions = new Exception[threadsCount];

                // Each thread has its own instance of the Parser (because of different position of read) and ColumnarDBClient (because of concurrent bulk import)
                for (int i = 0; i < threadsCount; i++)
                {
                    long start = i * streamThreadLength;
                    long end   = i * streamThreadLength + streamThreadLength + 1;
                    int  index = i;
                    threadExceptions[index] = null;
                    threads[index]          = new Thread(() =>
                    {
                        try
                        {
                            this.ParseAndImportBatch(index, path, configuration, types, start, end);
                        }
                        catch (Exception ex)
                        {
                            threadExceptions[index] = ex;
                        }
                    });
                    threads[index].Start();
                    //this.ParseAndImportBatch(index, path, configuration, types, start, end);
                }

                for (int i = 0; i < threadsCount; i++)
                {
                    threads[i].Join();
                }

                for (int i = 0; i < threadsCount; i++)
                {
                    if (threadExceptions[i] != null)
                    {
                        throw threadExceptions[i];
                    }
                }

                for (int i = 0; i < threadsCount; i++)
                {
                    lines  += linesImported[i];
                    errors += linesError[i];
                }

                client.Dispose();

                var endTime = DateTime.Now;
                Console.WriteLine();
                Console.WriteLine("Importing done (imported " + lines + " records, " + errors + " failed, " + (endTime - startTime).TotalSeconds.ToString() + "sec.).");
            }
            catch (FileNotFoundException)
            {
                Console.WriteLine(FileNotFound(path));
            }
            catch (IOException e)
            {
                Console.WriteLine(e.Message);
            }
            catch (QueryException e)
            {
                Console.WriteLine("Query Exception: " + e.Message);
            }
            catch (ParserCSV.ParserException e)
            {
                Console.WriteLine("Parser Exception: " + e.Message);
            }
            catch (Exception e)
            {
                Console.WriteLine(UnknownException() + e.Message);
            }
        }
Exemple #2
0
        /// <summary>
        /// Reads input from console
        /// commands:
        /// use [database]
        /// u
        /// [query]
        /// help
        /// h
        /// exit
        /// quit
        /// q
        /// timing
        /// t
        /// docs
        /// man
        /// </summary>
        public static void Main(string[] args)
        {
            // Parameter from config file
            if (GetConfigurationParameter <string>("host") != null)
            {
                ipAddress = GetConfigurationParameter <string>("host");
            }
            if (GetConfigurationParameter <short?>("port") != null)
            {
                port = GetConfigurationParameter <short?>("port").GetValueOrDefault();
            }

            // Parameter from program arguments
            int timeout = 30000;

            if (args.Length >= 2)
            {
                for (int i = 0; i < args.Length; i += 2)
                {
                    if (args[i] == "-t")
                    {
                        timeout = Convert.ToInt32(args[i + 1]);
                        Console.WriteLine("Set timeout to: " + timeout.ToString());
                    }
                    if (args[i] == "-h")
                    {
                        if (args[i + 1].Contains(':'))
                        {
                            ipAddress = args[i + 1].Split(':')[0];
                            port      = Convert.ToInt16(args[i + 1].Split(':')[1]);
                        }
                        else
                        {
                            ipAddress = args[i + 1];
                        }
                    }
                }
            }

            client = new ColumnarDBClient("Host=" + ipAddress + ";" + "Port=" + port.ToString() + ";");
            try
            {
                client.Connect();
                var heartBeatTimer = new System.Timers.Timer(timeout);
                heartBeatTimer.Elapsed  += HeartBeatTimer_Elapsed;
                heartBeatTimer.AutoReset = true;
                heartBeatTimer.Enabled   = true;
                UseDatabase use    = new UseDatabase();
                ImportCSV   import = new ImportCSV(ipAddress, port);
                Query       query  = new Query();
                mutex = new Mutex();
                ReadLine.HistoryEnabled = true;

                while (!exit)
                {
                    string wholeCommand = ReadLine.Read("> ");

                    if (wholeCommand == "")
                    {
                        continue;
                    }

                    wholeCommand = wholeCommand[wholeCommand.Length - 1] == ';' ? wholeCommand.Substring(0, wholeCommand.Length - 1).Trim() : wholeCommand.Trim();

                    string[] splitCommand = wholeCommand.Split(" ");

                    splitCommand = splitCommand.Where(x => !string.IsNullOrEmpty(x)).ToArray();

                    string command    = splitCommand[0].ToLower();
                    string parameters = "";

                    if (command != "s" && command != "script" && command != "docs" && command != "man" && command != "t" &&
                        command != "timing" && command != "q" && command != "quit" && command != "exit" && command != "u" &&
                        command != "use" && command != "h" && command != "help" && command != "i" && command != "import")
                    {
                        parameters = wholeCommand;
                        parameters = parameters.Trim();
                        parameters = parameters[parameters.Length - 1] != ';' ? parameters + ';' : parameters;
                        command    = "query";
                    }
                    else if (splitCommand.Length > 1)
                    {
                        parameters = wholeCommand.Substring(command.Length + 1);
                        parameters = parameters.Trim();
                    }
                    mutex.WaitOne();
                    switch (command)
                    {
                    case "exit":
                    case "quit":
                    case "q":
                        exit = true;
                        client.Close();
                        break;

                    case "u":
                    case "use":
                        if (parameters == "")
                        {
                            Console.WriteLine("USE: Missing argument - database name");
                            break;
                        }
                        use.Use(parameters, client);

                        break;

                    case "query":
                        if (parameters == "")
                        {
                            Console.WriteLine("QUERY: Missing argument - query string");
                            break;
                        }

                        query.RunQuery(parameters, Console.WindowWidth, client);

                        break;

                    case "s":
                    case "script":
                        if (parameters == "")
                        {
                            Console.WriteLine("SCRIPT: Missing argument - file path");
                            break;
                        }

                        try
                        {
                            var queryFile = new System.IO.StreamReader(parameters);
                            Console.WriteLine($"Script from file path '{parameters}' has been loaded.");
                            string queryString;
                            int    scriptFileLine = 0;

                            while ((queryString = queryFile.ReadLine()) != null)
                            {
                                scriptFileLine++;

                                // skip single line sql comments
                                if (queryString.Trim().StartsWith("--"))
                                {
                                    continue;
                                }

                                queryString = queryString[queryString.Length - 1] == ';' ? queryString.Substring(0, queryString.Length - 1).Trim() : queryString.Trim();


                                Console.WriteLine("SCRIPT: Executing query/command on line " + scriptFileLine + " from a script file: " + parameters);

                                try
                                {
                                    if (queryString.ToLower().StartsWith("u ") || queryString.ToLower().StartsWith("use "))
                                    {
                                        string[] splitCommand2 = queryString.Split(" ");

                                        splitCommand2 = splitCommand2.Where(x => !string.IsNullOrEmpty(x)).ToArray();

                                        use.Use(splitCommand2[1], client);
                                    }
                                    else
                                    {
                                        if (queryString.ToLower().StartsWith("i ") || queryString.ToLower().StartsWith("import "))
                                        {
                                            string[] splitCommand2 = queryString.Split(" ");
                                            splitCommand2 = splitCommand2.Where(x => !string.IsNullOrEmpty(x)).ToArray();
                                            string   command2         = splitCommand2[0];
                                            string   parameters2      = queryString.Substring(command2.Length + 1);
                                            string[] splitParameters2 = Regex.Matches(parameters2, @"[\""].+?[\""]|[^ ]+").Cast <Match>().Select(m => m.Value).ToArray();

                                            if (splitCommand2[1] == "" || splitParameters2.Length < 2)
                                            {
                                                Console.WriteLine("IMPORT: Missing arguments - database name or file path");
                                                break;
                                            }

                                            string database2 = splitParameters2[0];
                                            string filePath2 = splitParameters2[1];
                                            if (filePath2.Length > 0 && filePath2.ElementAt(0) == '\"')
                                            {
                                                filePath2 = filePath2.Substring(1, filePath2.Length - 2);
                                            }

                                            var importOptionsScript = ParseImportOptions(splitParameters2);

                                            if (importOptionsScript != null)
                                            {
                                                import.Import(filePath2, database2, tableName: importOptionsScript.TableName, blockSize: importOptionsScript.BlockSize, hasHeader: importOptionsScript.HasHeader, columnSeparator: importOptionsScript.ColumnSeparator, threadsCount: importOptionsScript.ThreadsCount, batchSize: importOptionsScript.BatchSize);
                                            }
                                        }
                                        else
                                        {
                                            queryString = queryString.Last() != ';' ? queryString + ";" : queryString;
                                            query.RunQuery(queryString, Console.WindowWidth, client);
                                        }
                                    }
                                }
                                catch (Exception e)
                                {
                                    Console.WriteLine(e);
                                    break;
                                }
                            }
                        }
                        catch (System.IO.FileNotFoundException)
                        {
                            Console.WriteLine($"File not found. File path: '{parameters}'.");
                        }

                        break;

                    case "t":
                    case "timing":
                        if (parameters == "")
                        {
                            Console.WriteLine("TIMING: Missing argument - query");
                            break;
                        }

                        parameters = parameters[parameters.Length - 1] != ';' ? parameters + ';' : parameters;
                        query.RunTestQuery(parameters, Console.WindowWidth, client);

                        break;

                    case "i":
                    case "import":
                        string[] splitParameters = Regex.Matches(parameters, @"[\""].+?[\""]|[^ ]+").Cast <Match>().Select(m => m.Value).ToArray();

                        if (parameters == "" || splitParameters.Length < 2)
                        {
                            Console.WriteLine("IMPORT: Missing arguments - database name or file path");
                            break;
                        }

                        string database = splitParameters[0];
                        string filePath = splitParameters[1];
                        if (filePath.Length > 0 && filePath.ElementAt(0) == '\"')
                        {
                            filePath = filePath.Substring(1, filePath.Length - 2);
                        }

                        var importOptions = ParseImportOptions(splitParameters);
                        if (importOptions != null)
                        {
                            import.Import(filePath, database, tableName: importOptions.TableName, blockSize: importOptions.BlockSize, hasHeader: importOptions.HasHeader, columnSeparator: importOptions.ColumnSeparator, threadsCount: importOptions.ThreadsCount, batchSize: importOptions.BatchSize);
                        }

                        break;

                    case "h":
                    case "help":
                        //formated console output
                        const string format = "{0,-48} {1,-48}";

                        Console.WriteLine();
                        Console.WriteLine("List of supported console commands:");
                        Console.WriteLine("______________________________________________________________________________________________________________________________");
                        Console.WriteLine(String.Format(format, "1.) h, help", "Show information about commands"));
                        Console.WriteLine(String.Format(format, "2.) docs, man", "Prints 'Documentation is available at https://docs.qikk.ly/'"));
                        Console.WriteLine(String.Format(format, "3.) u [db_name], use [db_name]", "Set current working database"));
                        Console.WriteLine(String.Format(format, "4.) i [db_name] [path], import [db_name] [path]", "Import CSV file with comma separated columns."));
                        Console.WriteLine(String.Format(format, "5.) [query]", "Run given query"));
                        Console.WriteLine(String.Format(format, "6.) s [file], script [file]", "Run SQL queries from a file path (also supports console client command USE)."));
                        Console.WriteLine(String.Format(format, "7.) t [query], timing [query]", "Run a query " + Query.numberOfQueryExec + 1 + " times and print the first and average cached execution time."));
                        Console.WriteLine(String.Format(format, "8.) q, quit, exit", "Exit the console"));

                        Console.WriteLine();
                        break;

                    case "docs":
                    case "man":
                        Console.WriteLine("Documentation is available at https://docs.qikk.ly/");
                        break;

                    default:
                        Console.WriteLine("Unknown command, for more information about commands type 'help'");
                        break;
                    }
                    mutex.ReleaseMutex();
                }
                heartBeatTimer.Stop();
                heartBeatTimer.Dispose();
            }
            catch (System.Net.Sockets.SocketException ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
Exemple #3
0
        /// <summary>
        /// Load benchmark queries from a file, execute them one by one and save results.
        /// </summary>
        public static int Main(string[] args)
        {
            bool avgTimePassed        = true;
            bool correctResultsPassed = true;

            Array.Sort(args);

            ColumnarDBClient client = new ColumnarDBClient($"Host={ipAddress};Port={port.ToString()};");

            client.Connect();
            Console.WriteLine("Client has successfully connected to server.");

            UseDatabase use = new UseDatabase();

            if (File.Exists(resultFilePath))
            {
                File.Delete(resultFilePath);
            }

            var resultFile = new System.IO.StreamWriter(resultFilePath);

            if (args.GetLength(0) == 0)
            {
                string[]      array    = { "-a" };
                List <string> tempList = new List <string>(array);
                args = tempList.ToArray();
            }

            resultFile.WriteLine($"Each query was executed {numberOfQueryExec} times and the average time was saved.");

            foreach (string arg in args)
            {
                string queryString;
                string queryExptectedString;

                if (String.Equals(arg, "-a") || String.Equals(arg, "-b"))
                {
                    // test telco queries:
                    use.Use(telcoDbName, client);

                    client.UseDatabase(telcoDbName);

                    var queryFile = new System.IO.StreamReader(telcoQueriesPath);
                    Console.WriteLine($"Benchmark queries from file '{telcoQueriesPath}' were loaded.");

                    while ((queryString = queryFile.ReadLine()) != null)
                    {
                        Console.WriteLine($"Executing benchmark query: {queryString}");
                        resultFile.WriteLine(queryString);

                        // execute query first time (no cache):
                        double resultSum = 0;
                        try
                        {
                            client.Query(queryString);
                        }
                        catch (Exception e)
                        {
                            Console.WriteLine(e);
                            break;
                        }
                        Dictionary <string, float> executionTimes = null;
                        ColumnarDataTable          result         = null;

                        while (((result, executionTimes) = client.GetNextQueryResult()).result != null)
                        {
                            resultSum += executionTimes.Values.Sum();
                        }

                        resultSum = Math.Round(resultSum);

                        // save query result to a file:
                        resultFile.WriteLine((resultSum).ToString() + " (first run)");
                        Console.WriteLine((resultSum).ToString() + " (first run)");

                        // execute query N times (used cache):
                        for (int i = 0; i < numberOfQueryExec; i++)
                        {
                            try
                            {
                                client.Query(queryString);
                            }
                            catch (Exception e)
                            {
                                Console.WriteLine(e);
                                break;
                            }

                            while (((result, executionTimes) = client.GetNextQueryResult()).result != null)
                            {
                                resultSum += executionTimes.Values.Sum();
                            }
                        }

                        double avgQueryExec = Math.Round(resultSum / numberOfQueryExec);

                        // save query result to a file:
                        resultFile.WriteLine(avgQueryExec.ToString() + " (average cached N runs)");
                        Console.WriteLine(avgQueryExec + " (average cached N runs)");

                        // check if query execution time is acceptable and save the result:
                        int queryExpectedExecTime = System.Convert.ToInt32(queryFile.ReadLine());
                        if (avgQueryExec < queryExpectedExecTime)
                        {
                            resultFile.WriteLine($"The query '{queryString}' has passed the execution time test. Expected / Actual average query execution time: {queryExpectedExecTime.ToString()} / {avgQueryExec.ToString()}");
                            Console.WriteLine($"The query '{queryString}' has passed the execution time test. Expected / Actual average query execution time: {queryExpectedExecTime} / {avgQueryExec}");
                        }
                        else
                        {
                            resultFile.WriteLine($"The query '{queryString}' has FAILED the execution time test. Expected / Actual average query execution time: {queryExpectedExecTime.ToString()} / {avgQueryExec.ToString()}");
                            Console.WriteLine($"The query '{queryString} ' has FAILED the execution time test. Expected / Actual average query execution time: {queryExpectedExecTime} / {avgQueryExec}");
                            avgTimePassed = false;
                        }
                    }
                    queryFile.Close();
                }

                if (String.Equals(arg, "-a") || String.Equals(arg, "-g"))
                {
                    // test geo queries:
                    use.Use(geoDbName, client);

                    client.UseDatabase(geoDbName);

                    var queryFile = new System.IO.StreamReader(geoQueriesPath);
                    Console.WriteLine($"Benchmark queries from file '{geoQueriesPath}' were loaded.");

                    while ((queryString = queryFile.ReadLine()) != null)
                    {
                        Console.WriteLine($"Executing benchmark query: {queryString}");
                        resultFile.WriteLine(queryString);

                        // execute query first time (no cache):
                        double resultSum = 0;
                        try
                        {
                            client.Query(queryString);
                        }
                        catch (Exception e)
                        {
                            Console.WriteLine(e);
                            break;
                        }
                        Dictionary <string, float> executionTimes = null;
                        ColumnarDataTable          result         = null;

                        while (((result, executionTimes) = client.GetNextQueryResult()).result != null)
                        {
                            resultSum += executionTimes.Values.Sum();
                        }

                        resultSum = Math.Round(resultSum);

                        // save query result to a file:
                        resultFile.WriteLine((resultSum).ToString() + " (first run)");
                        Console.WriteLine((resultSum).ToString() + " (first run)");

                        // execute query N times (used cache):
                        for (int i = 0; i < numberOfQueryExec; i++)
                        {
                            try
                            {
                                client.Query(queryString);
                            }
                            catch (Exception e)
                            {
                                Console.WriteLine(e);
                                break;
                            }

                            while (((result, executionTimes) = client.GetNextQueryResult()).result != null)
                            {
                                resultSum += executionTimes.Values.Sum();
                            }
                        }

                        double avgQueryExec = Math.Round(resultSum / numberOfQueryExec);

                        // save query result to a file:
                        resultFile.WriteLine(avgQueryExec.ToString() + " (average cached N runs)");
                        Console.WriteLine(avgQueryExec + " (average cached N runs)");

                        // check if query execution time is acceptable and save the result:
                        int queryExpectedExecTime = System.Convert.ToInt32(queryFile.ReadLine());
                        if (avgQueryExec < queryExpectedExecTime)
                        {
                            resultFile.WriteLine($"The query '{queryString}' has passed the execution time test. Expected / Actual average query execution time: {queryExpectedExecTime.ToString()} / {avgQueryExec.ToString()}");
                            Console.WriteLine($"The query '{queryString}' has passed the execution time test. Expected / Actual average query execution time: {queryExpectedExecTime} / {avgQueryExec}");
                        }
                        else
                        {
                            resultFile.WriteLine($"The query '{queryString}' has FAILED the execution time test. Expected / Actual average query execution time: {queryExpectedExecTime.ToString()} / {avgQueryExec.ToString()}");
                            Console.WriteLine($"The query '{queryString} ' has FAILED the execution time test. Expected / Actual average query execution time: {queryExpectedExecTime} / {avgQueryExec}");
                            avgTimePassed = false;
                        }
                    }
                    queryFile.Close();
                }

                if (String.Equals(arg, "-a") || String.Equals(arg, "-t"))
                {
                    // test taxi queries:
                    use.Use(taxiDbName, client);

                    client.UseDatabase(taxiDbName);

                    var queryFile = new System.IO.StreamReader(taxiQueriesPath);
                    Console.WriteLine($"Benchmark queries from file '{taxiQueriesPath}' were loaded.");

                    while ((queryString = queryFile.ReadLine()) != null)
                    {
                        Console.WriteLine($"Executing benchmark query: {queryString}");
                        resultFile.WriteLine(queryString);

                        // execute query first time (no cache):
                        double resultSum = 0;
                        client.Query(queryString);
                        Dictionary <string, float> executionTimes = null;
                        ColumnarDataTable          result         = null;

                        while (((result, executionTimes) = client.GetNextQueryResult()).result != null)
                        {
                            resultSum += executionTimes.Values.Sum();
                        }

                        resultSum = Math.Round(resultSum);

                        // save query result to a file:
                        resultFile.WriteLine((resultSum).ToString() + " (first run)");
                        Console.WriteLine((resultSum).ToString() + " (first run)");

                        // execute query N times (used cache):
                        for (int i = 0; i < numberOfQueryExec; i++)
                        {
                            client.Query(queryString);

                            while (((result, executionTimes) = client.GetNextQueryResult()).result != null)
                            {
                                resultSum += executionTimes.Values.Sum();
                            }
                        }

                        // save query result to a file:
                        resultFile.WriteLine((resultSum / numberOfQueryExec).ToString() + " (average cached N runs)");
                        Console.WriteLine((resultSum / numberOfQueryExec) + " (average cached N runs)");
                    }
                    queryFile.Close();
                }

                if (String.Equals(arg, "-a") || String.Equals(arg, "--ci"))
                {
                    // test taxi queries:
                    use.Use(taxiDbName, client);

                    client.UseDatabase(taxiDbName);

                    var queryFile = new System.IO.StreamReader(ciQueriesPath);
                    Console.WriteLine($"Benchmark queries from file '{ciQueriesPath}' were loaded.");

                    int queryIndex = 1; // indexing from 1, not zero, because we use to call it taxi rides query number 1, not number 0, so it is not confusing
                    Dictionary <string, IList> columnData = null;

                    while ((queryString = queryFile.ReadLine()) != null)
                    {
                        Console.WriteLine($"Executing benchmark query: {queryString}");
                        resultFile.WriteLine(queryString);

                        // execute query first time (no cache):
                        double resultSum = 0;
                        try
                        {
                            client.Query(queryString);
                        }
                        catch (Exception e)
                        {
                            Console.WriteLine(e);
                            break;
                        }
                        Dictionary <string, float> executionTimes = null;
                        ColumnarDataTable          result         = null;

                        // read file where the results of a particular query are saved:
                        var queryExpectedResultFile = new StreamReader($"../../../QikkDB.BenchmarkUtility/{taxiDbName}_testQuery_{queryIndex}.txt");
                        queryIndex++;

                        // read the file header
                        var expectedColumnNames = queryExpectedResultFile.ReadLine().Split('|');

                        // read expected column data types
                        var expectedDataTypes = queryExpectedResultFile.ReadLine().Split('|');

                        Dictionary <string, IList> exptectedColumns = new Dictionary <string, IList>();

                        bool firstPart = true;

                        while (((result, executionTimes) = client.GetNextQueryResult()).result != null)
                        {
                            // long results are divided into multiple parts
                            if (firstPart)
                            {
                                columnData = result.GetColumnData();
                            }
                            else
                            {
                                for (int i = 0; i < expectedColumnNames.Length; i++)
                                {
                                    for (int j = 0; j < result.GetColumnData()[expectedColumnNames[i]].Count; j++)
                                    {
                                        columnData[expectedColumnNames[i]].Add(result.GetColumnData()[expectedColumnNames[i]][j]);
                                    }
                                }
                            }

                            resultSum += executionTimes.Values.Sum();
                            firstPart  = false;
                        }

                        for (int i = 0; i < expectedColumnNames.Length; i++)
                        {
                            switch (expectedDataTypes[i])
                            {
                            case "INT":
                                exptectedColumns.Add(expectedColumnNames[i], new List <Int32>());
                                break;

                            case "LONG":
                                exptectedColumns.Add(expectedColumnNames[i], new List <Int64>());
                                break;

                            case "FLOAT":
                                exptectedColumns.Add(expectedColumnNames[i], new List <Single>());
                                break;

                            case "DOUBLE":
                                exptectedColumns.Add(expectedColumnNames[i], new List <Double>());
                                break;

                            case "STRING":
                                exptectedColumns.Add(expectedColumnNames[i], new List <String>());
                                break;
                            }
                        }

                        // read results from a file:
                        while ((queryExptectedString = queryExpectedResultFile.ReadLine()) != null)
                        {
                            var results = queryExptectedString.Split('|');

                            for (int i = 0; i < expectedColumnNames.Length; i++)
                            {
                                switch (expectedDataTypes[i])
                                {
                                case "INT":
                                    exptectedColumns[expectedColumnNames[i]].Add(Int32.Parse(results[i]));
                                    break;

                                case "LONG":
                                    exptectedColumns[expectedColumnNames[i]].Add(Int64.Parse(results[i]));
                                    break;

                                case "FLOAT":
                                    exptectedColumns[expectedColumnNames[i]].Add(Single.Parse(results[i]));
                                    break;

                                case "DOUBLE":
                                    var styles   = NumberStyles.AllowDecimalPoint;
                                    var provider = CultureInfo.CreateSpecificCulture("en-US");
                                    exptectedColumns[expectedColumnNames[i]].Add(Double.Parse(results[i], styles, provider));
                                    break;

                                case "STRING":
                                    exptectedColumns[expectedColumnNames[i]].Add(results[i]);
                                    break;
                                }
                            }
                        }

                        // check if the expected result dictionary is the same as actual query result dictionary:
                        for (int i = 0; i < expectedColumnNames.Length; i++)
                        {
                            try
                            {
                                if (exptectedColumns[expectedColumnNames[i]].Count != columnData[expectedColumnNames[i]].Count)
                                {
                                    resultFile.WriteLine($"The query '{queryString}' has FAILED the correct results test. Expected / Actual count of result entries: {exptectedColumns[expectedColumnNames[i]].Count.ToString()} / {columnData[expectedColumnNames[i]].Count.ToString()}");
                                    Console.WriteLine($"The query '{queryString} ' has FAILED the correct results test. Expected / Actual count of result entries: {exptectedColumns[expectedColumnNames[i]].Count} / {columnData[expectedColumnNames[i]].Count}");
                                    correctResultsPassed = false;
                                }
                                else
                                {
                                    // check each element in result's lists
                                    for (int j = 0; j < exptectedColumns[expectedColumnNames[i]].Count; j++)
                                    {
                                        bool tempCorrectResultsPassed = true;

                                        switch (expectedDataTypes[i])
                                        {
                                        case "INT":
                                            if ((int)exptectedColumns[expectedColumnNames[i]][j] != (int)columnData[expectedColumnNames[i]][j])
                                            {
                                                tempCorrectResultsPassed = false;
                                            }
                                            break;

                                        case "LONG":
                                            if ((long)exptectedColumns[expectedColumnNames[i]][j] != (long)columnData[expectedColumnNames[i]][j])
                                            {
                                                tempCorrectResultsPassed = false;
                                            }
                                            break;

                                        case "FLOAT":
                                            if (Math.Abs((float)exptectedColumns[expectedColumnNames[i]][j] - (float)columnData[expectedColumnNames[i]][j]) > 0.001)
                                            {
                                                tempCorrectResultsPassed = false;
                                            }
                                            break;

                                        case "DOUBLE":
                                            if (Math.Abs((double)exptectedColumns[expectedColumnNames[i]][j] - (double)columnData[expectedColumnNames[i]][j]) > 0.001)
                                            {
                                                tempCorrectResultsPassed = false;
                                            }
                                            break;

                                        case "STRING":
                                            if ((string)exptectedColumns[expectedColumnNames[i]][j] != (string)columnData[expectedColumnNames[i]][j])
                                            {
                                                tempCorrectResultsPassed = false;
                                            }
                                            break;
                                        }

                                        if (!tempCorrectResultsPassed)
                                        {
                                            resultFile.WriteLine($"The query '{queryString}' has FAILED the correct results test. Expected[{expectedColumnNames[i].ToString()}][{j.ToString()}] / Actual[{j.ToString()}] returned value: {exptectedColumns[expectedColumnNames[i]][j].ToString()} / {columnData[expectedColumnNames[i]][j].ToString()}");
                                            Console.WriteLine($"The query '{queryString}' has FAILED the correct results test.  Expected[{expectedColumnNames[i].ToString()}][{j}] / Actual[{j}] returned value: {exptectedColumns[expectedColumnNames[i]][j]} / {columnData[expectedColumnNames[i]][j]}");
                                            correctResultsPassed = false;
                                        }
                                    }
                                }
                            }
                            catch (System.Collections.Generic.KeyNotFoundException e)
                            {
                                resultFile.WriteLine($"The query '" + queryString + "' has FAILED the correct results test. Expected / Actual returned value: " + exptectedColumns[expectedColumnNames[i]].ToString() + " / System.Collections.Generic.KeyNotFoundException was thrown: " + e.Message);
                                Console.WriteLine($"The query '" + queryString + "' has FAILED the correct results test. Expected / Actual returned value: " + exptectedColumns[expectedColumnNames[i]] + " / System.Collections.Generic.KeyNotFoundException was thrown: " + e.Message);
                                correctResultsPassed = false;
                            }
                        }

                        resultSum = Math.Round(resultSum);

                        // save query result to a file:
                        resultFile.WriteLine((resultSum).ToString() + " (first run)");
                        Console.WriteLine((resultSum).ToString() + " (first run)");

                        // execute query N times (used cache):
                        for (int i = 0; i < numberOfQueryExec; i++)
                        {
                            try
                            {
                                client.Query(queryString);
                            }
                            catch (Exception e)
                            {
                                Console.WriteLine(e);
                                break;
                            }

                            while (((result, executionTimes) = client.GetNextQueryResult()).result != null)
                            {
                                resultSum += executionTimes.Values.Sum();
                            }
                        }

                        double avgQueryExec = Math.Round(resultSum / numberOfQueryExec);

                        // save query result to a file:
                        resultFile.WriteLine(avgQueryExec.ToString() + " (average cached N runs)");
                        Console.WriteLine(avgQueryExec + " (average cached N runs)");

                        // check if query execution time is acceptable and save the result:
                        int queryExpectedExecTime = System.Convert.ToInt32(queryFile.ReadLine());
                        if (avgQueryExec < queryExpectedExecTime)
                        {
                            resultFile.WriteLine($"The query '{queryString}' has passed the execution time test. Expected / Actual average query execution time: {queryExpectedExecTime.ToString()} / {avgQueryExec.ToString()}");
                            Console.WriteLine($"The query '{queryString}' has passed the execution time test. Expected / Actual average query execution time: {queryExpectedExecTime} / {avgQueryExec}");
                        }
                        else
                        {
                            resultFile.WriteLine($"The query '{queryString}' has FAILED the execution time test. Expected / Actual average query execution time: {queryExpectedExecTime.ToString()} / {avgQueryExec.ToString()}");
                            Console.WriteLine($"The query '{queryString} ' has FAILED the execution time test. Expected / Actual average query execution time: {queryExpectedExecTime} / {avgQueryExec}");
                            avgTimePassed = false;
                        }
                    }
                    queryFile.Close();
                }

                if (String.Equals(arg, "-a") || String.Equals(arg, "-s"))
                {
                    // test stcs queries:
                    use.Use(stcsDbName, client);

                    client.UseDatabase(stcsDbName);

                    var queryFile = new System.IO.StreamReader(stcsQueriesPath);
                    Console.WriteLine($"Benchmark queries from file '{stcsQueriesPath}' were loaded.");

                    while ((queryString = queryFile.ReadLine()) != null)
                    {
                        Console.WriteLine($"Executing benchmark query: {queryString}");
                        resultFile.WriteLine(queryString);

                        // execute query first time (no cache):
                        double resultSum = 0;
                        try
                        {
                            client.Query(queryString);
                        }
                        catch (Exception e)
                        {
                            Console.WriteLine(e);
                            break;
                        }
                        Dictionary <string, float> executionTimes = null;
                        ColumnarDataTable          result         = null;

                        while (((result, executionTimes) = client.GetNextQueryResult()).result != null)
                        {
                            resultSum += executionTimes.Values.Sum();
                        }

                        resultSum = Math.Round(resultSum);

                        // save query result to a file:
                        resultFile.WriteLine((resultSum).ToString() + " (first run)");
                        Console.WriteLine((resultSum).ToString() + " (first run)");

                        // execute query N times (used cache):
                        for (int i = 0; i < numberOfQueryExec; i++)
                        {
                            try
                            {
                                client.Query(queryString);
                            }
                            catch (Exception e)
                            {
                                Console.WriteLine(e);
                                break;
                            }

                            while (((result, executionTimes) = client.GetNextQueryResult()).result != null)
                            {
                                resultSum += executionTimes.Values.Sum();
                            }
                        }

                        double avgQueryExec = Math.Round(resultSum / numberOfQueryExec);

                        // save query result to a file:
                        resultFile.WriteLine(avgQueryExec.ToString() + " (average cached N runs)");
                        Console.WriteLine(avgQueryExec + " (average cached N runs)");

                        // check if query execution time is acceptable and save the result:
                        int queryExpectedExecTime = System.Convert.ToInt32(queryFile.ReadLine());
                        if (avgQueryExec < queryExpectedExecTime)
                        {
                            resultFile.WriteLine($"The query '{queryString}' has passed the execution time test. Expected / Actual average query execution time: {queryExpectedExecTime.ToString()} / {avgQueryExec.ToString()}");
                            Console.WriteLine($"The query '{queryString}' has passed the execution time test. Expected / Actual average query execution time: {queryExpectedExecTime} / {avgQueryExec}");
                        }
                        else
                        {
                            resultFile.WriteLine($"The query '{queryString}' has FAILED the execution time test. Expected / Actual average query execution time: {queryExpectedExecTime.ToString()} / {avgQueryExec.ToString()}");
                            Console.WriteLine($"The query '{queryString} ' has FAILED the execution time test. Expected / Actual average query execution time: {queryExpectedExecTime} / {avgQueryExec}");
                            avgTimePassed = false;
                        }
                    }
                    queryFile.Close();
                }
            }
            resultFile.Close();

            // return exit code:
            if (correctResultsPassed && avgTimePassed)
            {
                // everything was successful
                return(0);
            }

            if (!correctResultsPassed && avgTimePassed)
            {
                // query results were not correct, but query has finished in expected time
                return(1);
            }

            if (correctResultsPassed && !avgTimePassed)
            {
                // query results were corrcet, but query has not finished in expected time
                return(2);
            }

            if (!correctResultsPassed && !avgTimePassed)
            {
                // neither query results were correct, nor query has finished execution in expected time
                return(3);
            }

            // something else has happend
            return(4);
        }