src/SqlHandler.h
author František Kučera <franta-hg@frantovo.cz>
Thu, 04 Jun 2020 00:46:00 +0200
branchv_0
changeset 47 428c278af4be
parent 37 3de41719d7eb
child 48 c83119110c7b
permissions -rw-r--r--
rename option --data-source-url to --data-source-string In some implementations like JDBC, the connection string is URL, but in ODBC the string is not formally URL, so it is better to use more general term „data source string“ instead of URL. - data source name (DSN) = name of a pre-configured database connection that should be looked-up in configuration and used - data source string (connection string) = arbitrary string containing (in certain encoding which might and might not be URL) all needed parameters (e.g. server name + port + user name + password) Name and string might sometimes be also combined: in ODBC we can e.g. connect to a string: DSN=relpipe;someParameter=foo;someOther=bar which will lookup configuration for the „relpipe“ data source and will combine it with given parameters.
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
0
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
     1
/**
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
     2
 * Relational pipes
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
     3
 * Copyright © 2019 František Kučera (Frantovo.cz, GlobalCode.info)
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
     4
 *
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
     5
 * This program is free software: you can redistribute it and/or modify
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
     6
 * it under the terms of the GNU General Public License as published by
10
7da7173d84b0 fix license version: GNU GPLv3
František Kučera <franta-hg@frantovo.cz>
parents: 8
diff changeset
     7
 * the Free Software Foundation, version 3.
0
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
     8
 *
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
     9
 * This program is distributed in the hope that it will be useful,
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    12
 * GNU General Public License for more details.
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    13
 *
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    14
 * You should have received a copy of the GNU General Public License
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    15
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    16
 */
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    17
#pragma once
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    18
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    19
#include <memory>
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    20
#include <string>
6
32b4293307f4 create table for each relation
František Kučera <franta-hg@frantovo.cz>
parents: 5
diff changeset
    21
#include <sstream>
14
eacacf060755 add --dump option: allow pass through of relation specified by a regular expression
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
    22
#include <regex>
0
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    23
#include <vector>
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    24
#include <locale>
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    25
#include <codecvt>
5
cbc7817a3346 add option to create DB in a file and to keep that file
František Kučera <franta-hg@frantovo.cz>
parents: 4
diff changeset
    26
#include <unistd.h>
8
3e076cc76c89 data types
František Kučera <franta-hg@frantovo.cz>
parents: 7
diff changeset
    27
#include <cassert>
12
0b38339b871b improve --keep-file option: default is auto = file will be kept, if it was present before the transformation
František Kučera <franta-hg@frantovo.cz>
parents: 11
diff changeset
    28
#include <sys/stat.h>
0
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    29
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    30
#include <relpipe/reader/typedefs.h>
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    31
#include <relpipe/reader/TypeId.h>
8
3e076cc76c89 data types
František Kučera <franta-hg@frantovo.cz>
parents: 7
diff changeset
    32
#include <relpipe/reader/handlers/RelationalReaderValueHandler.h>
0
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    33
#include <relpipe/reader/handlers/AttributeMetadata.h>
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    34
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    35
#include <relpipe/writer/Factory.h>
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    36
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    37
#include "Configuration.h"
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    38
#include "SqlException.h"
33
86ceb97db7de SqlInputScanner for parsing SQL script and separating particular statements; does not depend on sqlite3_complete()
František Kučera <franta-hg@frantovo.cz>
parents: 29
diff changeset
    39
#include "SqlInputScanner.h"
26
49919a60c747 move PreparedStatement class to separate .h and .cpp files
František Kučera <franta-hg@frantovo.cz>
parents: 25
diff changeset
    40
#include "PreparedStatement.h"
27
9441a517fa1f move Connection class to separate .h and .cpp files
František Kučera <franta-hg@frantovo.cz>
parents: 26
diff changeset
    41
#include "Connection.h"
34
24c05e69d68f add ODBC library and --list-data-sources mode (DSN)
František Kučera <franta-hg@frantovo.cz>
parents: 33
diff changeset
    42
#include "DriverManager.h"
0
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    43
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    44
namespace relpipe {
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    45
namespace tr {
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    46
namespace sql {
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    47
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    48
using namespace std;
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    49
using namespace relpipe;
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    50
using namespace relpipe::reader;
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    51
using namespace relpipe::reader::handlers;
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    52
8
3e076cc76c89 data types
František Kučera <franta-hg@frantovo.cz>
parents: 7
diff changeset
    53
class SqlHandler : public RelationalReaderValueHandler {
0
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    54
private:
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    55
	Configuration configuration;
12
0b38339b871b improve --keep-file option: default is auto = file will be kept, if it was present before the transformation
František Kučera <franta-hg@frantovo.cz>
parents: 11
diff changeset
    56
	boolean_t fileAlreadyExisted = false;
0
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    57
	writer::RelationalWriter* relationalWriter;
34
24c05e69d68f add ODBC library and --list-data-sources mode (DSN)
František Kučera <franta-hg@frantovo.cz>
parents: 33
diff changeset
    58
	DriverManager* driverManager;
0
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    59
	std::wstring_convert<codecvt_utf8<wchar_t>> convertor; // TODO: support also other encodings
7
9119b29d1e7c insert records
František Kučera <franta-hg@frantovo.cz>
parents: 6
diff changeset
    60
	vector<AttributeMetadata> currentReaderMetadata;
9119b29d1e7c insert records
František Kučera <franta-hg@frantovo.cz>
parents: 6
diff changeset
    61
	integer_t currentAttributeIndex = 0;
3
0b932e05aa9f sqlite wrapper objects + execute query
František Kučera <franta-hg@frantovo.cz>
parents: 1
diff changeset
    62
	std::unique_ptr<Connection> connection;
7
9119b29d1e7c insert records
František Kučera <franta-hg@frantovo.cz>
parents: 6
diff changeset
    63
	std::unique_ptr<PreparedStatement> currentInsert;
3
0b932e05aa9f sqlite wrapper objects + execute query
František Kučera <franta-hg@frantovo.cz>
parents: 1
diff changeset
    64
15
0ecde5272f8e process SQL input in the relpipe-in-sql mode
František Kučera <franta-hg@frantovo.cz>
parents: 14
diff changeset
    65
	bool readNextSqlStatement(std::wistream* input, std::wstringstream* sql) {
0ecde5272f8e process SQL input in the relpipe-in-sql mode
František Kučera <franta-hg@frantovo.cz>
parents: 14
diff changeset
    66
		sql->str(L"");
0ecde5272f8e process SQL input in the relpipe-in-sql mode
František Kučera <franta-hg@frantovo.cz>
parents: 14
diff changeset
    67
		sql->clear();
0ecde5272f8e process SQL input in the relpipe-in-sql mode
František Kučera <franta-hg@frantovo.cz>
parents: 14
diff changeset
    68
33
86ceb97db7de SqlInputScanner for parsing SQL script and separating particular statements; does not depend on sqlite3_complete()
František Kučera <franta-hg@frantovo.cz>
parents: 29
diff changeset
    69
		SqlInputScanner scanner;
86ceb97db7de SqlInputScanner for parsing SQL script and separating particular statements; does not depend on sqlite3_complete()
František Kučera <franta-hg@frantovo.cz>
parents: 29
diff changeset
    70
15
0ecde5272f8e process SQL input in the relpipe-in-sql mode
František Kučera <franta-hg@frantovo.cz>
parents: 14
diff changeset
    71
		for (wchar_t ch; *input >> ch;) {
33
86ceb97db7de SqlInputScanner for parsing SQL script and separating particular statements; does not depend on sqlite3_complete()
František Kučera <franta-hg@frantovo.cz>
parents: 29
diff changeset
    72
			if (scanner.append(ch)) {
86ceb97db7de SqlInputScanner for parsing SQL script and separating particular statements; does not depend on sqlite3_complete()
František Kučera <franta-hg@frantovo.cz>
parents: 29
diff changeset
    73
				*sql << scanner.getAndReset().c_str();
86ceb97db7de SqlInputScanner for parsing SQL script and separating particular statements; does not depend on sqlite3_complete()
František Kučera <franta-hg@frantovo.cz>
parents: 29
diff changeset
    74
				return true;
86ceb97db7de SqlInputScanner for parsing SQL script and separating particular statements; does not depend on sqlite3_complete()
František Kučera <franta-hg@frantovo.cz>
parents: 29
diff changeset
    75
			}
15
0ecde5272f8e process SQL input in the relpipe-in-sql mode
František Kučera <franta-hg@frantovo.cz>
parents: 14
diff changeset
    76
		}
0ecde5272f8e process SQL input in the relpipe-in-sql mode
František Kučera <franta-hg@frantovo.cz>
parents: 14
diff changeset
    77
36
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 34
diff changeset
    78
		// TODO: support comments at the end of the script (after last ;)
33
86ceb97db7de SqlInputScanner for parsing SQL script and separating particular statements; does not depend on sqlite3_complete()
František Kučera <franta-hg@frantovo.cz>
parents: 29
diff changeset
    79
		string_t remainingSql = scanner.getAndReset();
15
0ecde5272f8e process SQL input in the relpipe-in-sql mode
František Kučera <franta-hg@frantovo.cz>
parents: 14
diff changeset
    80
		for (wchar_t ch : remainingSql) if (ch != L' ' && ch != L'\n' && ch != L'\r' && ch != L'\t') throw SqlException(L"Unexpected EOF, missing „;“ after: „" + remainingSql + L"“");
0ecde5272f8e process SQL input in the relpipe-in-sql mode
František Kučera <franta-hg@frantovo.cz>
parents: 14
diff changeset
    81
0ecde5272f8e process SQL input in the relpipe-in-sql mode
František Kučera <franta-hg@frantovo.cz>
parents: 14
diff changeset
    82
		return false;
0ecde5272f8e process SQL input in the relpipe-in-sql mode
František Kučera <franta-hg@frantovo.cz>
parents: 14
diff changeset
    83
	}
0ecde5272f8e process SQL input in the relpipe-in-sql mode
František Kučera <franta-hg@frantovo.cz>
parents: 14
diff changeset
    84
0ecde5272f8e process SQL input in the relpipe-in-sql mode
František Kučera <franta-hg@frantovo.cz>
parents: 14
diff changeset
    85
	void processSqlInput(std::wistream* input) {
0ecde5272f8e process SQL input in the relpipe-in-sql mode
František Kučera <franta-hg@frantovo.cz>
parents: 14
diff changeset
    86
		if (input == nullptr) return;
0ecde5272f8e process SQL input in the relpipe-in-sql mode
František Kučera <franta-hg@frantovo.cz>
parents: 14
diff changeset
    87
		*input >> std::ws >> std::noskipws;
0ecde5272f8e process SQL input in the relpipe-in-sql mode
František Kučera <franta-hg@frantovo.cz>
parents: 14
diff changeset
    88
		for (std::wstringstream sql; readNextSqlStatement(input, &sql);) {
36
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 34
diff changeset
    89
			std::unique_ptr<PreparedStatement> prepared(connection->prepareStatement(sql.str()));
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 34
diff changeset
    90
			prepared->executeUpdate();
15
0ecde5272f8e process SQL input in the relpipe-in-sql mode
František Kučera <franta-hg@frantovo.cz>
parents: 14
diff changeset
    91
		}
0ecde5272f8e process SQL input in the relpipe-in-sql mode
František Kučera <franta-hg@frantovo.cz>
parents: 14
diff changeset
    92
	}
0ecde5272f8e process SQL input in the relpipe-in-sql mode
František Kučera <franta-hg@frantovo.cz>
parents: 14
diff changeset
    93
36
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 34
diff changeset
    94
	relpipe::writer::TypeId findType(string_t columnName, const Statement& statement, relpipe::writer::TypeId typeFromResultSet) {
24
884ece10575d add --type-cast to allow explicit specification of type for given output attributes
František Kučera <franta-hg@frantovo.cz>
parents: 23
diff changeset
    95
		for (TypeCast typeCast : statement.typeCasts) if (typeCast.name == columnName) return relationalWriter->toTypeId(typeCast.type);
36
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 34
diff changeset
    96
		return typeFromResultSet;
24
884ece10575d add --type-cast to allow explicit specification of type for given output attributes
František Kučera <franta-hg@frantovo.cz>
parents: 23
diff changeset
    97
	}
884ece10575d add --type-cast to allow explicit specification of type for given output attributes
František Kučera <franta-hg@frantovo.cz>
parents: 23
diff changeset
    98
3
0b932e05aa9f sqlite wrapper objects + execute query
František Kučera <franta-hg@frantovo.cz>
parents: 1
diff changeset
    99
	void processStatement(const Statement& statement) {
36
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 34
diff changeset
   100
		std::shared_ptr<PreparedStatement> prepared(connection->prepareStatement(statement.sql));
3
0b932e05aa9f sqlite wrapper objects + execute query
František Kučera <franta-hg@frantovo.cz>
parents: 1
diff changeset
   101
		int parameterCount = statement.parameters.size();
0b932e05aa9f sqlite wrapper objects + execute query
František Kučera <franta-hg@frantovo.cz>
parents: 1
diff changeset
   102
0b932e05aa9f sqlite wrapper objects + execute query
František Kučera <franta-hg@frantovo.cz>
parents: 1
diff changeset
   103
		for (int i = 0; i < parameterCount; i++) {
36
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 34
diff changeset
   104
			prepared->setString(i + 1, statement.parameters[i].value);
3
0b932e05aa9f sqlite wrapper objects + execute query
František Kučera <franta-hg@frantovo.cz>
parents: 1
diff changeset
   105
		}
0b932e05aa9f sqlite wrapper objects + execute query
František Kučera <franta-hg@frantovo.cz>
parents: 1
diff changeset
   106
36
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 34
diff changeset
   107
		std::shared_ptr<ResultSet> resultSet(prepared->executeQuery());
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 34
diff changeset
   108
		std::shared_ptr<ResultSet::MetaData> metaData(resultSet->getMetaData());
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 34
diff changeset
   109
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 34
diff changeset
   110
		auto columnCount = metaData->getColumnCount();
4
925b15fb5c63 generate relational outputs
František Kučera <franta-hg@frantovo.cz>
parents: 3
diff changeset
   111
		std::vector<relpipe::writer::AttributeMetadata> metadata;
36
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 34
diff changeset
   112
		for (int columnNumber = 1; columnNumber <= columnCount; columnNumber++) {
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 34
diff changeset
   113
			auto columnDescriptor = metaData->describeColumn(columnNumber);
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 34
diff changeset
   114
			metadata.push_back({columnDescriptor.name, findType(columnDescriptor.name, statement, columnDescriptor.type)});
24
884ece10575d add --type-cast to allow explicit specification of type for given output attributes
František Kučera <franta-hg@frantovo.cz>
parents: 23
diff changeset
   115
		}
4
925b15fb5c63 generate relational outputs
František Kučera <franta-hg@frantovo.cz>
parents: 3
diff changeset
   116
		relationalWriter->startRelation(statement.relation, metadata, true);
3
0b932e05aa9f sqlite wrapper objects + execute query
František Kučera <franta-hg@frantovo.cz>
parents: 1
diff changeset
   117
36
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 34
diff changeset
   118
		while (resultSet->next()) {
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 34
diff changeset
   119
			for (int columnNumber = 1; columnNumber <= columnCount; columnNumber++) {
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 34
diff changeset
   120
				relationalWriter->writeAttribute(resultSet->getString(columnNumber));
3
0b932e05aa9f sqlite wrapper objects + execute query
František Kučera <franta-hg@frantovo.cz>
parents: 1
diff changeset
   121
			}
0b932e05aa9f sqlite wrapper objects + execute query
František Kučera <franta-hg@frantovo.cz>
parents: 1
diff changeset
   122
		}
0b932e05aa9f sqlite wrapper objects + execute query
František Kučera <franta-hg@frantovo.cz>
parents: 1
diff changeset
   123
	}
0
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
   124
16
3c51a2c32c86 rename --dump to --copy + add option --copy-renamed + allow multiple copy/copy-renamed commands
František Kučera <franta-hg@frantovo.cz>
parents: 15
diff changeset
   125
	void copyRelations(const CopyRelations& copy) {
3c51a2c32c86 rename --dump to --copy + add option --copy-renamed + allow multiple copy/copy-renamed commands
František Kučera <franta-hg@frantovo.cz>
parents: 15
diff changeset
   126
		std::wregex pattern(copy.pattern);
36
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 34
diff changeset
   127
		relpipe::writer::string_t userName = connection->getUserName();
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 34
diff changeset
   128
		for (Connection::TablePrivilege tableMetaData : connection->getTablePrivileges()) {
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 34
diff changeset
   129
			if (regex_match(tableMetaData.name, pattern) && tableMetaData.privilege == L"SELECT" && tableMetaData.grantee == userName) {
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 34
diff changeset
   130
				// TODO: May we have multiple SELECT permissions for same table? Copy it only once.
14
eacacf060755 add --dump option: allow pass through of relation specified by a regular expression
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   131
				std::wstringstream select;
eacacf060755 add --dump option: allow pass through of relation specified by a regular expression
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   132
				select << L"SELECT * FROM ";
36
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 34
diff changeset
   133
				if (tableMetaData.schema.size()) {
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 34
diff changeset
   134
					// TODO: use qualified table name also for regex matching and for relation name
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 34
diff changeset
   135
					writeIdentifier(select, tableMetaData.schema);
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 34
diff changeset
   136
					select << L".";
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 34
diff changeset
   137
				}
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 34
diff changeset
   138
				writeIdentifier(select, tableMetaData.name);
14
eacacf060755 add --dump option: allow pass through of relation specified by a regular expression
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   139
eacacf060755 add --dump option: allow pass through of relation specified by a regular expression
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   140
				Statement statement;
36
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 34
diff changeset
   141
				statement.relation = copy.replace ? regex_replace(tableMetaData.name, pattern, copy.replacement) : tableMetaData.name;
14
eacacf060755 add --dump option: allow pass through of relation specified by a regular expression
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   142
				statement.sql = select.str();
eacacf060755 add --dump option: allow pass through of relation specified by a regular expression
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   143
				processStatement(statement);
eacacf060755 add --dump option: allow pass through of relation specified by a regular expression
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   144
			}
eacacf060755 add --dump option: allow pass through of relation specified by a regular expression
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   145
		}
eacacf060755 add --dump option: allow pass through of relation specified by a regular expression
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   146
	}
eacacf060755 add --dump option: allow pass through of relation specified by a regular expression
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   147
6
32b4293307f4 create table for each relation
František Kučera <franta-hg@frantovo.cz>
parents: 5
diff changeset
   148
	relpipe::writer::string_t toSQLType(relpipe::reader::TypeId typeId) {
8
3e076cc76c89 data types
František Kučera <franta-hg@frantovo.cz>
parents: 7
diff changeset
   149
		if (typeId == relpipe::reader::TypeId::BOOLEAN) return L"integer"; // TODO: map selected values back to booleans or allow optional storage as string 
6
32b4293307f4 create table for each relation
František Kučera <franta-hg@frantovo.cz>
parents: 5
diff changeset
   150
		else if (typeId == relpipe::reader::TypeId::INTEGER) return L"integer";
32b4293307f4 create table for each relation
František Kučera <franta-hg@frantovo.cz>
parents: 5
diff changeset
   151
		else return L"text";
32b4293307f4 create table for each relation
František Kučera <franta-hg@frantovo.cz>
parents: 5
diff changeset
   152
	}
32b4293307f4 create table for each relation
František Kučera <franta-hg@frantovo.cz>
parents: 5
diff changeset
   153
32b4293307f4 create table for each relation
František Kučera <franta-hg@frantovo.cz>
parents: 5
diff changeset
   154
	void writeIdentifier(std::wstringstream& output, relpipe::writer::string_t identifier) {
11
ccf19c7e7adf finish writeIdentifier() method
František Kučera <franta-hg@frantovo.cz>
parents: 10
diff changeset
   155
		output << L'"';
6
32b4293307f4 create table for each relation
František Kučera <franta-hg@frantovo.cz>
parents: 5
diff changeset
   156
		for (auto & ch : identifier) {
32b4293307f4 create table for each relation
František Kučera <franta-hg@frantovo.cz>
parents: 5
diff changeset
   157
			if (ch == L'"') output << L"\"\"";
32b4293307f4 create table for each relation
František Kučera <franta-hg@frantovo.cz>
parents: 5
diff changeset
   158
			else output << ch;
32b4293307f4 create table for each relation
František Kučera <franta-hg@frantovo.cz>
parents: 5
diff changeset
   159
		}
11
ccf19c7e7adf finish writeIdentifier() method
František Kučera <franta-hg@frantovo.cz>
parents: 10
diff changeset
   160
		output << L'"';
6
32b4293307f4 create table for each relation
František Kučera <franta-hg@frantovo.cz>
parents: 5
diff changeset
   161
	}
32b4293307f4 create table for each relation
František Kučera <franta-hg@frantovo.cz>
parents: 5
diff changeset
   162
37
3de41719d7eb add options --data-source-name and --data-source-url for custom datasource; drop options --file and --file-keep
František Kučera <franta-hg@frantovo.cz>
parents: 36
diff changeset
   163
	Connection* getConnection() {
3de41719d7eb add options --data-source-name and --data-source-url for custom datasource; drop options --file and --file-keep
František Kučera <franta-hg@frantovo.cz>
parents: 36
diff changeset
   164
		if (configuration.dataSourceName.size()) return driverManager->getConnectionByDSN(configuration.dataSourceName);
47
428c278af4be rename option --data-source-url to --data-source-string
František Kučera <franta-hg@frantovo.cz>
parents: 37
diff changeset
   165
		else if (configuration.dataSourceString.size()) return driverManager->getConnectionByString(configuration.dataSourceString);
428c278af4be rename option --data-source-url to --data-source-string
František Kučera <franta-hg@frantovo.cz>
parents: 37
diff changeset
   166
		else return driverManager->getConnectionByString(L"Driver=SQLite3;Database=:memory:");
37
3de41719d7eb add options --data-source-name and --data-source-url for custom datasource; drop options --file and --file-keep
František Kučera <franta-hg@frantovo.cz>
parents: 36
diff changeset
   167
		// SQLite is default/fallback oprion
3de41719d7eb add options --data-source-name and --data-source-url for custom datasource; drop options --file and --file-keep
František Kučera <franta-hg@frantovo.cz>
parents: 36
diff changeset
   168
		// TODO: use environmental variable to allow setting a different default
3de41719d7eb add options --data-source-name and --data-source-url for custom datasource; drop options --file and --file-keep
František Kučera <franta-hg@frantovo.cz>
parents: 36
diff changeset
   169
	}
3de41719d7eb add options --data-source-name and --data-source-url for custom datasource; drop options --file and --file-keep
František Kučera <franta-hg@frantovo.cz>
parents: 36
diff changeset
   170
0
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
   171
public:
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
   172
34
24c05e69d68f add ODBC library and --list-data-sources mode (DSN)
František Kučera <franta-hg@frantovo.cz>
parents: 33
diff changeset
   173
	SqlHandler(writer::RelationalWriter* relationalWriter, DriverManager* driverManager, Configuration& configuration) : relationalWriter(relationalWriter), driverManager(driverManager), configuration(configuration) {
37
3de41719d7eb add options --data-source-name and --data-source-url for custom datasource; drop options --file and --file-keep
František Kučera <franta-hg@frantovo.cz>
parents: 36
diff changeset
   174
		connection.reset(getConnection());
36
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 34
diff changeset
   175
		//connection->setAutoCommit(false);
1
eb7134dfdcc5 link to the sqlite3 library
František Kučera <franta-hg@frantovo.cz>
parents: 0
diff changeset
   176
	}
eb7134dfdcc5 link to the sqlite3 library
František Kučera <franta-hg@frantovo.cz>
parents: 0
diff changeset
   177
eb7134dfdcc5 link to the sqlite3 library
František Kučera <franta-hg@frantovo.cz>
parents: 0
diff changeset
   178
	virtual ~SqlHandler() {
0
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
   179
	}
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
   180
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
   181
	void startRelation(string_t name, vector<AttributeMetadata> attributes) override {
7
9119b29d1e7c insert records
František Kučera <franta-hg@frantovo.cz>
parents: 6
diff changeset
   182
		currentReaderMetadata = attributes;
9119b29d1e7c insert records
František Kučera <franta-hg@frantovo.cz>
parents: 6
diff changeset
   183
9119b29d1e7c insert records
František Kučera <franta-hg@frantovo.cz>
parents: 6
diff changeset
   184
		// CREATE TABLE:
6
32b4293307f4 create table for each relation
František Kučera <franta-hg@frantovo.cz>
parents: 5
diff changeset
   185
		std::wstringstream sql;
7
9119b29d1e7c insert records
František Kučera <franta-hg@frantovo.cz>
parents: 6
diff changeset
   186
		// TODO: if already exist just append new columns
6
32b4293307f4 create table for each relation
František Kučera <franta-hg@frantovo.cz>
parents: 5
diff changeset
   187
		sql << L"CREATE TABLE ";
32b4293307f4 create table for each relation
František Kučera <franta-hg@frantovo.cz>
parents: 5
diff changeset
   188
		writeIdentifier(sql, name);
32b4293307f4 create table for each relation
František Kučera <franta-hg@frantovo.cz>
parents: 5
diff changeset
   189
		sql << L" (\n";
32b4293307f4 create table for each relation
František Kučera <franta-hg@frantovo.cz>
parents: 5
diff changeset
   190
		for (int i = 0; i < attributes.size(); i++) {
32b4293307f4 create table for each relation
František Kučera <franta-hg@frantovo.cz>
parents: 5
diff changeset
   191
			sql << L"\t";
32b4293307f4 create table for each relation
František Kučera <franta-hg@frantovo.cz>
parents: 5
diff changeset
   192
			writeIdentifier(sql, attributes[i].getAttributeName());
32b4293307f4 create table for each relation
František Kučera <franta-hg@frantovo.cz>
parents: 5
diff changeset
   193
			sql << L" " << toSQLType(attributes[i].getTypeId());
32b4293307f4 create table for each relation
František Kučera <franta-hg@frantovo.cz>
parents: 5
diff changeset
   194
			if (i < attributes.size() - 1) sql << L",\n";
32b4293307f4 create table for each relation
František Kučera <franta-hg@frantovo.cz>
parents: 5
diff changeset
   195
		}
32b4293307f4 create table for each relation
František Kučera <franta-hg@frantovo.cz>
parents: 5
diff changeset
   196
		sql << L"\n)";
0
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
   197
36
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 34
diff changeset
   198
		std::unique_ptr<PreparedStatement> createTable(connection->prepareStatement(sql.str()));
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 34
diff changeset
   199
		createTable->executeUpdate();
7
9119b29d1e7c insert records
František Kučera <franta-hg@frantovo.cz>
parents: 6
diff changeset
   200
9119b29d1e7c insert records
František Kučera <franta-hg@frantovo.cz>
parents: 6
diff changeset
   201
		// prepare INSERT:
9119b29d1e7c insert records
František Kučera <franta-hg@frantovo.cz>
parents: 6
diff changeset
   202
		sql = wstringstream();
9119b29d1e7c insert records
František Kučera <franta-hg@frantovo.cz>
parents: 6
diff changeset
   203
		sql << L"INSERT INTO ";
9119b29d1e7c insert records
František Kučera <franta-hg@frantovo.cz>
parents: 6
diff changeset
   204
		writeIdentifier(sql, name);
9119b29d1e7c insert records
František Kučera <franta-hg@frantovo.cz>
parents: 6
diff changeset
   205
		sql << L" VALUES (";
9119b29d1e7c insert records
František Kučera <franta-hg@frantovo.cz>
parents: 6
diff changeset
   206
		for (int i = 0; i < attributes.size(); i++) {
9119b29d1e7c insert records
František Kučera <franta-hg@frantovo.cz>
parents: 6
diff changeset
   207
			sql << L"?";
9119b29d1e7c insert records
František Kučera <franta-hg@frantovo.cz>
parents: 6
diff changeset
   208
			if (i < attributes.size() - 1) sql << L",";
9119b29d1e7c insert records
František Kučera <franta-hg@frantovo.cz>
parents: 6
diff changeset
   209
		}
9119b29d1e7c insert records
František Kučera <franta-hg@frantovo.cz>
parents: 6
diff changeset
   210
		sql << L")";
36
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 34
diff changeset
   211
		currentInsert.reset(connection->prepareStatement(sql.str()));
0
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
   212
	}
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
   213
8
3e076cc76c89 data types
František Kučera <franta-hg@frantovo.cz>
parents: 7
diff changeset
   214
	void attribute(const void* value, const std::type_info& typeInfo) override {
3e076cc76c89 data types
František Kučera <franta-hg@frantovo.cz>
parents: 7
diff changeset
   215
		relpipe::reader::TypeId type = currentReaderMetadata[currentAttributeIndex].getTypeId();
7
9119b29d1e7c insert records
František Kučera <franta-hg@frantovo.cz>
parents: 6
diff changeset
   216
		currentAttributeIndex++;
8
3e076cc76c89 data types
František Kučera <franta-hg@frantovo.cz>
parents: 7
diff changeset
   217
3e076cc76c89 data types
František Kučera <franta-hg@frantovo.cz>
parents: 7
diff changeset
   218
		switch (type) {
3e076cc76c89 data types
František Kučera <franta-hg@frantovo.cz>
parents: 7
diff changeset
   219
			case relpipe::reader::TypeId::BOOLEAN:
3e076cc76c89 data types
František Kučera <franta-hg@frantovo.cz>
parents: 7
diff changeset
   220
			{
3e076cc76c89 data types
František Kučera <franta-hg@frantovo.cz>
parents: 7
diff changeset
   221
				assert(typeInfo == typeid (boolean_t));
3e076cc76c89 data types
František Kučera <franta-hg@frantovo.cz>
parents: 7
diff changeset
   222
				auto* typedValue = static_cast<const boolean_t*> (value);
3e076cc76c89 data types
František Kučera <franta-hg@frantovo.cz>
parents: 7
diff changeset
   223
				currentInsert->setBoolean(currentAttributeIndex, *typedValue);
3e076cc76c89 data types
František Kučera <franta-hg@frantovo.cz>
parents: 7
diff changeset
   224
				break;
3e076cc76c89 data types
František Kučera <franta-hg@frantovo.cz>
parents: 7
diff changeset
   225
			}
3e076cc76c89 data types
František Kučera <franta-hg@frantovo.cz>
parents: 7
diff changeset
   226
			case relpipe::reader::TypeId::INTEGER:
3e076cc76c89 data types
František Kučera <franta-hg@frantovo.cz>
parents: 7
diff changeset
   227
			{
3e076cc76c89 data types
František Kučera <franta-hg@frantovo.cz>
parents: 7
diff changeset
   228
				assert(typeInfo == typeid (integer_t));
3e076cc76c89 data types
František Kučera <franta-hg@frantovo.cz>
parents: 7
diff changeset
   229
				auto* typedValue = static_cast<const integer_t*> (value);
3e076cc76c89 data types
František Kučera <franta-hg@frantovo.cz>
parents: 7
diff changeset
   230
				currentInsert->setInteger(currentAttributeIndex, *typedValue);
3e076cc76c89 data types
František Kučera <franta-hg@frantovo.cz>
parents: 7
diff changeset
   231
				break;
3e076cc76c89 data types
František Kučera <franta-hg@frantovo.cz>
parents: 7
diff changeset
   232
			}
3e076cc76c89 data types
František Kučera <franta-hg@frantovo.cz>
parents: 7
diff changeset
   233
			case relpipe::reader::TypeId::STRING:
3e076cc76c89 data types
František Kučera <franta-hg@frantovo.cz>
parents: 7
diff changeset
   234
			{
3e076cc76c89 data types
František Kučera <franta-hg@frantovo.cz>
parents: 7
diff changeset
   235
				assert(typeInfo == typeid (string_t));
3e076cc76c89 data types
František Kučera <franta-hg@frantovo.cz>
parents: 7
diff changeset
   236
				auto* typedValue = static_cast<const string_t*> (value);
36
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 34
diff changeset
   237
				currentInsert->setString(currentAttributeIndex, *typedValue);
8
3e076cc76c89 data types
František Kučera <franta-hg@frantovo.cz>
parents: 7
diff changeset
   238
				break;
3e076cc76c89 data types
František Kučera <franta-hg@frantovo.cz>
parents: 7
diff changeset
   239
			}
3e076cc76c89 data types
František Kučera <franta-hg@frantovo.cz>
parents: 7
diff changeset
   240
			default:
3e076cc76c89 data types
František Kučera <franta-hg@frantovo.cz>
parents: 7
diff changeset
   241
				throw SqlException(L"Unsupported type in attribute()");
3e076cc76c89 data types
František Kučera <franta-hg@frantovo.cz>
parents: 7
diff changeset
   242
		}
12
0b38339b871b improve --keep-file option: default is auto = file will be kept, if it was present before the transformation
František Kučera <franta-hg@frantovo.cz>
parents: 11
diff changeset
   243
8
3e076cc76c89 data types
František Kučera <franta-hg@frantovo.cz>
parents: 7
diff changeset
   244
		if (currentAttributeIndex % currentReaderMetadata.size() == 0) {
36
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 34
diff changeset
   245
			currentInsert->executeUpdate();
7
9119b29d1e7c insert records
František Kučera <franta-hg@frantovo.cz>
parents: 6
diff changeset
   246
			currentInsert->reset();
9119b29d1e7c insert records
František Kučera <franta-hg@frantovo.cz>
parents: 6
diff changeset
   247
			currentAttributeIndex = 0;
9119b29d1e7c insert records
František Kučera <franta-hg@frantovo.cz>
parents: 6
diff changeset
   248
		}
0
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
   249
	}
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
   250
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
   251
	void endOfPipe() {
15
0ecde5272f8e process SQL input in the relpipe-in-sql mode
František Kučera <franta-hg@frantovo.cz>
parents: 14
diff changeset
   252
		// process optional SQL input
0ecde5272f8e process SQL input in the relpipe-in-sql mode
František Kučera <franta-hg@frantovo.cz>
parents: 14
diff changeset
   253
		processSqlInput(configuration.sqlBeforeRelational);
0ecde5272f8e process SQL input in the relpipe-in-sql mode
František Kučera <franta-hg@frantovo.cz>
parents: 14
diff changeset
   254
12
0b38339b871b improve --keep-file option: default is auto = file will be kept, if it was present before the transformation
František Kučera <franta-hg@frantovo.cz>
parents: 11
diff changeset
   255
		// run the transformation – process all statements:
3
0b932e05aa9f sqlite wrapper objects + execute query
František Kučera <franta-hg@frantovo.cz>
parents: 1
diff changeset
   256
		for (const Statement& statement : configuration.statements) processStatement(statement);
6
32b4293307f4 create table for each relation
František Kučera <franta-hg@frantovo.cz>
parents: 5
diff changeset
   257
15
0ecde5272f8e process SQL input in the relpipe-in-sql mode
František Kučera <franta-hg@frantovo.cz>
parents: 14
diff changeset
   258
		// process optional SQL input
0ecde5272f8e process SQL input in the relpipe-in-sql mode
František Kučera <franta-hg@frantovo.cz>
parents: 14
diff changeset
   259
		processSqlInput(configuration.sqlAfterRelational);
0ecde5272f8e process SQL input in the relpipe-in-sql mode
František Kučera <franta-hg@frantovo.cz>
parents: 14
diff changeset
   260
14
eacacf060755 add --dump option: allow pass through of relation specified by a regular expression
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   261
		// pass-through some relations:
16
3c51a2c32c86 rename --dump to --copy + add option --copy-renamed + allow multiple copy/copy-renamed commands
František Kučera <franta-hg@frantovo.cz>
parents: 15
diff changeset
   262
		for (const CopyRelations& copy : configuration.copyRelations) copyRelations(copy);
23
d3bfbce022aa support signed integers, negative numbers; binary format change: encode numbers as SLEB128
František Kučera <franta-hg@frantovo.cz>
parents: 20
diff changeset
   263
25
ec793cb3e686 rename DB methods to match standard API
František Kučera <franta-hg@frantovo.cz>
parents: 24
diff changeset
   264
		connection->commit();
0
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
   265
	}
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
   266
34
24c05e69d68f add ODBC library and --list-data-sources mode (DSN)
František Kučera <franta-hg@frantovo.cz>
parents: 33
diff changeset
   267
	static void listDataSources(writer::RelationalWriter* relationalWriter, DriverManager* driverManager) {
24c05e69d68f add ODBC library and --list-data-sources mode (DSN)
František Kučera <franta-hg@frantovo.cz>
parents: 33
diff changeset
   268
		relationalWriter->startRelation(L"data_source",{
24c05e69d68f add ODBC library and --list-data-sources mode (DSN)
František Kučera <franta-hg@frantovo.cz>
parents: 33
diff changeset
   269
			{L"name", writer::TypeId::STRING},
24c05e69d68f add ODBC library and --list-data-sources mode (DSN)
František Kučera <franta-hg@frantovo.cz>
parents: 33
diff changeset
   270
			{L"description", writer::TypeId::STRING}
24c05e69d68f add ODBC library and --list-data-sources mode (DSN)
František Kučera <franta-hg@frantovo.cz>
parents: 33
diff changeset
   271
		}, true);
24c05e69d68f add ODBC library and --list-data-sources mode (DSN)
František Kučera <franta-hg@frantovo.cz>
parents: 33
diff changeset
   272
24c05e69d68f add ODBC library and --list-data-sources mode (DSN)
František Kučera <franta-hg@frantovo.cz>
parents: 33
diff changeset
   273
		for (DriverManager::DataSource ds : driverManager->getDataSources()) {
24c05e69d68f add ODBC library and --list-data-sources mode (DSN)
František Kučera <franta-hg@frantovo.cz>
parents: 33
diff changeset
   274
			relationalWriter->writeAttribute(ds.name);
24c05e69d68f add ODBC library and --list-data-sources mode (DSN)
František Kučera <franta-hg@frantovo.cz>
parents: 33
diff changeset
   275
			relationalWriter->writeAttribute(ds.description);
24c05e69d68f add ODBC library and --list-data-sources mode (DSN)
František Kučera <franta-hg@frantovo.cz>
parents: 33
diff changeset
   276
		}
24c05e69d68f add ODBC library and --list-data-sources mode (DSN)
František Kučera <franta-hg@frantovo.cz>
parents: 33
diff changeset
   277
	}
24c05e69d68f add ODBC library and --list-data-sources mode (DSN)
František Kučera <franta-hg@frantovo.cz>
parents: 33
diff changeset
   278
0
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
   279
};
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
   280
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
   281
}
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
   282
}
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
   283
}