summaryrefslogtreecommitdiffstats
path: root/dbaccess/source/filter/hsqldb
diff options
context:
space:
mode:
Diffstat (limited to 'dbaccess/source/filter/hsqldb')
-rw-r--r--dbaccess/source/filter/hsqldb/alterparser.cxx56
-rw-r--r--dbaccess/source/filter/hsqldb/alterparser.hxx52
-rw-r--r--dbaccess/source/filter/hsqldb/columndef.cxx44
-rw-r--r--dbaccess/source/filter/hsqldb/columndef.hxx47
-rw-r--r--dbaccess/source/filter/hsqldb/createparser.cxx298
-rw-r--r--dbaccess/source/filter/hsqldb/createparser.hxx68
-rw-r--r--dbaccess/source/filter/hsqldb/fbalterparser.cxx53
-rw-r--r--dbaccess/source/filter/hsqldb/fbalterparser.hxx30
-rw-r--r--dbaccess/source/filter/hsqldb/fbcreateparser.cxx218
-rw-r--r--dbaccess/source/filter/hsqldb/fbcreateparser.hxx37
-rw-r--r--dbaccess/source/filter/hsqldb/hsqlbinarynode.cxx58
-rw-r--r--dbaccess/source/filter/hsqldb/hsqlbinarynode.hxx63
-rw-r--r--dbaccess/source/filter/hsqldb/hsqlimport.cxx388
-rw-r--r--dbaccess/source/filter/hsqldb/hsqlimport.hxx60
-rw-r--r--dbaccess/source/filter/hsqldb/parseschema.cxx204
-rw-r--r--dbaccess/source/filter/hsqldb/parseschema.hxx84
-rw-r--r--dbaccess/source/filter/hsqldb/rowinputbinary.cxx399
-rw-r--r--dbaccess/source/filter/hsqldb/rowinputbinary.hxx52
-rw-r--r--dbaccess/source/filter/hsqldb/utils.cxx141
-rw-r--r--dbaccess/source/filter/hsqldb/utils.hxx27
20 files changed, 2379 insertions, 0 deletions
diff --git a/dbaccess/source/filter/hsqldb/alterparser.cxx b/dbaccess/source/filter/hsqldb/alterparser.cxx
new file mode 100644
index 000000000..1dd770b1f
--- /dev/null
+++ b/dbaccess/source/filter/hsqldb/alterparser.cxx
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <comphelper/string.hxx>
+#include <sal/log.hxx>
+#include "alterparser.hxx"
+#include "utils.hxx"
+
+namespace dbahsql
+{
+void AlterStmtParser::parse(const OUString& sSql)
+{
+ m_sStmt = sSql;
+ if (!sSql.startsWith("ALTER"))
+ {
+ SAL_WARN("dbaccess", "Not an ALTER statement");
+ return;
+ }
+
+ m_sTableName = utils::getTableNameFromStmt(sSql);
+ auto words = comphelper::string::split(sSql, sal_Unicode(u' '));
+
+ if (words[3] == "ALTER" && words[4] == "COLUMN")
+ {
+ m_sColumnName = words[5];
+ if (words[6] == "RESTART" && words[7] == "WITH")
+ {
+ m_eAction = AlterAction::IDENTITY_RESTART;
+ m_nIdentityParam = words[8].toInt32();
+ }
+ }
+ else if (words[3] == "ADD" && words[4] == "CONSTRAINT")
+ {
+ m_eAction = AlterAction::ADD_FOREIGN;
+ }
+}
+
+} // namespace dbahsql
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/dbaccess/source/filter/hsqldb/alterparser.hxx b/dbaccess/source/filter/hsqldb/alterparser.hxx
new file mode 100644
index 000000000..813ca7b0b
--- /dev/null
+++ b/dbaccess/source/filter/hsqldb/alterparser.hxx
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+
+namespace dbahsql
+{
+enum class AlterAction
+{
+ UNKNOWN,
+ ADD_FOREIGN,
+ IDENTITY_RESTART
+};
+
+class AlterStmtParser
+{
+private:
+ OUString m_sStmt;
+ OUString m_sTableName;
+ OUString m_sColumnName;
+ AlterAction m_eAction = AlterAction::UNKNOWN;
+ sal_Int32 m_nIdentityParam = 0;
+
+protected:
+ AlterAction getActionType() const { return m_eAction; }
+ OUString const& getColumnName() const { return m_sColumnName; }
+ sal_Int32 getIdentityParam() const { return m_nIdentityParam; }
+ OUString const& getStatement() const { return m_sStmt; }
+
+public:
+ virtual ~AlterStmtParser() = default;
+
+ /**
+ * @return name of the table which is to be created.
+ */
+ OUString const& getTableName() const { return m_sTableName; }
+
+ void parse(const OUString& sSql);
+
+ virtual OUString compose() const = 0;
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/dbaccess/source/filter/hsqldb/columndef.cxx b/dbaccess/source/filter/hsqldb/columndef.cxx
new file mode 100644
index 000000000..5a7b74108
--- /dev/null
+++ b/dbaccess/source/filter/hsqldb/columndef.cxx
@@ -0,0 +1,44 @@
+
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "columndef.hxx"
+#include <com/sun/star/sdbc/DataType.hpp>
+
+namespace dbahsql
+{
+using namespace css::sdbc;
+
+ColumnDefinition::ColumnDefinition(const OUString& sName, sal_Int32 eType,
+ std::vector<sal_Int32>&& aParams, bool bPrimary,
+ sal_Int32 nAutoIncr, bool bNullable, bool bCaseInsensitive,
+ const OUString& sDefault)
+ : m_sName(sName)
+ , m_eType(eType)
+ , m_aParams(std::move(aParams))
+ , m_bPrimaryKey(bPrimary)
+ , m_nAutoIncrement(nAutoIncr)
+ , m_bNullable(bNullable)
+ , m_bCaseInsensitive(bCaseInsensitive)
+ , m_sDefaultValue(sDefault)
+{
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/dbaccess/source/filter/hsqldb/columndef.hxx b/dbaccess/source/filter/hsqldb/columndef.hxx
new file mode 100644
index 000000000..5ffa18ba5
--- /dev/null
+++ b/dbaccess/source/filter/hsqldb/columndef.hxx
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+#include <vector>
+
+namespace dbahsql
+{
+/// nAutoIncrement: column is auto incremented started with value nAutoIncrement
+class ColumnDefinition
+{
+private:
+ OUString m_sName;
+ sal_Int32 m_eType; // css::sdbc::DataType
+ std::vector<sal_Int32> m_aParams;
+ bool m_bPrimaryKey;
+ sal_Int32 m_nAutoIncrement;
+ bool m_bNullable;
+ bool m_bCaseInsensitive;
+ OUString m_sDefaultValue;
+
+public:
+ ColumnDefinition(const OUString& sName, sal_Int32 eType, std::vector<sal_Int32>&& aParams,
+ bool bPrimary = false, sal_Int32 nAutoIncr = -1, bool bNullable = true,
+ bool bCaseInsensitive = false, const OUString& sDefault = OUString{});
+
+ OUString const& getName() const { return m_sName; }
+ sal_Int32 getDataType() const { return m_eType; }
+ bool isPrimaryKey() const { return m_bPrimaryKey; }
+ bool isNullable() const { return m_bNullable; }
+ bool isAutoIncremental() const { return m_nAutoIncrement >= 0; }
+ bool isCaseInsensitive() const { return m_bCaseInsensitive; }
+ sal_Int32 getStartValue() const { return m_nAutoIncrement; }
+ const std::vector<sal_Int32>& getParams() const { return m_aParams; }
+ OUString const& getDefault() const { return m_sDefaultValue; }
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/dbaccess/source/filter/hsqldb/createparser.cxx b/dbaccess/source/filter/hsqldb/createparser.cxx
new file mode 100644
index 000000000..360741ce0
--- /dev/null
+++ b/dbaccess/source/filter/hsqldb/createparser.cxx
@@ -0,0 +1,298 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <comphelper/string.hxx>
+#include <sal/log.hxx>
+#include <o3tl/string_view.hxx>
+#include "createparser.hxx"
+#include "utils.hxx"
+#include <com/sun/star/sdbc/DataType.hpp>
+
+using namespace ::comphelper;
+using namespace css::sdbc;
+
+namespace
+{
+/// Returns substring of sSql from the first occurrence of '(' until the
+/// last occurrence of ')' (excluding the parenthesis)
+std::u16string_view lcl_getColumnPart(std::u16string_view sSql)
+{
+ size_t nBeginIndex = sSql.find('(');
+ if (nBeginIndex == std::u16string_view::npos)
+ {
+ SAL_WARN("dbaccess", "No column definitions found");
+ return std::u16string_view();
+ }
+ sal_Int32 nCount = sSql.rfind(')') - nBeginIndex - 1;
+ auto sPart = sSql.substr(nBeginIndex + 1, nCount);
+ return sPart;
+}
+
+/// Constructs a vector of strings that represents the definitions of each
+/// column or constraint.
+///
+/// @param sColumnPart part of the create statement inside the parenthesis
+/// containing the column definitions
+std::vector<OUString> lcl_splitColumnPart(std::u16string_view sColumnPart)
+{
+ std::vector<OUString> sParts = string::split(sColumnPart, sal_Unicode(u','));
+ std::vector<OUString> sReturn;
+
+ OUStringBuffer current(128);
+ for (auto const& part : sParts)
+ {
+ current.append(part);
+ if (current.lastIndexOf("(") > current.lastIndexOf(")"))
+ current.append(","); // it was false split
+ else
+ {
+ sReturn.push_back(current.toString());
+ current.setLength(0);
+ }
+ }
+ return sReturn;
+}
+
+sal_Int32 lcl_getAutoIncrementDefault(std::u16string_view sColumnDef)
+{
+ // TODO what if there are more spaces?
+ size_t nPos = sColumnDef.find(u"GENERATED BY DEFAULT AS IDENTITY");
+ if (nPos != std::u16string_view::npos && nPos > 0)
+ {
+ // TODO parse starting sequence stated by "START WITH"
+ return 0;
+ }
+ return -1;
+}
+
+std::u16string_view lcl_getDefaultValue(std::u16string_view sColumnDef)
+{
+ constexpr std::u16string_view DEFAULT_KW = u"DEFAULT";
+ size_t nDefPos = sColumnDef.find(DEFAULT_KW);
+ if (nDefPos > 0 && nDefPos != std::u16string_view::npos
+ && lcl_getAutoIncrementDefault(sColumnDef) < 0)
+ {
+ std::u16string_view fromDefault
+ = o3tl::trim(sColumnDef.substr(nDefPos + DEFAULT_KW.size()));
+
+ // next word is the value
+ size_t nNextSpace = fromDefault.find(' ');
+ return (nNextSpace > 0 && nNextSpace != std::u16string_view::npos)
+ ? fromDefault.substr(0, nNextSpace)
+ : fromDefault;
+ }
+ return std::u16string_view();
+}
+
+bool lcl_isNullable(std::u16string_view sColumnDef)
+{
+ return sColumnDef.find(u"NOT NULL") == std::u16string_view::npos;
+}
+
+bool lcl_isPrimaryKey(std::u16string_view sColumnDef)
+{
+ return sColumnDef.find(u"PRIMARY KEY") != std::u16string_view::npos;
+}
+
+sal_Int32 lcl_getDataTypeFromHsql(std::u16string_view sTypeName)
+{
+ if (sTypeName == u"CHAR")
+ return DataType::CHAR;
+ else if (sTypeName == u"VARCHAR" || sTypeName == u"VARCHAR_IGNORECASE")
+ return DataType::VARCHAR;
+ else if (sTypeName == u"TINYINT")
+ return DataType::TINYINT;
+ else if (sTypeName == u"SMALLINT")
+ return DataType::SMALLINT;
+ else if (sTypeName == u"INTEGER")
+ return DataType::INTEGER;
+ else if (sTypeName == u"BIGINT")
+ return DataType::BIGINT;
+ else if (sTypeName == u"NUMERIC")
+ return DataType::NUMERIC;
+ else if (sTypeName == u"DECIMAL")
+ return DataType::DECIMAL;
+ else if (sTypeName == u"BOOLEAN")
+ return DataType::BOOLEAN;
+ else if (sTypeName == u"LONGVARCHAR")
+ return DataType::LONGVARCHAR;
+ else if (sTypeName == u"LONGVARBINARY")
+ return DataType::LONGVARBINARY;
+ else if (sTypeName == u"CLOB")
+ return DataType::CLOB;
+ else if (sTypeName == u"BLOB")
+ return DataType::BLOB;
+ else if (sTypeName == u"BINARY")
+ return DataType::BINARY;
+ else if (sTypeName == u"VARBINARY")
+ return DataType::VARBINARY;
+ else if (sTypeName == u"DATE")
+ return DataType::DATE;
+ else if (sTypeName == u"TIME")
+ return DataType::TIME;
+ else if (sTypeName == u"TIMESTAMP")
+ return DataType::TIMESTAMP;
+ else if (sTypeName == u"DOUBLE")
+ return DataType::DOUBLE;
+ else if (sTypeName == u"REAL")
+ return DataType::REAL;
+ else if (sTypeName == u"FLOAT")
+ return DataType::FLOAT;
+
+ assert(false);
+ return -1;
+}
+
+void lcl_addDefaultParameters(std::vector<sal_Int32>& aParams, sal_Int32 eType)
+{
+ if (eType == DataType::CHAR || eType == DataType::BINARY || eType == DataType::VARBINARY
+ || eType == DataType::VARCHAR)
+ aParams.push_back(8000); // from SQL standard
+}
+
+struct ColumnTypeParts
+{
+ OUString typeName;
+ std::vector<sal_Int32> params;
+};
+
+/**
+ * Separates full type descriptions (e.g. NUMERIC(5,4)) to type name (NUMERIC) and
+ * parameters (5,4)
+ */
+ColumnTypeParts lcl_getColumnTypeParts(std::u16string_view sFullTypeName)
+{
+ ColumnTypeParts parts;
+ auto nParenPos = sFullTypeName.find('(');
+ if (nParenPos > 0 && nParenPos != std::u16string_view::npos)
+ {
+ parts.typeName = o3tl::trim(sFullTypeName.substr(0, nParenPos));
+ std::u16string_view sParamStr
+ = sFullTypeName.substr(nParenPos + 1, sFullTypeName.find(')') - nParenPos - 1);
+ auto sParams = string::split(sParamStr, sal_Unicode(u','));
+ for (const auto& sParam : sParams)
+ {
+ parts.params.push_back(sParam.toInt32());
+ }
+ }
+ else
+ {
+ parts.typeName = o3tl::trim(sFullTypeName);
+ lcl_addDefaultParameters(parts.params, lcl_getDataTypeFromHsql(parts.typeName));
+ }
+ return parts;
+}
+
+} // unnamed namespace
+
+namespace dbahsql
+{
+CreateStmtParser::CreateStmtParser() {}
+
+void CreateStmtParser::parsePrimaryKeys(std::u16string_view sPrimaryPart)
+{
+ size_t nParenPos = sPrimaryPart.find('(');
+ if (nParenPos > 0 && nParenPos != std::u16string_view::npos)
+ {
+ std::u16string_view sParamStr
+ = sPrimaryPart.substr(nParenPos + 1, sPrimaryPart.rfind(')') - nParenPos - 1);
+ auto sParams = string::split(sParamStr, sal_Unicode(u','));
+ for (const auto& sParam : sParams)
+ {
+ m_PrimaryKeys.push_back(sParam);
+ }
+ }
+}
+
+void CreateStmtParser::parseColumnPart(std::u16string_view sColumnPart)
+{
+ auto sColumns = lcl_splitColumnPart(sColumnPart);
+ for (const OUString& sColumn : sColumns)
+ {
+ if (sColumn.startsWithIgnoreAsciiCase("PRIMARY KEY"))
+ {
+ parsePrimaryKeys(sColumn);
+ continue;
+ }
+
+ if (sColumn.startsWithIgnoreAsciiCase("CONSTRAINT"))
+ {
+ m_aForeignParts.push_back(sColumn);
+ continue;
+ }
+
+ bool bIsQuoteUsedForColumnName(sColumn[0] == '\"');
+
+ // find next quote after the initial quote
+ // or next space if quote isn't used as delimiter
+ auto nEndColumnName
+ = bIsQuoteUsedForColumnName ? sColumn.indexOf("\"", 1) + 1 : sColumn.indexOf(" ");
+ OUString rColumnName = sColumn.copy(0, nEndColumnName);
+
+ const OUString sFromTypeName(o3tl::trim(sColumn.subView(nEndColumnName)));
+
+ // Now let's manage the column type
+ // search next space to get the whole type name
+ // eg: INTEGER, VARCHAR(10), DECIMAL(6,3)
+ auto nNextSpace = sFromTypeName.indexOf(" ");
+ std::u16string_view sFullTypeName;
+ if (nNextSpace > 0)
+ sFullTypeName = sFromTypeName.subView(0, nNextSpace);
+ // perhaps column type corresponds to the last info here
+ else
+ sFullTypeName = sFromTypeName;
+
+ ColumnTypeParts typeParts = lcl_getColumnTypeParts(sFullTypeName);
+
+ bool bCaseInsensitive = typeParts.typeName.indexOf("IGNORECASE") >= 0;
+ bool isPrimaryKey = lcl_isPrimaryKey(sColumn);
+
+ if (isPrimaryKey)
+ m_PrimaryKeys.push_back(rColumnName);
+
+ const std::u16string_view sColumnWithoutName
+ = sColumn.subView(sColumn.indexOf(typeParts.typeName));
+
+ ColumnDefinition aColDef(rColumnName, lcl_getDataTypeFromHsql(typeParts.typeName),
+ std::move(typeParts.params), isPrimaryKey,
+ lcl_getAutoIncrementDefault(sColumnWithoutName),
+ lcl_isNullable(sColumnWithoutName), bCaseInsensitive,
+ OUString(lcl_getDefaultValue(sColumnWithoutName)));
+
+ m_aColumns.push_back(aColDef);
+ }
+}
+
+void CreateStmtParser::parse(std::u16string_view sSql)
+{
+ // TODO Foreign keys
+ if (!o3tl::starts_with(sSql, u"CREATE"))
+ {
+ SAL_WARN("dbaccess", "Not a create statement");
+ return;
+ }
+
+ m_sTableName = utils::getTableNameFromStmt(sSql);
+ std::u16string_view sColumnPart = lcl_getColumnPart(sSql);
+ parseColumnPart(sColumnPart);
+}
+
+} // namespace dbahsql
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/dbaccess/source/filter/hsqldb/createparser.hxx b/dbaccess/source/filter/hsqldb/createparser.hxx
new file mode 100644
index 000000000..a92f74586
--- /dev/null
+++ b/dbaccess/source/filter/hsqldb/createparser.hxx
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <vector>
+#include "columndef.hxx"
+
+namespace dbahsql
+{
+class SAL_DLLPUBLIC_EXPORT CreateStmtParser
+{
+private:
+ std::vector<ColumnDefinition> m_aColumns;
+ std::vector<OUString> m_aForeignParts;
+ std::vector<OUString> m_PrimaryKeys;
+ OUString m_sTableName;
+
+protected:
+ void parseColumnPart(std::u16string_view sColumnPart);
+ void parsePrimaryKeys(std::u16string_view sPrimaryPart);
+
+public:
+ CreateStmtParser();
+ virtual ~CreateStmtParser() {}
+
+ /**
+ * @return name of the table which is to be created.
+ */
+ OUString const& getTableName() const { return m_sTableName; }
+
+ /**
+ * @return primary keys of parsed table.
+ */
+ std::vector<OUString> const& getPrimaryKeys() const { return m_PrimaryKeys; }
+
+ /**
+ * @return a vector of column descriptors, representing the columns of the
+ * parsed statement.
+ */
+ const std::vector<ColumnDefinition>& getColumnDef() const { return m_aColumns; }
+
+ /**
+ * @return a vector of words.
+ */
+ const std::vector<OUString>& getForeignParts() const { return m_aForeignParts; }
+
+ /**
+ * Parses a create statement.
+ *
+ * @param SQL "CREATE" statement
+ */
+ void parse(std::u16string_view sSql);
+
+ /**
+ * Recreate the sql statement.
+ */
+ virtual OUString compose() const = 0;
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/dbaccess/source/filter/hsqldb/fbalterparser.cxx b/dbaccess/source/filter/hsqldb/fbalterparser.cxx
new file mode 100644
index 000000000..024598c0b
--- /dev/null
+++ b/dbaccess/source/filter/hsqldb/fbalterparser.cxx
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "fbalterparser.hxx"
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+
+namespace dbahsql
+{
+OUString FbAlterStmtParser::compose() const
+{
+ if (getActionType() == AlterAction::UNKNOWN)
+ {
+ SAL_WARN("dbaccess", "Unknown type of ALTER statement in FbAlterStmtParser");
+ return OUString{};
+ }
+ else if (getActionType() == AlterAction::ADD_FOREIGN)
+ return getStatement(); // do nothing with that
+ OUStringBuffer sSql("ALTER TABLE ");
+ sSql.append(getTableName());
+
+ if (getActionType() == AlterAction::IDENTITY_RESTART)
+ {
+ sSql.append(" ALTER COLUMN ");
+ }
+ sSql.append(getColumnName());
+ sSql.append(" RESTART WITH ");
+
+ // Firebird: restart with 0 means the first number is 1, not 0.
+ sSql.append(getIdentityParam() - 1);
+
+ return sSql.makeStringAndClear();
+}
+
+} // dbahsql
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/dbaccess/source/filter/hsqldb/fbalterparser.hxx b/dbaccess/source/filter/hsqldb/fbalterparser.hxx
new file mode 100644
index 000000000..cd48d94d5
--- /dev/null
+++ b/dbaccess/source/filter/hsqldb/fbalterparser.hxx
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include "alterparser.hxx"
+
+namespace dbahsql
+{
+class FbAlterStmtParser : public AlterStmtParser
+{
+protected:
+ void ensureProperTableLengths() const;
+
+public:
+ /**
+ * Compose the result of the parser to statements of Firebird dialect
+ */
+ virtual OUString compose() const override;
+};
+
+} // dbahsql
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/dbaccess/source/filter/hsqldb/fbcreateparser.cxx b/dbaccess/source/filter/hsqldb/fbcreateparser.cxx
new file mode 100644
index 000000000..f3399474c
--- /dev/null
+++ b/dbaccess/source/filter/hsqldb/fbcreateparser.cxx
@@ -0,0 +1,218 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "fbcreateparser.hxx"
+#include "columndef.hxx"
+#include "utils.hxx"
+
+#include <com/sun/star/sdbc/DataType.hpp>
+
+#include <rtl/ustrbuf.hxx>
+
+using namespace css::sdbc;
+
+namespace
+{
+void lcl_appendWithSpace(OUStringBuffer& sBuff, std::u16string_view sStr)
+{
+ sBuff.append(" ");
+ sBuff.append(sStr);
+}
+
+OUString lcl_DataTypetoFbTypeName(sal_Int32 eType)
+{
+ switch (eType)
+ {
+ case DataType::CHAR:
+ case DataType::BINARY:
+ return "CHAR";
+ case DataType::VARCHAR:
+ case DataType::VARBINARY:
+ return "VARCHAR";
+ case DataType::TINYINT: // no such type in Firebird
+ case DataType::SMALLINT:
+ return "SMALLINT";
+ case DataType::INTEGER:
+ return "INTEGER";
+ case DataType::BIGINT:
+ return "BIGINT";
+ case DataType::NUMERIC:
+ return "NUMERIC";
+ case DataType::DECIMAL:
+ return "DECIMAL";
+ case DataType::BOOLEAN:
+ return "BOOLEAN";
+ case DataType::LONGVARCHAR:
+ case DataType::LONGVARBINARY:
+ case DataType::CLOB:
+ case DataType::BLOB:
+ case DataType::OTHER:
+ return "BLOB";
+ case DataType::DATE:
+ return "DATE";
+ case DataType::TIME:
+ return "TIME";
+ case DataType::TIMESTAMP:
+ return "TIMESTAMP";
+ case DataType::DOUBLE:
+ case DataType::REAL:
+ return "DOUBLE PRECISION";
+ case DataType::FLOAT:
+ return "FLOAT";
+ default:
+ assert(false);
+ return OUString();
+ }
+}
+
+OUString lcl_getTypeModifier(sal_Int32 eType)
+{
+ // TODO bind -9546 magic number to a common definition. It also appears
+ // in the connectivity module.
+ switch (eType)
+ {
+ case DataType::CLOB:
+ case DataType::LONGVARCHAR:
+ return "SUB_TYPE 1";
+ case DataType::LONGVARBINARY:
+ return "SUB_TYPE -9546";
+ case DataType::BINARY:
+ case DataType::VARBINARY:
+ return "CHARACTER SET OCTETS";
+ default:
+ return OUString();
+ }
+}
+
+} // unnamed namespace
+
+namespace dbahsql
+{
+void FbCreateStmtParser::appendPrimaryKeyPart(OUStringBuffer& rSql) const
+{
+ const std::vector<OUString>& sPrimaryKeys = getPrimaryKeys();
+ if (sPrimaryKeys.empty())
+ return; // no primary key specified
+
+ rSql.append(",");
+ rSql.append("PRIMARY KEY(");
+ auto it = sPrimaryKeys.cbegin();
+ while (it != sPrimaryKeys.end())
+ {
+ rSql.append(*it);
+ ++it;
+ if (it != sPrimaryKeys.end())
+ rSql.append(",");
+ }
+
+ rSql.append(")"); // end of primary key declaration
+}
+
+void FbCreateStmtParser::ensureProperTableLengths() const
+{
+ const std::vector<ColumnDefinition>& rColumns = getColumnDef();
+ for (const auto& col : rColumns)
+ utils::ensureFirebirdTableLength(col.getName());
+}
+
+OUString FbCreateStmtParser::compose() const
+{
+ ensureProperTableLengths();
+ OUStringBuffer sSql(128);
+ sSql.append("CREATE TABLE ");
+ sSql.append(getTableName());
+
+ lcl_appendWithSpace(sSql, u"("); // column declaration
+ auto& rColumns = getColumnDef();
+ auto columnIter = rColumns.cbegin();
+ while (columnIter != rColumns.end())
+ {
+ lcl_appendWithSpace(sSql, columnIter->getName());
+ lcl_appendWithSpace(sSql, lcl_DataTypetoFbTypeName(columnIter->getDataType()));
+
+ std::vector<sal_Int32> params{ columnIter->getParams() };
+
+ if (columnIter->getDataType() == DataType::NUMERIC
+ || columnIter->getDataType() == DataType::DECIMAL)
+ {
+ // max precision is 18 here
+ if (params.at(0) > 18)
+ params[0] = 18;
+ }
+
+ // Firebird SQL dialect does not like parameters for TIMESTAMP
+ if (!params.empty() && columnIter->getDataType() != DataType::TIMESTAMP)
+ {
+ sSql.append("(");
+ auto it = params.cbegin();
+ while (it != params.end())
+ {
+ sSql.append(*it);
+ ++it;
+ if (it != params.end())
+ sSql.append(",");
+ }
+ sSql.append(")"); // end of param declaration
+ }
+
+ // special modifiers here, based on type (e.g. charset, subtype)
+ OUString sModifier = lcl_getTypeModifier(columnIter->getDataType());
+ if (!sModifier.isEmpty())
+ lcl_appendWithSpace(sSql, sModifier);
+
+ if (columnIter->isAutoIncremental())
+ {
+ lcl_appendWithSpace(sSql, u"GENERATED BY DEFAULT AS IDENTITY (START WITH ");
+
+ // start with 0:
+ // HSQLDB: first value will be 0.
+ // Firebird: first value will be 1.
+ sSql.append(columnIter->getStartValue() - 1);
+ sSql.append(")");
+ }
+ else if (!columnIter->isNullable())
+ lcl_appendWithSpace(sSql, u"NOT NULL");
+
+ if (columnIter->isCaseInsensitive())
+ lcl_appendWithSpace(sSql, u"COLLATE UNICODE_CI");
+
+ const OUString& sDefaultVal = columnIter->getDefault();
+ if (!sDefaultVal.isEmpty())
+ {
+ lcl_appendWithSpace(sSql, u"DEFAULT");
+ if (sDefaultVal.equalsIgnoreAsciiCase("NOW"))
+ lcl_appendWithSpace(sSql, u"\'NOW\'"); // Fb likes it single quoted
+ else
+ lcl_appendWithSpace(sSql, sDefaultVal);
+ }
+
+ ++columnIter;
+ if (columnIter != rColumns.end())
+ sSql.append(",");
+ }
+
+ appendPrimaryKeyPart(sSql);
+
+ sSql.append(")"); // end of column declaration
+ return sSql.makeStringAndClear();
+}
+
+} // dbahsql
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/dbaccess/source/filter/hsqldb/fbcreateparser.hxx b/dbaccess/source/filter/hsqldb/fbcreateparser.hxx
new file mode 100644
index 000000000..6706c05be
--- /dev/null
+++ b/dbaccess/source/filter/hsqldb/fbcreateparser.hxx
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include "createparser.hxx"
+
+namespace dbahsql
+{
+class SAL_DLLPUBLIC_EXPORT FbCreateStmtParser : public CreateStmtParser
+{
+protected:
+ void ensureProperTableLengths() const;
+ void appendPrimaryKeyPart(rtl::OUStringBuffer& rSql) const;
+
+public:
+ /**
+ * Create statement parser, which can compose the result to statements of
+ * Firebird dialect.
+ */
+ FbCreateStmtParser() = default;
+
+ /**
+ * Compose the result of the parser to statements of Firebird dialect
+ */
+ virtual OUString compose() const override;
+};
+
+} // dbahsql
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/dbaccess/source/filter/hsqldb/hsqlbinarynode.cxx b/dbaccess/source/filter/hsqldb/hsqlbinarynode.cxx
new file mode 100644
index 000000000..c02da2c4b
--- /dev/null
+++ b/dbaccess/source/filter/hsqldb/hsqlbinarynode.cxx
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "hsqlbinarynode.hxx"
+#include "rowinputbinary.hxx"
+
+namespace dbahsql
+{
+HsqlBinaryNode::HsqlBinaryNode(sal_Int32 nPos)
+ : m_nPos(nPos)
+{
+}
+
+void HsqlBinaryNode::readChildren(HsqlRowInputStream const& input)
+{
+ SvStream* pStream = input.getInputStream();
+ if (!pStream)
+ return;
+
+ pStream->Seek(m_nPos + 8); // skip size and balance
+ pStream->ReadInt32(m_nLeft);
+ if (m_nLeft <= 0)
+ m_nLeft = -1;
+ pStream->ReadInt32(m_nRight);
+ if (m_nRight <= 0)
+ m_nRight = -1;
+}
+
+std::vector<css::uno::Any> HsqlBinaryNode::readRow(HsqlRowInputStream& input,
+ const std::vector<ColumnDefinition>& aColTypes,
+ sal_Int32 nIndexCount)
+{
+ // skip first 4 bytes (size), and index nodes, 16 bytes each
+ input.seek(m_nPos + 4 + nIndexCount * 16);
+ return input.readOneRow(aColTypes);
+}
+
+sal_Int32 HsqlBinaryNode::getLeft() const { return m_nLeft; }
+sal_Int32 HsqlBinaryNode::getRight() const { return m_nRight; }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/dbaccess/source/filter/hsqldb/hsqlbinarynode.hxx b/dbaccess/source/filter/hsqldb/hsqlbinarynode.hxx
new file mode 100644
index 000000000..97777e7d1
--- /dev/null
+++ b/dbaccess/source/filter/hsqldb/hsqlbinarynode.hxx
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <vector>
+
+#include "rowinputbinary.hxx"
+#include "columndef.hxx"
+
+namespace dbahsql
+{
+class HsqlBinaryNode
+{
+private:
+ sal_Int32 m_nLeft = -1;
+ sal_Int32 m_nRight = -1;
+ sal_Int32 m_nPos = -1;
+
+public:
+ /**
+ * Represents one element of an AVL tree in the binary file which contains
+ * the data.
+ */
+ HsqlBinaryNode(sal_Int32 nPos);
+
+ /**
+ * Read position of children from data file.
+ *
+ * @param rInput input stream where the positions should be read from.
+ */
+ void readChildren(HsqlRowInputStream const& rInput);
+
+ /**
+ * Get Position of left children. It should be called only after position of
+ * children is read.
+ */
+ sal_Int32 getLeft() const;
+
+ /**
+ * Get Position of right children. It should be called only after position of
+ * children is read.
+ */
+ sal_Int32 getRight() const;
+
+ /**
+ * Read the row represented by this node.
+ *
+ * @param rInput input stream where the row should be read from.
+ */
+ std::vector<css::uno::Any> readRow(HsqlRowInputStream& rInput,
+ const std::vector<ColumnDefinition>& aColTypes,
+ sal_Int32 nIndexCount);
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/dbaccess/source/filter/hsqldb/hsqlimport.cxx b/dbaccess/source/filter/hsqldb/hsqlimport.cxx
new file mode 100644
index 000000000..045a32f93
--- /dev/null
+++ b/dbaccess/source/filter/hsqldb/hsqlimport.cxx
@@ -0,0 +1,388 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/embed/XStorage.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/io/WrongFormatException.hpp>
+#include <com/sun/star/util/Date.hpp>
+
+#include <com/sun/star/sdbc/XConnection.hpp>
+#include <com/sun/star/sdbc/XParameters.hpp>
+#include <com/sun/star/sdbc/DataType.hpp>
+#include <com/sun/star/sdbc/SQLException.hpp>
+
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+#include <connectivity/dbtools.hxx>
+#include <connectivity/dbexception.hxx>
+#include <comphelper/processfactory.hxx>
+
+#include <vcl/weld.hxx>
+
+#include "hsqlimport.hxx"
+#include "parseschema.hxx"
+#include "rowinputbinary.hxx"
+
+namespace
+{
+using namespace css::io;
+using namespace css::uno;
+using namespace css::sdbc;
+
+void lcl_setParams(const std::vector<Any>& row, Reference<XParameters> const& xParam,
+ const std::vector<dbahsql::ColumnDefinition>& rColTypes)
+{
+ assert(row.size() == rColTypes.size());
+ size_t nColIndex = 0;
+ for (size_t i = 0; i < rColTypes.size(); ++i)
+ {
+ if (!row.at(i).hasValue())
+ xParam->setNull(i + 1, rColTypes.at(i).getDataType());
+
+ switch (rColTypes.at(i).getDataType())
+ {
+ case DataType::CHAR:
+ case DataType::VARCHAR:
+ case DataType::LONGVARCHAR:
+ {
+ OUString sVal;
+ if (row.at(i) >>= sVal)
+ {
+ xParam->setString(nColIndex + 1, sVal);
+ }
+ }
+ break;
+ case DataType::TINYINT:
+ case DataType::SMALLINT:
+ {
+ sal_Int16 nVal;
+ if (row.at(i) >>= nVal)
+ {
+ xParam->setShort(nColIndex + 1, nVal);
+ }
+ }
+ break;
+ case DataType::INTEGER:
+ {
+ sal_Int32 nVal;
+ if (row.at(i) >>= nVal)
+ {
+ xParam->setInt(nColIndex + 1, nVal);
+ }
+ }
+ break;
+ case DataType::BIGINT:
+ {
+ sal_Int64 nVal;
+ if (row.at(i) >>= nVal)
+ {
+ xParam->setLong(nColIndex + 1, nVal);
+ }
+ }
+ break;
+ case DataType::REAL:
+ case DataType::FLOAT:
+ case DataType::DOUBLE:
+ {
+ double nVal;
+ if (row.at(i) >>= nVal)
+ {
+ xParam->setDouble(nColIndex + 1, nVal);
+ }
+ }
+ break;
+ case DataType::NUMERIC:
+ case DataType::DECIMAL:
+ {
+ Sequence<Any> aNumeric;
+ if (row.at(i) >>= aNumeric)
+ {
+ sal_Int32 nScale = 0;
+ if (aNumeric[1] >>= nScale)
+ xParam->setObjectWithInfo(nColIndex + 1, aNumeric[0],
+ rColTypes.at(i).getDataType(), nScale);
+ }
+ }
+ break;
+ case DataType::DATE:
+ {
+ css::util::Date date;
+ if (row.at(i) >>= date)
+ {
+ xParam->setDate(nColIndex + 1, date);
+ }
+ }
+ break;
+ case DataType::TIME:
+ {
+ css::util::Time time;
+ if (row.at(i) >>= time)
+ {
+ xParam->setTime(nColIndex + 1, time);
+ }
+ }
+ break;
+ case DataType::TIMESTAMP:
+ {
+ css::util::DateTime dateTime;
+ if (row.at(i) >>= dateTime)
+ {
+ xParam->setTimestamp(nColIndex + 1, dateTime);
+ }
+ }
+ break;
+ case DataType::BOOLEAN:
+ {
+ bool bVal = false;
+ if (row.at(i) >>= bVal)
+ xParam->setBoolean(nColIndex + 1, bVal);
+ }
+ break;
+ case DataType::OTHER:
+ // TODO
+ break;
+ case DataType::BINARY:
+ case DataType::VARBINARY:
+ case DataType::LONGVARBINARY:
+ {
+ Sequence<sal_Int8> nVal;
+ if (row.at(i) >>= nVal)
+ {
+ xParam->setBytes(nColIndex + 1, nVal);
+ }
+ break;
+ }
+ default:
+ throw WrongFormatException();
+ }
+ ++nColIndex;
+ }
+}
+
+OUString lcl_createInsertStatement(std::u16string_view sTableName,
+ const std::vector<dbahsql::ColumnDefinition>& rColTypes)
+{
+ assert(rColTypes.size() > 0);
+ OUStringBuffer sql("INSERT INTO ");
+ sql.append(sTableName);
+ sql.append(" (");
+
+ // column names
+ for (size_t i = 0; i < rColTypes.size(); ++i)
+ {
+ sql.append(rColTypes.at(i).getName());
+ if (i < rColTypes.size() - 1)
+ sql.append(", ");
+ }
+ sql.append(")");
+
+ sql.append(" VALUES (");
+ for (size_t i = 0; i < rColTypes.size(); ++i)
+ {
+ sql.append("?");
+ if (i < rColTypes.size() - 1)
+ sql.append(", ");
+ }
+ sql.append(")");
+ return sql.makeStringAndClear();
+}
+
+} // unnamed namespace
+
+namespace dbahsql
+{
+using namespace css::embed;
+
+HsqlImporter::HsqlImporter(Reference<XConnection>& rConnection, const Reference<XStorage>& rStorage)
+ : m_rConnection(rConnection)
+{
+ m_xStorage.set(rStorage);
+}
+
+void HsqlImporter::insertRow(const std::vector<css::uno::Any>& xRows,
+ std::u16string_view sTableName,
+ const std::vector<ColumnDefinition>& rColTypes)
+{
+ OUString sStatement = lcl_createInsertStatement(sTableName, rColTypes);
+ Reference<XPreparedStatement> xStatement = m_rConnection->prepareStatement(sStatement);
+
+ Reference<XParameters> xParameter(xStatement, UNO_QUERY);
+ assert(xParameter.is());
+ xParameter->clearParameters();
+
+ lcl_setParams(xRows, xParameter, rColTypes);
+ xStatement->executeQuery();
+}
+
+void HsqlImporter::processTree(HsqlBinaryNode& rNode, HsqlRowInputStream& rStream,
+ const std::vector<ColumnDefinition>& rColTypes,
+ const OUString& sTableName, sal_Int32 nIndexCount)
+{
+ rNode.readChildren(rStream);
+ sal_Int32 nNext = rNode.getLeft();
+ if (nNext > 0)
+ {
+ HsqlBinaryNode aLeft{ nNext };
+ processTree(aLeft, rStream, rColTypes, sTableName, nIndexCount);
+ }
+ std::vector<Any> row = rNode.readRow(rStream, rColTypes, nIndexCount);
+ insertRow(row, sTableName, rColTypes);
+
+ nNext = rNode.getRight();
+ if (nNext > 0)
+ {
+ HsqlBinaryNode aRight{ nNext };
+ processTree(aRight, rStream, rColTypes, sTableName, nIndexCount);
+ }
+}
+
+/**
+ * Format from the indexed file position is the following:
+ * <Node x20><Row>
+ * Where Node is a 20 byte data, representing the rows in a binary tree:
+ * <Size x4><Balance x4><Left x4> <Right x4><Parent x4>
+ *
+ * Size is the size of <Row>;
+ * Balance: ?
+ * Left/Right/Parent: File position of the Left/Right/Parent child
+ */
+void HsqlImporter::parseTableRows(const std::vector<sal_Int32>& rIndexes,
+ const std::vector<ColumnDefinition>& rColTypes,
+ const OUString& sTableName)
+{
+ static constexpr OUStringLiteral BINARY_FILENAME = u"data";
+
+ if (!m_xStorage->hasByName(BINARY_FILENAME))
+ {
+ SAL_WARN("dbaccess", "data file does not exist in storage during hsqldb import");
+ assert(false); // TODO throw error
+ }
+
+ Reference<css::io::XStream> xStream(
+ m_xStorage->openStreamElement(BINARY_FILENAME, ElementModes::READ));
+
+ HsqlRowInputStream rowInput;
+ Reference<XInputStream> xInput = xStream->getInputStream();
+ rowInput.setInputStream(xInput);
+
+ if (!rIndexes.empty())
+ {
+ HsqlBinaryNode aPrimaryNode{ rIndexes.at(0) };
+ processTree(aPrimaryNode, rowInput, rColTypes, sTableName, rIndexes.size());
+ }
+
+ xInput->closeInput();
+}
+
+void HsqlImporter::importHsqlDatabase(weld::Window* pParent)
+{
+ assert(m_xStorage);
+
+ SchemaParser parser(m_xStorage);
+ std::unique_ptr<SQLException> pException;
+ try
+ {
+ parser.parseSchema();
+ }
+ catch (SQLException& ex)
+ {
+ pException.reset(new SQLException{ std::move(ex) });
+ }
+
+ auto statements = parser.getCreateStatements();
+
+ if (statements.empty() && !pException)
+ {
+ SAL_WARN("dbaccess", "dbashql: there is nothing to import");
+ return; // there is nothing to import
+ }
+
+ // schema
+ for (const auto& sSql : statements)
+ {
+ Reference<XStatement> statement = m_rConnection->createStatement();
+ try
+ {
+ statement->executeQuery(sSql);
+ }
+ catch (SQLException& ex)
+ {
+ // chain errors and keep going
+ if (pException)
+ ex.NextException <<= *pException;
+ pException.reset(new SQLException{ std::move(ex) });
+ }
+ }
+
+ // data
+ for (const auto& tableIndex : parser.getTableIndexes())
+ {
+ try
+ {
+ std::vector<ColumnDefinition> aColTypes = parser.getTableColumnTypes(tableIndex.first);
+ parseTableRows(tableIndex.second, aColTypes, tableIndex.first);
+ }
+ catch (const std::out_of_range& e)
+ {
+ std::unique_ptr<SQLException> ex(new SQLException);
+ const char* msg = e.what();
+ ex->SQLState = OUString(msg, strlen(msg), RTL_TEXTENCODING_ASCII_US);
+ // chain errors and keep going
+ if (pException)
+ ex->NextException <<= *pException;
+ pException = std::move(ex);
+ }
+ catch (SQLException& ex)
+ {
+ // chain errors and keep going
+ if (pException)
+ ex.NextException <<= *pException;
+ pException.reset(new SQLException{ std::move(ex) });
+ }
+ }
+
+ // alter stmts
+ for (const auto& sSql : parser.getAlterStatements())
+ {
+ Reference<XStatement> statement = m_rConnection->createStatement();
+ try
+ {
+ statement->executeQuery(sSql);
+ }
+ catch (SQLException& ex)
+ {
+ // chain errors and keep going
+ if (pException)
+ ex.NextException <<= *pException;
+ pException.reset(new SQLException{ std::move(ex) });
+ }
+ }
+
+ if (pException)
+ {
+ SAL_WARN("dbaccess", "Error during migration");
+ dbtools::showError(dbtools::SQLExceptionInfo{ *pException },
+ pParent ? pParent->GetXWindow() : nullptr,
+ ::comphelper::getProcessComponentContext());
+ }
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/dbaccess/source/filter/hsqldb/hsqlimport.hxx b/dbaccess/source/filter/hsqldb/hsqlimport.hxx
new file mode 100644
index 000000000..7bab53c24
--- /dev/null
+++ b/dbaccess/source/filter/hsqldb/hsqlimport.hxx
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <com/sun/star/embed/XStorage.hpp>
+#include <com/sun/star/sdbc/XConnection.hpp>
+
+#include "rowinputbinary.hxx"
+#include "hsqlbinarynode.hxx"
+#include "columndef.hxx"
+
+namespace weld
+{
+class Window;
+}
+
+namespace dbahsql
+{
+class SAL_DLLPUBLIC_EXPORT HsqlImporter
+{
+private:
+ css::uno::Reference<css::sdbc::XConnection>& m_rConnection;
+ css::uno::Reference<css::embed::XStorage> m_xStorage;
+
+protected:
+ void insertRow(const std::vector<css::uno::Any>& xRows, std::u16string_view sTable,
+ const std::vector<ColumnDefinition>& rColTypes);
+ void processTree(HsqlBinaryNode& rNode, HsqlRowInputStream& rStream,
+ const std::vector<ColumnDefinition>& rColTypes, const OUString& sTableName,
+ sal_Int32 nIndexCount);
+ void parseTableRows(const std::vector<sal_Int32>& rIndexes,
+ const std::vector<ColumnDefinition>& rColTypes, const OUString& sTableName);
+
+public:
+ /**
+ * @param rConnection reference to an sdbc connection. The migration will
+ * perform to this connection.
+ *
+ * @param rStorage Storage where the HSQL database can be found. The schema
+ * definition should be in file "script" in form of DDL SQL statements. The
+ * data should be found in file "data". These are HSQLDB's own format.
+ */
+ HsqlImporter(css::uno::Reference<css::sdbc::XConnection>& rConnection,
+ const css::uno::Reference<css::embed::XStorage>& rStorage);
+
+ /**
+ * Migrate a HSQL database to another.
+ */
+ void importHsqlDatabase(weld::Window* pParent);
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/dbaccess/source/filter/hsqldb/parseschema.cxx b/dbaccess/source/filter/hsqldb/parseschema.cxx
new file mode 100644
index 000000000..b55340f2d
--- /dev/null
+++ b/dbaccess/source/filter/hsqldb/parseschema.cxx
@@ -0,0 +1,204 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include "parseschema.hxx"
+#include "fbcreateparser.hxx"
+#include "fbalterparser.hxx"
+#include "utils.hxx"
+
+#include <com/sun/star/io/TextInputStream.hpp>
+#include <com/sun/star/embed/XStorage.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/string.hxx>
+#include <sal/log.hxx>
+#include <connectivity/dbexception.hxx>
+
+namespace
+{
+using namespace ::comphelper;
+
+using IndexVector = std::vector<sal_Int32>;
+
+class IndexStmtParser
+{
+private:
+ OUString m_sql;
+
+public:
+ IndexStmtParser(const OUString& sSql)
+ : m_sql(sSql)
+ {
+ }
+
+ bool isIndexStatement() const
+ {
+ return m_sql.startsWith("SET TABLE") && m_sql.indexOf("INDEX") >= 0;
+ }
+
+ IndexVector getIndexes() const
+ {
+ assert(isIndexStatement());
+
+ std::u16string_view sIndexPart = m_sql.subView(m_sql.indexOf("INDEX") + 5);
+ size_t nQuotePos = sIndexPart.find('\'');
+ if (nQuotePos == std::u16string_view::npos)
+ nQuotePos = 0;
+ else
+ ++nQuotePos;
+ std::u16string_view sIndexNums
+ = sIndexPart.substr(nQuotePos, sIndexPart.rfind('\'') - nQuotePos);
+
+ std::vector<OUString> sIndexes = string::split(sIndexNums, u' ');
+ IndexVector indexes;
+ for (const auto& sIndex : sIndexes)
+ indexes.push_back(sIndex.toInt32());
+
+ // ignore last element
+ // TODO this is an identity peek, which indicates the value of the next
+ // identity. At the current state all migrated identities start with 0.
+ indexes.pop_back();
+
+ return indexes;
+ }
+
+ OUString getTableName() const
+ {
+ // SET TABLE <tableName> or SET TABLE "<multi word table name>"
+ OUString sName = string::split(m_sql, u' ')[2];
+ if (sName.indexOf('"') >= 0)
+ {
+ // Table name with string delimiter
+ sName = "\"" + string::split(m_sql, u'"')[1] + "\"";
+ }
+ return sName;
+ }
+};
+
+OUString lcl_createAlterForeign(std::u16string_view sForeignPart, std::u16string_view sTableName)
+{
+ return OUString::Concat("ALTER TABLE ") + sTableName + " ADD " + sForeignPart;
+}
+
+} // anonymous namespace
+
+namespace dbahsql
+{
+using namespace css::io;
+using namespace css::uno;
+using namespace css::embed;
+
+SchemaParser::SchemaParser(Reference<XStorage>& rStorage)
+ : m_rStorage(rStorage)
+{
+}
+
+void SchemaParser::parseSchema()
+{
+ assert(m_rStorage);
+
+ static constexpr OUStringLiteral SCHEMA_FILENAME = u"script";
+ if (!m_rStorage->hasByName(SCHEMA_FILENAME))
+ {
+ SAL_WARN("dbaccess", "script file does not exist in storage during hsqldb import");
+ return;
+ }
+
+ Reference<XStream> xStream(m_rStorage->openStreamElement(SCHEMA_FILENAME, ElementModes::READ));
+
+ Reference<XComponentContext> rContext = comphelper::getProcessComponentContext();
+ Reference<XTextInputStream2> xTextInput = TextInputStream::create(rContext);
+ xTextInput->setEncoding("UTF-8");
+ xTextInput->setInputStream(xStream->getInputStream());
+
+ while (!xTextInput->isEOF())
+ {
+ // every line contains exactly one DDL statement
+ OUString sSql = utils::convertToUTF8(
+ OUStringToOString(xTextInput->readLine(), RTL_TEXTENCODING_UTF8));
+
+ IndexStmtParser indexParser{ sSql };
+ if (indexParser.isIndexStatement())
+ {
+ m_Indexes[indexParser.getTableName()] = indexParser.getIndexes();
+ }
+ else if (sSql.startsWith("SET") || sSql.startsWith("CREATE USER")
+ || sSql.startsWith("CREATE SCHEMA") || sSql.startsWith("GRANT"))
+ continue;
+ else if (sSql.startsWith("CREATE CACHED TABLE") || sSql.startsWith("CREATE TABLE"))
+ {
+ FbCreateStmtParser aCreateParser;
+ aCreateParser.parse(sSql);
+
+ for (const auto& foreignParts : aCreateParser.getForeignParts())
+ {
+ m_sAlterStatements.push_back(
+ lcl_createAlterForeign(foreignParts, aCreateParser.getTableName()));
+ }
+
+ sSql = aCreateParser.compose();
+
+ // save column definitions
+ m_ColumnTypes[aCreateParser.getTableName()] = aCreateParser.getColumnDef();
+
+ m_sCreateStatements.push_back(sSql);
+ }
+ else if (sSql.startsWith("ALTER"))
+ {
+ FbAlterStmtParser aAlterParser;
+ aAlterParser.parse(sSql);
+ OUString parsedStmt = aAlterParser.compose();
+
+ if (!parsedStmt.isEmpty())
+ m_sAlterStatements.push_back(parsedStmt);
+ }
+ else if (sSql.startsWith("CREATE VIEW"))
+ m_sCreateStatements.push_back(sSql);
+ }
+}
+
+std::vector<ColumnDefinition> SchemaParser::getTableColumnTypes(const OUString& sTableName) const
+{
+ if (m_ColumnTypes.count(sTableName) < 1)
+ {
+ static constexpr OUStringLiteral NOT_EXIST
+ = u"Internal error while getting column information of table";
+ SAL_WARN("dbaccess", NOT_EXIST << ". Table name is: " << sTableName);
+ dbtools::throwGenericSQLException(NOT_EXIST, ::comphelper::getProcessComponentContext());
+ }
+ return m_ColumnTypes.at(sTableName);
+}
+
+const std::map<OUString, std::vector<sal_Int32>>& SchemaParser::getTableIndexes() const
+{
+ return m_Indexes;
+}
+
+const std::map<OUString, std::vector<OUString>>& SchemaParser::getPrimaryKeys() const
+{
+ return m_PrimaryKeys;
+}
+
+} // namespace dbahsql
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/dbaccess/source/filter/hsqldb/parseschema.hxx b/dbaccess/source/filter/hsqldb/parseschema.hxx
new file mode 100644
index 000000000..04a8e0905
--- /dev/null
+++ b/dbaccess/source/filter/hsqldb/parseschema.hxx
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <com/sun/star/embed/XStorage.hpp>
+#include <vector>
+#include <map>
+
+#include "columndef.hxx"
+
+namespace dbahsql
+{
+using SqlStatementVector = std::vector<OUString>;
+
+class SchemaParser
+{
+private:
+ css::uno::Reference<css::embed::XStorage>& m_rStorage;
+
+ // column type for each table. It is filled after parsing schema.
+ std::map<OUString, std::vector<ColumnDefinition>> m_ColumnTypes;
+
+ // root element's position of data for each table
+ std::map<OUString, std::vector<sal_Int32>> m_Indexes;
+
+ // primary keys of each table
+ std::map<OUString, std::vector<OUString>> m_PrimaryKeys;
+
+ SqlStatementVector m_sCreateStatements;
+ SqlStatementVector m_sAlterStatements;
+
+public:
+ explicit SchemaParser(css::uno::Reference<css::embed::XStorage>& rStorage);
+
+ /**
+ * Parses table definitions contained by a file called "script" in storage.
+ */
+ void parseSchema();
+
+ /**
+ * @return A vector of schema definition SQL strings in Firebird dialect.
+ */
+ const SqlStatementVector& getCreateStatements() const { return m_sCreateStatements; }
+
+ /**
+ * @return A vector of alter SQL strings in Firebird dialect.
+ */
+ const SqlStatementVector& getAlterStatements() const { return m_sAlterStatements; }
+
+ /**
+ * Returns the column types of a table. It should not be called before
+ * calling parseSchema().
+ *
+ * @param sTableName name of the table.
+ *
+ * @return A vector of column descriptors.
+ */
+ std::vector<ColumnDefinition> getTableColumnTypes(const OUString& sTableName) const;
+
+ /**
+ * Returns a vector of indexes for each table. These indexes are used for
+ * locating the data related to the actual table in the binary file.
+ *
+ * The indexes point to root elements of AVL trees. Each node of the tree
+ * contains one row.
+ */
+ const std::map<OUString, std::vector<sal_Int32>>& getTableIndexes() const;
+
+ /**
+ * Returns a vector of column names for each table. These columns are the
+ * primary keys of the table.
+ */
+ const std::map<OUString, std::vector<OUString>>& getPrimaryKeys() const;
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/dbaccess/source/filter/hsqldb/rowinputbinary.cxx b/dbaccess/source/filter/hsqldb/rowinputbinary.cxx
new file mode 100644
index 000000000..95ce05f69
--- /dev/null
+++ b/dbaccess/source/filter/hsqldb/rowinputbinary.cxx
@@ -0,0 +1,399 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include "rowinputbinary.hxx"
+#include <com/sun/star/sdbc/DataType.hpp>
+#include <com/sun/star/io/WrongFormatException.hpp>
+#include <com/sun/star/util/Time.hpp>
+#include <com/sun/star/util/Date.hpp>
+#include <com/sun/star/util/DateTime.hpp>
+
+#include <unotools/ucbstreamhelper.hxx>
+#include <tools/stream.hxx>
+#include <rtl/ustrbuf.hxx>
+
+#include <boost/date_time/posix_time/posix_time.hpp>
+
+namespace
+{
+/**
+ * Converts binary represented big integer value to BCD (Binary Coded
+ * Decimal), and returns a string representation of the number.
+ *
+ * Bytes[0] is the most significant part of the number.
+ */
+OUString lcl_double_dabble(const std::vector<sal_uInt8>& bytes)
+{
+ size_t nbits = 8 * bytes.size(); // length of array in bits
+ size_t nscratch = nbits / 2; // length of scratch in bytes
+ std::vector<char> scratch(nscratch, 0);
+
+ for (size_t i = 0; i < bytes.size(); ++i)
+ {
+ for (size_t j = 0; j < 8; ++j)
+ {
+ /* This bit will be shifted in on the right. */
+ int shifted_in = (bytes[i] & (1 << (7 - j))) ? 1 : 0;
+
+ /* Add 3 everywhere that scratch[k] >= 5. */
+ for (size_t k = 0; k < nscratch; ++k)
+ scratch[k] += (scratch[k] >= 5) ? 3 : 0;
+
+ /* Shift scratch to the left by one position. */
+ for (size_t k = 0; k < nscratch - 1; ++k)
+ {
+ scratch[k] <<= 1;
+ scratch[k] &= 0xF;
+ scratch[k] |= (scratch[k + 1] >= 8) ? 1 : 0;
+ }
+
+ /* Shift in the new bit from arr. */
+ scratch[nscratch - 1] <<= 1;
+ scratch[nscratch - 1] &= 0xF;
+ scratch[nscratch - 1] |= shifted_in;
+ }
+ }
+
+ auto it = scratch.begin();
+ /* Remove leading zeros from the scratch space. */
+ while (*it == 0 && scratch.size() > 1)
+ {
+ it = scratch.erase(it);
+ }
+
+ /* Convert the scratch space from BCD digits to ASCII. */
+ for (auto& digit : scratch)
+ digit += '0';
+
+ /* Resize and return the resulting string. */
+ return OStringToOUString(std::string_view(scratch.data(), scratch.size()),
+ RTL_TEXTENCODING_UTF8);
+}
+
+OUString lcl_makeStringFromBigint(std::vector<sal_uInt8>&& aBytes)
+{
+ OUStringBuffer sRet;
+
+ // two's complement
+ if ((aBytes[0] & 0x80) != 0)
+ {
+ sRet.append("-");
+ for (auto& byte : aBytes)
+ byte = ~byte;
+ // add 1 to byte array
+ // FIXME e.g. 10000 valid ?
+ for (size_t i = aBytes.size() - 1; i != 0; --i)
+ {
+ aBytes[i] += 1;
+ if (aBytes[i] != 0)
+ break;
+ }
+ }
+ // convert binary to BCD
+ OUString sNum = lcl_double_dabble(aBytes);
+ sRet.append(sNum);
+ return sRet.makeStringAndClear();
+}
+
+OUString lcl_putDot(const OUString& sNum, sal_Int32 nScale)
+{
+ // e.g. sNum = "0", nScale = 2 -> "0.00"
+ OUStringBuffer sBuf{ sNum };
+ sal_Int32 nNullsToAppend = nScale - sNum.getLength() + 1;
+ for (sal_Int32 i = 0; i < nNullsToAppend; ++i)
+ sBuf.insert(0, "0");
+
+ if (nScale > 0)
+ sBuf.insert(sBuf.getLength() - 1 - nScale, ".");
+ return sBuf.makeStringAndClear();
+}
+}
+
+namespace dbahsql
+{
+using namespace css::uno;
+using namespace css::sdbc;
+using namespace css::io;
+using namespace boost::posix_time;
+using namespace boost::gregorian;
+
+HsqlRowInputStream::HsqlRowInputStream() {}
+
+void HsqlRowInputStream::setInputStream(Reference<XInputStream> const& rStream)
+{
+ m_pStream = utl::UcbStreamHelper::CreateStream(rStream, true);
+ m_pStream->SetEndian(SvStreamEndian::BIG);
+}
+
+SvStream* HsqlRowInputStream::getInputStream() const { return m_pStream.get(); }
+
+void HsqlRowInputStream::seek(sal_Int32 nPos) { m_pStream->Seek(nPos); }
+
+OUString HsqlRowInputStream::readString()
+{
+ sal_Int32 nLen = 0;
+ m_pStream->ReadInt32(nLen);
+ return readUTF(nLen);
+}
+
+OUString HsqlRowInputStream::readUTF(sal_Int32 nUTFLen)
+{
+ Sequence<sal_Unicode> aBuffer(nUTFLen);
+ sal_Unicode* pStr = aBuffer.getArray();
+
+ sal_Int32 nCount = 0;
+ sal_Int32 nStrLen = 0;
+ while (nCount < nUTFLen)
+ {
+ sal_uInt8 c = 0;
+ m_pStream->ReadUChar(c);
+ sal_uInt8 char2, char3;
+ switch (c >> 4)
+ {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ // 0xxxxxxx
+ nCount++;
+ pStr[nStrLen++] = c;
+ break;
+
+ case 12:
+ case 13:
+ // 110x xxxx 10xx xxxx
+ nCount += 2;
+ if (nCount > nUTFLen)
+ {
+ throw WrongFormatException();
+ }
+
+ m_pStream->ReadUChar(char2);
+ if ((char2 & 0xC0) != 0x80)
+ {
+ throw WrongFormatException();
+ }
+
+ pStr[nStrLen++] = (sal_Unicode(c & 0x1F) << 6) | (char2 & 0x3F);
+ break;
+
+ case 14:
+ // 1110 xxxx 10xx xxxx 10xx xxxx
+ nCount += 3;
+ if (nCount > nUTFLen)
+ {
+ throw WrongFormatException();
+ }
+
+ m_pStream->ReadUChar(char2);
+ m_pStream->ReadUChar(char3);
+
+ if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80))
+ {
+ throw WrongFormatException();
+ }
+ pStr[nStrLen++] = (sal_Unicode(c & 0x0F) << 12) | (sal_Unicode(char2 & 0x3F) << 6)
+ | (char3 & 0x3F);
+ break;
+
+ default:
+ // 10xx xxxx, 1111 xxxx
+ throw WrongFormatException();
+ }
+ }
+ return OUString(pStr, nStrLen);
+}
+
+bool HsqlRowInputStream::checkNull()
+{
+ sal_uInt8 nNull = 0;
+ m_pStream->ReadUChar(nNull);
+ return nNull == 0;
+}
+
+std::vector<Any> HsqlRowInputStream::readOneRow(const std::vector<ColumnDefinition>& nColTypes)
+{
+ auto nLen = nColTypes.size();
+ std::vector<Any> aData;
+
+ for (size_t i = 0; i < nLen; ++i)
+ {
+ if (checkNull())
+ {
+ aData.push_back(Any());
+ continue;
+ }
+
+ sal_Int32 nType = nColTypes[i].getDataType();
+
+ // TODO throw error on EoF
+
+ switch (nType)
+ {
+ case DataType::CHAR:
+ case DataType::VARCHAR:
+ case DataType::LONGVARCHAR:
+ aData.push_back(Any(readString()));
+ break;
+ case DataType::TINYINT:
+ case DataType::SMALLINT:
+ {
+ sal_Int16 value = 0;
+ m_pStream->ReadInt16(value);
+ aData.push_back(Any(value));
+ }
+ break;
+ case DataType::INTEGER:
+ {
+ sal_Int32 value = 0;
+ m_pStream->ReadInt32(value);
+ aData.push_back(Any(value));
+ }
+ break;
+ case DataType::BIGINT:
+ {
+ sal_Int64 value = 0;
+ m_pStream->ReadInt64(value);
+ aData.push_back(Any(value));
+ }
+ break;
+ case DataType::REAL:
+ case DataType::FLOAT:
+ case DataType::DOUBLE:
+ {
+ double value = 0;
+ m_pStream->ReadDouble(value);
+ // FIXME double is not necessarily 4 bytes
+ aData.push_back(Any(value));
+ }
+ break;
+ case DataType::NUMERIC:
+ case DataType::DECIMAL:
+ {
+ sal_Int32 nSize = 0;
+ m_pStream->ReadInt32(nSize);
+
+ std::vector<sal_uInt8> aBytes(nSize);
+ m_pStream->ReadBytes(aBytes.data(), nSize);
+ assert(aBytes.size() > 0);
+
+ sal_Int32 nScale = 0;
+ m_pStream->ReadInt32(nScale);
+
+ OUString sNum = lcl_makeStringFromBigint(std::move(aBytes));
+ Sequence<Any> result{ Any(lcl_putDot(sNum, nScale)), Any(nScale) };
+ aData.push_back(Any(result));
+ }
+ break;
+ case DataType::DATE:
+ {
+ sal_Int64 value = 0;
+ m_pStream->ReadInt64(value); // in millisec, from 1970
+ ptime epoch = time_from_string("1970-01-01 00:00:00.000");
+ ptime time = epoch + milliseconds(value);
+ date asDate = time.date();
+
+ css::util::Date loDate(asDate.day(), asDate.month(),
+ asDate.year()); // day, month, year
+ aData.push_back(Any(loDate));
+ }
+ break;
+ case DataType::TIME:
+ {
+ sal_Int64 value = 0;
+ m_pStream->ReadInt64(value);
+ auto valueInSecs = value / 1000;
+ /* Observed valueInSecs fall in the range from
+ negative one day to positive two days. Coerce
+ valueInSecs between zero and positive one day.*/
+ const int secPerDay = 24 * 60 * 60;
+ valueInSecs = (valueInSecs + secPerDay) % secPerDay;
+
+ auto nHours = valueInSecs / (60 * 60);
+ valueInSecs = valueInSecs % 3600;
+ const sal_uInt16 nMins = valueInSecs / 60;
+ const sal_uInt16 nSecs = valueInSecs % 60;
+ css::util::Time time((value % 1000) * 1000000, nSecs, nMins, nHours, true);
+ aData.push_back(Any(time));
+ }
+ break;
+ case DataType::TIMESTAMP:
+ {
+ sal_Int64 nEpochMillis = 0;
+ m_pStream->ReadInt64(nEpochMillis);
+ ptime epoch = time_from_string("1970-01-01 00:00:00.000");
+ ptime time = epoch + milliseconds(nEpochMillis);
+ date asDate = time.date();
+
+ sal_Int32 nNanos = 0;
+ m_pStream->ReadInt32(nNanos);
+
+ // convert into LO internal representation of dateTime
+ css::util::DateTime dateTime;
+ dateTime.NanoSeconds = nNanos;
+ dateTime.Seconds = time.time_of_day().seconds();
+ dateTime.Minutes = time.time_of_day().minutes();
+ dateTime.Hours = time.time_of_day().hours();
+ dateTime.Day = asDate.day();
+ dateTime.Month = asDate.month();
+ dateTime.Year = asDate.year();
+ aData.push_back(Any(dateTime));
+ }
+ break;
+ case DataType::BOOLEAN:
+ {
+ sal_uInt8 nBool = 0;
+ m_pStream->ReadUChar(nBool);
+ aData.push_back(Any(static_cast<bool>(nBool)));
+ }
+ break;
+ case DataType::OTHER:
+ aData.push_back(Any{}); // TODO
+ break;
+ case DataType::BINARY:
+ case DataType::VARBINARY:
+ case DataType::LONGVARBINARY:
+ {
+ sal_Int32 nSize = 0;
+ m_pStream->ReadInt32(nSize);
+
+ Sequence<sal_Int8> aBytes(nSize);
+ m_pStream->ReadBytes(aBytes.getArray(), nSize);
+ aData.push_back(Any(aBytes));
+ }
+ break;
+
+ default:
+ throw WrongFormatException();
+ }
+ }
+ return aData;
+}
+
+} // namespace dbahsql
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/dbaccess/source/filter/hsqldb/rowinputbinary.hxx b/dbaccess/source/filter/hsqldb/rowinputbinary.hxx
new file mode 100644
index 000000000..f81fa446f
--- /dev/null
+++ b/dbaccess/source/filter/hsqldb/rowinputbinary.hxx
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <vector>
+#include <tools/stream.hxx>
+
+#include <com/sun/star/io/XInputStream.hpp>
+
+#include "columndef.hxx"
+
+namespace dbahsql
+{
+class HsqlRowInputStream
+{
+private:
+ std::unique_ptr<SvStream> m_pStream;
+
+protected:
+ OUString readString();
+ bool checkNull();
+
+ OUString readUTF(sal_Int32 nLen);
+
+public:
+ HsqlRowInputStream();
+
+ /**
+ * Reads one row from the actual position.
+ * @param colTypes Field types of the row, in a strict order.
+ */
+ std::vector<css::uno::Any> readOneRow(const std::vector<ColumnDefinition>& colTypes);
+
+ /**
+ * Sets the file-pointer offset, measured from the beginning of the file
+ */
+ void seek(sal_Int32 nPos);
+
+ void setInputStream(css::uno::Reference<css::io::XInputStream> const& rStream);
+ SvStream* getInputStream() const;
+};
+
+} // namespace dbahsql
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/dbaccess/source/filter/hsqldb/utils.cxx b/dbaccess/source/filter/hsqldb/utils.cxx
new file mode 100644
index 000000000..724ffccfb
--- /dev/null
+++ b/dbaccess/source/filter/hsqldb/utils.cxx
@@ -0,0 +1,141 @@
+
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <comphelper/string.hxx>
+#include <comphelper/processfactory.hxx>
+#include <connectivity/dbexception.hxx>
+
+#include "utils.hxx"
+
+using namespace dbahsql;
+
+namespace
+{
+int getHexValue(sal_Unicode c)
+{
+ if (c >= '0' && c <= '9')
+ {
+ return c - '0';
+ }
+ else if (c >= 'A' && c <= 'F')
+ {
+ return c - 'A' + 10;
+ }
+ else if (c >= 'a' && c <= 'f')
+ {
+ return c - 'a' + 10;
+ }
+ else
+ {
+ return -1;
+ }
+}
+
+} // unnamed namespace
+
+//Convert ascii escaped unicode to utf-8
+OUString utils::convertToUTF8(std::string_view original)
+{
+ OUString res = OStringToOUString(original, RTL_TEXTENCODING_UTF8);
+ for (sal_Int32 i = 0;;)
+ {
+ i = res.indexOf("\\u", i);
+ if (i == -1)
+ {
+ break;
+ }
+ i += 2;
+ if (res.getLength() - i >= 4)
+ {
+ bool escape = true;
+ sal_Unicode c = 0;
+ for (sal_Int32 j = 0; j != 4; ++j)
+ {
+ auto const n = getHexValue(res[i + j]);
+ if (n == -1)
+ {
+ escape = false;
+ break;
+ }
+ c = (c << 4) | n;
+ }
+ if (escape)
+ {
+ i -= 2;
+ res = res.replaceAt(i, 6, rtl::OUStringChar(c));
+ ++i;
+ }
+ }
+ }
+ return res;
+}
+
+OUString utils::getTableNameFromStmt(std::u16string_view sSql)
+{
+ auto stmtComponents = comphelper::string::split(sSql, sal_Unicode(u' '));
+ assert(stmtComponents.size() > 2);
+ auto wordIter = stmtComponents.begin();
+
+ if (*wordIter == "CREATE" || *wordIter == "ALTER")
+ ++wordIter;
+ if (*wordIter == "CACHED")
+ ++wordIter;
+ if (*wordIter == "TABLE")
+ ++wordIter;
+
+ // it may contain spaces if it's put into apostrophes.
+ if (wordIter->indexOf("\"") >= 0)
+ {
+ size_t nAposBegin = sSql.find('"');
+ size_t nAposEnd = nAposBegin;
+ bool bProperEndAposFound = false;
+ while (!bProperEndAposFound)
+ {
+ nAposEnd = sSql.find('"', nAposEnd + 1);
+ if (sSql[nAposEnd - 1] != u'\\')
+ bProperEndAposFound = true;
+ }
+ std::u16string_view result = sSql.substr(nAposBegin, nAposEnd - nAposBegin + 1);
+ return OUString(result);
+ }
+
+ // next word is the table's name
+ // it might stuck together with the column definitions.
+ sal_Int32 nParenPos = wordIter->indexOf("(");
+ if (nParenPos > 0)
+ return wordIter->copy(0, nParenPos);
+ else
+ return *wordIter;
+}
+
+void utils::ensureFirebirdTableLength(const OUString& sName)
+{
+ if (sName.getLength() > 30) // Firebird limitation
+ {
+ static constexpr OUStringLiteral NAME_TOO_LONG
+ = u"Firebird 3 doesn't support object (table, field) names "
+ "of more than 30 characters; please shorten your object "
+ "names in the original file and try again.";
+ dbtools::throwGenericSQLException(NAME_TOO_LONG,
+ ::comphelper::getProcessComponentContext());
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/dbaccess/source/filter/hsqldb/utils.hxx b/dbaccess/source/filter/hsqldb/utils.hxx
new file mode 100644
index 000000000..cc3e2f0df
--- /dev/null
+++ b/dbaccess/source/filter/hsqldb/utils.hxx
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <rtl/ustring.hxx>
+
+namespace dbahsql::utils
+{
+OUString convertToUTF8(std::string_view original);
+
+OUString getTableNameFromStmt(std::u16string_view sSql);
+
+void ensureFirebirdTableLength(const OUString& sName);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */