Skip to content
GitLab
Menu
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
code
pfabric
Commits
98679b10
Commit
98679b10
authored
Mar 27, 2017
by
Philipp Götze
Browse files
Working version of insert for persistent tables
parent
784b28f5
Changes
6
Expand all
Hide whitespace changes
Inline
Side-by-side
src/core/PTuple.hpp
View file @
98679b10
...
...
@@ -27,9 +27,9 @@
#include
<cstddef>
#include
<cstdint>
#include
<string>
#include
<vector>
#include
<core/PFabricTypes.hpp>
#include
<core/Tuple.hpp>
#include
<libpmemobj++/persistent_ptr.hpp>
...
...
@@ -39,21 +39,23 @@ namespace pfabric {
namespace
nvm
{
//TODO: Find a more suitable position for these constants
/* Positions in NVM_Block */
const
int
DDCRangePos1
=
0
;
const
int
DDCRangePos2
=
4
;
const
int
CountPos
=
8
;
const
int
FreeSpacePos
=
12
;
const
int
SmaOffsetPos
=
14
;
const
int
DataOffsetPos
=
16
;
const
int
g
DDCRangePos1
=
0
;
const
int
g
DDCRangePos2
=
4
;
const
int
g
CountPos
=
8
;
const
int
g
FreeSpacePos
=
12
;
const
int
g
SmaOffsetPos
=
14
;
const
int
g
DataOffsetPos
=
16
;
/* Sizes/Lengths in NVM_Block */
const
int
FixedHeaderSize
=
14
;
const
int
AttrOffsetSize
=
4
;
const
int
OffsetSize
=
2
;
const
int
gFixedHeaderSize
=
14
;
const
int
gDDCValueSize
=
4
;
const
int
gAttrOffsetSize
=
4
;
const
int
gOffsetSize
=
2
;
/** The size of a single block in persistent memory */
static
constexpr
uint16_t
BlockSize
=
1
<<
15
;
// 32KB
static
constexpr
uint16_t
g
BlockSize
=
1
<<
15
;
// 32KB
/**
* \brief This type represents a byte array used for persistent structures.
...
...
@@ -82,7 +84,7 @@ static constexpr uint16_t BlockSize = 1 << 15; // 32KB
* . ...
* . data -> size of all strings + ddc_cnt (Nul termination)
*/
typedef
typename
std
::
array
<
uint8_t
,
BlockSize
>
NVM_Block
;
typedef
typename
std
::
array
<
uint8_t
,
g
BlockSize
>
NVM_Block
;
namespace
detail
{
...
...
@@ -107,7 +109,7 @@ struct get_helper;
*****************************************************************************/
template
<
typename
T
,
std
::
size_t
ID
>
struct
get_helper
{
static
T
apply
(
persistent_ptr
<
NVM_Block
>
block
,
const
std
::
vector
<
uint16_t
>&
offsets
)
{
static
T
apply
(
persistent_ptr
<
NVM_Block
>
block
,
const
uint16_t
*
offsets
)
{
T
val
;
uint8_t
*
ptr
=
reinterpret_cast
<
uint8_t
*>
(
&
val
);
std
::
copy
(
block
->
begin
()
+
offsets
[
ID
],
block
->
begin
()
+
offsets
[
ID
]
+
sizeof
(
T
),
ptr
);
...
...
@@ -125,7 +127,7 @@ struct get_helper {
*****************************************************************************/
template
<
std
::
size_t
ID
>
struct
get_helper
<
std
::
string
,
ID
>
{
static
char
(
&
(
apply
(
persistent_ptr
<
NVM_Block
>
block
,
const
std
::
vector
<
uint16_t
>&
offsets
)))[]
{
static
char
(
&
(
apply
(
persistent_ptr
<
NVM_Block
>
block
,
const
uint16_t
*
offsets
)))[]
{
return
reinterpret_cast
<
char
(
&
)[]
>
(
block
->
at
(
offsets
[
ID
]));
}
};
...
...
@@ -140,7 +142,7 @@ struct get_helper<std::string, ID> {
*****************************************************************************/
template
<
std
::
size_t
ID
>
struct
get_helper
<
int32_t
,
ID
>
{
static
int32_t
&
apply
(
persistent_ptr
<
NVM_Block
>
block
,
const
std
::
vector
<
uint16_t
>&
offsets
)
{
static
int32_t
&
apply
(
persistent_ptr
<
NVM_Block
>
block
,
const
uint16_t
*
offsets
)
{
return
reinterpret_cast
<
int32_t
&>
(
block
->
at
(
offsets
[
ID
]));
}
};
...
...
@@ -155,11 +157,76 @@ struct get_helper<int32_t, ID> {
*****************************************************************************/
template
<
std
::
size_t
ID
>
struct
get_helper
<
double
,
ID
>
{
static
double
&
apply
(
persistent_ptr
<
NVM_Block
>
block
,
const
std
::
vector
<
uint16_t
>&
offsets
)
{
static
double
&
apply
(
persistent_ptr
<
NVM_Block
>
block
,
const
uint16_t
*
offsets
)
{
return
reinterpret_cast
<
double
&>
(
block
->
at
(
offsets
[
ID
]));
}
};
/**************************************************************************//**
* \brief PTuplePrinter is a helper function to print a persistent tuple of any
* size.
*
* PTuplePrinter is a helper function to print a PTuple instance of any size
* and member types to std::ostream. This template should not be directly used,
* but only via the Tuple members.
*
* \tparam Tuple
* the tuple type
* \tparam CurrentIndex
* the index of the attribute value to be printed
*****************************************************************************/
template
<
class
Tuple
,
std
::
size_t
CurrentIndex
>
struct
PTuplePrinter
;
/**************************************************************************//**
* \brief General overload for printing more than 1 element.
*
* This specialization will print the remaining elements first and appends the
* current one after a comma.
*
* \tparam Tuple
* the underlying tuple type
* \tparam CurrentIndex
* the index of the attribute value to be printed
*****************************************************************************/
template
<
class
Tuple
,
std
::
size_t
CurrentIndex
>
struct
PTuplePrinter
{
static
void
print
(
std
::
ostream
&
os
,
persistent_ptr
<
NVM_Block
>
block
,
const
uint16_t
*
offsets
)
{
PTuplePrinter
<
Tuple
,
CurrentIndex
-
1
>::
print
(
os
,
block
,
offsets
);
os
<<
","
<<
get_helper
<
typename
Tuple
::
template
getAttributeType
<
CurrentIndex
-
1
>
::
type
,
CurrentIndex
-
1
>::
apply
(
block
,
offsets
);
}
};
/**************************************************************************//**
* \brief Specialization for printing a persistent tuple with 1 element.
*
* This specialization will just print the element.
*
* \tparam Tuple
* the underlying tuple type having one element
*****************************************************************************/
template
<
class
Tuple
>
struct
PTuplePrinter
<
Tuple
,
1
>
{
static
void
print
(
std
::
ostream
&
os
,
persistent_ptr
<
NVM_Block
>
block
,
const
uint16_t
*
offsets
)
{
os
<<
get_helper
<
typename
Tuple
::
template
getAttributeType
<
0
>
::
type
,
0
>::
apply
(
block
,
offsets
);
}
};
/**************************************************************************//**
* \brief Specialization for printing a persistent tuple with no elements.
*
* This specialization will do nothing.
*
* \tparam Tuple
* the underlying tuple type having no elements
*****************************************************************************/
template
<
class
Tuple
>
struct
PTuplePrinter
<
Tuple
,
0
>
{
static
void
print
(
std
::
ostream
&
os
,
persistent_ptr
<
NVM_Block
>
block
,
const
uint16_t
*
offsets
)
{
}
};
}
/* end namespace detail */
/**************************************************************************//**
...
...
@@ -191,12 +258,6 @@ struct get_helper<double, ID> {
*****************************************************************************/
template
<
class
Tuple
>
class
PTuple
{
private:
persistent_ptr
<
NVM_Block
>
block
;
std
::
vector
<
uint16_t
>
offsets
;
//!< TODO: Something else than a runtime eater like vector
public:
/************************************************************************//**
* \brief the number of attributes for this tuple type.
...
...
@@ -225,7 +286,7 @@ public:
* \param[in] _offsets
* the offsets for each tuple element
***************************************************************************/
PTuple
(
persistent_ptr
<
NVM_Block
>
_block
,
std
::
vector
<
uint16_t
>
_offsets
)
:
PTuple
(
persistent_ptr
<
NVM_Block
>
_block
,
std
::
array
<
uint16_t
,
NUM_ATTRIBUTES
>
_offsets
)
:
block
(
_block
),
offsets
(
_offsets
)
{
}
...
...
@@ -238,15 +299,64 @@ public:
* a reference to the persistent tuple's attribute with the requested \c ID
***************************************************************************/
template
<
std
::
size_t
ID
>
inline
auto
getAttribute
()
->
typename
getAttributeType
<
ID
>::
type
{
return
detail
::
get_helper
<
typename
getAttributeType
<
ID
>::
type
,
ID
>::
apply
(
block
,
offsets
);
inline
auto
getAttribute
()
{
return
detail
::
get_helper
<
typename
getAttributeType
<
ID
>::
type
,
ID
>::
apply
(
block
,
offsets
.
data
()
);
}
/************************************************************************//**
* \brief Get a specific attribute value from the persistent tuple.
*
* \tparam ID
* the index of the requested attribute.
* \return
* a reference to the persistent tuple's attribute with the requested \c ID
***************************************************************************/
template
<
std
::
size_t
ID
>
inline
auto
get
()
->
typename
getAttributeType
<
ID
>::
type
{
return
detail
::
get_helper
<
typename
getAttributeType
<
ID
>::
type
,
ID
>::
apply
(
block
,
offsets
);
inline
auto
get
()
{
return
detail
::
get_helper
<
typename
getAttributeType
<
ID
>::
type
,
ID
>::
apply
(
block
,
offsets
.
data
()
);
}
/************************************************************************//**
* \brief Get a specific attribute value from the persistent tuple.
*
* \tparam ID
* the index of the requested attribute.
* \return
* a reference to the persistent tuple's attribute with the requested \c ID
***************************************************************************/
template
<
std
::
size_t
ID
>
inline
auto
getAttribute
()
const
{
return
detail
::
get_helper
<
typename
getAttributeType
<
ID
>::
type
,
ID
>::
apply
(
block
,
offsets
.
data
());
}
/************************************************************************//**
* \brief Get a specific attribute value from the persistent tuple.
*
* \tparam ID
* the index of the requested attribute.
* \return
* a reference to the persistent tuple's attribute with the requested \c ID
***************************************************************************/
template
<
std
::
size_t
ID
>
inline
auto
get
()
const
{
return
detail
::
get_helper
<
typename
getAttributeType
<
ID
>::
type
,
ID
>::
apply
(
block
,
offsets
.
data
());
}
/************************************************************************//**
* \brief Print this persistent tuple to an ostream.
*
* \param[in] os
* the output stream to print the tuple
***************************************************************************/
void
print
(
std
::
ostream
&
os
)
{
detail
::
PTuplePrinter
<
Tuple
,
NUM_ATTRIBUTES
>::
print
(
os
,
block
,
offsets
.
data
());
}
private:
persistent_ptr
<
NVM_Block
>
block
;
std
::
array
<
uint16_t
,
NUM_ATTRIBUTES
>
offsets
;
};
/* class PTuplePtr */
}
/* end namespace nvm */
...
...
@@ -267,10 +377,48 @@ public:
* a reference to the persistent tuple's attribute with the requested \c ID
*****************************************************************************/
template
<
std
::
size_t
ID
,
class
Tuple
>
auto
get
(
nvm
::
PTuple
<
Tuple
>
ptp
)
->
decltype
(
ptp
.
template
get
<
ID
>())
{
auto
get
(
const
nvm
::
PTuple
<
Tuple
>
&
ptp
)
->
decltype
(
(
ptp
.
template
get
<
ID
>())
)
{
return
ptp
.
template
get
<
ID
>();
}
/**************************************************************************//**
* \brief Print a persistent tuple to an ostream.
*
* \param[in] os
* the output stream to print the tuple
*****************************************************************************/
template
<
class
Tuple
>
void
print
(
std
::
ostream
&
os
,
const
nvm
::
PTuple
<
Tuple
>&
ptp
)
{
ptp
.
print
(
os
);
}
}
/* end namespace pfabric */
/**************************************************************************//**
* \brief Helper template for printing persistent tuples to an ostream
*
* \tparam Tuple
* the underlying Tuple of the PTuple
* \param[in] os
* the output stream to print the tuple
* \param[in] ptp
* PTuple instance to print
* \return
* the output stream
*****************************************************************************/
template
<
typename
Tuple
>
std
::
ostream
&
operator
<<
(
std
::
ostream
&
os
,
pfabric
::
nvm
::
PTuple
<
Tuple
>&
ptp
)
{
ptp
.
print
(
os
);
return
os
;
}
/*
namespace std {
template< std::size_t ID, typename Tuple >
auto get( pfabric::nvm::PTuple<Tuple>& ptp ) -> decltype(ptp.template getAttribute<ID>()) {
return ptp.template getAttribute<ID>();
}
}
*/
#endif
/* PTuple_hpp_ */
src/core/PTuplePrinter.hpp
0 → 100644
View file @
98679b10
/*
* Copyright (c) 2014-17 The PipeFabric team,
* All Rights Reserved.
*
* This file is part of the PipeFabric package.
*
* PipeFabric is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License (GPL) as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file LICENSE.
* If not you can find the GPL at http://www.gnu.org/copyleft/gpl.html
*/
#ifndef PTuplePrinter_hpp_
#define PTuplePrinter_hpp_
#include
<ostream>
#include
<core/PTuple.hpp>
namespace
pfabric
{
/**
* Forward declarations
*/
namespace
nvm
{
template
<
class
Tuple
>
class
PTuple
;
}
template
<
std
::
size_t
ID
,
class
Tuple
>
auto
get
(
const
nvm
::
PTuple
<
Tuple
>&
ptp
)
->
typename
Tuple
::
template
getAttributeType
<
ID
>
::
type
;
namespace
nvm
{
namespace
detail
{
template
<
class
Tuple
,
std
::
size_t
CurrentIndex
>
struct
PTuplePrinter
;
template
<
class
Tuple
,
std
::
size_t
CurrentIndex
>
struct
PTuplePrinter
{
static
void
print
(
std
::
ostream
&
os
,
const
nvm
::
PTuple
<
Tuple
>&
ptp
)
{
PTuplePrinter
<
Tuple
,
CurrentIndex
-
1
>::
print
(
os
,
ptp
);
os
<<
","
<<
pfabric
::
get
<
CurrentIndex
-
1
>
(
ptp
);
}
};
template
<
class
Tuple
>
struct
PTuplePrinter
<
Tuple
,
1
>
{
static
void
print
(
std
::
ostream
&
os
,
const
nvm
::
PTuple
<
Tuple
>&
ptp
)
{
os
<<
pfabric
::
get
<
0
>
(
ptp
);
}
};
template
<
class
Tuple
>
struct
PTuplePrinter
<
Tuple
,
0
>
{
static
void
print
(
std
::
ostream
&
os
,
const
nvm
::
PTuple
<
Tuple
>&
ptp
)
{
}
};
}
/* end namespace detail */
template
<
class
Tuple
>
void
print
(
std
::
ostream
&
os
,
const
nvm
::
PTuple
<
Tuple
>&
ptp
)
{
detail
::
PTuplePrinter
<
Tuple
,
ptp
.
NUM_ATTRIBUTES
>::
print
(
os
,
ptp
);
}
}
/* end namespace nvm */
}
/* end namespace pfabric */
#endif
/* PTuplePrinter_hpp_ */
src/table/BDCCInfo.hpp
0 → 100644
View file @
98679b10
/*
* Copyright (c) 2014-17 The PipeFabric team,
* All Rights Reserved.
*
* This file is part of the PipeFabric package.
*
* PipeFabric is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License (GPL) as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file LICENSE.
* If not you can find the GPL at http://www.gnu.org/copyleft/gpl.html
*/
#ifndef BDCCInfo_hpp_
#define BDCCInfo_hpp_
namespace
pfabric
{
struct
BDCCInfo
{
typedef
std
::
unordered_map
<
ColumnInfo
,
uint16_t
>
ColumnBitsMap
;
//BDCCInfo() {}
BDCCInfo
(
ColumnBitsMap
_bitMap
)
:
bitMap
(
_bitMap
),
numBins
(
std
::
accumulate
(
_bitMap
.
begin
(),
_bitMap
.
end
(),
0
,
[](
const
size_t
sum
,
decltype
(
*
_bitMap
.
begin
())
p
)
{
return
sum
+
p
.
second
;
}))
{}
const
ColumnBitsMap
bitMap
;
const
size_t
numBins
;
std
::
map
<
uint32_t
,
std
::
size_t
>
histogram
;
};
typedef
std
::
shared_ptr
<
TableInfo
>
TableInfoPtr
;
}
#endif
src/table/TableInfo.hpp
View file @
98679b10
...
...
@@ -15,8 +15,51 @@ struct ColumnInfo {
std
::
string
mColName
;
ColumnType
mColType
;
bool
operator
==
(
const
ColumnInfo
&
other
)
const
{
return
(
mColName
==
other
.
mColName
&&
mColType
==
other
.
mColType
);
}
bool
operator
<
(
const
ColumnInfo
&
other
)
const
{
return
mColName
<
other
.
mColName
;
}
};
template
<
bool
isFixedSize
>
struct
IsFixedSize
{
static
const
bool
IS_FIXED_SIZE
=
isFixedSize
;
};
template
<
ColumnInfo
::
ColumnType
ColType
>
struct
ColTypeTraits
;
template
<
>
struct
ColTypeTraits
<
ColumnInfo
::
Int_Type
>
:
IsFixedSize
<
true
>
{
static
const
std
::
size_t
COLUMN_SIZE
=
sizeof
(
int32_t
);
};
template
<
>
struct
ColTypeTraits
<
ColumnInfo
::
Double_Type
>
:
IsFixedSize
<
true
>
{
static
const
std
::
size_t
COLUMN_SIZE
=
sizeof
(
double
);
};
template
<
>
struct
ColTypeTraits
<
ColumnInfo
::
String_Type
>
:
IsFixedSize
<
false
>
{};
using
Offset
=
std
::
size_t
;
template
<
ColumnInfo
::
ColumnType
ColType
,
typename
Col
>
typename
std
::
enable_if
<
not
ColTypeTraits
<
ColType
>::
IS_FIXED_SIZE
,
Offset
>::
type
getSize
(
const
Col
&
t
)
{
//TODO: runtime size calculation
}
template
<
ColumnInfo
::
ColumnType
ColType
,
typename
Col
>
typename
std
::
enable_if
<
ColTypeTraits
<
ColType
>::
IS_FIXED_SIZE
,
Offset
>::
type
getSize
(
const
Col
&
t
)
{
using
ColTraits
=
ColTypeTraits
<
ColType
>
;
return
ColTraits
::
COLUMN_SIZE
;
}
class
TableInfo
{
public:
typedef
std
::
vector
<
ColumnInfo
>::
const_iterator
ColumnIterator
;
...
...
@@ -43,6 +86,8 @@ class TableInfo {
ColumnIterator
begin
()
const
{
return
mColumns
.
begin
();
}
ColumnIterator
end
()
const
{
return
mColumns
.
end
();
}
std
::
size_t
numColumns
()
const
{
return
mColumns
.
size
();
}
private:
std
::
string
mName
;
std
::
vector
<
ColumnInfo
>
mColumns
;
...
...
@@ -54,4 +99,13 @@ typedef std::shared_ptr<TableInfo> TableInfoPtr;
std
::
ostream
&
operator
<<
(
std
::
ostream
&
os
,
pfabric
::
ColumnInfo
::
ColumnType
ct
);
namespace
std
{
template
<
>
struct
hash
<
pfabric
::
ColumnInfo
>
{
std
::
size_t
operator
()(
const
pfabric
::
ColumnInfo
&
c
)
const
{
return
std
::
hash
<
std
::
string
>
()(
c
.
mColName
);
}
};
}
#endif
src/table/persistent_table.hpp
View file @
98679b10
This diff is collapsed.
Click to expand it.
src/test/persistent_tableTest.cpp
View file @
98679b10
#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do
// this in one cpp file
#include
"catch.hpp"
#include
<iostream>
#include
<sstream>
#include
<string>
#include
<tuple>
#include
"catch.hpp"
#include
<unistd.h>
#include
"fmt/format.h"
#include
"table/TableInfo.hpp"
#include
"table/persistent_table.hpp"
#include
"core/Tuple.hpp"
#include
"core/serialize.hpp"
#include
"pfabric.hpp"
#include
"fmt/format.h"
using
namespace
pfabric
;
using
nvml
::
obj
::
pool
;
...
...
@@ -31,6 +30,8 @@ struct root {
TEST_CASE
(
"Testing storing tuples in persistent_table"
,
"[persistent_table]"
)
{
pool
<
root
>
pop
;
std
::
chrono
::
high_resolution_clock
::
time_point
start
,
end
;
std
::
vector
<
typename
std
::
chrono
::
duration
<
int64_t
,
micro
>::
rep
>
measures
;
const
std
::
string
path
=
"/tmp/testdb.db"
;
std
::
remove
(
path
.
c_str
());
...
...
@@ -48,20 +49,39 @@ TEST_CASE("Testing storing tuples in persistent_table", "[persistent_table]") {
ColumnInfo
(
"c"
,
ColumnInfo
::
String_Type
),
ColumnInfo
(
"d"
,
ColumnInfo
::
Double_Type
)
});
auto
dimInfo
=
BDCCInfo
::
ColumnBitsMap
({
{
ColumnInfo
(
"b"
,
ColumnInfo
::
Int_Type
),
4
},
{
ColumnInfo
(
"d"
,
ColumnInfo
::
Double_Type
),
6
}
});
transaction
::
exec_tx
(
pop
,
[
&
]
{
q
->
pTable
=
make_persistent
<
pTable_type
>
(
tInfo
);});
[
&
]
{
q
->
pTable
=
make_persistent
<
pTable_type
>
(
tInfo
,
dimInfo
);});
}
else
{
std
::
cerr
<<
"WARNING: Table already exists"
<<
std
::
endl
;
}
for
(
unsigned
int
i
=
0
;
i
<
1
0
;
i
++
)
{
for
(
unsigned
int
i
=
0
;
i
<
50
0
;
i
++
)
{
auto
tup
=
MyTuple
(
i
+
1
,
(
i
+
1
)
*
100
,
fmt
::
format
(
"String #{0}"
,
i
),
i
*
12.345
);
q
->
pTable
->
insert
(
tup
);
start
=
std
::
chrono
::
high_resolution_clock
::
now
();
q
->
pTable
->
insert
(
i
+
1
,
tup
);
end
=
std
::
chrono
::
high_resolution_clock
::
now
();
auto
diff
=
std
::
chrono
::
duration_cast
<
std
::
chrono
::
microseconds
>
(
end
-
start
).
count
();
measures
.
push_back
(
diff
);
}
//q->pTable->print(true);
auto
avg
=
std
::
accumulate
(
measures
.
begin
(),
measures
.
end
(),
0
)
/
measures
.
size
();
auto
minmax
=
std
::
minmax_element
(
std
::
begin
(
measures
),
std
::
end
(
measures
));
std
::
cout
<<
"
\n
Insert Statistics in µs: "
<<
"
\n\t
Average: "
<<
avg
<<
"
\n\t
Min: "
<<
*
minmax
.
first
<<
"
\n\t
Max: "
<<
*
minmax
.
second
<<
'\n'
;
//q->pTable->print(false);
//auto ptp = q->pTable->getByKey(5);
//std::cout << "Tuple 5: " << ptp << '\n';
/* Clean up */
transaction
::
exec_tx
(
pop
,
[
&
]
{
delete_persistent
<
pTable_type
>
(
q
->
pTable
);});
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment