@@ -19,7 +19,7 @@ using Crayons
1919 @distinct , @left_join , @right_join , @inner_join , @count , @slice_max , @union ,
2020 @slice_min , @slice_sample , @rename , @relocate , @union_all , @setdiff , @intersect ,
2121 @semi_join , @full_join , @transmute , @anti_join , @head , @unnest_wider , @unnest_longer ,
22- @separate , @unite , @drop_missing
22+ @separate , @unite , @drop_missing , @pivot_wider , @summary
2323
2424 export db_table, set_sql_mode, connect, from_query, update_con,
2525 clickhouse, duckdb, sqlite, mysql, mssql, postgres, athena, snowflake, gbq,
@@ -72,6 +72,7 @@ include("relocate.jl")
7272include (" union_intersect_setdiff.jl" )
7373include (" unnest.jl" )
7474include (" sep_unite.jl" )
75+ include (" pivots.jl" )
7576
7677
7778# Unified expr_to_sql function to use right mode
@@ -173,7 +174,11 @@ function db_table(db, table, athena_params::Any=nothing; iceberg::Bool=false, de
173174 # println(table_name2)
174175 alias == " " ? alias = " gsheet" : alias = alias
175176 metadata = get_table_metadata (db, table_name2, alias = alias)
176- elseif startswith (table_name, " read" )
177+ elseif any (endswith (table_name, ext) for ext in [" .sas7bdat" , " .xpt" , " .sav" , " .zsav" , " .por" , " .dta" ])
178+ DuckDB. query (db, " install read_stat from community; load read_stat" )
179+ table_name2 = " read_stat('$table_name ')"
180+ metadata = get_table_metadata (db, table_name2)
181+ elseif startswith (table_name, " read" )
177182 table_name2 = " $table_name "
178183 alias = alias == " " ? " data" : alias
179184 # println(table_name2)
@@ -291,24 +296,41 @@ function db_table(db, table::Vector{String}, athena_params::Any=nothing)
291296end
292297
293298function db_table (db, table:: DataFrame , alias:: String )
299+ if any (any (lowercase (string (name)) == word for word in sql_words) for name in names (table))
300+ found_words = [word for word in sql_words if any (lowercase (string (name)) == word for name in names (table))]
301+ @warn " Column names containing SQL keywords detected: $(join (found_words, " , " )) .
302+ These may cause issues as they are reserved SQL keywords.
303+ Consider renaming the columns before scanning to DuckDB."
304+ end
305+ # COV_EXCL_STOP
294306 DuckDB. register_data_frame (db, table, alias)
295307 metadata = get_table_metadata (db, alias)
296308 return SQLQuery (from = alias, metadata= metadata, db= db)
297309end
298310const dt = db_table # COV_EXCL_LINE
299- # COV_EXCL_STOP
311+
312+
313+ sql_words = [" group" , " select" , " from" , " where" , " having" , " order" , " by" , " join" , " union" , " case" , " when" , " then" , " else" , " end" , " limit" , " right" , " left" ] # COV_EXCL_LINE
300314
301315"""
302316$docstring_copy_to
303317"""
304- function copy_to (conn, df_or_path:: Union{DataFrame, AbstractString} , name:: String )
318+ function copy_to (conn, df_or_path:: Union{DataFrame, AbstractString} , name:: String ; overwrite :: Bool = false )
305319 # Check if the input is a DataFrame
320+ rep = overwrite ? " OR REPLACE" : " "
306321 if isa (df_or_path, DataFrame)
307322 if current_sql_mode[] == duckdb ()
308323 name_view = name * " view"
309324 DuckDB. register_data_frame (conn, df_or_path, name_view)
310- DBInterface. execute (conn, " CREATE OR REPLACE TABLE $name AS SELECT * FROM $name_view " )
325+ DBInterface. execute (conn, " CREATE $rep TABLE $name AS SELECT * FROM $name_view " )
311326 DBInterface. execute (conn, " DROP VIEW $name_view " )
327+ # Check for 'group' in column names and warn if found
328+ if any (any (lowercase (string (name)) == word for word in sql_words) for name in names (df_or_path))
329+ found_words = [word for word in sql_words if any (lowercase (string (name)) == word for name in names (df_or_path))]
330+ @warn " Column names containing SQL keywords detected: $(join (found_words, " , " )) .
331+ These may cause issues as they are reserved SQL keywords.
332+ Consider renaming the columns before copying to DuckDB."
333+ end
312334 end
313335 # COV_EXCL_START
314336 elseif isa (df_or_path, AbstractString)
@@ -323,24 +345,24 @@ function copy_to(conn, df_or_path::Union{DataFrame, AbstractString}, name::Strin
323345 end
324346 if occursin (r" \. csv$" , df_or_path)
325347 # Construct and execute a SQL command for loading a CSV file
326- sql_command = " CREATE TABLE $name AS SELECT * FROM '$df_or_path ';"
348+ sql_command = " CREATE $rep TABLE $name AS SELECT * FROM '$df_or_path ';"
327349 DuckDB. execute (conn, sql_command)
328350 elseif occursin (r" \. parquet$" , df_or_path)
329351 # Construct and execute a SQL command for loading a Parquet file
330- sql_command = " CREATE TABLE $name AS SELECT * FROM '$df_or_path ';"
352+ sql_command = " CREATE $rep TABLE $name AS SELECT * FROM '$df_or_path ';"
331353 DuckDB. execute (conn, sql_command)
332354 elseif occursin (r" \. arrow$" , df_or_path)
333355 # Construct and execute a SQL command for loading a CSV file
334356 arrow_table = Arrow. Table (df_or_path)
335357 DuckDB. register_table (conn, arrow_table, name)
336358 elseif occursin (r" \. json$" , df_or_path)
337359 # For Arrow files, read the file into a DataFrame and then insert
338- sql_command = " CREATE TABLE $name AS SELECT * FROM read_json('$df_or_path ');"
360+ sql_command = " CREATE $rep TABLE $name AS SELECT * FROM read_json('$df_or_path ');"
339361 DuckDB. execute (conn, " INSTALL json;" )
340362 DuckDB. execute (conn, " LOAD json;" )
341363 DuckDB. execute (conn, sql_command)
342364 elseif startswith (df_or_path, " read" )
343- DuckDB. execute (conn, " CREATE TABLE $name AS SELECT * FROM $df_or_path ;" )
365+ DuckDB. execute (conn, " CREATE $rep TABLE $name AS SELECT * FROM $df_or_path ;" )
344366 else
345367 error (" Unsupported file type for: $df_or_path " )
346368 end
0 commit comments