Skip to content

Commit fe821cc

Browse files
committed
Add TypeScript SDK CommonJS support, and a few core bugfixes
1 parent e23b9b0 commit fe821cc

File tree

20 files changed

+1474
-186
lines changed

20 files changed

+1474
-186
lines changed

Cargo.lock

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ exclude = [
1212
]
1313

1414
[workspace.package]
15-
version = "0.1.9"
15+
version = "0.1.10"
1616
edition = "2021"
1717
license = "MIT"
1818
authors = ["polyglot contributors"]

crates/polyglot-sql-wasm/src/lib.rs

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ use polyglot_sql::{
1515
planner::{Plan, Step},
1616
validate_with_schema as core_validate_with_schema,
1717
FormatGuardOptions as CoreFormatGuardOptions,
18-
SchemaValidationOptions as CoreSchemaValidationOptions,
19-
Token, ValidationOptions as CoreValidationOptions,
20-
ValidationResult as CoreValidationResult, ValidationSchema as CoreValidationSchema,
18+
SchemaValidationOptions as CoreSchemaValidationOptions, Token,
19+
ValidationOptions as CoreValidationOptions, ValidationResult as CoreValidationResult,
20+
ValidationSchema as CoreValidationSchema,
2121
};
2222
use serde::{Deserialize, Serialize};
2323
use std::collections::HashMap;
@@ -138,8 +138,8 @@ fn transpile_internal(sql: &str, read_dialect: &str, write_dialect: &str) -> Tra
138138
error: Some(format!("Invalid read dialect: {}", e)),
139139
error_line: e.line(),
140140
error_column: e.column(),
141-
error_start: e.start(),
142-
error_end: e.end(),
141+
error_start: e.start(),
142+
error_end: e.end(),
143143
};
144144
}
145145
};
@@ -153,8 +153,8 @@ fn transpile_internal(sql: &str, read_dialect: &str, write_dialect: &str) -> Tra
153153
error: Some(format!("Invalid write dialect: {}", e)),
154154
error_line: e.line(),
155155
error_column: e.column(),
156-
error_start: e.start(),
157-
error_end: e.end(),
156+
error_start: e.start(),
157+
error_end: e.end(),
158158
};
159159
}
160160
};
@@ -176,8 +176,8 @@ fn transpile_internal(sql: &str, read_dialect: &str, write_dialect: &str) -> Tra
176176
error: Some(e.to_string()),
177177
error_line: e.line(),
178178
error_column: e.column(),
179-
error_start: e.start(),
180-
error_end: e.end(),
179+
error_start: e.start(),
180+
error_end: e.end(),
181181
},
182182
}
183183
}
@@ -252,8 +252,8 @@ fn parse_value_internal(sql: &str, dialect: &str) -> ParseValueResult {
252252
error: Some(format!("Invalid dialect: {}", e)),
253253
error_line: e.line(),
254254
error_column: e.column(),
255-
error_start: e.start(),
256-
error_end: e.end(),
255+
error_start: e.start(),
256+
error_end: e.end(),
257257
};
258258
}
259259
};
@@ -275,8 +275,8 @@ fn parse_value_internal(sql: &str, dialect: &str) -> ParseValueResult {
275275
error: Some(e.to_string()),
276276
error_line: e.line(),
277277
error_column: e.column(),
278-
error_start: e.start(),
279-
error_end: e.end(),
278+
error_start: e.start(),
279+
error_end: e.end(),
280280
},
281281
}
282282
}
@@ -311,8 +311,8 @@ pub fn generate_value(ast: JsValue, dialect: &str) -> JsValue {
311311
error: Some(format!("Invalid dialect: {}", e)),
312312
error_line: e.line(),
313313
error_column: e.column(),
314-
error_start: e.start(),
315-
error_end: e.end(),
314+
error_start: e.start(),
315+
error_end: e.end(),
316316
});
317317
}
318318
};
@@ -346,8 +346,8 @@ fn generate_internal(ast_json: &str, dialect: &str) -> TranspileResult {
346346
error: Some(format!("Invalid dialect: {}", e)),
347347
error_line: e.line(),
348348
error_column: e.column(),
349-
error_start: e.start(),
350-
error_end: e.end(),
349+
error_start: e.start(),
350+
error_end: e.end(),
351351
};
352352
}
353353
};
@@ -393,8 +393,8 @@ fn generate_from_expressions_with_dialect(
393393
error: Some(e.to_string()),
394394
error_line: e.line(),
395395
error_column: e.column(),
396-
error_start: e.start(),
397-
error_end: e.end(),
396+
error_start: e.start(),
397+
error_end: e.end(),
398398
},
399399
}
400400
}
@@ -572,8 +572,8 @@ fn format_sql_internal(
572572
error: Some(e.to_string()),
573573
error_line: e.line(),
574574
error_column: e.column(),
575-
error_start: e.start(),
576-
error_end: e.end(),
575+
error_start: e.start(),
576+
error_end: e.end(),
577577
};
578578
}
579579
};
@@ -599,8 +599,8 @@ fn format_sql_internal(
599599
error: Some(e.to_string()),
600600
error_line: e.line(),
601601
error_column: e.column(),
602-
error_start: e.start(),
603-
error_end: e.end(),
602+
error_start: e.start(),
603+
error_end: e.end(),
604604
},
605605
}
606606
}

crates/polyglot-sql/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ serde_json = { workspace = true }
7777
thiserror = { workspace = true }
7878
unicode-segmentation = { workspace = true }
7979
ts-rs = { version = "12.0", features = ["serde-compat"], optional = true }
80-
polyglot-sql-function-catalogs = { path = "../polyglot-sql-function-catalogs", version = "0.1.9", optional = true, default-features = false }
80+
polyglot-sql-function-catalogs = { path = "../polyglot-sql-function-catalogs", version = "0.1.10", optional = true, default-features = false }
8181

8282
[dev-dependencies]
8383
pretty_assertions = "1.4"

crates/polyglot-sql/benches/rust_parsing.rs

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -157,15 +157,33 @@ fn bench_rust_parse_quick_equivalent(c: &mut Criterion) {
157157
group.warm_up_time(Duration::from_millis(500));
158158
group.measurement_time(Duration::from_secs(3));
159159

160-
group.bench_with_input(BenchmarkId::new("parse_one", "short"), &SQLGLOT_SHORT, |b, sql| {
161-
b.iter(|| parse_one(black_box(sql), DialectType::Generic).expect("parse should succeed"))
162-
});
163-
group.bench_with_input(BenchmarkId::new("parse_one", "long"), &SQLGLOT_LONG, |b, sql| {
164-
b.iter(|| parse_one(black_box(sql), DialectType::Generic).expect("parse should succeed"))
165-
});
166-
group.bench_with_input(BenchmarkId::new("parse_one", "tpch"), &SQLGLOT_TPCH, |b, sql| {
167-
b.iter(|| parse_one(black_box(sql), DialectType::Generic).expect("parse should succeed"))
168-
});
160+
group.bench_with_input(
161+
BenchmarkId::new("parse_one", "short"),
162+
&SQLGLOT_SHORT,
163+
|b, sql| {
164+
b.iter(|| {
165+
parse_one(black_box(sql), DialectType::Generic).expect("parse should succeed")
166+
})
167+
},
168+
);
169+
group.bench_with_input(
170+
BenchmarkId::new("parse_one", "long"),
171+
&SQLGLOT_LONG,
172+
|b, sql| {
173+
b.iter(|| {
174+
parse_one(black_box(sql), DialectType::Generic).expect("parse should succeed")
175+
})
176+
},
177+
);
178+
group.bench_with_input(
179+
BenchmarkId::new("parse_one", "tpch"),
180+
&SQLGLOT_TPCH,
181+
|b, sql| {
182+
b.iter(|| {
183+
parse_one(black_box(sql), DialectType::Generic).expect("parse should succeed")
184+
})
185+
},
186+
);
169187

170188
let crazy = build_crazy_query();
171189
group.bench_with_input(BenchmarkId::new("parse_one", "crazy"), &crazy, |b, sql| {

crates/polyglot-sql/src/dialects/mod.rs

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1779,9 +1779,9 @@ impl CustomDialectBuilder {
17791779
use std::str::FromStr;
17801780

17811781
fn register_custom_dialect(config: CustomDialectConfig) -> Result<()> {
1782-
let mut registry = CUSTOM_DIALECT_REGISTRY
1783-
.write()
1784-
.map_err(|e| crate::error::Error::parse(format!("Registry lock poisoned: {}", e), 0, 0, 0, 0))?;
1782+
let mut registry = CUSTOM_DIALECT_REGISTRY.write().map_err(|e| {
1783+
crate::error::Error::parse(format!("Registry lock poisoned: {}", e), 0, 0, 0, 0)
1784+
})?;
17851785

17861786
if registry.contains_key(&config.name) {
17871787
return Err(crate::error::Error::parse(
@@ -31472,12 +31472,19 @@ mod tests {
3147231472

3147331473
#[test]
3147431474
fn test_generate_date_array_snowflake() {
31475-
let dialect = Dialect::get(DialectType::Generic);
31476-
let result = dialect.transpile_to(
31477-
"SELECT * FROM UNNEST(GENERATE_DATE_ARRAY(DATE '2020-01-01', DATE '2020-02-01', INTERVAL 1 WEEK))",
31478-
DialectType::Snowflake,
31479-
).unwrap();
31480-
eprintln!("GDA -> Snowflake: {}", result[0]);
31475+
std::thread::Builder::new()
31476+
.stack_size(16 * 1024 * 1024)
31477+
.spawn(|| {
31478+
let dialect = Dialect::get(DialectType::Generic);
31479+
let result = dialect.transpile_to(
31480+
"SELECT * FROM UNNEST(GENERATE_DATE_ARRAY(DATE '2020-01-01', DATE '2020-02-01', INTERVAL 1 WEEK))",
31481+
DialectType::Snowflake,
31482+
).unwrap();
31483+
eprintln!("GDA -> Snowflake: {}", result[0]);
31484+
})
31485+
.unwrap()
31486+
.join()
31487+
.unwrap();
3148131488
}
3148231489

3148331490
#[test]

crates/polyglot-sql/src/error.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,9 @@ impl Error {
120120
/// Get the end byte offset if available
121121
pub fn end(&self) -> Option<usize> {
122122
match self {
123-
Error::Tokenize { end, .. }
124-
| Error::Parse { end, .. }
125-
| Error::Syntax { end, .. } => Some(*end),
123+
Error::Tokenize { end, .. } | Error::Parse { end, .. } | Error::Syntax { end, .. } => {
124+
Some(*end)
125+
}
126126
_ => None,
127127
}
128128
}

crates/polyglot-sql/src/parser.rs

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11509,12 +11509,25 @@ impl Parser {
1150911509
}
1151011510
}
1151111511

11512+
// BigQuery (and others): CREATE TABLE t PARTITION BY ... CLUSTER BY ... OPTIONS(...) AS (SELECT ...)
11513+
// When there are no column definitions, skip straight to property/AS parsing
11514+
let no_column_defs = !self.check(TokenType::LParen)
11515+
&& (self.check(TokenType::Partition)
11516+
|| self.check(TokenType::PartitionBy)
11517+
|| self.check(TokenType::Cluster)
11518+
|| self.check_identifier("OPTIONS")
11519+
|| self.check(TokenType::As));
11520+
1151211521
// Parse column definitions
11513-
self.expect(TokenType::LParen)?;
11522+
if !no_column_defs {
11523+
self.expect(TokenType::LParen)?;
11524+
}
1151411525

1151511526
// For DYNAMIC TABLE, column list contains only names without types
1151611527
// e.g., CREATE DYNAMIC TABLE t (col1, col2, col3) TARGET_LAG=... AS SELECT ...
11517-
let (columns, constraints) = if table_modifier == Some("DYNAMIC") {
11528+
let (columns, constraints) = if no_column_defs {
11529+
(Vec::new(), Vec::new())
11530+
} else if table_modifier == Some("DYNAMIC") {
1151811531
// Check if this looks like a simple column name list (just identifiers separated by commas)
1151911532
// by peeking ahead - if next token after identifier is comma or rparen, it's a name-only list
1152011533
let saved = self.current;
@@ -11555,7 +11568,9 @@ impl Parser {
1155511568
self.parse_column_definitions()?
1155611569
};
1155711570

11558-
self.expect(TokenType::RParen)?;
11571+
if !no_column_defs {
11572+
self.expect(TokenType::RParen)?;
11573+
}
1155911574

1156011575
// Parse COMMENT before WITH properties (Presto: CREATE TABLE x (...) COMMENT 'text' WITH (...))
1156111576
let pre_with_comment = if self.check(TokenType::Comment) {
@@ -12097,7 +12112,8 @@ impl Parser {
1209712112
}
1209812113

1209912114
// Handle AS SELECT after columns/WITH (CTAS with column definitions)
12100-
let as_select = if self.match_token(TokenType::As) {
12115+
// When there are no column definitions, AS comes after PARTITION BY/CLUSTER BY/OPTIONS
12116+
let as_select = if !no_column_defs && self.match_token(TokenType::As) {
1210112117
Some(self.parse_statement()?)
1210212118
} else {
1210312119
None
@@ -12315,6 +12331,22 @@ impl Parser {
1231512331
}));
1231612332
}
1231712333

12334+
// No-column-defs path: OPTIONS and AS SELECT come after PARTITION BY / CLUSTER BY
12335+
if no_column_defs {
12336+
if self.match_identifier("OPTIONS") {
12337+
let options = self.parse_options_list()?;
12338+
table_properties.push(Expression::Properties(Box::new(Properties {
12339+
expressions: options,
12340+
})));
12341+
}
12342+
}
12343+
12344+
let as_select = if no_column_defs && self.match_token(TokenType::As) {
12345+
Some(self.parse_statement()?)
12346+
} else {
12347+
as_select
12348+
};
12349+
1231812350
// For EXTERNAL tables, parse additional Snowflake options that may come after PARTITION BY
1231912351
// (location=@s2/logs/, partition_type = user_specified, file_format = (...), etc.)
1232012352
if is_special_modifier {

0 commit comments

Comments
 (0)