반응형
이전 내용에서 크기가 고정된 데이터 유형을 멤버로 가지고 있는 구조체를 TimescaleDB에 넣고 꺼내어 값을 확인하였다. char 및 char array와 달리 string을 멤버로 가지고 있는 구조체는 데이터베이스에 삽입 후 조회하는 데 어려움이 있었다. 이번 글에서는 bit field를 멤버로 가지는 구조체를 삽입 후에 조회한다.
이전 내용을 학습하고 싶으면 아래의 링크를 따라가면 된다.
https://namsaenga.tistory.com/30
1. bit field 멤버를 가지고 있는 구조체를 varchar 필드에 삽입
a. InputBitFieldStruct.cpp 소스코드
#include <cpp_redis-master/includes/cpp_redis/cpp_redis>
#include <iostream>
#include <string>
#include <random>
#include <pqxx/pqxx>
using namespace std;
struct TWO_16 {
unsigned int a : 16;
unsigned int b : 16;
};
struct ONE_32{
unsigned int a : 32;
};
struct FOUR_8 {
unsigned int a : 8;
unsigned int b : 8;
unsigned int c : 8;
unsigned int d : 8;
};
struct EIGHT_4 {
unsigned int a : 4;
unsigned int b : 4;
unsigned int c : 4;
unsigned int d : 4;
unsigned int e : 4;
unsigned int f : 4;
unsigned int g : 4;
unsigned int h : 4;
};
struct SIXTEEN_2 {
unsigned int a : 2;
unsigned int b : 2;
unsigned int c : 2;
unsigned int d : 2;
unsigned int e : 2;
unsigned int f : 2;
unsigned int g : 2;
unsigned int h : 2;
unsigned int i : 2;
unsigned int j : 2;
unsigned int k : 2;
unsigned int l : 2;
unsigned int m : 2;
unsigned int n : 2;
unsigned int o : 2;
unsigned int p : 2;
};
struct ONE_32_1 {
unsigned int a;
};
struct TEST {
unsigned int a : 4;
unsigned int b : 9;
unsigned int c : 3;
unsigned int d : 4;
unsigned int e : 7;
unsigned int f : 5;
};
static int DoInputBitFieldStruct() {
ONE_32 test_struct = {232324444};
pqxx::connection c{"postgres://postgres:1234@127.0.0.1:5432/example"};
pqxx::work work{c};
c.prepare("DoInputBitFieldStructTest", "insert into test2(time, varchar_content, bytea_content) values($1, $2, $3)");
std::basic_string<std::byte> test_struct_byte_str(
static_cast<const std::byte *>(static_cast<const void *>(&test_struct)),
sizeof(ONE_32)
);
work.exec_prepared("DoInputBitFieldStructTest", "now()" , test_struct_byte_str, test_struct_byte_str);
work.commit();
return 0;
}
int main() {
return DoInputBitFieldStruct();
}
b. 실행 및 결과 화면
- unsigned int 자료형의 멤버가 있는 구조체를 varchar 자료형 필드에 삽입할 수 없다.
[namsaenga@localhost postgresql_examples]$ g++ InputBitFieldStruct.cpp -std=c++17 -o InputBitFieldStruct -lpqxx
[namsaenga@localhost postgresql_examples]$ ./InputBitFieldStruct
terminate called after throwing an instance of 'pqxx::data_exception'
what(): Failure during 'DoInputBitFieldStructTest': 오류: "UTF8" 인코딩에서 사용할 수 없는 문자가 있음: 0xa0
CONTEXT: unnamed portal parameter $2
Aborted (core dumped)
2. bit field 멤버를 가지고 있는 구조체를 bytea 필드에 삽입
- 1번과 코드 내용이 동일하다. 다만 insert문의 2번째 파라미터를 일반 문자열로 대체해서 오류가 안 나게 했다.
- TimescaleDB는 bit field를 Hex(16진수)로 변환해서 특정 규칙을 적용하여 저장한다.
[1. One-32 bit field]
- 정수 232324444는 16진수로 0dd8fd5c인데 bytea로 저장될 때 5cfdd80d로 저장이 된다.
a. 실행 및 결과 화면
example=# select * from test2;
time | varchar_content | bytea_content
----------------------------+----------------------+---------------
2022-03-22 10:33:04.747687 | One_32 BitField Test | \x5cfdd80d
(1 row)
example=# select convert_from(bytea_content, 'UTF8') from test2;
오류: "UTF8" 인코딩에서 사용할 수 없는 문자가 있음: 0xfd
[2. Two-16 bit field]
a. 소스코드 추가 부분
TWO_16 test_struct2 = {5432, 2345};
b. 실행 및 결과 화면
- 정수 5432는 16진수로 1538이고 정수 2345는 16진수로 929인데, 저장될 때는 각각 3815와 2909로 저장이 된다.
example=# select * from test2;
time | varchar_content | bytea_content
----------------------------+----------------------+---------------
2022-03-22 10:42:20.760586 | Two_16 BitField Test | \x38152909
(1 row)
[3. four-8 bit field]
a. 소스코드 추가 부분
FOUR_8 test_struct3 = {123,234,38,75};
b. 실행 및 결과 화면
- 123은 7B, 234는 EA, 38는 26, 75는 4B인데 저장될 때 동일하게 저장이 된다.
example=# select * from test2;
time | varchar_content | bytea_content
----------------------------+----------------------+---------------
2022-03-22 11:38:43.736602 | Four_8 BitField Test | \x7bea264b
(1 row)
[4. Eight-4 bit field]
a. 소스코드 추가 부분
EIGHT_4 test_struct4 = {1,2,5,6,10,11,12,13};
b. 실행 및 결과 화면
- 1은 1, 2는 2, 5는 5, 6은 6, 10은 a, 11은 b, 12는 c, 13은 d 이다.
example=# select * from test2;
time | varchar_content | bytea_content
----------------------------+----------------------+---------------
2022-03-22 11:43:48.326316 | Eight_4 BitField Test | \x2165badc
[5. Sixteen-2 bit field]
a. 소스코드 추가 부분
SIXTEEN_2 test_struct5 = {0,1,2,3,2,0,1,3,0,2,1,1,2,2,3,3};
b. 실행 및 결과 화면
- 규칙을 알기가 어렵다.
example=# select * from test2;
time | varchar_content | bytea_content
----------------------------+----------------------+---------------
2022-03-22 11:47:24.609994 | Sixteen_2 BitField Test | \xe4d258fa
(1 row)
[6. no-regular Random bit field]
a. 소스코드 추가 부분
TEST test_struct6 = {14, 253, 7, 10, 111, 18};
b. 실행 및 결과 화면
- 첫 4-bit field 만 제대로 값이 나왔고, 그다음부터 규칙을 알기 어렵다.
example=# select * from test2;
time | varchar_content | bytea_content
----------------------------+---------------------------------+---------------
2022-03-22 11:58:15.493215 | No-regular Random BitField Test | \xdeeffa96
(1 row)
[7. Regular Random bit field]
a. 소스코드 추가 부분
struct TEST2 {
unsigned int a : 4;
unsigned int b : 8;
unsigned int c : 16;
unsigned int d : 4;
};
TEST2 test_struct7 = {14, 60, 5555, 14};
b. 실행 및 결과 화면
- 5555는 15B3, 60은 3C, 14는 E이다. 각 hex값이 분포해있긴 한데 규칙을 알기 어렵다.
example=# select * from test2;
time | varchar_content | bytea_content
----------------------------+-----------------------+---------------
2022-03-22 12:05:35.175355 | Regular BitField Test | \xce335be1
(1 row)
c. 소스코드 추가 부분 2
struct TEST3 {
unsigned int a : 4;
unsigned int b : 4;
unsigned int c : 16;
unsigned int d : 8;
};
TEST3 test_struct8 = {14, 14, 5555, 60};
d. 실행 및 결과 화면
- TEST3이 TEST2보다 더 규칙성을 가지고 있었다.
example=# select * from test2;
time | varchar_content | bytea_content
----------------------------+-----------------------+---------------
2022-03-22 12:17:21.922064 | Regular BitField Test | \xeeb3153c
(1 row)
- 결론: TimescaleDB(또는 PostgreSQL)에서 bytea 필드에 bit field 구조체를 넣을 때는 4bit, 8bit, 16bit, 32bit의 고정된 크기의 멤버를 가진 구조체를 넣는다. 멤버의 비트 필드가 한 종류가 아닐 때는 적어도 앞에서부터 1byte 단위씩 맞추려고 해야 한다.
3. bit field 멤버를 가지고 있는 구조체를 조회한 후 사용하기
a. OutputBitFieldStruct.cpp 소스코드
#include <cpp_redis-master/includes/cpp_redis/cpp_redis>
#include <iostream>
#include <string>
#include <random>
#include <pqxx/pqxx>
#include <sstream>
using namespace std;
std::string base64_encode(const std::string &in) { typedef unsigned char uchar; std::string out; int val = 0, valb = -6; for (uchar c : in) { val = (val << 8) + c; valb += 8; while (valb >= 0) { out.push_back("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(val >> valb) & 0x3F]); valb -= 6; } } if (valb > -6) out.push_back("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[((val << 8) >> (valb + 8)) & 0x3F]); while (out.size() % 4) out.push_back('='); return out; }
std::string base64_decode(const std::string &in) { typedef unsigned char uchar; static const std::string b = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; std::string out; std::vector<int> T(256, -1); for (int i = 0; i < 64; i++) T[b[i]] = i; int val = 0, valb = -8; for (uchar c : in) { if (T[c] == -1) break; val = (val << 6) + T[c]; valb += 6; if (valb >= 0) { out.push_back(char((val >> valb) & 0xFF)); valb -= 8; } } return out; }
struct TWO_16 {
unsigned int a : 16;
unsigned int b : 16;
};
struct ONE_32{
unsigned int a : 32;
};
struct FOUR_8 {
unsigned int a : 8;
unsigned int b : 8;
unsigned int c : 8;
unsigned int d : 8;
};
struct EIGHT_4 {
unsigned int a : 4;
unsigned int b : 4;
unsigned int c : 4;
unsigned int d : 4;
unsigned int e : 4;
unsigned int f : 4;
unsigned int g : 4;
unsigned int h : 4;
};
struct SIXTEEN_2 {
unsigned int a : 2;
unsigned int b : 2;
unsigned int c : 2;
unsigned int d : 2;
unsigned int e : 2;
unsigned int f : 2;
unsigned int g : 2;
unsigned int h : 2;
unsigned int i : 2;
unsigned int j : 2;
unsigned int k : 2;
unsigned int l : 2;
unsigned int m : 2;
unsigned int n : 2;
unsigned int o : 2;
unsigned int p : 2;
};
struct TEST {
unsigned int a : 4;
unsigned int b : 9;
unsigned int c : 3;
unsigned int d : 4;
unsigned int e : 7;
unsigned int f : 5;
};
struct TEST2 {
unsigned int a : 4;
unsigned int b : 8;
unsigned int c : 16;
unsigned int d : 4;
};
struct TEST3 {
unsigned int a : 4;
unsigned int b : 4;
unsigned int c : 16;
unsigned int d : 8;
};
struct ONE_32_1 {
unsigned int a;
};
static int DoOutputStruct() {
TEST3 test;
char container[100];
pqxx::connection c{"postgres://postgres:1234@127.0.0.1:5432/example"};
pqxx::work work{c};
pqxx::result result_value = work.exec("select time, varchar_content, bytea_content from test2");
for(pqxx::result::const_iterator iter = result_value.begin(); iter != result_value.end(); iter++){
//cout << "time = " << iter[0].as<string>() << endl;
//cout << "ani1 = " << iter[1].as<string>() << endl;
//cout << "ani2 = " << iter[2].as<string>() << endl;
//memmove((char*)&container, iter[2].as<string>().c_str(), sizeof(iter[2].as<string>()));
//int size = sizeof(iter[2].as<string>().c_str());
string cont_str = iter[2].as<string>().c_str();
cont_str.replace(0, 2,"");
unsigned int a,b,c,d;
std::stringstream ss;
ss << std::hex << cont_str.substr(0,1);
ss >> b;
ss.clear();
ss << std::hex << cont_str.substr(1,1);
ss >> a;
ss.clear();
ss << std::hex << cont_str.substr(4,2);
ss << std::hex << cont_str.substr(2,2);
ss >> c;
ss.clear();
ss << std::hex << cont_str.substr(6,2);
ss >> d;
ss.clear();
test.a = static_cast<int>(a);
test.b = static_cast<int>(b);
test.c = static_cast<int>(c);
test.d = static_cast<int>(d);
std::cout << test.a << " " << test.b << " " << test.c << " " << test.d << std::endl;
}
return 0;
}
int main() {
return DoOutputStruct();
}
b. 실행 및 결과 화면
[namsaenga@localhost postgresql_examples]$ g++ OutputBitFieldStruct.cpp -std=c++17 -o OutputBitFieldStruct -lpqxx
[namsaenga@localhost postgresql_examples]$ ./OutputBitFieldStruct
14 14 5555 60
- 결론: 쿼리 결과를 문자열로 받기 때문에 문자열의 일부를 stringstream에 넣고 꺼내서 정수형으로 변환한 후에 사용한다.
반응형
'프로그래밍 > TimescaleDB | PostgreSQL' 카테고리의 다른 글
Linux에서 TimescaleDB(PostgreSQL)를 외부로 오픈할 때 필요한 명령어 (0) | 2022.03.25 |
---|---|
TimescaleDB에 구조체 삽입 후 조회(1) (0) | 2022.03.22 |
Server-side에서 TimescaleDB libpqxx 라이브러리 설치 및 사용하기 (0) | 2022.03.21 |
Linux에 TimescaleDB 설치하기 (0) | 2022.03.14 |
댓글