@@ -219,3 +219,155 @@ def test_dfs_are_equal_for_different_chunksizes(postgresql_table, postgresql_con
219219 df ["c1" ] = df ["c1" ].astype ("string" )
220220
221221 assert df .equals (df2 )
222+
223+
224+ def test_upsert (postgresql_table , postgresql_con ):
225+ create_table_sql = (
226+ f"CREATE TABLE public.{ postgresql_table } "
227+ "(c0 varchar NULL PRIMARY KEY,"
228+ "c1 int NULL DEFAULT 42,"
229+ "c2 int NOT NULL);"
230+ )
231+ with postgresql_con .cursor () as cursor :
232+ cursor .execute (create_table_sql )
233+ postgresql_con .commit ()
234+
235+ df = pd .DataFrame ({"c0" : ["foo" , "bar" ], "c2" : [1 , 2 ]})
236+
237+ with pytest .raises (wr .exceptions .InvalidArgumentValue ):
238+ wr .postgresql .to_sql (
239+ df = df ,
240+ con = postgresql_con ,
241+ schema = "public" ,
242+ table = postgresql_table ,
243+ mode = "upsert" ,
244+ upsert_conflict_columns = None ,
245+ use_column_names = True ,
246+ )
247+
248+ wr .postgresql .to_sql (
249+ df = df ,
250+ con = postgresql_con ,
251+ schema = "public" ,
252+ table = postgresql_table ,
253+ mode = "upsert" ,
254+ upsert_conflict_columns = ["c0" ],
255+ use_column_names = True ,
256+ )
257+ wr .postgresql .to_sql (
258+ df = df ,
259+ con = postgresql_con ,
260+ schema = "public" ,
261+ table = postgresql_table ,
262+ mode = "upsert" ,
263+ upsert_conflict_columns = ["c0" ],
264+ use_column_names = True ,
265+ )
266+ df2 = wr .postgresql .read_sql_table (con = postgresql_con , schema = "public" , table = postgresql_table )
267+ assert bool (len (df2 ) == 2 )
268+
269+ wr .postgresql .to_sql (
270+ df = df ,
271+ con = postgresql_con ,
272+ schema = "public" ,
273+ table = postgresql_table ,
274+ mode = "upsert" ,
275+ upsert_conflict_columns = ["c0" ],
276+ use_column_names = True ,
277+ )
278+ df3 = pd .DataFrame ({"c0" : ["baz" , "bar" ], "c2" : [3 , 2 ]})
279+ wr .postgresql .to_sql (
280+ df = df3 ,
281+ con = postgresql_con ,
282+ schema = "public" ,
283+ table = postgresql_table ,
284+ mode = "upsert" ,
285+ upsert_conflict_columns = ["c0" ],
286+ use_column_names = True ,
287+ )
288+ df4 = wr .postgresql .read_sql_table (con = postgresql_con , schema = "public" , table = postgresql_table )
289+ assert bool (len (df4 ) == 3 )
290+
291+ df5 = pd .DataFrame ({"c0" : ["foo" , "bar" ], "c2" : [4 , 5 ]})
292+ wr .postgresql .to_sql (
293+ df = df5 ,
294+ con = postgresql_con ,
295+ schema = "public" ,
296+ table = postgresql_table ,
297+ mode = "upsert" ,
298+ upsert_conflict_columns = ["c0" ],
299+ use_column_names = True ,
300+ )
301+
302+ df6 = wr .postgresql .read_sql_table (con = postgresql_con , schema = "public" , table = postgresql_table )
303+ assert bool (len (df6 ) == 3 )
304+ assert bool (len (df6 .loc [(df6 ["c0" ] == "foo" ) & (df6 ["c2" ] == 4 )]) == 1 )
305+ assert bool (len (df6 .loc [(df6 ["c0" ] == "bar" ) & (df6 ["c2" ] == 5 )]) == 1 )
306+
307+
308+ def test_upsert_multiple_conflict_columns (postgresql_table , postgresql_con ):
309+ create_table_sql = (
310+ f"CREATE TABLE public.{ postgresql_table } "
311+ "(c0 varchar NULL PRIMARY KEY,"
312+ "c1 int NOT NULL,"
313+ "c2 int NOT NULL,"
314+ "UNIQUE (c1, c2));"
315+ )
316+ with postgresql_con .cursor () as cursor :
317+ cursor .execute (create_table_sql )
318+ postgresql_con .commit ()
319+
320+ df = pd .DataFrame ({"c0" : ["foo" , "bar" ], "c1" : [1 , 2 ], "c2" : [3 , 4 ]})
321+ upsert_conflict_columns = ["c1" , "c2" ]
322+
323+ wr .postgresql .to_sql (
324+ df = df ,
325+ con = postgresql_con ,
326+ schema = "public" ,
327+ table = postgresql_table ,
328+ mode = "upsert" ,
329+ upsert_conflict_columns = upsert_conflict_columns ,
330+ use_column_names = True ,
331+ )
332+ wr .postgresql .to_sql (
333+ df = df ,
334+ con = postgresql_con ,
335+ schema = "public" ,
336+ table = postgresql_table ,
337+ mode = "upsert" ,
338+ upsert_conflict_columns = upsert_conflict_columns ,
339+ use_column_names = True ,
340+ )
341+ df2 = wr .postgresql .read_sql_table (con = postgresql_con , schema = "public" , table = postgresql_table )
342+ assert bool (len (df2 ) == 2 )
343+
344+ df3 = pd .DataFrame ({"c0" : ["baz" , "spam" ], "c1" : [1 , 5 ], "c2" : [3 , 2 ]})
345+ wr .postgresql .to_sql (
346+ df = df3 ,
347+ con = postgresql_con ,
348+ schema = "public" ,
349+ table = postgresql_table ,
350+ mode = "upsert" ,
351+ upsert_conflict_columns = upsert_conflict_columns ,
352+ use_column_names = True ,
353+ )
354+ df4 = wr .postgresql .read_sql_table (con = postgresql_con , schema = "public" , table = postgresql_table )
355+ assert bool (len (df4 ) == 3 )
356+
357+ df5 = pd .DataFrame ({"c0" : ["egg" , "spam" ], "c1" : [2 , 5 ], "c2" : [4 , 2 ]})
358+ wr .postgresql .to_sql (
359+ df = df5 ,
360+ con = postgresql_con ,
361+ schema = "public" ,
362+ table = postgresql_table ,
363+ mode = "upsert" ,
364+ upsert_conflict_columns = upsert_conflict_columns ,
365+ use_column_names = True ,
366+ )
367+
368+ df6 = wr .postgresql .read_sql_table (con = postgresql_con , schema = "public" , table = postgresql_table )
369+ df7 = pd .DataFrame ({"c0" : ["baz" , "egg" , "spam" ], "c1" : [1 , 2 , 5 ], "c2" : [3 , 4 , 2 ]})
370+ df7 ["c0" ] = df7 ["c0" ].astype ("string" )
371+ df7 ["c1" ] = df7 ["c1" ].astype ("Int64" )
372+ df7 ["c2" ] = df7 ["c2" ].astype ("Int64" )
373+ assert df6 .equals (df7 )
0 commit comments