Seeking assistance to INSERT data into Postgres DB using LibPQ.jl

Hi,

Firstly, I am still learning Julia and my recent attempts to expand into Julia is through testing how to write data into a PostgreSQL database. I have been able to successfully connect to and query data in a PG database.

However, I have been attempting to write some simple data to a PostgreSQL database using LibPQ.jl and have been unsuccessful. It is simple test data. I think I am having problems around the use of quotation marks, but I may very well be wrong. Here is an example of what I have tried.

LibPQ.load!(
    (dmdate = '2022-07-17', dmmetric = "overall", dmnum = 8),
	conn,
	"INSERT INTO dailymetrics (dmdate, dmmetric, dmnum) VALUES (\$1, \$2, \$3);")

In PG, the dmdate is a date, dmmetric is a varchar(20) and dmnum is an integer.

I am getting errors if I use single quotations around ‘overall’ because in Julia I know I need to use double quotes. However I am also getting errors when using single quotes around the date (“syntax: character literal contains multiple characters”). I seem to get different errors when I make Julia happy due to postgesql expectations around formating. I get argument errors that don’t satisfy the Tables.jl ‘AbstractionRow’ interface. I suspect this is due to when writing SQL I need to use single quotes around both the date and string (varchar). But I may be wrong on that. I’ve tried all variations and combinations of quotation marks.

Instead of LibPQ.load I’ve also tried variations around Data.stream to no avail. I feel like I am close but cannot work it out. There may be another way I can specify type but I cannot find how to do that.

I’d appreciate any assistance in fixing this.

Well, I have ruled out the types or quotation marks being the problem.

LibPQ.load!(
	(dmdate = Date("2022-07-17"), dmmetric = String("overall"), dmnum = Int(8)),
	conn,
	"INSERT INTO dailymetrics (dmdate, dmmetric, dmnum) VALUES (\$1, \$2, \$3);")

Now I am getting another error.

IOError: stream is closed or unusable

  1. check_open@stream.jl:386 [inlined]
  2. uv_write_async(::Base.PipeEndpoint, ::Ptr{UInt8}, ::UInt64)@stream.jl:1018
  3. uv_write(::Base.PipeEndpoint, ::Ptr{UInt8}, ::UInt64)@stream.jl:981
  4. unsafe_write(::Base.PipeEndpoint, ::Ptr{UInt8}, ::UInt64)@stream.jl:1064
  5. unsafe_write@io.jl:362 [inlined]
  6. write@io.jl:244 [inlined]
  7. print@io.jl:246 [inlined]
  8. var"#with_output_color#873"(::Bool, ::Bool, ::Bool, ::Bool, ::Bool, ::typeof(Base.with_output_color), ::Function, ::Symbol, ::IOContext{Base.PipeEndpoint}, ::String)@util.jl:106
  9. #printstyled#874@util.jl:129 [inlined]
  10. emit(::Memento.DefaultHandler{Memento.DefaultFormatter, IOContext{Base.PipeEndpoint}}, ::Memento.DefaultRecord)@handlers.jl:211
  11. log(::Memento.DefaultHandler{Memento.DefaultFormatter, IOContext{Base.PipeEndpoint}}, ::Memento.DefaultRecord)@handlers.jl:44
  12. log(::Memento.Logger, ::Memento.DefaultRecord)@loggers.jl:371
  13. _log(::Memento.Logger, ::String, ::String)@loggers.jl:416
  14. log(::Memento.Logger, ::String, ::String)@loggers.jl:395
  15. error(::Memento.Logger, ::LibPQ.Errors.PQResultError{LibPQ.Errors.C08, LibPQ.Errors.E08P01})@loggers.jl:462
  16. var"#handle_result#50"(::Bool, ::typeof(LibPQ.handle_result), ::LibPQ.Result{false})@results.jl:238
  17. var"#execute#70"(::Bool, ::Bool, ::Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}}, ::typeof(LibPQ.execute), ::LibPQ.Statement, ::Vector{Union{Missing, String}})@statements.jl:129
  18. load!(::NamedTuple{(:dmdate, :dmmetric, :dmnum), Tuple{Dates.Date, String, Int64}}, ::LibPQ.Connection, ::String)@tables.jl:166
  19. top-level scope@Local: 1 [inlined]

I have no idea what this means. When I do LibPQ.status(conn) I get 'CONNECTION_OK`.

Anyone have any ideas?

This code is working for me; so you can compare with your code.


using LibPQ
using Dates
using DataFrames
conn = LibPQ.Connection("dbname=pljulia")
result = execute(conn, """   
    DROP TABLE IF EXISTS test_dailymetrics;
    CREATE TABLE test_dailymetrics (
        dmdate   DATE,
        dmmetric TEXT,
        dmnum    integer
    );
    """,
    throw_error=true
)
LibPQ.load!(
    (     dmdate = [ Date(2022, 7, 17) ] 
        , dmmetric = [ "overall" ] 
        , dmnum = [ 8 ] 
    ),
	conn,
	"INSERT INTO test_dailymetrics (dmdate, dmmetric, dmnum) VALUES (\$1, \$2, \$3);"
)
df=DataFrame(execute(conn, "SELECT * FROM test_dailymetrics"));
df 
close(conn)

log

julia> using LibPQ

julia> using Dates

julia> using DataFrames

julia> conn = LibPQ.Connection("dbname=pljulia")
PostgreSQL connection (CONNECTION_OK) with parameters:
  user = pljulia
  password = ********************
  channel_binding = prefer
  dbname = pljulia
  hostaddr = 127.0.0.1
  port = 5432
  client_encoding = UTF8
  options = -c DateStyle=ISO,YMD -c IntervalStyle=iso_8601 -c TimeZone=UTC
  application_name = LibPQ.jl
  sslmode = prefer
  sslcompression = 0
  sslsni = 1
  ssl_min_protocol_version = TLSv1.2
  gssencmode = prefer
  krbsrvname = postgres
  target_session_attrs = any

julia> result = execute(conn, """   
           DROP TABLE IF EXISTS test_dailymetrics;
           CREATE TABLE test_dailymetrics (
               dmdate   DATE,
               dmmetric TEXT,
               dmnum    integer
           );
           """,
           throw_error=true
       )
PostgreSQL result

julia> LibPQ.load!(
           (     dmdate = [ Date(2022, 7, 17) ] 
               , dmmetric = [ "overall" ] 
               , dmnum = [ 8 ] 
           ),
               conn,
               "INSERT INTO test_dailymetrics (dmdate, dmmetric, dmnum) VALUES (\$1, \$2, \$3);"
       )
PostgreSQL prepared statement named __libpq_stmt_0__ with query INSERT INTO test_dailymetrics (dmdate, dmmetric, dmnum) VALUES ($1, $2, $3);

julia> df=DataFrame(execute(conn, "SELECT * FROM test_dailymetrics"));

julia> df
1×3 DataFrame
 Row │ dmdate      dmmetric  dmnum  
     │ Date?       String?   Int32? 
─────┼──────────────────────────────
   1 │ 2022-07-17  overall        8

julia> close(conn)

julia> 

some hints:

Hey thanks so much for your assistance. I did try to replicate your code for the LibPQ.load! function and I continued to get an error. So then because I am only testing I did use your result=execute(conn,…) function as well which dropped the table if exists and created a fresh one. That worked. Then I ran the LibPQ.load! and then it worked as well without any additional changes! So that is all good.

It appears that I did need to run that execute(conn…) section at least once. However I am aiming to have this script run daily and update the dailymetrics table. I don’t want to DROP TABLE IF EXISTS. So, I disabled the result=execute(conn…) function, shut down the script then ran it again and it worked without needing the result = execution(conn…). So that’s great. But I DID need to run at at least once it seems. I can only imagine there is some ownership or permissions issue with Postgres causing LibPQ.load! code to not run initially. I did go into Postgres and confirm both my dailymetrics and postgres user had INSERT privileges and they both did.

So now it is all seeming to work! I don’t really understand why the execute(conn) needed to run first but I may look into that later. And thanks for those links. I had read the LibPQ docs on Github, which I found dense to get through as a fresh user of the package. I did also see the result = execute(conn…) function but had incorrectly thought it would not be necessary.

I’ll stop waffling now but thanks again so much. I was really stuck.

1 Like

I made an example using Octo.jl that providing more simple way such insert things.

1 Like

Hey thanks for that. As a new Julia user, I wasn’t tracking Octo as a thing. I’ll look into that.

I might add for others that follow, I have my code all working well for now and I’ve simplified it slightly to draw on a dataframe that gets generated earlier in my script.

LibPQ.load!(
	df,  # this is the new bit, drawing on a dataframe rather than write it inside this load function.
	conn,
	"INSERT INTO dailymetrics (dmdate, dmmetric, dmnum) VALUES (\$1, \$2, \$3);")

I think where I was running into trouble is that I was playing around in a Pluto notebook and I wasn’t being careful about opening and closing the LibPQ connection to the database. I still have to explore this deeper but the connection can actually be hard to disconnect then reconnect to a different table unless you are very deliberate and careful.

But I now have all my code for this in a function where it establishes the connection, writes to the database, queries it to confirm the data has been written, then closes the connection, then returns a string output depending on successful read or not.