본문 바로가기
프로그래밍/TimescaleDB | PostgreSQL

TimescaleDB에 구조체 삽입 후 조회(2)

by 남생 namsaeng 2022. 3. 23.
반응형

이전 내용에서 크기가 고정된 데이터 유형을 멤버로 가지고 있는 구조체를 TimescaleDB에 넣고 꺼내어 값을 확인하였다. char 및 char array와 달리 string을 멤버로 가지고 있는 구조체는 데이터베이스에 삽입 후 조회하는 데 어려움이 있었다. 이번 글에서는 bit field를 멤버로 가지는 구조체를 삽입 후에 조회한다. 

 

이전 내용을 학습하고 싶으면 아래의 링크를 따라가면 된다.

https://namsaenga.tistory.com/30

 

TimescaleDB에 구조체 삽입 후 조회(1)

TimescaleDB에 일반 데이터 타입이 아닌 구조체 데이터를 삽입 후 조회하기 위한 방법이다. 1. 로그인 비밀번호가 1234인 postgres 유저로 호스트 127.0.0.1, 포트 5432인 DBMS에 접속한 후에 example 데이터베

namsaenga.tistory.com

 


 

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

 

 
 
 
 
반응형

댓글