demo: db<>fiddle
Test XML:
<products>
<product>
<code>0001</code>
<name>Prod1</name>
<active>t</active>
<barcodes>
<barcode>0001666</barcode>
<barcode>6660001</barcode>
</barcodes>
</product>
<product>
<code>0002</code>
<name>Prod2</name>
<active>f</active>
<barcodes>
<barcode>0000420</barcode>
</barcodes>
</product>
</products>
Query:
WITH xmldata AS (
SELECT '<products><product><code>0001</code><name>Prod1</name><active>t</active><barcodes><barcode>0001666</barcode><barcode>6660001</barcode></barcodes></product><product><code>0002</code><name>Prod2</name><active>f</active><barcodes><barcode>0000420</barcode></barcodes></product></products>'::xml
), insert_products AS (
INSERT INTO products (code, name, active)
SELECT
unnest(xpath('//product/code/text()', xml)),
unnest(xpath('//product/name/text()', xml)),
unnest(xpath('//product/active/text()', xml))::text = 't'
FROM xmldata
RETURNING code -- 1
)
INSERT INTO barcodes (barcode, product_code)
SELECT
unnest(xpath('//barcode/text()', xd.barcodes)), -- 4
ip.code
FROM (
SELECT
unnest(xpath('//product/code/text()', xml))::text as code, -- 2
unnest(xpath('//product/barcodes', xml)) as barcodes
FROM xmldata
)xd
JOIN insert_products ip -- 3
ON xd.code = ip.code
With the help of a CTE two chained INSERT statements can be created. So you could get the code as RETURNING value of the first statement.
With that you can search the right barcodes per product code to create the insert data of the second INSERT statement.
- Insert the product data as you did. But returning the product code.
- Selecting the product code again and the barcode data as XML.
- Joining the inserted products against the origin data to assign the inserted codes to the right barcodes.
- Extracting the barcode data and insert them into the barcodes table.
Result:
Table products:
code name active
0001 Prod1 t
0002 Prod2 f
----------------------
Table barcodes:
barcode product_code
0001666 0001
6660001 0001
0000420 0002
Technically you could safe the joining part if you don't have any filters in your product insert because in both steps you are working on the whole data of the same XML. The join only makes sense if you want to filter out some products and do not want to store out filtered barcodes.
demo:db<>fiddle without join
demo:db<>fiddle with join and filter
Notice the way how you could solve your boolean problem:
unnest(xpath('//product/active/text()', xml))::text = 't'
Convert the XML content into type text and compare it with your TRUE value. The comparison gives out a boolean.
Edit: In your case this could be done even simpler: You don't need the comparison but only a second cast:
unnest(xpath('//product/active/text()', xml))::text::boolean