PostgreSQL과 관련된 공식문서는 아래 링크에서 확인하면 된다.
https://www.postgresql.org/docs/17/index.html
PostgreSQL 17.2 Documentation
PostgreSQL 17.2 Documentation The PostgreSQL Global Development Group Copyright © 1996–2024 The PostgreSQL Global Development Group Legal Notice Table of …
www.postgresql.org
PostgreSQL의 Parser 부분에 대한 코드는 아래 git에서 보면 된다.
https://github.com/launchql/pgsql-parser
GitHub - launchql/pgsql-parser: PostgreSQL Query Parser for Node.js
PostgreSQL Query Parser for Node.js. Contribute to launchql/pgsql-parser development by creating an account on GitHub.
github.com
일단 기존, SQL2NL에서는 간단한 EMP / DEPT Table에 대해서 간단한 Select 구문에 한해서만(위 첨부된 parser.c 코드) 구현을 완료했었는데, 이게 코드가 좀 더럽(?)다.
일단 일차적으로 코드를 다듬고, 아무래도 LLM 통해서 대부분 만들어진 Code라 Error case 탐지를 위해 코드가 길어져있는 상태다.
연구를 할 땐, 일단 Error SQL문은 없다고 가정하고 진행..!
기존 코드
generate_text_description(SelectStmt *selectStmt)
{
StringInfoData buf;
initStringInfo(&buf);
appendStringInfoString(&buf, "This Query Retrieves ");
// elog(INFO, "Buffer initialized with: %s", buf.data);
/* SELECT 절 처리 */
// if (selectStmt->targetList == NULL)
// {
// appendStringInfoString(&buf, "No Columns ");
// }
ListCell *lc;
bool first = true;
foreach (lc, selectStmt->targetList)
{
ResTarget *resTarget = (ResTarget *) lfirst(lc);
if (resTarget == NULL)
{
elog(WARNING, "NULL ResTarget in targetList.");
continue;
}
/* 쉼표 처리 */
if (!first)
appendStringInfoString(&buf, ", ");
first = false;
/* 컬럼 이름 처리 */
if (resTarget->name)
{
appendStringInfo(&buf, "column '%s'", resTarget->name);
}
else if (IsA(resTarget->val, ColumnRef))
{
ColumnRef *colRef = (ColumnRef *) resTarget->val;
if (colRef->fields != NIL)
{
Node *firstField = (Node *) linitial(colRef->fields);
if (IsA(firstField, A_Star)) // '*' 가 입력된 경우
{
appendStringInfoString(&buf, "All Records");
}
else // 수정 들어갑니다.. column 명이 입력된 경우
{
if (colRef->fields == NIL)
{
elog(WARNING, "ColumnRef fields are NIL.");
appendStringInfoString(&buf, "unknown column, ");
}
else
{
/* "Record of"는 targetList 루프의 첫 번째 컬럼에서만 추가 */
if (buf.len == strlen("This Query Retrieves "))
{
appendStringInfoString(&buf, "Records Of Field ");
}
/* fields 리스트 순회 */
ListCell *fieldCell;
foreach (fieldCell, colRef->fields)
{
Node *fieldNode = (Node *) lfirst(fieldCell);
if (IsA(fieldNode, String)) // 필드가 문자열인지 확인
{
appendStringInfo(&buf, "%s", strVal(fieldNode));
}
else
{
elog(WARNING, "Unexpected node type in ColumnRef fields: %d", nodeTag(fieldNode));
appendStringInfo(&buf, "unknown_field(type:%d).", nodeTag(fieldNode));
}
}
}
}
}
else
{
elog(WARNING, "ColumnRef fields are NIL.");
appendStringInfoString(&buf, "unknown column, ");
}
}
}
/* FROM 절 처리 */
if (selectStmt->fromClause == NULL)
{
appendStringInfoString(&buf, " With No FROM Clause");
}
else
{
appendStringInfoString(&buf, " From ");
ListCell *lc;
foreach (lc, selectStmt->fromClause)
{
if (IsA(lfirst(lc), RangeVar))
{
RangeVar *rangeVar = (RangeVar *) lfirst(lc);
if (rangeVar->relname)
appendStringInfo(&buf, "Data Table Set '%s', ", rangeVar->relname);
}
}
/* 마지막 쉼표 안전하게 제거 */
if (buf.len > 2 && buf.data[buf.len - 2] == ',')
{
/* 안전하게 문자열 길이를 줄임 */
buf.len -= 2;
buf.data[buf.len] = '\0';
}
}
/* WHERE 절 처리 */
if (selectStmt->whereClause != NULL)
{
// elog(INFO, "WHERE clause detected, nodeTag: %d", nodeTag(selectStmt->whereClause));
appendStringInfoString(&buf, " With Condition ");
if (IsA(selectStmt->whereClause, A_Expr))
{
A_Expr *expr = (A_Expr *) selectStmt->whereClause;
/* 왼쪽 피연산자 처리 */
// elog(INFO, "Processing left operand...");
if (IsA(expr->lexpr, ColumnRef))
{
ColumnRef *colRef = (ColumnRef *) expr->lexpr;
if (colRef->fields != NIL)
{
Node *field = (Node *) linitial(colRef->fields);
if (IsA(field, String))
{
const char *fieldName = strVal(field);
appendStringInfo(&buf, "Field '%s' ", fieldName);
// elog(INFO, "Left operand: Field '%s'", fieldName);
}
else
{
// elog(WARNING, "Unsupported ColumnRef field type in lexpr: %d", nodeTag(field));
appendStringInfoString(&buf, "unknown field ");
}
}
else
{
elog(WARNING, "ColumnRef fields are NIL in lexpr.");
appendStringInfoString(&buf, "unknown field ");
}
}
else
{
elog(WARNING, "Unsupported left operand type in WHERE clause: %d", nodeTag(expr->lexpr));
appendStringInfoString(&buf, "unknown left operand ");
}
/* 연산자 처리 */
// elog(INFO, "Processing operator...");
if (list_length(expr->name) == 1)
{
Node *opNode = (Node *) linitial(expr->name);
if (IsA(opNode, String))
{
const char *operatorStr = strVal(opNode);
// elog(INFO, "Operator: %s", operatorStr);
if (strcmp(operatorStr, "=") == 0)
{
appendStringInfoString(&buf, "is equal to ");
}
else if (strcmp(operatorStr, ">") == 0)
{
appendStringInfoString(&buf, "is greater than ");
}
else if (strcmp(operatorStr, "<") == 0)
{
appendStringInfoString(&buf, "is less than ");
}
else
{
elog(WARNING, "Unsupported operator in WHERE clause: %s", operatorStr);
appendStringInfoString(&buf, "unknown operator ");
}
}
else
{
elog(WARNING, "Unsupported operator node type in WHERE clause, nodeTag: %d", nodeTag(opNode));
appendStringInfoString(&buf, "unknown operator ");
}
}
else
{
elog(WARNING, "Unsupported operator structure in WHERE clause.");
}
/* 오른쪽 피연산자 처리 */
// elog(INFO, "Processing right operand...");
if (IsA(expr->rexpr, A_Const))
{
A_Const *constVal = (A_Const *) expr->rexpr;
if (IsA(&constVal->val, Integer))
{
int rightValue = intVal(&constVal->val);
appendStringInfo(&buf, "%d", rightValue);
// elog(INFO, "Right operand is Integer: %d", rightValue);
}
else if (IsA(&constVal->val, Float))
{
const char *rightValue = strVal(&constVal->val);
appendStringInfo(&buf, "%s", rightValue);
// elog(INFO, "Right operand is Float: %s", rightValue);
}
else if (IsA(&constVal->val, String))
{
const char *rightValue = strVal(&constVal->val);
appendStringInfo(&buf, "'%s'", rightValue);
// elog(INFO, "Right operand is String: '%s'", rightValue);
}
else
{
elog(WARNING, "Unsupported A_Const type in rexpr.");
appendStringInfoString(&buf, "unknown value");
}
}
else
{
elog(WARNING, "Unsupported right operand in WHERE clause, nodeTag: %d", nodeTag(expr->rexpr));
appendStringInfoString(&buf, "unknown right operand ");
}
}
else
{
elog(WARNING, "Unsupported WHERE clause structure, nodeTag: %d", nodeTag(selectStmt->whereClause));
appendStringInfoString(&buf, "unknown condition");
}
}
else
{
// elog(INFO, "No WHERE clause found.");
}
// elog(INFO, "Final NL Description: %s", buf.data);
return buf.data;
}
PSQL의 SQL문을 String 처리함에 있어서 자주 등장하는 두 Method
appendStringInfo 와 appendStringInfoString의 차이
그냥 문자열에 Formating을 쓰냐 안 쓰냐의 차이였다.
즉 appendStringInfo를 쓰면 '변수'를 삽입할 수 있는 것이고, appendStringInfoString을 쓰면 그냥 '변수'없이 일반 문구를 삽입하는 것이다.
이하 Code의 변경사항은 Flow tracking을 위해서 아래 개인 git - hub에서 관리하는 것으로 한다.
https://github.com/bgg-lee/SQL2NL.git
'Data Science > Research & Paper' 카테고리의 다른 글
PostgreSQL SQL2NL(3) (0) | 2025.02.03 |
---|---|
PostgreSQL SQL2NL(2) (0) | 2025.01.14 |
PostgreSQL 실습에 유용한 code (1) | 2025.01.02 |
PostgreSQL 설치 및 특징 (2) | 2024.12.27 |
Relational Diagrams, Query Patterns, Pattern Expressiveness of Relational Languages (SIGMOD 2024) (1) | 2024.09.25 |