add ODBC library and --list-data-sources mode (DSN) v_0
authorFrantišek Kučera <franta-hg@frantovo.cz>
Mon, 25 May 2020 19:36:06 +0200
branchv_0
changeset 34 24c05e69d68f
parent 33 86ceb97db7de
child 35 cd9db43db120
add ODBC library and --list-data-sources mode (DSN)
bash-completion.sh
nbproject/configurations.xml
src/CLIParser.h
src/CMakeLists.txt
src/Configuration.h
src/DriverManager.cpp
src/DriverManager.h
src/FindODBC.cmake
src/OdbcCommon.h
src/SqlHandler.h
src/relpipe-tr-sql.cpp
--- a/bash-completion.sh	Sat May 23 23:58:50 2020 +0200
+++ b/bash-completion.sh	Mon May 25 19:36:06 2020 +0200
@@ -46,6 +46,7 @@
 			"--copy-renamed"
 			"--file"
 			"--file-keep"
+			"--list-data-sources"
 		)
 		COMPREPLY=($(compgen -W "${OPTIONS[*]}" -- "$w0"))
 	fi
--- a/nbproject/configurations.xml	Sat May 23 23:58:50 2020 +0200
+++ b/nbproject/configurations.xml	Mon May 25 19:36:06 2020 +0200
@@ -43,6 +43,9 @@
     <df root="." name="0">
       <df name="src">
         <in>Connection.cpp</in>
+        <in>DriverManager.cpp</in>
+        <in>DriverManager.h</in>
+        <in>OdbcCommon.h</in>
         <in>PreparedStatement.cpp</in>
         <in>SqlException.h</in>
         <in>relpipe-tr-sql.cpp</in>
@@ -99,6 +102,12 @@
         <ccTool flags="0">
         </ccTool>
       </item>
+      <item path="src/DriverManager.cpp" ex="false" tool="1" flavor2="0">
+        <ccTool flags="0">
+        </ccTool>
+      </item>
+      <item path="src/OdbcCommon.h" ex="false" tool="3" flavor2="0">
+      </item>
       <item path="src/PreparedStatement.cpp" ex="false" tool="1" flavor2="0">
         <ccTool flags="0">
         </ccTool>
@@ -143,6 +152,12 @@
           <preBuildFirst>true</preBuildFirst>
         </preBuild>
       </makefileType>
+      <item path="src/DriverManager.cpp" ex="false" tool="1" flavor2="0">
+      </item>
+      <item path="src/DriverManager.h" ex="false" tool="3" flavor2="0">
+      </item>
+      <item path="src/OdbcCommon.h" ex="false" tool="3" flavor2="0">
+      </item>
       <item path="src/SqlException.h" ex="false" tool="3" flavor2="0">
       </item>
       <item path="src/relpipe-tr-sql.cpp" ex="false" tool="1" flavor2="0">
--- a/src/CLIParser.h	Sat May 23 23:58:50 2020 +0200
+++ b/src/CLIParser.h	Mon May 25 19:36:06 2020 +0200
@@ -54,6 +54,7 @@
 	static const string_t OPTION_COPY_RENAMED;
 	static const string_t OPTION_FILE;
 	static const string_t OPTION_FILE_KEEP;
+	static const string_t OPTION_LIST_DATA_SOURCES;
 
 	Configuration parse(const std::vector<string_t>& arguments) {
 		Configuration c;
@@ -87,6 +88,8 @@
 				else if (value == L"true") c.keepFile = KeepFile::Always;
 				else if (value == L"false") c.keepFile = KeepFile::Never;
 				else throw relpipe::cli::RelpipeCLIException(L"Unsupported keep-file value: " + value + L" Expecting: true, false, auto", relpipe::cli::CLI::EXIT_CODE_BAD_CLI_ARGUMENTS);
+			} else if (option == OPTION_LIST_DATA_SOURCES) {
+				c.listDataSources = true;
 			} else throw relpipe::cli::RelpipeCLIException(L"Unsupported CLI option: " + option, relpipe::cli::CLI::EXIT_CODE_BAD_CLI_ARGUMENTS);
 		}
 		addQuery(c, currentQuery); // last relation
@@ -109,6 +112,7 @@
 const string_t CLIParser::OPTION_COPY_RENAMED = L"--copy-renamed";
 const string_t CLIParser::OPTION_FILE = L"--file";
 const string_t CLIParser::OPTION_FILE_KEEP = L"--file-keep";
+const string_t CLIParser::OPTION_LIST_DATA_SOURCES = L"--list-data-sources";
 
 }
 }
--- a/src/CMakeLists.txt	Sat May 23 23:58:50 2020 +0200
+++ b/src/CMakeLists.txt	Mon May 25 19:36:06 2020 +0200
@@ -16,10 +16,13 @@
 set(EXECUTABLE_FILE    "relpipe-tr-sql")
 set(EXECUTABLE_FILE_IN "relpipe-in-sql")
 
+# ODBC libraries:
+include("FindODBC.cmake")
+
 # Relpipe libraries:
 INCLUDE(FindPkgConfig)
 pkg_check_modules (RELPIPE_LIBS relpipe-lib-reader.cpp relpipe-lib-writer.cpp relpipe-lib-cli.cpp sqlite3)
-include_directories(${RELPIPE_LIBS_INCLUDE_DIRS})
+include_directories(${RELPIPE_LIBS_INCLUDE_DIRS} ${ODBC_INCLUDE_DIRS})
 link_directories(${RELPIPE_LIBS_LIBRARY_DIRS})
 
 # Add ASan AddressSanitizer
@@ -32,13 +35,14 @@
 	${EXECUTABLE_FILE}
 	PreparedStatement.cpp
 	Connection.cpp
+	DriverManager.cpp
 	relpipe-tr-sql.cpp
 )
 
 ADD_CUSTOM_TARGET(in_mode_symlink ALL COMMAND ${CMAKE_COMMAND} -E create_symlink ${EXECUTABLE_FILE} ${EXECUTABLE_FILE_IN})
 
 # Link libraries:
-target_link_libraries(${EXECUTABLE_FILE} ${RELPIPE_LIBS_LIBRARIES})
+target_link_libraries(${EXECUTABLE_FILE} ${RELPIPE_LIBS_LIBRARIES} ${ODBC_LIBRARIES})
 set_property(TARGET ${EXECUTABLE_FILE} PROPERTY INSTALL_RPATH_USE_LINK_PATH TRUE)
 
 install(TARGETS ${EXECUTABLE_FILE} DESTINATION bin)
--- a/src/Configuration.h	Sat May 23 23:58:50 2020 +0200
+++ b/src/Configuration.h	Mon May 25 19:36:06 2020 +0200
@@ -121,6 +121,8 @@
 	std::wistream* sqlAfterRelational = nullptr;
 
 	std::vector<CopyRelations> copyRelations;
+	
+	relpipe::writer::boolean_t listDataSources = false;
 
 	virtual ~Configuration() {
 	}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/DriverManager.cpp	Mon May 25 19:36:06 2020 +0200
@@ -0,0 +1,62 @@
+/**
+ * Relational pipes
+ * Copyright © 2020 František Kučera (Frantovo.cz, GlobalCode.info)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <cstring>
+
+#include <sql.h>
+#include <sqlext.h>
+
+#include "SqlException.h"
+#include "DriverManager.h"
+#include "OdbcCommon.h"
+
+namespace relpipe {
+namespace tr {
+namespace sql {
+
+DriverManager::DriverManager() {
+	env = OdbcCommon::allocateHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE);
+	SQLRETURN result = SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (void*) SQL_OV_ODBC3, 0);
+	if (OdbcCommon::isNotSuccessful(result)) throw SqlException(L"Unable to set ODBC version"); // TODO:, result, SQL_HANDLE_ENV, environment);
+}
+
+DriverManager::~DriverManager() {
+	OdbcCommon::freeHandle(SQL_HANDLE_ENV, env);
+}
+
+std::vector<DriverManager::DataSource> DriverManager::getDataSources() {
+	std::vector<DriverManager::DataSource> list;
+	SQLCHAR name[SQL_MAX_DSN_LENGTH + 1];
+	SQLCHAR description[255];
+	memset(name, 0, sizeof (name));
+	memset(description, 0, sizeof (description));
+	SQLSMALLINT nameLength;
+	SQLSMALLINT descriptionLength;
+	while (true) {
+		SQLRETURN result = SQLDataSources(env, SQL_FETCH_NEXT, name, sizeof (name), &nameLength, description, sizeof (description), &descriptionLength);
+		// TODO: check nameLength and descriptionLength whether values were truncated?
+		if (OdbcCommon::isSuccessful(result)) list.push_back({convertor.from_bytes((char*) name), convertor.from_bytes((char*) description)});
+		else if (result == SQL_NO_DATA_FOUND) break;
+		else throw SqlException(L"Unable to list data sources: " + std::to_wstring(result));
+	}
+	return list;
+}
+
+
+}
+}
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/DriverManager.h	Mon May 25 19:36:06 2020 +0200
@@ -0,0 +1,51 @@
+/**
+ * Relational pipes
+ * Copyright © 2020 František Kučera (Frantovo.cz, GlobalCode.info)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#pragma once
+
+#include <vector>
+#include <codecvt>
+#include <locale>
+
+#include <relpipe/reader/typedefs.h>
+
+#include "SqlException.h"
+
+namespace relpipe {
+namespace tr {
+namespace sql {
+
+class DriverManager {
+private:
+	void* env;
+	std::wstring_convert<std::codecvt_utf8<wchar_t>> convertor; // TODO: support also other encodings
+
+public:
+
+	class DataSource {
+	public:
+		relpipe::reader::string_t name;
+		relpipe::reader::string_t description;
+	};
+
+	DriverManager();
+	virtual ~DriverManager();
+	std::vector<DataSource> getDataSources();
+};
+
+}
+}
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/FindODBC.cmake	Mon May 25 19:36:06 2020 +0200
@@ -0,0 +1,231 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+#[=======================================================================[.rst:
+FindODBC
+--------
+
+Find an Open Database Connectivity (ODBC) include directory and library.
+
+On Windows, when building with Visual Studio, this module assumes the ODBC
+library is provided by the available Windows SDK.
+
+On Unix, this module allows to search for ODBC library provided by
+unixODBC or iODBC implementations of ODBC API.
+This module reads hint about location of the config program:
+
+.. variable:: ODBC_CONFIG
+
+  Location of odbc_config or iodbc-config program
+
+Otherwise, this module tries to find the config program,
+first from unixODBC, then from iODBC.
+If no config program found, this module searches for ODBC header
+and library in list of known locations.
+
+Imported targets
+^^^^^^^^^^^^^^^^
+
+This module defines the following :prop_tgt:`IMPORTED` targets:
+
+.. variable:: ODBC::ODBC
+
+  Imported target for using the ODBC library, if found.
+
+Result variables
+^^^^^^^^^^^^^^^^
+
+.. variable:: ODBC_FOUND
+
+  Set to true if ODBC library found, otherwise false or undefined.
+
+.. variable:: ODBC_INCLUDE_DIRS
+
+  Paths to include directories listed in one variable for use by ODBC client.
+  May be empty on Windows, where the include directory corresponding to the
+  expected Windows SDK is already available in the compilation environment.
+
+.. variable:: ODBC_LIBRARIES
+
+  Paths to libraries to linked against to use ODBC.
+  May just a library name on Windows, where the library directory corresponding
+  to the expected Windows SDK is already available in the compilation environment.
+
+.. variable:: ODBC_CONFIG
+
+  Path to unixODBC or iODBC config program, if found or specified.
+
+Cache variables
+^^^^^^^^^^^^^^^
+
+For users who wish to edit and control the module behavior, this module
+reads hints about search locations from the following variables:
+
+.. variable:: ODBC_INCLUDE_DIR
+
+  Path to ODBC include directory with ``sql.h`` header.
+
+.. variable:: ODBC_LIBRARY
+
+  Path to ODBC library to be linked.
+
+These variables should not be used directly by project code.
+
+Limitations
+^^^^^^^^^^^
+
+On Windows, this module does not search for iODBC.
+On Unix, there is no way to prefer unixODBC over iODBC, or vice versa,
+other than providing the config program location using the ``ODBC_CONFIG``.
+This module does not allow to search for a specific ODBC driver.
+
+#]=======================================================================]
+
+# Define lists used internally
+set(_odbc_include_paths)
+set(_odbc_lib_paths)
+set(_odbc_lib_names)
+set(_odbc_required_libs_names)
+
+### Try Windows Kits ##########################################################
+if(WIN32)
+  # List names of ODBC libraries on Windows
+  if(NOT MINGW)
+    set(ODBC_LIBRARY odbc32.lib)
+  else()
+    set(ODBC_LIBRARY libodbc32.a)
+  endif()
+  set(_odbc_lib_names odbc32;)
+
+  # List additional libraries required to use ODBC library
+  if(MSVC OR CMAKE_CXX_COMPILER_ID MATCHES "Intel")
+    set(_odbc_required_libs_names odbccp32;ws2_32)
+  elseif(MINGW)
+    set(_odbc_required_libs_names odbccp32)
+  endif()
+endif()
+
+### Try unixODBC or iODBC config program ######################################
+if (UNIX)
+  find_program(ODBC_CONFIG
+    NAMES odbc_config iodbc-config
+    DOC "Path to unixODBC or iODBC config program")
+  mark_as_advanced(ODBC_CONFIG)
+endif()
+
+if (UNIX AND ODBC_CONFIG)
+  # unixODBC and iODBC accept unified command line options
+  execute_process(COMMAND ${ODBC_CONFIG} --cflags
+    OUTPUT_VARIABLE _cflags OUTPUT_STRIP_TRAILING_WHITESPACE)
+  execute_process(COMMAND ${ODBC_CONFIG} --libs
+    OUTPUT_VARIABLE _libs OUTPUT_STRIP_TRAILING_WHITESPACE)
+
+  # Collect paths of include directories from CFLAGS
+  separate_arguments(_cflags NATIVE_COMMAND "${_cflags}")
+  foreach(arg IN LISTS _cflags)
+    if("${arg}" MATCHES "^-I(.*)$")
+      list(APPEND _odbc_include_paths "${CMAKE_MATCH_1}")
+    endif()
+  endforeach()
+  unset(_cflags)
+
+  # Collect paths of library names and directories from LIBS
+  separate_arguments(_libs NATIVE_COMMAND "${_libs}")
+  foreach(arg IN LISTS _libs)
+    if("${arg}" MATCHES "^-L(.*)$")
+      list(APPEND _odbc_lib_paths "${CMAKE_MATCH_1}")
+    elseif("${arg}" MATCHES "^-l(.*)$")
+      set(_lib_name ${CMAKE_MATCH_1})
+      string(REGEX MATCH "odbc" _is_odbc ${_lib_name})
+      if(_is_odbc)
+        list(APPEND _odbc_lib_names ${_lib_name})
+      else()
+        list(APPEND _odbc_required_libs_names ${_lib_name})
+      endif()
+      unset(_lib_name)
+    endif()
+  endforeach()
+  unset(_libs)
+endif()
+
+### Try unixODBC or iODBC in include/lib filesystems ##########################
+if (UNIX AND NOT ODBC_CONFIG)
+  # List names of both ODBC libraries, unixODBC and iODBC
+  set(_odbc_lib_names odbc;iodbc;unixodbc;)
+endif()
+
+### Find include directories ##################################################
+find_path(ODBC_INCLUDE_DIR
+  NAMES sql.h
+  PATHS ${_odbc_include_paths})
+
+if(NOT ODBC_INCLUDE_DIR AND WIN32)
+  set(ODBC_INCLUDE_DIR "")
+endif()
+
+### Find libraries ############################################################
+if(NOT ODBC_LIBRARY)
+  find_library(ODBC_LIBRARY
+    NAMES ${_odbc_lib_names}
+    PATHS ${_odbc_lib_paths}
+    PATH_SUFFIXES odbc)
+
+  foreach(_lib IN LISTS _odbc_required_libs_names)
+    find_library(_lib_path
+      NAMES ${_lib}
+      PATHS ${_odbc_lib_paths} # system parths or collected from ODBC_CONFIG
+      PATH_SUFFIXES odbc)
+    if(_lib_path)
+      list(APPEND _odbc_required_libs_paths ${_lib_path})
+    endif()
+    unset(_lib_path CACHE)
+  endforeach()
+endif()
+
+# Unset internal lists as no longer used
+unset(_odbc_include_paths)
+unset(_odbc_lib_paths)
+unset(_odbc_lib_names)
+unset(_odbc_required_libs_names)
+
+### Set result variables ######################################################
+set(_odbc_required_vars ODBC_LIBRARY)
+if(NOT WIN32)
+  list(APPEND _odbc_required_vars ODBC_INCLUDE_DIR)
+endif()
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(ODBC DEFAULT_MSG ${_odbc_required_vars})
+
+unset(_odbc_required_vars)
+
+mark_as_advanced(ODBC_LIBRARY ODBC_INCLUDE_DIR)
+
+set(ODBC_INCLUDE_DIRS ${ODBC_INCLUDE_DIR})
+list(APPEND ODBC_LIBRARIES ${ODBC_LIBRARY})
+list(APPEND ODBC_LIBRARIES ${_odbc_required_libs_paths})
+
+### Import targets ############################################################
+if(ODBC_FOUND)
+  if(NOT TARGET ODBC::ODBC)
+    if(IS_ABSOLUTE "${ODBC_LIBRARY}")
+      add_library(ODBC::ODBC UNKNOWN IMPORTED)
+      set_target_properties(ODBC::ODBC PROPERTIES
+        IMPORTED_LINK_INTERFACE_LANGUAGES "C"
+        IMPORTED_LOCATION "${ODBC_LIBRARY}")
+    else()
+      add_library(ODBC::ODBC INTERFACE IMPORTED)
+      set_target_properties(ODBC::ODBC PROPERTIES
+        IMPORTED_LIBNAME "${ODBC_LIBRARY}")
+    endif()
+    set_target_properties(ODBC::ODBC PROPERTIES
+      INTERFACE_INCLUDE_DIRECTORIES "${ODBC_INCLUDE_DIR}")
+
+    if(_odbc_required_libs_paths)
+      set_property(TARGET ODBC::ODBC APPEND PROPERTY
+        INTERFACE_LINK_LIBRARIES "${_odbc_required_libs_paths}")
+    endif()
+  endif()
+endif()
+
+unset(_odbc_required_libs_paths)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/OdbcCommon.h	Mon May 25 19:36:06 2020 +0200
@@ -0,0 +1,53 @@
+/**
+ * Relational pipes
+ * Copyright © 2020 František Kučera (Frantovo.cz, GlobalCode.info)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#pragma once
+
+#include <sql.h>
+
+#include "SqlException.h"
+
+namespace relpipe {
+namespace tr {
+namespace sql {
+
+class OdbcCommon {
+public:
+
+	static bool isSuccessful(SQLRETURN result) {
+		return result == SQL_SUCCESS || result == SQL_SUCCESS_WITH_INFO;
+	}
+
+	static bool isNotSuccessful(SQLRETURN result) {
+		return !isSuccessful(result);
+	}
+
+	static SQLHANDLE allocateHandle(SQLSMALLINT type, SQLHANDLE input) {
+		SQLHANDLE output;
+		SQLRETURN result = SQLAllocHandle(type, input, &output);
+		if (isNotSuccessful(result)) throw SqlException(L"Unable to allocate handle " + std::to_wstring(type));
+		return output;
+	}
+
+	static void freeHandle(SQLSMALLINT type, SQLHANDLE handle) {
+		SQLRETURN result = SQLFreeHandle(type, handle);
+		if (isNotSuccessful(result)) throw SqlException(L"Unable to free handle of type: " + std::to_wstring(type));
+	}
+};
+
+}
+}
+}
\ No newline at end of file
--- a/src/SqlHandler.h	Sat May 23 23:58:50 2020 +0200
+++ b/src/SqlHandler.h	Mon May 25 19:36:06 2020 +0200
@@ -39,6 +39,7 @@
 #include "SqlInputScanner.h"
 #include "PreparedStatement.h"
 #include "Connection.h"
+#include "DriverManager.h"
 
 namespace relpipe {
 namespace tr {
@@ -54,6 +55,7 @@
 	Configuration configuration;
 	boolean_t fileAlreadyExisted = false;
 	writer::RelationalWriter* relationalWriter;
+	DriverManager* driverManager;
 	std::wstring_convert<codecvt_utf8<wchar_t>> convertor; // TODO: support also other encodings
 	vector<AttributeMetadata> currentReaderMetadata;
 	integer_t currentAttributeIndex = 0;
@@ -156,7 +158,7 @@
 
 public:
 
-	SqlHandler(writer::RelationalWriter* relationalWriter, Configuration& configuration) : relationalWriter(relationalWriter), configuration(configuration) {
+	SqlHandler(writer::RelationalWriter* relationalWriter, DriverManager* driverManager, Configuration& configuration) : relationalWriter(relationalWriter), driverManager(driverManager), configuration(configuration) {
 		std::string file;
 		if (configuration.file.size()) {
 			file = convertor.to_bytes(configuration.file);
@@ -270,6 +272,18 @@
 		} // else: we had no file, everything was in memory
 	}
 
+	static void listDataSources(writer::RelationalWriter* relationalWriter, DriverManager* driverManager) {
+		relationalWriter->startRelation(L"data_source",{
+			{L"name", writer::TypeId::STRING},
+			{L"description", writer::TypeId::STRING}
+		}, true);
+
+		for (DriverManager::DataSource ds : driverManager->getDataSources()) {
+			relationalWriter->writeAttribute(ds.name);
+			relationalWriter->writeAttribute(ds.description);
+		}
+	}
+
 };
 
 }
--- a/src/relpipe-tr-sql.cpp	Sat May 23 23:58:50 2020 +0200
+++ b/src/relpipe-tr-sql.cpp	Mon May 25 19:36:06 2020 +0200
@@ -36,6 +36,7 @@
 #include "SqlHandler.h"
 #include "CLIParser.h"
 #include "Configuration.h"
+#include "DriverManager.h"
 
 using namespace relpipe::cli;
 using namespace relpipe::reader;
@@ -52,19 +53,23 @@
 		CLIParser cliParser;
 		Configuration configuration = cliParser.parse(cli.arguments());
 
-		if (std::regex_match(cli.programName(), std::wregex(L"^(.*/)?relpipe-in-sql$"))) {
+		std::shared_ptr<DriverManager> driverManager = std::make_shared<DriverManager>();
+		std::shared_ptr<writer::RelationalWriter> writer(writer::Factory::create(std::cout));
+
+		if (configuration.listDataSources) {
+			// --list-data-sources:
+			SqlHandler::listDataSources(writer.get(), driverManager.get());
+		} else if (std::regex_match(cli.programName(), std::wregex(L"^(.*/)?relpipe-in-sql$"))) {
 			// relpipe-in-sql:
 			if (cli.arguments().size() == 0) configuration.copyRelations.push_back({L".*", L"", false});
-			std::shared_ptr<writer::RelationalWriter> writer(writer::Factory::create(std::cout));
 			configuration.sqlBeforeRelational = isatty(fileno(stdin)) ? nullptr : &std::wcin;
 			configuration.sqlAfterRelational = nullptr;
-			SqlHandler handler(writer.get(), configuration);
+			SqlHandler handler(writer.get(), driverManager.get(), configuration);
 			handler.endOfPipe();
 		} else {
 			// relpipe-tr-sql:
 			std::shared_ptr<reader::RelationalReader> reader(reader::Factory::create(std::cin));
-			std::shared_ptr<writer::RelationalWriter> writer(writer::Factory::create(std::cout));
-			SqlHandler handler(writer.get(), configuration);
+			SqlHandler handler(writer.get(), driverManager.get(), configuration);
 			reader->addHandler(&handler);
 			reader->process();
 		}