@@ -5,14 +5,11 @@ Provides commands for executing ggsql queries with various data sources and outp
55*/
66
77use clap:: { Parser , Subcommand } ;
8+ use ggsql:: reader:: { Reader , Spec } ;
9+ use ggsql:: validate:: validate;
810use ggsql:: { parser, VERSION } ;
911use std:: path:: PathBuf ;
1012
11- #[ cfg( feature = "duckdb" ) ]
12- use ggsql:: reader:: { DuckDBReader , Reader } ;
13- #[ cfg( feature = "duckdb" ) ]
14- use ggsql:: validate:: validate;
15-
1613#[ cfg( feature = "vegalite" ) ]
1714use ggsql:: writer:: { VegaLiteWriter , Writer } ;
1815
@@ -32,11 +29,11 @@ pub enum Commands {
3229 /// The ggsql query to execute
3330 query : String ,
3431
35- /// Data source connection string
32+ /// Data source connection string (duckdb://, sqlite://, polars://)
3633 #[ arg( long, default_value = "duckdb://memory" ) ]
3734 reader : String ,
3835
39- /// Output format
36+ /// Output format (vegalite)
4037 #[ arg( long, default_value = "vegalite" ) ]
4138 writer : String ,
4239
@@ -54,11 +51,11 @@ pub enum Commands {
5451 /// Path to .sql file containing ggsql query
5552 file : PathBuf ,
5653
57- /// Data source connection string
54+ /// Data source connection string (duckdb://, sqlite://, polars://)
5855 #[ arg( long, default_value = "duckdb://memory" ) ]
5956 reader : String ,
6057
61- /// Output format
58+ /// Output format (vegalite)
6259 #[ arg( long, default_value = "vegalite" ) ]
6360 writer : String ,
6461
@@ -86,7 +83,7 @@ pub enum Commands {
8683 /// The ggsql query to validate
8784 query : String ,
8885
89- /// Data source connection string (needed for column validation)
86+ /// Data source connection string for column validation (duckdb://, sqlite://, polars:// )
9087 #[ arg( long) ]
9188 reader : Option < String > ,
9289 } ,
@@ -153,23 +150,75 @@ fn cmd_exec(query: String, reader: String, writer: String, output: Option<PathBu
153150 }
154151 }
155152
156- // Setup reader
157- #[ cfg( feature = "duckdb" ) ]
158- if !reader. starts_with ( "duckdb://" ) {
159- eprintln ! ( "Unsupported reader: {}" , reader) ;
160- eprintln ! ( "Currently only 'duckdb://' readers are supported" ) ;
153+ if reader. starts_with ( "duckdb://" ) {
154+ #[ cfg( feature = "duckdb" ) ]
155+ {
156+ let r = match ggsql:: reader:: DuckDBReader :: from_connection_string ( & reader) {
157+ Ok ( r) => r,
158+ Err ( e) => {
159+ eprintln ! ( "Failed to create reader: {}" , e) ;
160+ std:: process:: exit ( 1 ) ;
161+ }
162+ } ;
163+ exec_with_reader ( & query, & r, & writer, output, verbose) ;
164+ }
165+ #[ cfg( not( feature = "duckdb" ) ) ]
166+ {
167+ eprintln ! ( "DuckDB reader not compiled in. Rebuild with --features duckdb" ) ;
168+ std:: process:: exit ( 1 ) ;
169+ }
170+ } else if reader. starts_with ( "polars://" ) {
171+ #[ cfg( feature = "polars-sql" ) ]
172+ {
173+ let r = match ggsql:: reader:: PolarsReader :: from_connection_string ( & reader) {
174+ Ok ( r) => r,
175+ Err ( e) => {
176+ eprintln ! ( "Failed to create reader: {}" , e) ;
177+ std:: process:: exit ( 1 ) ;
178+ }
179+ } ;
180+ exec_with_reader ( & query, & r, & writer, output, verbose) ;
181+ }
182+ #[ cfg( not( feature = "polars-sql" ) ) ]
183+ {
184+ eprintln ! ( "Polars reader not compiled in. Rebuild with --features polars-sql" ) ;
185+ std:: process:: exit ( 1 ) ;
186+ }
187+ } else if reader. starts_with ( "sqlite://" ) {
188+ #[ cfg( feature = "sqlite" ) ]
189+ {
190+ let r = match ggsql:: reader:: SqliteReader :: from_connection_string ( & reader) {
191+ Ok ( r) => r,
192+ Err ( e) => {
193+ eprintln ! ( "Failed to create reader: {}" , e) ;
194+ std:: process:: exit ( 1 ) ;
195+ }
196+ } ;
197+ exec_with_reader ( & query, & r, & writer, output, verbose) ;
198+ }
199+ #[ cfg( not( feature = "sqlite" ) ) ]
200+ {
201+ eprintln ! ( "SQLite reader not compiled in. Rebuild with --features sqlite" ) ;
202+ std:: process:: exit ( 1 ) ;
203+ }
204+ } else if reader. starts_with ( "postgres://" ) || reader. starts_with ( "postgresql://" ) {
205+ eprintln ! ( "PostgreSQL reader is not yet implemented" ) ;
161206 std:: process:: exit ( 1 ) ;
162- }
163-
164- let db_reader = DuckDBReader :: from_connection_string ( & reader) ;
165- if let Err ( e) = db_reader {
166- eprintln ! ( "Failed to create DuckDB reader: {}" , e) ;
207+ } else {
208+ eprintln ! ( "Unsupported connection string: {}" , reader) ;
167209 std:: process:: exit ( 1 ) ;
168210 }
169- let db_reader = db_reader . unwrap ( ) ;
211+ }
170212
213+ fn exec_with_reader < R : Reader > (
214+ query : & str ,
215+ reader : & R ,
216+ writer : & str ,
217+ output : Option < PathBuf > ,
218+ verbose : bool ,
219+ ) {
171220 // Use validate() to check if query has visualization
172- let validated = match validate ( & query) {
221+ let validated = match validate ( query) {
173222 Ok ( v) => v,
174223 Err ( e) => {
175224 eprintln ! ( "Failed to validate query: {}" , e) ;
@@ -181,19 +230,23 @@ fn cmd_exec(query: String, reader: String, writer: String, output: Option<PathBu
181230 if verbose {
182231 eprintln ! ( "Visualisation is empty. Printing table instead." ) ;
183232 }
184- print_table_fallback ( & query, & db_reader , 100 ) ;
233+ print_table_fallback ( query, reader , 100 ) ;
185234 return ;
186235 }
187236
188237 // Execute ggsql query
189- let spec = match db_reader . execute ( & query) {
238+ let spec = match reader . execute ( query) {
190239 Ok ( s) => s,
191240 Err ( e) => {
192241 eprintln ! ( "Failed to execute query: {}" , e) ;
193242 std:: process:: exit ( 1 ) ;
194243 }
195244 } ;
196245
246+ render_spec ( spec, writer, output, verbose) ;
247+ }
248+
249+ fn render_spec ( spec : Spec , writer : & str , output : Option < PathBuf > , verbose : bool ) {
197250 if verbose {
198251 let metadata = spec. metadata ( ) ;
199252 eprintln ! ( "\n Query executed:" ) ;
@@ -292,41 +345,32 @@ fn cmd_parse(query: String, format: String) {
292345}
293346
294347fn cmd_validate ( query : String , _reader : Option < String > ) {
295- #[ cfg( feature = "duckdb" ) ]
296- {
297- match validate ( & query) {
298- Ok ( validated) if validated. valid ( ) => {
299- println ! ( "✓ Query syntax is valid" ) ;
348+ match validate ( & query) {
349+ Ok ( validated) if validated. valid ( ) => {
350+ println ! ( "✓ Query syntax is valid" ) ;
351+ }
352+ Ok ( validated) => {
353+ println ! ( "✗ Validation errors:" ) ;
354+ for err in validated. errors ( ) {
355+ println ! ( " - {}" , err. message) ;
300356 }
301- Ok ( validated) => {
302- println ! ( "✗ Validation errors :" ) ;
303- for err in validated. errors ( ) {
304- println ! ( " - {}" , err . message) ;
357+ if ! validated. warnings ( ) . is_empty ( ) {
358+ println ! ( "\n Warnings :" ) ;
359+ for warning in validated. warnings ( ) {
360+ println ! ( " - {}" , warning . message) ;
305361 }
306- if !validated. warnings ( ) . is_empty ( ) {
307- println ! ( "\n Warnings:" ) ;
308- for warning in validated. warnings ( ) {
309- println ! ( " - {}" , warning. message) ;
310- }
311- }
312- std:: process:: exit ( 1 ) ;
313- }
314- Err ( e) => {
315- eprintln ! ( "Error during validation: {}" , e) ;
316- std:: process:: exit ( 1 ) ;
317362 }
363+ std:: process:: exit ( 1 ) ;
364+ }
365+ Err ( e) => {
366+ eprintln ! ( "Error during validation: {}" , e) ;
367+ std:: process:: exit ( 1 ) ;
318368 }
319- }
320-
321- #[ cfg( not( feature = "duckdb" ) ) ]
322- {
323- eprintln ! ( "Validation requires the duckdb feature" ) ;
324- std:: process:: exit ( 1 ) ;
325369 }
326370}
327371
328372// Prints a CSV-like output to stdout with aligned columns
329- fn print_table_fallback ( query : & str , reader : & DuckDBReader , max_rows : usize ) {
373+ fn print_table_fallback < R : Reader > ( query : & str , reader : & R , max_rows : usize ) {
330374 let source_tree = match parser:: SourceTree :: new ( query) {
331375 Ok ( st) => st,
332376 Err ( e) => {
0 commit comments