@@ -130,25 +130,17 @@ impl DependencyAnalyzer {
130130 for ( idx, sql) in statements. iter ( ) . enumerate ( ) {
131131 let number = idx + 1 ; // 1-based numbering for user display
132132
133- // Check if this creates a temp table
134- let creates_temp_table = Self :: is_temp_table_creation ( sql) ;
135-
136- // Extract the SELECT portion for parsing
137- let select_sql = if creates_temp_table {
138- // For CREATE TEMP TABLE, extract the SELECT part after "AS"
139- Self :: extract_select_from_create_temp_table ( sql) ?
140- } else {
141- sql. clone ( )
142- } ;
143-
144- // Parse the SELECT statement
145- let mut parser = Parser :: new ( & select_sql) ;
133+ // Parse the SQL statement directly - parser handles all syntaxes
134+ let mut parser = Parser :: new ( sql) ;
146135 let ast = parser
147136 . parse ( )
148137 . map_err ( |e| anyhow ! ( "Failed to parse statement {}: {}" , number, e) ) ?;
149138
150- // Extract table references
151- let references = Self :: extract_table_references ( & ast, sql) ?;
139+ // Check if this creates a temp table using AST
140+ let creates_temp_table = ast. into_table . is_some ( ) || Self :: is_create_temp_table ( sql) ;
141+
142+ // Extract table references from the AST
143+ let references = Self :: extract_table_references ( & ast) ?;
152144
153145 analyzed. push ( DependencyStatement {
154146 number,
@@ -162,14 +154,12 @@ impl DependencyAnalyzer {
162154 }
163155
164156 /// Extract table references from a parsed AST
165- fn extract_table_references ( ast : & SelectStatement , sql : & str ) -> Result < TableReferences > {
157+ fn extract_table_references ( ast : & SelectStatement ) -> Result < TableReferences > {
166158 let mut refs = TableReferences :: new ( ) ;
167159
168- // Check for CREATE TEMP TABLE (write operation)
169- if Self :: is_temp_table_creation ( sql) {
170- if let Some ( table_name) = Self :: extract_temp_table_name ( sql) {
171- refs. add_write ( table_name) ;
172- }
160+ // Check for SELECT INTO (write operation) - parser extracts this for us!
161+ if let Some ( ref into_table) = ast. into_table {
162+ refs. add_write ( into_table. name . clone ( ) ) ;
173163 }
174164
175165 // Extract FROM clause tables (read operations)
@@ -179,7 +169,7 @@ impl DependencyAnalyzer {
179169
180170 // Extract from subquery if present
181171 if let Some ( subquery) = & ast. from_subquery {
182- let subquery_refs = Self :: extract_table_references ( subquery, "" ) ?;
172+ let subquery_refs = Self :: extract_table_references ( subquery) ?;
183173 for table in subquery_refs. reads {
184174 refs. add_read ( table) ;
185175 }
@@ -199,7 +189,7 @@ impl DependencyAnalyzer {
199189 for cte in & ast. ctes {
200190 match & cte. cte_type {
201191 crate :: sql:: parser:: ast:: CTEType :: Standard ( stmt) => {
202- let cte_refs = Self :: extract_table_references ( stmt, "" ) ?;
192+ let cte_refs = Self :: extract_table_references ( stmt) ?;
203193 for table in cte_refs. reads {
204194 refs. add_read ( table) ;
205195 }
@@ -228,7 +218,7 @@ impl DependencyAnalyzer {
228218 refs. add_read ( name. clone ( ) ) ;
229219 }
230220 TableSource :: DerivedTable { query, .. } => {
231- let subquery_refs = Self :: extract_table_references ( query, "" ) ?;
221+ let subquery_refs = Self :: extract_table_references ( query) ?;
232222 for table in subquery_refs. reads {
233223 refs. add_read ( table) ;
234224 }
@@ -241,7 +231,7 @@ impl DependencyAnalyzer {
241231 fn extract_from_expression ( expr : & SqlExpression , refs : & mut TableReferences ) -> Result < ( ) > {
242232 match expr {
243233 SqlExpression :: ScalarSubquery { query } => {
244- let subquery_refs = Self :: extract_table_references ( query, "" ) ?;
234+ let subquery_refs = Self :: extract_table_references ( query) ?;
245235 for table in subquery_refs. reads {
246236 refs. add_read ( table) ;
247237 }
@@ -251,7 +241,7 @@ impl DependencyAnalyzer {
251241 subquery,
252242 } => {
253243 Self :: extract_from_expression ( inner_expr, refs) ?;
254- let subquery_refs = Self :: extract_table_references ( subquery, "" ) ?;
244+ let subquery_refs = Self :: extract_table_references ( subquery) ?;
255245 for table in subquery_refs. reads {
256246 refs. add_read ( table) ;
257247 }
@@ -261,7 +251,7 @@ impl DependencyAnalyzer {
261251 subquery,
262252 } => {
263253 Self :: extract_from_expression ( inner_expr, refs) ?;
264- let subquery_refs = Self :: extract_table_references ( subquery, "" ) ?;
254+ let subquery_refs = Self :: extract_table_references ( subquery) ?;
265255 for table in subquery_refs. reads {
266256 refs. add_read ( table) ;
267257 }
@@ -296,113 +286,10 @@ impl DependencyAnalyzer {
296286 Ok ( ( ) )
297287 }
298288
299- /// Check if SQL creates a temp table
300- fn is_temp_table_creation ( sql : & str ) -> bool {
301- let sql_lower = sql. to_lowercase ( ) ;
302- sql_lower. contains ( "create temp table" )
303- || sql_lower. contains ( "create temporary table" )
304- || sql_lower. contains ( "into #" ) // SELECT ... INTO #temp_table syntax (can have newline before)
305- }
306-
307- /// Extract the SELECT portion from a CREATE TEMP TABLE statement
308- /// Example: "CREATE TEMP TABLE foo AS SELECT * FROM bar" -> "SELECT * FROM bar"
309- /// Also handles: "SELECT * INTO #foo FROM bar" -> "SELECT * FROM bar"
310- fn extract_select_from_create_temp_table ( sql : & str ) -> Result < String > {
311- let sql_lower = sql. to_lowercase ( ) ;
312-
313- // Check if this is SELECT ... INTO #temp syntax
314- if sql_lower. contains ( "into #" ) {
315- // For SELECT INTO, we need to remove the "INTO #table_name" part
316- // Pattern: SELECT ... INTO #table_name FROM ...
317- // We want: SELECT ... FROM ...
318-
319- let into_pos = sql_lower
320- . find ( "into #" )
321- . ok_or_else ( || anyhow ! ( "Could not find INTO # in statement" ) ) ?;
322-
323- // Find the start of the line containing INTO (to handle newlines)
324- let before_into = & sql_lower[ ..into_pos] ;
325- let line_start = before_into. rfind ( '\n' ) . map ( |p| p + 1 ) . unwrap_or ( 0 ) ;
326-
327- // Skip any whitespace before INTO on the same line
328- let into_line_start = sql_lower[ line_start..into_pos]
329- . trim_start ( )
330- . is_empty ( )
331- . then_some ( line_start)
332- . unwrap_or ( into_pos) ;
333-
334- // Find the end of the table name (next whitespace after INTO #)
335- let after_into = & sql_lower[ into_pos + 6 ..] ; // skip "into #" (6 chars)
336- let table_end = after_into
337- . find ( char:: is_whitespace)
338- . ok_or_else ( || anyhow ! ( "Could not find end of table name in SELECT INTO" ) ) ?;
339-
340- let from_start_in_remaining = into_pos + 6 + table_end;
341-
342- // Reconstruct: SELECT ... (before INTO line) + (after table name)
343- let select_part = sql[ ..into_line_start] . trim ( ) ;
344- let from_part = sql[ from_start_in_remaining..] . trim ( ) ;
345-
346- Ok ( format ! ( "{} {}" , select_part, from_part) )
347- } else {
348- // Original CREATE TEMP TABLE AS SELECT syntax
349- // Find the position of "AS" keyword
350- let as_pos = sql_lower
351- . find ( " as " )
352- . ok_or_else ( || anyhow ! ( "CREATE TEMP TABLE statement must have AS keyword" ) ) ?;
353-
354- // Extract everything after "AS"
355- let select_start = as_pos + 4 ; // " as " is 4 characters
356- let select_sql = sql[ select_start..] . trim ( ) ;
357-
358- if select_sql. is_empty ( ) {
359- return Err ( anyhow ! (
360- "CREATE TEMP TABLE statement has no SELECT after AS"
361- ) ) ;
362- }
363-
364- Ok ( select_sql. to_string ( ) )
365- }
366- }
367-
368- /// Extract temp table name from CREATE TEMP TABLE statement or SELECT INTO
369- fn extract_temp_table_name ( sql : & str ) -> Option < String > {
289+ /// Check if SQL uses CREATE TEMP TABLE syntax (not SELECT INTO, which is handled by AST)
290+ fn is_create_temp_table ( sql : & str ) -> bool {
370291 let sql_lower = sql. to_lowercase ( ) ;
371-
372- // Check for SELECT ... INTO #table syntax first
373- if sql_lower. contains ( "into #" ) {
374- let into_pos = sql_lower. find ( "into #" ) ?;
375- let after_into = & sql[ into_pos + 6 ..] ; // skip "into #" (6 chars)
376-
377- // Extract table name (everything until next whitespace)
378- let table_name = after_into
379- . split_whitespace ( )
380- . next ( ) ?
381- . trim_end_matches ( |c : char | c == '(' || c == ';' ) ;
382-
383- return Some ( format ! ( "#{}" , table_name) ) ; // Include the # prefix
384- }
385-
386- // Find "create temp table" or "create temporary table"
387- let start_pattern = if sql_lower. contains ( "create temp table" ) {
388- "create temp table"
389- } else if sql_lower. contains ( "create temporary table" ) {
390- "create temporary table"
391- } else {
392- return None ;
393- } ;
394-
395- // Find the position after the pattern
396- let start_idx = sql_lower. find ( start_pattern) ? + start_pattern. len ( ) ;
397-
398- // Extract the table name (next word after the pattern)
399- let remaining = sql[ start_idx..] . trim ( ) ;
400- let table_name = remaining
401- . split_whitespace ( )
402- . next ( ) ?
403- . trim_end_matches ( |c : char | c == '(' || c == ';' ) ;
404-
405- Some ( table_name. to_string ( ) )
292+ sql_lower. contains ( "create temp table" ) || sql_lower. contains ( "create temporary table" )
406293 }
407294
408295 /// Compute execution plan for a target statement
@@ -502,59 +389,41 @@ impl DependencyAnalyzer {
502389mod tests {
503390 use super :: * ;
504391
505- #[ test]
506- fn test_extract_temp_table_name ( ) {
507- let sql1 = "CREATE TEMP TABLE foo AS SELECT * FROM bar" ;
508- assert_eq ! (
509- DependencyAnalyzer :: extract_temp_table_name( sql1) ,
510- Some ( "foo" . to_string( ) )
511- ) ;
512-
513- let sql2 = "CREATE TEMPORARY TABLE my_temp AS SELECT 1" ;
514- assert_eq ! (
515- DependencyAnalyzer :: extract_temp_table_name( sql2) ,
516- Some ( "my_temp" . to_string( ) )
517- ) ;
518-
519- let sql3 = "SELECT * FROM baz" ;
520- assert_eq ! ( DependencyAnalyzer :: extract_temp_table_name( sql3) , None ) ;
521- }
522-
523392 #[ test]
524393 fn test_simple_dependency ( ) {
525394 let statements = vec ! [
526- "CREATE TEMP TABLE raw_data AS SELECT * FROM sales" . to_string( ) ,
395+ "SELECT * FROM sales INTO #raw_data " . to_string( ) ,
527396 "SELECT COUNT(*) FROM customers" . to_string( ) ,
528- "SELECT * FROM raw_data WHERE amount > 100" . to_string( ) ,
397+ "SELECT * FROM # raw_data WHERE amount > 100" . to_string( ) ,
529398 ] ;
530399
531400 let analyzed = DependencyAnalyzer :: analyze_statements ( & statements) . unwrap ( ) ;
532401 assert_eq ! ( analyzed. len( ) , 3 ) ;
533402
534- // Statement 1: creates raw_data, reads sales
535- assert_eq ! ( analyzed[ 0 ] . references. writes, vec![ "raw_data" ] ) ;
403+ // Statement 1: creates # raw_data, reads sales
404+ assert_eq ! ( analyzed[ 0 ] . references. writes, vec![ "# raw_data" ] ) ;
536405 assert_eq ! ( analyzed[ 0 ] . references. reads, vec![ "sales" ] ) ;
537406
538407 // Statement 2: reads customers (independent)
539408 assert_eq ! ( analyzed[ 1 ] . references. reads, vec![ "customers" ] ) ;
540409 assert ! ( analyzed[ 1 ] . references. writes. is_empty( ) ) ;
541410
542- // Statement 3: reads raw_data
543- assert_eq ! ( analyzed[ 2 ] . references. reads, vec![ "raw_data" ] ) ;
411+ // Statement 3: reads # raw_data
412+ assert_eq ! ( analyzed[ 2 ] . references. reads, vec![ "# raw_data" ] ) ;
544413 }
545414
546415 #[ test]
547416 fn test_execution_plan ( ) {
548417 let statements = vec ! [
549- "CREATE TEMP TABLE raw_data AS SELECT * FROM sales" . to_string( ) ,
418+ "SELECT * FROM sales INTO #raw_data " . to_string( ) ,
550419 "SELECT COUNT(*) FROM customers" . to_string( ) ,
551- "SELECT * FROM raw_data WHERE amount > 100" . to_string( ) ,
420+ "SELECT * FROM # raw_data WHERE amount > 100" . to_string( ) ,
552421 ] ;
553422
554423 let analyzed = DependencyAnalyzer :: analyze_statements ( & statements) . unwrap ( ) ;
555424 let plan = DependencyAnalyzer :: compute_execution_plan ( & analyzed, 3 ) . unwrap ( ) ;
556425
557- // Should execute statement 1 (creates raw_data) and 3 (target)
426+ // Should execute statement 1 (creates # raw_data) and 3 (target)
558427 // Should skip statement 2 (independent)
559428 assert_eq ! ( plan. statements_to_execute, vec![ 1 , 3 ] ) ;
560429 assert_eq ! ( plan. statements_to_skip, vec![ 2 ] ) ;
@@ -564,11 +433,11 @@ mod tests {
564433 #[ test]
565434 fn test_transitive_dependencies ( ) {
566435 let statements = vec ! [
567- "CREATE TEMP TABLE t1 AS SELECT * FROM base" . to_string( ) ,
568- "CREATE TEMP TABLE t2 AS SELECT * FROM t1 " . to_string( ) ,
569- "CREATE TEMP TABLE t3 AS SELECT * FROM t2 " . to_string( ) ,
436+ "SELECT * FROM base INTO #t1 " . to_string( ) ,
437+ "SELECT * FROM #t1 INTO #t2 " . to_string( ) ,
438+ "SELECT * FROM #t2 INTO #t3 " . to_string( ) ,
570439 "SELECT * FROM unrelated" . to_string( ) ,
571- "SELECT * FROM t3" . to_string( ) ,
440+ "SELECT * FROM # t3" . to_string( ) ,
572441 ] ;
573442
574443 let analyzed = DependencyAnalyzer :: analyze_statements ( & statements) . unwrap ( ) ;
0 commit comments