postgres_scanner icon indicating copy to clipboard operation
postgres_scanner copied to clipboard

Inserting into PG table from DuckDB table with same name does nothing in Python

Open wstrausser opened this issue 7 months ago • 0 comments

Software versions

Postgres: 13.16 Python: 3.13.0 DuckDB: 1.2.0

Issue

I have the following SQL script attempting to insert into a Postgres table my_table all data from a local DuckDB table also called my_table:

INSTALL postgres;
LOAD postgres;

DETACH DATABASE IF EXISTS pg_db;
ATTACH '
    host=127.0.0.1
    port=5432
    dbname=postgres
    user=postgres
    password=postgres
' AS pg_db (TYPE POSTGRES);

CALL postgres_execute('pg_db', 'SET log_statement = ''all'';');

CREATE OR REPLACE TABLE pg_db.public.my_table (
    id INT,
    val TEXT
);

CREATE OR REPLACE TABLE my_table (
    id INT,
    val TEXT
);

INSERT INTO my_table VALUES
    (1, 'a'),
    (2, 'b');

INSERT INTO pg_db.public.my_table
    SELECT *
    FROM my_table;

SELECT *
FROM pg_db.public.my_table;

When executed from a query console in JetBrains DataGrip (which uses JDBC), this produces the expected output:

id val
1 a
2 b

However, when executing the same commands from a Python script, no data is written into the PG table, with no errors or warnings thrown:

import duckdb

def main():
    db = duckdb.connect()
    db.execute(
        """
            INSTALL postgres;
            LOAD postgres;

            DETACH DATABASE IF EXISTS pg_db;
            ATTACH '
                host=127.0.0.1
                port=5432
                dbname=postgres
                user=postgres
                password=postgres
            ' AS pg_db (TYPE POSTGRES);

            CALL postgres_execute('pg_db', 'SET log_statement = ''all'';');

            CREATE OR REPLACE TABLE pg_db.public.my_table (
                id INT,
                val TEXT
            );

            CREATE OR REPLACE TABLE my_table (
                id INT,
                val TEXT
            );

            INSERT INTO my_table VALUES
                (1, 'a'),
                (2, 'b');

            INSERT INTO pg_db.public.my_table
                SELECT *
                FROM my_table;

            SELECT *
            FROM pg_db.public.my_table;
        """
    )

    print(db.fetchall())


if __name__ == "__main__":
    main()

# Output:
# []

When changing the name of the DuckDB table so that it is not the same as the Postgres table, the script now works:

import duckdb

def main():
    db = duckdb.connect()
    db.execute(
        """
            INSTALL postgres;
            LOAD postgres;

            DETACH DATABASE IF EXISTS pg_db;
            ATTACH '
                host=127.0.0.1
                port=5432
                dbname=postgres
                user=postgres
                password=postgres
            ' AS pg_db (TYPE POSTGRES);

            CALL postgres_execute('pg_db', 'SET log_statement = ''all'';');

            CREATE OR REPLACE TABLE pg_db.public.my_table (
                id INT,
                val TEXT
            );

            CREATE OR REPLACE TABLE my_table_local (
                id INT,
                val TEXT
            );

            INSERT INTO my_table_local VALUES
                (1, 'a'),
                (2, 'b');

            INSERT INTO pg_db.public.my_table
                SELECT *
                FROM my_table_local;

            SELECT *
            FROM pg_db.public.my_table;
        """
    )

    print(db.fetchall())


if __name__ == "__main__":
    main()

# Output:
# [(1, 'a'), (2, 'b')]

Logs

Inspecting the Postgres logs shows the following when the script is executed from DataGrip:

2025-03-07 18:32:59.492 UTC [850] LOG:  statement: COMMIT
2025-03-07 18:32:59.698 UTC [850] LOG:  statement: BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;

        SELECT oid, nspname
        FROM pg_namespace

        ORDER BY oid;

        SELECT pg_namespace.oid AS namespace_id, relname, relpages, attname,
            pg_type.typname type_name, atttypmod type_modifier, pg_attribute.attndims ndim,
            attnum, pg_attribute.attnotnull AS notnull, NULL constraint_id,
            NULL constraint_type, NULL constraint_key
        FROM pg_class
        JOIN pg_namespace ON relnamespace = pg_namespace.oid
        JOIN pg_attribute ON pg_class.oid=pg_attribute.attrelid
        JOIN pg_type ON atttypid=pg_type.oid
        WHERE attnum > 0 AND relkind IN ('r', 'v', 'm', 'f', 'p')
        UNION ALL
        SELECT pg_namespace.oid AS namespace_id, relname, NULL relpages, NULL attname, NULL type_name,
            NULL type_modifier, NULL ndim, NULL attnum, NULL AS notnull,
            pg_constraint.oid AS constraint_id, contype AS constraint_type,
            conkey AS constraint_key
        FROM pg_class
        JOIN pg_namespace ON relnamespace = pg_namespace.oid
        JOIN pg_constraint ON (pg_class.oid=pg_constraint.conrelid)
        WHERE relkind IN ('r', 'v', 'm', 'f', 'p') AND contype IN ('p', 'u')
        ORDER BY namespace_id, relname, attnum, constraint_id;

        SELECT n.oid, enumtypid, typname, enumlabel
        FROM pg_enum e
        JOIN pg_type t ON e.enumtypid = t.oid
        JOIN pg_namespace AS n ON (typnamespace=n.oid)

        ORDER BY n.oid, enumtypid, enumsortorder;

        SELECT n.oid, t.typrelid AS id, t.typname as type, pg_attribute.attname, sub_type.typname
        FROM pg_type t
        JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
        JOIN pg_class ON pg_class.oid = t.typrelid
        JOIN pg_attribute ON attrelid=t.typrelid
        JOIN pg_type sub_type ON (pg_attribute.atttypid=sub_type.oid)
        WHERE pg_class.relkind = 'c'
        AND t.typtype='c'

        ORDER BY n.oid, t.oid, attrelid, attnum;

        SELECT pg_namespace.oid, tablename, indexname
        FROM pg_indexes
        JOIN pg_namespace ON (schemaname=nspname)

        ORDER BY pg_namespace.oid;

2025-03-07 18:32:59.707 UTC [850] LOG:  statement: COMMIT
2025-03-07 18:32:59.708 UTC [850] LOG:  statement: BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;
        DROP Table  IF EXISTS "public"."my_table"
2025-03-07 18:32:59.709 UTC [850] LOG:  statement: CREATE TABLE "public"."my_table"("id" INTEGER, "val" VARCHAR);
2025-03-07 18:32:59.721 UTC [850] LOG:  statement: COMMIT
2025-03-07 18:32:59.785 UTC [850] LOG:  statement: BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ
2025-03-07 18:32:59.786 UTC [850] LOG:  statement: COPY "public"."my_table" FROM STDIN (FORMAT BINARY)
2025-03-07 18:32:59.788 UTC [850] LOG:  statement: COMMIT
2025-03-07 18:32:59.807 UTC [850] LOG:  statement: BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ
2025-03-07 18:32:59.808 UTC [850] LOG:  statement:
                COPY (SELECT "id", "val" FROM "public"."my_table" ) TO STDOUT (FORMAT "binary");

2025-03-07 18:32:59.808 UTC [850] LOG:  statement: COMMIT

And the following when run from Python:

2025-03-07 18:33:56.585 UTC [852] LOG:  statement: COMMIT
2025-03-07 18:33:56.586 UTC [852] LOG:  statement: BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;

        SELECT oid, nspname
        FROM pg_namespace

        ORDER BY oid;

        SELECT pg_namespace.oid AS namespace_id, relname, relpages, attname,
            pg_type.typname type_name, atttypmod type_modifier, pg_attribute.attndims ndim,
            attnum, pg_attribute.attnotnull AS notnull, NULL constraint_id,
            NULL constraint_type, NULL constraint_key
        FROM pg_class
        JOIN pg_namespace ON relnamespace = pg_namespace.oid
        JOIN pg_attribute ON pg_class.oid=pg_attribute.attrelid
        JOIN pg_type ON atttypid=pg_type.oid
        WHERE attnum > 0 AND relkind IN ('r', 'v', 'm', 'f', 'p')
        UNION ALL
        SELECT pg_namespace.oid AS namespace_id, relname, NULL relpages, NULL attname, NULL type_name,
            NULL type_modifier, NULL ndim, NULL attnum, NULL AS notnull,
            pg_constraint.oid AS constraint_id, contype AS constraint_type,
            conkey AS constraint_key
        FROM pg_class
        JOIN pg_namespace ON relnamespace = pg_namespace.oid
        JOIN pg_constraint ON (pg_class.oid=pg_constraint.conrelid)
        WHERE relkind IN ('r', 'v', 'm', 'f', 'p') AND contype IN ('p', 'u')
        ORDER BY namespace_id, relname, attnum, constraint_id;

        SELECT n.oid, enumtypid, typname, enumlabel
        FROM pg_enum e
        JOIN pg_type t ON e.enumtypid = t.oid
        JOIN pg_namespace AS n ON (typnamespace=n.oid)

        ORDER BY n.oid, enumtypid, enumsortorder;

        SELECT n.oid, t.typrelid AS id, t.typname as type, pg_attribute.attname, sub_type.typname
        FROM pg_type t
        JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
        JOIN pg_class ON pg_class.oid = t.typrelid
        JOIN pg_attribute ON attrelid=t.typrelid
        JOIN pg_type sub_type ON (pg_attribute.atttypid=sub_type.oid)
        WHERE pg_class.relkind = 'c'
        AND t.typtype='c'

        ORDER BY n.oid, t.oid, attrelid, attnum;

        SELECT pg_namespace.oid, tablename, indexname
        FROM pg_indexes
        JOIN pg_namespace ON (schemaname=nspname)

        ORDER BY pg_namespace.oid;

2025-03-07 18:33:56.598 UTC [852] LOG:  statement: DROP Table  IF EXISTS "public"."my_table"
2025-03-07 18:33:56.599 UTC [852] LOG:  statement: CREATE TABLE "public"."my_table"("id" INTEGER, "val" VARCHAR);
2025-03-07 18:33:56.616 UTC [852] LOG:  statement: COMMIT
2025-03-07 18:33:56.623 UTC [852] LOG:  statement: BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ
2025-03-07 18:33:56.624 UTC [852] LOG:  statement: COPY (SELECT "id", "val" FROM "public"."my_table" ) TO STDOUT (FORMAT "binary");
2025-03-07 18:33:56.624 UTC [852] LOG:  statement: COPY "public"."my_table" FROM STDIN (FORMAT BINARY)
2025-03-07 18:33:56.627 UTC [852] LOG:  statement: COMMIT
2025-03-07 18:33:56.628 UTC [852] LOG:  statement: BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ
2025-03-07 18:33:56.628 UTC [852] LOG:  statement: COPY (SELECT "id", "val" FROM "public"."my_table" ) TO STDOUT (FORMAT "binary");
2025-03-07 18:33:56.629 UTC [852] LOG:  statement: COMMIT

wstrausser avatar Mar 07 '25 18:03 wstrausser