Saya sedang mempelajari pengurai PostgreSQL, penulis ulang kueri, dan perencana kueri saat ini, sebagai bagian dari pekerjaan keamanan tingkat baris untuk proyek AXLE. Seperti yang saya perhatikan bahwa ada beberapa dokumentasi yang bagus tentang keseluruhan struktur dan alur tetapi tidak banyak pada beberapa detail, saya pikir saya akan mulai memposting tentang beberapa sudut yang lebih membingungkan.
Jika Anda tidak tertarik dengan kode sumber PostgreSQL dan cara kerjanya, Anda dapat berhenti membaca sekarang.
resjunk
Topik hari ini adalah istilah "resjunk", yang mengacu pada resjunk atribut daftar target. Anda akan melihat istilah ini di seluruh perencana dan penulis ulang, biasanya sebagai pengetahuan yang diasumsikan. Nama ini tidak terlalu membantu.
resjunk kolom dijelaskan di src/backend/executor/execJunk.c , di mana terdapat komentar yang cukup mendetail. Namun, itu tidak benar-benar menjelaskan gagasan menyeluruh.
Konsep adalah terkadang PostgreSQL perlu melacak informasi per-tupel yang bukan merupakan bagian dari keluaran kueri. Ini mungkin kunci pengurutan yang bukan bagian dari daftar pilih, hasil antara dari subkueri yang digunakan sebagai filter kemudian dibuang, atau mungkin kolom internal seperti ctid yang tidak diekspos ke pengguna.
Node rencana memiliki daftar target – ini adalah daftar kolom yang dihasilkan oleh node rencana tersebut. Untuk pengujian SELECT a, b FROM sederhana kolom a dan b akan muncul di daftar target indeks atau node rencana seqscan untuk kueri. Anda dapat mengamatinya sendiri dengan mengaktifkan logging rencana, sesuai dengan output yang dipangkas berikut:
regress=> CREATE TABLE regress=> SET enable_print_plan = on; regress=> SET client_min_messages = debug; regress=> SELECT a, b FROM test; LOG: plan: DETAIL: {PLANNEDSTMT :commandType 1 :queryId 0 .... :planTree {SEQSCAN :startup_cost 0.00 :total_cost 29.40 :plan_rows 1940 :plan_width 12 :targetlist ( {TARGETENTRY :expr {VAR :varno 1 :varattno 1 ... :location 7 } ... :resjunk false } {TARGETENTRY :expr {VAR :varno 1 :varattno 2 ... :location 10 } .... :resjunk false } ) :qual :lefttree :righttree :initPlan :extParam (b) :allParam (b) :scanrelid 1 } :rtable ( {RTE :alias :eref {ALIAS :aliasname test :colnames ("a" "b") } ... :selectedCols (b 9 10) :modifiedCols (b) } ) .... }
Itulah detail rencana untuk:
QUERY PLAN -------------------------------------------------------- Seq Scan on test (cost=0.00..29.40 rows=1940 width=8)
Di dalamnya Anda akan melihat bahwa PILIH memiliki dua entri dalam daftar target, satu untuk setiap kolom. Juga bukan resjunk karena keduanya dikeluarkan oleh kueri.
Bagaimana jika kita menambahkan pengurutan berdasarkan kolom c , yang tidak ada di PILIH -list, kita akan melihat kolom baru ditambahkan ke daftar target dan ditandai sebagai resjunk:
regress=> SELECT a, b FROM test ORDER BY c; LOG: plan: DETAIL: {PLANNEDSTMT :commandType 1 .... :planTree {SORT .... :targetlist ( {TARGETENTRY :expr {VAR :varno 65001 :varattno 1 ... } :resno 1 :resname a ... :resjunk false } {TARGETENTRY :expr {VAR :varno 65001 :varattno 2 ... } :resno 2 :resname b .... :resjunk false } {TARGETENTRY :expr {VAR :varno 65001 :varattno 3 ... } :resno 3 :resname .... :resjunk true } ) :qual :lefttree {SEQSCAN ... :targetlist ( {TARGETENTRY :expr {VAR :varno 1 :varattno 1 ... } :resno 1 ... :resjunk false } {TARGETENTRY :expr {VAR :varno 1 :varattno 2 ... } :resno 2 ... :resjunk false } {TARGETENTRY :expr {VAR :varno 1 :varattno 3 ... } :resno 3 ... :resjunk true } ) .... } :rtable ( {RTE :alias :eref {ALIAS :aliasname test :colnames ("a" "b" "c") } .... :selectedCols (b 9 10 11) :modifiedCols (b) } ) .... }
untuk paket kueri:
QUERY PLAN --------------------------------------------------------------- Sort (cost=135.34..140.19 rows=1940 width=12) Sort Key: c -> Seq Scan on test (cost=0.00..29.40 rows=1940 width=12) (3 rows)
Jadi c ditandai sebagai resjunk karena ini adalah kunci pengurutan yang bukan merupakan bagian dari hasil akhir rencana.
Anda juga akan melihat ctid ditandai resjunk dalam PERBARUI dan HAPUS rencana untuk alasan yang sama – bagian read dari rencana mengambil baris yang akan dimodifikasi dan ID tupelnya; ini ditarik ke bagian terluar MODIFYTABLE rencanakan node yang memperbarui baris untuk menandainya dihapus dan, jika itu adalah pembaruan, menyisipkan versi baru dari baris tersebut.
Penelitian yang mengarah pada hasil ini telah menerima dana dari Program Kerangka Ketujuh Uni Eropa (FP7/2007-2013) di bawah perjanjian hibah n° 318633