1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <comphelper/string.hxx>
21 #include <sal/log.hxx>
22 #include "createparser.hxx"
24 #include <com/sun/star/sdbc/DataType.hpp>
26 using namespace ::comphelper
;
27 using namespace css::sdbc
;
31 /// Returns substring of sSql from the first occurrence of '(' until the
32 /// last occurrence of ')' (excluding the parenthesis)
33 OUString
lcl_getColumnPart(const OUString
& sSql
)
35 sal_Int32 nBeginIndex
= sSql
.indexOf("(") + 1;
38 SAL_WARN("dbaccess", "No column definitions found");
41 sal_Int32 nCount
= sSql
.lastIndexOf(")") - nBeginIndex
;
42 return sSql
.copy(nBeginIndex
, nCount
);
45 /// Constructs a vector of strings that represents the definitions of each
46 /// column or constraint.
48 /// @param sColumnPart part of the create statement inside the parenthesis
49 /// containing the column definitions
50 std::vector
<OUString
> lcl_splitColumnPart(const OUString
& sColumnPart
)
52 std::vector
<OUString
> sParts
= string::split(sColumnPart
, sal_Unicode(u
','));
53 std::vector
<OUString
> sReturn
;
55 OUStringBuffer
current(128);
56 for (auto const& part
: sParts
)
59 if (current
.lastIndexOf("(") > current
.lastIndexOf(")"))
60 current
.append(","); // it was false split
63 sReturn
.push_back(current
.toString());
70 sal_Int32
lcl_getAutoIncrementDefault(const OUString
& sColumnDef
)
72 // TODO what if there are more spaces?
73 if (sColumnDef
.indexOf("GENERATED BY DEFAULT AS IDENTITY") > 0)
75 // TODO parse starting sequence stated by "START WITH"
81 OUString
lcl_getDefaultValue(const OUString
& sColumnDef
)
83 constexpr char DEFAULT_KW
[] = "DEFAULT";
84 auto nDefPos
= sColumnDef
.indexOf(DEFAULT_KW
);
85 if (nDefPos
> 0 && lcl_getAutoIncrementDefault(sColumnDef
) < 0)
87 const OUString
& fromDefault
= sColumnDef
.copy(nDefPos
+ sizeof(DEFAULT_KW
)).trim();
89 // next word is the value
90 auto nNextSpace
= fromDefault
.indexOf(" ");
91 return nNextSpace
> 0 ? fromDefault
.copy(0, fromDefault
.indexOf(" ")) : fromDefault
;
96 bool lcl_isNullable(const OUString
& sColumnDef
) { return sColumnDef
.indexOf("NOT NULL") < 0; }
98 bool lcl_isPrimaryKey(const OUString
& sColumnDef
) { return sColumnDef
.indexOf("PRIMARY KEY") >= 0; }
100 sal_Int32
lcl_getDataTypeFromHsql(const OUString
& sTypeName
)
102 if (sTypeName
== "CHAR")
103 return DataType::CHAR
;
104 else if (sTypeName
== "VARCHAR" || sTypeName
== "VARCHAR_IGNORECASE")
105 return DataType::VARCHAR
;
106 else if (sTypeName
== "TINYINT")
107 return DataType::TINYINT
;
108 else if (sTypeName
== "SMALLINT")
109 return DataType::SMALLINT
;
110 else if (sTypeName
== "INTEGER")
111 return DataType::INTEGER
;
112 else if (sTypeName
== "BIGINT")
113 return DataType::BIGINT
;
114 else if (sTypeName
== "NUMERIC")
115 return DataType::NUMERIC
;
116 else if (sTypeName
== "DECIMAL")
117 return DataType::DECIMAL
;
118 else if (sTypeName
== "BOOLEAN")
119 return DataType::BOOLEAN
;
120 else if (sTypeName
== "LONGVARCHAR")
121 return DataType::LONGVARCHAR
;
122 else if (sTypeName
== "LONGVARBINARY")
123 return DataType::LONGVARBINARY
;
124 else if (sTypeName
== "CLOB")
125 return DataType::CLOB
;
126 else if (sTypeName
== "BLOB")
127 return DataType::BLOB
;
128 else if (sTypeName
== "BINARY")
129 return DataType::BINARY
;
130 else if (sTypeName
== "VARBINARY")
131 return DataType::VARBINARY
;
132 else if (sTypeName
== "DATE")
133 return DataType::DATE
;
134 else if (sTypeName
== "TIME")
135 return DataType::TIME
;
136 else if (sTypeName
== "TIMESTAMP")
137 return DataType::TIMESTAMP
;
138 else if (sTypeName
== "DOUBLE")
139 return DataType::DOUBLE
;
140 else if (sTypeName
== "REAL")
141 return DataType::REAL
;
142 else if (sTypeName
== "FLOAT")
143 return DataType::FLOAT
;
149 void lcl_addDefaultParameters(std::vector
<sal_Int32
>& aParams
, sal_Int32 eType
)
151 if (eType
== DataType::CHAR
|| eType
== DataType::BINARY
|| eType
== DataType::VARBINARY
152 || eType
== DataType::VARCHAR
)
153 aParams
.push_back(8000); // from SQL standard
156 struct ColumnTypeParts
159 std::vector
<sal_Int32
> params
;
163 * Separates full type descriptions (e.g. NUMERIC(5,4)) to type name (NUMERIC) and
166 ColumnTypeParts
lcl_getColumnTypeParts(const OUString
& sFullTypeName
)
168 ColumnTypeParts parts
;
169 auto nParenPos
= sFullTypeName
.indexOf("(");
172 parts
.typeName
= sFullTypeName
.copy(0, nParenPos
).trim();
174 = sFullTypeName
.copy(nParenPos
+ 1, sFullTypeName
.indexOf(")") - nParenPos
- 1);
175 auto sParams
= string::split(sParamStr
, sal_Unicode(u
','));
176 for (const auto& sParam
: sParams
)
178 parts
.params
.push_back(sParam
.toInt32());
183 parts
.typeName
= sFullTypeName
.trim();
184 lcl_addDefaultParameters(parts
.params
, lcl_getDataTypeFromHsql(parts
.typeName
));
189 } // unnamed namespace
193 CreateStmtParser::CreateStmtParser() {}
195 void CreateStmtParser::parsePrimaryKeys(const OUString
& sPrimaryPart
)
197 sal_Int32 nParenPos
= sPrimaryPart
.indexOf("(");
201 = sPrimaryPart
.copy(nParenPos
+ 1, sPrimaryPart
.lastIndexOf(")") - nParenPos
- 1);
202 auto sParams
= string::split(sParamStr
, sal_Unicode(u
','));
203 for (const auto& sParam
: sParams
)
205 m_PrimaryKeys
.push_back(sParam
);
210 void CreateStmtParser::parseColumnPart(const OUString
& sColumnPart
)
212 auto sColumns
= lcl_splitColumnPart(sColumnPart
);
213 for (const OUString
& sColumn
: sColumns
)
215 if (sColumn
.startsWithIgnoreAsciiCase("PRIMARY KEY"))
217 parsePrimaryKeys(sColumn
);
221 if (sColumn
.startsWithIgnoreAsciiCase("CONSTRAINT"))
223 m_aForeignParts
.push_back(sColumn
);
227 bool bIsQuoteUsedForColumnName(sColumn
[0] == '\"');
229 // find next quote after the initial quote
230 // or next space if quote isn't used as delimiter
232 = bIsQuoteUsedForColumnName
? sColumn
.indexOf("\"", 1) + 1 : sColumn
.indexOf(" ");
233 OUString rColumnName
= sColumn
.copy(0, nEndColumnName
);
235 const OUString
& sFromTypeName
= sColumn
.copy(nEndColumnName
).trim();
237 // Now let's manage the column type
238 // search next space to get the whole type name
239 // eg: INTEGER, VARCHAR(10), DECIMAL(6,3)
240 auto nNextSpace
= sFromTypeName
.indexOf(" ");
241 OUString sFullTypeName
;
243 sFullTypeName
= sFromTypeName
.copy(0, nNextSpace
);
244 // perhaps column type corresponds to the last info here
246 sFullTypeName
= sFromTypeName
;
248 ColumnTypeParts typeParts
= lcl_getColumnTypeParts(sFullTypeName
);
250 bool bCaseInsensitive
= typeParts
.typeName
.indexOf("IGNORECASE") >= 0;
251 bool isPrimaryKey
= lcl_isPrimaryKey(sColumn
);
254 m_PrimaryKeys
.push_back(rColumnName
);
256 const OUString sColumnWithoutName
= sColumn
.copy(sColumn
.indexOf(typeParts
.typeName
));
258 ColumnDefinition
aColDef(rColumnName
, lcl_getDataTypeFromHsql(typeParts
.typeName
),
259 typeParts
.params
, isPrimaryKey
,
260 lcl_getAutoIncrementDefault(sColumnWithoutName
),
261 lcl_isNullable(sColumnWithoutName
), bCaseInsensitive
,
262 lcl_getDefaultValue(sColumnWithoutName
));
264 m_aColumns
.push_back(aColDef
);
268 void CreateStmtParser::parse(const OUString
& sSql
)
271 if (!sSql
.startsWith("CREATE"))
273 SAL_WARN("dbaccess", "Not a create statement");
277 m_sTableName
= utils::getTableNameFromStmt(sSql
);
278 OUString sColumnPart
= lcl_getColumnPart(sSql
);
279 parseColumnPart(sColumnPart
);
282 } // namespace dbahsql
284 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */