Skip to content
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
52338d6
Started to remove whitespace
LucaCappelletti94 Oct 28, 2025
c75f11b
Extended placeholder syntax test and moved check in tokenizer
LucaCappelletti94 Oct 28, 2025
1b8d716
Made `test_table_ident_err` more verbose
LucaCappelletti94 Oct 28, 2025
b862dc7
Added handling of CSVs in COPY STDIN
LucaCappelletti94 Oct 28, 2025
93ea5d2
Extended CSV STDIN tests and resolved more corner cases in tokenizer
LucaCappelletti94 Oct 29, 2025
819c095
Tentatively added support for path identifiers
LucaCappelletti94 Oct 29, 2025
7ea9746
Tentatively fixed snowflake ident in @-prefixed paths
LucaCappelletti94 Oct 29, 2025
c6c391c
Fixed broken doc test
LucaCappelletti94 Oct 29, 2025
5120d8c
Fixed code smells
LucaCappelletti94 Oct 29, 2025
07a828f
Replaced CSV with custom csv parser
LucaCappelletti94 Oct 29, 2025
586ec01
Update src/parser/mod.rs
LucaCappelletti94 Nov 14, 2025
110271b
Update tests/sqlparser_common.rs
LucaCappelletti94 Nov 14, 2025
25b5e3e
Refactored CSV parsing
LucaCappelletti94 Nov 14, 2025
2957be4
Improved dialect method documentation
LucaCappelletti94 Nov 14, 2025
9cd94c5
Merge branch 'main' into no-whitespace-parser
LucaCappelletti94 Nov 14, 2025
f024efc
Fixed the std corner case
LucaCappelletti94 Nov 14, 2025
48c476e
Merge branch 'main' into no-whitespace-parser
LucaCappelletti94 Nov 18, 2025
38a7c6b
Removed CSV validation
LucaCappelletti94 Nov 18, 2025
03a884d
Merge branch 'main' into no-whitespace-parser
LucaCappelletti94 Nov 19, 2025
2f8d5f6
Merge branch 'main' into no-whitespace-parser
LucaCappelletti94 Nov 26, 2025
df0b9f2
Merge branch 'main' into no-whitespace-parser
LucaCappelletti94 Nov 26, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/ast/ddl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
//! (commonly referred to as Data Definition Language, or DDL)

#[cfg(not(feature = "std"))]
use alloc::{boxed::Box, format, string::String, vec, vec::Vec};
use alloc::{boxed::Box, format, string::String, vec::Vec};
use core::fmt::{self, Display, Write};

#[cfg(feature = "serde")]
Expand Down
107 changes: 90 additions & 17 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2787,10 +2787,11 @@ impl fmt::Display for Declare {
}

/// Sql options of a `CREATE TABLE` statement.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum CreateTableOptions {
#[default]
None,
/// Options specified using the `WITH` keyword.
/// e.g. `WITH (description = "123")`
Expand Down Expand Up @@ -2819,12 +2820,6 @@ pub enum CreateTableOptions {
TableProperties(Vec<SqlOption>),
}

impl Default for CreateTableOptions {
fn default() -> Self {
Self::None
}
}

impl fmt::Display for CreateTableOptions {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Expand Down Expand Up @@ -3227,7 +3222,7 @@ pub enum Statement {
/// WITH options (before PostgreSQL version 9.0)
legacy_options: Vec<CopyLegacyOption>,
/// VALUES a vector of values to be copied
values: Vec<Option<String>>,
values: Vec<Vec<Option<String>>>,
},
/// ```sql
/// COPY INTO <table> | <location>
Expand Down Expand Up @@ -4579,19 +4574,97 @@ impl fmt::Display for Statement {
if !legacy_options.is_empty() {
write!(f, " {}", display_separated(legacy_options, " "))?;
}

let mut null_symbol = "\\N";
let mut delimiter = '\t';
let mut quote = '"';
let mut escape = '\\';

// Apply options
for option in options {
match option {
CopyOption::Delimiter(c) => {
delimiter = *c;
}
CopyOption::Quote(c) => {
quote = *c;
}
CopyOption::Escape(c) => {
escape = *c;
}
CopyOption::Null(null) => {
null_symbol = null;
}
_ => {}
}
}

// Apply legacy options
for option in legacy_options {
match option {
CopyLegacyOption::Delimiter(c) => {
delimiter = *c;
}
CopyLegacyOption::Null(null) => {
null_symbol = null;
}
CopyLegacyOption::Csv(csv_options) => {
for csv_option in csv_options {
match csv_option {
CopyLegacyCsvOption::Quote(c) => {
quote = *c;
}
CopyLegacyCsvOption::Escape(c) => {
escape = *c;
}
_ => {}
}
}
}
_ => {}
}
}

if !values.is_empty() {
writeln!(f, ";")?;
let mut delim = "";
for v in values {
write!(f, "{delim}")?;
delim = "\t";
if let Some(v) = v {
write!(f, "{v}")?;
} else {
write!(f, "\\N")?;

// Simple CSV writer
for row in values {
for (idx, column) in row.iter().enumerate() {
if idx > 0 {
write!(f, "{}", delimiter)?;
}

let field_value = column.as_deref().unwrap_or(null_symbol);

// Check if field needs quoting
let needs_quoting = field_value.contains(delimiter)
|| field_value.contains(quote)
|| field_value.contains('\n')
|| field_value.contains('\r');

if needs_quoting {
write!(f, "{}", quote)?;
for ch in field_value.chars() {
if ch == quote {
// Escape quote by doubling it
write!(f, "{}{}", quote, quote)?;
} else if ch == escape {
// Escape escape character
write!(f, "{}{}", escape, escape)?;
} else {
write!(f, "{}", ch)?;
}
}
write!(f, "{}", quote)?;
} else {
write!(f, "{}", field_value)?;
}
}
writeln!(f)?;
}
write!(f, "\n\\.")?;

write!(f, "\\.")?;
}
Ok(())
}
Expand Down
4 changes: 4 additions & 0 deletions src/dialect/bigquery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ impl Dialect for BigQueryDialect {
ch.is_ascii_lowercase() || ch.is_ascii_uppercase() || ch.is_ascii_digit() || ch == '_'
}

fn supports_hyphenated_identifiers(&self) -> bool {
true
}

/// See [doc](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals)
fn supports_triple_quoted_string(&self) -> bool {
true
Expand Down
10 changes: 10 additions & 0 deletions src/dialect/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,16 @@ pub trait Dialect: Debug + Any {
/// Determine if a character is a valid unquoted identifier character
fn is_identifier_part(&self, ch: char) -> bool;

/// Returns whether the dialect supports hyphenated identifiers
fn supports_hyphenated_identifiers(&self) -> bool {
false
}

/// Returns whether the dialect supports path-like identifiers
fn supports_path_like_identifiers(&self) -> bool {
false
}

/// Most dialects do not have custom operators. Override this method to provide custom operators.
fn is_custom_operator_part(&self, _ch: char) -> bool {
false
Expand Down
19 changes: 15 additions & 4 deletions src/dialect/snowflake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,10 @@ impl Dialect for SnowflakeDialect {
|| ch == '_'
}

fn supports_path_like_identifiers(&self) -> bool {
true
}

// See https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#escape_sequences
fn supports_string_literal_backslash_escape(&self) -> bool {
true
Expand Down Expand Up @@ -1049,9 +1053,9 @@ pub fn parse_create_stage(

pub fn parse_stage_name_identifier(parser: &mut Parser) -> Result<Ident, ParserError> {
let mut ident = String::new();
while let Some(next_token) = parser.next_token_no_skip() {
match &next_token.token {
Token::Whitespace(_) | Token::SemiColon => break,
loop {
match &parser.next_token().token {
Token::SemiColon | Token::EOF => break,
Token::Period => {
parser.prev_token();
break;
Expand All @@ -1067,7 +1071,14 @@ pub fn parse_stage_name_identifier(parser: &mut Parser) -> Result<Ident, ParserE
Token::Plus => ident.push('+'),
Token::Minus => ident.push('-'),
Token::Number(n, _) => ident.push_str(n),
Token::Word(w) => ident.push_str(&w.to_string()),
Token::Word(w) => {
if matches!(w.keyword, Keyword::NoKeyword) || ident.ends_with("@") {
ident.push_str(w.to_string().as_str());
} else {
parser.prev_token();
break;
}
}
_ => return parser.expected("stage name identifier", parser.peek_token()),
}
}
Expand Down
Loading