summaryrefslogtreecommitdiffstats
path: root/dbaccess/source/filter/hsqldb/createparser.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'dbaccess/source/filter/hsqldb/createparser.cxx')
-rw-r--r--dbaccess/source/filter/hsqldb/createparser.cxx298
1 files changed, 298 insertions, 0 deletions
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: */