@@ -87,6 +87,7 @@ static void FindAggregateCalls(ParsedExpression* expr, std::vector<AggregateCall
8787static void CollectTablesFromTableRef (TableRef* ref, std::vector<YardstickTableRef>& tables);
8888static bool ExpressionContainsAggregate (ParsedExpression* expr);
8989static bool ExpressionContainsMeasureRef (ParsedExpression* expr);
90+ static void QualifyColumnRefs (ParsedExpression* expr, const std::string& qualifier);
9091
9192// =============================================================================
9293// AST Walking: Find AGGREGATE() function calls
@@ -458,6 +459,95 @@ static bool ExpressionContainsMeasureRef(ParsedExpression* expr) {
458459 }
459460}
460461
462+ static void QualifyColumnRefs (ParsedExpression* expr, const std::string& qualifier) {
463+ if (!expr) return ;
464+
465+ switch (expr->expression_class ) {
466+ case ExpressionClass::COLUMN_REF: {
467+ auto * col = static_cast <ColumnRefExpression*>(expr);
468+ if (col->column_names .size () == 1 ) {
469+ col->column_names .insert (col->column_names .begin (), qualifier);
470+ }
471+ break ;
472+ }
473+ case ExpressionClass::FUNCTION: {
474+ auto * func = static_cast <FunctionExpression*>(expr);
475+ for (auto & child : func->children ) {
476+ QualifyColumnRefs (child.get (), qualifier);
477+ }
478+ if (func->filter ) {
479+ QualifyColumnRefs (func->filter .get (), qualifier);
480+ }
481+ break ;
482+ }
483+ case ExpressionClass::COMPARISON: {
484+ auto * comp = static_cast <ComparisonExpression*>(expr);
485+ QualifyColumnRefs (comp->left .get (), qualifier);
486+ QualifyColumnRefs (comp->right .get (), qualifier);
487+ break ;
488+ }
489+ case ExpressionClass::CONJUNCTION: {
490+ auto * conj = static_cast <ConjunctionExpression*>(expr);
491+ for (auto & child : conj->children ) {
492+ QualifyColumnRefs (child.get (), qualifier);
493+ }
494+ break ;
495+ }
496+ case ExpressionClass::OPERATOR: {
497+ auto * op = static_cast <OperatorExpression*>(expr);
498+ for (auto & child : op->children ) {
499+ QualifyColumnRefs (child.get (), qualifier);
500+ }
501+ break ;
502+ }
503+ case ExpressionClass::CASE: {
504+ auto * case_expr = static_cast <CaseExpression*>(expr);
505+ for (auto & check : case_expr->case_checks ) {
506+ QualifyColumnRefs (check.when_expr .get (), qualifier);
507+ QualifyColumnRefs (check.then_expr .get (), qualifier);
508+ }
509+ if (case_expr->else_expr ) {
510+ QualifyColumnRefs (case_expr->else_expr .get (), qualifier);
511+ }
512+ break ;
513+ }
514+ case ExpressionClass::CAST: {
515+ auto * cast = static_cast <CastExpression*>(expr);
516+ QualifyColumnRefs (cast->child .get (), qualifier);
517+ break ;
518+ }
519+ case ExpressionClass::SUBQUERY: {
520+ auto * subq = static_cast <SubqueryExpression*>(expr);
521+ if (subq->child ) {
522+ QualifyColumnRefs (subq->child .get (), qualifier);
523+ }
524+ break ;
525+ }
526+ case ExpressionClass::WINDOW: {
527+ auto * window = static_cast <WindowExpression*>(expr);
528+ for (auto & child : window->children ) {
529+ QualifyColumnRefs (child.get (), qualifier);
530+ }
531+ for (auto & part : window->partitions ) {
532+ QualifyColumnRefs (part.get (), qualifier);
533+ }
534+ if (window->filter_expr ) {
535+ QualifyColumnRefs (window->filter_expr .get (), qualifier);
536+ }
537+ break ;
538+ }
539+ case ExpressionClass::BETWEEN: {
540+ auto * between = static_cast <BetweenExpression*>(expr);
541+ QualifyColumnRefs (between->input .get (), qualifier);
542+ QualifyColumnRefs (between->lower .get (), qualifier);
543+ QualifyColumnRefs (between->upper .get (), qualifier);
544+ break ;
545+ }
546+ default :
547+ break ;
548+ }
549+ }
550+
461551// =============================================================================
462552// FFI Implementation: yardstick_find_aggregates
463553// =============================================================================
@@ -919,6 +1009,34 @@ extern "C" char* yardstick_replace_range(
9191009 return safe_strdup (result);
9201010}
9211011
1012+ extern " C" char * yardstick_qualify_expression (const char * expr_str, const char * qualifier) {
1013+ if (!expr_str || !qualifier) return nullptr ;
1014+
1015+ try {
1016+ auto expressions = Parser::ParseExpressionList (expr_str);
1017+ if (expressions.empty ()) {
1018+ return safe_strdup (expr_str);
1019+ }
1020+
1021+ for (auto & expr : expressions) {
1022+ QualifyColumnRefs (expr.get (), qualifier);
1023+ }
1024+
1025+ if (expressions.size () == 1 ) {
1026+ return safe_strdup (expressions[0 ]->ToString ());
1027+ }
1028+
1029+ std::string result;
1030+ for (size_t i = 0 ; i < expressions.size (); i++) {
1031+ if (i > 0 ) result += " , " ;
1032+ result += expressions[i]->ToString ();
1033+ }
1034+ return safe_strdup (result);
1035+ } catch (const std::exception&) {
1036+ return nullptr ;
1037+ }
1038+ }
1039+
9221040// =============================================================================
9231041// FFI Implementation: yardstick_free_string
9241042// =============================================================================
0 commit comments