mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-22 20:03:02 +01:00
Merge branch 'master' into ods14-header-page
This commit is contained in:
commit
52e7efa305
@ -43,7 +43,7 @@ $ANDROID_HOME/platform-tools/adb -s $AndroidDeviceName shell "rm -rf $AndroidDir
|
||||
$ANDROID_HOME/platform-tools/adb -s $AndroidDeviceName shell "mkdir $AndroidDir"
|
||||
$ANDROID_HOME/platform-tools/adb -s $AndroidDeviceName push gen/$InitialDebugTar $AndroidDir/
|
||||
$ANDROID_HOME/platform-tools/adb -s $AndroidDeviceName shell "(cd $AndroidDir && tar xvf $InitialDebugTar)"
|
||||
$ANDROID_HOME/platform-tools/adb -s $AndroidDeviceName shell "(cd $AndroidDir/firebird && ./common_test --log_level=all && ./libEngine14_test --log_level=all)"
|
||||
$ANDROID_HOME/platform-tools/adb -s $AndroidDeviceName shell "(cd $AndroidDir/firebird && ./common_test --log_level=error && ./libEngine14_test --log_level=error && ./isql_test --log_level=error)"
|
||||
$ANDROID_HOME/platform-tools/adb -s $AndroidDeviceName shell "(cd $AndroidDir/firebird && ./AfterUntar.sh)"
|
||||
$ANDROID_HOME/platform-tools/adb -s $AndroidDeviceName pull $AndroidDir/firebird/firebird.msg gen/Release/firebird/
|
||||
$ANDROID_HOME/platform-tools/adb -s $AndroidDeviceName pull $AndroidDir/firebird/security6.fdb gen/Release/firebird/
|
||||
|
@ -1,6 +1,6 @@
|
||||
#########################################
|
||||
#
|
||||
# Firebird version 5.0 configuration file
|
||||
# Firebird version 6.0 configuration file
|
||||
#
|
||||
#########################################
|
||||
|
||||
|
@ -799,7 +799,7 @@ install install-embedded silent_install package packages dist:
|
||||
|
||||
.PHONY: tests tests_process run_tests run_tests_process
|
||||
|
||||
log_level ?= all
|
||||
log_level ?= error
|
||||
|
||||
tests:
|
||||
$(MAKE) TARGET?=$(DefaultTarget) tests_process
|
||||
|
@ -111,7 +111,7 @@ GLOB_OPTIONS:=
|
||||
#____________________________________________________________________________
|
||||
|
||||
# Global c++ flags: firebird needs no RTTI, choose build standard and c++ specific warnings level
|
||||
PLUSPLUS_FLAGS:= -fno-rtti -std=c++17 -Werror=delete-incomplete
|
||||
PLUSPLUS_FLAGS:= -fno-rtti -std=c++17 -Werror=delete-incomplete -Werror=return-type
|
||||
|
||||
# If this is defined then we use special rules useful for developers only
|
||||
IsDeveloper = @DEVEL_FLG@
|
||||
|
@ -28,6 +28,7 @@
|
||||
<UseFullPaths>false</UseFullPaths>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<TreatSpecificWarningsAsErrors>4715</TreatSpecificWarningsAsErrors>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
|
@ -24,6 +24,7 @@
|
||||
<ClCompile Include="..\..\..\gen\isql\extract.cpp" />
|
||||
<ClCompile Include="..\..\..\src\common\fb_exception.cpp" />
|
||||
<ClCompile Include="..\..\..\src\isql\FrontendLexer.cpp" />
|
||||
<ClCompile Include="..\..\..\src\isql\FrontendParser.cpp" />
|
||||
<ClCompile Include="..\..\..\src\isql\InputDevices.cpp" />
|
||||
<ClCompile Include="..\..\..\gen\isql\isql.cpp" />
|
||||
<ClCompile Include="..\..\..\src\isql\iutils.cpp" />
|
||||
@ -40,6 +41,7 @@
|
||||
<ClInclude Include="..\..\..\src\isql\Extender.h" />
|
||||
<ClInclude Include="..\..\..\src\isql\extra_proto.h" />
|
||||
<ClInclude Include="..\..\..\src\isql\FrontendLexer.h" />
|
||||
<ClInclude Include="..\..\..\src\isql\FrontendParser.h" />
|
||||
<ClInclude Include="..\..\..\src\isql\InputDevices.h" />
|
||||
<ClInclude Include="..\..\..\src\isql\isql.h" />
|
||||
<ClInclude Include="..\..\..\src\isql\isql_proto.h" />
|
||||
|
@ -30,6 +30,9 @@
|
||||
<ClCompile Include="..\..\..\src\isql\FrontendLexer.cpp">
|
||||
<Filter>ISQL files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\src\isql\FrontendParser.cpp">
|
||||
<Filter>ISQL files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\src\isql\InputDevices.cpp">
|
||||
<Filter>ISQL files</Filter>
|
||||
</ClCompile>
|
||||
@ -73,6 +76,9 @@
|
||||
<ClInclude Include="..\..\..\src\isql\FrontendLexer.h">
|
||||
<Filter>Header files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\src\isql\FrontendParser.h">
|
||||
<Filter>Header files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\src\isql\InputDevices.h">
|
||||
<Filter>Header files</Filter>
|
||||
</ClInclude>
|
||||
|
@ -177,6 +177,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\..\src\isql\tests\FrontendLexerTest.cpp" />
|
||||
<ClCompile Include="..\..\..\src\isql\tests\FrontendParserTest.cpp" />
|
||||
<ClCompile Include="..\..\..\src\isql\tests\ISqlTest.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@ -10,6 +10,9 @@
|
||||
<ClCompile Include="..\..\..\src\isql\tests\FrontendLexerTest.cpp">
|
||||
<Filter>source</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\src\isql\tests\FrontendParserTest.cpp">
|
||||
<Filter>source</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\src\isql\tests\ISqlTest.cpp">
|
||||
<Filter>source</Filter>
|
||||
</ClCompile>
|
||||
|
@ -5,8 +5,8 @@
|
||||
@call setenvvar.bat %*
|
||||
@if errorlevel 1 (goto :END)
|
||||
|
||||
@%FB_BIN_DIR%\common_test --log_level=all || exit /b
|
||||
@%FB_BIN_DIR%\engine_test --log_level=all || exit /b
|
||||
@%FB_BIN_DIR%\isql_test --log_level=all || exit /b
|
||||
@%FB_BIN_DIR%\common_test --log_level=error || exit /b
|
||||
@%FB_BIN_DIR%\engine_test --log_level=error || exit /b
|
||||
@%FB_BIN_DIR%\isql_test --log_level=error || exit /b
|
||||
|
||||
:END
|
||||
|
@ -132,6 +132,11 @@
|
||||
# define CDS_PROCESSOR__NAME "LOONGARCH"
|
||||
# define CDS_PROCESSOR__NICK "loongarch"
|
||||
# define CDS_BUILD_BITS 64
|
||||
#elif defined(__riscv) && __riscv_xlen == 64
|
||||
# define CDS_PROCESSOR_ARCH CDS_PROCESSOR_RISCV64
|
||||
# define CDS_PROCESSOR__NAME "RISC-V64"
|
||||
# define CDS_PROCESSOR__NICK "riscv64"
|
||||
# define CDS_BUILD_BITS 64
|
||||
#else
|
||||
# if defined(CDS_USE_LIBCDS_ATOMIC)
|
||||
# error "Libcds does not support atomic implementation for the processor architecture. Try to use C++11-compatible compiler and remove CDS_USE_LIBCDS_ATOMIC flag from compiler command line"
|
||||
|
@ -4204,53 +4204,85 @@ bool get_files(BurpGlobals* tdgbl)
|
||||
att_type attribute;
|
||||
scan_attr_t scan_next_attr;
|
||||
|
||||
STORE (REQUEST_HANDLE tdgbl->handles_get_files_req_handle1)
|
||||
X IN RDB$FILES
|
||||
X.RDB$FILE_FLAGS = 0;
|
||||
BASED_ON RDB$FILES.RDB$FILE_NAME filename = "";
|
||||
SSHORT flags = 0, sequence = 0, number = 0;
|
||||
SLONG start = 0, length = 0;
|
||||
|
||||
skip_init(&scan_next_attr);
|
||||
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
|
||||
skip_init(&scan_next_attr);
|
||||
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
|
||||
{
|
||||
switch (attribute)
|
||||
{
|
||||
switch (attribute)
|
||||
{
|
||||
case att_file_filename:
|
||||
GET_TEXT(X.RDB$FILE_NAME);
|
||||
BURP_verbose (116, X.RDB$FILE_NAME);
|
||||
// msg 116 restoring file %s
|
||||
break;
|
||||
case att_file_filename:
|
||||
GET_TEXT(filename);
|
||||
break;
|
||||
|
||||
case att_file_sequence:
|
||||
X.RDB$FILE_SEQUENCE = (USHORT) get_int32(tdgbl);
|
||||
break;
|
||||
case att_file_sequence:
|
||||
sequence = (SSHORT) get_int32(tdgbl);
|
||||
break;
|
||||
|
||||
case att_file_start:
|
||||
X.RDB$FILE_START = get_int32(tdgbl);
|
||||
break;
|
||||
case att_file_start:
|
||||
start = get_int32(tdgbl);
|
||||
break;
|
||||
|
||||
case att_file_length:
|
||||
X.RDB$FILE_LENGTH = get_int32(tdgbl);
|
||||
break;
|
||||
case att_file_length:
|
||||
length = get_int32(tdgbl);
|
||||
break;
|
||||
|
||||
case att_file_flags:
|
||||
X.RDB$FILE_FLAGS |= get_int32(tdgbl);
|
||||
break;
|
||||
case att_file_flags:
|
||||
flags |= (SSHORT) get_int32(tdgbl);
|
||||
break;
|
||||
|
||||
case att_shadow_number:
|
||||
X.RDB$SHADOW_NUMBER = (USHORT) get_int32(tdgbl);
|
||||
if (tdgbl->gbl_sw_kill && X.RDB$SHADOW_NUMBER)
|
||||
X.RDB$FILE_FLAGS |= FILE_inactive;
|
||||
break;
|
||||
case att_shadow_number:
|
||||
number = (SSHORT) get_int32(tdgbl);
|
||||
if (tdgbl->gbl_sw_kill && number)
|
||||
flags |= FILE_inactive;
|
||||
break;
|
||||
|
||||
default:
|
||||
bad_attribute(scan_next_attr, attribute, 85);
|
||||
// msg 85 file
|
||||
break;
|
||||
}
|
||||
default:
|
||||
bad_attribute(scan_next_attr, attribute, 85);
|
||||
// msg 85 file
|
||||
break;
|
||||
}
|
||||
END_STORE;
|
||||
ON_ERROR
|
||||
general_on_error ();
|
||||
END_ERROR;
|
||||
}
|
||||
|
||||
const bool multiFileSupport = (tdgbl->runtimeODS <= DB_VERSION_DDL13_1);
|
||||
|
||||
if ((multiFileSupport || !sequence) && filename[0])
|
||||
{
|
||||
BURP_verbose (116, filename);
|
||||
// msg 116 restoring file %s
|
||||
|
||||
STORE (REQUEST_HANDLE tdgbl->handles_get_files_req_handle1)
|
||||
X IN RDB$FILES
|
||||
|
||||
strncpy(X.RDB$FILE_NAME, filename, sizeof(X.RDB$FILE_NAME));
|
||||
X.RDB$FILE_FLAGS = flags;
|
||||
X.RDB$SHADOW_NUMBER = number;
|
||||
|
||||
if (multiFileSupport)
|
||||
{
|
||||
X.RDB$FILE_SEQUENCE.NULL = FALSE;
|
||||
X.RDB$FILE_SEQUENCE = sequence;
|
||||
|
||||
X.RDB$FILE_START.NULL = FALSE;
|
||||
X.RDB$FILE_START = start;
|
||||
|
||||
X.RDB$FILE_LENGTH.NULL = FALSE;
|
||||
X.RDB$FILE_LENGTH = length;
|
||||
}
|
||||
else
|
||||
{
|
||||
X.RDB$FILE_SEQUENCE.NULL = TRUE;
|
||||
X.RDB$FILE_START.NULL = TRUE;
|
||||
X.RDB$FILE_LENGTH.NULL = TRUE;
|
||||
}
|
||||
|
||||
END_STORE;
|
||||
ON_ERROR
|
||||
general_on_error ();
|
||||
END_ERROR;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
bool contains(const char* value, USHORT& outTimezoneId, int& outParsedTimezoneLength)
|
||||
bool contains(const char* value, USHORT& outTimezoneId, unsigned int& outParsedTimezoneLength)
|
||||
{
|
||||
const TrieNode* currentNode = m_root;
|
||||
FB_SIZE_T valueLength = fb_strlen(value);
|
||||
@ -97,7 +97,7 @@ namespace
|
||||
TrieNode* currentNode = m_root;
|
||||
FB_SIZE_T valueLength = fb_strlen(value);
|
||||
|
||||
for (int i = 0; i < valueLength; i++)
|
||||
for (unsigned int i = 0; i < valueLength; i++)
|
||||
{
|
||||
int index = calculateIndex(value[i]);
|
||||
|
||||
@ -460,7 +460,7 @@ namespace
|
||||
patternStr = std::string_view(format + formatStart, offset - formatStart + 1);
|
||||
bool isFound = false;
|
||||
|
||||
for (int j = 0; j < PatternsSize; j++)
|
||||
for (unsigned int j = 0; j < PatternsSize; j++)
|
||||
{
|
||||
if (!strncmp(patterns[j], patternStr.data(), patternStr.length()))
|
||||
{
|
||||
@ -1124,6 +1124,7 @@ namespace
|
||||
return twelveHours == 12 ? twelveHours : 12 + twelveHours;
|
||||
|
||||
cb->err(Arg::Gds(isc_incorrect_hours_period) << string(period.data(), period.length()));
|
||||
return 0; // suppress compiler warning/error
|
||||
}
|
||||
|
||||
constexpr int roundYearPatternImplementation(int parsedRRValue, int currentYear)
|
||||
@ -1331,7 +1332,7 @@ namespace
|
||||
bool isFound = false;
|
||||
|
||||
std::string_view monthShortName = getSubstringFromString(str, strLength, strOffset, 3);
|
||||
for (int i = 0; i < FB_NELEM(FB_SHORT_MONTHS) - 1; i++)
|
||||
for (FB_SIZE_T i = 0; i < FB_NELEM(FB_SHORT_MONTHS) - 1; i++)
|
||||
{
|
||||
if (std::equal(monthShortName.begin(), monthShortName.end(),
|
||||
FB_SHORT_MONTHS[i], FB_SHORT_MONTHS[i] + strlen(FB_SHORT_MONTHS[i]),
|
||||
@ -1352,7 +1353,7 @@ namespace
|
||||
bool isFound = false;
|
||||
|
||||
std::string_view monthFullName = getSubstringFromString(str, strLength, strOffset);
|
||||
for (int i = 0; i < FB_NELEM(FB_LONG_MONTHS_UPPER) - 1; i++)
|
||||
for (FB_SIZE_T i = 0; i < FB_NELEM(FB_LONG_MONTHS_UPPER) - 1; i++)
|
||||
{
|
||||
if (std::equal(monthFullName.begin(), monthFullName.end(),
|
||||
FB_LONG_MONTHS_UPPER[i], FB_LONG_MONTHS_UPPER[i] + strlen(FB_LONG_MONTHS_UPPER[i]),
|
||||
@ -1541,7 +1542,7 @@ namespace
|
||||
}
|
||||
case Format::TZR:
|
||||
{
|
||||
int parsedTimezoneNameLength = 0;
|
||||
unsigned int parsedTimezoneNameLength = 0;
|
||||
const bool timezoneNameIsCorrect = timeZoneTrie().contains(str + strOffset, outTimezoneId, parsedTimezoneNameLength);
|
||||
if (!timezoneNameIsCorrect)
|
||||
status_exception::raise(Arg::Gds(isc_invalid_timezone_region) << string(str + strOffset, parsedTimezoneNameLength));
|
||||
@ -1668,7 +1669,7 @@ ISC_TIMESTAMP_TZ CVT_format_string_to_datetime(const dsc* desc, const Firebird::
|
||||
stringUpper[i] = toupper(sourceString[i]);
|
||||
|
||||
string formatUpper(format.length(), '\0');
|
||||
for (int i = 0; i < format.length(); i++)
|
||||
for (unsigned int i = 0; i < format.length(); i++)
|
||||
formatUpper[i] = toupper(format[i]);
|
||||
|
||||
StringToDateTimeData cvtData;
|
||||
|
@ -410,7 +410,7 @@ namespace
|
||||
if (!hasPatternChar() || getPatternChar() != ']')
|
||||
status_exception::raise(Arg::Gds(isc_invalid_similar_pattern));
|
||||
|
||||
for (item.clazz = 0; item.clazz < FB_NELEM(classes); ++item.clazz)
|
||||
for (item.clazz = 0; static_cast<FB_SIZE_T>(item.clazz) < FB_NELEM(classes); ++item.clazz)
|
||||
{
|
||||
if (fb_utils::strnicmp(patternStr + charSavePos,
|
||||
classes[item.clazz].similarClass, len) == 0)
|
||||
@ -419,7 +419,7 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
if (item.clazz >= FB_NELEM(classes))
|
||||
if (static_cast<FB_SIZE_T>(item.clazz) >= FB_NELEM(classes))
|
||||
status_exception::raise(Arg::Gds(isc_invalid_similar_pattern));
|
||||
}
|
||||
else
|
||||
|
42
src/common/StdHelper.h
Normal file
42
src/common/StdHelper.h
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* The contents of this file are subject to the Initial
|
||||
* Developer's Public License Version 1.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.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl.
|
||||
*
|
||||
* Software distributed under the License is distributed AS IS,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing rights
|
||||
* and limitations under the License.
|
||||
*
|
||||
* The Original Code was created by Adriano dos Santos Fernandes
|
||||
* for the Firebird Open Source RDBMS project.
|
||||
*
|
||||
* Copyright (c) 2024 Adriano dos Santos Fernandes <adrianosf at gmail.com>
|
||||
* and all contributors signed below.
|
||||
*
|
||||
* All Rights Reserved.
|
||||
* Contributor(s): ______________________________________.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef FB_COMMON_STD_HELPER_H
|
||||
#define FB_COMMON_STD_HELPER_H
|
||||
|
||||
namespace Firebird {
|
||||
|
||||
// To be used with std::visit
|
||||
|
||||
template <typename... Ts>
|
||||
struct StdVisitOverloads : Ts...
|
||||
{
|
||||
using Ts::operator()...;
|
||||
};
|
||||
|
||||
template <typename... Ts>
|
||||
StdVisitOverloads(Ts...) -> StdVisitOverloads<Ts...>;
|
||||
|
||||
} // namespace Firebird
|
||||
|
||||
#endif // FB_COMMON_STD_HELPER_H
|
@ -153,7 +153,7 @@ TextType::TextType(TTYPE_ID _type, texttype *_tt, USHORT _attributes, CharSet* _
|
||||
{'S', CHAR_UPPER_S}
|
||||
};
|
||||
|
||||
for (int i = 0; i < FB_NELEM(conversions); i++)
|
||||
for (FB_SIZE_T i = 0; i < FB_NELEM(conversions); i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -465,7 +465,7 @@ void callRemoteServiceManager(ISC_STATUS* status,
|
||||
{
|
||||
const char request[] = {isc_info_svc_get_users};
|
||||
int startQuery = 0;
|
||||
Auth::StackUserData uData;
|
||||
Auth::UserData uData;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
|
@ -93,6 +93,30 @@ public:
|
||||
int compare(const AbstractString& s) const { return compare(s.c_str(), s.length()); }
|
||||
int compare(const MetaString& m) const { return memcmp(data, m.data, MAX_SQL_IDENTIFIER_SIZE); }
|
||||
|
||||
string toQuotedString() const
|
||||
{
|
||||
string s;
|
||||
|
||||
if (hasData())
|
||||
{
|
||||
s.reserve(count + 2);
|
||||
|
||||
s.append("\"");
|
||||
|
||||
for (const auto c : *this)
|
||||
{
|
||||
if (c == '"')
|
||||
s.append("\"");
|
||||
|
||||
s.append(&c, 1);
|
||||
}
|
||||
|
||||
s.append("\"");
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
bool operator==(const char* s) const { return compare(s) == 0; }
|
||||
bool operator!=(const char* s) const { return compare(s) != 0; }
|
||||
bool operator==(const AbstractString& s) const { return compare(s) == 0; }
|
||||
|
@ -330,7 +330,7 @@ void NoThrowTimeStamp::round_time(ISC_TIME &ntime, const int precision)
|
||||
if (scale <= 0)
|
||||
return;
|
||||
|
||||
fb_assert(scale < FB_NELEM(POW_10_TABLE));
|
||||
fb_assert(static_cast<FB_SIZE_T>(scale) < FB_NELEM(POW_10_TABLE));
|
||||
|
||||
const ISC_TIME period = POW_10_TABLE[scale];
|
||||
|
||||
|
@ -14,7 +14,7 @@ BOOST_AUTO_TEST_CASE(ConstructionWithStdInitializerTest)
|
||||
{
|
||||
Array<int> array(*getDefaultMemoryPool(), {1, 2, 3, 4});
|
||||
|
||||
BOOST_TEST(array.getCount() == 4);
|
||||
BOOST_TEST(array.getCount() == 4u);
|
||||
BOOST_TEST(array[0] == 1);
|
||||
BOOST_TEST(array[3] == 4);
|
||||
}
|
||||
@ -23,22 +23,22 @@ BOOST_AUTO_TEST_CASE(ClearTest)
|
||||
{
|
||||
Array<int> array(*getDefaultMemoryPool(), {1, 2, 3, 4});
|
||||
|
||||
BOOST_TEST(array.getCount() == 4);
|
||||
BOOST_TEST(array.getCount() == 4u);
|
||||
|
||||
array.clear();
|
||||
BOOST_TEST(array.getCount() == 0);
|
||||
BOOST_TEST(array.getCount() == 0u);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(IsEmptyAndHasDataTest)
|
||||
{
|
||||
Array<int> array(*getDefaultMemoryPool(), {1, 2, 3, 4});
|
||||
|
||||
BOOST_TEST(array.getCount() > 0);
|
||||
BOOST_TEST(array.getCount() > 0u);
|
||||
BOOST_TEST(!array.isEmpty());
|
||||
BOOST_TEST(array.hasData());
|
||||
|
||||
array.clear();
|
||||
BOOST_TEST(array.getCount() == 0);
|
||||
BOOST_TEST(array.getCount() == 0u);
|
||||
BOOST_TEST(array.isEmpty());
|
||||
BOOST_TEST(!array.hasData());
|
||||
}
|
||||
@ -46,12 +46,12 @@ BOOST_AUTO_TEST_CASE(IsEmptyAndHasDataTest)
|
||||
BOOST_AUTO_TEST_CASE(CapacityAndCountTest)
|
||||
{
|
||||
Array<int> array1(10);
|
||||
BOOST_TEST(array1.getCapacity() == 10);
|
||||
BOOST_TEST(array1.getCount() == 0);
|
||||
BOOST_TEST(array1.getCapacity() == 10u);
|
||||
BOOST_TEST(array1.getCount() == 0u);
|
||||
|
||||
Array<int> array2(*getDefaultMemoryPool(), 11);
|
||||
BOOST_TEST(array2.getCapacity() == 11);
|
||||
BOOST_TEST(array2.getCount() == 0);
|
||||
BOOST_TEST(array2.getCapacity() == 11u);
|
||||
BOOST_TEST(array2.getCount() == 0u);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END() // ArrayTests
|
||||
|
@ -17,7 +17,7 @@ BOOST_AUTO_TEST_CASE(ConstructionWithStdInitializerTest)
|
||||
{
|
||||
DoublyLinkedList<int> list(*getDefaultMemoryPool(), {1, 2, 3, 4});
|
||||
|
||||
BOOST_TEST(list.getCount() == 4);
|
||||
BOOST_TEST(list.getCount() == 4u);
|
||||
BOOST_TEST(list.front() == 1);
|
||||
BOOST_TEST(list.back() == 4);
|
||||
}
|
||||
@ -26,10 +26,10 @@ BOOST_AUTO_TEST_CASE(ClearTest)
|
||||
{
|
||||
DoublyLinkedList<int> list(*getDefaultMemoryPool(), {1, 2, 3, 4});
|
||||
|
||||
BOOST_TEST(list.getCount() == 4);
|
||||
BOOST_TEST(list.getCount() == 4u);
|
||||
|
||||
list.clear();
|
||||
BOOST_TEST(list.getCount() == 0);
|
||||
BOOST_TEST(list.getCount() == 0u);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(SpliceTest)
|
||||
|
@ -1356,7 +1356,7 @@ static bool validate_dsc_tables();
|
||||
|
||||
|
||||
|
||||
int dsc::getStringLength() const
|
||||
USHORT dsc::getStringLength() const
|
||||
{
|
||||
return DSC_string_length(this);
|
||||
}
|
||||
@ -1533,7 +1533,7 @@ bool DSC_make_descriptor(DSC* desc,
|
||||
}
|
||||
|
||||
|
||||
int DSC_string_length(const dsc* desc)
|
||||
USHORT DSC_string_length(const dsc* desc)
|
||||
{
|
||||
/**************************************
|
||||
*
|
||||
@ -1561,10 +1561,10 @@ int DSC_string_length(const dsc* desc)
|
||||
return desc->dsc_length - sizeof(USHORT);
|
||||
default:
|
||||
if (!DTYPE_IS_EXACT(desc->dsc_dtype) || desc->dsc_scale == 0)
|
||||
return (int) _DSC_convert_to_text_length[desc->dsc_dtype];
|
||||
return _DSC_convert_to_text_length[desc->dsc_dtype];
|
||||
if (desc->dsc_scale < 0)
|
||||
return (int) _DSC_convert_to_text_length[desc->dsc_dtype] + 1;
|
||||
return (int) _DSC_convert_to_text_length[desc->dsc_dtype] + desc->dsc_scale;
|
||||
return _DSC_convert_to_text_length[desc->dsc_dtype] + 1;
|
||||
return _DSC_convert_to_text_length[desc->dsc_dtype] + desc->dsc_scale;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -499,7 +499,7 @@ typedef struct dsc
|
||||
dsc_address = address;
|
||||
}
|
||||
|
||||
int getStringLength() const;
|
||||
USHORT getStringLength() const;
|
||||
|
||||
operator Ods::Descriptor() const
|
||||
{
|
||||
|
@ -26,7 +26,7 @@
|
||||
|
||||
#include "../common/dsc.h"
|
||||
|
||||
int DSC_string_length(const struct dsc*);
|
||||
USHORT DSC_string_length(const struct dsc*);
|
||||
const TEXT* DSC_dtype_tostring(UCHAR);
|
||||
void DSC_get_dtype_name(const dsc*, TEXT*, USHORT);
|
||||
bool DSC_make_descriptor(dsc*, USHORT, SSHORT,
|
||||
|
@ -159,7 +159,7 @@ int PRETTY_print_cdb(const UCHAR* blr, FPTR_PRINT_CALLBACK routine, void* user_a
|
||||
while (parameter = BLR_BYTE)
|
||||
{
|
||||
const char* p;
|
||||
if (parameter > FB_NELEM(cdb_table) || !(p = cdb_table[parameter]))
|
||||
if (parameter > static_cast<FB_SSIZE_T>(FB_NELEM(cdb_table)) || !(p = cdb_table[parameter]))
|
||||
{
|
||||
return error(control, 0, "*** cdb parameter %d is undefined ***\n", parameter);
|
||||
}
|
||||
|
@ -155,7 +155,7 @@ private:
|
||||
|
||||
typedef Firebird::Array<UCHAR> AuthenticationBlock;
|
||||
|
||||
class UserData :
|
||||
class UserData final :
|
||||
public Firebird::VersionedIface<Firebird::IUserImpl<UserData, Firebird::CheckStatusWrapper> >
|
||||
{
|
||||
public:
|
||||
@ -233,31 +233,6 @@ public:
|
||||
IntField u, g;
|
||||
};
|
||||
|
||||
class StackUserData final : public UserData
|
||||
{
|
||||
public:
|
||||
void* operator new(size_t, void* memory) noexcept
|
||||
{
|
||||
return memory;
|
||||
}
|
||||
};
|
||||
|
||||
class DynamicUserData final : public UserData
|
||||
{
|
||||
public:
|
||||
#ifdef DEBUG_GDS_ALLOC
|
||||
void* operator new(size_t size, Firebird::MemoryPool& pool, const char* fileName, int line)
|
||||
{
|
||||
return pool.allocate(size, fileName, line);
|
||||
}
|
||||
#else // DEBUG_GDS_ALLOC
|
||||
void* operator new(size_t size, Firebird::MemoryPool& pool)
|
||||
{
|
||||
return pool.allocate(size);
|
||||
}
|
||||
#endif // DEBUG_GDS_ALLOC
|
||||
};
|
||||
|
||||
class Get : public Firebird::GetPlugins<Firebird::IManagement>
|
||||
{
|
||||
public:
|
||||
|
@ -418,47 +418,47 @@ BOOST_AUTO_TEST_CASE(FindTest)
|
||||
// 9
|
||||
string b = "345";
|
||||
|
||||
check(a.find(b), 3);
|
||||
check(a.find("45"), 4);
|
||||
check(a.find('5'), 5);
|
||||
check(a.find(b), 3u);
|
||||
check(a.find("45"), 4u);
|
||||
check(a.find('5'), 5u);
|
||||
check(a.find("ZZ"), string::npos);
|
||||
|
||||
check(a.rfind(b), 9);
|
||||
check(a.rfind("45"), 10);
|
||||
check(a.rfind('5'), 11);
|
||||
check(a.rfind(b), 9u);
|
||||
check(a.rfind("45"), 10u);
|
||||
check(a.rfind('5'), 11u);
|
||||
check(a.rfind("ZZ"), string::npos);
|
||||
|
||||
check(a.find("45", 8), 10);
|
||||
check(a.find("45", 8), 10u);
|
||||
|
||||
check(a.find_first_of("aub"), 6);
|
||||
check(a.find_first_of(b), 3);
|
||||
check(a.find_first_of("54"), 4);
|
||||
check(a.find_first_of('5'), 5);
|
||||
check(a.find_first_of("aub"), 6u);
|
||||
check(a.find_first_of(b), 3u);
|
||||
check(a.find_first_of("54"), 4u);
|
||||
check(a.find_first_of('5'), 5u);
|
||||
check(a.find_first_of("ZZ"), string::npos);
|
||||
|
||||
check(a.find_last_of("aub"), 8);
|
||||
check(a.find_last_of(b), 11);
|
||||
check(a.find_last_of("54"), 11);
|
||||
check(a.find_last_of('5'), 11);
|
||||
check(a.find_last_of("aub"), 8u);
|
||||
check(a.find_last_of(b), 11u);
|
||||
check(a.find_last_of("54"), 11u);
|
||||
check(a.find_last_of('5'), 11u);
|
||||
check(a.find_last_of("ZZ"), string::npos);
|
||||
|
||||
check(a.find_first_of("45", 8), 10);
|
||||
check(a.find_first_of("45", 8u), 10u);
|
||||
|
||||
b = "010";
|
||||
check(a.find_first_not_of("aub"), 0);
|
||||
check(a.find_first_not_of(b), 2);
|
||||
check(a.find_first_not_of("0102"), 3);
|
||||
check(a.find_first_not_of('0'), 1);
|
||||
check(a.find_first_not_of("aub"), 0u);
|
||||
check(a.find_first_not_of(b), 2u);
|
||||
check(a.find_first_not_of("0102"), 3u);
|
||||
check(a.find_first_not_of('0'), 1u);
|
||||
check(a.find_first_not_of(a), string::npos);
|
||||
|
||||
b = "878";
|
||||
check(a.find_last_not_of("aub"), 14);
|
||||
check(a.find_last_not_of(b), 12);
|
||||
check(a.find_last_not_of("78"), 12);
|
||||
check(a.find_last_not_of('8'), 13);
|
||||
check(a.find_last_not_of("aub"), 14u);
|
||||
check(a.find_last_not_of(b), 12u);
|
||||
check(a.find_last_not_of("78"), 12u);
|
||||
check(a.find_last_not_of('8'), 13u);
|
||||
check(a.find_last_not_of(a), string::npos);
|
||||
|
||||
check(a.find_first_not_of("u345", 8), 12);
|
||||
check(a.find_first_not_of("u345", 8u), 12u);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(SubstrTest)
|
||||
@ -466,13 +466,13 @@ BOOST_AUTO_TEST_CASE(SubstrTest)
|
||||
string a = lbl;
|
||||
string b;
|
||||
|
||||
b = a.substr(3, 4);
|
||||
b = a.substr(3u, 4u);
|
||||
validate(b, "3456");
|
||||
|
||||
b = a.substr(5, 20);
|
||||
b = a.substr(5u, 20u);
|
||||
validate(b, "56789");
|
||||
|
||||
b = a.substr(50, 20);
|
||||
b = a.substr(50u, 20u);
|
||||
validate(b, "");
|
||||
}
|
||||
|
||||
|
@ -2176,16 +2176,20 @@ BoolExprNode* RseBoolNode::convertNeqAllToNotAny(thread_db* tdbb, CompilerScratc
|
||||
|
||||
andNode->arg1 = missNode;
|
||||
|
||||
RseNode* newInnerRse1 = innerRse->clone(csb->csb_pool);
|
||||
newInnerRse1->flags |= RseNode::FLAG_SUB_QUERY;
|
||||
|
||||
RseBoolNode* rseBoolNode = FB_NEW_POOL(csb->csb_pool) RseBoolNode(csb->csb_pool, blr_any);
|
||||
rseBoolNode->rse = innerRse;
|
||||
rseBoolNode->rse = newInnerRse1;
|
||||
rseBoolNode->ownSavepoint = this->ownSavepoint;
|
||||
|
||||
andNode->arg2 = rseBoolNode;
|
||||
|
||||
RseNode* newInnerRse = innerRse->clone(csb->csb_pool);
|
||||
RseNode* newInnerRse2 = innerRse->clone(csb->csb_pool);
|
||||
newInnerRse2->flags |= RseNode::FLAG_SUB_QUERY;
|
||||
|
||||
rseBoolNode = FB_NEW_POOL(csb->csb_pool) RseBoolNode(csb->csb_pool, blr_any);
|
||||
rseBoolNode->rse = newInnerRse;
|
||||
rseBoolNode->rse = newInnerRse2;
|
||||
rseBoolNode->ownSavepoint = this->ownSavepoint;
|
||||
|
||||
orNode->arg2 = rseBoolNode;
|
||||
@ -2201,16 +2205,16 @@ BoolExprNode* RseBoolNode::convertNeqAllToNotAny(thread_db* tdbb, CompilerScratc
|
||||
outerRseNeq->blrOp = blr_eql;
|
||||
|
||||
// If there was a boolean on the stream, append (AND) the new one
|
||||
if (newInnerRse->rse_boolean)
|
||||
if (newInnerRse2->rse_boolean)
|
||||
{
|
||||
andNode = FB_NEW_POOL(csb->csb_pool) BinaryBoolNode(csb->csb_pool, blr_and);
|
||||
|
||||
andNode->arg1 = newInnerRse->rse_boolean;
|
||||
andNode->arg1 = newInnerRse2->rse_boolean;
|
||||
andNode->arg2 = boolean;
|
||||
boolean = andNode;
|
||||
}
|
||||
|
||||
newInnerRse->rse_boolean = boolean;
|
||||
newInnerRse2->rse_boolean = boolean;
|
||||
|
||||
SubExprNodeCopier copier(csb->csb_pool, csb);
|
||||
return copier.copy(tdbb, static_cast<BoolExprNode*>(newNode));
|
||||
|
@ -80,9 +80,6 @@ static void defineComputed(DsqlCompilerScratch* dsqlScratch, RelationSourceNode*
|
||||
dsql_fld* field, ValueSourceClause* clause, string& source, BlrDebugWriter::BlrData& value);
|
||||
static void deleteKeyConstraint(thread_db* tdbb, jrd_tra* transaction,
|
||||
const MetaName& relationName, const MetaName& constraintName, const MetaName& indexName);
|
||||
static void defineFile(thread_db* tdbb, jrd_tra* transaction, SLONG shadowNumber, bool manualShadow,
|
||||
bool conditionalShadow, SLONG& dbAlloc,
|
||||
const PathName& name, SLONG start, SLONG length);
|
||||
static bool fieldExists(thread_db* tdbb, jrd_tra* transaction, const MetaName& relationName,
|
||||
const MetaName& fieldName);
|
||||
static bool isItSqlRole(thread_db* tdbb, jrd_tra* transaction, const MetaName& inputName,
|
||||
@ -490,46 +487,6 @@ static void deleteKeyConstraint(thread_db* tdbb, jrd_tra* transaction,
|
||||
}
|
||||
}
|
||||
|
||||
// Define a database or shadow file.
|
||||
static void defineFile(thread_db* tdbb, jrd_tra* transaction, SLONG shadowNumber, bool manualShadow,
|
||||
bool conditionalShadow, SLONG& dbAlloc, const PathName& name, SLONG start, SLONG length)
|
||||
{
|
||||
PathName expandedName = name;
|
||||
|
||||
if (!ISC_expand_filename(expandedName, false))
|
||||
status_exception::raise(Arg::PrivateDyn(231)); // File name is invalid.
|
||||
|
||||
if (tdbb->getDatabase()->dbb_filename == expandedName)
|
||||
status_exception::raise(Arg::PrivateDyn(166));
|
||||
|
||||
AutoCacheRequest request(tdbb, drq_l_files, DYN_REQUESTS);
|
||||
|
||||
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
|
||||
FIRST 1 X IN RDB$FILES
|
||||
WITH X.RDB$FILE_NAME EQ expandedName.c_str()
|
||||
{
|
||||
status_exception::raise(Arg::PrivateDyn(166));
|
||||
}
|
||||
END_FOR
|
||||
|
||||
request.reset(tdbb, drq_s_files, DYN_REQUESTS);
|
||||
|
||||
STORE(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
|
||||
X IN RDB$FILES
|
||||
{
|
||||
expandedName.copyTo(X.RDB$FILE_NAME, sizeof(X.RDB$FILE_NAME));
|
||||
X.RDB$SHADOW_NUMBER = shadowNumber;
|
||||
X.RDB$FILE_FLAGS = (manualShadow ? FILE_manual : 0) |
|
||||
(conditionalShadow ? FILE_conditional : 0);
|
||||
|
||||
dbAlloc = MAX(dbAlloc, start);
|
||||
X.RDB$FILE_START = dbAlloc;
|
||||
X.RDB$FILE_LENGTH = length;
|
||||
dbAlloc += length;
|
||||
}
|
||||
END_STORE
|
||||
}
|
||||
|
||||
// Checks to see if the given field already exists in a relation.
|
||||
static bool fieldExists(thread_db* tdbb, jrd_tra* transaction, const MetaName& relationName,
|
||||
const MetaName& fieldName)
|
||||
@ -10462,7 +10419,7 @@ string CreateShadowNode::internalPrint(NodePrinter& printer) const
|
||||
NODE_PRINT(printer, number);
|
||||
NODE_PRINT(printer, manual);
|
||||
NODE_PRINT(printer, conditional);
|
||||
NODE_PRINT(printer, files);
|
||||
NODE_PRINT(printer, fileName);
|
||||
|
||||
return "CreateShadowNode";
|
||||
}
|
||||
@ -10486,7 +10443,7 @@ void CreateShadowNode::execute(thread_db* tdbb, DsqlCompilerScratch* /*dsqlScrat
|
||||
// run all statements under savepoint control
|
||||
AutoSavePoint savePoint(tdbb, transaction);
|
||||
|
||||
// If a shadow set identified by the shadow number already exists return error.
|
||||
// If a shadow set identified by the shadow number already exists return error
|
||||
|
||||
AutoCacheRequest request(tdbb, drq_l_shadow, DYN_REQUESTS);
|
||||
|
||||
@ -10502,25 +10459,34 @@ void CreateShadowNode::execute(thread_db* tdbb, DsqlCompilerScratch* /*dsqlScrat
|
||||
}
|
||||
END_FOR
|
||||
|
||||
SLONG start = 0;
|
||||
PathName expandedName = fileName.ToPathName();
|
||||
|
||||
for (NestConst<DbFileClause>* i = files.begin(); i != files.end(); ++i)
|
||||
if (!ISC_expand_filename(expandedName, false))
|
||||
status_exception::raise(Arg::PrivateDyn(231)); // File name is invalid
|
||||
|
||||
if (tdbb->getDatabase()->dbb_filename == expandedName)
|
||||
status_exception::raise(Arg::PrivateDyn(166));
|
||||
|
||||
request.reset(tdbb, drq_l_files, DYN_REQUESTS);
|
||||
|
||||
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
|
||||
FIRST 1 X IN RDB$FILES
|
||||
WITH X.RDB$FILE_NAME EQ expandedName.c_str()
|
||||
{
|
||||
bool first = i == files.begin();
|
||||
DbFileClause* file = *i;
|
||||
|
||||
if (!first && i[-1]->length == 0 && file->start == 0)
|
||||
{
|
||||
// Preceding file did not specify length, so %s must include starting page number
|
||||
status_exception::raise(
|
||||
Arg::Gds(isc_sqlerr) << Arg::Num(-607) <<
|
||||
Arg::Gds(isc_dsql_command_err) <<
|
||||
Arg::Gds(isc_dsql_file_length_err) << file->name);
|
||||
}
|
||||
|
||||
defineFile(tdbb, transaction, number, manual && first, conditional && first,
|
||||
start, file->name.c_str(), file->start, file->length);
|
||||
status_exception::raise(Arg::PrivateDyn(166));
|
||||
}
|
||||
END_FOR
|
||||
|
||||
request.reset(tdbb, drq_s_files, DYN_REQUESTS);
|
||||
|
||||
STORE(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
|
||||
X IN RDB$FILES
|
||||
{
|
||||
expandedName.copyTo(X.RDB$FILE_NAME, sizeof(X.RDB$FILE_NAME));
|
||||
X.RDB$SHADOW_NUMBER = number;
|
||||
X.RDB$FILE_FLAGS = (manual ? FILE_manual : 0) | (conditional ? FILE_conditional : 0);
|
||||
}
|
||||
END_STORE
|
||||
|
||||
savePoint.release(); // everything is ok
|
||||
}
|
||||
@ -11352,7 +11318,7 @@ void CreateAlterUserNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra
|
||||
// run all statements under savepoint control
|
||||
AutoSavePoint savePoint(tdbb, transaction);
|
||||
|
||||
Auth::DynamicUserData* userData = FB_NEW_POOL(*transaction->tra_pool) Auth::DynamicUserData;
|
||||
Auth::UserData* userData = FB_NEW_POOL(*transaction->tra_pool) Auth::UserData;
|
||||
|
||||
MetaName text(name);
|
||||
if (text.isEmpty() && mode == USER_MOD)
|
||||
@ -11469,7 +11435,7 @@ void DropUserNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jr
|
||||
// run all statements under savepoint control
|
||||
AutoSavePoint savePoint(tdbb, transaction);
|
||||
|
||||
Auth::DynamicUserData* userData = FB_NEW_POOL(*transaction->tra_pool) Auth::DynamicUserData;
|
||||
Auth::UserData* userData = FB_NEW_POOL(*transaction->tra_pool) Auth::UserData;
|
||||
|
||||
string text = name.c_str();
|
||||
|
||||
@ -12742,13 +12708,11 @@ string AlterDatabaseNode::internalPrint(NodePrinter& printer) const
|
||||
DdlNode::internalPrint(printer);
|
||||
|
||||
NODE_PRINT(printer, create);
|
||||
NODE_PRINT(printer, createLength);
|
||||
NODE_PRINT(printer, linger);
|
||||
NODE_PRINT(printer, clauses);
|
||||
NODE_PRINT(printer, differenceFile);
|
||||
NODE_PRINT(printer, setDefaultCharSet);
|
||||
NODE_PRINT(printer, setDefaultCollation);
|
||||
NODE_PRINT(printer, files);
|
||||
NODE_PRINT(printer, cryptPlugin);
|
||||
NODE_PRINT(printer, keyName);
|
||||
|
||||
@ -12924,18 +12888,6 @@ void AlterDatabaseNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratc
|
||||
}
|
||||
}
|
||||
|
||||
SLONG dbAlloc = PageSpace::maxAlloc(tdbb->getDatabase());
|
||||
SLONG start = create ? createLength + 1 : 0;
|
||||
for (NestConst<DbFileClause>* i = files.begin(); i != files.end(); ++i)
|
||||
{
|
||||
DbFileClause* file = *i;
|
||||
|
||||
start = MAX(start, file->start);
|
||||
defineFile(tdbb, transaction, 0, false, false, dbAlloc,
|
||||
file->name.c_str(), start, file->length);
|
||||
start += file->length;
|
||||
}
|
||||
|
||||
if (differenceFile.hasData())
|
||||
defineDifference(tdbb, transaction, differenceFile.c_str());
|
||||
|
||||
@ -13064,7 +13016,6 @@ void AlterDatabaseNode::changeBackupMode(thread_db* tdbb, jrd_tra* transaction,
|
||||
X IN RDB$FILES
|
||||
{
|
||||
X.RDB$FILE_FLAGS = FILE_difference | FILE_backing_up;
|
||||
X.RDB$FILE_START = 0;
|
||||
}
|
||||
END_STORE
|
||||
|
||||
@ -13116,7 +13067,6 @@ void AlterDatabaseNode::defineDifference(thread_db* tdbb, jrd_tra* transaction,
|
||||
|
||||
strcpy(FIL.RDB$FILE_NAME, file.c_str());
|
||||
FIL.RDB$FILE_FLAGS = FILE_difference;
|
||||
FIL.RDB$FILE_START = 0;
|
||||
}
|
||||
END_STORE
|
||||
}
|
||||
|
@ -101,40 +101,6 @@ public:
|
||||
};
|
||||
|
||||
|
||||
class DbFileClause : public Printable
|
||||
{
|
||||
public:
|
||||
DbFileClause(MemoryPool& p, const DbFileClause& o)
|
||||
: name(p, o.name),
|
||||
start(o.start),
|
||||
length(o.length)
|
||||
{
|
||||
}
|
||||
|
||||
explicit DbFileClause(MemoryPool& p, const Firebird::string& aName)
|
||||
: name(p, aName),
|
||||
start(0),
|
||||
length(0)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
virtual Firebird::string internalPrint(NodePrinter& printer) const
|
||||
{
|
||||
NODE_PRINT(printer, name);
|
||||
NODE_PRINT(printer, start);
|
||||
NODE_PRINT(printer, length);
|
||||
|
||||
return "DbFileClause";
|
||||
}
|
||||
|
||||
public:
|
||||
Firebird::string name; // File name
|
||||
SLONG start; // Starting page
|
||||
SLONG length; // File length in pages
|
||||
};
|
||||
|
||||
|
||||
class ExternalClause : public Printable
|
||||
{
|
||||
public:
|
||||
@ -1967,12 +1933,13 @@ public:
|
||||
class CreateShadowNode : public DdlNode
|
||||
{
|
||||
public:
|
||||
CreateShadowNode(MemoryPool& p, const SSHORT aNumber)
|
||||
CreateShadowNode(MemoryPool& p, SSHORT aNumber, bool aManual, bool aConditional,
|
||||
const Firebird::string& aFileName)
|
||||
: DdlNode(p),
|
||||
number(aNumber),
|
||||
manual(false),
|
||||
conditional(false),
|
||||
files(p)
|
||||
manual(aManual),
|
||||
conditional(aConditional),
|
||||
fileName(p, aFileName)
|
||||
{
|
||||
}
|
||||
|
||||
@ -1996,7 +1963,7 @@ public:
|
||||
SSHORT number;
|
||||
bool manual;
|
||||
bool conditional;
|
||||
Firebird::Array<NestConst<DbFileClause> > files;
|
||||
Firebird::string fileName;
|
||||
bool createIfNotExistsOnly = false;
|
||||
};
|
||||
|
||||
@ -2425,14 +2392,9 @@ public:
|
||||
public:
|
||||
AlterDatabaseNode(MemoryPool& p)
|
||||
: DdlNode(p),
|
||||
create(false),
|
||||
createLength(0),
|
||||
linger(-1),
|
||||
clauses(0),
|
||||
differenceFile(p),
|
||||
setDefaultCharSet(p),
|
||||
setDefaultCollation(p),
|
||||
files(p),
|
||||
cryptPlugin(p),
|
||||
keyName(p),
|
||||
pubTables(p)
|
||||
@ -2468,13 +2430,12 @@ private:
|
||||
void checkClauses(thread_db* tdbb);
|
||||
|
||||
public:
|
||||
bool create; // Is the node created with a CREATE DATABASE command?
|
||||
SLONG createLength, linger;
|
||||
unsigned clauses;
|
||||
bool create = false; // Is the node created with a CREATE DATABASE command?
|
||||
SLONG linger = -1;
|
||||
unsigned clauses = 0;
|
||||
Firebird::string differenceFile;
|
||||
MetaName setDefaultCharSet;
|
||||
MetaName setDefaultCollation;
|
||||
Firebird::Array<NestConst<DbFileClause> > files;
|
||||
MetaName cryptPlugin;
|
||||
MetaName keyName;
|
||||
Firebird::TriState ssDefiner;
|
||||
|
@ -56,17 +56,15 @@ void DsqlStatement::rethrowDdlException(status_exception& ex, bool metadataUpdat
|
||||
status_exception::raise(newVector);
|
||||
}
|
||||
|
||||
int DsqlStatement::release()
|
||||
void DsqlStatement::release()
|
||||
{
|
||||
fb_assert(refCounter.value() > 0);
|
||||
int refCnt = --refCounter;
|
||||
|
||||
if (!refCnt)
|
||||
if (!--refCounter)
|
||||
{
|
||||
if (cacheKey)
|
||||
{
|
||||
dsqlAttachment->dbb_statement_cache->statementGoingInactive(cacheKey);
|
||||
refCnt = refCounter;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -74,8 +72,6 @@ int DsqlStatement::release()
|
||||
dsqlAttachment->deletePool(&getPool());
|
||||
}
|
||||
}
|
||||
|
||||
return refCnt;
|
||||
}
|
||||
|
||||
void DsqlStatement::doRelease()
|
||||
|
@ -83,12 +83,12 @@ protected:
|
||||
virtual ~DsqlStatement() = default;
|
||||
|
||||
public:
|
||||
int addRef()
|
||||
void addRef()
|
||||
{
|
||||
return ++refCounter;
|
||||
++refCounter;
|
||||
}
|
||||
|
||||
int release();
|
||||
void release();
|
||||
|
||||
bool isCursorBased() const
|
||||
{
|
||||
|
@ -767,8 +767,6 @@ using namespace Firebird;
|
||||
Firebird::string* stringPtr;
|
||||
Jrd::IntlString* intlStringPtr;
|
||||
Jrd::Lim64String* lim64ptr;
|
||||
Jrd::DbFileClause* dbFileClause;
|
||||
Firebird::Array<NestConst<Jrd::DbFileClause> >* dbFilesClause;
|
||||
Jrd::ExternalClause* externalClause;
|
||||
Firebird::NonPooledPair<Jrd::MetaName*, Jrd::ValueExprNode*>* namedArgument;
|
||||
Firebird::NonPooledPair<Firebird::ObjectsArray<Jrd::MetaName>*, Jrd::ValueListNode*>* namedArguments;
|
||||
@ -1807,16 +1805,8 @@ index_condition_opt
|
||||
// CREATE SHADOW
|
||||
%type <createShadowNode> shadow_clause
|
||||
shadow_clause
|
||||
: pos_short_integer manual_auto conditional utf_string first_file_length
|
||||
{
|
||||
$$ = newNode<CreateShadowNode>($1);
|
||||
$$->manual = $2;
|
||||
$$->conditional = $3;
|
||||
$$->files.add(newNode<DbFileClause>(*$4));
|
||||
$$->files.front()->length = $5;
|
||||
}
|
||||
sec_shadow_files(NOTRIAL(&$6->files))
|
||||
{ $$ = $6; }
|
||||
: pos_short_integer manual_auto conditional utf_string
|
||||
{ $$ = newNode<CreateShadowNode>($1, $2, $3, *$4); }
|
||||
;
|
||||
|
||||
%type <boolVal> manual_auto
|
||||
@ -1832,24 +1822,6 @@ conditional
|
||||
| CONDITIONAL { $$ = true; }
|
||||
;
|
||||
|
||||
%type <int32Val> first_file_length
|
||||
first_file_length
|
||||
: /* nothing */ { $$ = 0; }
|
||||
| LENGTH equals long_integer page_noise { $$ = $3; }
|
||||
;
|
||||
|
||||
%type sec_shadow_files(<dbFilesClause>)
|
||||
sec_shadow_files($dbFilesClause)
|
||||
: // nothing
|
||||
| db_file_list($dbFilesClause)
|
||||
;
|
||||
|
||||
%type db_file_list(<dbFilesClause>)
|
||||
db_file_list($dbFilesClause)
|
||||
: db_file { $dbFilesClause->add($1); }
|
||||
| db_file_list db_file { $dbFilesClause->add($2); }
|
||||
;
|
||||
|
||||
|
||||
// CREATE DOMAIN
|
||||
|
||||
@ -2279,8 +2251,6 @@ db_initial_option($alterDatabaseNode)
|
||||
| ROLE utf_string
|
||||
| PASSWORD utf_string
|
||||
| SET NAMES utf_string
|
||||
| LENGTH equals long_integer page_noise
|
||||
{ $alterDatabaseNode->createLength = $3; }
|
||||
;
|
||||
|
||||
%type db_rem_desc1(<alterDatabaseNode>)
|
||||
@ -2297,9 +2267,7 @@ db_rem_desc($alterDatabaseNode)
|
||||
|
||||
%type db_rem_option(<alterDatabaseNode>)
|
||||
db_rem_option($alterDatabaseNode)
|
||||
: db_file
|
||||
{ $alterDatabaseNode->files.add($1); }
|
||||
| DEFAULT CHARACTER SET symbol_character_set_name
|
||||
: DEFAULT CHARACTER SET symbol_character_set_name
|
||||
{ $alterDatabaseNode->setDefaultCharSet = *$4; }
|
||||
| DEFAULT CHARACTER SET symbol_character_set_name COLLATION symbol_collation_name
|
||||
{
|
||||
@ -2310,49 +2278,6 @@ db_rem_option($alterDatabaseNode)
|
||||
{ $alterDatabaseNode->differenceFile = *$3; }
|
||||
;
|
||||
|
||||
%type <dbFileClause> db_file
|
||||
db_file
|
||||
: FILE utf_string
|
||||
{
|
||||
DbFileClause* clause = newNode<DbFileClause>(*$2);
|
||||
$$ = clause;
|
||||
}
|
||||
file_desc1($3)
|
||||
{ $$ = $3; }
|
||||
;
|
||||
|
||||
%type file_desc1(<dbFileClause>)
|
||||
file_desc1($dbFileClause)
|
||||
: // nothing
|
||||
| file_desc($dbFileClause)
|
||||
;
|
||||
|
||||
%type file_desc(<dbFileClause>)
|
||||
file_desc($dbFileClause)
|
||||
: file_clause($dbFileClause)
|
||||
| file_desc file_clause($dbFileClause)
|
||||
;
|
||||
|
||||
%type file_clause(<dbFileClause>)
|
||||
file_clause($dbFileClause)
|
||||
: STARTING file_clause_noise long_integer
|
||||
{ $dbFileClause->start = $3; }
|
||||
| LENGTH equals long_integer page_noise
|
||||
{ $dbFileClause->length = $3; }
|
||||
;
|
||||
|
||||
file_clause_noise
|
||||
: // nothing
|
||||
| AT
|
||||
| AT PAGE
|
||||
;
|
||||
|
||||
page_noise
|
||||
: // nothing
|
||||
| PAGE
|
||||
| PAGES
|
||||
;
|
||||
|
||||
|
||||
// CREATE TABLE
|
||||
|
||||
@ -4770,8 +4695,7 @@ alter_db($alterDatabaseNode)
|
||||
|
||||
%type db_alter_clause(<alterDatabaseNode>)
|
||||
db_alter_clause($alterDatabaseNode)
|
||||
: ADD db_file_list(NOTRIAL(&$alterDatabaseNode->files))
|
||||
| ADD DIFFERENCE FILE utf_string
|
||||
: ADD DIFFERENCE FILE utf_string
|
||||
{ $alterDatabaseNode->differenceFile = *$4; }
|
||||
| DROP DIFFERENCE FILE
|
||||
{ $alterDatabaseNode->clauses |= AlterDatabaseNode::CLAUSE_DROP_DIFFERENCE; }
|
||||
|
@ -38,7 +38,7 @@ static int hash(const SCHAR*);
|
||||
static bool scompare(const SCHAR*, const SCHAR*);
|
||||
static bool scompare2(const SCHAR*, const SCHAR*);
|
||||
|
||||
const int HASH_SIZE = 211;
|
||||
const FB_SIZE_T HASH_SIZE = 211;
|
||||
|
||||
static gpre_sym* hash_table[HASH_SIZE];
|
||||
static gpre_sym* key_symbols;
|
||||
@ -80,7 +80,7 @@ void HSH_init()
|
||||
{
|
||||
//const char *string;
|
||||
|
||||
int i = 0;
|
||||
FB_SIZE_T i = 0;
|
||||
for (gpre_sym** ptr = hash_table; i < HASH_SIZE; i++)
|
||||
*ptr++ = NULL;
|
||||
|
||||
|
@ -2293,7 +2293,10 @@ static void gen_for( const act* action, int column)
|
||||
const gpre_req* request = action->act_request;
|
||||
|
||||
if (action->act_error || (action->act_flags & ACT_sql))
|
||||
success(column, true, global_status_name, " {");
|
||||
{
|
||||
printa(column, "if (%s && !(%s->getState() & Firebird::IStatus::STATE_ERRORS)) {",
|
||||
request->req_handle, global_status_name);
|
||||
}
|
||||
|
||||
printa(column, "while (1)");
|
||||
column += INDENT;
|
||||
@ -3075,11 +3078,10 @@ static void gen_s_start( const act* action, int column)
|
||||
if (action->act_error || (action->act_flags & ACT_sql))
|
||||
{
|
||||
make_ok_test(action, request, column);
|
||||
printa(column, "{");
|
||||
column += INDENT;
|
||||
}
|
||||
gen_start(action, port, column, false);
|
||||
if (action->act_error || (action->act_flags & ACT_sql))
|
||||
column -= INDENT;
|
||||
|
||||
const TEXT* pattern1 = "if (%V1->getErrors()[1] == isc_bad_req_handle) { %RH->release(); %RH = NULL; }";
|
||||
PAT args;
|
||||
@ -3088,6 +3090,12 @@ static void gen_s_start( const act* action, int column)
|
||||
PATTERN_expand((USHORT) column, pattern1, &args);
|
||||
printa(column, "else break;");
|
||||
|
||||
if (action->act_error || (action->act_flags & ACT_sql))
|
||||
{
|
||||
column -= INDENT;
|
||||
printa(column, "}");
|
||||
}
|
||||
|
||||
if (action->act_type == ACT_open)
|
||||
{
|
||||
endp(column);
|
||||
|
@ -135,9 +135,9 @@ typedef int (*lock_ast_t)(void*);
|
||||
|
||||
// Number of elements in an array
|
||||
template <typename T, std::size_t N>
|
||||
constexpr int FB_NELEM(const T (&)[N])
|
||||
constexpr FB_SIZE_T FB_NELEM(const T (&)[N])
|
||||
{
|
||||
return N;
|
||||
return static_cast<FB_SIZE_T>(N);
|
||||
}
|
||||
|
||||
// Intl types
|
||||
|
@ -28,9 +28,7 @@
|
||||
#include <cctype>
|
||||
|
||||
|
||||
static std::string trim(std::string_view str);
|
||||
|
||||
static std::string trim(std::string_view str)
|
||||
std::string FrontendLexer::trim(std::string_view str)
|
||||
{
|
||||
auto finish = str.end();
|
||||
auto start = str.begin();
|
||||
@ -142,7 +140,7 @@ std::variant<FrontendLexer::SingleStatement, FrontendLexer::IncompleteTokenError
|
||||
|
||||
while (pos < end)
|
||||
{
|
||||
if (end - pos >= term.length() && std::equal(term.begin(), term.end(), pos))
|
||||
if (std::size_t(end - pos) >= term.length() && std::equal(term.begin(), term.end(), pos))
|
||||
{
|
||||
const auto initialStatement = std::string(buffer.cbegin(), pos);
|
||||
pos += term.length();
|
||||
@ -189,22 +187,8 @@ FrontendLexer::Token FrontendLexer::getToken()
|
||||
|
||||
switch (toupper(*pos))
|
||||
{
|
||||
case '(':
|
||||
token.type = Token::TYPE_OPEN_PAREN;
|
||||
token.processedText = *pos++;
|
||||
break;
|
||||
|
||||
case ')':
|
||||
token.type = Token::TYPE_CLOSE_PAREN;
|
||||
token.processedText = *pos++;
|
||||
break;
|
||||
|
||||
case ',':
|
||||
token.type = Token::TYPE_COMMA;
|
||||
token.processedText = *pos++;
|
||||
break;
|
||||
|
||||
case ';':
|
||||
case '.':
|
||||
token.type = Token::TYPE_OTHER;
|
||||
token.processedText = *pos++;
|
||||
break;
|
||||
@ -224,13 +208,83 @@ FrontendLexer::Token FrontendLexer::getToken()
|
||||
return token;
|
||||
}
|
||||
|
||||
std::optional<FrontendLexer::Token> FrontendLexer::getStringToken()
|
||||
FrontendLexer::Token FrontendLexer::getNameToken()
|
||||
{
|
||||
skipSpacesAndComments();
|
||||
|
||||
Token token;
|
||||
|
||||
if (pos >= end)
|
||||
{
|
||||
token.type = Token::TYPE_EOF;
|
||||
return token;
|
||||
}
|
||||
|
||||
if (const auto optStringToken = getStringToken(); optStringToken.has_value())
|
||||
return optStringToken.value();
|
||||
|
||||
/*** Revert to strict parsing with schemas support branch.
|
||||
const auto start = pos;
|
||||
bool first = true;
|
||||
|
||||
while (pos < end)
|
||||
{
|
||||
const auto c = *pos++;
|
||||
|
||||
if (!((c >= 'A' && c <= 'Z') ||
|
||||
(c >= 'a' && c <= 'z') ||
|
||||
c == '{' ||
|
||||
c == '}' ||
|
||||
(!first && c >= '0' && c <= '9') ||
|
||||
(!first && c == '$') ||
|
||||
(!first && c == '_')))
|
||||
{
|
||||
if (!first)
|
||||
--pos;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
first = false;
|
||||
}
|
||||
|
||||
token.processedText = token.rawText = std::string(start, pos);
|
||||
std::transform(token.processedText.begin(), token.processedText.end(),
|
||||
token.processedText.begin(), toupper);
|
||||
|
||||
return token;
|
||||
***/
|
||||
|
||||
const auto start = pos;
|
||||
|
||||
switch (toupper(*pos))
|
||||
{
|
||||
case ';':
|
||||
token.type = Token::TYPE_OTHER;
|
||||
token.processedText = *pos++;
|
||||
break;
|
||||
|
||||
default:
|
||||
while (pos != end && !fb_utils::isspace(*pos) && *pos != '.')
|
||||
++pos;
|
||||
|
||||
token.processedText = std::string(start, pos);
|
||||
std::transform(token.processedText.begin(), token.processedText.end(),
|
||||
token.processedText.begin(), toupper);
|
||||
break;
|
||||
}
|
||||
|
||||
token.rawText = std::string(start, pos);
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
std::optional<FrontendLexer::Token> FrontendLexer::getStringToken()
|
||||
{
|
||||
if (pos >= end)
|
||||
return std::nullopt;
|
||||
|
||||
Token token;
|
||||
const auto start = pos;
|
||||
|
||||
switch (toupper(*pos))
|
||||
|
@ -39,15 +39,18 @@ public:
|
||||
TYPE_EOF,
|
||||
TYPE_STRING,
|
||||
TYPE_META_STRING,
|
||||
TYPE_OPEN_PAREN,
|
||||
TYPE_CLOSE_PAREN,
|
||||
TYPE_COMMA,
|
||||
TYPE_OTHER
|
||||
};
|
||||
|
||||
Type type = TYPE_OTHER;
|
||||
std::string rawText;
|
||||
std::string processedText;
|
||||
|
||||
const std::string& getProcessedString() const
|
||||
{
|
||||
return type == FrontendLexer::Token::TYPE_STRING || type == FrontendLexer::Token::TYPE_META_STRING ?
|
||||
processedText : rawText;
|
||||
}
|
||||
};
|
||||
|
||||
struct SingleStatement
|
||||
@ -74,6 +77,7 @@ public:
|
||||
FrontendLexer& operator=(const FrontendLexer&) = delete;
|
||||
|
||||
public:
|
||||
static std::string trim(std::string_view str);
|
||||
static std::string stripComments(std::string_view statement);
|
||||
|
||||
public:
|
||||
@ -87,6 +91,11 @@ public:
|
||||
return pos;
|
||||
}
|
||||
|
||||
void setPos(std::string::const_iterator newPos)
|
||||
{
|
||||
pos = newPos;
|
||||
}
|
||||
|
||||
void rewind()
|
||||
{
|
||||
deletePos = buffer.begin();
|
||||
@ -97,7 +106,9 @@ public:
|
||||
void appendBuffer(std::string_view newBuffer);
|
||||
void reset();
|
||||
std::variant<SingleStatement, FrontendLexer::IncompleteTokenError> getSingleStatement(std::string_view term);
|
||||
|
||||
Token getToken();
|
||||
Token getNameToken();
|
||||
|
||||
private:
|
||||
std::optional<Token> getStringToken();
|
||||
|
732
src/isql/FrontendParser.cpp
Normal file
732
src/isql/FrontendParser.cpp
Normal file
@ -0,0 +1,732 @@
|
||||
/*
|
||||
* The contents of this file are subject to the Initial
|
||||
* Developer's Public License Version 1.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.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl.
|
||||
*
|
||||
* Software distributed under the License is distributed AS IS,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing rights
|
||||
* and limitations under the License.
|
||||
*
|
||||
* The Original Code was created by Adriano dos Santos Fernandes
|
||||
* for the Firebird Open Source RDBMS project.
|
||||
*
|
||||
* Copyright (c) 2024 Adriano dos Santos Fernandes <adrianosf at gmail.com>
|
||||
* and all contributors signed below.
|
||||
*
|
||||
* All Rights Reserved.
|
||||
* Contributor(s): ______________________________________.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "firebird.h"
|
||||
#include "../isql/FrontendParser.h"
|
||||
#include <cctype>
|
||||
|
||||
|
||||
FrontendParser::AnyNode FrontendParser::internalParse()
|
||||
{
|
||||
static constexpr std::string_view TOKEN_ADD("ADD");
|
||||
static constexpr std::string_view TOKEN_BLOBDUMP("BLOBDUMP");
|
||||
static constexpr std::string_view TOKEN_BLOBVIEW("BLOBVIEW");
|
||||
static constexpr std::string_view TOKEN_CONNECT("CONNECT");
|
||||
static constexpr std::string_view TOKEN_COPY("COPY");
|
||||
static constexpr std::string_view TOKEN_CREATE("CREATE");
|
||||
static constexpr std::string_view TOKEN_DROP("DROP");
|
||||
static constexpr std::string_view TOKEN_EDIT("EDIT");
|
||||
static constexpr std::string_view TOKEN_EXIT("EXIT");
|
||||
static constexpr std::string_view TOKEN_EXPLAIN("EXPLAIN");
|
||||
static constexpr std::string_view TOKEN_HELP("HELP");
|
||||
static constexpr std::string_view TOKEN_INPUT("INPUT");
|
||||
static constexpr std::string_view TOKEN_OUTPUT("OUTPUT");
|
||||
static constexpr std::string_view TOKEN_QUIT("QUIT");
|
||||
static constexpr std::string_view TOKEN_SET("SET");
|
||||
static constexpr std::string_view TOKEN_SHELL("SHELL");
|
||||
static constexpr std::string_view TOKEN_SHOW("SHOW");
|
||||
|
||||
const auto commandToken = lexer.getToken();
|
||||
|
||||
if (commandToken.type != Token::TYPE_OTHER)
|
||||
return InvalidNode();
|
||||
|
||||
const auto& command = commandToken.processedText;
|
||||
|
||||
if (command == TOKEN_ADD)
|
||||
{
|
||||
if (auto tableName = parseName())
|
||||
{
|
||||
AddNode node;
|
||||
node.tableName = std::move(tableName.value());
|
||||
|
||||
if (parseEof())
|
||||
return node;
|
||||
}
|
||||
}
|
||||
else if (command == TOKEN_BLOBDUMP || command == TOKEN_BLOBVIEW)
|
||||
{
|
||||
if (const auto blobId = lexer.getToken(); blobId.type != Token::TYPE_EOF)
|
||||
{
|
||||
BlobDumpViewNode node;
|
||||
|
||||
// Find the high and low values of the blob id
|
||||
if (blobId.processedText.empty())
|
||||
return InvalidNode();
|
||||
|
||||
sscanf(blobId.processedText.c_str(), "%" xLONGFORMAT":%" xLONGFORMAT,
|
||||
&node.blobId.gds_quad_high, &node.blobId.gds_quad_low);
|
||||
|
||||
if (command == TOKEN_BLOBDUMP)
|
||||
{
|
||||
if (auto file = parseFileName())
|
||||
{
|
||||
node.file = std::move(file.value());
|
||||
|
||||
if (parseEof())
|
||||
return node;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (parseEof())
|
||||
return node;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (command == TOKEN_CONNECT)
|
||||
{
|
||||
ConnectNode node;
|
||||
|
||||
do
|
||||
{
|
||||
auto token = lexer.getToken();
|
||||
|
||||
if (token.type == Token::TYPE_EOF)
|
||||
{
|
||||
if (node.args.empty())
|
||||
break;
|
||||
else
|
||||
return node;
|
||||
}
|
||||
else if (token.type != Token::TYPE_OTHER &&
|
||||
token.type != Token::TYPE_STRING &&
|
||||
token.type != Token::TYPE_META_STRING)
|
||||
{
|
||||
return InvalidNode();
|
||||
}
|
||||
|
||||
node.args.push_back(std::move(token));
|
||||
} while(true);
|
||||
}
|
||||
else if (command == TOKEN_COPY)
|
||||
{
|
||||
CopyNode node;
|
||||
|
||||
if (auto source = parseName())
|
||||
node.source = std::move(source.value());
|
||||
else
|
||||
return InvalidNode();
|
||||
|
||||
if (auto destination = parseName())
|
||||
node.destination = std::move(destination.value());
|
||||
else
|
||||
return InvalidNode();
|
||||
|
||||
if (auto database = parseFileName())
|
||||
node.database = std::move(database.value());
|
||||
else
|
||||
return InvalidNode();
|
||||
|
||||
if (parseEof())
|
||||
return node;
|
||||
}
|
||||
else if (command == TOKEN_CREATE)
|
||||
{
|
||||
if (const auto createWhat = lexer.getToken();
|
||||
createWhat.type == Token::TYPE_OTHER &&
|
||||
(createWhat.processedText == "DATABASE" ||
|
||||
(options.schemaAsDatabase && createWhat.processedText == "SCHEMA")))
|
||||
{
|
||||
CreateDatabaseNode node;
|
||||
|
||||
do
|
||||
{
|
||||
auto token = lexer.getToken();
|
||||
|
||||
if (token.type == Token::TYPE_EOF)
|
||||
{
|
||||
if (node.args.empty())
|
||||
break;
|
||||
else
|
||||
return node;
|
||||
}
|
||||
else if (token.type != Token::TYPE_OTHER &&
|
||||
token.type != Token::TYPE_STRING &&
|
||||
token.type != Token::TYPE_META_STRING)
|
||||
{
|
||||
return InvalidNode();
|
||||
}
|
||||
|
||||
node.args.push_back(std::move(token));
|
||||
} while(true);
|
||||
}
|
||||
}
|
||||
else if (command == TOKEN_DROP)
|
||||
{
|
||||
if (const auto dropWhat = lexer.getToken();
|
||||
dropWhat.type == Token::TYPE_OTHER &&
|
||||
(dropWhat.processedText == "DATABASE" ||
|
||||
(options.schemaAsDatabase && dropWhat.processedText == "SCHEMA")))
|
||||
{
|
||||
if (parseEof())
|
||||
return DropDatabaseNode();
|
||||
}
|
||||
}
|
||||
else if (command == TOKEN_EDIT)
|
||||
{
|
||||
EditNode node;
|
||||
node.file = parseFileName();
|
||||
|
||||
if (parseEof())
|
||||
return node;
|
||||
}
|
||||
else if (command == TOKEN_EXIT)
|
||||
{
|
||||
if (parseEof())
|
||||
return ExitNode();
|
||||
}
|
||||
else if (command == TOKEN_EXPLAIN)
|
||||
{
|
||||
ExplainNode node;
|
||||
|
||||
if (auto query = parseUtilEof())
|
||||
{
|
||||
node.query = std::move(query.value());
|
||||
return node;
|
||||
}
|
||||
}
|
||||
else if (command == TOKEN_HELP || command == "?")
|
||||
{
|
||||
HelpNode node;
|
||||
|
||||
if (const auto token = lexer.getToken(); token.type == Token::TYPE_EOF)
|
||||
return node;
|
||||
else if (token.type == Token::TYPE_OTHER)
|
||||
{
|
||||
node.command = token.processedText;
|
||||
|
||||
if (parseEof())
|
||||
return node;
|
||||
}
|
||||
}
|
||||
else if (command.length() >= 2 && TOKEN_INPUT.find(command) == 0)
|
||||
{
|
||||
if (auto file = parseFileName())
|
||||
{
|
||||
InputNode node;
|
||||
node.file = std::move(file.value());
|
||||
|
||||
if (parseEof())
|
||||
return node;
|
||||
}
|
||||
}
|
||||
else if (command.length() >= 3 && TOKEN_OUTPUT.find(command) == 0)
|
||||
{
|
||||
OutputNode node;
|
||||
node.file = parseFileName();
|
||||
|
||||
if (parseEof())
|
||||
return node;
|
||||
}
|
||||
else if (command == TOKEN_QUIT)
|
||||
{
|
||||
if (parseEof())
|
||||
return QuitNode();
|
||||
}
|
||||
else if (command == TOKEN_SET)
|
||||
{
|
||||
if (const auto setNode = parseSet(); !std::holds_alternative<InvalidNode>(setNode))
|
||||
return setNode;
|
||||
}
|
||||
else if (command == TOKEN_SHELL)
|
||||
{
|
||||
ShellNode node;
|
||||
node.command = parseUtilEof();
|
||||
return node;
|
||||
}
|
||||
else if (command == TOKEN_SHOW)
|
||||
{
|
||||
if (const auto showNode = parseShow(); !std::holds_alternative<InvalidNode>(showNode))
|
||||
return showNode;
|
||||
}
|
||||
|
||||
return InvalidNode();
|
||||
}
|
||||
|
||||
FrontendParser::AnySetNode FrontendParser::parseSet()
|
||||
{
|
||||
static constexpr std::string_view TOKEN_AUTODDL("AUTODDL");
|
||||
static constexpr std::string_view TOKEN_AUTOTERM("AUTOTERM");
|
||||
static constexpr std::string_view TOKEN_BAIL("BAIL");
|
||||
static constexpr std::string_view TOKEN_BLOBDISPLAY("BLOBDISPLAY");
|
||||
static constexpr std::string_view TOKEN_BULK_INSERT("BULK_INSERT");
|
||||
static constexpr std::string_view TOKEN_COUNT("COUNT");
|
||||
static constexpr std::string_view TOKEN_ECHO("ECHO");
|
||||
static constexpr std::string_view TOKEN_EXEC_PATH_DISPLAY("EXEC_PATH_DISPLAY");
|
||||
static constexpr std::string_view TOKEN_EXPLAIN("EXPLAIN");
|
||||
static constexpr std::string_view TOKEN_HEADING("HEADING");
|
||||
static constexpr std::string_view TOKEN_KEEP_TRAN_PARAMS("KEEP_TRAN_PARAMS");
|
||||
static constexpr std::string_view TOKEN_LIST("LIST");
|
||||
static constexpr std::string_view TOKEN_LOCAL_TIMEOUT("LOCAL_TIMEOUT");
|
||||
static constexpr std::string_view TOKEN_MAXROWS("MAXROWS");
|
||||
static constexpr std::string_view TOKEN_NAMES("NAMES");
|
||||
static constexpr std::string_view TOKEN_PER_TABLE_STATS("PER_TABLE_STATS");
|
||||
static constexpr std::string_view TOKEN_PLAN("PLAN");
|
||||
static constexpr std::string_view TOKEN_PLANONLY("PLANONLY");
|
||||
static constexpr std::string_view TOKEN_ROWCOUNT("ROWCOUNT");
|
||||
static constexpr std::string_view TOKEN_SQL("SQL");
|
||||
static constexpr std::string_view TOKEN_SQLDA_DISPLAY("SQLDA_DISPLAY");
|
||||
static constexpr std::string_view TOKEN_STATS("STATS");
|
||||
static constexpr std::string_view TOKEN_TERMINATOR("TERMINATOR");
|
||||
static constexpr std::string_view TOKEN_TIME("TIME");
|
||||
static constexpr std::string_view TOKEN_TRANSACTION("TRANSACTION");
|
||||
static constexpr std::string_view TOKEN_WARNINGS("WARNINGS");
|
||||
static constexpr std::string_view TOKEN_WIDTH("WIDTH");
|
||||
static constexpr std::string_view TOKEN_WNG("WNG");
|
||||
static constexpr std::string_view TOKEN_WIRE_STATS("WIRE_STATS");
|
||||
|
||||
switch (const auto setCommandToken = lexer.getToken(); setCommandToken.type)
|
||||
{
|
||||
case Token::TYPE_EOF:
|
||||
return SetNode();
|
||||
|
||||
case Token::TYPE_OTHER:
|
||||
{
|
||||
const auto& text = setCommandToken.processedText;
|
||||
|
||||
if (const auto parsed = parseSet<SetAutoDdlNode>(text, TOKEN_AUTODDL, 4))
|
||||
return parsed.value();
|
||||
else if (const auto parsed = parseSet<SetAutoTermNode>(text, TOKEN_AUTOTERM))
|
||||
return parsed.value();
|
||||
else if (const auto parsed = parseSet<SetBailNode>(text, TOKEN_BAIL))
|
||||
return parsed.value();
|
||||
else if (const auto parsed = parseSet<SetBlobDisplayNode>(text, TOKEN_BLOBDISPLAY, 4))
|
||||
return parsed.value();
|
||||
else if (text == TOKEN_BULK_INSERT)
|
||||
{
|
||||
SetBulkInsertNode node;
|
||||
|
||||
if (const auto statement = parseUtilEof())
|
||||
{
|
||||
node.statement = statement.value();
|
||||
return node;
|
||||
}
|
||||
}
|
||||
else if (const auto parsed = parseSet<SetCountNode>(text, TOKEN_COUNT))
|
||||
return parsed.value();
|
||||
else if (const auto parsed = parseSet<SetEchoNode>(text, TOKEN_ECHO))
|
||||
return parsed.value();
|
||||
else if (const auto parsed = parseSet<SetExecPathDisplayNode>(text, TOKEN_EXEC_PATH_DISPLAY))
|
||||
return parsed.value();
|
||||
else if (const auto parsed = parseSet<SetExplainNode>(text, TOKEN_EXPLAIN))
|
||||
return parsed.value();
|
||||
else if (const auto parsed = parseSet<SetHeadingNode>(text, TOKEN_HEADING))
|
||||
return parsed.value();
|
||||
else if (const auto parsed = parseSet<SetKeepTranParamsNode>(text, TOKEN_KEEP_TRAN_PARAMS, 9))
|
||||
return parsed.value();
|
||||
else if (const auto parsed = parseSet<SetListNode>(text, TOKEN_LIST))
|
||||
return parsed.value();
|
||||
else if (const auto parsed = parseSet<SetLocalTimeoutNode>(text, TOKEN_LOCAL_TIMEOUT))
|
||||
return parsed.value();
|
||||
else if (const auto parsed = parseSet<SetMaxRowsNode>(text, TOKEN_MAXROWS))
|
||||
return parsed.value();
|
||||
else if (text == TOKEN_NAMES)
|
||||
{
|
||||
SetNamesNode node;
|
||||
node.name = parseName();
|
||||
|
||||
if (parseEof())
|
||||
return node;
|
||||
}
|
||||
else if (const auto parsed = parseSet<SetPerTableStatsNode>(text, TOKEN_PER_TABLE_STATS, 7))
|
||||
return parsed.value();
|
||||
else if (const auto parsed = parseSet<SetPlanNode>(text, TOKEN_PLAN))
|
||||
return parsed.value();
|
||||
else if (const auto parsed = parseSet<SetPlanOnlyNode>(text, TOKEN_PLANONLY))
|
||||
return parsed.value();
|
||||
else if (const auto parsed = parseSet<SetMaxRowsNode>(text, TOKEN_ROWCOUNT))
|
||||
return parsed.value();
|
||||
else if (text == TOKEN_SQL)
|
||||
{
|
||||
SetSqlDialectNode node;
|
||||
|
||||
if (const auto dialectToken = lexer.getToken();
|
||||
dialectToken.type == Token::TYPE_OTHER && dialectToken.processedText == "DIALECT")
|
||||
{
|
||||
if (const auto arg = lexer.getToken(); arg.type != Token::TYPE_EOF)
|
||||
{
|
||||
node.arg = arg.processedText;
|
||||
|
||||
if (parseEof())
|
||||
return node;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (const auto parsed = parseSet<SetSqldaDisplayNode>(text, TOKEN_SQLDA_DISPLAY))
|
||||
return parsed.value();
|
||||
else if (const auto parsed = parseSet<SetStatsNode>(text, TOKEN_STATS, 4))
|
||||
return parsed.value();
|
||||
else if (const auto parsed = parseSet<SetTermNode>(text, TOKEN_TERMINATOR, 4, false))
|
||||
return parsed.value();
|
||||
else if (const auto parsed = parseSet<SetTimeNode>(text, TOKEN_TIME))
|
||||
{
|
||||
if (const auto setTimeNode = std::get_if<SetTimeNode>(&parsed.value());
|
||||
setTimeNode && setTimeNode->arg == "ZONE")
|
||||
{
|
||||
return InvalidNode();
|
||||
}
|
||||
|
||||
return parsed.value();
|
||||
}
|
||||
else if (text.length() >= 5 && TOKEN_TRANSACTION.find(text) == 0)
|
||||
{
|
||||
SetTransactionNode node;
|
||||
node.statement = lexer.getBuffer();
|
||||
return node;
|
||||
}
|
||||
else if (const auto parsed = parseSet<SetWarningsNode>(text, TOKEN_WARNINGS, 7))
|
||||
return parsed.value();
|
||||
else if (const auto parsed = parseSet<SetWarningsNode>(text, TOKEN_WNG))
|
||||
return parsed.value();
|
||||
else if (text == TOKEN_WIDTH)
|
||||
{
|
||||
SetWidthNode node;
|
||||
|
||||
if (const auto column = lexer.getToken(); column.type != Token::TYPE_EOF)
|
||||
{
|
||||
node.column = column.processedText;
|
||||
|
||||
if (const auto width = lexer.getToken(); width.type != Token::TYPE_EOF)
|
||||
{
|
||||
node.width = width.processedText;
|
||||
|
||||
if (!parseEof())
|
||||
return InvalidNode();
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
}
|
||||
else if (const auto parsed = parseSet<SetWireStatsNode>(text, TOKEN_WIRE_STATS, 4))
|
||||
return parsed.value();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return InvalidNode();
|
||||
}
|
||||
|
||||
template <typename Node>
|
||||
std::optional<FrontendParser::AnySetNode> FrontendParser::parseSet(std::string_view setCommand,
|
||||
std::string_view testCommand, unsigned testCommandMinLen, bool useProcessedText)
|
||||
{
|
||||
if (setCommand == testCommand ||
|
||||
(testCommandMinLen && setCommand.length() >= testCommandMinLen &&
|
||||
testCommand.find(setCommand) == 0))
|
||||
{
|
||||
Node node;
|
||||
|
||||
if (const auto arg = lexer.getToken(); arg.type != Token::TYPE_EOF)
|
||||
{
|
||||
node.arg = useProcessedText ? arg.processedText : arg.rawText;
|
||||
|
||||
if (!parseEof())
|
||||
return InvalidNode();
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
FrontendParser::AnyShowNode FrontendParser::parseShow()
|
||||
{
|
||||
static constexpr std::string_view TOKEN_CHECKS("CHECKS");
|
||||
static constexpr std::string_view TOKEN_COLLATES("COLLATES");
|
||||
static constexpr std::string_view TOKEN_COLLATIONS("COLLATIONS");
|
||||
static constexpr std::string_view TOKEN_COMMENTS("COMMENTS");
|
||||
static constexpr std::string_view TOKEN_DATABASE("DATABASE");
|
||||
static constexpr std::string_view TOKEN_DEPENDENCIES("DEPENDENCIES");
|
||||
static constexpr std::string_view TOKEN_DEPENDENCY("DEPENDENCY");
|
||||
static constexpr std::string_view TOKEN_DOMAINS("DOMAINS");
|
||||
static constexpr std::string_view TOKEN_EXCEPTIONS("EXCEPTIONS");
|
||||
static constexpr std::string_view TOKEN_FILTERS("FILTERS");
|
||||
static constexpr std::string_view TOKEN_FUNCTIONS("FUNCTIONS");
|
||||
static constexpr std::string_view TOKEN_INDEXES("INDEXES");
|
||||
static constexpr std::string_view TOKEN_INDICES("INDICES");
|
||||
static constexpr std::string_view TOKEN_GENERATORS("GENERATORS");
|
||||
static constexpr std::string_view TOKEN_GRANTS("GRANTS");
|
||||
static constexpr std::string_view TOKEN_MAPPINGS("MAPPINGS");
|
||||
static constexpr std::string_view TOKEN_PACKAGES("PACKAGES");
|
||||
static constexpr std::string_view TOKEN_PROCEDURES("PROCEDURES");
|
||||
static constexpr std::string_view TOKEN_PUBLICATIONS("PUBLICATIONS");
|
||||
static constexpr std::string_view TOKEN_ROLES("ROLES");
|
||||
static constexpr std::string_view TOKEN_SECCLASSES("SECCLASSES");
|
||||
static constexpr std::string_view TOKEN_SEQUENCES("SEQUENCES");
|
||||
static constexpr std::string_view TOKEN_SQL("SQL");
|
||||
static constexpr std::string_view TOKEN_SYSTEM("SYSTEM");
|
||||
static constexpr std::string_view TOKEN_TABLES("TABLES");
|
||||
static constexpr std::string_view TOKEN_TRIGGERS("TRIGGERS");
|
||||
static constexpr std::string_view TOKEN_USERS("USERS");
|
||||
static constexpr std::string_view TOKEN_VER("VER");
|
||||
static constexpr std::string_view TOKEN_VERSION("VERSION");
|
||||
static constexpr std::string_view TOKEN_VIEWS("VIEWS");
|
||||
static constexpr std::string_view TOKEN_WIRE_STATISTICS("WIRE_STATISTICS");
|
||||
static constexpr std::string_view TOKEN_WIRE_STATS("WIRE_STATS");
|
||||
|
||||
switch (const auto showCommandToken = lexer.getToken(); showCommandToken.type)
|
||||
{
|
||||
case Token::TYPE_EOF:
|
||||
return ShowNode();
|
||||
|
||||
case Token::TYPE_OTHER:
|
||||
{
|
||||
const auto& text = showCommandToken.processedText;
|
||||
|
||||
if (const auto parsed = parseShowOptName<ShowChecksNode>(text, TOKEN_CHECKS, 5))
|
||||
return parsed.value();
|
||||
else if (const auto parsed = parseShowOptName<ShowCollationsNode>(text, TOKEN_COLLATES, 7))
|
||||
return parsed.value();
|
||||
else if (const auto parsed = parseShowOptName<ShowCollationsNode>(text, TOKEN_COLLATIONS, 9))
|
||||
return parsed.value();
|
||||
else if (text.length() >= 7 && TOKEN_COMMENTS.find(text) == 0)
|
||||
{
|
||||
if (parseEof())
|
||||
return ShowCommentsNode();
|
||||
}
|
||||
else if (text == TOKEN_DATABASE)
|
||||
{
|
||||
if (parseEof())
|
||||
return ShowDatabaseNode();
|
||||
}
|
||||
else if (const auto parsed = parseShowOptName<ShowDependenciesNode>(text, TOKEN_DEPENDENCIES, 5))
|
||||
return parsed.value();
|
||||
else if (const auto parsed = parseShowOptName<ShowDependenciesNode>(text, TOKEN_DEPENDENCY, 5))
|
||||
return parsed.value();
|
||||
else if (const auto parsed = parseShowOptName<ShowDomainsNode>(text, TOKEN_DOMAINS, 6))
|
||||
return parsed.value();
|
||||
else if (const auto parsed = parseShowOptName<ShowExceptionsNode>(text, TOKEN_EXCEPTIONS, 5))
|
||||
return parsed.value();
|
||||
else if (const auto parsed = parseShowOptName<ShowFiltersNode>(text, TOKEN_FILTERS, 6))
|
||||
return parsed.value();
|
||||
else if (text.length() >= 4 && TOKEN_FUNCTIONS.find(text) == 0)
|
||||
{
|
||||
ShowFunctionsNode node;
|
||||
node.name = parseName();
|
||||
|
||||
if (node.name)
|
||||
{
|
||||
if (const auto token = lexer.getToken();
|
||||
token.type == Token::TYPE_OTHER && token.rawText == ".")
|
||||
{
|
||||
node.package = node.name;
|
||||
node.name = parseName();
|
||||
|
||||
if (parseEof())
|
||||
return node;
|
||||
}
|
||||
else if (token.type == Token::TYPE_EOF)
|
||||
{
|
||||
return node;
|
||||
}
|
||||
}
|
||||
else
|
||||
return node;
|
||||
}
|
||||
else if (const auto parsed = parseShowOptName<ShowIndexesNode>(text, TOKEN_INDEXES, 3))
|
||||
return parsed.value();
|
||||
else if (const auto parsed = parseShowOptName<ShowIndexesNode>(text, TOKEN_INDICES, 0))
|
||||
return parsed.value();
|
||||
else if (const auto parsed = parseShowOptName<ShowGeneratorsNode>(text, TOKEN_GENERATORS, 3))
|
||||
return parsed.value();
|
||||
else if (const auto parsed = parseShowOptName<ShowGrantsNode>(text, TOKEN_GRANTS, 5))
|
||||
return parsed.value();
|
||||
else if (const auto parsed = parseShowOptName<ShowMappingsNode>(text, TOKEN_MAPPINGS, 3))
|
||||
return parsed.value();
|
||||
else if (const auto parsed = parseShowOptName<ShowPackagesNode>(text, TOKEN_PACKAGES, 4))
|
||||
return parsed.value();
|
||||
else if (text.length() >= 4 && TOKEN_PROCEDURES.find(text) == 0)
|
||||
{
|
||||
ShowProceduresNode node;
|
||||
node.name = parseName();
|
||||
|
||||
if (node.name)
|
||||
{
|
||||
if (const auto token = lexer.getToken();
|
||||
token.type == Token::TYPE_OTHER && token.rawText == ".")
|
||||
{
|
||||
node.package = node.name;
|
||||
node.name = parseName();
|
||||
|
||||
if (parseEof())
|
||||
return node;
|
||||
}
|
||||
else if (token.type == Token::TYPE_EOF)
|
||||
{
|
||||
return node;
|
||||
}
|
||||
}
|
||||
else
|
||||
return node;
|
||||
}
|
||||
else if (const auto parsed = parseShowOptName<ShowPublicationsNode>(text, TOKEN_PUBLICATIONS, 3))
|
||||
return parsed.value();
|
||||
else if (const auto parsed = parseShowOptName<ShowRolesNode>(text, TOKEN_ROLES, 4))
|
||||
return parsed.value();
|
||||
else if (text.length() >= 6 && TOKEN_SECCLASSES.find(text) == 0)
|
||||
{
|
||||
const auto lexerPos = lexer.getPos();
|
||||
const auto token = lexer.getNameToken();
|
||||
|
||||
ShowSecClassesNode node;
|
||||
|
||||
if (!(token.type == Token::TYPE_OTHER && token.rawText == "*"))
|
||||
{
|
||||
lexer.setPos(lexerPos);
|
||||
node.name = parseName();
|
||||
|
||||
if (!node.name)
|
||||
return InvalidNode();
|
||||
}
|
||||
|
||||
const auto optDetail = parseName();
|
||||
node.detail = optDetail == "DET" || optDetail == "DETAIL";
|
||||
|
||||
if (!node.detail && optDetail)
|
||||
return InvalidNode();
|
||||
|
||||
if (parseEof())
|
||||
return node;
|
||||
}
|
||||
else if (const auto parsed = parseShowOptName<ShowGeneratorsNode>(text, TOKEN_SEQUENCES, 3))
|
||||
return parsed.value();
|
||||
else if (text == TOKEN_SQL)
|
||||
{
|
||||
if (const auto dialectToken = lexer.getToken();
|
||||
dialectToken.type == Token::TYPE_OTHER && dialectToken.processedText == "DIALECT")
|
||||
{
|
||||
if (parseEof())
|
||||
return ShowSqlDialectNode();
|
||||
}
|
||||
}
|
||||
else if (text.length() >= 3 && TOKEN_SYSTEM.find(text) == 0)
|
||||
{
|
||||
ShowSystemNode node;
|
||||
|
||||
if (const auto objectType = parseName())
|
||||
{
|
||||
const auto objectTypeText = std::string(objectType->c_str());
|
||||
|
||||
if ((objectTypeText.length() >= 7 && TOKEN_COLLATES.find(objectTypeText) == 0) ||
|
||||
(objectTypeText.length() >= 9 && TOKEN_COLLATIONS.find(objectTypeText) == 0))
|
||||
{
|
||||
node.objType = obj_collation;
|
||||
}
|
||||
else if (objectTypeText.length() >= 4 && TOKEN_FUNCTIONS.find(objectTypeText) == 0)
|
||||
node.objType = obj_udf;
|
||||
else if (objectTypeText.length() >= 5 && TOKEN_TABLES.find(objectTypeText) == 0)
|
||||
node.objType = obj_relation;
|
||||
else if (objectTypeText.length() >= 4 && TOKEN_ROLES.find(objectTypeText) == 0)
|
||||
node.objType = obj_sql_role;
|
||||
else if (objectTypeText.length() >= 4 && TOKEN_PROCEDURES.find(objectTypeText) == 0)
|
||||
node.objType = obj_procedure;
|
||||
else if (objectTypeText.length() >= 4 && TOKEN_PACKAGES.find(objectTypeText) == 0)
|
||||
node.objType = obj_package_header;
|
||||
else if (objectTypeText.length() >= 3 && TOKEN_PUBLICATIONS.find(objectTypeText) == 0)
|
||||
node.objType = obj_publication;
|
||||
else
|
||||
return InvalidNode();
|
||||
|
||||
if (!parseEof())
|
||||
return InvalidNode();
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
else if (const auto parsed = parseShowOptName<ShowTablesNode>(text, TOKEN_TABLES, 5))
|
||||
return parsed.value();
|
||||
else if (const auto parsed = parseShowOptName<ShowTriggersNode>(text, TOKEN_TRIGGERS, 4))
|
||||
return parsed.value();
|
||||
else if (text == TOKEN_USERS)
|
||||
{
|
||||
if (parseEof())
|
||||
return ShowUsersNode();
|
||||
}
|
||||
else if (text == TOKEN_VER || text == TOKEN_VERSION)
|
||||
{
|
||||
if (parseEof())
|
||||
return ShowVersionNode();
|
||||
}
|
||||
else if (const auto parsed = parseShowOptName<ShowViewsNode>(text, TOKEN_VIEWS, 4))
|
||||
return parsed.value();
|
||||
else if (text.length() >= 9 && TOKEN_WIRE_STATISTICS.find(text) == 0 ||
|
||||
text == TOKEN_WIRE_STATS)
|
||||
{
|
||||
if (parseEof())
|
||||
return ShowWireStatsNode();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return InvalidNode();
|
||||
}
|
||||
|
||||
template <typename Node>
|
||||
std::optional<FrontendParser::AnyShowNode> FrontendParser::parseShowOptName(std::string_view showCommand,
|
||||
std::string_view testCommand, unsigned testCommandMinLen)
|
||||
{
|
||||
if (showCommand == testCommand ||
|
||||
(testCommandMinLen && showCommand.length() >= testCommandMinLen &&
|
||||
testCommand.find(showCommand) == 0))
|
||||
{
|
||||
Node node;
|
||||
node.name = parseName();
|
||||
|
||||
if (!parseEof())
|
||||
return InvalidNode();
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> FrontendParser::parseUtilEof()
|
||||
{
|
||||
const auto startIt = lexer.getPos();
|
||||
auto lastPosIt = startIt;
|
||||
bool first = true;
|
||||
|
||||
do
|
||||
{
|
||||
const auto token = lexer.getToken();
|
||||
|
||||
if (token.type == Token::TYPE_EOF)
|
||||
{
|
||||
if (first)
|
||||
return std::nullopt;
|
||||
|
||||
return FrontendLexer::trim(std::string(startIt, lastPosIt));
|
||||
}
|
||||
|
||||
lastPosIt = lexer.getPos();
|
||||
first = false;
|
||||
} while (true);
|
||||
|
||||
return std::nullopt;
|
||||
}
|
286
src/isql/FrontendParser.h
Normal file
286
src/isql/FrontendParser.h
Normal file
@ -0,0 +1,286 @@
|
||||
/*
|
||||
* The contents of this file are subject to the Initial
|
||||
* Developer's Public License Version 1.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.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl.
|
||||
*
|
||||
* Software distributed under the License is distributed AS IS,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing rights
|
||||
* and limitations under the License.
|
||||
*
|
||||
* The Original Code was created by Adriano dos Santos Fernandes
|
||||
* for the Firebird Open Source RDBMS project.
|
||||
*
|
||||
* Copyright (c) 2024 Adriano dos Santos Fernandes <adrianosf at gmail.com>
|
||||
* and all contributors signed below.
|
||||
*
|
||||
* All Rights Reserved.
|
||||
* Contributor(s): ______________________________________.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef FB_ISQL_FRONTEND_PARSER_H
|
||||
#define FB_ISQL_FRONTEND_PARSER_H
|
||||
|
||||
#include "../isql/FrontendLexer.h"
|
||||
#include "../jrd/obj.h"
|
||||
#include "../common/classes/MetaString.h"
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
class FrontendParser
|
||||
{
|
||||
private:
|
||||
using Token = FrontendLexer::Token;
|
||||
|
||||
public:
|
||||
struct Options
|
||||
{
|
||||
bool schemaAsDatabase = false;
|
||||
};
|
||||
|
||||
struct InvalidNode {};
|
||||
|
||||
struct AddNode { Firebird::MetaString tableName; };
|
||||
struct BlobDumpViewNode { ISC_QUAD blobId; std::optional<std::string> file; };
|
||||
struct ConnectNode { std::vector<Token> args; };
|
||||
struct CopyNode { Firebird::MetaString source; Firebird::MetaString destination; std::string database; };
|
||||
struct CreateDatabaseNode { std::vector<Token> args; };
|
||||
struct DropDatabaseNode {};
|
||||
struct EditNode { std::optional<std::string> file; };
|
||||
struct ExitNode {};
|
||||
struct ExplainNode { std::string query; };
|
||||
struct HelpNode { std::optional<std::string> command; };
|
||||
struct InputNode { std::string file; };
|
||||
struct OutputNode { std::optional<std::string> file; };
|
||||
struct QuitNode {};
|
||||
struct ShellNode { std::optional<std::string> command; };
|
||||
|
||||
struct SetNode {};
|
||||
struct SetAutoDdlNode { std::string arg; };
|
||||
struct SetAutoTermNode { std::string arg; };
|
||||
struct SetBailNode { std::string arg; };
|
||||
struct SetBlobDisplayNode { std::string arg; };
|
||||
struct SetBulkInsertNode { std::string statement; };
|
||||
struct SetCountNode { std::string arg; };
|
||||
struct SetEchoNode { std::string arg; };
|
||||
struct SetExecPathDisplayNode { std::string arg; };
|
||||
struct SetExplainNode { std::string arg; };
|
||||
struct SetHeadingNode { std::string arg; };
|
||||
struct SetKeepTranParamsNode { std::string arg; };
|
||||
struct SetListNode { std::string arg; };
|
||||
struct SetLocalTimeoutNode { std::string arg; };
|
||||
struct SetMaxRowsNode { std::string arg; };
|
||||
struct SetNamesNode { std::optional<Firebird::MetaString> name; };
|
||||
struct SetPerTableStatsNode { std::string arg; };
|
||||
struct SetPlanNode { std::string arg; };
|
||||
struct SetPlanOnlyNode { std::string arg; };
|
||||
struct SetSqldaDisplayNode { std::string arg; };
|
||||
struct SetSqlDialectNode { std::string arg; };
|
||||
struct SetStatsNode { std::string arg; };
|
||||
struct SetTermNode { std::string arg; };
|
||||
struct SetTimeNode { std::string arg; };
|
||||
struct SetTransactionNode { std::string statement; };
|
||||
struct SetWarningsNode { std::string arg; };
|
||||
struct SetWidthNode { std::string column; std::string width; };
|
||||
struct SetWireStatsNode { std::string arg; };
|
||||
|
||||
struct ShowNode {};
|
||||
struct ShowChecksNode { std::optional<Firebird::MetaString> name; };
|
||||
struct ShowCollationsNode { std::optional<Firebird::MetaString> name; };
|
||||
struct ShowCommentsNode {};
|
||||
struct ShowDatabaseNode {};
|
||||
struct ShowDomainsNode { std::optional<Firebird::MetaString> name; };
|
||||
struct ShowDependenciesNode { std::optional<Firebird::MetaString> name; };
|
||||
struct ShowExceptionsNode { std::optional<Firebird::MetaString> name; };
|
||||
struct ShowFiltersNode { std::optional<Firebird::MetaString> name; };
|
||||
struct ShowFunctionsNode { std::optional<Firebird::MetaString> name; std::optional<Firebird::MetaString> package; };
|
||||
struct ShowGeneratorsNode { std::optional<Firebird::MetaString> name; };
|
||||
struct ShowGrantsNode { std::optional<Firebird::MetaString> name; };
|
||||
struct ShowIndexesNode { std::optional<Firebird::MetaString> name; };
|
||||
struct ShowMappingsNode { std::optional<Firebird::MetaString> name; };
|
||||
struct ShowPackagesNode { std::optional<Firebird::MetaString> name; };
|
||||
struct ShowProceduresNode { std::optional<Firebird::MetaString> name; std::optional<Firebird::MetaString> package; };
|
||||
struct ShowPublicationsNode { std::optional<Firebird::MetaString> name; };
|
||||
struct ShowRolesNode { std::optional<Firebird::MetaString> name; };
|
||||
struct ShowSecClassesNode { std::optional<Firebird::MetaString> name; bool detail = false; };
|
||||
struct ShowSqlDialectNode {};
|
||||
struct ShowSystemNode { std::optional<ObjectType> objType; };
|
||||
struct ShowTablesNode { std::optional<Firebird::MetaString> name; };
|
||||
struct ShowTriggersNode { std::optional<Firebird::MetaString> name; };
|
||||
struct ShowUsersNode {};
|
||||
struct ShowVersionNode {};
|
||||
struct ShowViewsNode { std::optional<Firebird::MetaString> name; };
|
||||
struct ShowWireStatsNode {};
|
||||
|
||||
using AnySetNode = std::variant<
|
||||
SetNode,
|
||||
|
||||
SetAutoDdlNode,
|
||||
SetAutoTermNode,
|
||||
SetBailNode,
|
||||
SetBlobDisplayNode,
|
||||
SetBulkInsertNode,
|
||||
SetCountNode,
|
||||
SetEchoNode,
|
||||
SetExecPathDisplayNode,
|
||||
SetExplainNode,
|
||||
SetHeadingNode,
|
||||
SetKeepTranParamsNode,
|
||||
SetListNode,
|
||||
SetLocalTimeoutNode,
|
||||
SetMaxRowsNode,
|
||||
SetNamesNode,
|
||||
SetPerTableStatsNode,
|
||||
SetPlanNode,
|
||||
SetPlanOnlyNode,
|
||||
SetSqldaDisplayNode,
|
||||
SetSqlDialectNode,
|
||||
SetStatsNode,
|
||||
SetTermNode,
|
||||
SetTimeNode,
|
||||
SetTransactionNode,
|
||||
SetWarningsNode,
|
||||
SetWidthNode,
|
||||
SetWireStatsNode,
|
||||
|
||||
InvalidNode
|
||||
>;
|
||||
|
||||
using AnyShowNode = std::variant<
|
||||
ShowNode,
|
||||
|
||||
ShowChecksNode,
|
||||
ShowCollationsNode,
|
||||
ShowCommentsNode,
|
||||
ShowDatabaseNode,
|
||||
ShowDomainsNode,
|
||||
ShowDependenciesNode,
|
||||
ShowExceptionsNode,
|
||||
ShowFiltersNode,
|
||||
ShowFunctionsNode,
|
||||
ShowGeneratorsNode,
|
||||
ShowGrantsNode,
|
||||
ShowIndexesNode,
|
||||
ShowMappingsNode,
|
||||
ShowPackagesNode,
|
||||
ShowProceduresNode,
|
||||
ShowPublicationsNode,
|
||||
ShowRolesNode,
|
||||
ShowSecClassesNode,
|
||||
ShowSqlDialectNode,
|
||||
ShowSystemNode,
|
||||
ShowTablesNode,
|
||||
ShowTriggersNode,
|
||||
ShowUsersNode,
|
||||
ShowVersionNode,
|
||||
ShowViewsNode,
|
||||
ShowWireStatsNode,
|
||||
|
||||
InvalidNode
|
||||
>;
|
||||
|
||||
using AnyNode = std::variant<
|
||||
AddNode,
|
||||
BlobDumpViewNode,
|
||||
ConnectNode,
|
||||
CopyNode,
|
||||
CreateDatabaseNode,
|
||||
DropDatabaseNode,
|
||||
EditNode,
|
||||
ExitNode,
|
||||
ExplainNode,
|
||||
HelpNode,
|
||||
InputNode,
|
||||
OutputNode,
|
||||
QuitNode,
|
||||
ShellNode,
|
||||
|
||||
AnySetNode,
|
||||
AnyShowNode,
|
||||
|
||||
InvalidNode
|
||||
>;
|
||||
|
||||
template <typename>
|
||||
static inline constexpr bool AlwaysFalseV = false;
|
||||
|
||||
private:
|
||||
FrontendParser(std::string_view statement, const Options& aOptions)
|
||||
: lexer(statement),
|
||||
options(aOptions)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
FrontendParser(const FrontendParser&) = delete;
|
||||
FrontendParser& operator=(const FrontendParser&) = delete;
|
||||
|
||||
public:
|
||||
static AnyNode parse(std::string_view statement, const Options& options)
|
||||
{
|
||||
try
|
||||
{
|
||||
FrontendParser parser(statement, options);
|
||||
return parser.internalParse();
|
||||
}
|
||||
catch (const FrontendLexer::IncompleteTokenError&)
|
||||
{
|
||||
return InvalidNode();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
AnyNode internalParse();
|
||||
AnySetNode parseSet();
|
||||
|
||||
template <typename Node>
|
||||
std::optional<AnySetNode> parseSet(std::string_view setCommand,
|
||||
std::string_view testCommand, unsigned testCommandMinLen = 0, bool useProcessedText = true);
|
||||
|
||||
AnyShowNode parseShow();
|
||||
|
||||
template <typename Node>
|
||||
std::optional<AnyShowNode> parseShowOptName(std::string_view showCommand,
|
||||
std::string_view testCommand, unsigned testCommandMinLen = 0);
|
||||
|
||||
bool parseEof()
|
||||
{
|
||||
return lexer.getToken().type == Token::TYPE_EOF;
|
||||
}
|
||||
|
||||
std::optional<Firebird::MetaString> parseName()
|
||||
{
|
||||
const auto token = lexer.getNameToken();
|
||||
|
||||
if (token.type != Token::TYPE_EOF)
|
||||
return Firebird::MetaString(token.processedText.c_str());
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> parseFileName()
|
||||
{
|
||||
const auto token = lexer.getToken();
|
||||
|
||||
if (token.type == Token::TYPE_STRING || token.type == Token::TYPE_META_STRING)
|
||||
return token.processedText;
|
||||
else if (token.type != Token::TYPE_EOF)
|
||||
return token.rawText;
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> parseUtilEof();
|
||||
|
||||
private:
|
||||
FrontendLexer lexer;
|
||||
const Options options;
|
||||
};
|
||||
|
||||
#endif // FB_ISQL_FRONTEND_PARSER_H
|
1358
src/isql/isql.epp
1358
src/isql/isql.epp
File diff suppressed because it is too large
Load Diff
@ -37,6 +37,7 @@
|
||||
#include "../common/utils_proto.h"
|
||||
#include <stdarg.h>
|
||||
|
||||
using namespace Firebird;
|
||||
using MsgFormat::SafeArg;
|
||||
|
||||
|
||||
@ -148,6 +149,16 @@ void IUTILS_msg_get(USHORT number, USHORT size, TEXT* msg, const SafeArg& args)
|
||||
fb_msg_format(NULL, ISQL_MSG_FAC, number, size, msg, args);
|
||||
}
|
||||
|
||||
|
||||
string IUTILS_name_to_string(const MetaString& name)
|
||||
{
|
||||
if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
|
||||
return name.toQuotedString();
|
||||
else
|
||||
return name.c_str();
|
||||
}
|
||||
|
||||
|
||||
void IUTILS_printf(FILE* fp, const char* buffer)
|
||||
{
|
||||
/**************************************
|
||||
|
@ -24,6 +24,7 @@
|
||||
#ifndef ISQL_IUTILS_PROTO_H
|
||||
#define ISQL_IUTILS_PROTO_H
|
||||
|
||||
#include "../common/classes/MetaString.h"
|
||||
#include "../common/classes/SafeArg.h"
|
||||
#include <stdio.h>
|
||||
|
||||
@ -33,6 +34,7 @@ void IUTILS_msg_get(USHORT number, TEXT* msg,
|
||||
const MsgFormat::SafeArg& args = MsgFormat::SafeArg());
|
||||
void IUTILS_msg_get(USHORT number, USHORT size, TEXT* msg,
|
||||
const MsgFormat::SafeArg& args = MsgFormat::SafeArg());
|
||||
Firebird::string IUTILS_name_to_string(const Firebird::MetaString& name);
|
||||
void IUTILS_printf(FILE*, const char*);
|
||||
void IUTILS_printf2(FILE*, const char*, ...);
|
||||
void IUTILS_put_errmsg(USHORT number, const MsgFormat::SafeArg& args);
|
||||
|
1425
src/isql/show.epp
1425
src/isql/show.epp
File diff suppressed because it is too large
Load Diff
@ -26,6 +26,7 @@
|
||||
|
||||
#include "../common/classes/fb_string.h"
|
||||
#include <firebird/Interface.h>
|
||||
#include "../isql/FrontendParser.h"
|
||||
#include "../jrd/obj.h"
|
||||
|
||||
void SHOW_comments(bool force);
|
||||
@ -36,7 +37,7 @@ void SHOW_grant_roles (const SCHAR*, bool*);
|
||||
void SHOW_grant_roles2 (const SCHAR*, bool*, const TEXT*, bool);
|
||||
void SHOW_print_metadata_text_blob(FILE*, ISC_QUAD*, bool escape_squote = false,
|
||||
bool avoid_end_in_single_line_comment = false);
|
||||
processing_state SHOW_metadata(const SCHAR* const*, SCHAR**);
|
||||
processing_state SHOW_metadata(const FrontendParser::AnyShowNode& node);
|
||||
void SHOW_read_owner();
|
||||
const Firebird::string SHOW_trigger_action(SINT64);
|
||||
processing_state SHOW_maps(bool extract, const SCHAR* map_name);
|
||||
|
621
src/isql/tests/FrontendParserTest.cpp
Normal file
621
src/isql/tests/FrontendParserTest.cpp
Normal file
@ -0,0 +1,621 @@
|
||||
/*
|
||||
* The contents of this file are subject to the Initial
|
||||
* Developer's Public License Version 1.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.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl.
|
||||
*
|
||||
* Software distributed under the License is distributed AS IS,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing rights
|
||||
* and limitations under the License.
|
||||
*
|
||||
* The Original Code was created by Adriano dos Santos Fernandes
|
||||
* for the Firebird Open Source RDBMS project.
|
||||
*
|
||||
* Copyright (c) 2024 Adriano dos Santos Fernandes <adrianosf at gmail.com>
|
||||
* and all contributors signed below.
|
||||
*
|
||||
* All Rights Reserved.
|
||||
* Contributor(s): ______________________________________.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "firebird.h"
|
||||
#include "boost/test/unit_test.hpp"
|
||||
#include "../FrontendParser.h"
|
||||
#include <variant>
|
||||
|
||||
using namespace Firebird;
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(ISqlSuite)
|
||||
BOOST_AUTO_TEST_SUITE(FrontendParserSuite)
|
||||
BOOST_AUTO_TEST_SUITE(FrontendParserTests)
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(ParseCommandTest)
|
||||
{
|
||||
const FrontendParser::Options parserOptions;
|
||||
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"add", parserOptions)));
|
||||
BOOST_TEST((std::get<FrontendParser::AddNode>(FrontendParser::parse(
|
||||
"add table1", parserOptions)).tableName == "TABLE1"));
|
||||
BOOST_TEST((std::get<FrontendParser::AddNode>(FrontendParser::parse(
|
||||
"add \"table2\"", parserOptions)).tableName == "table2"));
|
||||
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"blobdump", parserOptions)));
|
||||
|
||||
{
|
||||
const auto blobDump1 = std::get<FrontendParser::BlobDumpViewNode>(FrontendParser::parse(
|
||||
"blobdump 1:2 /tmp/blob.txt", parserOptions));
|
||||
BOOST_TEST(blobDump1.blobId.gds_quad_high == 1);
|
||||
BOOST_TEST(blobDump1.blobId.gds_quad_low == 2u);
|
||||
BOOST_TEST(blobDump1.file.value() == "/tmp/blob.txt");
|
||||
|
||||
const auto blobDump2 = std::get<FrontendParser::BlobDumpViewNode>(FrontendParser::parse(
|
||||
"blobdump 1:2 'C:\\A dir\\blob.txt'", parserOptions));
|
||||
BOOST_TEST((blobDump2.file.value() == "C:\\A dir\\blob.txt"));
|
||||
}
|
||||
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"blobview", parserOptions)));
|
||||
|
||||
{
|
||||
const auto blobView1 = std::get<FrontendParser::BlobDumpViewNode>(FrontendParser::parse(
|
||||
"blobview 1:2", parserOptions));
|
||||
BOOST_TEST(blobView1.blobId.gds_quad_high == 1);
|
||||
BOOST_TEST(blobView1.blobId.gds_quad_low == 2u);
|
||||
BOOST_TEST(!blobView1.file);
|
||||
}
|
||||
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"connect", parserOptions)));
|
||||
|
||||
{
|
||||
const auto connect1 = std::get<FrontendParser::ConnectNode>(FrontendParser::parse(
|
||||
"connect 'test.fdb'", parserOptions));
|
||||
BOOST_TEST(connect1.args[0].getProcessedString() == "test.fdb");
|
||||
|
||||
const auto connect2 = std::get<FrontendParser::ConnectNode>(FrontendParser::parse(
|
||||
"connect 'test.fdb' user user", parserOptions));
|
||||
BOOST_TEST(connect2.args.size() == 3u);
|
||||
}
|
||||
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"copy", parserOptions)));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"copy source destination", parserOptions)));
|
||||
|
||||
{
|
||||
const auto copy1 = std::get<FrontendParser::CopyNode>(FrontendParser::parse(
|
||||
"copy source \"destination\" localhost:/tmp/database.fdb", parserOptions));
|
||||
BOOST_TEST((copy1.source == "SOURCE"));
|
||||
BOOST_TEST((copy1.destination == "destination"));
|
||||
BOOST_TEST(copy1.database == "localhost:/tmp/database.fdb");
|
||||
}
|
||||
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"create", parserOptions)));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"create database", parserOptions)));
|
||||
|
||||
{
|
||||
const auto createDatabase1 = std::get<FrontendParser::CreateDatabaseNode>(FrontendParser::parse(
|
||||
"create database 'test.fdb'", parserOptions));
|
||||
BOOST_TEST(createDatabase1.args[0].getProcessedString() == "test.fdb");
|
||||
|
||||
const auto createDatabase2 = std::get<FrontendParser::CreateDatabaseNode>(FrontendParser::parse(
|
||||
"create database 'test.fdb' user user", parserOptions));
|
||||
BOOST_TEST(createDatabase2.args.size() == 3u);
|
||||
}
|
||||
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"drop database x", parserOptions)));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::DropDatabaseNode>(FrontendParser::parse(
|
||||
"drop database", parserOptions)));
|
||||
|
||||
BOOST_TEST(!std::get<FrontendParser::EditNode>(FrontendParser::parse(
|
||||
"edit", parserOptions)).file);
|
||||
BOOST_TEST(std::get<FrontendParser::EditNode>(FrontendParser::parse(
|
||||
"edit /tmp/file.sql", parserOptions)).file.value() == "/tmp/file.sql");
|
||||
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"exit x", parserOptions)));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::ExitNode>(FrontendParser::parse(
|
||||
"exit", parserOptions)));
|
||||
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"explain", parserOptions)));
|
||||
BOOST_TEST(std::get<FrontendParser::ExplainNode>(FrontendParser::parse(
|
||||
"explain select 1 from rdb$database", parserOptions)).query == "select 1 from rdb$database");
|
||||
|
||||
BOOST_TEST(!std::get<FrontendParser::HelpNode>(FrontendParser::parse(
|
||||
"help", parserOptions)).command.has_value());
|
||||
BOOST_TEST(std::get<FrontendParser::HelpNode>(FrontendParser::parse(
|
||||
"help set", parserOptions)).command.value() == "SET");
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"help set x", parserOptions)));
|
||||
|
||||
BOOST_TEST(!std::get<FrontendParser::HelpNode>(FrontendParser::parse(
|
||||
"?", parserOptions)).command.has_value());
|
||||
BOOST_TEST(std::get<FrontendParser::HelpNode>(FrontendParser::parse(
|
||||
"? set", parserOptions)).command.value() == "SET");
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"? set x", parserOptions)));
|
||||
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"input", parserOptions)));
|
||||
BOOST_TEST(std::get<FrontendParser::InputNode>(FrontendParser::parse(
|
||||
"input /tmp/file.sql", parserOptions)).file == "/tmp/file.sql");
|
||||
|
||||
BOOST_TEST(!std::get<FrontendParser::OutputNode>(FrontendParser::parse(
|
||||
"output", parserOptions)).file);
|
||||
BOOST_TEST(std::get<FrontendParser::OutputNode>(FrontendParser::parse(
|
||||
"output /tmp/file.txt", parserOptions)).file.value() == "/tmp/file.txt");
|
||||
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::QuitNode>(FrontendParser::parse(
|
||||
"quit", parserOptions)));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"quit x", parserOptions)));
|
||||
|
||||
BOOST_TEST(!std::get<FrontendParser::ShellNode>(FrontendParser::parse(
|
||||
"shell", parserOptions)).command);
|
||||
BOOST_TEST(std::get<FrontendParser::ShellNode>(FrontendParser::parse(
|
||||
"shell ls -l /tmp", parserOptions)).command.value() == "ls -l /tmp");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(ParseSetTest)
|
||||
{
|
||||
const FrontendParser::Options parserOptions;
|
||||
|
||||
const auto parseSet = [&](const std::string_view text) {
|
||||
return std::get<FrontendParser::AnySetNode>(FrontendParser::parse(text, parserOptions));
|
||||
};
|
||||
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"\"set\"", parserOptions)));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set x", parserOptions)));
|
||||
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::SetNode>(parseSet("set")));
|
||||
|
||||
BOOST_TEST(std::get<FrontendParser::SetAutoDdlNode>(parseSet(
|
||||
"set auto")).arg.empty());
|
||||
BOOST_TEST((std::get<FrontendParser::SetAutoDdlNode>(parseSet(
|
||||
"set auto on")).arg == "ON"));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set auto off x", parserOptions)));
|
||||
|
||||
BOOST_TEST(std::get<FrontendParser::SetAutoDdlNode>(parseSet(
|
||||
"set autoddl")).arg.empty());
|
||||
BOOST_TEST((std::get<FrontendParser::SetAutoDdlNode>(parseSet(
|
||||
"set autoddl on")).arg == "ON"));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set autoddl off x", parserOptions)));
|
||||
|
||||
BOOST_TEST(std::get<FrontendParser::SetAutoTermNode>(parseSet(
|
||||
"set autoterm")).arg.empty());
|
||||
BOOST_TEST((std::get<FrontendParser::SetAutoTermNode>(parseSet(
|
||||
"set autoterm on")).arg == "ON"));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set autoterm off x", parserOptions)));
|
||||
|
||||
BOOST_TEST(std::get<FrontendParser::SetBailNode>(parseSet(
|
||||
"set bail")).arg.empty());
|
||||
BOOST_TEST((std::get<FrontendParser::SetBailNode>(parseSet(
|
||||
"set bail on")).arg == "ON"));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set bail off x", parserOptions)));
|
||||
|
||||
BOOST_TEST((std::get<FrontendParser::SetBulkInsertNode>(parseSet(
|
||||
"set bulk_insert insert into mytable (a, b) values (1, ?)")).statement ==
|
||||
"insert into mytable (a, b) values (1, ?)"));
|
||||
|
||||
BOOST_TEST(std::get<FrontendParser::SetBlobDisplayNode>(parseSet(
|
||||
"set blob")).arg.empty());
|
||||
BOOST_TEST((std::get<FrontendParser::SetBlobDisplayNode>(parseSet(
|
||||
"set blob on")).arg == "ON"));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set blob off x", parserOptions)));
|
||||
|
||||
BOOST_TEST(std::get<FrontendParser::SetBlobDisplayNode>(parseSet(
|
||||
"set blobdisplay")).arg.empty());
|
||||
BOOST_TEST((std::get<FrontendParser::SetBlobDisplayNode>(parseSet(
|
||||
"set blobdisplay on")).arg == "ON"));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set blobdisplay off x", parserOptions)));
|
||||
|
||||
BOOST_TEST(std::get<FrontendParser::SetCountNode>(parseSet(
|
||||
"set count")).arg.empty());
|
||||
BOOST_TEST((std::get<FrontendParser::SetCountNode>(parseSet(
|
||||
"set count on")).arg == "ON"));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set count off x", parserOptions)));
|
||||
|
||||
BOOST_TEST(std::get<FrontendParser::SetEchoNode>(parseSet(
|
||||
"set echo")).arg.empty());
|
||||
BOOST_TEST((std::get<FrontendParser::SetEchoNode>(parseSet(
|
||||
"set echo on")).arg == "ON"));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set echo off x", parserOptions)));
|
||||
|
||||
BOOST_TEST(std::get<FrontendParser::SetExecPathDisplayNode>(parseSet(
|
||||
"set exec_path_display")).arg.empty());
|
||||
BOOST_TEST((std::get<FrontendParser::SetExecPathDisplayNode>(parseSet(
|
||||
"set exec_path_display blr")).arg == "BLR"));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set exec_path_display off x", parserOptions)));
|
||||
|
||||
BOOST_TEST(std::get<FrontendParser::SetExplainNode>(parseSet(
|
||||
"set explain")).arg.empty());
|
||||
BOOST_TEST((std::get<FrontendParser::SetExplainNode>(parseSet(
|
||||
"set explain on")).arg == "ON"));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set explain off x", parserOptions)));
|
||||
|
||||
BOOST_TEST(std::get<FrontendParser::SetHeadingNode>(parseSet(
|
||||
"set heading")).arg.empty());
|
||||
BOOST_TEST((std::get<FrontendParser::SetHeadingNode>(parseSet(
|
||||
"set heading on")).arg == "ON"));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set heading off x", parserOptions)));
|
||||
|
||||
BOOST_TEST(std::get<FrontendParser::SetKeepTranParamsNode>(parseSet(
|
||||
"set keep_tran")).arg.empty());
|
||||
BOOST_TEST((std::get<FrontendParser::SetKeepTranParamsNode>(parseSet(
|
||||
"set keep_tran on")).arg == "ON"));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set keep_tran off x", parserOptions)));
|
||||
|
||||
BOOST_TEST(std::get<FrontendParser::SetKeepTranParamsNode>(parseSet(
|
||||
"set keep_tran_params")).arg.empty());
|
||||
BOOST_TEST((std::get<FrontendParser::SetKeepTranParamsNode>(parseSet(
|
||||
"set keep_tran_params on")).arg == "ON"));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set keep_tran_params off x", parserOptions)));
|
||||
|
||||
BOOST_TEST(std::get<FrontendParser::SetListNode>(parseSet(
|
||||
"set list")).arg.empty());
|
||||
BOOST_TEST((std::get<FrontendParser::SetListNode>(parseSet(
|
||||
"set list on")).arg == "ON"));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set list off x", parserOptions)));
|
||||
|
||||
BOOST_TEST(std::get<FrontendParser::SetLocalTimeoutNode>(parseSet(
|
||||
"set local_timeout")).arg.empty());
|
||||
BOOST_TEST((std::get<FrontendParser::SetLocalTimeoutNode>(parseSet(
|
||||
"set local_timeout 80")).arg == "80"));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set local_timeout 90 x", parserOptions)));
|
||||
|
||||
BOOST_TEST(std::get<FrontendParser::SetMaxRowsNode>(parseSet(
|
||||
"set maxrows")).arg.empty());
|
||||
BOOST_TEST((std::get<FrontendParser::SetMaxRowsNode>(parseSet(
|
||||
"set maxrows 80")).arg == "80"));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set maxrows 90 x", parserOptions)));
|
||||
|
||||
BOOST_TEST(!std::get<FrontendParser::SetNamesNode>(parseSet(
|
||||
"set names")).name.has_value());
|
||||
BOOST_TEST((std::get<FrontendParser::SetNamesNode>(parseSet(
|
||||
"set names utf8")).name == "UTF8"));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set names utf8 x", parserOptions)));
|
||||
|
||||
BOOST_TEST(std::get<FrontendParser::SetPerTableStatsNode>(parseSet(
|
||||
"set per_tab")).arg.empty());
|
||||
BOOST_TEST((std::get<FrontendParser::SetPerTableStatsNode>(parseSet(
|
||||
"set per_tab on")).arg == "ON"));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set per_tab off x", parserOptions)));
|
||||
|
||||
BOOST_TEST(std::get<FrontendParser::SetPerTableStatsNode>(parseSet(
|
||||
"set per_table_stats")).arg.empty());
|
||||
BOOST_TEST((std::get<FrontendParser::SetPerTableStatsNode>(parseSet(
|
||||
"set per_table_stats on")).arg == "ON"));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set per_table_stats off x", parserOptions)));
|
||||
|
||||
BOOST_TEST(std::get<FrontendParser::SetPlanNode>(parseSet(
|
||||
"set plan")).arg.empty());
|
||||
BOOST_TEST((std::get<FrontendParser::SetPlanNode>(parseSet(
|
||||
"set plan on")).arg == "ON"));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set plan off x", parserOptions)));
|
||||
|
||||
BOOST_TEST(std::get<FrontendParser::SetPlanOnlyNode>(parseSet(
|
||||
"set planonly")).arg.empty());
|
||||
BOOST_TEST((std::get<FrontendParser::SetPlanOnlyNode>(parseSet(
|
||||
"set planonly on")).arg == "ON"));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set planonly off x", parserOptions)));
|
||||
|
||||
BOOST_TEST(std::get<FrontendParser::SetMaxRowsNode>(parseSet(
|
||||
"set rowcount")).arg.empty());
|
||||
BOOST_TEST((std::get<FrontendParser::SetMaxRowsNode>(parseSet(
|
||||
"set rowcount 80")).arg == "80"));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set rowcount 90 x", parserOptions)));
|
||||
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set sql", parserOptions)));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set sql dialect", parserOptions)));
|
||||
BOOST_TEST((std::get<FrontendParser::SetSqlDialectNode>(parseSet(
|
||||
"set sql dialect 3")).arg == "3"));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set sql dialect 3 x", parserOptions)));
|
||||
|
||||
BOOST_TEST(std::get<FrontendParser::SetSqldaDisplayNode>(parseSet(
|
||||
"set sqlda_display")).arg.empty());
|
||||
BOOST_TEST((std::get<FrontendParser::SetSqldaDisplayNode>(parseSet(
|
||||
"set sqlda_display on")).arg == "ON"));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set sqlda_display off x", parserOptions)));
|
||||
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set sta", parserOptions)));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set sta on", parserOptions)));
|
||||
BOOST_TEST(std::get<FrontendParser::SetStatsNode>(parseSet(
|
||||
"set stat")).arg.empty());
|
||||
BOOST_TEST((std::get<FrontendParser::SetStatsNode>(parseSet(
|
||||
"set stat on")).arg == "ON"));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set stat off x", parserOptions)));
|
||||
|
||||
BOOST_TEST(std::get<FrontendParser::SetStatsNode>(parseSet(
|
||||
"set stats")).arg.empty());
|
||||
BOOST_TEST((std::get<FrontendParser::SetStatsNode>(parseSet(
|
||||
"set stats on")).arg == "ON"));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set stats off x", parserOptions)));
|
||||
|
||||
BOOST_TEST(std::get<FrontendParser::SetTermNode>(parseSet(
|
||||
"set term")).arg.empty());
|
||||
BOOST_TEST((std::get<FrontendParser::SetTermNode>(parseSet(
|
||||
"set term !")).arg == "!"));
|
||||
BOOST_TEST((std::get<FrontendParser::SetTermNode>(parseSet(
|
||||
"set term Go")).arg == "Go"));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set term a b", parserOptions)));
|
||||
|
||||
BOOST_TEST(std::get<FrontendParser::SetTermNode>(parseSet(
|
||||
"set terminator")).arg.empty());
|
||||
BOOST_TEST((std::get<FrontendParser::SetTermNode>(parseSet(
|
||||
"set terminator !")).arg == "!"));
|
||||
BOOST_TEST((std::get<FrontendParser::SetTermNode>(parseSet(
|
||||
"set terminator Go")).arg == "Go"));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set terminator a b", parserOptions)));
|
||||
|
||||
BOOST_TEST(std::get<FrontendParser::SetTimeNode>(parseSet(
|
||||
"set time")).arg.empty());
|
||||
BOOST_TEST((std::get<FrontendParser::SetTimeNode>(parseSet(
|
||||
"set time on")).arg == "ON"));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set time off x", parserOptions)));
|
||||
|
||||
BOOST_TEST(std::get<FrontendParser::SetTransactionNode>(parseSet(
|
||||
"set transaction")).statement == "set transaction");
|
||||
BOOST_TEST(std::get<FrontendParser::SetTransactionNode>(parseSet(
|
||||
"set transaction read committed")).statement == "set transaction read committed");
|
||||
|
||||
BOOST_TEST(std::get<FrontendParser::SetWarningsNode>(parseSet(
|
||||
"set warning")).arg.empty());
|
||||
BOOST_TEST((std::get<FrontendParser::SetWarningsNode>(parseSet(
|
||||
"set warning on")).arg == "ON"));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set warning off x", parserOptions)));
|
||||
|
||||
BOOST_TEST(std::get<FrontendParser::SetWarningsNode>(parseSet(
|
||||
"set warnings")).arg.empty());
|
||||
BOOST_TEST((std::get<FrontendParser::SetWarningsNode>(parseSet(
|
||||
"set warnings on")).arg == "ON"));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set warnings off x", parserOptions)));
|
||||
|
||||
BOOST_TEST(std::get<FrontendParser::SetWarningsNode>(parseSet(
|
||||
"set wng")).arg.empty());
|
||||
BOOST_TEST((std::get<FrontendParser::SetWarningsNode>(parseSet(
|
||||
"set wng on")).arg == "ON"));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set wng off x", parserOptions)));
|
||||
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set width", parserOptions)));
|
||||
BOOST_TEST((std::get<FrontendParser::SetWidthNode>(parseSet(
|
||||
"set width x")).column == "X"));
|
||||
BOOST_TEST(std::get<FrontendParser::SetWidthNode>(parseSet(
|
||||
"set width x")).width.empty());
|
||||
BOOST_TEST((std::get<FrontendParser::SetWidthNode>(parseSet(
|
||||
"set width x 80")).column == "X"));
|
||||
BOOST_TEST((std::get<FrontendParser::SetWidthNode>(parseSet(
|
||||
"set width x 90")).width == "90"));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set width x 90 y", parserOptions)));
|
||||
|
||||
BOOST_TEST(std::get<FrontendParser::SetWireStatsNode>(parseSet(
|
||||
"set wire")).arg.empty());
|
||||
BOOST_TEST((std::get<FrontendParser::SetWireStatsNode>(parseSet(
|
||||
"set wire on")).arg == "ON"));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set wire off x", parserOptions)));
|
||||
|
||||
BOOST_TEST(std::get<FrontendParser::SetWireStatsNode>(parseSet(
|
||||
"set wire_stats")).arg.empty());
|
||||
BOOST_TEST((std::get<FrontendParser::SetWireStatsNode>(parseSet(
|
||||
"set wire_stats on")).arg == "ON"));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set wire_stats off x", parserOptions)));
|
||||
|
||||
// Engine commands.
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set decfloat", parserOptions)));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set decfloat x", parserOptions)));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set generator", parserOptions)));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set generator x", parserOptions)));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set role", parserOptions)));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set role x", parserOptions)));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set statistics", parserOptions)));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set statistics x", parserOptions)));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set time zone", parserOptions)));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set trusted", parserOptions)));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"set trusted x", parserOptions)));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(ParseShowTest)
|
||||
{
|
||||
const FrontendParser::Options parserOptions;
|
||||
|
||||
const auto parseShow = [&](const std::string_view text) {
|
||||
return std::get<FrontendParser::AnyShowNode>(FrontendParser::parse(text, parserOptions));
|
||||
};
|
||||
|
||||
BOOST_TEST((std::holds_alternative<FrontendParser::ShowNode>(parseShow("show"))));
|
||||
|
||||
BOOST_TEST(!std::get<FrontendParser::ShowChecksNode>(parseShow(
|
||||
"show check")).name);
|
||||
BOOST_TEST((std::get<FrontendParser::ShowChecksNode>(parseShow(
|
||||
"show check name")).name == "NAME"));
|
||||
|
||||
BOOST_TEST(!std::get<FrontendParser::ShowCollationsNode>(parseShow(
|
||||
"show collate")).name);
|
||||
BOOST_TEST((std::get<FrontendParser::ShowCollationsNode>(parseShow(
|
||||
"show collate name")).name == "NAME"));
|
||||
|
||||
BOOST_TEST(!std::get<FrontendParser::ShowCollationsNode>(parseShow(
|
||||
"show collation")).name);
|
||||
BOOST_TEST((std::get<FrontendParser::ShowCollationsNode>(parseShow(
|
||||
"show collation name")).name == "NAME"));
|
||||
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::ShowCommentsNode>(parseShow(
|
||||
"show comments")));
|
||||
|
||||
BOOST_TEST(!std::get<FrontendParser::ShowDependenciesNode>(parseShow(
|
||||
"show depen")).name);
|
||||
BOOST_TEST((std::get<FrontendParser::ShowDependenciesNode>(parseShow(
|
||||
"show depen name")).name == "NAME"));
|
||||
|
||||
BOOST_TEST(!std::get<FrontendParser::ShowDomainsNode>(parseShow(
|
||||
"show domain")).name);
|
||||
BOOST_TEST((std::get<FrontendParser::ShowDomainsNode>(parseShow(
|
||||
"show domain name")).name == "NAME"));
|
||||
|
||||
BOOST_TEST(!std::get<FrontendParser::ShowExceptionsNode>(parseShow(
|
||||
"show excep")).name);
|
||||
BOOST_TEST((std::get<FrontendParser::ShowExceptionsNode>(parseShow(
|
||||
"show excep name")).name == "NAME"));
|
||||
|
||||
BOOST_TEST(!std::get<FrontendParser::ShowFiltersNode>(parseShow(
|
||||
"show filter")).name);
|
||||
BOOST_TEST((std::get<FrontendParser::ShowFiltersNode>(parseShow(
|
||||
"show filter name")).name == "NAME"));
|
||||
|
||||
BOOST_TEST(!std::get<FrontendParser::ShowFunctionsNode>(parseShow(
|
||||
"show func")).name);
|
||||
BOOST_TEST((std::get<FrontendParser::ShowFunctionsNode>(parseShow(
|
||||
"show func name")).name == "NAME"));
|
||||
BOOST_TEST((std::get<FrontendParser::ShowFunctionsNode>(parseShow(
|
||||
"show func package.name")).package == "PACKAGE"));
|
||||
BOOST_TEST((std::get<FrontendParser::ShowFunctionsNode>(parseShow(
|
||||
"show func package.name")).name == "NAME"));
|
||||
|
||||
BOOST_TEST(!std::get<FrontendParser::ShowIndexesNode>(parseShow(
|
||||
"show ind")).name);
|
||||
BOOST_TEST((std::get<FrontendParser::ShowIndexesNode>(parseShow(
|
||||
"show index name")).name == "NAME"));
|
||||
BOOST_TEST((std::get<FrontendParser::ShowIndexesNode>(parseShow(
|
||||
"show indices name")).name == "NAME"));
|
||||
|
||||
BOOST_TEST(!std::get<FrontendParser::ShowGeneratorsNode>(parseShow(
|
||||
"show gen")).name);
|
||||
BOOST_TEST((std::get<FrontendParser::ShowGeneratorsNode>(parseShow(
|
||||
"show generator name")).name == "NAME"));
|
||||
|
||||
BOOST_TEST(!std::get<FrontendParser::ShowMappingsNode>(parseShow(
|
||||
"show map")).name);
|
||||
BOOST_TEST((std::get<FrontendParser::ShowMappingsNode>(parseShow(
|
||||
"show mapping name")).name == "NAME"));
|
||||
|
||||
BOOST_TEST(!std::get<FrontendParser::ShowPackagesNode>(parseShow(
|
||||
"show pack")).name);
|
||||
BOOST_TEST((std::get<FrontendParser::ShowPackagesNode>(parseShow(
|
||||
"show package name")).name == "NAME"));
|
||||
|
||||
BOOST_TEST(!std::get<FrontendParser::ShowProceduresNode>(parseShow(
|
||||
"show proc")).name);
|
||||
BOOST_TEST((std::get<FrontendParser::ShowProceduresNode>(parseShow(
|
||||
"show proc name")).name == "NAME"));
|
||||
BOOST_TEST((std::get<FrontendParser::ShowProceduresNode>(parseShow(
|
||||
"show proc package.name")).package == "PACKAGE"));
|
||||
BOOST_TEST((std::get<FrontendParser::ShowProceduresNode>(parseShow(
|
||||
"show proc package.name")).name == "NAME"));
|
||||
|
||||
BOOST_TEST(!std::get<FrontendParser::ShowPublicationsNode>(parseShow(
|
||||
"show pub")).name);
|
||||
BOOST_TEST((std::get<FrontendParser::ShowPublicationsNode>(parseShow(
|
||||
"show publication name")).name == "NAME"));
|
||||
|
||||
BOOST_TEST(!std::get<FrontendParser::ShowRolesNode>(parseShow(
|
||||
"show role")).name);
|
||||
BOOST_TEST((std::get<FrontendParser::ShowRolesNode>(parseShow(
|
||||
"show roles name")).name == "NAME"));
|
||||
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::InvalidNode>(FrontendParser::parse(
|
||||
"show seccla", parserOptions)));
|
||||
BOOST_TEST(!std::get<FrontendParser::ShowSecClassesNode>(parseShow(
|
||||
"show seccla *")).detail);
|
||||
BOOST_TEST(std::get<FrontendParser::ShowSecClassesNode>(parseShow(
|
||||
"show seccla * detail")).detail);
|
||||
BOOST_TEST(!std::get<FrontendParser::ShowSecClassesNode>(parseShow(
|
||||
"show seccla * detail")).name);
|
||||
BOOST_TEST((std::get<FrontendParser::ShowSecClassesNode>(parseShow(
|
||||
"show secclasses name")).name == "NAME"));
|
||||
|
||||
BOOST_TEST((!std::get<FrontendParser::ShowSystemNode>(parseShow(
|
||||
"show system")).objType.has_value()));
|
||||
BOOST_TEST((!std::get<FrontendParser::ShowSystemNode>(parseShow(
|
||||
"show system table")).objType == obj_relation));
|
||||
BOOST_TEST((std::get<FrontendParser::ShowTablesNode>(parseShow(
|
||||
"show table \"test\"")).name == "test"));
|
||||
BOOST_TEST((std::get<FrontendParser::ShowTablesNode>(parseShow(
|
||||
"show table \"te\"\"st\"")).name == "te\"st"));
|
||||
|
||||
BOOST_TEST(!std::get<FrontendParser::ShowTablesNode>(parseShow(
|
||||
"show table")).name);
|
||||
BOOST_TEST((std::get<FrontendParser::ShowTablesNode>(parseShow(
|
||||
"show tables name")).name == "NAME"));
|
||||
|
||||
BOOST_TEST(!std::get<FrontendParser::ShowTriggersNode>(parseShow(
|
||||
"show trig")).name);
|
||||
BOOST_TEST((std::get<FrontendParser::ShowTriggersNode>(parseShow(
|
||||
"show triggers name")).name == "NAME"));
|
||||
|
||||
BOOST_TEST(!std::get<FrontendParser::ShowViewsNode>(parseShow(
|
||||
"show view")).name);
|
||||
BOOST_TEST((std::get<FrontendParser::ShowViewsNode>(parseShow(
|
||||
"show views name")).name == "NAME"));
|
||||
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::ShowWireStatsNode>(parseShow(
|
||||
"show wire_stat")));
|
||||
BOOST_TEST(std::holds_alternative<FrontendParser::ShowWireStatsNode>(parseShow(
|
||||
"show wire_statistics")));
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END() // FrontendParserTests
|
||||
BOOST_AUTO_TEST_SUITE_END() // FrontendParserSuite
|
||||
BOOST_AUTO_TEST_SUITE_END() // ISqlSuite
|
@ -4,16 +4,3 @@
|
||||
|
||||
#define BOOST_TEST_MODULE ISqlTest
|
||||
#include "boost/test/included/unit_test.hpp"
|
||||
|
||||
|
||||
// TODO: Remove.
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(IsqlSuite)
|
||||
BOOST_AUTO_TEST_SUITE(DummySuite)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(DummyTest)
|
||||
{
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END() // DummySuite
|
||||
BOOST_AUTO_TEST_SUITE_END() // IsqlSuite
|
||||
|
@ -898,7 +898,7 @@ private:
|
||||
if (*control == *(CharType*) obj->getCanonicalChar(CHAR_GDML_SUBSTITUTE))
|
||||
{
|
||||
// Note: don't allow substitution characters larger than vector
|
||||
CharType** const end_vector = vector + (((int) c < FB_NELEM(vector)) ? c : 0);
|
||||
CharType** const end_vector = vector + ((static_cast<FB_SSIZE_T>(c) < static_cast<FB_SSIZE_T>(FB_NELEM(vector))) ? c : 0);
|
||||
while (v <= end_vector)
|
||||
*v++ = 0;
|
||||
*end_vector = t;
|
||||
|
@ -367,7 +367,7 @@ namespace Jrd {
|
||||
return;
|
||||
|
||||
fb_assert(tdbb);
|
||||
lockAndReadHeader(tdbb, CRYPT_HDR_NOWAIT);
|
||||
lockAndReadHeader(tdbb, CRYPT_HDR_NOWAIT | CRYPT_RELOAD_PLUGIN);
|
||||
}
|
||||
|
||||
void CryptoManager::lockAndReadHeader(thread_db* tdbb, unsigned flags)
|
||||
@ -407,9 +407,15 @@ namespace Jrd {
|
||||
crypt = hdr->hdr_flags & Ods::hdr_encrypted;
|
||||
process = hdr->hdr_flags & Ods::hdr_crypt_process;
|
||||
|
||||
if (flags & CRYPT_RELOAD_PLUGIN && cryptPlugin)
|
||||
{
|
||||
PluginManagerInterfacePtr()->releasePlugin(cryptPlugin);
|
||||
cryptPlugin = NULL;
|
||||
}
|
||||
|
||||
// tdbb w/o attachment comes when database is shutting down in the end of detachDatabase()
|
||||
// the only needed here page is header, i.e. we can live w/o cryptPlugin
|
||||
if ((crypt || process) && tdbb->getAttachment())
|
||||
if ((crypt || process) && !cryptPlugin && tdbb->getAttachment())
|
||||
{
|
||||
ClumpletWriter hc(ClumpletWriter::UnTagged, hdr->hdr_page_size);
|
||||
hdr.getClumplets(hc);
|
||||
@ -418,56 +424,18 @@ namespace Jrd {
|
||||
else
|
||||
keyName = "";
|
||||
|
||||
if (!cryptPlugin)
|
||||
loadPlugin(tdbb, hdr->hdr_crypt_plugin);
|
||||
pluginName = hdr->hdr_crypt_plugin;
|
||||
string valid;
|
||||
calcValidation(valid, cryptPlugin);
|
||||
if (hc.find(Ods::HDR_crypt_hash))
|
||||
{
|
||||
loadPlugin(tdbb, hdr->hdr_crypt_plugin);
|
||||
pluginName = hdr->hdr_crypt_plugin;
|
||||
string valid;
|
||||
calcValidation(valid, cryptPlugin);
|
||||
if (hc.find(Ods::HDR_crypt_hash))
|
||||
{
|
||||
hc.getString(hash);
|
||||
if (hash != valid)
|
||||
(Arg::Gds(isc_bad_crypt_key) << keyName).raise();
|
||||
}
|
||||
else
|
||||
hash = valid;
|
||||
hc.getString(hash);
|
||||
if (hash != valid)
|
||||
(Arg::Gds(isc_bad_crypt_key) << keyName).raise();
|
||||
}
|
||||
else
|
||||
{
|
||||
for (GetPlugins<IKeyHolderPlugin> keyControl(IPluginManager::TYPE_KEY_HOLDER, dbb.dbb_config);
|
||||
keyControl.hasData(); keyControl.next())
|
||||
{
|
||||
// check does keyHolder want to provide a key for us
|
||||
IKeyHolderPlugin* keyHolder = keyControl.plugin();
|
||||
|
||||
FbLocalStatus st;
|
||||
int keyCallbackRc = keyHolder->keyCallback(&st, tdbb->getAttachment()->att_crypt_callback);
|
||||
st.check();
|
||||
if (!keyCallbackRc)
|
||||
continue;
|
||||
|
||||
// validate a key
|
||||
AutoPlugin<IDbCryptPlugin> crypt(checkFactory->makeInstance());
|
||||
setDbInfo(crypt);
|
||||
crypt->setKey(&st, 1, &keyHolder, keyName.c_str());
|
||||
|
||||
|
||||
string valid;
|
||||
calcValidation(valid, crypt);
|
||||
if (hc.find(Ods::HDR_crypt_hash))
|
||||
{
|
||||
hc.getString(hash);
|
||||
if (hash == valid)
|
||||
{
|
||||
// unload old plugin and set new one
|
||||
PluginManagerInterfacePtr()->releasePlugin(cryptPlugin);
|
||||
cryptPlugin = NULL;
|
||||
cryptPlugin = crypt.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
hash = valid;
|
||||
}
|
||||
|
||||
if (cryptPlugin && (flags & CRYPT_HDR_INIT))
|
||||
|
@ -364,6 +364,7 @@ private:
|
||||
void lockAndReadHeader(thread_db* tdbb, unsigned flags = 0);
|
||||
static const unsigned CRYPT_HDR_INIT = 0x01;
|
||||
static const unsigned CRYPT_HDR_NOWAIT = 0x02;
|
||||
static const unsigned CRYPT_RELOAD_PLUGIN = 0x04;
|
||||
|
||||
void addClumplet(Firebird::string& value, Firebird::ClumpletReader& block, UCHAR tag);
|
||||
void calcDigitalSignature(thread_db* tdbb, Firebird::string& signature, const class Header& hdr);
|
||||
|
@ -62,6 +62,7 @@
|
||||
#include "../common/classes/FpeControl.h"
|
||||
#include "../jrd/extds/ExtDS.h"
|
||||
#include "../jrd/align.h"
|
||||
#include "firebird/impl/types_pub.h"
|
||||
|
||||
#include <functional>
|
||||
#include <cmath>
|
||||
@ -2625,7 +2626,7 @@ dsc* evlCharToUuid(thread_db* tdbb, const SysFunction* function, const NestValue
|
||||
Arg::Str(function->name));
|
||||
}
|
||||
|
||||
for (int i = 0; i < Uuid::STR_LEN; ++i)
|
||||
for (unsigned int i = 0; i < Uuid::STR_LEN; ++i)
|
||||
{
|
||||
if (i == 8 || i == 13 || i == 18 || i == 23)
|
||||
{
|
||||
@ -2709,7 +2710,7 @@ const char* extractParts[] =
|
||||
|
||||
const char* getPartName(int n)
|
||||
{
|
||||
if (n < 0 || n >= FB_NELEM(extractParts) || !extractParts[n])
|
||||
if (n < 0 || static_cast<FB_SIZE_T>(n) >= FB_NELEM(extractParts) || !extractParts[n])
|
||||
return "Unknown";
|
||||
|
||||
return extractParts[n];
|
||||
|
@ -314,7 +314,7 @@ void UserManagement::commit()
|
||||
}
|
||||
}
|
||||
|
||||
USHORT UserManagement::put(Auth::DynamicUserData* userData)
|
||||
USHORT UserManagement::put(Auth::UserData* userData)
|
||||
{
|
||||
const FB_SIZE_T ret = commands.getCount();
|
||||
if (ret > MAX_USHORT)
|
||||
@ -379,7 +379,7 @@ void UserManagement::execute(USHORT id)
|
||||
|
||||
if (command->attr.entered() || command->op == Auth::ADDMOD_OPER)
|
||||
{
|
||||
Auth::StackUserData cmd;
|
||||
Auth::UserData cmd;
|
||||
cmd.op = Auth::DIS_OPER;
|
||||
cmd.user.set(&statusWrapper, command->userName()->get());
|
||||
check(&statusWrapper);
|
||||
@ -614,7 +614,7 @@ RecordBuffer* UserManagement::getList(thread_db* tdbb, jrd_rel* relation)
|
||||
|
||||
for (FillSnapshot fillSnapshot(this); fillSnapshot.pos < managers.getCount(); ++fillSnapshot.pos)
|
||||
{
|
||||
Auth::StackUserData u;
|
||||
Auth::UserData u;
|
||||
u.op = Auth::DIS_OPER;
|
||||
|
||||
*ec = managers[fillSnapshot.pos].second->execute(currentWrapper, &u, &fillSnapshot);
|
||||
|
@ -59,7 +59,7 @@ public:
|
||||
~UserManagement();
|
||||
|
||||
// store userData for DFW-time processing
|
||||
USHORT put(Auth::DynamicUserData* userData);
|
||||
USHORT put(Auth::UserData* userData);
|
||||
// execute command with ID
|
||||
void execute(USHORT id);
|
||||
// commit transaction in security database
|
||||
@ -71,7 +71,7 @@ public:
|
||||
|
||||
private:
|
||||
thread_db* threadDbb;
|
||||
Firebird::HalfStaticArray<Auth::DynamicUserData*, 8> commands;
|
||||
Firebird::HalfStaticArray<Auth::UserData*, 8> commands;
|
||||
typedef Firebird::Pair<Firebird::NonPooled<MetaName, Firebird::IManagement*> > Manager;
|
||||
Firebird::ObjectsArray<Manager> managers;
|
||||
Firebird::NoCaseString plugins;
|
||||
|
@ -49,8 +49,10 @@ const unsigned WORKER_IDLE_TIMEOUT = 60; // 1 minute
|
||||
|
||||
/// class WorkerStableAttachment
|
||||
|
||||
WorkerStableAttachment::WorkerStableAttachment(FbStatusVector* status, Jrd::Attachment* attachment) :
|
||||
SysStableAttachment(attachment)
|
||||
WorkerStableAttachment::WorkerStableAttachment(FbStatusVector* status, Jrd::Attachment* attachment,
|
||||
WorkerAttachment* workers) :
|
||||
SysStableAttachment(attachment),
|
||||
m_workers(workers)
|
||||
{
|
||||
UserId user;
|
||||
user.setUserName("<Worker>");
|
||||
@ -69,6 +71,7 @@ WorkerStableAttachment::WorkerStableAttachment(FbStatusVector* status, Jrd::Atta
|
||||
Monitoring::publishAttachment(tdbb);
|
||||
|
||||
initDone();
|
||||
m_workers->incWorkers();
|
||||
}
|
||||
|
||||
WorkerStableAttachment::~WorkerStableAttachment()
|
||||
@ -76,16 +79,17 @@ WorkerStableAttachment::~WorkerStableAttachment()
|
||||
fini();
|
||||
}
|
||||
|
||||
WorkerStableAttachment* WorkerStableAttachment::create(FbStatusVector* status, Jrd::Database* dbb)
|
||||
WorkerStableAttachment* WorkerStableAttachment::create(FbStatusVector* status, Database* dbb,
|
||||
JProvider* provider, WorkerAttachment* workers)
|
||||
{
|
||||
Attachment* attachment = NULL;
|
||||
try
|
||||
{
|
||||
attachment = Attachment::create(dbb, NULL);
|
||||
attachment = Attachment::create(dbb, provider);
|
||||
attachment->att_filename = dbb->dbb_filename;
|
||||
attachment->att_flags |= ATT_worker;
|
||||
|
||||
WorkerStableAttachment* sAtt = FB_NEW WorkerStableAttachment(status, attachment);
|
||||
WorkerStableAttachment* sAtt = FB_NEW WorkerStableAttachment(status, attachment, workers);
|
||||
return sAtt;
|
||||
}
|
||||
catch (const Exception& ex)
|
||||
@ -120,6 +124,8 @@ void WorkerStableAttachment::fini()
|
||||
BackgroundContextHolder tdbb(dbb, attachment, &status_vector, FB_FUNCTION);
|
||||
|
||||
Monitoring::cleanupAttachment(tdbb);
|
||||
dbb->dbb_extManager->closeAttachment(tdbb, attachment);
|
||||
|
||||
attachment->releaseLocks(tdbb);
|
||||
LCK_fini(tdbb, LCK_OWNER_attachment);
|
||||
|
||||
@ -127,6 +133,8 @@ void WorkerStableAttachment::fini()
|
||||
}
|
||||
|
||||
destroy(attachment);
|
||||
|
||||
m_workers->decWorkers();
|
||||
}
|
||||
|
||||
/// class WorkerAttachment
|
||||
@ -137,8 +145,7 @@ bool WorkerAttachment::m_shutdown = false;
|
||||
|
||||
WorkerAttachment::WorkerAttachment() :
|
||||
m_idleAtts(*getDefaultMemoryPool()),
|
||||
m_activeAtts(*getDefaultMemoryPool()),
|
||||
m_cntUserAtts(0)
|
||||
m_activeAtts(*getDefaultMemoryPool())
|
||||
{
|
||||
}
|
||||
|
||||
@ -175,6 +182,30 @@ void WorkerAttachment::decUserAtts(const PathName& dbname)
|
||||
}
|
||||
}
|
||||
|
||||
void WorkerAttachment::incWorkers()
|
||||
{
|
||||
fb_assert(Config::getServerMode() == MODE_SUPER);
|
||||
|
||||
MutexLockGuard guard(m_mutex, FB_FUNCTION);
|
||||
++m_cntWorkers;
|
||||
}
|
||||
|
||||
void WorkerAttachment::decWorkers()
|
||||
{
|
||||
fb_assert(Config::getServerMode() == MODE_SUPER);
|
||||
|
||||
MutexLockGuard guard(m_mutex, FB_FUNCTION);
|
||||
if (--m_cntWorkers == 0)
|
||||
m_noWorkers.notifyAll();
|
||||
}
|
||||
|
||||
void WorkerAttachment::waitForWorkers()
|
||||
{
|
||||
MutexLockGuard guard(m_mutex, FB_FUNCTION);
|
||||
while (m_cntWorkers != 0)
|
||||
m_noWorkers.wait(m_mutex);
|
||||
}
|
||||
|
||||
WorkerAttachment* WorkerAttachment::getByName(const PathName& dbname)
|
||||
{
|
||||
if (m_shutdown)
|
||||
@ -228,13 +259,17 @@ void WorkerAttachment::shutdownDbb(Database* dbb)
|
||||
if (Config::getServerMode() != MODE_SUPER)
|
||||
return;
|
||||
|
||||
MutexLockGuard guard(m_mapMutex, FB_FUNCTION);
|
||||
|
||||
WorkerAttachment* item = NULL;
|
||||
if (!m_map->get(dbb->dbb_filename, item))
|
||||
return;
|
||||
|
||||
{
|
||||
MutexLockGuard guard(m_mapMutex, FB_FUNCTION);
|
||||
|
||||
if (!m_map->get(dbb->dbb_filename, item))
|
||||
return;
|
||||
}
|
||||
|
||||
item->clear(false);
|
||||
item->waitForWorkers();
|
||||
}
|
||||
|
||||
StableAttachmentPart* WorkerAttachment::getAttachment(FbStatusVector* status, Database* dbb)
|
||||
@ -299,7 +334,7 @@ StableAttachmentPart* WorkerAttachment::getAttachment(FbStatusVector* status, Da
|
||||
|
||||
MutexUnlockGuard unlock(item->m_mutex, FB_FUNCTION);
|
||||
status->init();
|
||||
sAtt = doAttach(status, dbb);
|
||||
sAtt = item->doAttach(status, dbb);
|
||||
if (!sAtt)
|
||||
{
|
||||
// log error ?
|
||||
@ -433,18 +468,17 @@ StableAttachmentPart* WorkerAttachment::doAttach(FbStatusVector* status, Databas
|
||||
{
|
||||
StableAttachmentPart* sAtt = NULL;
|
||||
|
||||
AutoPlugin<JProvider> jInstance(JProvider::getInstance());
|
||||
//jInstance->setDbCryptCallback(&status, tdbb->getAttachment()->att_crypt_callback);
|
||||
|
||||
if (Config::getServerMode() == MODE_SUPER)
|
||||
sAtt = WorkerStableAttachment::create(status, dbb);
|
||||
sAtt = WorkerStableAttachment::create(status, dbb, jInstance, this);
|
||||
else
|
||||
{
|
||||
ClumpletWriter dpb(ClumpletReader::Tagged, MAX_DPB_SIZE, isc_dpb_version1);
|
||||
dpb.insertString(isc_dpb_trusted_auth, DBA_USER_NAME);
|
||||
dpb.insertInt(isc_dpb_worker_attach, 1);
|
||||
|
||||
AutoPlugin<JProvider> jInstance(JProvider::getInstance());
|
||||
|
||||
//jInstance->setDbCryptCallback(&status, tdbb->getAttachment()->att_crypt_callback);
|
||||
|
||||
JAttachment* jAtt = jInstance->attachDatabase(status, dbb->dbb_filename.c_str(),
|
||||
dpb.getBufferLength(), dpb.getBuffer());
|
||||
|
||||
@ -456,6 +490,7 @@ StableAttachmentPart* WorkerAttachment::doAttach(FbStatusVector* status, Databas
|
||||
{
|
||||
sAtt->addRef(); // !!
|
||||
sAtt->getHandle()->setIdleTimeout(WORKER_IDLE_TIMEOUT);
|
||||
jInstance->addRef();
|
||||
}
|
||||
|
||||
return sAtt;
|
||||
@ -465,6 +500,15 @@ void WorkerAttachment::doDetach(FbStatusVector* status, StableAttachmentPart* sA
|
||||
{
|
||||
status->init();
|
||||
|
||||
AutoPlugin<JProvider> provider;
|
||||
{
|
||||
AttSyncLockGuard guard(*sAtt->getSync(), FB_FUNCTION);
|
||||
|
||||
Attachment* attachment = sAtt->getHandle();
|
||||
if (attachment)
|
||||
provider.reset(attachment->getProvider());
|
||||
}
|
||||
|
||||
// if (att->att_flags & ATT_system)
|
||||
if (Config::getServerMode() == MODE_SUPER)
|
||||
{
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include "firebird.h"
|
||||
#include "../common/classes/alloc.h"
|
||||
#include "../common/classes/array.h"
|
||||
#include "../common/classes/condition.h"
|
||||
#include "../common/classes/fb_string.h"
|
||||
#include "../common/classes/GenericMap.h"
|
||||
#include "../common/classes/init.h"
|
||||
@ -43,10 +44,13 @@
|
||||
namespace Jrd
|
||||
{
|
||||
|
||||
class WorkerAttachment;
|
||||
|
||||
class WorkerStableAttachment : public SysStableAttachment
|
||||
{
|
||||
public:
|
||||
static WorkerStableAttachment* create(FbStatusVector* status, Jrd::Database* dbb);
|
||||
static WorkerStableAttachment* create(FbStatusVector* status, Database* dbb, JProvider* provider,
|
||||
WorkerAttachment* workers);
|
||||
|
||||
void fini();
|
||||
|
||||
@ -54,8 +58,10 @@ protected:
|
||||
virtual void doOnIdleTimer(Firebird::TimerImpl* timer);
|
||||
|
||||
private:
|
||||
explicit WorkerStableAttachment(FbStatusVector* status, Jrd::Attachment* att);
|
||||
explicit WorkerStableAttachment(FbStatusVector* status, Jrd::Attachment* att, WorkerAttachment* workers);
|
||||
virtual ~WorkerStableAttachment();
|
||||
|
||||
WorkerAttachment* m_workers;
|
||||
};
|
||||
|
||||
|
||||
@ -77,6 +83,8 @@ private:
|
||||
|
||||
class WorkerAttachment
|
||||
{
|
||||
friend class WorkerStableAttachment;
|
||||
|
||||
public:
|
||||
explicit WorkerAttachment();
|
||||
|
||||
@ -93,10 +101,14 @@ public:
|
||||
|
||||
private:
|
||||
static WorkerAttachment* getByName(const Firebird::PathName& dbname);
|
||||
static Jrd::StableAttachmentPart* doAttach(FbStatusVector* status, Jrd::Database* dbb);
|
||||
Jrd::StableAttachmentPart* doAttach(FbStatusVector* status, Jrd::Database* dbb);
|
||||
static void doDetach(FbStatusVector* status, Jrd::StableAttachmentPart* sAtt);
|
||||
void clear(bool checkRefs);
|
||||
|
||||
void incWorkers();
|
||||
void decWorkers();
|
||||
void waitForWorkers();
|
||||
|
||||
|
||||
typedef Firebird::GenericMap<Firebird::Pair<Firebird::Left<Firebird::PathName, WorkerAttachment*> > >
|
||||
MapDbIdToWorkAtts;
|
||||
@ -109,7 +121,15 @@ private:
|
||||
Firebird::HalfStaticArray<Jrd::StableAttachmentPart*, 8> m_idleAtts;
|
||||
Firebird::SortedArray<Jrd::StableAttachmentPart*,
|
||||
Firebird::InlineStorage<Jrd::StableAttachmentPart*, 8> > m_activeAtts;
|
||||
int m_cntUserAtts;
|
||||
|
||||
// count of regular user attachments, used with non-shared Database
|
||||
int m_cntUserAtts = 0;
|
||||
|
||||
// count of internal worker attachments, used with shared Database
|
||||
int m_cntWorkers = 0;
|
||||
|
||||
// used to wait for "no internal workers" condition
|
||||
Firebird::Condition m_noWorkers;
|
||||
};
|
||||
|
||||
} // namespace Jrd
|
||||
|
@ -239,7 +239,7 @@ BlobFilter* BLF_lookup_internal_filter(thread_db* tdbb, SSHORT from, SSHORT to)
|
||||
|
||||
// Check for system defined filter
|
||||
|
||||
if (to == isc_blob_text && from >= 0 && from < FB_NELEM(filters))
|
||||
if (to == isc_blob_text && from >= 0 && static_cast<FB_SIZE_T>(from) < FB_NELEM(filters))
|
||||
{
|
||||
BlobFilter* result = FB_NEW_POOL(*dbb->dbb_permanent) BlobFilter(*dbb->dbb_permanent);
|
||||
result->blf_next = NULL;
|
||||
|
@ -3,16 +3,16 @@
|
||||
*** DO NOT EDIT ***
|
||||
TO CHANGE ANY INFORMATION IN HERE PLEASE
|
||||
EDIT src/misc/writeBuildNum.sh
|
||||
FORMAL BUILD NUMBER:553
|
||||
FORMAL BUILD NUMBER:587
|
||||
*/
|
||||
|
||||
#define PRODUCT_VER_STRING "6.0.0.553"
|
||||
#define FILE_VER_STRING "WI-T6.0.0.553"
|
||||
#define LICENSE_VER_STRING "WI-T6.0.0.553"
|
||||
#define FILE_VER_NUMBER 6, 0, 0, 553
|
||||
#define PRODUCT_VER_STRING "6.0.0.587"
|
||||
#define FILE_VER_STRING "WI-T6.0.0.587"
|
||||
#define LICENSE_VER_STRING "WI-T6.0.0.587"
|
||||
#define FILE_VER_NUMBER 6, 0, 0, 587
|
||||
#define FB_MAJOR_VER "6"
|
||||
#define FB_MINOR_VER "0"
|
||||
#define FB_REV_NO "0"
|
||||
#define FB_BUILD_NO "553"
|
||||
#define FB_BUILD_NO "587"
|
||||
#define FB_BUILD_TYPE "T"
|
||||
#define FB_BUILD_SUFFIX "Firebird 6.0 Initial"
|
||||
|
@ -2488,15 +2488,6 @@ bool CCH_write_all_shadows(thread_db* tdbb, Shadow* shadow, BufferDesc* bdb, Ods
|
||||
PAG_add_header_entry(tdbb, header, HDR_root_file_name,
|
||||
(USHORT) strlen((const char*) q), q);
|
||||
|
||||
jrd_file* next_file = shadow_file->fil_next;
|
||||
if (next_file)
|
||||
{
|
||||
q = (UCHAR *) next_file->fil_string;
|
||||
const SLONG last = next_file->fil_min_page - 1;
|
||||
PAG_add_header_entry(tdbb, header, HDR_file, (USHORT) strlen((const char*) q), q);
|
||||
PAG_add_header_entry(tdbb, header, HDR_last_page, sizeof(last), (const UCHAR*) &last);
|
||||
}
|
||||
|
||||
header->hdr_flags |= hdr_active_shadow;
|
||||
header->hdr_header.pag_pageno = bdb->bdb_page.getPageNum();
|
||||
}
|
||||
|
219
src/jrd/dfw.epp
219
src/jrd/dfw.epp
@ -56,7 +56,7 @@
|
||||
* BUGCHECK(291) took place. To avoid that issue, it was decided not to modify data
|
||||
* in system transaction. An exception is RDB$FORMATS relation, which is always modified
|
||||
* by transaction zero. Also an aspect of 'dirty' access from system transaction was
|
||||
* taken into an account in add_file(), make_version() and create_index().
|
||||
* taken into an account in make_version() and create_index().
|
||||
*
|
||||
*/
|
||||
|
||||
@ -449,7 +449,6 @@ private:
|
||||
*
|
||||
*==================================================================
|
||||
*/
|
||||
static bool add_file(thread_db*, SSHORT, DeferredWork*, jrd_tra*);
|
||||
static bool add_shadow(thread_db*, SSHORT, DeferredWork*, jrd_tra*);
|
||||
static bool delete_shadow(thread_db*, SSHORT, DeferredWork*, jrd_tra*);
|
||||
static bool compute_security(thread_db*, SSHORT, DeferredWork*, jrd_tra*);
|
||||
@ -1272,7 +1271,6 @@ namespace
|
||||
|
||||
static const deferred_task task_table[] =
|
||||
{
|
||||
{ dfw_add_file, add_file },
|
||||
{ dfw_add_shadow, add_shadow },
|
||||
{ dfw_delete_index, modify_index },
|
||||
{ dfw_delete_rfr, delete_rfr },
|
||||
@ -1876,133 +1874,6 @@ void DFW_update_index(const TEXT* name, USHORT id, const SelectivityList& select
|
||||
}
|
||||
|
||||
|
||||
static bool add_file(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction)
|
||||
{
|
||||
/**************************************
|
||||
*
|
||||
* a d d _ f i l e
|
||||
*
|
||||
**************************************
|
||||
*
|
||||
* Functional description
|
||||
* Add a file to a database.
|
||||
* This file could be a regular database
|
||||
* file or a shadow file. Either way we
|
||||
* require exclusive access to the database.
|
||||
*
|
||||
**************************************/
|
||||
USHORT section, shadow_number;
|
||||
SLONG start, min_start;
|
||||
|
||||
SET_TDBB(tdbb);
|
||||
Database* const dbb = tdbb->getDatabase();
|
||||
|
||||
switch (phase)
|
||||
{
|
||||
case 0:
|
||||
CCH_release_exclusive(tdbb);
|
||||
return false;
|
||||
|
||||
case 1:
|
||||
case 2:
|
||||
return true;
|
||||
|
||||
case 3:
|
||||
if (!CCH_exclusive(tdbb, LCK_EX, WAIT_PERIOD, NULL))
|
||||
raiseDatabaseInUseError(true);
|
||||
return true;
|
||||
|
||||
case 4:
|
||||
CCH_flush(tdbb, FLUSH_FINI, 0);
|
||||
start = PageSpace::maxAlloc(dbb) + 1;
|
||||
AutoRequest handle;
|
||||
AutoRequest handle2;
|
||||
|
||||
// Check the file name for node name. This has already
|
||||
// been done for shadows in add_shadow()
|
||||
|
||||
if (work->dfw_type != dfw_add_shadow) {
|
||||
check_filename(work->dfw_name, true);
|
||||
}
|
||||
|
||||
// User transaction may be safely used instead of system, cause
|
||||
// we requested and got exclusive database access. AP-2008.
|
||||
|
||||
// get any files to extend into
|
||||
|
||||
FOR(REQUEST_HANDLE handle TRANSACTION_HANDLE transaction) X IN RDB$FILES
|
||||
WITH X.RDB$FILE_NAME EQ work->dfw_name.c_str()
|
||||
// First expand the file name This has already been done
|
||||
// for shadows in add_shadow ())
|
||||
if (work->dfw_type != dfw_add_shadow)
|
||||
{
|
||||
MODIFY X USING
|
||||
ISC_expand_filename(X.RDB$FILE_NAME, 0,
|
||||
X.RDB$FILE_NAME, sizeof(X.RDB$FILE_NAME), false);
|
||||
END_MODIFY
|
||||
}
|
||||
|
||||
// Check the previous file length
|
||||
FOR(REQUEST_HANDLE handle2 TRANSACTION_HANDLE transaction)
|
||||
FIRST 1 Y IN RDB$FILES
|
||||
WITH Y.RDB$SHADOW_NUMBER EQ X.RDB$SHADOW_NUMBER
|
||||
AND Y.RDB$FILE_SEQUENCE NOT MISSING
|
||||
SORTED BY DESCENDING Y.RDB$FILE_SEQUENCE
|
||||
{
|
||||
if (!Y.RDB$FILE_START.NULL && !Y.RDB$FILE_LENGTH.NULL)
|
||||
{
|
||||
min_start = Y.RDB$FILE_START + (Y.RDB$FILE_LENGTH ? Y.RDB$FILE_LENGTH : 1);
|
||||
start = MAX(min_start, start);
|
||||
}
|
||||
}
|
||||
END_FOR
|
||||
|
||||
// If there is no starting position specified, or if it is
|
||||
// too low a value, raise the error.
|
||||
if (X.RDB$FILE_START < start)
|
||||
{
|
||||
ERR_post(Arg::Gds(isc_file_starting_page_err) <<
|
||||
Arg::Str(X.RDB$FILE_NAME) << Arg::Num(start));
|
||||
}
|
||||
|
||||
start = X.RDB$FILE_START;
|
||||
|
||||
shadow_number = X.RDB$SHADOW_NUMBER;
|
||||
if ((shadow_number &&
|
||||
(section = SDW_add_file(tdbb, X.RDB$FILE_NAME, start, shadow_number))) ||
|
||||
(section = PAG_add_file(tdbb, X.RDB$FILE_NAME, start)))
|
||||
{
|
||||
MODIFY X USING
|
||||
X.RDB$FILE_SEQUENCE = section;
|
||||
X.RDB$FILE_START = start;
|
||||
END_MODIFY
|
||||
}
|
||||
END_FOR
|
||||
|
||||
if (section)
|
||||
{
|
||||
handle.reset();
|
||||
section--;
|
||||
FOR(REQUEST_HANDLE handle TRANSACTION_HANDLE transaction) X IN RDB$FILES
|
||||
WITH X.RDB$FILE_SEQUENCE EQ section
|
||||
AND X.RDB$SHADOW_NUMBER EQ shadow_number
|
||||
{
|
||||
MODIFY X USING
|
||||
X.RDB$FILE_LENGTH = start - X.RDB$FILE_START;
|
||||
END_MODIFY
|
||||
}
|
||||
END_FOR
|
||||
}
|
||||
|
||||
CCH_release_exclusive(tdbb);
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static bool add_shadow(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction)
|
||||
{
|
||||
/**************************************
|
||||
@ -2053,11 +1924,11 @@ static bool add_shadow(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tr
|
||||
finished = false;
|
||||
handle.reset();
|
||||
FOR(REQUEST_HANDLE handle TRANSACTION_HANDLE transaction)
|
||||
F IN RDB$FILES
|
||||
WITH F.RDB$FILE_NAME EQ work->dfw_name.c_str()
|
||||
|
||||
F IN RDB$FILES WITH F.RDB$FILE_NAME EQ work->dfw_name.c_str()
|
||||
{
|
||||
expanded_fname = F.RDB$FILE_NAME;
|
||||
ISC_expand_filename(expanded_fname, false);
|
||||
|
||||
MODIFY F USING
|
||||
expanded_fname.copyTo(F.RDB$FILE_NAME, sizeof(F.RDB$FILE_NAME));
|
||||
END_MODIFY
|
||||
@ -2066,94 +1937,34 @@ static bool add_shadow(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tr
|
||||
{
|
||||
if ((F.RDB$SHADOW_NUMBER == shadow->sdw_number) && !(shadow->sdw_flags & SDW_IGNORE))
|
||||
{
|
||||
if (F.RDB$FILE_FLAGS & FILE_shadow)
|
||||
{
|
||||
// This is the case of a bogus duplicate posted
|
||||
// work when we added a multi-file shadow
|
||||
finished = true;
|
||||
}
|
||||
else if (shadow->sdw_flags & (SDW_dumped))
|
||||
{
|
||||
/* Case of adding a file to a currently active
|
||||
* shadow set.
|
||||
* Note: as of 1995-January-31 there is
|
||||
* no SQL syntax that supports this, but there
|
||||
* may be GDML
|
||||
*/
|
||||
add_file(tdbb, 3, work, transaction);
|
||||
add_file(tdbb, 4, work, transaction);
|
||||
finished = true;
|
||||
}
|
||||
else
|
||||
if (!(F.RDB$FILE_FLAGS & FILE_shadow))
|
||||
{
|
||||
// We cannot add a file to a shadow that is still
|
||||
// in the process of being created.
|
||||
raiseDatabaseInUseError(false);
|
||||
}
|
||||
|
||||
// This is the case of a bogus duplicate posted
|
||||
// work when we added a multi-file shadow
|
||||
finished = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
END_FOR
|
||||
|
||||
if (finished)
|
||||
return false;
|
||||
|
||||
// this file is part of a new shadow, so get all files for the shadow
|
||||
// in order of the starting page for the file
|
||||
|
||||
// Note that for a multi-file shadow, we have several pieces of
|
||||
// work posted (one dfw_add_shadow for each file). Rather than
|
||||
// trying to cancel the other pieces of work we ignore them
|
||||
// when they arrive in this routine.
|
||||
|
||||
sequence = 0;
|
||||
min_page = 0;
|
||||
shadow = NULL;
|
||||
handle.reset();
|
||||
FOR(REQUEST_HANDLE handle TRANSACTION_HANDLE transaction)
|
||||
X IN RDB$FILES CROSS
|
||||
Y IN RDB$FILES
|
||||
OVER RDB$SHADOW_NUMBER
|
||||
WITH X.RDB$FILE_NAME EQ expanded_fname.c_str()
|
||||
SORTED BY Y.RDB$FILE_START
|
||||
F IN RDB$FILES WITH F.RDB$FILE_NAME EQ expanded_fname.c_str()
|
||||
{
|
||||
// for the first file, create a brand new shadow; for secondary
|
||||
// files that have a starting page specified, add a file
|
||||
if (!sequence)
|
||||
SDW_add(tdbb, Y.RDB$FILE_NAME, Y.RDB$SHADOW_NUMBER, Y.RDB$FILE_FLAGS);
|
||||
else if (Y.RDB$FILE_START)
|
||||
{
|
||||
if (!shadow)
|
||||
{
|
||||
for (shadow = dbb->dbb_shadow; shadow; shadow = shadow->sdw_next)
|
||||
{
|
||||
if ((Y.RDB$SHADOW_NUMBER == shadow->sdw_number) &&
|
||||
!(shadow->sdw_flags & SDW_IGNORE))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
SDW_add(tdbb, F.RDB$FILE_NAME, F.RDB$SHADOW_NUMBER, F.RDB$FILE_FLAGS);
|
||||
|
||||
if (!shadow)
|
||||
BUGCHECK(203); // msg 203 shadow block not found for extend file
|
||||
|
||||
min_page = MAX((min_page + 1), (ULONG) Y.RDB$FILE_START);
|
||||
add_sequence = SDW_add_file(tdbb, Y.RDB$FILE_NAME, min_page, Y.RDB$SHADOW_NUMBER);
|
||||
}
|
||||
|
||||
// update the sequence number and bless the file entry as being good
|
||||
|
||||
if (!sequence || (Y.RDB$FILE_START && add_sequence))
|
||||
{
|
||||
MODIFY Y
|
||||
Y.RDB$FILE_FLAGS |= FILE_shadow;
|
||||
Y.RDB$FILE_SEQUENCE = sequence;
|
||||
Y.RDB$FILE_START = min_page;
|
||||
END_MODIFY
|
||||
sequence++;
|
||||
}
|
||||
MODIFY F
|
||||
F.RDB$FILE_FLAGS |= FILE_shadow;
|
||||
END_MODIFY
|
||||
}
|
||||
END_FOR
|
||||
|
||||
|
@ -171,7 +171,7 @@ void InternalConnection::attach(thread_db* tdbb)
|
||||
|
||||
memset(m_features, false, sizeof(m_features));
|
||||
static const info_features features[] = ENGINE_FEATURES;
|
||||
for (int i = 0; i < FB_NELEM(features); i++)
|
||||
for (FB_SIZE_T i = 0; i < FB_NELEM(features); i++)
|
||||
setFeature(features[i]);
|
||||
}
|
||||
|
||||
|
@ -811,7 +811,7 @@ ISC_STATUS filter_transliterate_text(USHORT action, BlobControl* control)
|
||||
{
|
||||
case isc_blob_filter_open:
|
||||
case isc_blob_filter_create:
|
||||
for (SSHORT i = 0; i < FB_NELEM(control->ctl_data); i++)
|
||||
for (FB_SIZE_T i = 0; i < FB_NELEM(control->ctl_data); i++)
|
||||
control->ctl_data[i] = 0;
|
||||
aux = NULL;
|
||||
|
||||
|
@ -633,6 +633,8 @@ void INF_database_info(thread_db* tdbb,
|
||||
}
|
||||
|
||||
{
|
||||
StrArray names;
|
||||
|
||||
SyncLockGuard sync(&dbb->dbb_sync, SYNC_SHARED, "INF_database_info");
|
||||
|
||||
for (const Jrd::Attachment* att = dbb->dbb_attachments; att; att = att->att_next)
|
||||
@ -643,6 +645,13 @@ void INF_database_info(thread_db* tdbb,
|
||||
{
|
||||
const char* userName = user->getUserName().hasData() ?
|
||||
user->getUserName().c_str() : "(Firebird Worker Thread)";
|
||||
|
||||
FB_SIZE_T pos;
|
||||
if (names.find(userName, pos))
|
||||
continue;
|
||||
|
||||
names.insert(pos, userName);
|
||||
|
||||
p = buffer;
|
||||
const ULONG len = MIN(strlen(userName), MAX_UCHAR);
|
||||
*p++ = static_cast<UCHAR>(len);
|
||||
|
@ -1619,7 +1619,7 @@ static void store_indices(thread_db* tdbb, USHORT odsVersion)
|
||||
|
||||
AutoRequest handle1, handle2, handle3;
|
||||
|
||||
for (int n = 0; n < SYSTEM_INDEX_COUNT; n++)
|
||||
for (FB_SIZE_T n = 0; n < SYSTEM_INDEX_COUNT; n++)
|
||||
{
|
||||
const ini_idx_t* index = &indices[n];
|
||||
const auto relation = MET_relation(tdbb, index->ini_idx_relid);
|
||||
|
@ -1301,7 +1301,7 @@ private:
|
||||
|
||||
static void check_database(thread_db* tdbb, bool async = false);
|
||||
static void commit(thread_db*, jrd_tra*, const bool);
|
||||
static bool drop_files(const jrd_file*);
|
||||
static bool drop_file(Database*, const jrd_file*);
|
||||
static void find_intl_charset(thread_db*, Jrd::Attachment*, const DatabaseOptions*);
|
||||
static void init_database_lock(thread_db*);
|
||||
static void run_commit_triggers(thread_db* tdbb, jrd_tra* transaction);
|
||||
@ -1831,7 +1831,7 @@ JAttachment* JProvider::internalAttach(CheckStatusWrapper* user_status, const ch
|
||||
dbb->dbb_crypto_manager = FB_NEW_POOL(*dbb->dbb_permanent) CryptoManager(tdbb);
|
||||
dbb->dbb_monitoring_data = FB_NEW_POOL(*dbb->dbb_permanent) MonitoringData(dbb);
|
||||
|
||||
PAG_init2(tdbb, 0);
|
||||
PAG_init2(tdbb);
|
||||
PAG_header(tdbb, false, newForceWrite);
|
||||
dbb->dbb_page_manager.initTempPageSpace(tdbb);
|
||||
dbb->dbb_crypto_manager->attach(tdbb, attachment);
|
||||
@ -3552,11 +3552,9 @@ void JAttachment::internalDropDatabase(CheckStatusWrapper* user_status)
|
||||
// This point on database is useless
|
||||
|
||||
// drop the files here
|
||||
bool err = drop_files(file);
|
||||
bool err = drop_file(dbb, file);
|
||||
for (; shadow; shadow = shadow->sdw_next)
|
||||
{
|
||||
err = drop_files(shadow->sdw_file) || err;
|
||||
}
|
||||
err = drop_file(dbb, shadow->sdw_file) || err;
|
||||
|
||||
tdbb->setDatabase(NULL);
|
||||
Database::destroy(dbb);
|
||||
@ -6798,31 +6796,28 @@ static void commit(thread_db* tdbb, jrd_tra* transaction, const bool retaining_f
|
||||
}
|
||||
|
||||
|
||||
static bool drop_files(const jrd_file* file)
|
||||
static bool drop_file(Database* dbb, const jrd_file* file)
|
||||
{
|
||||
/**************************************
|
||||
*
|
||||
* d r o p _ f i l e s
|
||||
* d r o p _ f i l e
|
||||
*
|
||||
**************************************
|
||||
*
|
||||
* Functional description
|
||||
* drop a linked list of files
|
||||
* Drop a file.
|
||||
*
|
||||
**************************************/
|
||||
FbLocalStatus status;
|
||||
|
||||
for (; file; file = file->fil_next)
|
||||
if (unlink(file->fil_string))
|
||||
{
|
||||
if (unlink(file->fil_string))
|
||||
{
|
||||
ERR_build_status(&status, Arg::Gds(isc_io_error) << Arg::Str("unlink") <<
|
||||
Arg::Str(file->fil_string) <<
|
||||
Arg::Gds(isc_io_delete_err) << SYS_ERR(errno));
|
||||
Database* dbb = GET_DBB();
|
||||
PageSpace* pageSpace = dbb->dbb_page_manager.findPageSpace(DB_PAGE_SPACE);
|
||||
iscDbLogStatus(pageSpace->file->fil_string, &status);
|
||||
}
|
||||
ERR_build_status(&status, Arg::Gds(isc_io_error) << Arg::Str("unlink") <<
|
||||
Arg::Str(file->fil_string) <<
|
||||
Arg::Gds(isc_io_delete_err) << SYS_ERR(errno));
|
||||
|
||||
PageSpace* pageSpace = dbb->dbb_page_manager.findPageSpace(DB_PAGE_SPACE);
|
||||
iscDbLogStatus(pageSpace->file->fil_string, &status);
|
||||
}
|
||||
|
||||
return status->getState() & IStatus::STATE_ERRORS ? true : false;
|
||||
|
@ -1782,7 +1782,6 @@ void MET_get_shadow_files(thread_db* tdbb, bool delete_files)
|
||||
FOR(REQUEST_HANDLE handle) X IN RDB$FILES
|
||||
WITH X.RDB$SHADOW_NUMBER NOT MISSING
|
||||
AND X.RDB$SHADOW_NUMBER NE 0
|
||||
AND X.RDB$FILE_SEQUENCE EQ 0
|
||||
{
|
||||
if ((X.RDB$FILE_FLAGS & FILE_shadow) && !(X.RDB$FILE_FLAGS & FILE_inactive))
|
||||
{
|
||||
|
@ -24,6 +24,8 @@
|
||||
#ifndef JRD_OBJ_H
|
||||
#define JRD_OBJ_H
|
||||
|
||||
#include "../common/gdsassert.h"
|
||||
|
||||
// Object types used in RDB$DEPENDENCIES and RDB$USER_PRIVILEGES and stored in backup.
|
||||
// Note: some values are hard coded in grant.gdl
|
||||
// Keep existing constants unchanged.
|
||||
@ -76,10 +78,11 @@ const ObjectType obj_index_condition = 37;
|
||||
|
||||
const ObjectType obj_type_MAX = 38;
|
||||
|
||||
// used in the parser only / no relation with obj_type_MAX (should be greater)
|
||||
// not used in metadata / no relation with obj_type_MAX (should be greater)
|
||||
const ObjectType obj_user_or_role= 100;
|
||||
const ObjectType obj_parameter = 101;
|
||||
const ObjectType obj_column = 102;
|
||||
const ObjectType obj_publication = 103;
|
||||
|
||||
const ObjectType obj_any = 255;
|
||||
|
||||
|
@ -530,8 +530,8 @@ static_assert(offsetof(struct header_page, hdr_data) == 136, "hdr_data offset mi
|
||||
|
||||
inline constexpr UCHAR HDR_end = 0;
|
||||
inline constexpr UCHAR HDR_root_file_name = 1; // Original name of root file
|
||||
inline constexpr UCHAR HDR_file = 2; // Secondary file
|
||||
inline constexpr UCHAR HDR_last_page = 3; // Last logical page number of file
|
||||
//inline constexpr UCHAR HDR_file = 2; // Secondary file
|
||||
//inline constexpr UCHAR HDR_last_page = 3; // Last logical page number of file
|
||||
inline constexpr UCHAR HDR_sweep_interval = 4; // Transactions between sweeps
|
||||
inline constexpr UCHAR HDR_crypt_checksum = 5; // Checksum of critical crypt parameters
|
||||
inline constexpr UCHAR HDR_difference_file = 6; // Delta file that is used during backup lock
|
||||
|
@ -922,7 +922,7 @@ public:
|
||||
RecordSource* generate();
|
||||
|
||||
private:
|
||||
RecordSource* process(const JoinType joinType);
|
||||
RecordSource* process(StreamList* outerStreams = nullptr);
|
||||
|
||||
thread_db* const tdbb;
|
||||
Optimizer* const optimizer;
|
||||
|
@ -94,15 +94,25 @@ OuterJoin::OuterJoin(thread_db* aTdbb, Optimizer* opt,
|
||||
|
||||
RecordSource* OuterJoin::generate()
|
||||
{
|
||||
const auto outerJoinRsb = process(OUTER_JOIN);
|
||||
|
||||
if (!optimizer->isFullJoin())
|
||||
return outerJoinRsb;
|
||||
{
|
||||
fb_assert(optimizer->isLeftJoin());
|
||||
return process();
|
||||
}
|
||||
|
||||
// A FULL JOIN B is currently implemented similar to (A LEFT JOIN B) UNION ALL (B ANTI-JOIN A).
|
||||
StreamList outerStreams;
|
||||
const auto outerJoinRsb = process(&outerStreams);
|
||||
|
||||
// A FULL JOIN B is currently implemented similar to:
|
||||
//
|
||||
// (A LEFT JOIN B)
|
||||
// UNION ALL
|
||||
// (B LEFT JOIN A WHERE A.* IS NULL)
|
||||
//
|
||||
// See also FullOuterJoin class implementation.
|
||||
//
|
||||
// At this point we already have the first part -- (A LEFT JOIN B) -- ready,
|
||||
// so just swap the sides and make an anti-join.
|
||||
// so just swap the sides and make the second (inverted) join.
|
||||
|
||||
auto& outerStream = joinStreams[0];
|
||||
auto& innerStream = joinStreams[1];
|
||||
@ -131,15 +141,15 @@ RecordSource* OuterJoin::generate()
|
||||
iter.reset(CMP_clone_node_opt(tdbb, csb, iter));
|
||||
}
|
||||
|
||||
const auto antiJoinRsb = process(ANTI_JOIN);
|
||||
const auto antiJoinRsb = process();
|
||||
|
||||
// Allocate and return the final join record source
|
||||
|
||||
return FB_NEW_POOL(getPool()) FullOuterJoin(csb, outerJoinRsb, antiJoinRsb);
|
||||
return FB_NEW_POOL(getPool()) FullOuterJoin(csb, outerJoinRsb, antiJoinRsb, outerStreams);
|
||||
}
|
||||
|
||||
|
||||
RecordSource* OuterJoin::process(const JoinType joinType)
|
||||
RecordSource* OuterJoin::process(StreamList* outerStreams)
|
||||
{
|
||||
BoolExprNode* boolean = nullptr;
|
||||
|
||||
@ -153,8 +163,7 @@ RecordSource* OuterJoin::process(const JoinType joinType)
|
||||
{
|
||||
fb_assert(!outerStream.rsb);
|
||||
outerStream.rsb = optimizer->generateRetrieval(outerStream.number,
|
||||
optimizer->isFullJoin() ? nullptr : sortPtr,
|
||||
true, false, &boolean);
|
||||
optimizer->isFullJoin() ? nullptr : sortPtr, true, false, &boolean);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -173,13 +182,15 @@ RecordSource* OuterJoin::process(const JoinType joinType)
|
||||
boolean = optimizer->composeBoolean();
|
||||
}
|
||||
|
||||
if (outerStreams)
|
||||
outerStream.rsb->findUsedStreams(*outerStreams);
|
||||
|
||||
if (innerStream.number != INVALID_STREAM)
|
||||
{
|
||||
fb_assert(!innerStream.rsb);
|
||||
// AB: the sort clause for the inner stream of an OUTER JOIN
|
||||
// should never be used for the index retrieval
|
||||
innerStream.rsb = optimizer->generateRetrieval(innerStream.number, nullptr,
|
||||
false, (joinType == OUTER_JOIN) ? true : false);
|
||||
innerStream.rsb = optimizer->generateRetrieval(innerStream.number, nullptr, false, true);
|
||||
}
|
||||
|
||||
// Generate a parent filter record source for any remaining booleans that
|
||||
@ -189,8 +200,7 @@ RecordSource* OuterJoin::process(const JoinType joinType)
|
||||
|
||||
// Allocate and return the join record source
|
||||
|
||||
return FB_NEW_POOL(getPool())
|
||||
NestedLoopJoin(csb, outerStream.rsb, innerRsb, boolean, joinType);
|
||||
return FB_NEW_POOL(getPool()) NestedLoopJoin(csb, outerStream.rsb, innerRsb, boolean);
|
||||
};
|
||||
|
||||
|
||||
|
@ -1414,17 +1414,37 @@ InversionCandidate* Retrieval::makeInversion(InversionCandidateList& inversions)
|
||||
|
||||
for (auto inversion : inversions)
|
||||
{
|
||||
const auto indexScratch = inversion->scratch;
|
||||
|
||||
// If the explicit plan doesn't mention this index, fake it as used
|
||||
// thus excluding it from the cost-based algorithm. Otherwise,
|
||||
// given this index is suitable for navigation, also mark it as used.
|
||||
|
||||
if ((indexScratch &&
|
||||
(indexScratch->index->idx_runtime_flags & idx_plan_dont_use)) ||
|
||||
(!customPlan && inversion == navigationCandidate))
|
||||
if (const auto indexScratch = inversion->scratch)
|
||||
{
|
||||
inversion->used = true;
|
||||
const auto idx = indexScratch->index;
|
||||
|
||||
// If the explicit plan doesn't mention this index, fake it as used
|
||||
// thus excluding it from the cost-based algorithm. Otherwise,
|
||||
// given this index is suitable for navigation, also mark it as used.
|
||||
|
||||
if (((idx->idx_runtime_flags & idx_plan_dont_use)) ||
|
||||
(!customPlan && inversion == navigationCandidate))
|
||||
{
|
||||
inversion->used = true;
|
||||
}
|
||||
|
||||
// If the index is conditional and its condition is also present in
|
||||
// some other inversion as a boolean (it represents the OR operation),
|
||||
// fake these other inversions as used, so that the full index scan would
|
||||
// be preferred to multiple range scans. The cost-based algorithm below
|
||||
// cannot handle it currently.
|
||||
|
||||
if (idx->idx_flags & idx_condition)
|
||||
{
|
||||
for (auto otherInversion : inversions)
|
||||
{
|
||||
if (otherInversion->boolean &&
|
||||
idx->idx_condition->sameAs(otherInversion->boolean, true))
|
||||
{
|
||||
otherInversion->used = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1460,11 +1480,9 @@ InversionCandidate* Retrieval::makeInversion(InversionCandidateList& inversions)
|
||||
if (!invCandidate)
|
||||
invCandidate = FB_NEW_POOL(getPool()) InversionCandidate(getPool());
|
||||
|
||||
if (!currentInv->inversion && currentInv->scratch)
|
||||
invCandidate->inversion = makeIndexScanNode(currentInv->scratch);
|
||||
else
|
||||
invCandidate->inversion = currentInv->inversion;
|
||||
|
||||
const auto inversionNode = (!currentInv->inversion && currentInv->scratch) ?
|
||||
makeIndexScanNode(currentInv->scratch) : currentInv->inversion;
|
||||
invCandidate->inversion = inversionNode;
|
||||
invCandidate->dbkeyRanges.assign(currentInv->dbkeyRanges);
|
||||
invCandidate->unique = currentInv->unique;
|
||||
invCandidate->selectivity = currentInv->selectivity;
|
||||
@ -1473,44 +1491,32 @@ InversionCandidate* Retrieval::makeInversion(InversionCandidateList& inversions)
|
||||
invCandidate->nonFullMatchedSegments = 0;
|
||||
invCandidate->matchedSegments = currentInv->matchedSegments;
|
||||
invCandidate->dependencies = currentInv->dependencies;
|
||||
matches.clear();
|
||||
|
||||
for (const auto currentMatch : currentInv->matches)
|
||||
{
|
||||
if (!matches.exist(currentMatch))
|
||||
matches.add(currentMatch);
|
||||
if (!invCandidate->matches.exist(currentMatch))
|
||||
invCandidate->matches.add(currentMatch);
|
||||
}
|
||||
|
||||
if (currentInv->boolean)
|
||||
if (const auto currentMatch = currentInv->boolean)
|
||||
{
|
||||
if (!matches.exist(currentInv->boolean))
|
||||
matches.add(currentInv->boolean);
|
||||
if (!invCandidate->matches.exist(currentMatch))
|
||||
invCandidate->matches.add(currentMatch);
|
||||
}
|
||||
|
||||
invCandidate->matches.join(matches);
|
||||
matches.assign(invCandidate->matches);
|
||||
|
||||
if (customPlan)
|
||||
continue;
|
||||
|
||||
return invCandidate;
|
||||
}
|
||||
|
||||
// Look if a match is already used by previous matches.
|
||||
bool anyMatchAlreadyUsed = false, matchUsedByNavigation = false;
|
||||
if (currentInv->boolean)
|
||||
if (!customPlan)
|
||||
{
|
||||
if (matches.exist(currentInv->boolean))
|
||||
{
|
||||
anyMatchAlreadyUsed = true;
|
||||
// Look if a match is already used by previous matches
|
||||
bool anyMatchAlreadyUsed = false, matchUsedByNavigation = false;
|
||||
|
||||
if (navigationCandidate &&
|
||||
navigationCandidate->matches.exist(currentInv->boolean))
|
||||
{
|
||||
matchUsedByNavigation = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const auto currentMatch : currentInv->matches)
|
||||
{
|
||||
if (matches.exist(currentMatch))
|
||||
@ -1526,29 +1532,49 @@ InversionCandidate* Retrieval::makeInversion(InversionCandidateList& inversions)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (currentInv->boolean && matches.exist(currentInv->boolean))
|
||||
anyMatchAlreadyUsed = true;
|
||||
|
||||
if (anyMatchAlreadyUsed && !customPlan)
|
||||
{
|
||||
currentInv->used = true;
|
||||
|
||||
if (matchUsedByNavigation)
|
||||
continue;
|
||||
|
||||
// If a match on this index was already used by another
|
||||
// index, add also the other matches from this index.
|
||||
for (const auto currentMatch : currentInv->matches)
|
||||
if (const auto currentMatch = currentInv->boolean)
|
||||
{
|
||||
if (!matches.exist(currentMatch))
|
||||
matches.add(currentMatch);
|
||||
if (matches.exist(currentMatch))
|
||||
{
|
||||
anyMatchAlreadyUsed = true;
|
||||
|
||||
if (navigationCandidate &&
|
||||
navigationCandidate->matches.exist(currentMatch))
|
||||
{
|
||||
matchUsedByNavigation = true;
|
||||
}
|
||||
}
|
||||
else if (matchUsedByNavigation)
|
||||
anyMatchAlreadyUsed = false;
|
||||
}
|
||||
|
||||
// Restart loop, because other indexes could also be excluded now.
|
||||
restartLoop = true;
|
||||
break;
|
||||
// If some match was already used by another index, skip this index
|
||||
|
||||
if (anyMatchAlreadyUsed)
|
||||
{
|
||||
if (!matchUsedByNavigation)
|
||||
{
|
||||
// Add the other matches from this index
|
||||
|
||||
for (const auto currentMatch : currentInv->matches)
|
||||
{
|
||||
if (!matches.exist(currentMatch))
|
||||
matches.add(currentMatch);
|
||||
}
|
||||
|
||||
if (const auto currentMatch = currentInv->boolean)
|
||||
{
|
||||
if (!matches.exist(currentMatch))
|
||||
matches.add(currentMatch);
|
||||
}
|
||||
}
|
||||
|
||||
// Restart loop, because other indexes could also be excluded now
|
||||
currentInv->used = true;
|
||||
restartLoop = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bestCandidate)
|
||||
@ -1645,12 +1671,9 @@ InversionCandidate* Retrieval::makeInversion(InversionCandidateList& inversions)
|
||||
if (!invCandidate)
|
||||
{
|
||||
invCandidate = FB_NEW_POOL(getPool()) InversionCandidate(getPool());
|
||||
if (!bestCandidate->inversion && bestCandidate->scratch) {
|
||||
invCandidate->inversion = makeIndexScanNode(bestCandidate->scratch);
|
||||
}
|
||||
else {
|
||||
invCandidate->inversion = bestCandidate->inversion;
|
||||
}
|
||||
const auto inversionNode = (!bestCandidate->inversion && bestCandidate->scratch) ?
|
||||
makeIndexScanNode(bestCandidate->scratch) : bestCandidate->inversion;
|
||||
invCandidate->inversion = inversionNode;
|
||||
invCandidate->dbkeyRanges.assign(bestCandidate->dbkeyRanges);
|
||||
invCandidate->unique = bestCandidate->unique;
|
||||
invCandidate->selectivity = bestCandidate->selectivity;
|
||||
@ -1661,30 +1684,26 @@ InversionCandidate* Retrieval::makeInversion(InversionCandidateList& inversions)
|
||||
invCandidate->dependencies = bestCandidate->dependencies;
|
||||
invCandidate->condition = bestCandidate->condition;
|
||||
|
||||
for (FB_SIZE_T j = 0; j < bestCandidate->matches.getCount(); j++)
|
||||
for (const auto bestMatch : bestCandidate->matches)
|
||||
{
|
||||
if (!matches.exist(bestCandidate->matches[j]))
|
||||
matches.add(bestCandidate->matches[j]);
|
||||
if (!invCandidate->matches.exist(bestMatch))
|
||||
invCandidate->matches.add(bestMatch);
|
||||
}
|
||||
if (bestCandidate->boolean)
|
||||
|
||||
if (const auto bestMatch = bestCandidate->boolean)
|
||||
{
|
||||
if (!matches.exist(bestCandidate->boolean))
|
||||
matches.add(bestCandidate->boolean);
|
||||
if (!invCandidate->matches.exist(bestMatch))
|
||||
invCandidate->matches.add(bestMatch);
|
||||
}
|
||||
|
||||
matches.join(invCandidate->matches);
|
||||
}
|
||||
else if (!bestCandidate->condition)
|
||||
{
|
||||
if (!bestCandidate->inversion && bestCandidate->scratch)
|
||||
{
|
||||
invCandidate->inversion = composeInversion(invCandidate->inversion,
|
||||
makeIndexScanNode(bestCandidate->scratch), InversionNode::TYPE_AND);
|
||||
}
|
||||
else
|
||||
{
|
||||
invCandidate->inversion = composeInversion(invCandidate->inversion,
|
||||
bestCandidate->inversion, InversionNode::TYPE_AND);
|
||||
}
|
||||
|
||||
const auto inversionNode = (!bestCandidate->inversion && bestCandidate->scratch) ?
|
||||
makeIndexScanNode(bestCandidate->scratch) : bestCandidate->inversion;
|
||||
invCandidate->inversion = composeInversion(invCandidate->inversion,
|
||||
inversionNode, InversionNode::TYPE_AND);
|
||||
invCandidate->dbkeyRanges.join(bestCandidate->dbkeyRanges);
|
||||
invCandidate->unique = (invCandidate->unique || bestCandidate->unique);
|
||||
invCandidate->selectivity = totalSelectivity;
|
||||
@ -1697,15 +1716,17 @@ InversionCandidate* Retrieval::makeInversion(InversionCandidateList& inversions)
|
||||
|
||||
for (const auto bestMatch : bestCandidate->matches)
|
||||
{
|
||||
if (!matches.exist(bestMatch))
|
||||
matches.add(bestMatch);
|
||||
if (!invCandidate->matches.exist(bestMatch))
|
||||
invCandidate->matches.add(bestMatch);
|
||||
}
|
||||
|
||||
if (bestCandidate->boolean)
|
||||
if (const auto bestMatch = bestCandidate->boolean)
|
||||
{
|
||||
if (!matches.exist(bestCandidate->boolean))
|
||||
matches.add(bestCandidate->boolean);
|
||||
if (!invCandidate->matches.exist(bestMatch))
|
||||
invCandidate->matches.add(bestMatch);
|
||||
}
|
||||
|
||||
matches.join(invCandidate->matches);
|
||||
}
|
||||
|
||||
if (invCandidate->unique)
|
||||
@ -1739,10 +1760,13 @@ InversionCandidate* Retrieval::makeInversion(InversionCandidateList& inversions)
|
||||
invCandidate->cost += navigationCandidate->cost;
|
||||
++invCandidate->indexes;
|
||||
invCandidate->navigated = true;
|
||||
}
|
||||
|
||||
if (invCandidate)
|
||||
invCandidate->matches.join(matches);
|
||||
for (const auto navMatch : navigationCandidate->matches)
|
||||
{
|
||||
if (!invCandidate->matches.exist(navMatch))
|
||||
invCandidate->matches.add(navMatch);
|
||||
}
|
||||
}
|
||||
|
||||
return invCandidate;
|
||||
}
|
||||
@ -1754,6 +1778,16 @@ bool Retrieval::matchBoolean(IndexScratch* indexScratch,
|
||||
if (boolean->nodFlags & ExprNode::FLAG_DEOPTIMIZE)
|
||||
return false;
|
||||
|
||||
const auto idx = indexScratch->index;
|
||||
|
||||
if (idx->idx_flags & idx_condition)
|
||||
{
|
||||
// If index condition matches the boolean, this should not be
|
||||
// considered a match. Full index scan will be used instead.
|
||||
if (idx->idx_condition->sameAs(boolean, true))
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto cmpNode = nodeAs<ComparativeBoolNode>(boolean);
|
||||
const auto missingNode = nodeAs<MissingBoolNode>(boolean);
|
||||
const auto listNode = nodeAs<InListBoolNode>(boolean);
|
||||
@ -1788,16 +1822,6 @@ bool Retrieval::matchBoolean(IndexScratch* indexScratch,
|
||||
ValueExprNode* value2 = (cmpNode && cmpNode->blrOp == blr_between) ?
|
||||
cmpNode->arg3 : nullptr;
|
||||
|
||||
const auto idx = indexScratch->index;
|
||||
|
||||
if (idx->idx_flags & idx_condition)
|
||||
{
|
||||
// If index condition matches the boolean, this should not be
|
||||
// considered a match. Full index scan will be used instead.
|
||||
if (idx->idx_condition->sameAs(boolean, true))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (idx->idx_flags & idx_expression)
|
||||
{
|
||||
// see if one side or the other is matchable to the index expression
|
||||
|
@ -41,11 +41,6 @@ namespace Jrd {
|
||||
class jrd_file : public pool_alloc_rpt<SCHAR, type_fil>
|
||||
{
|
||||
public:
|
||||
jrd_file* fil_next; // Next file in database
|
||||
ULONG fil_min_page; // Minimum page number in file
|
||||
ULONG fil_max_page; // Maximum page number in file
|
||||
USHORT fil_sequence; // Sequence number of file
|
||||
USHORT fil_fudge; // Fudge factor for page relocation
|
||||
int fil_desc;
|
||||
Firebird::Mutex fil_mutex;
|
||||
USHORT fil_flags;
|
||||
@ -69,11 +64,6 @@ public:
|
||||
delete fil_ext_lock;
|
||||
}
|
||||
|
||||
jrd_file* fil_next; // Next file in database
|
||||
ULONG fil_min_page; // Minimum page number in file
|
||||
ULONG fil_max_page; // Maximum page number in file
|
||||
USHORT fil_sequence; // Sequence number of file
|
||||
USHORT fil_fudge; // Fudge factor for page relocation
|
||||
HANDLE fil_desc; // File descriptor
|
||||
Firebird::RWLock* fil_ext_lock; // file extend lock
|
||||
USHORT fil_flags;
|
||||
|
@ -36,7 +36,6 @@ namespace Ods {
|
||||
struct pag;
|
||||
}
|
||||
|
||||
int PIO_add_file(Jrd::thread_db*, Jrd::jrd_file*, const Firebird::PathName&, SLONG);
|
||||
void PIO_close(Jrd::jrd_file*);
|
||||
Jrd::jrd_file* PIO_create(Jrd::thread_db*, const Firebird::PathName&,
|
||||
const bool, const bool);
|
||||
|
@ -132,7 +132,7 @@ using namespace Firebird;
|
||||
|
||||
static const mode_t MASK = 0660;
|
||||
|
||||
static jrd_file* seek_file(jrd_file*, BufferDesc*, FB_UINT64*, FbStatusVector*);
|
||||
static bool seek_file(jrd_file*, BufferDesc*, FB_UINT64*, FbStatusVector*);
|
||||
static jrd_file* setup_file(Database*, const PathName&, int, USHORT);
|
||||
static void lockDatabaseFile(int& desc, const bool shareMode, const bool temporary,
|
||||
const char* fileName, ISC_STATUS operation);
|
||||
@ -149,42 +149,8 @@ static int raw_devices_unlink_database (const PathName&);
|
||||
static int openFile(const Firebird::PathName&, const bool, const bool, const bool);
|
||||
static void maybeCloseFile(int&);
|
||||
|
||||
int PIO_add_file(thread_db* tdbb, jrd_file* main_file, const PathName& file_name, SLONG start)
|
||||
{
|
||||
/**************************************
|
||||
*
|
||||
* P I O _ a d d _ f i l e
|
||||
*
|
||||
**************************************
|
||||
*
|
||||
* Functional description
|
||||
* Add a file to an existing database. Return the sequence
|
||||
* number of the new file. If anything goes wrong, return a
|
||||
* sequence of 0.
|
||||
* NOTE: This routine does not lock any mutexes on
|
||||
* its own behalf. It is assumed that mutexes will
|
||||
* have been locked before entry.
|
||||
*
|
||||
**************************************/
|
||||
jrd_file* new_file = PIO_create(tdbb, file_name, false, false);
|
||||
if (!new_file)
|
||||
return 0;
|
||||
|
||||
new_file->fil_min_page = start;
|
||||
USHORT sequence = 1;
|
||||
|
||||
jrd_file* file;
|
||||
for (file = main_file; file->fil_next; file = file->fil_next)
|
||||
++sequence;
|
||||
|
||||
file->fil_max_page = start - 1;
|
||||
file->fil_next = new_file;
|
||||
|
||||
return sequence;
|
||||
}
|
||||
|
||||
|
||||
void PIO_close(jrd_file* main_file)
|
||||
void PIO_close(jrd_file* file)
|
||||
{
|
||||
/**************************************
|
||||
*
|
||||
@ -199,13 +165,10 @@ void PIO_close(jrd_file* main_file)
|
||||
*
|
||||
**************************************/
|
||||
|
||||
for (jrd_file* file = main_file; file; file = file->fil_next)
|
||||
if (file->fil_desc && file->fil_desc != -1)
|
||||
{
|
||||
if (file->fil_desc && file->fil_desc != -1)
|
||||
{
|
||||
close(file->fil_desc);
|
||||
file->fil_desc = -1;
|
||||
}
|
||||
close(file->fil_desc);
|
||||
file->fil_desc = -1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -336,7 +299,7 @@ bool PIO_expand(const TEXT* file_name, USHORT file_length, TEXT* expanded_name,
|
||||
}
|
||||
|
||||
|
||||
void PIO_extend(thread_db* tdbb, jrd_file* main_file, const ULONG extPages, const USHORT pageSize)
|
||||
void PIO_extend(thread_db* tdbb, jrd_file* file, const ULONG extPages, const USHORT pageSize)
|
||||
{
|
||||
/**************************************
|
||||
*
|
||||
@ -348,56 +311,46 @@ void PIO_extend(thread_db* tdbb, jrd_file* main_file, const ULONG extPages, cons
|
||||
* Extend file by extPages pages of pageSize size.
|
||||
*
|
||||
**************************************/
|
||||
fb_assert(extPages);
|
||||
|
||||
#if defined(HAVE_LINUX_FALLOC_H) && defined(HAVE_FALLOCATE)
|
||||
|
||||
EngineCheckout cout(tdbb, FB_FUNCTION, EngineCheckout::UNNECESSARY);
|
||||
|
||||
ULONG leftPages = extPages;
|
||||
for (jrd_file* file = main_file; file && leftPages; file = file->fil_next)
|
||||
if (file->fil_flags & FIL_no_fast_extend)
|
||||
return;
|
||||
|
||||
const ULONG filePages = PIO_get_number_of_pages(file, pageSize);
|
||||
const ULONG extendBy = MIN(MAX_ULONG - filePages, extPages);
|
||||
|
||||
int r;
|
||||
for (r = 0; r < IO_RETRY; r++)
|
||||
{
|
||||
const ULONG filePages = PIO_get_number_of_pages(file, pageSize);
|
||||
const ULONG fileMaxPages = (file->fil_max_page == MAX_ULONG) ?
|
||||
MAX_ULONG : file->fil_max_page - file->fil_min_page + 1;
|
||||
if (filePages < fileMaxPages)
|
||||
{
|
||||
if (file->fil_flags & FIL_no_fast_extend)
|
||||
return;
|
||||
int err = fallocate(file->fil_desc, 0, filePages * pageSize, extendBy * pageSize);
|
||||
if (err == 0)
|
||||
break;
|
||||
|
||||
const ULONG extendBy = MIN(fileMaxPages - filePages + file->fil_fudge, leftPages);
|
||||
err = errno;
|
||||
if (SYSCALL_INTERRUPTED(err))
|
||||
continue;
|
||||
|
||||
int r;
|
||||
for (r = 0; r < IO_RETRY; r++)
|
||||
{
|
||||
int err = fallocate(file->fil_desc, 0, filePages * pageSize, extendBy * pageSize);
|
||||
if (err == 0)
|
||||
break;
|
||||
if (err != EOPNOTSUPP && err != ENOSYS)
|
||||
unix_error("fallocate", file, isc_io_write_err);
|
||||
|
||||
err = errno;
|
||||
if (SYSCALL_INTERRUPTED(err))
|
||||
continue;
|
||||
file->fil_flags |= FIL_no_fast_extend;
|
||||
return;
|
||||
}
|
||||
|
||||
if (err != EOPNOTSUPP && err != ENOSYS)
|
||||
unix_error("fallocate", file, isc_io_write_err);
|
||||
|
||||
file->fil_flags |= FIL_no_fast_extend;
|
||||
return;
|
||||
}
|
||||
|
||||
if (r == IO_RETRY)
|
||||
{
|
||||
if (r == IO_RETRY)
|
||||
{
|
||||
#ifdef DEV_BUILD
|
||||
fprintf(stderr, "PIO_extend: retry count exceeded\n");
|
||||
fflush(stderr);
|
||||
fprintf(stderr, "PIO_extend: retry count exceeded\n");
|
||||
fflush(stderr);
|
||||
#endif
|
||||
unix_error("fallocate_retry", file, isc_io_write_err);
|
||||
}
|
||||
|
||||
leftPages -= extendBy;
|
||||
}
|
||||
unix_error("fallocate_retry", file, isc_io_write_err);
|
||||
}
|
||||
#else
|
||||
main_file->fil_flags |= FIL_no_fast_extend;
|
||||
file->fil_flags |= FIL_no_fast_extend;
|
||||
#endif // fallocate present
|
||||
|
||||
// not implemented
|
||||
@ -405,7 +358,7 @@ void PIO_extend(thread_db* tdbb, jrd_file* main_file, const ULONG extPages, cons
|
||||
}
|
||||
|
||||
|
||||
void PIO_flush(thread_db* tdbb, jrd_file* main_file)
|
||||
void PIO_flush(thread_db* tdbb, jrd_file* file)
|
||||
{
|
||||
/**************************************
|
||||
*
|
||||
@ -422,15 +375,12 @@ void PIO_flush(thread_db* tdbb, jrd_file* main_file)
|
||||
#ifndef SUPERSERVER_V2
|
||||
|
||||
EngineCheckout cout(tdbb, FB_FUNCTION, EngineCheckout::UNNECESSARY);
|
||||
MutexLockGuard guard(main_file->fil_mutex, FB_FUNCTION);
|
||||
MutexLockGuard guard(file->fil_mutex, FB_FUNCTION);
|
||||
|
||||
for (jrd_file* file = main_file; file; file = file->fil_next)
|
||||
if (file->fil_desc != -1)
|
||||
{
|
||||
if (file->fil_desc != -1)
|
||||
{
|
||||
// This really should be an error
|
||||
fsync(file->fil_desc);
|
||||
}
|
||||
// This really should be an error
|
||||
fsync(file->fil_desc);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@ -615,7 +565,7 @@ void PIO_header(thread_db* tdbb, UCHAR* address, int length)
|
||||
static Firebird::InitInstance<ZeroBuffer> zeros;
|
||||
|
||||
|
||||
USHORT PIO_init_data(thread_db* tdbb, jrd_file* main_file, FbStatusVector* status_vector,
|
||||
USHORT PIO_init_data(thread_db* tdbb, jrd_file* file, FbStatusVector* status_vector,
|
||||
ULONG startPage, USHORT initPages)
|
||||
{
|
||||
/**************************************
|
||||
@ -642,16 +592,14 @@ USHORT PIO_init_data(thread_db* tdbb, jrd_file* main_file, FbStatusVector* statu
|
||||
|
||||
EngineCheckout cout(tdbb, FB_FUNCTION, EngineCheckout::UNNECESSARY);
|
||||
|
||||
jrd_file* file = seek_file(main_file, &bdb, &offset, status_vector);
|
||||
|
||||
if (!file)
|
||||
if (!seek_file(file, &bdb, &offset, status_vector))
|
||||
return 0;
|
||||
|
||||
if (file->fil_min_page + 8 > startPage)
|
||||
if (startPage < 8)
|
||||
return 0;
|
||||
|
||||
USHORT leftPages = initPages;
|
||||
const ULONG initBy = MIN(file->fil_max_page - startPage, leftPages);
|
||||
const ULONG initBy = MIN(MAX_ULONG - startPage, leftPages);
|
||||
if (initBy < leftPages)
|
||||
leftPages = initBy;
|
||||
|
||||
@ -667,15 +615,16 @@ USHORT PIO_init_data(thread_db* tdbb, jrd_file* main_file, FbStatusVector* statu
|
||||
|
||||
for (int r = 0; r < IO_RETRY; r++)
|
||||
{
|
||||
if (!(file = seek_file(file, &bdb, &offset, status_vector)))
|
||||
return false;
|
||||
if (!seek_file(file, &bdb, &offset, status_vector))
|
||||
return 0;
|
||||
|
||||
if ((written = os_utils::pwrite(file->fil_desc, zero_buff, to_write, LSEEK_OFFSET_CAST offset)) == to_write)
|
||||
break;
|
||||
|
||||
if (written < 0 && !SYSCALL_INTERRUPTED(errno))
|
||||
return unix_error("write", file, isc_io_write_err, status_vector);
|
||||
}
|
||||
|
||||
|
||||
leftPages -= write_pages;
|
||||
i += write_pages;
|
||||
}
|
||||
@ -812,7 +761,7 @@ bool PIO_read(thread_db* tdbb, jrd_file* file, BufferDesc* bdb, Ods::pag* page,
|
||||
|
||||
for (i = 0; i < IO_RETRY; i++)
|
||||
{
|
||||
if (!(file = seek_file(file, bdb, &offset, status_vector)))
|
||||
if (!seek_file(file, bdb, &offset, status_vector))
|
||||
return false;
|
||||
|
||||
if ((bytes = os_utils::pread(file->fil_desc, page, size, LSEEK_OFFSET_CAST offset)) == size)
|
||||
@ -864,7 +813,7 @@ bool PIO_write(thread_db* tdbb, jrd_file* file, BufferDesc* bdb, Ods::pag* page,
|
||||
|
||||
for (i = 0; i < IO_RETRY; i++)
|
||||
{
|
||||
if (!(file = seek_file(file, bdb, &offset, status_vector)))
|
||||
if (!seek_file(file, bdb, &offset, status_vector))
|
||||
return false;
|
||||
|
||||
if ((bytes = os_utils::pwrite(file->fil_desc, page, size, LSEEK_OFFSET_CAST offset)) == size)
|
||||
@ -881,8 +830,8 @@ bool PIO_write(thread_db* tdbb, jrd_file* file, BufferDesc* bdb, Ods::pag* page,
|
||||
}
|
||||
|
||||
|
||||
static jrd_file* seek_file(jrd_file* file, BufferDesc* bdb, FB_UINT64* offset,
|
||||
FbStatusVector* status_vector)
|
||||
static bool seek_file(jrd_file* file, BufferDesc* bdb, FB_UINT64* offset,
|
||||
FbStatusVector* status_vector)
|
||||
{
|
||||
/**************************************
|
||||
*
|
||||
@ -891,43 +840,29 @@ static jrd_file* seek_file(jrd_file* file, BufferDesc* bdb, FB_UINT64* offset,
|
||||
**************************************
|
||||
*
|
||||
* Functional description
|
||||
* Given a buffer descriptor block, find the appropriate
|
||||
* file block and seek to the proper page in that file.
|
||||
* Given a buffer descriptor block, seek to the proper page in that file.
|
||||
*
|
||||
**************************************/
|
||||
BufferControl* bcb = bdb->bdb_bcb;
|
||||
Database* dbb = bcb->bcb_database;
|
||||
ULONG page = bdb->bdb_page.getPageNum();
|
||||
|
||||
for (;; file = file->fil_next)
|
||||
{
|
||||
if (!file) {
|
||||
CORRUPT(158); // msg 158 database file not available
|
||||
}
|
||||
else if (page >= file->fil_min_page && page <= file->fil_max_page)
|
||||
break;
|
||||
}
|
||||
BufferControl* const bcb = bdb->bdb_bcb;
|
||||
const ULONG page = bdb->bdb_page.getPageNum();
|
||||
|
||||
if (file->fil_desc == -1)
|
||||
{
|
||||
unix_error("lseek", file, isc_io_access_err, status_vector);
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
page -= file->fil_min_page - file->fil_fudge;
|
||||
|
||||
FB_UINT64 lseek_offset = page;
|
||||
lseek_offset *= dbb->dbb_page_size;
|
||||
lseek_offset *= bcb->bcb_page_size;
|
||||
|
||||
if (lseek_offset != (FB_UINT64) LSEEK_OFFSET_CAST lseek_offset)
|
||||
{
|
||||
unix_error("lseek", file, isc_io_32bit_exceeded_err, status_vector);
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
*offset = lseek_offset;
|
||||
|
||||
return file;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -999,7 +934,6 @@ static jrd_file* setup_file(Database* dbb, const PathName& file_name, int desc,
|
||||
{
|
||||
file = FB_NEW_RPT(*dbb->dbb_permanent, file_name.length() + 1) jrd_file();
|
||||
file->fil_desc = desc;
|
||||
file->fil_max_page = MAX_ULONG;
|
||||
file->fil_flags = flags;
|
||||
strcpy(file->fil_string, file_name.c_str());
|
||||
}
|
||||
|
@ -106,7 +106,7 @@ using namespace Firebird;
|
||||
#define TEXT SCHAR
|
||||
|
||||
static bool maybeCloseFile(HANDLE&);
|
||||
static jrd_file* seek_file(jrd_file*, BufferDesc*, OVERLAPPED*);
|
||||
static bool seek_file(jrd_file*, BufferDesc*, OVERLAPPED*);
|
||||
static jrd_file* setup_file(Database*, const Firebird::PathName&, HANDLE, USHORT);
|
||||
static bool nt_error(const TEXT*, const jrd_file*, ISC_STATUS, FbStatusVector* const);
|
||||
|
||||
@ -121,39 +121,7 @@ static const DWORD g_dwExtraTempFlags = FILE_ATTRIBUTE_TEMPORARY |
|
||||
FILE_FLAG_DELETE_ON_CLOSE;
|
||||
|
||||
|
||||
int PIO_add_file(thread_db* tdbb, jrd_file* main_file, const Firebird::PathName& file_name, SLONG start)
|
||||
{
|
||||
/**************************************
|
||||
*
|
||||
* P I O _ a d d _ f i l e
|
||||
*
|
||||
**************************************
|
||||
*
|
||||
* Functional description
|
||||
* Add a file to an existing database. Return the sequence
|
||||
* number of the new file. If anything goes wrong, return a
|
||||
* sequence of 0.
|
||||
*
|
||||
**************************************/
|
||||
jrd_file* const new_file = PIO_create(tdbb, file_name, false, false);
|
||||
if (!new_file)
|
||||
return 0;
|
||||
|
||||
new_file->fil_min_page = start;
|
||||
USHORT sequence = 1;
|
||||
|
||||
jrd_file* file;
|
||||
for (file = main_file; file->fil_next; file = file->fil_next)
|
||||
++sequence;
|
||||
|
||||
file->fil_max_page = start - 1;
|
||||
file->fil_next = new_file;
|
||||
|
||||
return sequence;
|
||||
}
|
||||
|
||||
|
||||
void PIO_close(jrd_file* main_file)
|
||||
void PIO_close(jrd_file* file)
|
||||
{
|
||||
/**************************************
|
||||
*
|
||||
@ -164,10 +132,7 @@ void PIO_close(jrd_file* main_file)
|
||||
* Functional description
|
||||
*
|
||||
**************************************/
|
||||
for (jrd_file* file = main_file; file; file = file->fil_next)
|
||||
{
|
||||
maybeCloseFile(file->fil_desc);
|
||||
}
|
||||
maybeCloseFile(file->fil_desc);
|
||||
}
|
||||
|
||||
|
||||
@ -251,7 +216,7 @@ bool PIO_expand(const TEXT* file_name, USHORT file_length, TEXT* expanded_name,
|
||||
}
|
||||
|
||||
|
||||
void PIO_extend(thread_db* tdbb, jrd_file* main_file, const ULONG extPages, const USHORT pageSize)
|
||||
void PIO_extend(thread_db* tdbb, jrd_file* file, const ULONG extPages, const USHORT pageSize)
|
||||
{
|
||||
/**************************************
|
||||
*
|
||||
@ -263,6 +228,8 @@ void PIO_extend(thread_db* tdbb, jrd_file* main_file, const ULONG extPages, cons
|
||||
* Extend file by extPages pages of pageSize size.
|
||||
*
|
||||
**************************************/
|
||||
fb_assert(extPages);
|
||||
|
||||
// hvlad: prevent other reading\writing threads from changing file pointer.
|
||||
// As we open file without FILE_FLAG_OVERLAPPED, ReadFile\WriteFile calls
|
||||
// will change file pointer we set here and file truncation instead of file
|
||||
@ -271,42 +238,30 @@ void PIO_extend(thread_db* tdbb, jrd_file* main_file, const ULONG extPages, cons
|
||||
// and read\write activity performed simultaneously)
|
||||
|
||||
// if file have no extend lock it is better to not extend file than corrupt it
|
||||
if (!main_file->fil_ext_lock)
|
||||
if (!file->fil_ext_lock)
|
||||
return;
|
||||
|
||||
EngineCheckout cout(tdbb, FB_FUNCTION, EngineCheckout::UNNECESSARY);
|
||||
FileExtendLockGuard extLock(main_file->fil_ext_lock, true);
|
||||
FileExtendLockGuard extLock(file->fil_ext_lock, true);
|
||||
|
||||
ULONG leftPages = extPages;
|
||||
for (jrd_file* file = main_file; file && leftPages; file = file->fil_next)
|
||||
{
|
||||
const ULONG filePages = PIO_get_number_of_pages(file, pageSize);
|
||||
const ULONG fileMaxPages = (file->fil_max_page == MAX_ULONG) ?
|
||||
MAX_ULONG : file->fil_max_page - file->fil_min_page + 1;
|
||||
if (filePages < fileMaxPages)
|
||||
{
|
||||
const ULONG extendBy = MIN(fileMaxPages - filePages + file->fil_fudge, leftPages);
|
||||
const ULONG filePages = PIO_get_number_of_pages(file, pageSize);
|
||||
const ULONG extendBy = MIN(MAX_ULONG - filePages, extPages);
|
||||
|
||||
HANDLE hFile = file->fil_desc;
|
||||
const HANDLE hFile = file->fil_desc;
|
||||
|
||||
LARGE_INTEGER newSize;
|
||||
newSize.QuadPart = ((ULONGLONG) filePages + extendBy) * pageSize;
|
||||
LARGE_INTEGER newSize;
|
||||
newSize.QuadPart = ((ULONGLONG) filePages + extendBy) * pageSize;
|
||||
|
||||
const DWORD ret = SetFilePointer(hFile, newSize.LowPart, &newSize.HighPart, FILE_BEGIN);
|
||||
if (ret == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) {
|
||||
nt_error("SetFilePointer", file, isc_io_write_err, NULL);
|
||||
}
|
||||
if (!SetEndOfFile(hFile)) {
|
||||
nt_error("SetEndOfFile", file, isc_io_write_err, NULL);
|
||||
}
|
||||
const DWORD ret = SetFilePointer(hFile, newSize.LowPart, &newSize.HighPart, FILE_BEGIN);
|
||||
if (ret == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR)
|
||||
nt_error("SetFilePointer", file, isc_io_write_err, NULL);
|
||||
|
||||
leftPages -= extendBy;
|
||||
}
|
||||
}
|
||||
if (!SetEndOfFile(hFile))
|
||||
nt_error("SetEndOfFile", file, isc_io_write_err, NULL);
|
||||
}
|
||||
|
||||
|
||||
void PIO_flush(thread_db* tdbb, jrd_file* main_file)
|
||||
void PIO_flush(thread_db* tdbb, jrd_file* file)
|
||||
{
|
||||
/**************************************
|
||||
*
|
||||
@ -320,7 +275,7 @@ void PIO_flush(thread_db* tdbb, jrd_file* main_file)
|
||||
**************************************/
|
||||
EngineCheckout cout(tdbb, FB_FUNCTION, EngineCheckout::UNNECESSARY);
|
||||
|
||||
for (jrd_file* file = main_file; file; file = file->fil_next)
|
||||
if (file->fil_desc != INVALID_HANDLE_VALUE)
|
||||
FlushFileBuffers(file->fil_desc);
|
||||
}
|
||||
|
||||
@ -417,7 +372,7 @@ void PIO_header(thread_db* tdbb, UCHAR* address, int length)
|
||||
static Firebird::InitInstance<ZeroBuffer> zeros;
|
||||
|
||||
|
||||
USHORT PIO_init_data(thread_db* tdbb, jrd_file* main_file, FbStatusVector* status_vector,
|
||||
USHORT PIO_init_data(thread_db* tdbb, jrd_file* file, FbStatusVector* status_vector,
|
||||
ULONG startPage, USHORT initPages)
|
||||
{
|
||||
/**************************************
|
||||
@ -436,7 +391,7 @@ USHORT PIO_init_data(thread_db* tdbb, jrd_file* main_file, FbStatusVector* statu
|
||||
Database* const dbb = tdbb->getDatabase();
|
||||
|
||||
EngineCheckout cout(tdbb, FB_FUNCTION, EngineCheckout::UNNECESSARY);
|
||||
FileExtendLockGuard extLock(main_file->fil_ext_lock, false);
|
||||
FileExtendLockGuard extLock(file->fil_ext_lock, false);
|
||||
|
||||
// Fake buffer, used in seek_file. Page space ID doesn't matter there
|
||||
// as we already know file to work with
|
||||
@ -444,16 +399,14 @@ USHORT PIO_init_data(thread_db* tdbb, jrd_file* main_file, FbStatusVector* statu
|
||||
bdb.bdb_page = PageNumber(0, startPage);
|
||||
|
||||
OVERLAPPED overlapped;
|
||||
jrd_file* file = seek_file(main_file, &bdb, &overlapped);
|
||||
|
||||
if (!file)
|
||||
if (!seek_file(file, &bdb, &overlapped))
|
||||
return 0;
|
||||
|
||||
if (file->fil_min_page + 8 > startPage)
|
||||
if (startPage < 8)
|
||||
return 0;
|
||||
|
||||
USHORT leftPages = initPages;
|
||||
const ULONG initBy = MIN(file->fil_max_page - startPage, leftPages);
|
||||
const ULONG initBy = MIN(MAX_ULONG - startPage, leftPages);
|
||||
if (initBy < leftPages)
|
||||
leftPages = initBy;
|
||||
|
||||
@ -464,8 +417,8 @@ USHORT PIO_init_data(thread_db* tdbb, jrd_file* main_file, FbStatusVector* statu
|
||||
if (write_pages > leftPages)
|
||||
write_pages = leftPages;
|
||||
|
||||
jrd_file* file1 = seek_file(main_file, &bdb, &overlapped);
|
||||
fb_assert(file1 == file);
|
||||
if (!seek_file(file, &bdb, &overlapped))
|
||||
return 0;
|
||||
|
||||
const DWORD to_write = (DWORD) write_pages * dbb->dbb_page_size;
|
||||
DWORD written;
|
||||
@ -590,7 +543,7 @@ bool PIO_read(thread_db* tdbb, jrd_file* file, BufferDesc* bdb, Ods::pag* page,
|
||||
FileExtendLockGuard extLock(file->fil_ext_lock, false);
|
||||
|
||||
OVERLAPPED overlapped;
|
||||
if (!(file = seek_file(file, bdb, &overlapped)))
|
||||
if (!seek_file(file, bdb, &overlapped))
|
||||
return false;
|
||||
|
||||
HANDLE desc = file->fil_desc;
|
||||
@ -625,14 +578,12 @@ bool PIO_read_ahead(thread_db* tdbb,
|
||||
**************************************
|
||||
*
|
||||
* Functional description
|
||||
* Read a contiguous set of pages. The only
|
||||
* tricky part is to segment the I/O when crossing
|
||||
* file boundaries.
|
||||
* Read a contiguous set of pages.
|
||||
*
|
||||
**************************************/
|
||||
OVERLAPPED overlapped, *overlapped_ptr;
|
||||
|
||||
Database* const dbb = tdbb->getDatabase();
|
||||
const auto dbb = tdbb->getDatabase();
|
||||
|
||||
EngineCheckout cout(tdbb, FB_FUNCTION, EngineCheckout::UNNECESSARY);
|
||||
|
||||
@ -647,60 +598,45 @@ bool PIO_read_ahead(thread_db* tdbb,
|
||||
piob->piob_flags = 0;
|
||||
}
|
||||
|
||||
// Setup up a dummy buffer descriptor block for seeking file
|
||||
BufferDesc bdb;
|
||||
while (pages)
|
||||
bdb.bdb_dbb = dbb;
|
||||
bdb.bdb_page = start_page;
|
||||
|
||||
const jrd_file* const file = dbb->dbb_file;
|
||||
|
||||
if (!seek_file(file, &bdb, status_vector, overlapped_ptr, &overlapped_ptr))
|
||||
return false;
|
||||
|
||||
const HANDLE desc = file->fil_desc;
|
||||
const DWORD length = pages * dbb->dbb_page_size;
|
||||
|
||||
DWORD actual_length;
|
||||
if (ReadFile(desc, buffer, length, &actual_length, overlapped_ptr) &&
|
||||
actual_length == length)
|
||||
{
|
||||
// Setup up a dummy buffer descriptor block for seeking file.
|
||||
|
||||
bdb.bdb_dbb = dbb;
|
||||
bdb.bdb_page = start_page;
|
||||
|
||||
jrd_file* file = seek_file(dbb->dbb_file, &bdb, status_vector, overlapped_ptr, &overlapped_ptr);
|
||||
if (!file)
|
||||
return false;
|
||||
|
||||
// Check that every page within the set resides in the same database
|
||||
// file. If not read what you can and loop back for the rest.
|
||||
|
||||
DWORD segmented_length = 0;
|
||||
while (pages && start_page >= file->fil_min_page && start_page <= file->fil_max_page)
|
||||
{
|
||||
segmented_length += dbb->dbb_page_size;
|
||||
++start_page;
|
||||
--pages;
|
||||
}
|
||||
|
||||
HANDLE desc = file->fil_desc;
|
||||
|
||||
DWORD actual_length;
|
||||
if (ReadFile(desc, buffer, segmented_length, &actual_length, overlapped_ptr) &&
|
||||
actual_length == segmented_length)
|
||||
{
|
||||
if (piob && !pages)
|
||||
piob->piob_flags = PIOB_success;
|
||||
}
|
||||
else if (piob && !pages)
|
||||
{
|
||||
piob->piob_flags = PIOB_pending;
|
||||
piob->piob_desc = reinterpret_cast<SLONG>(desc);
|
||||
piob->piob_file = file;
|
||||
piob->piob_io_length = segmented_length;
|
||||
}
|
||||
else if (!GetOverlappedResult(desc, overlapped_ptr, &actual_length, TRUE) ||
|
||||
actual_length != segmented_length)
|
||||
{
|
||||
if (piob)
|
||||
piob->piob_flags = PIOB_error;
|
||||
|
||||
release_io_event(file, overlapped_ptr);
|
||||
return nt_error("GetOverlappedResult", file, isc_io_read_err, status_vector);
|
||||
}
|
||||
|
||||
if (!piob || (piob->piob_flags & (PIOB_success | PIOB_error)))
|
||||
release_io_event(file, overlapped_ptr);
|
||||
|
||||
buffer += segmented_length;
|
||||
if (piob)
|
||||
piob->piob_flags = PIOB_success;
|
||||
}
|
||||
else if (piob)
|
||||
{
|
||||
piob->piob_flags = PIOB_pending;
|
||||
piob->piob_desc = reinterpret_cast<SLONG>(desc);
|
||||
piob->piob_file = file;
|
||||
piob->piob_io_length = segmented_length;
|
||||
}
|
||||
else if (!GetOverlappedResult(desc, overlapped_ptr, &actual_length, TRUE) ||
|
||||
actual_length != length)
|
||||
{
|
||||
if (piob)
|
||||
piob->piob_flags = PIOB_error;
|
||||
|
||||
release_io_event(file, overlapped_ptr);
|
||||
return nt_error("GetOverlappedResult", file, isc_io_read_err, status_vector);
|
||||
}
|
||||
|
||||
if (!piob || (piob->piob_flags & (PIOB_success | PIOB_error)))
|
||||
release_io_event(file, overlapped_ptr);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -767,8 +703,7 @@ bool PIO_write(thread_db* tdbb, jrd_file* file, BufferDesc* bdb, Ods::pag* page,
|
||||
FileExtendLockGuard extLock(file->fil_ext_lock, false);
|
||||
|
||||
OVERLAPPED overlapped;
|
||||
file = seek_file(file, bdb, &overlapped);
|
||||
if (!file)
|
||||
if (!seek_file(file, bdb, &overlapped))
|
||||
return false;
|
||||
|
||||
HANDLE desc = file->fil_desc;
|
||||
@ -813,9 +748,7 @@ ULONG PIO_get_number_of_pages(const jrd_file* file, const USHORT pagesize)
|
||||
}
|
||||
|
||||
|
||||
static jrd_file* seek_file(jrd_file* file,
|
||||
BufferDesc* bdb,
|
||||
OVERLAPPED* overlapped)
|
||||
static bool seek_file(jrd_file* file, BufferDesc* bdb, OVERLAPPED* overlapped)
|
||||
{
|
||||
/**************************************
|
||||
*
|
||||
@ -824,24 +757,11 @@ static jrd_file* seek_file(jrd_file* file,
|
||||
**************************************
|
||||
*
|
||||
* Functional description
|
||||
* Given a buffer descriptor block, find the appropriate
|
||||
* file block and seek to the proper page in that file.
|
||||
* Given a buffer descriptor block, seek to the proper page in that file.
|
||||
*
|
||||
**************************************/
|
||||
BufferControl *bcb = bdb->bdb_bcb;
|
||||
ULONG page = bdb->bdb_page.getPageNum();
|
||||
|
||||
for (;; file = file->fil_next)
|
||||
{
|
||||
if (!file) {
|
||||
CORRUPT(158); // msg 158 database file not available
|
||||
}
|
||||
else if (page >= file->fil_min_page && page <= file->fil_max_page) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
page -= file->fil_min_page - file->fil_fudge;
|
||||
BufferControl* const bcb = bdb->bdb_bcb;
|
||||
const ULONG page = bdb->bdb_page.getPageNum();
|
||||
|
||||
LARGE_INTEGER liOffset;
|
||||
liOffset.QuadPart = UInt32x32To64((DWORD) page, (DWORD) bcb->bcb_page_size);
|
||||
@ -854,7 +774,7 @@ static jrd_file* seek_file(jrd_file* file,
|
||||
ThreadSync* thd = ThreadSync::getThread(FB_FUNCTION);
|
||||
overlapped->hEvent = thd->getIOEvent();
|
||||
|
||||
return file;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -876,7 +796,6 @@ static jrd_file* setup_file(Database* dbb, const Firebird::PathName& file_name,
|
||||
{
|
||||
file = FB_NEW_RPT(*dbb->dbb_permanent, file_name.length() + 1) jrd_file();
|
||||
file->fil_desc = desc;
|
||||
file->fil_max_page = MAX_ULONG;
|
||||
file->fil_flags = flags;
|
||||
strcpy(file->fil_string, file_name.c_str());
|
||||
|
||||
|
294
src/jrd/pag.cpp
294
src/jrd/pag.cpp
@ -364,115 +364,6 @@ namespace
|
||||
} // namespace
|
||||
|
||||
|
||||
USHORT PAG_add_file(thread_db* tdbb, const TEXT* file_name, SLONG start)
|
||||
{
|
||||
/**************************************
|
||||
*
|
||||
* P A G _ a d d _ f i l e
|
||||
*
|
||||
**************************************
|
||||
*
|
||||
* Functional description
|
||||
* Add a file to the current database. Return the sequence number for the new file.
|
||||
*
|
||||
**************************************/
|
||||
SET_TDBB(tdbb);
|
||||
ensureDbWritable(tdbb);
|
||||
|
||||
const auto dbb = tdbb->getDatabase();
|
||||
|
||||
// Find current last file
|
||||
|
||||
PageSpace* pageSpace = dbb->dbb_page_manager.findPageSpace(DB_PAGE_SPACE);
|
||||
jrd_file* file = pageSpace->file;
|
||||
while (file->fil_next) {
|
||||
file = file->fil_next;
|
||||
}
|
||||
|
||||
// Verify database file path against DatabaseAccess entry of firebird.conf
|
||||
if (!JRD_verify_database_access(file_name))
|
||||
{
|
||||
string fileName(file_name);
|
||||
ISC_systemToUtf8(fileName);
|
||||
ERR_post(Arg::Gds(isc_conf_access_denied) << Arg::Str("additional database file") <<
|
||||
Arg::Str(fileName));
|
||||
}
|
||||
|
||||
// Create the file. If the sequence number comes back zero, it didn't work, so punt
|
||||
|
||||
const USHORT sequence = PIO_add_file(tdbb, pageSpace->file, file_name, start);
|
||||
if (!sequence)
|
||||
return 0;
|
||||
|
||||
// Create header page for new file
|
||||
|
||||
jrd_file* next = file->fil_next;
|
||||
|
||||
WIN window(DB_PAGE_SPACE, next->fil_min_page);
|
||||
header_page* header = (header_page*) CCH_fake(tdbb, &window, 1);
|
||||
header->hdr_header.pag_type = pag_header;
|
||||
header->hdr_sequence = sequence;
|
||||
header->hdr_page_size = dbb->dbb_page_size;
|
||||
header->hdr_data[0] = HDR_end;
|
||||
header->hdr_end = HDR_SIZE;
|
||||
next->fil_sequence = sequence;
|
||||
|
||||
#ifdef SUPPORT_RAW_DEVICES
|
||||
// The following lines (taken from PAG_format_header) are needed to identify
|
||||
// this file in raw_devices_validate_database as a valid database attachment.
|
||||
*(ISC_TIMESTAMP*) header->hdr_creation_date = TimeZoneUtil::getCurrentGmtTimeStamp().utc_timestamp;
|
||||
// should we include milliseconds or not?
|
||||
//TimeStamp::round_time(header->hdr_creation_date->timestamp_time, 0);
|
||||
|
||||
header->hdr_ods_version = ODS_VERSION | ODS_FIREBIRD_FLAG;
|
||||
DbImplementation::current.store(header);
|
||||
header->hdr_ods_minor = ODS_CURRENT;
|
||||
if (dbb->dbb_flags & DBB_DB_SQL_dialect_3)
|
||||
header->hdr_flags |= hdr_SQL_dialect_3;
|
||||
#endif
|
||||
|
||||
header->hdr_header.pag_pageno = window.win_page.getPageNum();
|
||||
// It's header, never encrypted
|
||||
PIO_write(tdbb, pageSpace->file, window.win_bdb, window.win_buffer, tdbb->tdbb_status_vector);
|
||||
CCH_RELEASE(tdbb, &window);
|
||||
next->fil_fudge = 1;
|
||||
|
||||
// Update the previous header page to point to new file
|
||||
|
||||
file->fil_fudge = 0;
|
||||
window.win_page = file->fil_min_page;
|
||||
header = (header_page*) CCH_FETCH(tdbb, &window, LCK_write, pag_header);
|
||||
if (!file->fil_min_page)
|
||||
CCH_MARK_MUST_WRITE(tdbb, &window);
|
||||
else
|
||||
CCH_MARK(tdbb, &window);
|
||||
|
||||
--start;
|
||||
|
||||
if (file->fil_min_page)
|
||||
{
|
||||
PAG_add_header_entry(tdbb, header, HDR_file, static_cast<USHORT>(strlen(file_name)),
|
||||
reinterpret_cast<const UCHAR*>(file_name));
|
||||
PAG_add_header_entry(tdbb, header, HDR_last_page, sizeof(SLONG), (UCHAR*) &start);
|
||||
}
|
||||
else
|
||||
{
|
||||
storeClump(tdbb, HDR_file, static_cast<USHORT>(strlen(file_name)),
|
||||
reinterpret_cast<const UCHAR*>(file_name));
|
||||
storeClump(tdbb, HDR_last_page, sizeof(SLONG), (UCHAR*) &start);
|
||||
}
|
||||
|
||||
header->hdr_header.pag_pageno = window.win_page.getPageNum();
|
||||
// It's header, never encrypted
|
||||
PIO_write(tdbb, pageSpace->file, window.win_bdb, window.win_buffer, tdbb->tdbb_status_vector);
|
||||
CCH_RELEASE(tdbb, &window);
|
||||
if (file->fil_min_page)
|
||||
file->fil_fudge = 1;
|
||||
|
||||
return sequence;
|
||||
}
|
||||
|
||||
|
||||
void PAG_add_header_entry(thread_db* tdbb, header_page* header,
|
||||
USHORT type, USHORT len, const UCHAR* entry)
|
||||
{
|
||||
@ -1134,8 +1025,7 @@ void PAG_header(thread_db* tdbb, bool info, const TriState newForceWrite)
|
||||
|
||||
// Ensure the file-level FW mode matches the actual FW mode in the database
|
||||
const auto pageSpace = dbb->dbb_page_manager.findPageSpace(DB_PAGE_SPACE);
|
||||
for (jrd_file* file = pageSpace->file; file; file = file->fil_next)
|
||||
PIO_force_write(file, forceWrite && !readOnly);
|
||||
PIO_force_write(pageSpace->file, forceWrite && !readOnly);
|
||||
|
||||
if (dbb->dbb_backup_manager->getState() != Ods::hdr_nbak_normal)
|
||||
dbb->dbb_backup_manager->setForcedWrites(forceWrite);
|
||||
@ -1310,7 +1200,7 @@ void PAG_init(thread_db* tdbb)
|
||||
}
|
||||
|
||||
|
||||
void PAG_init2(thread_db* tdbb, USHORT shadow_number)
|
||||
void PAG_init2(thread_db* tdbb)
|
||||
{
|
||||
/**************************************
|
||||
*
|
||||
@ -1319,128 +1209,40 @@ void PAG_init2(thread_db* tdbb, USHORT shadow_number)
|
||||
**************************************
|
||||
*
|
||||
* Functional description
|
||||
* Perform second phase of page initialization -- the eternal
|
||||
* search for additional files.
|
||||
* Read and apply the database header options.
|
||||
*
|
||||
**************************************/
|
||||
SET_TDBB(tdbb);
|
||||
const auto dbb = tdbb->getDatabase();
|
||||
|
||||
FbStatusVector* status = tdbb->tdbb_status_vector;
|
||||
WIN window(HEADER_PAGE_NUMBER);
|
||||
const auto header = (header_page*) CCH_FETCH(tdbb, &window, LCK_read, pag_header);
|
||||
|
||||
// allocate a spare buffer which is large enough,
|
||||
// and set up to release it in case of error. Align
|
||||
// the temporary page buffer for raw disk access.
|
||||
|
||||
Array<UCHAR> temp;
|
||||
UCHAR* const temp_page = temp.getAlignedBuffer(dbb->dbb_page_size, dbb->getIOBlockSize());
|
||||
|
||||
PageSpace* const pageSpace = dbb->dbb_page_manager.findPageSpace(DB_PAGE_SPACE);
|
||||
jrd_file* file = pageSpace->file;
|
||||
if (shadow_number)
|
||||
for (const UCHAR* p = header->hdr_data; *p != HDR_end; p += 2 + p[1])
|
||||
{
|
||||
Shadow* shadow = dbb->dbb_shadow;
|
||||
for (; shadow; shadow = shadow->sdw_next)
|
||||
switch (*p)
|
||||
{
|
||||
if (shadow->sdw_number == shadow_number)
|
||||
{
|
||||
file = shadow->sdw_file;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!shadow)
|
||||
BUGCHECK(161); // msg 161 shadow block not found
|
||||
}
|
||||
|
||||
USHORT sequence = 1;
|
||||
WIN window(DB_PAGE_SPACE, -1);
|
||||
|
||||
// Loop thru files and header pages until everything is open
|
||||
|
||||
while (true)
|
||||
{
|
||||
window.win_page = file->fil_min_page;
|
||||
ULONG last_page = 0;
|
||||
BufferDesc temp_bdb(dbb->dbb_bcb);
|
||||
|
||||
// note that we do not have to get a read lock on
|
||||
// the header page (except for header page 0) because
|
||||
// the only time it will be modified is when adding a file,
|
||||
// which must be done with an exclusive lock on the database --
|
||||
// if this changes, this policy will have to be reevaluated;
|
||||
// at any rate there is a problem with getting a read lock
|
||||
// because the corresponding page in the main database file may not exist
|
||||
|
||||
if (!file->fil_min_page)
|
||||
CCH_FETCH(tdbb, &window, LCK_read, pag_header);
|
||||
|
||||
header_page* header = (header_page*) temp_page;
|
||||
temp_bdb.bdb_buffer = (pag*) header;
|
||||
temp_bdb.bdb_page = window.win_page;
|
||||
|
||||
// Read the required page into the local buffer
|
||||
// It's header, never encrypted
|
||||
PIO_read(tdbb, file, &temp_bdb, (PAG) header, status);
|
||||
|
||||
if (shadow_number && !file->fil_min_page)
|
||||
CCH_RELEASE(tdbb, &window);
|
||||
|
||||
PathName nextFileName;
|
||||
|
||||
for (const UCHAR* p = header->hdr_data; *p != HDR_end; p += 2 + p[1])
|
||||
{
|
||||
switch (*p)
|
||||
{
|
||||
case HDR_file:
|
||||
nextFileName.assign(p + 2, p[1]);
|
||||
break;
|
||||
|
||||
case HDR_last_page:
|
||||
fb_assert(p[1] == sizeof(last_page));
|
||||
memcpy(&last_page, p + 2, sizeof(last_page));
|
||||
break;
|
||||
|
||||
case HDR_sweep_interval:
|
||||
fb_assert(p[1] == sizeof(SLONG));
|
||||
memcpy(&dbb->dbb_sweep_interval, p + 2, sizeof(SLONG));
|
||||
break;
|
||||
|
||||
case HDR_db_guid:
|
||||
fb_assert(p[1] == Guid::SIZE);
|
||||
dbb->dbb_guid = Guid(p + 2);
|
||||
break;
|
||||
|
||||
case HDR_repl_seq:
|
||||
fb_assert(p[1] == sizeof(FB_UINT64));
|
||||
memcpy(&dbb->dbb_repl_sequence, p + 2, sizeof(FB_UINT64));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!shadow_number && !file->fil_min_page)
|
||||
CCH_RELEASE(tdbb, &window);
|
||||
|
||||
if (file->fil_min_page)
|
||||
file->fil_fudge = 1;
|
||||
|
||||
if (nextFileName.isEmpty())
|
||||
case HDR_sweep_interval:
|
||||
fb_assert(p[1] == sizeof(SLONG));
|
||||
memcpy(&dbb->dbb_sweep_interval, p + 2, sizeof(SLONG));
|
||||
break;
|
||||
|
||||
// Verify database file path against DatabaseAccess entry of firebird.conf
|
||||
if (!JRD_verify_database_access(nextFileName))
|
||||
{
|
||||
ISC_systemToUtf8(nextFileName);
|
||||
ERR_post(Arg::Gds(isc_conf_access_denied) << Arg::Str("additional database file") <<
|
||||
Arg::Str(nextFileName));
|
||||
case HDR_db_guid:
|
||||
fb_assert(p[1] == Guid::SIZE);
|
||||
dbb->dbb_guid = Guid(p + 2);
|
||||
break;
|
||||
|
||||
case HDR_repl_seq:
|
||||
fb_assert(p[1] == sizeof(FB_UINT64));
|
||||
memcpy(&dbb->dbb_repl_sequence, p + 2, sizeof(FB_UINT64));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
file->fil_next = PIO_open(tdbb, nextFileName, nextFileName);
|
||||
file->fil_max_page = last_page;
|
||||
file = file->fil_next;
|
||||
|
||||
file->fil_min_page = last_page + 1;
|
||||
file->fil_sequence = sequence++;
|
||||
}
|
||||
|
||||
CCH_RELEASE(tdbb, &window);
|
||||
}
|
||||
|
||||
|
||||
@ -1627,14 +1429,10 @@ void PAG_set_force_write(thread_db* tdbb, bool flag)
|
||||
CCH_RELEASE(tdbb, &window);
|
||||
|
||||
PageSpace* const pageSpace = dbb->dbb_page_manager.findPageSpace(DB_PAGE_SPACE);
|
||||
for (jrd_file* file = pageSpace->file; file; file = file->fil_next)
|
||||
PIO_force_write(file, flag);
|
||||
PIO_force_write(pageSpace->file, flag);
|
||||
|
||||
for (Shadow* shadow = dbb->dbb_shadow; shadow; shadow = shadow->sdw_next)
|
||||
{
|
||||
for (jrd_file* file = shadow->sdw_file; file; file = file->fil_next)
|
||||
PIO_force_write(file, flag);
|
||||
}
|
||||
PIO_force_write(shadow->sdw_file, flag);
|
||||
|
||||
if (dbb->dbb_backup_manager->getState() != Ods::hdr_nbak_normal)
|
||||
dbb->dbb_backup_manager->setForcedWrites(flag);
|
||||
@ -1898,13 +1696,7 @@ PageSpace::~PageSpace()
|
||||
if (file)
|
||||
{
|
||||
PIO_close(file);
|
||||
|
||||
while (file)
|
||||
{
|
||||
jrd_file* next = file->fil_next;
|
||||
delete file;
|
||||
file = next;
|
||||
}
|
||||
delete file;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1917,15 +1709,7 @@ ULONG PageSpace::actAlloc()
|
||||
*
|
||||
**************************************/
|
||||
|
||||
// Traverse the linked list of files and add up the
|
||||
// number of pages in each file
|
||||
const USHORT pageSize = dbb->dbb_page_size;
|
||||
ULONG tot_pages = 0;
|
||||
for (const jrd_file* f = file; f != NULL; f = f->fil_next) {
|
||||
tot_pages += PIO_get_number_of_pages(f, pageSize);
|
||||
}
|
||||
|
||||
return tot_pages;
|
||||
return PIO_get_number_of_pages(file, dbb->dbb_page_size);
|
||||
}
|
||||
|
||||
ULONG PageSpace::actAlloc(const Database* dbb)
|
||||
@ -1942,17 +1726,7 @@ ULONG PageSpace::maxAlloc()
|
||||
* Compute last physically allocated page of database.
|
||||
*
|
||||
**************************************/
|
||||
const USHORT pageSize = dbb->dbb_page_size;
|
||||
const jrd_file* f = file;
|
||||
ULONG nPages = PIO_get_number_of_pages(f, pageSize);
|
||||
|
||||
while (f->fil_next && nPages == f->fil_max_page - f->fil_min_page + 1 + f->fil_fudge)
|
||||
{
|
||||
f = f->fil_next;
|
||||
nPages = PIO_get_number_of_pages(f, pageSize);
|
||||
}
|
||||
|
||||
nPages += f->fil_min_page - f->fil_fudge;
|
||||
const ULONG nPages = PIO_get_number_of_pages(file, dbb->dbb_page_size);
|
||||
|
||||
if (maxPageNumber < nPages)
|
||||
maxPageNumber = nPages;
|
||||
@ -1968,15 +1742,7 @@ ULONG PageSpace::maxAlloc(const Database* dbb)
|
||||
|
||||
bool PageSpace::onRawDevice() const
|
||||
{
|
||||
#ifdef SUPPORT_RAW_DEVICES
|
||||
for (const jrd_file* f = file; f != NULL; f = f->fil_next)
|
||||
{
|
||||
if (f->fil_flags & FIL_raw_device)
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
return false;
|
||||
return (file->fil_flags & FIL_raw_device) != 0;
|
||||
}
|
||||
|
||||
ULONG PageSpace::lastUsedPage()
|
||||
|
@ -37,7 +37,6 @@ namespace Ods {
|
||||
struct header_page;
|
||||
}
|
||||
|
||||
USHORT PAG_add_file(Jrd::thread_db* tdbb, const TEXT*, SLONG);
|
||||
void PAG_add_header_entry(Jrd::thread_db* tdbb, Ods::header_page*, USHORT, USHORT, const UCHAR*);
|
||||
bool PAG_replace_entry_first(Jrd::thread_db* tdbb, Ods::header_page*, USHORT, USHORT, const UCHAR*);
|
||||
Ods::pag* PAG_allocate_pages(Jrd::thread_db* tdbb, Jrd::win* window, unsigned cntAlloc, bool aligned);
|
||||
@ -49,7 +48,7 @@ bool PAG_get_clump(Jrd::thread_db*, USHORT, USHORT*, UCHAR*);
|
||||
void PAG_header(Jrd::thread_db*, bool, const Firebird::TriState newForceWrite = Firebird::TriState());
|
||||
void PAG_header_init(Jrd::thread_db*);
|
||||
void PAG_init(Jrd::thread_db*);
|
||||
void PAG_init2(Jrd::thread_db*, USHORT);
|
||||
void PAG_init2(Jrd::thread_db*);
|
||||
SLONG PAG_last_page(Jrd::thread_db* tdbb);
|
||||
void PAG_release_page(Jrd::thread_db* tdbb, const Jrd::PageNumber&, const Jrd::PageNumber&);
|
||||
void PAG_release_pages(Jrd::thread_db* tdbb, USHORT pageSpaceID, int cntRelease,
|
||||
|
@ -1519,7 +1519,7 @@ DmlNode* PAR_parse_node(thread_db* tdbb, CompilerScratch* csb)
|
||||
const ULONG blrOffset = csb->csb_blr_reader.getOffset();
|
||||
const SSHORT blrOperator = csb->csb_blr_reader.getByte();
|
||||
|
||||
if (blrOperator < 0 || blrOperator >= FB_NELEM(blr_parsers))
|
||||
if (blrOperator < 0 || static_cast<FB_SIZE_T>(blrOperator) >= FB_NELEM(blr_parsers))
|
||||
{
|
||||
// NS: This error string is correct, please do not mangle it again and again.
|
||||
// The whole error message is "BLR syntax error: expected %s at offset %d, encountered %d"
|
||||
|
@ -37,10 +37,13 @@ using namespace Jrd;
|
||||
// Data access: full outer join
|
||||
// ----------------------------
|
||||
|
||||
FullOuterJoin::FullOuterJoin(CompilerScratch* csb, RecordSource* arg1, RecordSource* arg2)
|
||||
FullOuterJoin::FullOuterJoin(CompilerScratch* csb,
|
||||
RecordSource* arg1, RecordSource* arg2,
|
||||
const StreamList& checkStreams)
|
||||
: RecordSource(csb),
|
||||
m_arg1(arg1),
|
||||
m_arg2(arg2)
|
||||
m_arg2(arg2),
|
||||
m_checkStreams(csb->csb_pool, checkStreams)
|
||||
{
|
||||
fb_assert(m_arg1 && m_arg2);
|
||||
|
||||
@ -97,7 +100,27 @@ bool FullOuterJoin::internalGetRecord(thread_db* tdbb) const
|
||||
m_arg2->open(tdbb);
|
||||
}
|
||||
|
||||
return m_arg2->getRecord(tdbb);
|
||||
// We should exclude matching records from the right-joined (second) record source,
|
||||
// as they're already returned from the left-joined (first) record source
|
||||
|
||||
while (m_arg2->getRecord(tdbb))
|
||||
{
|
||||
bool matched = false;
|
||||
|
||||
for (const auto stream : m_checkStreams)
|
||||
{
|
||||
if (request->req_rpb[stream].rpb_number.isValid())
|
||||
{
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!matched)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FullOuterJoin::refetchRecord(thread_db* /*tdbb*/) const
|
||||
|
@ -53,10 +53,11 @@ NestedLoopJoin::NestedLoopJoin(CompilerScratch* csb, FB_SIZE_T count, RecordSour
|
||||
}
|
||||
}
|
||||
|
||||
NestedLoopJoin::NestedLoopJoin(CompilerScratch* csb, RecordSource* outer, RecordSource* inner,
|
||||
BoolExprNode* boolean, JoinType joinType)
|
||||
NestedLoopJoin::NestedLoopJoin(CompilerScratch* csb,
|
||||
RecordSource* outer, RecordSource* inner,
|
||||
BoolExprNode* boolean)
|
||||
: RecordSource(csb),
|
||||
m_joinType(joinType),
|
||||
m_joinType(OUTER_JOIN),
|
||||
m_args(csb->csb_pool),
|
||||
m_boolean(boolean)
|
||||
{
|
||||
|
@ -1137,7 +1137,7 @@ namespace Jrd
|
||||
public:
|
||||
NestedLoopJoin(CompilerScratch* csb, FB_SIZE_T count, RecordSource* const* args);
|
||||
NestedLoopJoin(CompilerScratch* csb, RecordSource* outer, RecordSource* inner,
|
||||
BoolExprNode* boolean, JoinType joinType);
|
||||
BoolExprNode* boolean);
|
||||
|
||||
void close(thread_db* tdbb) const override;
|
||||
|
||||
@ -1168,7 +1168,8 @@ namespace Jrd
|
||||
class FullOuterJoin : public RecordSource
|
||||
{
|
||||
public:
|
||||
FullOuterJoin(CompilerScratch* csb, RecordSource* arg1, RecordSource* arg2);
|
||||
FullOuterJoin(CompilerScratch* csb, RecordSource* arg1, RecordSource* arg2,
|
||||
const StreamList& checkStreams);
|
||||
|
||||
void close(thread_db* tdbb) const override;
|
||||
|
||||
@ -1191,6 +1192,7 @@ namespace Jrd
|
||||
private:
|
||||
NestConst<RecordSource> m_arg1;
|
||||
NestConst<RecordSource> m_arg2;
|
||||
const StreamList m_checkStreams;
|
||||
};
|
||||
|
||||
class HashJoin : public RecordSource
|
||||
|
204
src/jrd/sdw.cpp
204
src/jrd/sdw.cpp
@ -64,7 +64,6 @@ static bool check_for_file(thread_db* tdbb, const SCHAR*, USHORT);
|
||||
#ifdef NOT_USED_OR_REPLACED
|
||||
static void check_if_got_ast(thread_db* tdbb, jrd_file*);
|
||||
#endif
|
||||
static void copy_header(thread_db* tdbb);
|
||||
static void update_dbb_to_sdw(Database*);
|
||||
|
||||
|
||||
@ -112,147 +111,6 @@ void SDW_add(thread_db* tdbb, const TEXT* file_name, USHORT shadow_number, USHOR
|
||||
}
|
||||
|
||||
|
||||
int SDW_add_file(thread_db* tdbb, const TEXT* file_name, SLONG start, USHORT shadow_number)
|
||||
{
|
||||
/**************************************
|
||||
*
|
||||
* S D W _ a d d _ f i l e
|
||||
*
|
||||
**************************************
|
||||
*
|
||||
* Functional description
|
||||
* Add a file to a shadow set.
|
||||
* Return the sequence number for the new file.
|
||||
*
|
||||
**************************************/
|
||||
SET_TDBB(tdbb);
|
||||
Database* dbb = tdbb->getDatabase();
|
||||
|
||||
SyncLockGuard guard(&dbb->dbb_shadow_sync, SYNC_EXCLUSIVE, "SDW_add_file");
|
||||
|
||||
// Find the file to be extended
|
||||
|
||||
jrd_file* shadow_file = 0;
|
||||
Shadow* shadow;
|
||||
for (shadow = dbb->dbb_shadow; shadow; shadow = shadow->sdw_next)
|
||||
{
|
||||
if ((shadow->sdw_number == shadow_number) &&
|
||||
!(shadow->sdw_flags & (SDW_IGNORE | SDW_rollover)))
|
||||
{
|
||||
shadow_file = shadow->sdw_file;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!shadow) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// find the last file in the list, open the new file
|
||||
|
||||
jrd_file* file = shadow_file;
|
||||
while (file->fil_next) {
|
||||
file = file->fil_next;
|
||||
}
|
||||
|
||||
// Verify shadow file path against DatabaseAccess entry of firebird.conf
|
||||
if (!JRD_verify_database_access(file_name))
|
||||
{
|
||||
ERR_post(Arg::Gds(isc_conf_access_denied) << Arg::Str("database shadow") <<
|
||||
Arg::Str(file_name));
|
||||
}
|
||||
|
||||
const SLONG sequence = PIO_add_file(tdbb, shadow_file, file_name, start);
|
||||
if (!sequence)
|
||||
return 0;
|
||||
|
||||
jrd_file* next = file->fil_next;
|
||||
|
||||
// Always write the header page, even for a conditional
|
||||
// shadow that hasn't been activated.
|
||||
|
||||
// allocate a spare buffer which is large enough,
|
||||
// and set up to release it in case of error. Align
|
||||
// the spare page buffer for raw disk access.
|
||||
|
||||
Array<UCHAR> temp;
|
||||
UCHAR* const spare_page = temp.getAlignedBuffer(dbb->dbb_page_size, dbb->getIOBlockSize());
|
||||
|
||||
// create the header using the spare_buffer
|
||||
|
||||
header_page* header = (header_page*) spare_page;
|
||||
header->hdr_header.pag_type = pag_header;
|
||||
header->hdr_sequence = sequence;
|
||||
header->hdr_page_size = dbb->dbb_page_size;
|
||||
header->hdr_data[0] = HDR_end;
|
||||
header->hdr_end = HDR_SIZE;
|
||||
|
||||
// fool PIO_write into writing the scratch page into the correct place
|
||||
BufferDesc temp_bdb(dbb->dbb_bcb);
|
||||
temp_bdb.bdb_page = next->fil_min_page;
|
||||
temp_bdb.bdb_buffer = (PAG) header;
|
||||
header->hdr_header.pag_pageno = temp_bdb.bdb_page.getPageNum();
|
||||
// It's header, never encrypted
|
||||
if (!PIO_write(tdbb, shadow_file, &temp_bdb, reinterpret_cast<Ods::pag*>(header), 0))
|
||||
return 0;
|
||||
|
||||
next->fil_fudge = 1;
|
||||
|
||||
// Update the previous header page to point to new file --
|
||||
// we can use the same header page, suitably modified,
|
||||
// because they all look pretty much the same at this point
|
||||
|
||||
/*******************
|
||||
Fix for bug 7925. drop_gdb wan not dropping secondary file in
|
||||
multi-shadow files. The structure was not being filled with the
|
||||
info. Commented some code so that the structure will always be filled.
|
||||
|
||||
-Sudesh 07/06/95
|
||||
|
||||
The original code :
|
||||
===
|
||||
if (shadow_file == file)
|
||||
copy_header(tdbb);
|
||||
else
|
||||
===
|
||||
************************/
|
||||
|
||||
// Temporarly reverting the change ------- Sudesh 07/07/95 *******
|
||||
|
||||
if (shadow_file == file)
|
||||
{
|
||||
copy_header(tdbb);
|
||||
}
|
||||
else
|
||||
{
|
||||
--start;
|
||||
header->hdr_data[0] = HDR_end;
|
||||
header->hdr_end = HDR_SIZE;
|
||||
|
||||
PAG_add_header_entry(tdbb, header, HDR_file, static_cast<USHORT>(strlen(file_name)),
|
||||
reinterpret_cast<const UCHAR*>(file_name));
|
||||
PAG_add_header_entry(tdbb, header, HDR_last_page, sizeof(start),
|
||||
reinterpret_cast<const UCHAR*>(&start));
|
||||
file->fil_fudge = 0;
|
||||
temp_bdb.bdb_page = file->fil_min_page;
|
||||
header->hdr_header.pag_pageno = temp_bdb.bdb_page.getPageNum();
|
||||
// It's header, never encrypted
|
||||
if (!PIO_write(tdbb, shadow_file, &temp_bdb, reinterpret_cast<Ods::pag*>(header), 0))
|
||||
return 0;
|
||||
|
||||
if (file->fil_min_page) {
|
||||
file->fil_fudge = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (file->fil_min_page) {
|
||||
file->fil_fudge = 1;
|
||||
}
|
||||
|
||||
return sequence;
|
||||
}
|
||||
|
||||
|
||||
void SDW_check(thread_db* tdbb)
|
||||
{
|
||||
/**************************************
|
||||
@ -814,12 +672,7 @@ bool SDW_rollover_to_shadow(thread_db* tdbb, jrd_file* file, const bool inAst)
|
||||
// close the main database file if possible and release all file blocks
|
||||
|
||||
PIO_close(pageSpace->file);
|
||||
|
||||
while ( (file = pageSpace->file) )
|
||||
{
|
||||
pageSpace->file = file->fil_next;
|
||||
delete file;
|
||||
}
|
||||
delete pageSpace->file;
|
||||
|
||||
/* point the main database file at the file of the first shadow
|
||||
in the list and mark that shadow as rolled over to
|
||||
@ -894,13 +747,7 @@ static void shutdown_shadow(Shadow* shadow)
|
||||
// close the shadow files and free up the associated memory
|
||||
|
||||
PIO_close(shadow->sdw_file);
|
||||
jrd_file* file;
|
||||
jrd_file* free = shadow->sdw_file;
|
||||
|
||||
for (; (file = free->fil_next); free = file)
|
||||
delete free;
|
||||
|
||||
delete free;
|
||||
delete shadow->sdw_file;
|
||||
delete shadow;
|
||||
}
|
||||
|
||||
@ -1003,12 +850,6 @@ void SDW_start(thread_db* tdbb, const TEXT* file_name,
|
||||
|
||||
const header_page* shadow_header = (header_page*) spare_page;
|
||||
|
||||
// NOTE ! NOTE! NOTE!
|
||||
// Starting V4.0, header pages can have overflow pages. For the shadow,
|
||||
// we are making an assumption that the shadow header page will not
|
||||
// overflow, as the only things written on a shadow header is the
|
||||
// HDR_root_file_name, HDR_file, and HDR_last_page
|
||||
|
||||
const UCHAR* p = shadow_header->hdr_data;
|
||||
while (*p != HDR_end && *p != HDR_root_file_name) {
|
||||
p += 2 + p[1];
|
||||
@ -1046,10 +887,6 @@ void SDW_start(thread_db* tdbb, const TEXT* file_name,
|
||||
shadow->sdw_flags |= SDW_dumped;
|
||||
}
|
||||
|
||||
// get the ancillary files and reset the error environment
|
||||
|
||||
PAG_init2(tdbb, shadow_number);
|
||||
|
||||
} // try
|
||||
catch (const Firebird::Exception& ex)
|
||||
{
|
||||
@ -1257,35 +1094,6 @@ static void check_if_got_ast(thread_db* tdbb, jrd_file* file)
|
||||
}
|
||||
#endif
|
||||
|
||||
static void copy_header(thread_db* tdbb)
|
||||
{
|
||||
/**************************************
|
||||
*
|
||||
* c o p y _ h e a d e r
|
||||
*
|
||||
**************************************
|
||||
*
|
||||
* Functional description
|
||||
* Fetch the header page from the database
|
||||
* and write it to the shadow file. This is
|
||||
* done so that if this shadow is extended,
|
||||
* the header page will be there for writing
|
||||
* the name of the extend file.
|
||||
*
|
||||
**************************************/
|
||||
SET_TDBB(tdbb);
|
||||
Database* dbb = tdbb->getDatabase();
|
||||
CHECK_DBB(dbb);
|
||||
|
||||
// get the database header page and write it out --
|
||||
// CCH will take care of modifying it
|
||||
|
||||
WIN window(HEADER_PAGE_NUMBER);
|
||||
CCH_FETCH(tdbb, &window, LCK_write, pag_header);
|
||||
CCH_MARK_MUST_WRITE(tdbb, &window);
|
||||
CCH_RELEASE(tdbb, &window);
|
||||
}
|
||||
|
||||
|
||||
static void update_dbb_to_sdw(Database* dbb)
|
||||
{
|
||||
@ -1319,13 +1127,7 @@ static void update_dbb_to_sdw(Database* dbb)
|
||||
// hvlad: need sync for this code
|
||||
PageSpace* pageSpace = dbb->dbb_page_manager.findPageSpace(DB_PAGE_SPACE);
|
||||
PIO_close(pageSpace->file);
|
||||
|
||||
jrd_file* file;
|
||||
while ( (file = pageSpace->file) )
|
||||
{
|
||||
pageSpace->file = file->fil_next;
|
||||
delete file;
|
||||
}
|
||||
delete pageSpace->file;
|
||||
|
||||
pageSpace->file = shadow->sdw_file;
|
||||
shadow->sdw_flags |= SDW_rollover;
|
||||
|
@ -29,7 +29,6 @@ namespace Jrd {
|
||||
}
|
||||
|
||||
void SDW_add(Jrd::thread_db* tdbb, const TEXT*, USHORT, USHORT);
|
||||
int SDW_add_file(Jrd::thread_db* tdbb, const TEXT*, SLONG, USHORT);
|
||||
void SDW_check(Jrd::thread_db* tdbb);
|
||||
bool SDW_check_conditional(Jrd::thread_db* tdbb);
|
||||
void SDW_close();
|
||||
|
@ -1525,7 +1525,7 @@ void Sort::mergeRuns(USHORT n)
|
||||
// and there n < RUN_GROUP * MAX_MERGE_LEVEL
|
||||
merge_control blks[RUN_GROUP * MAX_MERGE_LEVEL];
|
||||
|
||||
fb_assert((n - 1) <= FB_NELEM(blks)); // stack var big enough?
|
||||
fb_assert(static_cast<FB_SIZE_T>(n - 1) <= FB_NELEM(blks)); // stack var big enough?
|
||||
|
||||
m_longs -= SIZEOF_SR_BCKPTR_IN_LONGS;
|
||||
|
||||
|
@ -478,7 +478,6 @@ enum dfw_t {
|
||||
dfw_create_index,
|
||||
dfw_delete_index,
|
||||
dfw_compute_security,
|
||||
dfw_add_file,
|
||||
dfw_add_shadow,
|
||||
dfw_delete_shadow,
|
||||
dfw_delete_shadow_nodelete,
|
||||
|
@ -2240,27 +2240,32 @@ bool VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction)
|
||||
case rel_files:
|
||||
protect_system_table_delupd(tdbb, relation, "DELETE");
|
||||
{
|
||||
const bool name_defined = EVL_field(0, rpb->rpb_record, f_file_name, &desc);
|
||||
const USHORT file_flags = EVL_field(0, rpb->rpb_record, f_file_flags, &desc2) ?
|
||||
const bool nameDefined = EVL_field(0, rpb->rpb_record, f_file_name, &desc);
|
||||
|
||||
const auto shadowNumber = EVL_field(0, rpb->rpb_record, f_file_shad_num, &desc2) ?
|
||||
MOV_get_long(tdbb, &desc2, 0) : 0;
|
||||
if (file_flags & FILE_difference)
|
||||
|
||||
const auto fileFlags = EVL_field(0, rpb->rpb_record, f_file_flags, &desc2) ?
|
||||
MOV_get_long(tdbb, &desc2, 0) : 0;
|
||||
|
||||
if (shadowNumber)
|
||||
{
|
||||
if (file_flags & FILE_backing_up)
|
||||
DFW_post_work(transaction, dfw_end_backup, &desc, 0);
|
||||
if (name_defined)
|
||||
DFW_post_work(transaction, dfw_delete_difference, &desc, 0);
|
||||
}
|
||||
else if (EVL_field(0, rpb->rpb_record, f_file_shad_num, &desc2) &&
|
||||
(id = MOV_get_long(tdbb, &desc2, 0)))
|
||||
{
|
||||
if (!(file_flags & FILE_inactive))
|
||||
if (!(fileFlags & FILE_inactive))
|
||||
{
|
||||
if (file_flags & FILE_nodelete)
|
||||
DFW_post_work(transaction, dfw_delete_shadow_nodelete, &desc, id);
|
||||
else
|
||||
DFW_post_work(transaction, dfw_delete_shadow, &desc, id);
|
||||
const auto work = (fileFlags & FILE_nodelete) ?
|
||||
dfw_delete_shadow_nodelete : dfw_delete_shadow;
|
||||
|
||||
DFW_post_work(transaction, work, &desc, shadowNumber);
|
||||
}
|
||||
}
|
||||
else if (fileFlags & FILE_difference)
|
||||
{
|
||||
if (fileFlags & FILE_backing_up)
|
||||
DFW_post_work(transaction, dfw_end_backup, &desc, 0);
|
||||
|
||||
if (nameDefined)
|
||||
DFW_post_work(transaction, dfw_delete_difference, &desc, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@ -3617,18 +3622,24 @@ bool VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j
|
||||
case rel_files:
|
||||
protect_system_table_delupd(tdbb, relation, "UPDATE");
|
||||
{
|
||||
SSHORT new_rel_flags, old_rel_flags;
|
||||
EVL_field(0, new_rpb->rpb_record, f_file_name, &desc1);
|
||||
if (EVL_field(0, new_rpb->rpb_record, f_file_flags, &desc2) &&
|
||||
((new_rel_flags = MOV_get_long(tdbb, &desc2, 0)) & FILE_difference) &&
|
||||
EVL_field(0, org_rpb->rpb_record, f_file_flags, &desc2) &&
|
||||
((old_rel_flags = MOV_get_long(tdbb, &desc2, 0)) != new_rel_flags))
|
||||
|
||||
const auto orgFileFlags = EVL_field(0, org_rpb->rpb_record, f_file_flags, &desc2) ?
|
||||
MOV_get_long(tdbb, &desc2, 0) : 0;
|
||||
const auto newFileFlags = EVL_field(0, new_rpb->rpb_record, f_file_flags, &desc2) ?
|
||||
MOV_get_long(tdbb, &desc2, 0) : 0;
|
||||
|
||||
if ((newFileFlags & FILE_difference) && orgFileFlags != newFileFlags)
|
||||
{
|
||||
DFW_post_work(transaction,
|
||||
(new_rel_flags & FILE_backing_up ? dfw_begin_backup : dfw_end_backup),
|
||||
(newFileFlags & FILE_backing_up) ? dfw_begin_backup : dfw_end_backup,
|
||||
&desc1, 0);
|
||||
}
|
||||
}
|
||||
// Nullify the unsupported fields
|
||||
new_rpb->rpb_record->setNull(f_file_seq);
|
||||
new_rpb->rpb_record->setNull(f_file_start);
|
||||
new_rpb->rpb_record->setNull(f_file_length);
|
||||
break;
|
||||
|
||||
case rel_charsets:
|
||||
@ -4175,34 +4186,32 @@ void VIO_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction)
|
||||
case rel_files:
|
||||
protect_system_table_insert(tdbb, request, relation);
|
||||
{
|
||||
const bool name_defined = EVL_field(0, rpb->rpb_record, f_file_name, &desc);
|
||||
if (EVL_field(0, rpb->rpb_record, f_file_shad_num, &desc2) &&
|
||||
MOV_get_long(tdbb, &desc2, 0))
|
||||
const bool nameDefined = EVL_field(0, rpb->rpb_record, f_file_name, &desc);
|
||||
|
||||
const auto shadowNumber = EVL_field(0, rpb->rpb_record, f_file_shad_num, &desc2) ?
|
||||
MOV_get_long(tdbb, &desc2, 0) : 0;
|
||||
|
||||
const auto fileFlags = EVL_field(0, rpb->rpb_record, f_file_flags, &desc2) ?
|
||||
MOV_get_long(tdbb, &desc2, 0) : 0;
|
||||
|
||||
if (shadowNumber)
|
||||
{
|
||||
EVL_field(0, rpb->rpb_record, f_file_flags, &desc2);
|
||||
if (!(MOV_get_long(tdbb, &desc2, 0) & FILE_inactive)) {
|
||||
if (!(fileFlags & FILE_inactive))
|
||||
DFW_post_work(transaction, dfw_add_shadow, &desc, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (fileFlags & FILE_difference)
|
||||
{
|
||||
USHORT rel_flags;
|
||||
if (EVL_field(0, rpb->rpb_record, f_file_flags, &desc2) &&
|
||||
((rel_flags = MOV_get_long(tdbb, &desc2, 0)) & FILE_difference))
|
||||
{
|
||||
if (name_defined) {
|
||||
DFW_post_work(transaction, dfw_add_difference, &desc, 0);
|
||||
}
|
||||
if (rel_flags & FILE_backing_up)
|
||||
{
|
||||
DFW_post_work(transaction, dfw_begin_backup, &desc, 0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
DFW_post_work(transaction, dfw_add_file, &desc, 0);
|
||||
}
|
||||
if (nameDefined)
|
||||
DFW_post_work(transaction, dfw_add_difference, &desc, 0);
|
||||
|
||||
if (fileFlags & FILE_backing_up)
|
||||
DFW_post_work(transaction, dfw_begin_backup, &desc, 0);
|
||||
}
|
||||
}
|
||||
// Nullify the unsupported fields
|
||||
rpb->rpb_record->setNull(f_file_seq);
|
||||
rpb->rpb_record->setNull(f_file_start);
|
||||
rpb->rpb_record->setNull(f_file_length);
|
||||
break;
|
||||
|
||||
case rel_triggers:
|
||||
|
@ -9,7 +9,7 @@ BuildType=T
|
||||
MajorVer=6
|
||||
MinorVer=0
|
||||
RevNo=0
|
||||
BuildNum=553
|
||||
BuildNum=587
|
||||
|
||||
NowAt=`pwd`
|
||||
cd `dirname $0`
|
||||
|
@ -308,7 +308,7 @@ namespace {
|
||||
public Firebird::AutoIface<Firebird::IListUsersImpl<Callback, Firebird::CheckStatusWrapper> >
|
||||
{
|
||||
public:
|
||||
explicit Callback(StackUserData* pu)
|
||||
explicit Callback(UserData* pu)
|
||||
: u(pu)
|
||||
{ }
|
||||
|
||||
@ -329,7 +329,7 @@ namespace {
|
||||
}
|
||||
|
||||
private:
|
||||
StackUserData* u;
|
||||
UserData* u;
|
||||
};
|
||||
} // anonymous namespace
|
||||
|
||||
@ -356,7 +356,7 @@ int gsec(Firebird::UtilSvc* uSvc)
|
||||
tsec* tdsec = &tsecInstance;
|
||||
tsec::putSpecific(tdsec);
|
||||
|
||||
StackUserData u;
|
||||
UserData u;
|
||||
tdsec->tsec_user_data = &u;
|
||||
|
||||
Firebird::LocalStatus lsManager;
|
||||
@ -549,7 +549,7 @@ int gsec(Firebird::UtilSvc* uSvc)
|
||||
if (user_data->operation() == MOD_OPER && user_data->userName()->entered() &&
|
||||
(fieldSet(&user_data->u) || fieldSet(&user_data->g) || fieldSet(&user_data->group)))
|
||||
{
|
||||
StackUserData u;
|
||||
UserData u;
|
||||
u.op = DIS_OPER;
|
||||
u.user.set(&statusWrapper, user_data->userName()->get());
|
||||
check(&statusWrapper);
|
||||
|
@ -164,10 +164,6 @@ struct dba_rel
|
||||
|
||||
struct dba_fil
|
||||
{
|
||||
dba_fil* fil_next; // Next file in database
|
||||
ULONG fil_min_page; // Minimum page number in file
|
||||
ULONG fil_max_page; // Maximum page number in file
|
||||
USHORT fil_fudge; // Fudge factor for page relocation
|
||||
#ifdef WIN_NT
|
||||
void *fil_desc;
|
||||
#else
|
||||
@ -216,16 +212,6 @@ static void print_help();
|
||||
|
||||
#include "../utilities/gstat/dba_proto.h"
|
||||
|
||||
struct open_files
|
||||
{
|
||||
#ifdef WIN_NT
|
||||
void* desc;
|
||||
#else
|
||||
int desc;
|
||||
#endif
|
||||
open_files* open_files_next;
|
||||
};
|
||||
|
||||
struct dba_mem
|
||||
{
|
||||
char* memory;
|
||||
@ -241,7 +227,7 @@ public:
|
||||
: ThreadData(tddDBA), uSvc(us)
|
||||
{
|
||||
//dba_throw = false;
|
||||
files = 0;
|
||||
file = 0;
|
||||
relations = 0;
|
||||
page_size = 0;
|
||||
dp_per_pp = 0;
|
||||
@ -252,14 +238,13 @@ public:
|
||||
global_buffer = 0;
|
||||
exit_code = 0;
|
||||
head_of_mem_list = 0;
|
||||
head_of_files_list = 0;
|
||||
memset(dba_status_vector, 0, sizeof (dba_status_vector));
|
||||
dba_status = dba_status_vector;
|
||||
}
|
||||
|
||||
//bool dba_throw;
|
||||
Firebird::UtilSvc* uSvc;
|
||||
dba_fil* files;
|
||||
dba_fil* file;
|
||||
dba_rel* relations;
|
||||
USHORT page_size;
|
||||
USHORT dp_per_pp;
|
||||
@ -270,7 +255,6 @@ public:
|
||||
pag* global_buffer;
|
||||
int exit_code;
|
||||
dba_mem *head_of_mem_list;
|
||||
open_files *head_of_files_list;
|
||||
ISC_STATUS *dba_status;
|
||||
ISC_STATUS_ARRAY dba_status_vector;
|
||||
|
||||
@ -613,7 +597,7 @@ int gstat(Firebird::UtilSvc* uSvc)
|
||||
expandDatabaseName(fileName, tempStr, NULL);
|
||||
fileName = tempStr;
|
||||
|
||||
dba_fil* current = db_open(fileName.c_str(), fileName.length());
|
||||
dba_fil* file = db_open(fileName.c_str(), fileName.length());
|
||||
|
||||
alignas(DIRECT_IO_BLOCK_SIZE) SCHAR temp[MAX(RAW_HEADER_SIZE, DIRECT_IO_BLOCK_SIZE)];
|
||||
tddba->page_size = MAX(RAW_HEADER_SIZE, DIRECT_IO_BLOCK_SIZE);
|
||||
@ -661,38 +645,6 @@ int gstat(Firebird::UtilSvc* uSvc)
|
||||
if (sw_header)
|
||||
dba_exit(FINI_OK, tddba);
|
||||
|
||||
// gather continuation files
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (page != HEADER_PAGE)
|
||||
{
|
||||
current = db_open(file_name, static_cast<USHORT>(strlen(file_name)));
|
||||
header = (const header_page*) db_read(page);
|
||||
}
|
||||
|
||||
if (current != tddba->files)
|
||||
current->fil_fudge = 1; // ignore header page once read it
|
||||
|
||||
PathName nextFileName;
|
||||
|
||||
const UCHAR* vp = header->hdr_data;
|
||||
for (const auto vend = reinterpret_cast<const UCHAR*>(header) + header->hdr_page_size;
|
||||
vp < vend && *vp != HDR_end; vp += 2 + vp[1])
|
||||
{
|
||||
if (*vp == HDR_file)
|
||||
nextFileName.assign(vp + 2, vp[1]);
|
||||
|
||||
if (*vp == HDR_last_page)
|
||||
memcpy(¤t->fil_max_page, vp + 2, sizeof(current->fil_max_page));
|
||||
}
|
||||
|
||||
if (nextFileName.isEmpty())
|
||||
break;
|
||||
|
||||
page = current->fil_max_page + 1; // first page of next file
|
||||
}
|
||||
|
||||
if (sw_enc)
|
||||
{
|
||||
class Statist
|
||||
@ -724,6 +676,7 @@ int gstat(Firebird::UtilSvc* uSvc)
|
||||
private:
|
||||
ULONG enc, non;
|
||||
};
|
||||
|
||||
Statist data, index, blob, generator, other;
|
||||
|
||||
ULONG last = lastUsedPage(header->hdr_page_size);
|
||||
@ -766,18 +719,6 @@ int gstat(Firebird::UtilSvc* uSvc)
|
||||
dba_exit(FINI_OK, tddba);
|
||||
}
|
||||
|
||||
// print continuation file sequence
|
||||
|
||||
dba_print(false, 7);
|
||||
// msg 7: \n\nDatabase file sequence:
|
||||
for (current = tddba->files; current->fil_next; current = current->fil_next)
|
||||
{
|
||||
dba_print(false, 8, SafeArg() << current->fil_string << current->fil_next->fil_string);
|
||||
// msg 8: File %s continues as file %s
|
||||
}
|
||||
dba_print(false, 9, SafeArg() << current->fil_string << ((current == tddba->files) ? "only" : "last"));
|
||||
// msg 9: File %s is the %s file\n
|
||||
|
||||
// Check to make sure that the user accessing the database is either
|
||||
// SYSDBA or owner of the database
|
||||
|
||||
@ -1192,22 +1133,11 @@ int gstat(Firebird::UtilSvc* uSvc)
|
||||
alloced = alloced->mem_next;
|
||||
}
|
||||
|
||||
// close files
|
||||
open_files* open_file = tddba->head_of_files_list;
|
||||
while (open_file)
|
||||
{
|
||||
db_close(open_file->desc);
|
||||
open_file = open_file->open_files_next;
|
||||
}
|
||||
// close file
|
||||
if (tddba->file)
|
||||
db_close(tddba->file->fil_desc);
|
||||
|
||||
// free linked lists
|
||||
while (tddba->head_of_files_list != 0)
|
||||
{
|
||||
open_files* tmp1 = tddba->head_of_files_list;
|
||||
tddba->head_of_files_list = tddba->head_of_files_list->open_files_next;
|
||||
delete tmp1;
|
||||
}
|
||||
|
||||
while (tddba->head_of_mem_list != 0)
|
||||
{
|
||||
dba_mem* tmp2 = tddba->head_of_mem_list;
|
||||
@ -1865,29 +1795,12 @@ static dba_fil* db_open(const char* file_name, USHORT file_length)
|
||||
**************************************/
|
||||
tdba* tddba = tdba::getSpecific();
|
||||
|
||||
dba_fil* fil;
|
||||
const auto file = (dba_fil*) alloc(sizeof(dba_fil) + file_length + 1);
|
||||
|
||||
if (tddba->files)
|
||||
{
|
||||
for (fil = tddba->files; fil->fil_next; fil = fil->fil_next);
|
||||
fil->fil_next = (dba_fil*) alloc(sizeof(dba_fil) + file_length + 1);
|
||||
fil->fil_next->fil_min_page = fil->fil_max_page + 1;
|
||||
fil = fil->fil_next;
|
||||
}
|
||||
else
|
||||
{
|
||||
// empty list
|
||||
fil = tddba->files = (dba_fil*) alloc(sizeof(dba_fil) + file_length + 1);
|
||||
fil->fil_min_page = 0L;
|
||||
}
|
||||
strcpy(file->fil_string, file_name);
|
||||
file->fil_length = file_length;
|
||||
|
||||
fil->fil_next = NULL;
|
||||
strcpy(fil->fil_string, file_name);
|
||||
fil->fil_length = file_length;
|
||||
fil->fil_fudge = 0;
|
||||
fil->fil_max_page = 0L;
|
||||
|
||||
fil->fil_desc = CreateFile( file_name,
|
||||
file->fil_desc = CreateFile(file_name,
|
||||
GENERIC_READ,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
NULL,
|
||||
@ -1895,31 +1808,15 @@ static dba_fil* db_open(const char* file_name, USHORT file_length)
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
0);
|
||||
|
||||
if (fil->fil_desc == INVALID_HANDLE_VALUE)
|
||||
if (file->fil_desc == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
tddba->uSvc->getStatusAccessor().setServiceStatus(GSTAT_MSG_FAC, 29, SafeArg() << file_name);
|
||||
// msg 29: Can't open database file %s
|
||||
db_error(GetLastError());
|
||||
}
|
||||
|
||||
open_files* file_list = FB_NEW_POOL(*getDefaultMemoryPool()) open_files;
|
||||
if (!file_list)
|
||||
{
|
||||
// NOMEM: return error
|
||||
dba_error(31);
|
||||
}
|
||||
file_list->desc = fil->fil_desc;
|
||||
file_list->open_files_next = 0;
|
||||
|
||||
if (tddba->head_of_files_list == 0)
|
||||
tddba->head_of_files_list = file_list;
|
||||
else
|
||||
{
|
||||
file_list->open_files_next = tddba->head_of_files_list;
|
||||
tddba->head_of_files_list = file_list;
|
||||
}
|
||||
|
||||
return fil;
|
||||
tddba->file = file;
|
||||
return file;
|
||||
}
|
||||
|
||||
|
||||
@ -1945,17 +1842,9 @@ static const pag* db_read( SLONG page_number, bool ok_enc)
|
||||
|
||||
tddba->page_number = page_number;
|
||||
|
||||
dba_fil* fil;
|
||||
for (fil = tddba->files; page_number > (SLONG) fil->fil_max_page && fil->fil_next;)
|
||||
{
|
||||
fil = fil->fil_next;
|
||||
}
|
||||
|
||||
page_number -= fil->fil_min_page - fil->fil_fudge;
|
||||
|
||||
LARGE_INTEGER liOffset;
|
||||
liOffset.QuadPart = UInt32x32To64((DWORD) page_number, (DWORD) tddba->page_size);
|
||||
if (SetFilePointer(fil->fil_desc, (LONG) liOffset.LowPart, &liOffset.HighPart, FILE_BEGIN) ==
|
||||
if (SetFilePointer(tddba->file->fil_desc, (LONG) liOffset.LowPart, &liOffset.HighPart, FILE_BEGIN) ==
|
||||
(DWORD) -1)
|
||||
{
|
||||
int lastError = GetLastError();
|
||||
@ -1968,7 +1857,7 @@ static const pag* db_read( SLONG page_number, bool ok_enc)
|
||||
}
|
||||
|
||||
SLONG actual_length;
|
||||
if (!ReadFile( fil->fil_desc,
|
||||
if (!ReadFile( tddba->file->fil_desc,
|
||||
tddba->global_buffer,
|
||||
tddba->page_size,
|
||||
reinterpret_cast<LPDWORD>(&actual_length),
|
||||
@ -2057,54 +1946,20 @@ static dba_fil* db_open(const char* file_name, USHORT file_length)
|
||||
**************************************/
|
||||
tdba* tddba = tdba::getSpecific();
|
||||
|
||||
dba_fil* fil;
|
||||
if (tddba->files)
|
||||
{
|
||||
for (fil = tddba->files; fil->fil_next; fil = fil->fil_next)
|
||||
;
|
||||
fil->fil_next = (dba_fil*) alloc(sizeof(dba_fil) + file_length + 1);
|
||||
fil->fil_next->fil_min_page = fil->fil_max_page + 1;
|
||||
fil = fil->fil_next;
|
||||
}
|
||||
else
|
||||
{
|
||||
// empty list
|
||||
const auto file = (dba_fil*) alloc(sizeof(dba_fil) + file_length + 1);
|
||||
|
||||
fil = tddba->files = (dba_fil*) alloc(sizeof(dba_fil) + file_length + 1);
|
||||
fil->fil_min_page = 0L;
|
||||
}
|
||||
strcpy(file->fil_string, file_name);
|
||||
file->fil_length = file_length;
|
||||
|
||||
fil->fil_next = NULL;
|
||||
strcpy(fil->fil_string, file_name);
|
||||
fil->fil_length = file_length;
|
||||
fil->fil_fudge = 0;
|
||||
fil->fil_max_page = 0L;
|
||||
|
||||
if ((fil->fil_desc = os_utils::open(file_name, O_RDONLY)) == -1)
|
||||
if ((file->fil_desc = os_utils::open(file_name, O_RDONLY)) == -1)
|
||||
{
|
||||
tddba->uSvc->getStatusAccessor().setServiceStatus(GSTAT_MSG_FAC, 29, SafeArg() << file_name);
|
||||
// msg 29: Can't open database file %s
|
||||
db_error(errno);
|
||||
}
|
||||
|
||||
open_files* file_list = FB_NEW_POOL(*getDefaultMemoryPool()) open_files;
|
||||
if (!file_list)
|
||||
{
|
||||
// NOMEM: return error
|
||||
dba_error(31);
|
||||
}
|
||||
file_list->desc = fil->fil_desc;
|
||||
file_list->open_files_next = 0;
|
||||
|
||||
if (tddba->head_of_files_list == 0)
|
||||
tddba->head_of_files_list = file_list;
|
||||
else
|
||||
{
|
||||
file_list->open_files_next = tddba->head_of_files_list;
|
||||
tddba->head_of_files_list = file_list;
|
||||
}
|
||||
|
||||
return fil;
|
||||
tddba->file = file;
|
||||
return file;
|
||||
}
|
||||
|
||||
|
||||
@ -2127,15 +1982,8 @@ static const pag* db_read( SLONG page_number, bool ok_enc)
|
||||
|
||||
tddba->page_number = page_number;
|
||||
|
||||
dba_fil* fil;
|
||||
for (fil = tddba->files; page_number > (SLONG) fil->fil_max_page && fil->fil_next;)
|
||||
{
|
||||
fil = fil->fil_next;
|
||||
}
|
||||
|
||||
page_number -= fil->fil_min_page - fil->fil_fudge;
|
||||
const FB_UINT64 offset = ((FB_UINT64) page_number) * ((FB_UINT64) tddba->page_size);
|
||||
if (os_utils::lseek (fil->fil_desc, offset, 0) == -1)
|
||||
if (os_utils::lseek (tddba->file->fil_desc, offset, 0) == -1)
|
||||
{
|
||||
tddba->uSvc->getStatusAccessor().setServiceStatus(GSTAT_MSG_FAC, 30, SafeArg());
|
||||
// msg 30: Can't read a database page
|
||||
@ -2145,7 +1993,7 @@ static const pag* db_read( SLONG page_number, bool ok_enc)
|
||||
USHORT length = tddba->page_size;
|
||||
for (SCHAR* p = (SCHAR *) tddba->global_buffer; length > 0;)
|
||||
{
|
||||
const int l = read(fil->fil_desc, p, length);
|
||||
const int l = read(tddba->file->fil_desc, p, length);
|
||||
if (l < 0)
|
||||
{
|
||||
tddba->uSvc->getStatusAccessor().setServiceStatus(GSTAT_MSG_FAC, 30, SafeArg());
|
||||
|
@ -233,17 +233,6 @@ void PPG_print_header(const header_page* header, bool nocreation, Firebird::Util
|
||||
uSvc->printf(false, "\tRoot file name:\t\t%s\n", temp);
|
||||
break;
|
||||
|
||||
case HDR_file:
|
||||
memcpy(temp, p + 2, p[1]);
|
||||
temp[p[1]] = '\0';
|
||||
uSvc->printf(false, "\tContinuation file:\t\t%s\n", temp);
|
||||
break;
|
||||
|
||||
case HDR_last_page:
|
||||
memcpy(&number, p + 2, sizeof(number));
|
||||
uSvc->printf(false, "\tLast logical page:\t\t%ld\n", number);
|
||||
break;
|
||||
|
||||
case HDR_sweep_interval:
|
||||
memcpy(&number, p + 2, sizeof(number));
|
||||
uSvc->printf(false, "\tSweep interval:\t\t%ld\n", number);
|
||||
|
@ -1818,7 +1818,7 @@ void TracePluginImpl::register_sql_statement(ITraceSQLStatement* statement)
|
||||
stmt_data.description = FB_NEW_POOL(*getDefaultMemoryPool()) string(*getDefaultMemoryPool());
|
||||
|
||||
if (stmt_data.id) {
|
||||
stmt_data.description->printf(NEWLINE "Statement %d:", stmt_data.id);
|
||||
stmt_data.description->printf(NEWLINE "Statement %" SQUADFORMAT":", stmt_data.id);
|
||||
}
|
||||
|
||||
string temp(*getDefaultMemoryPool());
|
||||
|
@ -63,7 +63,6 @@ static void format_index_root(index_root_page*, int, SSHORT, SSHORT);
|
||||
static void format_pointer(pointer_page*, int, SSHORT, SSHORT, bool, SSHORT, const SLONG*);
|
||||
static void format_pip(page_inv_page*, int, int);
|
||||
static void format_tip(tx_inv_page*, int, SLONG);
|
||||
static void get_next_file(rbdb*, header_page*);
|
||||
static void get_range(TEXT***, const TEXT* const* const, ULONG*, ULONG*);
|
||||
static void get_switch(TEXT**, swc*);
|
||||
static header_page* open_database(rbdb*, ULONG);
|
||||
@ -197,8 +196,7 @@ int main( int argc, char *argv[])
|
||||
rbdb = (rbdb*) RBDB_alloc((SLONG) (sizeof(struct rbdb) + strlen(db_in)));
|
||||
strcpy(rbdb->rbdb_file.fil_name, db_in);
|
||||
rbdb->rbdb_file.fil_length = strlen(db_in);
|
||||
if (header = open_database(rbdb, pg_size))
|
||||
get_next_file(rbdb, header);
|
||||
header = open_database(rbdb, pg_size);
|
||||
|
||||
// some systems don't care for this write sharing stuff...
|
||||
if (rbdb && (sw_dump_tips || sw_dump_pages))
|
||||
@ -210,7 +208,6 @@ int main( int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
gdbb = &tdbb_struct;
|
||||
gdbb->tdbb_database = &dbb_struct;
|
||||
gdbb->tdbb_transaction = &dull;
|
||||
@ -252,17 +249,15 @@ int main( int argc, char *argv[])
|
||||
fclose(dbg_file);
|
||||
|
||||
if (rbdb)
|
||||
{
|
||||
RBDB_close(rbdb);
|
||||
|
||||
while (rbdb)
|
||||
{
|
||||
rbdb* const next_db = rbdb->rbdb_next;
|
||||
if (rbdb->rbdb_buffer1)
|
||||
gds__free(rbdb->rbdb_buffer1);
|
||||
if (rbdb->rbdb_buffer2)
|
||||
gds__free(rbdb->rbdb_buffer2);
|
||||
|
||||
gds__free(rbdb);
|
||||
rbdb = next_db;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -332,8 +327,7 @@ void RBDB_close( rbdb* rbdb)
|
||||
* Functional description
|
||||
*
|
||||
**************************************/
|
||||
for (; rbdb; rbdb = rbdb->rbdb_next)
|
||||
close(rbdb->rbdb_file.fil_file);
|
||||
close(rbdb->rbdb_file.fil_file);
|
||||
}
|
||||
|
||||
|
||||
@ -732,36 +726,6 @@ static void format_tip( tx_inv_page* page, int page_size, SLONG next_page)
|
||||
}
|
||||
|
||||
|
||||
static void get_next_file( rbdb* rbdb, header_page* header)
|
||||
{
|
||||
/**************************************
|
||||
*
|
||||
* g e t _ n e x t _ f i l e
|
||||
*
|
||||
**************************************
|
||||
*
|
||||
* Functional description
|
||||
* If there's another file as part of
|
||||
* this database, get it now.
|
||||
*
|
||||
**************************************/
|
||||
rbdb** next = &rbdb->rbdb_next;
|
||||
const UCHAR* p = header->hdr_data;
|
||||
for (const UCHAR* const end = p + header->hdr_page_size; p < end && *p != HDR_end; p += 2 + p[1])
|
||||
{
|
||||
if (*p == HDR_file)
|
||||
{
|
||||
rbdb* next_rbdb = (rbdb*) RBDB_alloc(sizeof(struct rbdb) + (SSHORT) p[1]);
|
||||
next_rbdb->rbdb_file.fil_length = (SSHORT) p[1];
|
||||
strncpy(next_rbdb->rbdb_file.fil_name, p + 2, (SSHORT) p[1]);
|
||||
*next = next_rbdb;
|
||||
next = &next_rbdb->rbdb_next;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void get_range(TEXT*** argv, const TEXT* const* const end, ULONG* lower, ULONG* upper)
|
||||
{
|
||||
/**************************************
|
||||
|
@ -250,7 +250,7 @@ ISC_STATUS API_ROUTINE_VARARG gds__start_transaction(ISC_STATUS* status_vector,
|
||||
teb_t tebs[16];
|
||||
teb_t* teb = tebs;
|
||||
|
||||
if (count > FB_NELEM(tebs))
|
||||
if (static_cast<FB_SIZE_T>(count) > FB_NELEM(tebs))
|
||||
teb = (teb_t*) gds__alloc(((SLONG) sizeof(teb_t) * count));
|
||||
// FREE: later in this module
|
||||
|
||||
@ -862,7 +862,7 @@ ISC_STATUS API_ROUTINE isc_add_user(ISC_STATUS* status, const USER_SEC_DATA* inp
|
||||
* Return > 0 if any error occurs.
|
||||
*
|
||||
**************************************/
|
||||
Auth::StackUserData userInfo;
|
||||
Auth::UserData userInfo;
|
||||
userInfo.op = Auth::ADD_OPER;
|
||||
Firebird::LocalStatus s;
|
||||
Firebird::CheckStatusWrapper statusWrapper(&s);
|
||||
@ -925,7 +925,7 @@ ISC_STATUS API_ROUTINE isc_delete_user(ISC_STATUS* status, const USER_SEC_DATA*
|
||||
* Return > 0 if any error occurs.
|
||||
*
|
||||
**************************************/
|
||||
Auth::StackUserData userInfo;
|
||||
Auth::UserData userInfo;
|
||||
userInfo.op = Auth::DEL_OPER;
|
||||
Firebird::LocalStatus s;
|
||||
Firebird::CheckStatusWrapper statusWrapper(&s);
|
||||
@ -970,7 +970,7 @@ ISC_STATUS API_ROUTINE isc_modify_user(ISC_STATUS* status, const USER_SEC_DATA*
|
||||
* Return > 0 if any error occurs.
|
||||
*
|
||||
**************************************/
|
||||
Auth::StackUserData userInfo;
|
||||
Auth::UserData userInfo;
|
||||
userInfo.op = Auth::MOD_OPER;
|
||||
Firebird::LocalStatus s;
|
||||
Firebird::CheckStatusWrapper statusWrapper(&s);
|
||||
|
@ -975,7 +975,7 @@ static SLONG safe_interpret(char* const s, const FB_SIZE_T bufsize,
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
sprintf(s, "unknown ISC error %ld", (SLONG) code); // TXNN
|
||||
sprintf(s, "unknown ISC error %" SLONGFORMAT, (SLONG) code); // TXNN
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1000,11 +1000,11 @@ static SLONG safe_interpret(char* const s, const FB_SIZE_T bufsize,
|
||||
break;
|
||||
|
||||
case isc_arg_dos:
|
||||
sprintf(s, "unknown dos error %ld", (SLONG) code); // TXNN
|
||||
sprintf(s, "unknown dos error %" SLONGFORMAT, (SLONG) code); // TXNN
|
||||
break;
|
||||
|
||||
case isc_arg_next_mach:
|
||||
sprintf(s, "next/mach error %ld", (SLONG) code); // AP
|
||||
sprintf(s, "next/mach error %" SLONGFORMAT, (SLONG) code); // AP
|
||||
break;
|
||||
|
||||
case isc_arg_win32:
|
||||
@ -1016,7 +1016,7 @@ static SLONG safe_interpret(char* const s, const FB_SIZE_T bufsize,
|
||||
s, bufsize, NULL))
|
||||
#endif
|
||||
{
|
||||
sprintf(s, "unknown Win32 error %ld", (SLONG) code); // TXNN
|
||||
sprintf(s, "unknown Win32 error %" SLONGFORMAT, (SLONG) code); // TXNN
|
||||
}
|
||||
break;
|
||||
|
||||
@ -4025,7 +4025,7 @@ static void blr_print_verb(gds_ctl* control, SSHORT level)
|
||||
case blr_invoke_function_type:
|
||||
n = control->ctl_blr_reader.getByte();
|
||||
|
||||
if (n == 0 || n >= FB_NELEM(typeSubCodes))
|
||||
if (n == 0 || n >= static_cast<FB_SSIZE_T>(FB_NELEM(typeSubCodes)))
|
||||
blr_error(control, "*** invalid blr_invoke_function_type sub code ***");
|
||||
|
||||
blr_format(control, "blr_invoke_function_type_%s,", typeSubCodes[n]);
|
||||
@ -4123,7 +4123,7 @@ static void blr_print_verb(gds_ctl* control, SSHORT level)
|
||||
case blr_invsel_procedure_type:
|
||||
n = control->ctl_blr_reader.getByte();
|
||||
|
||||
if (n == 0 || n >= FB_NELEM(typeSubCodes))
|
||||
if (n == 0 || n >= static_cast<FB_SSIZE_T>(FB_NELEM(typeSubCodes)))
|
||||
blr_error(control, "*** invalid blr_invsel_procedure_type sub code ***");
|
||||
|
||||
blr_format(control, "blr_invsel_procedure_type_%s,", typeSubCodes[n]);
|
||||
|
@ -831,11 +831,11 @@ private:
|
||||
explicit CtrlCHandler(MemoryPool& p)
|
||||
: ShutdownInit(p)
|
||||
{
|
||||
shutdownSemaphore = &semaphore;
|
||||
Thread::start(shutdownThread, 0, 0, &handle);
|
||||
|
||||
procInt = ISC_signal(SIGINT, handlerInt, 0);
|
||||
procTerm = ISC_signal(SIGTERM, handlerTerm, 0);
|
||||
shutdownSemaphore = &semaphore;
|
||||
}
|
||||
|
||||
~CtrlCHandler()
|
||||
|
Loading…
Reference in New Issue
Block a user