diff --git a/Manifest.toml b/Manifest.toml index bbc87ae..cb74cfb 100644 --- a/Manifest.toml +++ b/Manifest.toml @@ -1,8 +1,8 @@ # This file is machine-generated - editing it directly is not advised -julia_version = "1.12.1" +julia_version = "1.12.5" manifest_format = "2.0" -project_hash = "c7a2fd47754abe433f089d6232e90fcb82c87f85" +project_hash = "d50b7edf21ea6bb3104189be326e8a78a00fd426" [[deps.AliasTables]] deps = ["PtrArrays", "Random"] @@ -25,15 +25,15 @@ version = "1.2.2" [[deps.CSV]] deps = ["CodecZlib", "Dates", "FilePathsBase", "InlineStrings", "Mmap", "Parsers", "PooledArrays", "PrecompileTools", "SentinelArrays", "Tables", "Unicode", "WeakRefStrings", "WorkerUtilities"] -git-tree-sha1 = "deddd8725e5e1cc49ee205a1964256043720a6c3" +git-tree-sha1 = "8d8e0b0f350b8e1c91420b5e64e5de774c2f0f4d" uuid = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b" -version = "0.10.15" +version = "0.10.16" [[deps.CodeTracking]] deps = ["InteractiveUtils", "UUIDs"] -git-tree-sha1 = "9ce926a33a8608421a4d45c012884165b3fcd3ee" +git-tree-sha1 = "b7231a755812695b8046e8471ddc34c8268cbad5" uuid = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2" -version = "2.0.2" +version = "3.0.0" [[deps.CodecBase]] deps = ["TranscodingStreams"] @@ -101,9 +101,9 @@ version = "1.11.0" [[deps.Distributions]] deps = ["AliasTables", "FillArrays", "LinearAlgebra", "PDMats", "Printf", "QuadGK", "Random", "SpecialFunctions", "Statistics", "StatsAPI", "StatsBase", "StatsFuns"] -git-tree-sha1 = "3bc002af51045ca3b47d2e1787d6ce02e68b943a" +git-tree-sha1 = "fbcc7610f6d8348428f722ecbe0e6cfe22e672c6" uuid = "31c24e10-a181-5473-b8eb-7969acd0382f" -version = "0.25.122" +version = "0.25.123" [deps.Distributions.extensions] DistributionsChainRulesCoreExt = "ChainRulesCore" @@ -140,16 +140,22 @@ version = "1.11.0" [[deps.FillArrays]] deps = ["LinearAlgebra"] -git-tree-sha1 = "5bfcd42851cf2f1b303f51525a54dc5e98d408a3" +git-tree-sha1 = "2f979084d1e13948a3352cf64a25df6bd3b4dca3" uuid = "1a297f60-69ca-5386-bcde-b61e274b549b" -version = "1.15.0" -weakdeps = ["PDMats", "SparseArrays", "Statistics"] +version = "1.16.0" [deps.FillArrays.extensions] FillArraysPDMatsExt = "PDMats" FillArraysSparseArraysExt = "SparseArrays" + FillArraysStaticArraysExt = "StaticArrays" FillArraysStatisticsExt = "Statistics" + [deps.FillArrays.weakdeps] + PDMats = "90014a1f-27ba-587c-ab20-58faa44d9150" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" + Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" + [[deps.Future]] deps = ["Random"] uuid = "9fa8497b-333b-5362-9e8d-4d0656e87820" @@ -213,9 +219,9 @@ version = "1.7.1" [[deps.JSON]] deps = ["Dates", "Logging", "Parsers", "PrecompileTools", "StructUtils", "UUIDs", "Unicode"] -git-tree-sha1 = "5b6bb73f555bc753a6153deec3717b8904f5551c" +git-tree-sha1 = "b3ad4a0255688dcb895a52fafbaae3023b588a90" uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" -version = "1.3.0" +version = "1.4.0" [deps.JSON.extensions] JSONArrowExt = ["ArrowTypes"] @@ -237,9 +243,9 @@ version = "1.14.3" [[deps.JuliaInterpreter]] deps = ["CodeTracking", "InteractiveUtils", "Random", "UUIDs"] -git-tree-sha1 = "b6c76964c65ebf8309460fb8f0f437b4a59d809b" +git-tree-sha1 = "80580012d4ed5a3e8b18c7cd86cebe4b816d17a6" uuid = "aa1ae85d-cabe-5617-a682-6adf51b2e16a" -version = "0.10.7" +version = "0.10.9" [[deps.JuliaSyntaxHighlighting]] deps = ["StyledStrings"] @@ -297,9 +303,9 @@ version = "1.11.0" [[deps.LoweredCodeUtils]] deps = ["CodeTracking", "Compiler", "JuliaInterpreter"] -git-tree-sha1 = "e24491cb83551e44a69b9106c50666dea9d953ab" +git-tree-sha1 = "65ae3db6ab0e5b1b5f217043c558d9d1d33cc88d" uuid = "6f1432cf-f94c-5a45-995e-cdbf5db27b0b" -version = "3.4.4" +version = "3.5.0" [[deps.Markdown]] deps = ["Base64", "JuliaSyntaxHighlighting", "StyledStrings"] @@ -330,7 +336,7 @@ version = "1.11.0" [[deps.MozillaCACerts_jll]] uuid = "14a3606d-f60d-562e-9121-12d972cd8159" -version = "2025.5.20" +version = "2025.11.4" [[deps.NATS]] deps = ["Base64", "BufferedStreams", "CodecBase", "Dates", "DocStringExtensions", "JSON3", "MbedTLS", "NanoDates", "Random", "ScopedValues", "Sockets", "Sodium", "StructTypes", "URIs"] @@ -361,7 +367,7 @@ version = "0.8.7+0" [[deps.OpenSSL_jll]] deps = ["Artifacts", "Libdl"] uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95" -version = "3.5.1+0" +version = "3.5.4+0" [[deps.OpenSpecFun_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl"] @@ -376,9 +382,9 @@ version = "1.8.1" [[deps.PDMats]] deps = ["LinearAlgebra", "SparseArrays", "SuiteSparse"] -git-tree-sha1 = "d922b4d80d1e12c658da7785e754f4796cc1d60d" +git-tree-sha1 = "e4cff168707d441cd6bf3ff7e4832bdf34278e4a" uuid = "90014a1f-27ba-587c-ab20-58faa44d9150" -version = "0.11.36" +version = "0.11.37" weakdeps = ["StatsBase"] [deps.PDMats.extensions] @@ -404,9 +410,9 @@ version = "1.3.3" [[deps.Preferences]] deps = ["TOML"] -git-tree-sha1 = "0f27480397253da18fe2c12a4ba4eb9eb208bf3d" +git-tree-sha1 = "522f093a29b31a93e34eaea17ba055d850edea28" uuid = "21216c6a-2e73-6563-6e65-726566657250" -version = "1.5.0" +version = "1.5.1" [[deps.PrettyPrinting]] git-tree-sha1 = "142ee93724a9c5d04d78df7006670a93ed1b244e" @@ -415,9 +421,15 @@ version = "0.4.2" [[deps.PrettyTables]] deps = ["Crayons", "LaTeXStrings", "Markdown", "PrecompileTools", "Printf", "REPL", "Reexport", "StringManipulation", "Tables"] -git-tree-sha1 = "c5a07210bd060d6a8491b0ccdee2fa0235fc00bf" +git-tree-sha1 = "211530a7dc76ab59087f4d4d1fc3f086fbe87594" uuid = "08abe8d2-0d0c-5749-adfa-8a2ac140af0d" -version = "3.1.2" +version = "3.2.3" + + [deps.PrettyTables.extensions] + PrettyTablesTypstryExt = "Typstry" + + [deps.PrettyTables.weakdeps] + Typstry = "f0ed7684-a786-439e-b1e3-3b82803b501e" [[deps.Printf]] deps = ["Unicode"] @@ -456,17 +468,11 @@ git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b" uuid = "189a3867-3050-52da-a836-e630ba90ab69" version = "1.2.2" -[[deps.Requires]] -deps = ["UUIDs"] -git-tree-sha1 = "62389eeff14780bfe55195b7204c0d8738436d64" -uuid = "ae029012-a4dd-5104-9daa-d747884805df" -version = "1.3.1" - [[deps.Revise]] -deps = ["CodeTracking", "FileWatching", "JuliaInterpreter", "LibGit2", "LoweredCodeUtils", "OrderedCollections", "REPL", "Requires", "UUIDs", "Unicode"] -git-tree-sha1 = "85d94c2be31f58728cd69d13f2e0bdd7ecf6dfe9" +deps = ["CodeTracking", "FileWatching", "InteractiveUtils", "JuliaInterpreter", "LibGit2", "LoweredCodeUtils", "OrderedCollections", "Preferences", "REPL", "UUIDs"] +git-tree-sha1 = "14d1bfb0a30317edc77e11094607ace3c800f193" uuid = "295af30f-e4ad-537b-8983-00126c2a3abe" -version = "3.12.1" +version = "3.13.2" [deps.Revise.extensions] DistributedExt = "Distributed" @@ -498,9 +504,9 @@ version = "1.5.0" [[deps.SentinelArrays]] deps = ["Dates", "Random"] -git-tree-sha1 = "712fb0231ee6f9120e005ccd56297abbc053e7e0" +git-tree-sha1 = "ebe7e59b37c400f694f52b58c93d26201387da70" uuid = "91c51154-3ec4-41a3-a24f-3f23e20d615c" -version = "1.4.8" +version = "1.4.9" [[deps.Serialization]] uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" @@ -529,9 +535,9 @@ version = "1.12.0" [[deps.SpecialFunctions]] deps = ["IrrationalConstants", "LogExpFunctions", "OpenLibm_jll", "OpenSpecFun_jll"] -git-tree-sha1 = "f2685b435df2613e25fc10ad8c26dddb8640f547" +git-tree-sha1 = "5acc6a41b3082920f79ca3c759acbcecf18a8d78" uuid = "276daf66-3868-5448-9aa4-cd146d93841b" -version = "2.6.1" +version = "2.7.1" [deps.SpecialFunctions.extensions] SpecialFunctionsChainRulesCoreExt = "ChainRulesCore" @@ -551,15 +557,15 @@ weakdeps = ["SparseArrays"] [[deps.StatsAPI]] deps = ["LinearAlgebra"] -git-tree-sha1 = "9d72a13a3f4dd3795a195ac5a44d7d6ff5f552ff" +git-tree-sha1 = "178ed29fd5b2a2cfc3bd31c13375ae925623ff36" uuid = "82ae8749-77ed-4fe6-ae5f-f523153014b0" -version = "1.7.1" +version = "1.8.0" [[deps.StatsBase]] -deps = ["AliasTables", "DataAPI", "DataStructures", "LinearAlgebra", "LogExpFunctions", "Missings", "Printf", "Random", "SortingAlgorithms", "SparseArrays", "Statistics", "StatsAPI"] -git-tree-sha1 = "064b532283c97daae49e544bb9cb413c26511f8c" +deps = ["AliasTables", "DataAPI", "DataStructures", "IrrationalConstants", "LinearAlgebra", "LogExpFunctions", "Missings", "Printf", "Random", "SortingAlgorithms", "SparseArrays", "Statistics", "StatsAPI"] +git-tree-sha1 = "aceda6f4e598d331548e04cc6b2124a6148138e3" uuid = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" -version = "0.34.8" +version = "0.34.10" [[deps.StatsFuns]] deps = ["HypergeometricFunctions", "IrrationalConstants", "LogExpFunctions", "Reexport", "Rmath", "SpecialFunctions"] @@ -577,9 +583,9 @@ version = "1.5.2" [[deps.StringManipulation]] deps = ["PrecompileTools"] -git-tree-sha1 = "725421ae8e530ec29bcbdddbe91ff8053421d023" +git-tree-sha1 = "a3c1536470bf8c5e02096ad4853606d7c8f62721" uuid = "892a3eda-7b42-436c-8928-eab12a02cf0e" -version = "0.4.1" +version = "0.4.2" [[deps.StructTypes]] deps = ["Dates", "UUIDs"] @@ -589,9 +595,9 @@ version = "1.11.0" [[deps.StructUtils]] deps = ["Dates", "UUIDs"] -git-tree-sha1 = "79529b493a44927dd5b13dde1c7ce957c2d049e4" +git-tree-sha1 = "28145feabf717c5d65c1d5e09747ee7b1ff3ed13" uuid = "ec057cc2-7a8d-4b58-b3b3-92acb9f63b42" -version = "2.6.0" +version = "2.6.3" [deps.StructUtils.extensions] StructUtilsMeasurementsExt = ["Measurements"] diff --git a/Project.toml b/Project.toml index c25f415..1f21afc 100644 --- a/Project.toml +++ b/Project.toml @@ -20,4 +20,4 @@ UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" [compat] JSON = "1.3.0" NATS = "0.1.0" -Revise = "3.12.1" +Revise = "3.13.2" diff --git a/src/dbUtil.jl b/src/dbUtil.jl index e5f6b08..1925ac8 100644 --- a/src/dbUtil.jl +++ b/src/dbUtil.jl @@ -9,6 +9,38 @@ using ..util # ---------------------------------------------- 100 --------------------------------------------- # +""" +dictToPostgresKeyValueString - Convert dictionary to PostgreSQL key-value string format + +This function takes a dictionary and converts it into a PostgreSQL-compatible key-value string +format suitable for storage in a TEXT field. The output format uses curly braces with comma-separated +key-value pairs, where string values are quoted. + +# Function Workflow: +1. Iterates through dictionary key-value pairs +2. Handles nested dictionaries by recursively converting them +3. Wraps string values in double quotes +4. Formats numeric and other values without quotes +5. Returns a PostgreSQL-compatible key-value string enclosed in curly braces + +# Arguments: + - `dict::Dict` - Dictionary containing key-value pairs to convert + +# Return: + - A String in PostgreSQL key-value format: "{key1: value1, key2: \"value2\", ...}" + +# Example +```jldoctest +julia> data = Dict{String, Any}( + "name" => "John", + "age" => 30, + "city" => "New York" + ); + +julia> dictToPostgresKeyValueString(data) +"{\"name\": \"John\", \"age\": 30, \"city\": \"New York\"}" +``` +""" function dictToPostgresKeyValueString(dict) parts = [] for (k, v) in dict @@ -26,58 +58,67 @@ end -""" Get characters between specified characters. +""" generateInsertSQL - Generate SQL INSERT statement from dictionary data -# Arguments - - `text::T` - a text being searched - - `startChar::Char` - start character - - `endChar::Char` - end character -# Keyword Arguments - - `endCharLocation::String` - end character position after startChar. Can be "next" or "end". "next" means the closed - endChar just after startChar. "end" means the furthest endChar. - - `includeChar::Bool` - whether to include the startChar and endChar. Default is true -# Return - the characters between specified characters. +This function constructs a SQL INSERT statement by extracting values for specified columns +from a dictionary and formatting them into a valid PostgreSQL INSERT query. + +# Function Workflow: +1. Iterates through the dictionary key-value pairs +2. Filters keys to only include those present in `columnToInsert` +3. Collects column names and their corresponding values +4. Constructs the final SQL INSERT statement + +# Arguments: + - `table_name::String` - Name of the database table to insert into + - `columnToInsert::Vector{Symbol}` - List of column names to include in the INSERT statement + - `data::Dict{Symbol, Any}` - Dictionary containing column-value pairs for the insert + +# Return: + - A String containing the SQL INSERT statement # Example ```jldoctest -julia> using Revise -julia> using GeneralUtils -julia> insert_data = Dict( - :grape => "NA", - :acidity => "0", - :tannin => "0", - :country => "NA", - :description => "NA", - :region => "NA", - :winery => "ccc", - :intensity => "0", - :sweetness => "0", - :tasting_notes => "NA", - :wine_name => "new_wine", - :wine_id => "9e1deb6a-d57f-4d2c-abbe-da813f4e91ad", - :wine_type => "NA", - :other_attributes => "{\"attribute3\":{\"attribute5\":666,\"attribute4\":\"text\"},\"attribute1\":\"hello world\",\"attribute2\":555}", - :fizziness => "0", - :serving_temperature => "0", - :additional_search_term => "{NA1,NA2}") -``` -# TODO - - [] update docs +julia> using UUIDs -# Signature +# Insert a single record with specific columns +table_name = "wine" +columnToInsert = [:acidity, :tannin, :country, :region, :winery] +data = Dict{Symbol, Any}( + :grape => "Cabernet Sauvignon", + :acidity => "medium", # using descriptive scale (low/medium/full) + :tannin => "medium-plus", # common wine descriptor + :country => "France", + :description => "A rich and structured red wine with notes of blackcurrant, cedar, and subtle oak.", + :region => "Bordeaux", + :winery => "Château Margaux", + :intensity => "medium", # intensity is usually low/medium/full + :sweetness => "dry", # dry/medium-dry/medium/medium-sweet/sweet + :tasting_notes => "Blackberry, graphite, tobacco, vanilla, and subtle earth.", + :wine_name => "Château Margaux Grand Cru", + :wine_id => "8f3c7a2e-1b4d-4a9f-9c2e-4a8b3d6e5f7a", # UUID-like (valid hex) + :wine_type => "Red", + :other_attributes => Dict{String, Any}( + "vintage" => 2018, + "alcohol_percent" => 13.5, + "ph" => 3.6, + " aging_years" => 24, # years in barrel + " producer_code" => "CM-GRAND" + ), + :fizziness => "still", + :serving_temperature => "16–18°C", + :additional_search_term => ["Cabernet", "Bordeaux red", "premium wine", "CabSav"] +) + +julia> generateInsertSQL(table_name, columnToInsert, data) +"INSERT INTO wine (acidity, tannin, country, region, winery) VALUES ('medium', 'medium-plus', 'France', 'Bordeaux', 'Château Margaux');" +``` """ -function generateInsertSQL(table_name::String, columnToInsert::Vector{Symbol}, - insert_data::Dict{Symbol, Any}) +function generateInsertSQL(table_name::String, columnToInsert::Vector{Symbol}, data::Dict{Symbol, Any}) columns = String[] values = String[] - for (key, value) in insert_data + for (key, value) in data if key ∈ columnToInsert push!(columns, string(key)) push!(values, "'$value'") #[] number should not wrapped in '' @@ -89,113 +130,57 @@ function generateInsertSQL(table_name::String, columnToInsert::Vector{Symbol}, return "INSERT INTO $table_name ($columns_str) VALUES ($values_str);" end -# function generateInsertSQL(table_name::String, insert_data::Dict{Symbol, Any}) -# columns = String[] -# values = String[] - -# for (key, value) in insert_data -# push!(columns, string(key)) -# if key == :other_attributes -# push!(values, "'$value'") -# else -# push!(values, "'$value'") -# end -# end - -# columns_str = join(columns, ", ") -# values_str = join(values, ", ") - -# return "INSERT INTO $table_name ($columns_str) VALUES ($values_str);" -# end - - +# ---------------------------------------------- 100 --------------------------------------------- # +""" generateUpdateSQL - Generate SQL UPDATE statement from dictionary data + +This function constructs a SQL UPDATE statement by updating multiple columns +based on a primary key condition. + +# Arguments: + - `table_name::String` - Name of the database table to update + - `pk_column::Symbol` - The primary key column name + - `pk_value` - The primary key value (used in WHERE clause) + - `data::Dict{Symbol, Any}` - Dictionary containing column-value pairs to update + +# Return: + - A String containing the SQL UPDATE statement + +# Example +```jldoctest +julia> using UUIDs + +# Update multiple columns using a dictionary +table_name = "wine" +pk_column = :wine_id +pk_value = "8f3c7a2e-1b4d-4a9f-9c2e-4a8b3d6e5f7a" +data = Dict{Symbol, Any}( + :acidity => "full", + :tannin => "medium", + :country => "Italy" +) + +julia> generateUpdateSQL(table_name, pk_column, pk_value, data) +"UPDATE wine SET acidity = 'full', tannin = 'medium', country = 'Italy' WHERE wine_id = '8f3c7a2e-1b4d-4a9f-9c2e-4a8b3d6e5f7a';" +``` """ -example: - -insert_data = Dict( - :grape => "NA", - :acidity => "0", - :tannin => "0", - :country => "NA", - :description => "NA", - :region => "NA", - :winery => "ccc", - :intensity => "0", - :sweetness => "0", - :tasting_notes => "NA", - :wine_name => "new_wine", - :wine_id => "9e1deb6a-d57f-4d2c-abbe-da813f4e91ad", - :wine_type => "NA", - :other_attributes => "{\"attribute3\":{\"attribute5\":666,\"attribute4\":\"text\"},\"attribute1\":\"hello world\",\"attribute2\":555}", - :fizziness => "0", - :serving_temperature => "0", - :additional_search_term => "{NA1,NA2}") - -id_keys is the primary key columns -""" -# function generateUpdateSQL(table_name::String, update_data::Dict{Symbol, Any}, id_keys::Vector{Symbol}) -# set_clauses = String[] -# where_clauses = String[] - -# for (key, value) in update_data -# if key in id_keys -# push!(where_clauses, "$key = '$value'") -# else -# if key == :other_attributes -# push!(set_clauses, "$key = '$value'") -# else -# push!(set_clauses, "$key = '$value'") -# end -# end -# end - -# set_clause = join(set_clauses, ", ") -# where_clause = join(where_clauses, " AND ") - -# return "UPDATE $table_name SET $set_clause WHERE $where_clause;" -# end - -function generateUpdateSQL(table_name::String, columnToUpdate::Vector{Symbol}, - updatedata::Dict{Symbol, Any}, id_keys::Vector{Symbol}) - - set_clauses = String[] - where_clauses = String[] - - for (key, value) in updatedata - if key in id_keys - push!(where_clauses, "$key = '$value'") - else - if key ∈ columnToUpdate # update only specified columns - push!(set_clauses, "$key = '$value'") - end - end - end - - set_clause = join(set_clauses, ", ") - where_clause = join(where_clauses, " AND ") - - return "UPDATE $table_name SET $set_clause WHERE $where_clause;" +function generateUpdateSQL(table_name::String, pk_column::Symbol, pk_value, data::Dict{Symbol, Any}) + # Build SET clause + set_parts = String[] + for (key, value) in data + value_str = isa(value, AbstractString) ? "'$value'" : "$value" + push!(set_parts, "$(string(key)) = $value_str") + end + + set_clause = join(set_parts, ", ") + + # Handle primary key value + pk_val_str = isa(pk_value, AbstractString) ? "'$pk_value'" : "$pk_value" + + return "UPDATE $table_name SET $set_clause WHERE $pk_column = $pk_val_str;" end - - - - - - - - - - - - - - - - - end # module \ No newline at end of file