Skip to content

Quoted field names beginning with @ treated as variables by SQL parser #19249

@pmallex

Description

@pmallex

Describe the bug

My understanding, which could be wrong, is that quoted field names should not be treated as placeholder variables when parsing SQL statements.

For example:

SELECT `@my_field` FROM my_table

The SqlToRel parser currently treats these fields as variables, because it only checks for the name starting with @ and does not check if the name was quoted. I think the fix is as simple as updating the condition from id.value.starts_with('@') to id.value.starts_with('@') && id.quote_style.is_none(), but would like to hear some thoughts on this.

To Reproduce

Execute the following test:

    #[test]
    fn test_quoted_field_names_with_at_sign() {
        use std::sync::Arc;
        use datafusion::arrow::datatypes::{DataType, Field, Schema};
        use datafusion::config::ConfigOptions;
        use datafusion::logical_expr::LogicalTableSource;
        use datafusion_sql::planner::{ContextProvider, SqlToRel};
        use datafusion_sql::sqlparser::dialect::GenericDialect;
        use datafusion_sql::sqlparser::parser::Parser;

        struct MyContextProvider {
            config_options: ConfigOptions
        }

        impl ContextProvider for MyContextProvider {
            fn get_table_source(&self, _name: datafusion_sql::TableReference) -> datafusion_common::Result<Arc<dyn datafusion::logical_expr::TableSource>> {
                let mut fields = Vec::new();
                fields.push(Field::new("@my_field", DataType::Utf8, true));
                let schema = Arc::new(Schema::new(fields));
                let table = Arc::new(LogicalTableSource::new(schema));
                Ok(table)
            }
            fn get_function_meta(&self, _name: &str) -> Option<Arc<datafusion::logical_expr::ScalarUDF>> { None }
            fn get_aggregate_meta(&self, _name: &str) -> Option<Arc<datafusion::logical_expr::AggregateUDF>> { None }
            fn get_window_meta(&self, _name: &str) -> Option<Arc<datafusion::logical_expr::WindowUDF>> { None }
            fn get_variable_type(&self, _variable_names: &[String]) -> Option<datafusion::arrow::datatypes::DataType> { None }
            fn options(&self) -> &datafusion::config::ConfigOptions { &self.config_options }
            fn udf_names(&self) -> Vec<String> { Vec::new() }
            fn udaf_names(&self) -> Vec<String> { Vec::new() }
            fn udwf_names(&self) -> Vec<String> { Vec::new() }
        }

        let sql = r#"SELECT `@my_field` FROM my_table"#;
        let dialect = GenericDialect {};
        let abstract_syntax_tree = Parser::parse_sql(&dialect, sql).unwrap();
        let statement = &abstract_syntax_tree[0];
        let context_provider = MyContextProvider { config_options: ConfigOptions::default() };
        let sql_to_rel = SqlToRel::new(&context_provider);
        let plan = sql_to_rel.sql_statement_to_plan(statement.clone());

        println!("{}", plan.unwrap());
    }

Running this test will result in the following error:

called `Result::unwrap()` on an `Err` value: Plan("variable [\"@my_field\"] has no type information")

Expected behavior

This should produce a logical plan with @my_field as an identifier:

Projection: my_table.@my_field
  TableScan: my_table

Additional context

No response

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions