Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
24 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
f5e6209
Merge branch 'main' into no-whitespace-parser
LucaCappelletti94 Nov 29, 2025
7e0d3a9
Merge branch 'main' into no-whitespace-parser
LucaCappelletti94 Dec 4, 2025
6a85def
Merge branch 'main' into no-whitespace-parser
LucaCappelletti94 Dec 4, 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
Prev Previous commit
Next Next commit
Added handling of CSVs in COPY STDIN
  • Loading branch information
LucaCappelletti94 committed Oct 28, 2025
commit b862dc7eab00b45913cf1af742a7d3b53ab95998
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ visitor = ["sqlparser_derive"]
[dependencies]
bigdecimal = { version = "0.4.1", features = ["serde"], optional = true }
log = "0.4"
csv = "1.4.0"
recursive = { version = "0.1.1", optional = true}

serde = { version = "1.0", default-features = false, features = ["derive", "alloc"], optional = true }
Expand Down
78 changes: 68 additions & 10 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3227,7 +3227,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,18 +4579,76 @@ impl fmt::Display for Statement {
if !legacy_options.is_empty() {
write!(f, " {}", display_separated(legacy_options, " "))?;
}

let mut null_symbol = "\\N";
let mut writer_builder = csv::WriterBuilder::new();

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

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

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")?;
}
let mut writer = writer_builder.from_writer(vec![]);
for row in values {
writer
.write_record(
row.iter()
.map(|column| column.as_deref().unwrap_or(null_symbol)),
)
.map_err(|_| fmt::Error)?
}
writer.flush().map_err(|_| fmt::Error)?;
let data = String::from_utf8(writer.into_inner().map_err(|_| fmt::Error)?)
.map_err(|_| fmt::Error)?;
write!(f, "{}", data)?;
write!(f, "\n\\.")?;
}
Ok(())
Expand Down
2 changes: 1 addition & 1 deletion src/dialect/bigquery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ impl Dialect for BigQueryDialect {
}

fn is_identifier_part(&self, ch: char) -> bool {
ch.is_ascii_lowercase() || ch.is_ascii_uppercase() || ch.is_ascii_digit() || ch == '_'
ch.is_ascii_lowercase() || ch.is_ascii_uppercase() || ch.is_ascii_digit() || ch == '_' || ch == '-'
}

/// See [doc](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals)
Expand Down
6 changes: 3 additions & 3 deletions src/dialect/snowflake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1049,9 +1049,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::SemiColon => break,
loop {
match &parser.next_token().token {
Token::SemiColon | Token::EOF => break,
Token::Period => {
parser.prev_token();
break;
Expand Down
Loading