From dbd5095dba2596186335cf6d2acc407c19834217 Mon Sep 17 00:00:00 2001 From: Petr Novotnik Date: Sun, 23 Nov 2025 09:51:46 +0100 Subject: [PATCH] Include location in errors --- src/parser/merge.rs | 75 +++++++++++++++++++++++++-------------------- 1 file changed, 41 insertions(+), 34 deletions(-) diff --git a/src/parser/merge.rs b/src/parser/merge.rs index 23522268..4e1b7374 100644 --- a/src/parser/merge.rs +++ b/src/parser/merge.rs @@ -18,12 +18,13 @@ use alloc::{boxed::Box, format, string::ToString, vec, vec::Vec}; use crate::{ ast::{ Ident, Merge, MergeAction, MergeClause, MergeClauseKind, MergeInsertExpr, MergeInsertKind, - MergeUpdateExpr, ObjectName, ObjectNamePart, OutputClause, SetExpr, Statement, TableFactor, + MergeUpdateExpr, ObjectName, ObjectNamePart, OutputClause, SetExpr, Spanned, Statement, + TableFactor, }, dialect::{BigQueryDialect, GenericDialect, MySqlDialect}, keywords::Keyword, parser::IsOptional, - tokenizer::TokenWithSpan, + tokenizer::{Location, TokenWithSpan}, }; use super::{Parser, ParserError}; @@ -232,42 +233,48 @@ impl Parser<'_> { if let Some(alias) = alias { if alias.columns.is_empty() { // ~ only the alias is supported at this point - unqualify_columns(cols, None, Some(&alias.name)).map_err(|e| { - ParserError::ParserError(format!( - "Invalid column for INSERT in a {clause_kind} merge clause: {e}" - )) - }) + match unqualify_columns(cols, None, Some(&alias.name)) { + Ok(column) => Ok(column), + Err((err, loc)) => parser_err!( + format_args!("Invalid column for INSERT in a {clause_kind} merge clause: {err}"), + loc + ), + } } else { - Err(ParserError::ParserError(format!( - "Invalid target ALIAS for INSERT in a {clause_kind} merge clause; must be an identifier" - ))) + parser_err!( + format_args!("Invalid target ALIAS for INSERT in a {clause_kind} merge clause; must be an identifier"), + alias.name.span.start + ) } } else { // ~ allow the full qualifier, but also just the table name if name.0.len() == 1 { - unqualify_columns(cols, Some(name), None).map_err(|e| { - ParserError::ParserError(format!( - "Invalid column for INSERT in a {clause_kind} merge clause: {e}" - )) - }) - } else if let Some(table_name) = + match unqualify_columns(cols, Some(name), None) { + Ok(column) => Ok(column), + Err((err, loc)) => parser_err!( + format_args!("Invalid column for INSERT in a {clause_kind} merge clause: {err}"), + loc) + } + } else if let Some(unqualified_name) = name.0.last().and_then(ObjectNamePart::as_ident) { - unqualify_columns(cols, Some(name), Some(table_name)).map_err(|e| { - ParserError::ParserError(format!( - "Invalid column for INSERT in a {clause_kind} merge clause: {e}" - )) - }) + match unqualify_columns(cols, Some(name), Some(unqualified_name)) { + Ok(column) => Ok(column), + Err((err, loc)) => parser_err!( + format_args!("Invalid column for INSERT in a {clause_kind} merge clause: {err}"), + loc) + } } else { - Err(ParserError::ParserError(format!( - "Invalid target table NAME for INSERT in a {clause_kind} merge clause; must be an identifier" - ))) + parser_err!( + format_args!("Invalid target table NAME for INSERT in a {clause_kind} merge clause; must be an identifier"), + name.span().start + ) } } } else { - Err(ParserError::ParserError(format!( - "Invalid target for INSERT in a {clause_kind} merge clause; must be a TABLE identifier" - ))) + parser_err!( + format_args!("Invalid target for INSERT in a {clause_kind} merge clause; must be a TABLE identifier"), + target_table.span().start) } } else { self.parse_parenthesized_column_list(IsOptional::Optional, allow_empty) @@ -302,8 +309,8 @@ impl Parser<'_> { } } -/// Helper to unqualify a list of columns with either a qualified prefix or a -/// qualifier identifier +/// Helper to unqualify a list of columns with either a qualified prefix +/// (`allowed_qualifier_1`) or a qualifier identifier (`allowed_qualifier_2`.) /// /// Oracle allows `INSERT ([qualifier.]column_name, ...)` in MERGE statements /// with `qualifier` referring to the alias of the target table (if one is @@ -313,13 +320,13 @@ fn unqualify_columns( columns: Vec, allowed_qualifier_1: Option<&ObjectName>, allowed_qualifier_2: Option<&Ident>, -) -> Result, &'static str> { +) -> Result, (&'static str, Location)> { // ~ helper to turn a column name (part) into a plain `ident` // possibly bailing with error - fn to_ident(name: ObjectNamePart) -> Result { + fn to_ident(name: ObjectNamePart) -> Result { match name { ObjectNamePart::Identifier(ident) => Ok(ident), - ObjectNamePart::Function(_) => Err("not an identifier"), + ObjectNamePart::Function(_) => Err(("not an identifier", name.span().start)), } } @@ -353,7 +360,7 @@ fn unqualify_columns( let mut unqualified = Vec::::with_capacity(columns.len()); for mut name in columns { if name.0.is_empty() { - return Err("empty column name"); + return Err(("empty column name", name.span().start)); } if name.0.len() == 1 { @@ -390,7 +397,7 @@ fn unqualify_columns( } } - return Err("not matching target table"); + return Err(("not matching target table", name.span().start)); } Ok(unqualified) }