src/Connection.cpp
author František Kučera <franta-hg@frantovo.cz>
Sun, 31 May 2020 21:20:24 +0200
branchv_0
changeset 37 3de41719d7eb
parent 36 91cb012d779a
child 38 a871779a4e3c
permissions -rw-r--r--
add options --data-source-name and --data-source-url for custom datasource; drop options --file and --file-keep
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
36
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    18
#include <memory>
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    19
#include <iostream>
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    20
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    21
#include <sql.h>
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    22
#include <sqlext.h>
29
b0ef1e1dc9c8 keep sqlite3.h dependency in Connection.cpp and PreparedStatement.cpp only
František Kučera <franta-hg@frantovo.cz>
parents: 27
diff changeset
    23
27
9441a517fa1f move Connection class to separate .h and .cpp files
František Kučera <franta-hg@frantovo.cz>
parents: 26
diff changeset
    24
#include "Connection.h"
36
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    25
#include "OdbcCommon.h"
0
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    26
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    27
namespace relpipe {
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    28
namespace tr {
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    29
namespace sql {
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    30
36
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    31
Connection::Connection(void* db) : connection(db) {
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    32
	// TODO: transaction control
27
9441a517fa1f move Connection class to separate .h and .cpp files
František Kučera <franta-hg@frantovo.cz>
parents: 26
diff changeset
    33
}
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
    34
27
9441a517fa1f move Connection class to separate .h and .cpp files
František Kučera <franta-hg@frantovo.cz>
parents: 26
diff changeset
    35
Connection::~Connection() {
36
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    36
	SQLRETURN result = SQLDisconnect(connection);
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
    37
	// FIXME: do not throw exception from destructor
36
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    38
	if (OdbcCommon::isNotSuccessful(result)) throw SqlException(L"Unable to disconnect: " + std::to_wstring(result));
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    39
	OdbcCommon::freeHandle(SQL_HANDLE_DBC, connection);
27
9441a517fa1f move Connection class to separate .h and .cpp files
František Kučera <franta-hg@frantovo.cz>
parents: 26
diff changeset
    40
}
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
    41
36
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    42
PreparedStatement* Connection::prepareStatement(relpipe::reader::string_t sql) {
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    43
	SQLHSTMT statement = OdbcCommon::allocateHandle(SQL_HANDLE_STMT, connection);
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    44
	SQLRETURN result = SQLPrepare(statement, (SQLCHAR*) convertor.to_bytes(sql).c_str(), SQL_NTS);
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    45
	if (OdbcCommon::isNotSuccessful(result)) {
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    46
		OdbcCommon::freeHandle(SQL_HANDLE_STMT, statement);
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    47
		throw SqlException(L"Unable to prepare statement", result, SQL_HANDLE_DBC, connection); // TODO: SQL_HANDLE_STMT?
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    48
	}
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    49
	return new PreparedStatement(statement);
27
9441a517fa1f move Connection class to separate .h and .cpp files
František Kučera <franta-hg@frantovo.cz>
parents: 26
diff changeset
    50
}
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
    51
27
9441a517fa1f move Connection class to separate .h and .cpp files
František Kučera <franta-hg@frantovo.cz>
parents: 26
diff changeset
    52
bool Connection::getAutoCommit() {
36
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    53
	// TODO: transaction control
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    54
	return false;
27
9441a517fa1f move Connection class to separate .h and .cpp files
František Kučera <franta-hg@frantovo.cz>
parents: 26
diff changeset
    55
}
7
9119b29d1e7c insert records
František Kučera <franta-hg@frantovo.cz>
parents: 6
diff changeset
    56
27
9441a517fa1f move Connection class to separate .h and .cpp files
František Kučera <franta-hg@frantovo.cz>
parents: 26
diff changeset
    57
void Connection::setAutoCommit(bool autoCommit) {
36
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    58
	// TODO: transaction control
27
9441a517fa1f move Connection class to separate .h and .cpp files
František Kučera <franta-hg@frantovo.cz>
parents: 26
diff changeset
    59
}
8
3e076cc76c89 data types
František Kučera <franta-hg@frantovo.cz>
parents: 7
diff changeset
    60
27
9441a517fa1f move Connection class to separate .h and .cpp files
František Kučera <franta-hg@frantovo.cz>
parents: 26
diff changeset
    61
void Connection::commit() {
36
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    62
	SQLRETURN result = SQLEndTran(SQL_HANDLE_DBC, connection, SQL_COMMIT);
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    63
	if (OdbcCommon::isNotSuccessful(result)) throw SqlException(L"Unable to COMMIT: " + std::to_wstring(result));
27
9441a517fa1f move Connection class to separate .h and .cpp files
František Kučera <franta-hg@frantovo.cz>
parents: 26
diff changeset
    64
}
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
    65
27
9441a517fa1f move Connection class to separate .h and .cpp files
František Kučera <franta-hg@frantovo.cz>
parents: 26
diff changeset
    66
void Connection::rollback() {
36
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    67
	SQLRETURN result = SQLEndTran(SQL_HANDLE_DBC, connection, SQL_ROLLBACK);
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    68
	if (OdbcCommon::isNotSuccessful(result)) throw SqlException(L"Unable to ROLLBACK: " + std::to_wstring(result));
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    69
}
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    70
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    71
std::vector<Connection::TableMetaData> Connection::getTables() {
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    72
	std::vector<TableMetaData> tables;
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    73
	SQLHSTMT statementHandle = OdbcCommon::allocateHandle(SQL_HANDLE_STMT, connection);
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    74
	SQLRETURN result = SQLTables(statementHandle, nullptr, 0, nullptr, 0, nullptr, 0, nullptr, 0);
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    75
	if (OdbcCommon::isNotSuccessful(result)) {
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    76
		OdbcCommon::freeHandle(SQL_HANDLE_STMT, statementHandle);
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    77
		throw SqlException(L"Unable get tables (prepare)", result, SQL_HANDLE_DBC, connection); // TODO: SQL_HANDLE_STMT?
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    78
	}
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    79
	ResultSet resultSet(statementHandle);
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    80
	while (resultSet.next()) {
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    81
		TableMetaData tm;
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    82
		tm.catalog = resultSet.getString(1); // FIXME: column name: table_cat
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    83
		tm.schema = resultSet.getString(2); // FIXME: column name: table_schem
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    84
		tm.name = resultSet.getString(3); // FIXME: column name: table_name
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    85
		tm.type = resultSet.getString(4); // FIXME: column name: table_type
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    86
		tables.emplace_back(tm);
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    87
	}
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    88
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    89
	return tables;
27
9441a517fa1f move Connection class to separate .h and .cpp files
František Kučera <franta-hg@frantovo.cz>
parents: 26
diff changeset
    90
}
0
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    91
36
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    92
std::vector<Connection::TablePrivilege> Connection::getTablePrivileges() {
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    93
	std::vector<TablePrivilege> tables;
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    94
	SQLHSTMT statementHandle = OdbcCommon::allocateHandle(SQL_HANDLE_STMT, connection);
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    95
	SQLRETURN result = SQLTablePrivileges(statementHandle, nullptr, 0, nullptr, 0, nullptr, 0);
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    96
	if (OdbcCommon::isNotSuccessful(result)) {
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    97
		OdbcCommon::freeHandle(SQL_HANDLE_STMT, statementHandle);
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    98
		throw SqlException(L"Unable get tables (prepare)", result, SQL_HANDLE_DBC, connection); // TODO: SQL_HANDLE_STMT?
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
    99
	}
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
   100
	ResultSet resultSet(statementHandle);
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
   101
	while (resultSet.next()) {
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
   102
		TablePrivilege tp;
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
   103
		tp.catalog = resultSet.getString(1); // FIXME: column name: table_cat
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
   104
		tp.schema = resultSet.getString(2); // FIXME: column name: table_schem
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
   105
		tp.name = resultSet.getString(3); // FIXME: column name: table_name
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
   106
		tp.grantor = resultSet.getString(4); // FIXME: column name: grantor
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
   107
		tp.grantee = resultSet.getString(5); // FIXME: column name: grantee
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
   108
		tp.privilege = resultSet.getString(6); // FIXME: column name: privilege
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
   109
		tp.isGrantable = resultSet.getString(7) == L"YES"; // FIXME: column name: is_grantable
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
   110
		tables.emplace_back(tp);
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
   111
	}
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
   112
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
   113
	return tables;
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
   114
}
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
   115
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
   116
relpipe::reader::string_t Connection::getUserName() {
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
   117
	std::vector<char> buffer(100);
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
   118
	SQLSMALLINT stringLength;
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
   119
	SQLRETURN result = SQLGetInfo(connection, SQL_USER_NAME, buffer.data(), buffer.size(), &stringLength);
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
   120
	if (OdbcCommon::isNotSuccessful(result)) throw SqlException(L"Unable to get user name: " + std::to_wstring(result));
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
   121
	if (stringLength >= buffer.size()) throw SqlException(L"Unable to get user name: too long" + std::to_wstring(stringLength));
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
   122
	return convertor.from_bytes(buffer.data(), buffer.data() + stringLength);
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
   123
}
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
   124
91cb012d779a use ODBC, avoid direct dependency on SQLite
František Kučera <franta-hg@frantovo.cz>
parents: 32
diff changeset
   125
0
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
   126
}
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
   127
}
c205f5d06418 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
   128
}