agent_patch
runs/2026-02-28__18-27-22__gpt-5-1-codex-mini/flux-pr-1576/flux-pr-1576.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/agent-logs/agent.patch
6427 bytes
diff --git a/tmp/agent-patch-flux-pr-1576.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/src/dialect/mod.rs b/app/src/dialect/mod.rs
index 8cce6a3..f635a83 100644
--- a/tmp/agent-patch-flux-pr-1576.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/src/dialect/mod.rs
+++ b/app/src/dialect/mod.rs
@@ -138,6 +138,11 @@ pub trait Dialect: Debug + Any {
true
}
+ /// Determine if brackets should be interpreted as nested delimited identifiers (e.g. Redshift `["foo"]`).
+ fn is_nested_delimited_identifier_start(&self, _chars: Peekable<Chars<'_>>) -> bool {
+ false
+ }
+
/// Determine if a character is a valid start character for an unquoted identifier
fn is_identifier_start(&self, ch: char) -> bool;
diff --git a/tmp/agent-patch-flux-pr-1576.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/src/dialect/redshift.rs b/app/src/dialect/redshift.rs
index 48eb00a..65bdfcd 100644
--- a/tmp/agent-patch-flux-pr-1576.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/src/dialect/redshift.rs
+++ b/app/src/dialect/redshift.rs
@@ -33,18 +33,14 @@ pub struct RedshiftSqlDialect {}
// be a json path
impl Dialect for RedshiftSqlDialect {
fn is_delimited_identifier_start(&self, ch: char) -> bool {
- ch == '"' || ch == '['
+ ch == '"'
}
- /// Determine if quoted characters are proper for identifier
- /// It's needed to distinguish treating square brackets as quotes from
- /// treating them as json path. If there is identifier then we assume
- /// there is no json path.
- fn is_proper_identifier_inside_quotes(&self, mut chars: Peekable<Chars<'_>>) -> bool {
+ fn is_nested_delimited_identifier_start(&self, mut chars: Peekable<Chars<'_>>) -> bool {
chars.next();
let mut not_white_chars = chars.skip_while(|ch| ch.is_whitespace()).peekable();
if let Some(&ch) = not_white_chars.peek() {
- return self.is_identifier_start(ch);
+ return ch == '"' || self.is_identifier_start(ch);
}
false
}
diff --git a/tmp/agent-patch-flux-pr-1576.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/src/tokenizer.rs b/app/src/tokenizer.rs
index aacfc16..0a4268f 100644
--- a/tmp/agent-patch-flux-pr-1576.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/src/tokenizer.rs
+++ b/app/src/tokenizer.rs
@@ -1074,6 +1074,26 @@ impl<'a> Tokenizer<'a> {
Ok(Some(Token::DoubleQuotedString(s)))
}
+ // nested bracket-delimited identifiers (Redshift)
+ '['
+ if self
+ .dialect
+ .is_nested_delimited_identifier_start(chars.peekable.clone()) =>
+ {
+ let error_loc = chars.location();
+ chars.next(); // consume the opening '['
+ let (s, last_char) = self.parse_bracket_delimited_identifier(chars);
+ let quote_end = Word::matching_end_quote('[');
+
+ if last_char == Some(quote_end) {
+ Ok(Some(Token::make_word(&s, Some('['))))
+ } else {
+ self.tokenizer_error(
+ error_loc,
+ format!("Expected close delimiter '{quote_end}' before EOF."),
+ )
+ }
+ }
// delimited (quoted) identifier
quote_start
if self.dialect.is_delimited_identifier_start(ch)
@@ -1843,6 +1863,58 @@ impl<'a> Tokenizer<'a> {
(s, last_char)
}
+ fn parse_bracket_delimited_identifier(
+ &self,
+ chars: &mut State,
+ ) -> (String, Option<char>) {
+ let mut lookahead = chars.peekable.clone();
+ while let Some(&ch) = lookahead.peek() {
+ if ch.is_whitespace() {
+ lookahead.next();
+ } else {
+ break;
+ }
+ }
+ let nested = matches!(lookahead.peek(), Some('"'));
+
+ if nested {
+ while let Some(&ch) = chars.peek() {
+ if ch.is_whitespace() {
+ chars.next();
+ } else {
+ break;
+ }
+ }
+
+ match chars.next() {
+ Some('"') => {
+ let (value, last_char) = self.parse_quoted_ident(chars, '"');
+ if last_char != Some('"') {
+ return (value, None);
+ }
+
+ while let Some(&ch) = chars.peek() {
+ if ch.is_whitespace() {
+ chars.next();
+ } else {
+ break;
+ }
+ }
+
+ if chars.peek() == Some(&']') {
+ chars.next();
+ (value, Some(']'))
+ } else {
+ (value, None)
+ }
+ }
+ _ => (String::new(), None),
+ }
+ } else {
+ self.parse_quoted_ident(chars, ']')
+ }
+ }
+
#[allow(clippy::unnecessary_wraps)]
fn consume_and_return(
&self,
diff --git a/tmp/agent-patch-flux-pr-1576.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/tests/sqlparser_redshift.rs b/app/tests/sqlparser_redshift.rs
index 9492946..c6d41f2 100644
--- a/tmp/agent-patch-flux-pr-1576.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/tests/sqlparser_redshift.rs
+++ b/app/tests/sqlparser_redshift.rs
@@ -66,6 +66,24 @@ fn brackets_over_db_schema_table_name_with_whites_paces() {
}
}
+#[test]
+fn nested_bracket_double_quoted_column_identifiers() {
+ let select = redshift().verified_only_select(r#"SELECT ["foo"] FROM foo"#);
+ assert_eq!(
+ SelectItem::UnnamedExpr(Expr::Identifier(Ident::with_quote('[', "foo"))),
+ select.projection[0],
+ );
+}
+
+#[test]
+fn nested_bracket_double_quoted_numeric_identifiers() {
+ let select = redshift().verified_only_select(r#"SELECT ["0"] FROM foo"#);
+ assert_eq!(
+ SelectItem::UnnamedExpr(Expr::Identifier(Ident::with_quote('[', "0"))),
+ select.projection[0],
+ );
+}
+
#[test]
fn test_double_quotes_over_db_schema_table_name() {
let select =