Eat Study Love

먹고 공부하고 사랑하라

Data Science/Research & Paper

PostgreSQL SQL2NL(1)

eatplaylove 2025. 1. 8. 15:20

parser(1)
0.02MB

 

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

appendStringInfoappendStringInfoString의 차이

 

그냥 문자열에 Formating을 쓰냐 안 쓰냐의 차이였다.

즉 appendStringInfo를 쓰면 '변수'를 삽입할 수 있는 것이고, appendStringInfoString을 쓰면 그냥 '변수'없이 일반 문구를 삽입하는 것이다.

 

이하 Code의 변경사항은 Flow tracking을 위해서 아래 개인 git - hub에서 관리하는 것으로 한다.

https://github.com/bgg-lee/SQL2NL.git