diff options
Diffstat (limited to '')
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: */ |