Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CUBVEC-24] Convert CHAR types to DB Vector in tp_value_cast #5771

Open
wants to merge 13 commits into
base: cubvec/cubvec
Choose a base branch
from

Conversation

vimkim
Copy link
Contributor

@vimkim vimkim commented Jan 6, 2025

http://jira.cubrid.org/browse/CUBVEC-24

Purpose

  • object_domain.c에 문자열을 VECTOR 타입으로 변환하는 기능을 추가했습니다
  • "[1.1, 2.2, 3.3]"와 같은 문자열을 VECTOR 타입으로 파싱하는 기능을 구현했습니다
  • CHAR/VARCHAR 타입에서 VECTOR 타입으로의 타입 캐스팅을 지원합니다

Implementation

핵심 구현 사항

  • 문자열을 float 배열로 변환하는 tp_str_to_vector() 헬퍼 함수 추가

    static int tp_str_to_vector(const DB_VALUE * src, DB_VALUE * result)
  • tp_value_cast_internal() 함수에 VECTOR 케이스 처리 추가

    case DB_TYPE_VECTOR:
      switch (original_type) {
        case DB_TYPE_CHAR:
        case DB_TYPE_VARCHAR:
          err = tp_str_to_vector(src, target);
          break;
        ...
      }
  • 벡터 생성을 위한 새로운 함수 추가 (db_set.c)

    DB_SET * db_vec_create(MOP classop, const char *name, int size)

파일별 변경사항

  1. src/object/object_domain.c

    • tp_str_to_vector() 함수 구현
    • VECTOR 타입 캐스팅 로직 추가
  2. src/compat/db_set.c

    • db_vec_create() 함수 구현
    • 벡터 메모리 할당 및 초기화 로직
  3. src/compat/db_set_function.h

    • db_vec_create() 함수 선언 추가

Remarks

  • 파싱 구현 시 고려사항:

    • 공백 문자 처리 (intl_skip_spaces() 사용)
    • 숫자 형식 검증
    • 벡터 형식 검증 (대괄호, 콤마 등)
    • 오버플로우 방지
  • 기존 시퀀스 타입 처리 패턴을 참고하여 일관성 있게 구현

    • db_seq_create() 함수의 패턴을 따라 구현
    • 에러 처리도 동일한 방식 적용
  • 제한사항

    • 벡터 최대 크기: 2000 요소
    • float 타입만 지원
    • 메모리 overflow 방지를 위한 버퍼 크기 제한 (number_buffer[64])

Test Cases Example

-- Basic floating point formats
insert into vt (vec) values('[1,2,3,4,5,6,7,7]');
insert into vt (vec) values('[1234,12341234,123412341234,1234123412341234.1234]');
insert into vt (vec) values('[3,3,3,3.5,2.4]');
insert into vt (vec) values('[1.0, 2.0, 3.0]');
insert into vt (vec) values('[1.1111, 2.2222, 3.3333, 4.4444]');
insert into vt (vec) values('[0.1, 0.01, 0.001, 0.0001]');

-- Scientific notation
insert into vt (vec) values('[1.234e-5, 1.234e5, 1.234e10]');
insert into vt (vec) values('[1E-10, 1E10, 1E-3]');

-- Mixed integer and float
insert into vt (vec) values('[1, 2.5, 3, 4.75, 5]');

-- Negative numbers
insert into vt (vec) values('[-1.5, -2.25, -3.75]');
insert into vt (vec) values('[-0.001, -0.002, -0.003]');

-- Edge cases
insert into vt (vec) values('[0.0, -0.0, 1.0, -1.0]');
insert into vt (vec) values('[9999999.9999999, -9999999.9999999]');

-- Whitespace handling
insert into vt (vec) values('[  1.5  ,  2.5  ,  3.5  ]');
insert into vt (vec) values('[1.1,    2.2,   3.3   ]');

-- Precision tests
insert into vt (vec) values('[1.123456789, 2.123456789, 3.123456789]');
insert into vt (vec) values('[0.000000001, 0.999999999]');

-- Mixed precision
insert into vt (vec) values('[1.1, 2.22, 3.333, 4.4444, 5.55555]');

Results


  vec
======================
  [1.000000e+00, 2.000000e+00, 3.000000e+00, 4.000000e+00, 5.000000e+00, 6.000000e+00, 7.000000e+00, 7.000000e+00]
  [1.234000e+03, 1.234123e+07, 1.234123e+11, 1.234123e+15]
  [3.000000e+00, 3.000000e+00, 3.000000e+00, 3.500000e+00, 2.400000e+00]
  [1.000000e+00, 2.000000e+00, 3.000000e+00]
  [1.111100e+00, 2.222200e+00, 3.333300e+00, 4.444400e+00]
  [1.000000e-01, 1.000000e-02, 1.000000e-03, 1.000000e-04]
  [1.234000e-05, 1.234000e+05, 1.234000e+10]
  [1.000000e-10, 1.000000e+10, 1.000000e-03]
  [1.000000e+00, 2.500000e+00, 3.000000e+00, 4.750000e+00, 5.000000e+00]
  [-1.500000e+00, -2.250000e+00, -3.750000e+00]
  [-1.000000e-03, -2.000000e-03, -3.000000e-03]
  [0.000000e+00, -0.000000e+00, 1.000000e+00, -1.000000e+00]
  [1.000000e+07, -1.000000e+07]
  [1.500000e+00, 2.500000e+00, 3.500000e+00]
  [1.100000e+00, 2.200000e+00, 3.300000e+00]
  [1.123457e+00, 2.123457e+00, 3.123457e+00]
  [1.000000e-09, 1.000000e+00]
  [1.100000e+00, 2.220000e+00, 3.333000e+00, 4.444400e+00, 5.555550e+00]

@vimkim vimkim changed the title Cubvec/24 insert value coerce [CUBVEC-24] 테이블의 Vector 컬럼과 Insert 값 타입을 호환 Jan 6, 2025
Comment on lines 5124 to 5144
// Create vector and populate it
vec = db_vec_create (NULL, NULL, 0);
if (vec == NULL)
{
assert (er_errid () != NO_ERROR);
return er_errid ();
}

db_make_vector (result, vec);

for (int i = 0; i < count; ++i)
{
db_make_float (&e_val, float_array[i]);
if (db_seq_put (db_get_set (result), i, &e_val) != NO_ERROR)
{
return ER_FAILED;
}
}

return NO_ERROR;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

내부적으로 Set (Sequence)로 구현된 벡터의 틀을 우선 만들고,

db_make_vector(): db_set을 DB_VALUE화 한다.
db_make_float() : c 언어의 float을 DB_VALUE화한다.
db_seq_put (db_get_set ( ... )) : DB_VALUE가 된 Float을 Vector에 삽입합니다.

해당 코드는,

INSERT INTO st VALUES ({1.1, 2.2, 3.3});

실행 스택을 벤치마킹하였습니다.

Comment on lines +5013 to +5015
const int number_buffer_size = 64;
char number_buffer[number_buffer_size];
int buffer_idx;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

object_domain.c 코드 전반에서 64라는 값을 버퍼의 최대 값으로 설정하는 함수들을 벤치마킹했습니다.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

64자로 지정하면 숫자 문자열을 63자까지만 처리하다는 의미죠 ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네, 맞습니다. 검토 결과, 뒤에 '\0'을 넣을 공간을 확보하기 위해 number_buffer_size를 사용하는 모든 부분에서 -1을 적용해야 한다는 점을 깨달았습니다. 꼼꼼한 리뷰에 감사드립니다.

Comment on lines +5016 to +5017
const int max_vector_size = 2000;
float float_array[max_vector_size];
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

float 배열의 최대 크기를 2000 으로 설정했습니다.

@vimkim vimkim changed the title [CUBVEC-24] 테이블의 Vector 컬럼과 Insert 값 타입을 호환 [CUBVEC-24] Convert CHAR types to DB Vector in tp_value_cast Jan 6, 2025
Comment on lines +9280 to +9284
case DB_TYPE_CHAR:
case DB_TYPE_VARCHAR:
case DB_TYPE_NCHAR:
case DB_TYPE_VARNCHAR:
{
Copy link
Contributor Author

@vimkim vimkim Jan 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분에 대한 확신이 없습니다. 죄송합니다. 면밀히 리뷰 부탁드립니다.

현재 테스트 케이스는 DB_TYPE_CHAR 만으로도 통과하고 있습니다. 다른 테이블의 varchar 값을 vector로 변환하는 과정에서 나머지 3개의 case들이 필요할 것으로 추정하고 있습니다.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DB_TYPE_NCHAR와 DB_TYPE_VARNCHAR는 제거하는 것은 어떨까요 ?

Comment on lines +4998 to +4999
/*
* tp_str_to_vector - Coerce a string to a vector.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 함수를 특히 면밀히 리뷰를 해주시면 감사하겠습니다. 감사합니다.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 함수의 구현은 db_date.c에 구현되어 있는 db_string_to_XXX 와 비슷한 기능 목적으로 보여서, 동일한 루틴을 가져가도 좋을 것 같습니다. 동일한 파일의 tp_atotimestamptz ()를 참고해서 비슷하게 구현해보면 좋지 않을까요.

다음 두가지에 대해서 검토해볼 수 있을 것 같습니다

  1. db_string_to_vector() 구현 위치
    option 1. src/compat/db_vector.cpp, src/compat/db_vector.hpp 생성 후 db_string_to_vector 구현
    option 2. src/compat/db_set.c, src/compat/db_set.h 에 구현 후 이후에 다른 이슈에서 option 1 진행

option 1 또는 option 2 진행 후에는 이 함수가 잘 정리될 것 같습니다.

  1. intl_skip_spaces () 사용

문자열은 float array를 가지므로, 간단히 '[', [0-9], ']', ',', '-', ' ' 외에는 다른 문자는 담겨있지 않다고 가정하고 있을 것 같습니다. 따라서 모든 문자는 ASCII 영역에 있을 것이므로 intl_skip_spaces ()를 사용하지 않고 isspace ()로 충분해 보입니다.

위 구현으로 충분하려면 intl_skip_spaces ()에서 INTL_CODESET_KSC5601_EUC (= EUC-KR) 에서 확인하고 있는 0xa1a1에 대해서 다음의 결정이 필요해보입니다.

  • vector를 나타내는 문자열에 0xa1a1을 사용 가능한 공백으로 볼지, 에러 문자로 생각할지

이슈 진행 과정에서 예외 사항에 대한 최소한의 확인으로 진행하기로 했기 때문에 제 의견은 0xa1a1에 대해서는 유효하지 않은 문자라고 생각하고 char_isspace() 만으로만 공백을 처리하는 것이 좋을 것 같습니다.

의견 부탁드립니다 :-)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

정말 중요한 조언 감사드립니다.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. 파일을 분리하는 것이 고민이었는데 아이디어에 감사드립니다.
  2. 최소한의 예외 사항을 처리해야 할지 고민이었는데, 말씀하신 것처럼 기존에 정한 스펙을 따라가겠습니다.

@vimkim
Copy link
Contributor Author

vimkim commented Jan 6, 2025

테스트 실패한 케이스는 isdeterministic 의 추가 때문입니다. 디벨롭 최신화하면 통과할 것으로 추정하고 있습니다.

@vimkim vimkim marked this pull request as ready for review January 6, 2025 09:19
@vimkim vimkim requested a review from a team as a code owner January 6, 2025 09:19
@vimkim vimkim requested review from hgryoo, hornetmj, mhoh3963 and YeunjunLee and removed request for a team January 6, 2025 09:19
// Convert to float
char *end_ptr = NULL;
errno = 0;
float_array[count] = strtof (number_buffer, &end_ptr);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

큐브리드는 bison에서 strtof, strtod 를 통해 파싱하고 있습니다.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

strtof 의 스펙 덕분에 자연그럽게 아래 쿼리도 동작합니다.

insert into vt (vec) values('[1.234e-5, 1.234e5, 1.234e10]');
insert into vt (vec) values('[1E-10, 1E10, 1E-3]');

확인:

#include <stdio.h>
#include <stdlib.h>

int main() {
  const char *str1 = "3.14e2";   // Scientific notation
  const char *str2 = "2.718E-1"; // Scientific notation with uppercase E

  char *end;

  double num1 = strtod(str1, &end);
  float num2 = strtof(str2, &end);

  printf("strtod: %f\n", num1); // Outputs 314.000000
  printf("strtof: %f\n", num2); // Outputs 0.271800

  return 0;
}

Comment on lines +309 to +316
* note : The new set will not be attached to any object, so you must use the
* db_put( ) function to assign it as the value of an attribute. If the size
* is not known, it is permissible to pass zero.
*/
DB_SET *
db_vec_create (MOP classop, const char *name, int size)
{
DB_SET *set;
Copy link
Contributor Author

@vimkim vimkim Jan 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분은 보조 함수이니, 리뷰에 공을 많이 들이지 않으셔도 될 것 같습니다.

기존의 db_seq_create() 함수를 완전히 모방하였으며,

기존에는 set_create_vector() 함수만으로 진행하려 계획이었으나
기존 sequence insert의 콜스택을 분석하면서 db_seq_create 이 호출되는 것을 인지하고,
조금 더 완벽하게 sequence를 모방하고자 db_vec_create() 함수를 생성하기로 했습니다.

src/object/object_domain.c Outdated Show resolved Hide resolved
@vimkim vimkim force-pushed the cubvec/24-insert-value-coerce branch from fe70164 to 2bbfce7 Compare January 10, 2025 05:16
@vimkim vimkim changed the base branch from cubvec/24-base to cubvec/cubvec January 10, 2025 05:21
@vimkim
Copy link
Contributor Author

vimkim commented Jan 13, 2025

형규님의 의견을 받아들여서 리팩토링을 수행하기 이전에,
동일한 행동을 보장하고자

http://jira.cubrid.org/browse/CUBVEC-24?focusedCommentId=4769349&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-4769349

위와 같은 쉘 테스트를 만들었습니다.

Comment on lines 60 to 65
${COMPAT_DIR}/db_temp.c
${COMPAT_DIR}/db_value_printer.cpp
${COMPAT_DIR}/db_vdb.c
${COMPAT_DIR}/db_vector.cpp
${COMPAT_DIR}/db_virt.c
${COMPAT_DIR}/dbtype_function.c
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sa 모드에는 COMPAT_HEADERS 파일을 추가하는 부분이 없습니다.
따라서 db_vector.hpp를 추가하지 않았고, 그럼에도 정상 동작합니다.

사실 코드를 살펴보니 cs/CMakeLists.txt 파일에서도 COMPAT_HEADERS가 전혀 사용되지 않고 있기 때문에, COMPAT_HEADERS를 전부 삭제해도 괜찮은 것으로 분석했습니다.

다만 해당 이슈에서는 고려하지 않겠습니다.

Comment on lines 30 to 39
int
db_string_to_vector (const char *p, int str_len, float * vector, int *p_count)
{
const char *end = p + str_len;
int count = 0;
const int number_buffer_size = 64;
char number_buffer[number_buffer_size];
int buffer_idx;
const int max_vector_size = 2000;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

기존에 리뷰를 승인 받은 구현을 그대로 db_vector.cpp 파일로 옮겨와, 코드를 다른 파일로 독립시켰습니다.

Comment on lines +5022 to +5025
int error = db_string_to_vector(p, db_get_string_size(src), float_array, &count);
if (error != NO_ERROR) {
return ER_FAILED;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

기존의 긴 구현을 db_vector.cpp로 분리시켰습니다.

Comment on lines 102 to 108
if (!std::isspace (input[number_end]))
{
number_buffer.push_back (input[number_end]);
}
++number_end;
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재 '[1.00 00, 2.0]' 을 정상으로 인식하는 오류가 있습니다. 수정하겠습니다.

@vimkim vimkim self-assigned this Jan 20, 2025
src/compat/db_vector.cpp Outdated Show resolved Hide resolved
src/compat/db_vector.cpp Outdated Show resolved Hide resolved
Comment on lines +74 to +75
float num = doc[i].GetFloat();

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

내부구현은 GetDouble 후 static_cast 입니다. 엄밀한 검사를 위하면 lossless get float을 사용하라는 주석이 rapidjson에 있습니다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants