1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <sal/config.h>
21 
22 #include <string_view>
23 
24 #include "parseschema.hxx"
25 #include "fbcreateparser.hxx"
26 #include "fbalterparser.hxx"
27 #include "utils.hxx"
28 
29 #include <com/sun/star/io/TextInputStream.hpp>
30 #include <com/sun/star/embed/XStorage.hpp>
31 #include <com/sun/star/embed/ElementModes.hpp>
32 #include <comphelper/processfactory.hxx>
33 #include <comphelper/string.hxx>
34 #include <sal/log.hxx>
35 #include <connectivity/dbexception.hxx>
36 
37 namespace
38 {
39 using namespace ::comphelper;
40 
41 using IndexVector = std::vector<sal_Int32>;
42 
43 class IndexStmtParser
44 {
45 private:
46     OUString m_sql;
47 
48 public:
IndexStmtParser(const OUString & sSql)49     IndexStmtParser(const OUString& sSql)
50         : m_sql(sSql)
51     {
52     }
53 
isIndexStatement() const54     bool isIndexStatement() const
55     {
56         return m_sql.startsWith("SET TABLE") && m_sql.indexOf("INDEX") >= 0;
57     }
58 
getIndexes() const59     IndexVector getIndexes() const
60     {
61         assert(isIndexStatement());
62 
63         OUString sIndexPart = m_sql.copy(m_sql.indexOf("INDEX") + 5);
64         sal_Int32 nQuotePos = sIndexPart.indexOf("'") + 1;
65         OUString sIndexNums = sIndexPart.copy(nQuotePos, sIndexPart.lastIndexOf("'") - nQuotePos);
66 
67         std::vector<OUString> sIndexes = string::split(sIndexNums, u' ');
68         IndexVector indexes;
69         for (const auto& sIndex : sIndexes)
70             indexes.push_back(sIndex.toInt32());
71 
72         // ignore last element
73         // TODO this is an identity peek, which indicates the value of the next
74         // identity. At the current state all migrated identities start with 0.
75         indexes.pop_back();
76 
77         return indexes;
78     }
79 
getTableName() const80     OUString getTableName() const
81     {
82         // SET TABLE <tableName> or SET TABLE "<multi word table name>"
83         OUString sName = string::split(m_sql, u' ')[2];
84         if (sName.indexOf('"') >= 0)
85         {
86             // Table name with string delimiter
87             sName = "\"" + string::split(m_sql, u'"')[1] + "\"";
88         }
89         return sName;
90     }
91 };
92 
lcl_createAlterForeign(std::u16string_view sForeignPart,std::u16string_view sTableName)93 OUString lcl_createAlterForeign(std::u16string_view sForeignPart, std::u16string_view sTableName)
94 {
95     return OUString::Concat("ALTER TABLE ") + sTableName + " ADD " + sForeignPart;
96 }
97 
98 } // anonymous namespace
99 
100 namespace dbahsql
101 {
102 using namespace css::io;
103 using namespace css::uno;
104 using namespace css::embed;
105 
SchemaParser(Reference<XStorage> & rStorage)106 SchemaParser::SchemaParser(Reference<XStorage>& rStorage)
107     : m_rStorage(rStorage)
108 {
109 }
110 
parseSchema()111 void SchemaParser::parseSchema()
112 {
113     assert(m_rStorage);
114 
115     static constexpr OUStringLiteral SCHEMA_FILENAME = u"script";
116     if (!m_rStorage->hasByName(SCHEMA_FILENAME))
117     {
118         SAL_WARN("dbaccess", "script file does not exist in storage during hsqldb import");
119         return;
120     }
121 
122     Reference<XStream> xStream(m_rStorage->openStreamElement(SCHEMA_FILENAME, ElementModes::READ));
123 
124     Reference<XComponentContext> rContext = comphelper::getProcessComponentContext();
125     Reference<XTextInputStream2> xTextInput = TextInputStream::create(rContext);
126     xTextInput->setEncoding("UTF-8");
127     xTextInput->setInputStream(xStream->getInputStream());
128 
129     while (!xTextInput->isEOF())
130     {
131         // every line contains exactly one DDL statement
132         OUString sSql = utils::convertToUTF8(
133             OUStringToOString(xTextInput->readLine(), RTL_TEXTENCODING_UTF8));
134 
135         IndexStmtParser indexParser{ sSql };
136         if (indexParser.isIndexStatement())
137         {
138             m_Indexes[indexParser.getTableName()] = indexParser.getIndexes();
139         }
140         else if (sSql.startsWith("SET") || sSql.startsWith("CREATE USER")
141                  || sSql.startsWith("CREATE SCHEMA") || sSql.startsWith("GRANT"))
142             continue;
143         else if (sSql.startsWith("CREATE CACHED TABLE") || sSql.startsWith("CREATE TABLE"))
144         {
145             FbCreateStmtParser aCreateParser;
146             aCreateParser.parse(sSql);
147 
148             for (const auto& foreignParts : aCreateParser.getForeignParts())
149             {
150                 m_sAlterStatements.push_back(
151                     lcl_createAlterForeign(foreignParts, aCreateParser.getTableName()));
152             }
153 
154             sSql = aCreateParser.compose();
155 
156             // save column definitions
157             m_ColumnTypes[aCreateParser.getTableName()] = aCreateParser.getColumnDef();
158 
159             m_sCreateStatements.push_back(sSql);
160         }
161         else if (sSql.startsWith("ALTER"))
162         {
163             FbAlterStmtParser aAlterParser;
164             aAlterParser.parse(sSql);
165             OUString parsedStmt = aAlterParser.compose();
166 
167             if (!parsedStmt.isEmpty())
168                 m_sAlterStatements.push_back(parsedStmt);
169         }
170         else if (sSql.startsWith("CREATE VIEW"))
171             m_sCreateStatements.push_back(sSql);
172     }
173 }
174 
getTableColumnTypes(const OUString & sTableName) const175 std::vector<ColumnDefinition> SchemaParser::getTableColumnTypes(const OUString& sTableName) const
176 {
177     if (m_ColumnTypes.count(sTableName) < 1)
178     {
179         static constexpr OUStringLiteral NOT_EXIST
180             = u"Internal error while getting column information of table";
181         SAL_WARN("dbaccess", NOT_EXIST << ". Table name is: " << sTableName);
182         dbtools::throwGenericSQLException(NOT_EXIST, ::comphelper::getProcessComponentContext());
183     }
184     return m_ColumnTypes.at(sTableName);
185 }
186 
getTableIndexes() const187 const std::map<OUString, std::vector<sal_Int32>>& SchemaParser::getTableIndexes() const
188 {
189     return m_Indexes;
190 }
191 
getPrimaryKeys() const192 const std::map<OUString, std::vector<OUString>>& SchemaParser::getPrimaryKeys() const
193 {
194     return m_PrimaryKeys;
195 }
196 
197 } // namespace dbahsql
198 
199 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
200