update.out 40 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028
  1. --
  2. -- UPDATE syntax tests
  3. --
  4. CREATE TABLE update_test (
  5. a INT DEFAULT 10,
  6. b INT,
  7. c TEXT
  8. );
  9. CREATE TABLE upsert_test (
  10. a INT PRIMARY KEY,
  11. b TEXT
  12. );
  13. INSERT INTO update_test VALUES (5, 10, 'foo');
  14. INSERT INTO update_test(b, a) VALUES (15, 10);
  15. SELECT * FROM update_test;
  16. a | b | c
  17. ----+----+-----
  18. 5 | 10 | foo
  19. 10 | 15 |
  20. (2 rows)
  21. UPDATE update_test SET a = DEFAULT, b = DEFAULT;
  22. SELECT * FROM update_test;
  23. a | b | c
  24. ----+---+-----
  25. 10 | | foo
  26. 10 | |
  27. (2 rows)
  28. -- aliases for the UPDATE target table
  29. UPDATE update_test AS t SET b = 10 WHERE t.a = 10;
  30. SELECT * FROM update_test;
  31. a | b | c
  32. ----+----+-----
  33. 10 | 10 | foo
  34. 10 | 10 |
  35. (2 rows)
  36. UPDATE update_test t SET b = t.b + 10 WHERE t.a = 10;
  37. SELECT * FROM update_test;
  38. a | b | c
  39. ----+----+-----
  40. 10 | 20 | foo
  41. 10 | 20 |
  42. (2 rows)
  43. --
  44. -- Test VALUES in FROM
  45. --
  46. UPDATE update_test SET a=v.i FROM (VALUES(100, 20)) AS v(i, j)
  47. WHERE update_test.b = v.j;
  48. SELECT * FROM update_test;
  49. a | b | c
  50. -----+----+-----
  51. 100 | 20 | foo
  52. 100 | 20 |
  53. (2 rows)
  54. -- fail, wrong data type:
  55. UPDATE update_test SET a = v.* FROM (VALUES(100, 20)) AS v(i, j)
  56. WHERE update_test.b = v.j;
  57. ERROR: column "a" is of type integer but expression is of type record
  58. LINE 1: UPDATE update_test SET a = v.* FROM (VALUES(100, 20)) AS v(i...
  59. ^
  60. HINT: You will need to rewrite or cast the expression.
  61. --
  62. -- Test multiple-set-clause syntax
  63. --
  64. INSERT INTO update_test SELECT a,b+1,c FROM update_test;
  65. SELECT * FROM update_test;
  66. a | b | c
  67. -----+----+-----
  68. 100 | 20 | foo
  69. 100 | 20 |
  70. 100 | 21 | foo
  71. 100 | 21 |
  72. (4 rows)
  73. UPDATE update_test SET (c,b,a) = ('bugle', b+11, DEFAULT) WHERE c = 'foo';
  74. SELECT * FROM update_test;
  75. a | b | c
  76. -----+----+-------
  77. 100 | 20 |
  78. 100 | 21 |
  79. 10 | 31 | bugle
  80. 10 | 32 | bugle
  81. (4 rows)
  82. UPDATE update_test SET (c,b) = ('car', a+b), a = a + 1 WHERE a = 10;
  83. SELECT * FROM update_test;
  84. a | b | c
  85. -----+----+-----
  86. 100 | 20 |
  87. 100 | 21 |
  88. 11 | 41 | car
  89. 11 | 42 | car
  90. (4 rows)
  91. -- fail, multi assignment to same column:
  92. UPDATE update_test SET (c,b) = ('car', a+b), b = a + 1 WHERE a = 10;
  93. ERROR: multiple assignments to same column "b"
  94. -- uncorrelated sub-select:
  95. UPDATE update_test
  96. SET (b,a) = (select a,b from update_test where b = 41 and c = 'car')
  97. WHERE a = 100 AND b = 20;
  98. SELECT * FROM update_test;
  99. a | b | c
  100. -----+----+-----
  101. 100 | 21 |
  102. 11 | 41 | car
  103. 11 | 42 | car
  104. 41 | 11 |
  105. (4 rows)
  106. -- correlated sub-select:
  107. UPDATE update_test o
  108. SET (b,a) = (select a+1,b from update_test i
  109. where i.a=o.a and i.b=o.b and i.c is not distinct from o.c);
  110. SELECT * FROM update_test;
  111. a | b | c
  112. ----+-----+-----
  113. 21 | 101 |
  114. 41 | 12 | car
  115. 42 | 12 | car
  116. 11 | 42 |
  117. (4 rows)
  118. -- fail, multiple rows supplied:
  119. UPDATE update_test SET (b,a) = (select a+1,b from update_test);
  120. ERROR: more than one row returned by a subquery used as an expression
  121. -- set to null if no rows supplied:
  122. UPDATE update_test SET (b,a) = (select a+1,b from update_test where a = 1000)
  123. WHERE a = 11;
  124. SELECT * FROM update_test;
  125. a | b | c
  126. ----+-----+-----
  127. 21 | 101 |
  128. 41 | 12 | car
  129. 42 | 12 | car
  130. | |
  131. (4 rows)
  132. -- *-expansion should work in this context:
  133. UPDATE update_test SET (a,b) = ROW(v.*) FROM (VALUES(21, 100)) AS v(i, j)
  134. WHERE update_test.a = v.i;
  135. -- you might expect this to work, but syntactically it's not a RowExpr:
  136. UPDATE update_test SET (a,b) = (v.*) FROM (VALUES(21, 101)) AS v(i, j)
  137. WHERE update_test.a = v.i;
  138. ERROR: source for a multiple-column UPDATE item must be a sub-SELECT or ROW() expression
  139. LINE 1: UPDATE update_test SET (a,b) = (v.*) FROM (VALUES(21, 101)) ...
  140. ^
  141. -- if an alias for the target table is specified, don't allow references
  142. -- to the original table name
  143. UPDATE update_test AS t SET b = update_test.b + 10 WHERE t.a = 10;
  144. ERROR: invalid reference to FROM-clause entry for table "update_test"
  145. LINE 1: UPDATE update_test AS t SET b = update_test.b + 10 WHERE t.a...
  146. ^
  147. HINT: Perhaps you meant to reference the table alias "t".
  148. -- Make sure that we can update to a TOASTed value.
  149. UPDATE update_test SET c = repeat('x', 10000) WHERE c = 'car';
  150. SELECT a, b, char_length(c) FROM update_test;
  151. a | b | char_length
  152. ----+-----+-------------
  153. | |
  154. 21 | 100 |
  155. 41 | 12 | 10000
  156. 42 | 12 | 10000
  157. (4 rows)
  158. -- Check multi-assignment with a Result node to handle a one-time filter.
  159. EXPLAIN (VERBOSE, COSTS OFF)
  160. UPDATE update_test t
  161. SET (a, b) = (SELECT b, a FROM update_test s WHERE s.a = t.a)
  162. WHERE CURRENT_USER = SESSION_USER;
  163. QUERY PLAN
  164. -------------------------------------------------------------
  165. Update on public.update_test t
  166. -> Result
  167. Output: $1, $2, (SubPlan 1 (returns $1,$2)), t.ctid
  168. One-Time Filter: (CURRENT_USER = SESSION_USER)
  169. -> Seq Scan on public.update_test t
  170. Output: t.a, t.ctid
  171. SubPlan 1 (returns $1,$2)
  172. -> Seq Scan on public.update_test s
  173. Output: s.b, s.a
  174. Filter: (s.a = t.a)
  175. (10 rows)
  176. UPDATE update_test t
  177. SET (a, b) = (SELECT b, a FROM update_test s WHERE s.a = t.a)
  178. WHERE CURRENT_USER = SESSION_USER;
  179. SELECT a, b, char_length(c) FROM update_test;
  180. a | b | char_length
  181. -----+----+-------------
  182. | |
  183. 100 | 21 |
  184. 12 | 41 | 10000
  185. 12 | 42 | 10000
  186. (4 rows)
  187. -- Test ON CONFLICT DO UPDATE
  188. INSERT INTO upsert_test VALUES(1, 'Boo'), (3, 'Zoo');
  189. -- uncorrelated sub-select:
  190. WITH aaa AS (SELECT 1 AS a, 'Foo' AS b) INSERT INTO upsert_test
  191. VALUES (1, 'Bar') ON CONFLICT(a)
  192. DO UPDATE SET (b, a) = (SELECT b, a FROM aaa) RETURNING *;
  193. a | b
  194. ---+-----
  195. 1 | Foo
  196. (1 row)
  197. -- correlated sub-select:
  198. INSERT INTO upsert_test VALUES (1, 'Baz'), (3, 'Zaz') ON CONFLICT(a)
  199. DO UPDATE SET (b, a) = (SELECT b || ', Correlated', a from upsert_test i WHERE i.a = upsert_test.a)
  200. RETURNING *;
  201. a | b
  202. ---+-----------------
  203. 1 | Foo, Correlated
  204. 3 | Zoo, Correlated
  205. (2 rows)
  206. -- correlated sub-select (EXCLUDED.* alias):
  207. INSERT INTO upsert_test VALUES (1, 'Bat'), (3, 'Zot') ON CONFLICT(a)
  208. DO UPDATE SET (b, a) = (SELECT b || ', Excluded', a from upsert_test i WHERE i.a = excluded.a)
  209. RETURNING *;
  210. a | b
  211. ---+---------------------------
  212. 1 | Foo, Correlated, Excluded
  213. 3 | Zoo, Correlated, Excluded
  214. (2 rows)
  215. -- ON CONFLICT using system attributes in RETURNING, testing both the
  216. -- inserting and updating paths. See bug report at:
  217. -- https://www.postgresql.org/message-id/73436355-6432-49B1-92ED-1FE4F7E7E100%40finefun.com.au
  218. INSERT INTO upsert_test VALUES (2, 'Beeble') ON CONFLICT(a)
  219. DO UPDATE SET (b, a) = (SELECT b || ', Excluded', a from upsert_test i WHERE i.a = excluded.a)
  220. RETURNING tableoid::regclass, xmin = pg_current_xact_id()::xid AS xmin_correct, xmax = 0 AS xmax_correct;
  221. tableoid | xmin_correct | xmax_correct
  222. -------------+--------------+--------------
  223. upsert_test | t | t
  224. (1 row)
  225. -- currently xmax is set after a conflict - that's probably not good,
  226. -- but it seems worthwhile to have to be explicit if that changes.
  227. INSERT INTO upsert_test VALUES (2, 'Brox') ON CONFLICT(a)
  228. DO UPDATE SET (b, a) = (SELECT b || ', Excluded', a from upsert_test i WHERE i.a = excluded.a)
  229. RETURNING tableoid::regclass, xmin = pg_current_xact_id()::xid AS xmin_correct, xmax = pg_current_xact_id()::xid AS xmax_correct;
  230. tableoid | xmin_correct | xmax_correct
  231. -------------+--------------+--------------
  232. upsert_test | t | t
  233. (1 row)
  234. DROP TABLE update_test;
  235. DROP TABLE upsert_test;
  236. -- Test ON CONFLICT DO UPDATE with partitioned table and non-identical children
  237. CREATE TABLE upsert_test (
  238. a INT PRIMARY KEY,
  239. b TEXT
  240. ) PARTITION BY LIST (a);
  241. CREATE TABLE upsert_test_1 PARTITION OF upsert_test FOR VALUES IN (1);
  242. CREATE TABLE upsert_test_2 (b TEXT, a INT PRIMARY KEY);
  243. ALTER TABLE upsert_test ATTACH PARTITION upsert_test_2 FOR VALUES IN (2);
  244. INSERT INTO upsert_test VALUES(1, 'Boo'), (2, 'Zoo');
  245. -- uncorrelated sub-select:
  246. WITH aaa AS (SELECT 1 AS a, 'Foo' AS b) INSERT INTO upsert_test
  247. VALUES (1, 'Bar') ON CONFLICT(a)
  248. DO UPDATE SET (b, a) = (SELECT b, a FROM aaa) RETURNING *;
  249. a | b
  250. ---+-----
  251. 1 | Foo
  252. (1 row)
  253. -- correlated sub-select:
  254. WITH aaa AS (SELECT 1 AS ctea, ' Foo' AS cteb) INSERT INTO upsert_test
  255. VALUES (1, 'Bar'), (2, 'Baz') ON CONFLICT(a)
  256. DO UPDATE SET (b, a) = (SELECT upsert_test.b||cteb, upsert_test.a FROM aaa) RETURNING *;
  257. a | b
  258. ---+---------
  259. 1 | Foo Foo
  260. 2 | Zoo Foo
  261. (2 rows)
  262. DROP TABLE upsert_test;
  263. ---------------------------
  264. -- UPDATE with row movement
  265. ---------------------------
  266. -- When a partitioned table receives an UPDATE to the partitioned key and the
  267. -- new values no longer meet the partition's bound, the row must be moved to
  268. -- the correct partition for the new partition key (if one exists). We must
  269. -- also ensure that updatable views on partitioned tables properly enforce any
  270. -- WITH CHECK OPTION that is defined. The situation with triggers in this case
  271. -- also requires thorough testing as partition key updates causing row
  272. -- movement convert UPDATEs into DELETE+INSERT.
  273. CREATE TABLE range_parted (
  274. a text,
  275. b bigint,
  276. c numeric,
  277. d int,
  278. e varchar
  279. ) PARTITION BY RANGE (a, b);
  280. -- Create partitions intentionally in descending bound order, so as to test
  281. -- that update-row-movement works with the leaf partitions not in bound order.
  282. CREATE TABLE part_b_20_b_30 (e varchar, c numeric, a text, b bigint, d int);
  283. ALTER TABLE range_parted ATTACH PARTITION part_b_20_b_30 FOR VALUES FROM ('b', 20) TO ('b', 30);
  284. CREATE TABLE part_b_10_b_20 (e varchar, c numeric, a text, b bigint, d int) PARTITION BY RANGE (c);
  285. CREATE TABLE part_b_1_b_10 PARTITION OF range_parted FOR VALUES FROM ('b', 1) TO ('b', 10);
  286. ALTER TABLE range_parted ATTACH PARTITION part_b_10_b_20 FOR VALUES FROM ('b', 10) TO ('b', 20);
  287. CREATE TABLE part_a_10_a_20 PARTITION OF range_parted FOR VALUES FROM ('a', 10) TO ('a', 20);
  288. CREATE TABLE part_a_1_a_10 PARTITION OF range_parted FOR VALUES FROM ('a', 1) TO ('a', 10);
  289. -- Check that partition-key UPDATE works sanely on a partitioned table that
  290. -- does not have any child partitions.
  291. UPDATE part_b_10_b_20 set b = b - 6;
  292. -- Create some more partitions following the above pattern of descending bound
  293. -- order, but let's make the situation a bit more complex by having the
  294. -- attribute numbers of the columns vary from their parent partition.
  295. CREATE TABLE part_c_100_200 (e varchar, c numeric, a text, b bigint, d int) PARTITION BY range (abs(d));
  296. ALTER TABLE part_c_100_200 DROP COLUMN e, DROP COLUMN c, DROP COLUMN a;
  297. ALTER TABLE part_c_100_200 ADD COLUMN c numeric, ADD COLUMN e varchar, ADD COLUMN a text;
  298. ALTER TABLE part_c_100_200 DROP COLUMN b;
  299. ALTER TABLE part_c_100_200 ADD COLUMN b bigint;
  300. CREATE TABLE part_d_1_15 PARTITION OF part_c_100_200 FOR VALUES FROM (1) TO (15);
  301. CREATE TABLE part_d_15_20 PARTITION OF part_c_100_200 FOR VALUES FROM (15) TO (20);
  302. ALTER TABLE part_b_10_b_20 ATTACH PARTITION part_c_100_200 FOR VALUES FROM (100) TO (200);
  303. CREATE TABLE part_c_1_100 (e varchar, d int, c numeric, b bigint, a text);
  304. ALTER TABLE part_b_10_b_20 ATTACH PARTITION part_c_1_100 FOR VALUES FROM (1) TO (100);
  305. \set init_range_parted 'truncate range_parted; insert into range_parted VALUES (''a'', 1, 1, 1), (''a'', 10, 200, 1), (''b'', 12, 96, 1), (''b'', 13, 97, 2), (''b'', 15, 105, 16), (''b'', 17, 105, 19)'
  306. \set show_data 'select tableoid::regclass::text COLLATE "C" partname, * from range_parted ORDER BY 1, 2, 3, 4, 5, 6'
  307. :init_range_parted;
  308. :show_data;
  309. partname | a | b | c | d | e
  310. ----------------+---+----+-----+----+---
  311. part_a_10_a_20 | a | 10 | 200 | 1 |
  312. part_a_1_a_10 | a | 1 | 1 | 1 |
  313. part_c_1_100 | b | 12 | 96 | 1 |
  314. part_c_1_100 | b | 13 | 97 | 2 |
  315. part_d_15_20 | b | 15 | 105 | 16 |
  316. part_d_15_20 | b | 17 | 105 | 19 |
  317. (6 rows)
  318. -- The order of subplans should be in bound order
  319. EXPLAIN (costs off) UPDATE range_parted set c = c - 50 WHERE c > 97;
  320. QUERY PLAN
  321. -------------------------------------------------------
  322. Update on range_parted
  323. Update on part_a_1_a_10 range_parted_1
  324. Update on part_a_10_a_20 range_parted_2
  325. Update on part_b_1_b_10 range_parted_3
  326. Update on part_c_1_100 range_parted_4
  327. Update on part_d_1_15 range_parted_5
  328. Update on part_d_15_20 range_parted_6
  329. Update on part_b_20_b_30 range_parted_7
  330. -> Append
  331. -> Seq Scan on part_a_1_a_10 range_parted_1
  332. Filter: (c > '97'::numeric)
  333. -> Seq Scan on part_a_10_a_20 range_parted_2
  334. Filter: (c > '97'::numeric)
  335. -> Seq Scan on part_b_1_b_10 range_parted_3
  336. Filter: (c > '97'::numeric)
  337. -> Seq Scan on part_c_1_100 range_parted_4
  338. Filter: (c > '97'::numeric)
  339. -> Seq Scan on part_d_1_15 range_parted_5
  340. Filter: (c > '97'::numeric)
  341. -> Seq Scan on part_d_15_20 range_parted_6
  342. Filter: (c > '97'::numeric)
  343. -> Seq Scan on part_b_20_b_30 range_parted_7
  344. Filter: (c > '97'::numeric)
  345. (23 rows)
  346. -- fail, row movement happens only within the partition subtree.
  347. UPDATE part_c_100_200 set c = c - 20, d = c WHERE c = 105;
  348. ERROR: new row for relation "part_c_100_200" violates partition constraint
  349. DETAIL: Failing row contains (105, 85, null, b, 15).
  350. -- fail, no partition key update, so no attempt to move tuple,
  351. -- but "a = 'a'" violates partition constraint enforced by root partition)
  352. UPDATE part_b_10_b_20 set a = 'a';
  353. ERROR: new row for relation "part_b_10_b_20" violates partition constraint
  354. DETAIL: Failing row contains (null, 96, a, 12, 1).
  355. -- ok, partition key update, no constraint violation
  356. UPDATE range_parted set d = d - 10 WHERE d > 10;
  357. -- ok, no partition key update, no constraint violation
  358. UPDATE range_parted set e = d;
  359. -- No row found
  360. UPDATE part_c_1_100 set c = c + 20 WHERE c = 98;
  361. -- ok, row movement
  362. UPDATE part_b_10_b_20 set c = c + 20 returning c, b, a;
  363. c | b | a
  364. -----+----+---
  365. 116 | 12 | b
  366. 117 | 13 | b
  367. 125 | 15 | b
  368. 125 | 17 | b
  369. (4 rows)
  370. :show_data;
  371. partname | a | b | c | d | e
  372. ----------------+---+----+-----+---+---
  373. part_a_10_a_20 | a | 10 | 200 | 1 | 1
  374. part_a_1_a_10 | a | 1 | 1 | 1 | 1
  375. part_d_1_15 | b | 12 | 116 | 1 | 1
  376. part_d_1_15 | b | 13 | 117 | 2 | 2
  377. part_d_1_15 | b | 15 | 125 | 6 | 6
  378. part_d_1_15 | b | 17 | 125 | 9 | 9
  379. (6 rows)
  380. -- fail, row movement happens only within the partition subtree.
  381. UPDATE part_b_10_b_20 set b = b - 6 WHERE c > 116 returning *;
  382. ERROR: new row for relation "part_b_10_b_20" violates partition constraint
  383. DETAIL: Failing row contains (2, 117, b, 7, 2).
  384. -- ok, row movement, with subset of rows moved into different partition.
  385. UPDATE range_parted set b = b - 6 WHERE c > 116 returning a, b + c;
  386. a | ?column?
  387. ---+----------
  388. a | 204
  389. b | 124
  390. b | 134
  391. b | 136
  392. (4 rows)
  393. :show_data;
  394. partname | a | b | c | d | e
  395. ---------------+---+----+-----+---+---
  396. part_a_1_a_10 | a | 1 | 1 | 1 | 1
  397. part_a_1_a_10 | a | 4 | 200 | 1 | 1
  398. part_b_1_b_10 | b | 7 | 117 | 2 | 2
  399. part_b_1_b_10 | b | 9 | 125 | 6 | 6
  400. part_d_1_15 | b | 11 | 125 | 9 | 9
  401. part_d_1_15 | b | 12 | 116 | 1 | 1
  402. (6 rows)
  403. -- Common table needed for multiple test scenarios.
  404. CREATE TABLE mintab(c1 int);
  405. INSERT into mintab VALUES (120);
  406. -- update partition key using updatable view.
  407. CREATE VIEW upview AS SELECT * FROM range_parted WHERE (select c > c1 FROM mintab) WITH CHECK OPTION;
  408. -- ok
  409. UPDATE upview set c = 199 WHERE b = 4;
  410. -- fail, check option violation
  411. UPDATE upview set c = 120 WHERE b = 4;
  412. ERROR: new row violates check option for view "upview"
  413. DETAIL: Failing row contains (a, 4, 120, 1, 1).
  414. -- fail, row movement with check option violation
  415. UPDATE upview set a = 'b', b = 15, c = 120 WHERE b = 4;
  416. ERROR: new row violates check option for view "upview"
  417. DETAIL: Failing row contains (b, 15, 120, 1, 1).
  418. -- ok, row movement, check option passes
  419. UPDATE upview set a = 'b', b = 15 WHERE b = 4;
  420. :show_data;
  421. partname | a | b | c | d | e
  422. ---------------+---+----+-----+---+---
  423. part_a_1_a_10 | a | 1 | 1 | 1 | 1
  424. part_b_1_b_10 | b | 7 | 117 | 2 | 2
  425. part_b_1_b_10 | b | 9 | 125 | 6 | 6
  426. part_d_1_15 | b | 11 | 125 | 9 | 9
  427. part_d_1_15 | b | 12 | 116 | 1 | 1
  428. part_d_1_15 | b | 15 | 199 | 1 | 1
  429. (6 rows)
  430. -- cleanup
  431. DROP VIEW upview;
  432. -- RETURNING having whole-row vars.
  433. :init_range_parted;
  434. UPDATE range_parted set c = 95 WHERE a = 'b' and b > 10 and c > 100 returning (range_parted), *;
  435. range_parted | a | b | c | d | e
  436. ---------------+---+----+----+----+---
  437. (b,15,95,16,) | b | 15 | 95 | 16 |
  438. (b,17,95,19,) | b | 17 | 95 | 19 |
  439. (2 rows)
  440. :show_data;
  441. partname | a | b | c | d | e
  442. ----------------+---+----+-----+----+---
  443. part_a_10_a_20 | a | 10 | 200 | 1 |
  444. part_a_1_a_10 | a | 1 | 1 | 1 |
  445. part_c_1_100 | b | 12 | 96 | 1 |
  446. part_c_1_100 | b | 13 | 97 | 2 |
  447. part_c_1_100 | b | 15 | 95 | 16 |
  448. part_c_1_100 | b | 17 | 95 | 19 |
  449. (6 rows)
  450. -- Transition tables with update row movement
  451. :init_range_parted;
  452. CREATE FUNCTION trans_updatetrigfunc() RETURNS trigger LANGUAGE plpgsql AS
  453. $$
  454. begin
  455. raise notice 'trigger = %, old table = %, new table = %',
  456. TG_NAME,
  457. (select string_agg(old_table::text, ', ' ORDER BY a) FROM old_table),
  458. (select string_agg(new_table::text, ', ' ORDER BY a) FROM new_table);
  459. return null;
  460. end;
  461. $$;
  462. CREATE TRIGGER trans_updatetrig
  463. AFTER UPDATE ON range_parted REFERENCING OLD TABLE AS old_table NEW TABLE AS new_table
  464. FOR EACH STATEMENT EXECUTE PROCEDURE trans_updatetrigfunc();
  465. UPDATE range_parted set c = (case when c = 96 then 110 else c + 1 end ) WHERE a = 'b' and b > 10 and c >= 96;
  466. NOTICE: trigger = trans_updatetrig, old table = (b,12,96,1,), (b,13,97,2,), (b,15,105,16,), (b,17,105,19,), new table = (b,12,110,1,), (b,13,98,2,), (b,15,106,16,), (b,17,106,19,)
  467. :show_data;
  468. partname | a | b | c | d | e
  469. ----------------+---+----+-----+----+---
  470. part_a_10_a_20 | a | 10 | 200 | 1 |
  471. part_a_1_a_10 | a | 1 | 1 | 1 |
  472. part_c_1_100 | b | 13 | 98 | 2 |
  473. part_d_15_20 | b | 15 | 106 | 16 |
  474. part_d_15_20 | b | 17 | 106 | 19 |
  475. part_d_1_15 | b | 12 | 110 | 1 |
  476. (6 rows)
  477. :init_range_parted;
  478. -- Enabling OLD TABLE capture for both DELETE as well as UPDATE stmt triggers
  479. -- should not cause DELETEd rows to be captured twice. Similar thing for
  480. -- INSERT triggers and inserted rows.
  481. CREATE TRIGGER trans_deletetrig
  482. AFTER DELETE ON range_parted REFERENCING OLD TABLE AS old_table
  483. FOR EACH STATEMENT EXECUTE PROCEDURE trans_updatetrigfunc();
  484. CREATE TRIGGER trans_inserttrig
  485. AFTER INSERT ON range_parted REFERENCING NEW TABLE AS new_table
  486. FOR EACH STATEMENT EXECUTE PROCEDURE trans_updatetrigfunc();
  487. UPDATE range_parted set c = c + 50 WHERE a = 'b' and b > 10 and c >= 96;
  488. NOTICE: trigger = trans_updatetrig, old table = (b,12,96,1,), (b,13,97,2,), (b,15,105,16,), (b,17,105,19,), new table = (b,12,146,1,), (b,13,147,2,), (b,15,155,16,), (b,17,155,19,)
  489. :show_data;
  490. partname | a | b | c | d | e
  491. ----------------+---+----+-----+----+---
  492. part_a_10_a_20 | a | 10 | 200 | 1 |
  493. part_a_1_a_10 | a | 1 | 1 | 1 |
  494. part_d_15_20 | b | 15 | 155 | 16 |
  495. part_d_15_20 | b | 17 | 155 | 19 |
  496. part_d_1_15 | b | 12 | 146 | 1 |
  497. part_d_1_15 | b | 13 | 147 | 2 |
  498. (6 rows)
  499. DROP TRIGGER trans_deletetrig ON range_parted;
  500. DROP TRIGGER trans_inserttrig ON range_parted;
  501. -- Don't drop trans_updatetrig yet. It is required below.
  502. -- Test with transition tuple conversion happening for rows moved into the
  503. -- new partition. This requires a trigger that references transition table
  504. -- (we already have trans_updatetrig). For inserted rows, the conversion
  505. -- is not usually needed, because the original tuple is already compatible with
  506. -- the desired transition tuple format. But conversion happens when there is a
  507. -- BR trigger because the trigger can change the inserted row. So install a
  508. -- BR triggers on those child partitions where the rows will be moved.
  509. CREATE FUNCTION func_parted_mod_b() RETURNS trigger AS $$
  510. BEGIN
  511. NEW.b = NEW.b + 1;
  512. return NEW;
  513. END $$ language plpgsql;
  514. CREATE TRIGGER trig_c1_100 BEFORE UPDATE OR INSERT ON part_c_1_100
  515. FOR EACH ROW EXECUTE PROCEDURE func_parted_mod_b();
  516. CREATE TRIGGER trig_d1_15 BEFORE UPDATE OR INSERT ON part_d_1_15
  517. FOR EACH ROW EXECUTE PROCEDURE func_parted_mod_b();
  518. CREATE TRIGGER trig_d15_20 BEFORE UPDATE OR INSERT ON part_d_15_20
  519. FOR EACH ROW EXECUTE PROCEDURE func_parted_mod_b();
  520. :init_range_parted;
  521. UPDATE range_parted set c = (case when c = 96 then 110 else c + 1 end) WHERE a = 'b' and b > 10 and c >= 96;
  522. NOTICE: trigger = trans_updatetrig, old table = (b,13,96,1,), (b,14,97,2,), (b,16,105,16,), (b,18,105,19,), new table = (b,15,110,1,), (b,15,98,2,), (b,17,106,16,), (b,19,106,19,)
  523. :show_data;
  524. partname | a | b | c | d | e
  525. ----------------+---+----+-----+----+---
  526. part_a_10_a_20 | a | 10 | 200 | 1 |
  527. part_a_1_a_10 | a | 1 | 1 | 1 |
  528. part_c_1_100 | b | 15 | 98 | 2 |
  529. part_d_15_20 | b | 17 | 106 | 16 |
  530. part_d_15_20 | b | 19 | 106 | 19 |
  531. part_d_1_15 | b | 15 | 110 | 1 |
  532. (6 rows)
  533. :init_range_parted;
  534. UPDATE range_parted set c = c + 50 WHERE a = 'b' and b > 10 and c >= 96;
  535. NOTICE: trigger = trans_updatetrig, old table = (b,13,96,1,), (b,14,97,2,), (b,16,105,16,), (b,18,105,19,), new table = (b,15,146,1,), (b,16,147,2,), (b,17,155,16,), (b,19,155,19,)
  536. :show_data;
  537. partname | a | b | c | d | e
  538. ----------------+---+----+-----+----+---
  539. part_a_10_a_20 | a | 10 | 200 | 1 |
  540. part_a_1_a_10 | a | 1 | 1 | 1 |
  541. part_d_15_20 | b | 17 | 155 | 16 |
  542. part_d_15_20 | b | 19 | 155 | 19 |
  543. part_d_1_15 | b | 15 | 146 | 1 |
  544. part_d_1_15 | b | 16 | 147 | 2 |
  545. (6 rows)
  546. -- Case where per-partition tuple conversion map array is allocated, but the
  547. -- map is not required for the particular tuple that is routed, thanks to
  548. -- matching table attributes of the partition and the target table.
  549. :init_range_parted;
  550. UPDATE range_parted set b = 15 WHERE b = 1;
  551. NOTICE: trigger = trans_updatetrig, old table = (a,1,1,1,), new table = (a,15,1,1,)
  552. :show_data;
  553. partname | a | b | c | d | e
  554. ----------------+---+----+-----+----+---
  555. part_a_10_a_20 | a | 10 | 200 | 1 |
  556. part_a_10_a_20 | a | 15 | 1 | 1 |
  557. part_c_1_100 | b | 13 | 96 | 1 |
  558. part_c_1_100 | b | 14 | 97 | 2 |
  559. part_d_15_20 | b | 16 | 105 | 16 |
  560. part_d_15_20 | b | 18 | 105 | 19 |
  561. (6 rows)
  562. DROP TRIGGER trans_updatetrig ON range_parted;
  563. DROP TRIGGER trig_c1_100 ON part_c_1_100;
  564. DROP TRIGGER trig_d1_15 ON part_d_1_15;
  565. DROP TRIGGER trig_d15_20 ON part_d_15_20;
  566. DROP FUNCTION func_parted_mod_b();
  567. -- RLS policies with update-row-movement
  568. -----------------------------------------
  569. ALTER TABLE range_parted ENABLE ROW LEVEL SECURITY;
  570. CREATE USER regress_range_parted_user;
  571. GRANT ALL ON range_parted, mintab TO regress_range_parted_user;
  572. CREATE POLICY seeall ON range_parted AS PERMISSIVE FOR SELECT USING (true);
  573. CREATE POLICY policy_range_parted ON range_parted for UPDATE USING (true) WITH CHECK (c % 2 = 0);
  574. :init_range_parted;
  575. SET SESSION AUTHORIZATION regress_range_parted_user;
  576. -- This should fail with RLS violation error while moving row from
  577. -- part_a_10_a_20 to part_d_1_15, because we are setting 'c' to an odd number.
  578. UPDATE range_parted set a = 'b', c = 151 WHERE a = 'a' and c = 200;
  579. ERROR: new row violates row-level security policy for table "range_parted"
  580. RESET SESSION AUTHORIZATION;
  581. -- Create a trigger on part_d_1_15
  582. CREATE FUNCTION func_d_1_15() RETURNS trigger AS $$
  583. BEGIN
  584. NEW.c = NEW.c + 1; -- Make even numbers odd, or vice versa
  585. return NEW;
  586. END $$ LANGUAGE plpgsql;
  587. CREATE TRIGGER trig_d_1_15 BEFORE INSERT ON part_d_1_15
  588. FOR EACH ROW EXECUTE PROCEDURE func_d_1_15();
  589. :init_range_parted;
  590. SET SESSION AUTHORIZATION regress_range_parted_user;
  591. -- Here, RLS checks should succeed while moving row from part_a_10_a_20 to
  592. -- part_d_1_15. Even though the UPDATE is setting 'c' to an odd number, the
  593. -- trigger at the destination partition again makes it an even number.
  594. UPDATE range_parted set a = 'b', c = 151 WHERE a = 'a' and c = 200;
  595. RESET SESSION AUTHORIZATION;
  596. :init_range_parted;
  597. SET SESSION AUTHORIZATION regress_range_parted_user;
  598. -- This should fail with RLS violation error. Even though the UPDATE is setting
  599. -- 'c' to an even number, the trigger at the destination partition again makes
  600. -- it an odd number.
  601. UPDATE range_parted set a = 'b', c = 150 WHERE a = 'a' and c = 200;
  602. ERROR: new row violates row-level security policy for table "range_parted"
  603. -- Cleanup
  604. RESET SESSION AUTHORIZATION;
  605. DROP TRIGGER trig_d_1_15 ON part_d_1_15;
  606. DROP FUNCTION func_d_1_15();
  607. -- Policy expression contains SubPlan
  608. RESET SESSION AUTHORIZATION;
  609. :init_range_parted;
  610. CREATE POLICY policy_range_parted_subplan on range_parted
  611. AS RESTRICTIVE for UPDATE USING (true)
  612. WITH CHECK ((SELECT range_parted.c <= c1 FROM mintab));
  613. SET SESSION AUTHORIZATION regress_range_parted_user;
  614. -- fail, mintab has row with c1 = 120
  615. UPDATE range_parted set a = 'b', c = 122 WHERE a = 'a' and c = 200;
  616. ERROR: new row violates row-level security policy "policy_range_parted_subplan" for table "range_parted"
  617. -- ok
  618. UPDATE range_parted set a = 'b', c = 120 WHERE a = 'a' and c = 200;
  619. -- RLS policy expression contains whole row.
  620. RESET SESSION AUTHORIZATION;
  621. :init_range_parted;
  622. CREATE POLICY policy_range_parted_wholerow on range_parted AS RESTRICTIVE for UPDATE USING (true)
  623. WITH CHECK (range_parted = row('b', 10, 112, 1, NULL)::range_parted);
  624. SET SESSION AUTHORIZATION regress_range_parted_user;
  625. -- ok, should pass the RLS check
  626. UPDATE range_parted set a = 'b', c = 112 WHERE a = 'a' and c = 200;
  627. RESET SESSION AUTHORIZATION;
  628. :init_range_parted;
  629. SET SESSION AUTHORIZATION regress_range_parted_user;
  630. -- fail, the whole row RLS check should fail
  631. UPDATE range_parted set a = 'b', c = 116 WHERE a = 'a' and c = 200;
  632. ERROR: new row violates row-level security policy "policy_range_parted_wholerow" for table "range_parted"
  633. -- Cleanup
  634. RESET SESSION AUTHORIZATION;
  635. DROP POLICY policy_range_parted ON range_parted;
  636. DROP POLICY policy_range_parted_subplan ON range_parted;
  637. DROP POLICY policy_range_parted_wholerow ON range_parted;
  638. REVOKE ALL ON range_parted, mintab FROM regress_range_parted_user;
  639. DROP USER regress_range_parted_user;
  640. DROP TABLE mintab;
  641. -- statement triggers with update row movement
  642. ---------------------------------------------------
  643. :init_range_parted;
  644. CREATE FUNCTION trigfunc() returns trigger language plpgsql as
  645. $$
  646. begin
  647. raise notice 'trigger = % fired on table % during %',
  648. TG_NAME, TG_TABLE_NAME, TG_OP;
  649. return null;
  650. end;
  651. $$;
  652. -- Triggers on root partition
  653. CREATE TRIGGER parent_delete_trig
  654. AFTER DELETE ON range_parted for each statement execute procedure trigfunc();
  655. CREATE TRIGGER parent_update_trig
  656. AFTER UPDATE ON range_parted for each statement execute procedure trigfunc();
  657. CREATE TRIGGER parent_insert_trig
  658. AFTER INSERT ON range_parted for each statement execute procedure trigfunc();
  659. -- Triggers on leaf partition part_c_1_100
  660. CREATE TRIGGER c1_delete_trig
  661. AFTER DELETE ON part_c_1_100 for each statement execute procedure trigfunc();
  662. CREATE TRIGGER c1_update_trig
  663. AFTER UPDATE ON part_c_1_100 for each statement execute procedure trigfunc();
  664. CREATE TRIGGER c1_insert_trig
  665. AFTER INSERT ON part_c_1_100 for each statement execute procedure trigfunc();
  666. -- Triggers on leaf partition part_d_1_15
  667. CREATE TRIGGER d1_delete_trig
  668. AFTER DELETE ON part_d_1_15 for each statement execute procedure trigfunc();
  669. CREATE TRIGGER d1_update_trig
  670. AFTER UPDATE ON part_d_1_15 for each statement execute procedure trigfunc();
  671. CREATE TRIGGER d1_insert_trig
  672. AFTER INSERT ON part_d_1_15 for each statement execute procedure trigfunc();
  673. -- Triggers on leaf partition part_d_15_20
  674. CREATE TRIGGER d15_delete_trig
  675. AFTER DELETE ON part_d_15_20 for each statement execute procedure trigfunc();
  676. CREATE TRIGGER d15_update_trig
  677. AFTER UPDATE ON part_d_15_20 for each statement execute procedure trigfunc();
  678. CREATE TRIGGER d15_insert_trig
  679. AFTER INSERT ON part_d_15_20 for each statement execute procedure trigfunc();
  680. -- Move all rows from part_c_100_200 to part_c_1_100. None of the delete or
  681. -- insert statement triggers should be fired.
  682. UPDATE range_parted set c = c - 50 WHERE c > 97;
  683. NOTICE: trigger = parent_update_trig fired on table range_parted during UPDATE
  684. :show_data;
  685. partname | a | b | c | d | e
  686. ----------------+---+----+-----+----+---
  687. part_a_10_a_20 | a | 10 | 150 | 1 |
  688. part_a_1_a_10 | a | 1 | 1 | 1 |
  689. part_c_1_100 | b | 12 | 96 | 1 |
  690. part_c_1_100 | b | 13 | 97 | 2 |
  691. part_c_1_100 | b | 15 | 55 | 16 |
  692. part_c_1_100 | b | 17 | 55 | 19 |
  693. (6 rows)
  694. DROP TRIGGER parent_delete_trig ON range_parted;
  695. DROP TRIGGER parent_update_trig ON range_parted;
  696. DROP TRIGGER parent_insert_trig ON range_parted;
  697. DROP TRIGGER c1_delete_trig ON part_c_1_100;
  698. DROP TRIGGER c1_update_trig ON part_c_1_100;
  699. DROP TRIGGER c1_insert_trig ON part_c_1_100;
  700. DROP TRIGGER d1_delete_trig ON part_d_1_15;
  701. DROP TRIGGER d1_update_trig ON part_d_1_15;
  702. DROP TRIGGER d1_insert_trig ON part_d_1_15;
  703. DROP TRIGGER d15_delete_trig ON part_d_15_20;
  704. DROP TRIGGER d15_update_trig ON part_d_15_20;
  705. DROP TRIGGER d15_insert_trig ON part_d_15_20;
  706. -- Creating default partition for range
  707. :init_range_parted;
  708. create table part_def partition of range_parted default;
  709. \d+ part_def
  710. Table "public.part_def"
  711. Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
  712. --------+-------------------+-----------+----------+---------+----------+--------------+-------------
  713. a | text | | | | extended | |
  714. b | bigint | | | | plain | |
  715. c | numeric | | | | main | |
  716. d | integer | | | | plain | |
  717. e | character varying | | | | extended | |
  718. Partition of: range_parted DEFAULT
  719. Partition constraint: (NOT ((a IS NOT NULL) AND (b IS NOT NULL) AND (((a = 'a'::text) AND (b >= '1'::bigint) AND (b < '10'::bigint)) OR ((a = 'a'::text) AND (b >= '10'::bigint) AND (b < '20'::bigint)) OR ((a = 'b'::text) AND (b >= '1'::bigint) AND (b < '10'::bigint)) OR ((a = 'b'::text) AND (b >= '10'::bigint) AND (b < '20'::bigint)) OR ((a = 'b'::text) AND (b >= '20'::bigint) AND (b < '30'::bigint)))))
  720. insert into range_parted values ('c', 9);
  721. -- ok
  722. update part_def set a = 'd' where a = 'c';
  723. -- fail
  724. update part_def set a = 'a' where a = 'd';
  725. ERROR: new row for relation "part_def" violates partition constraint
  726. DETAIL: Failing row contains (a, 9, null, null, null).
  727. :show_data;
  728. partname | a | b | c | d | e
  729. ----------------+---+----+-----+----+---
  730. part_a_10_a_20 | a | 10 | 200 | 1 |
  731. part_a_1_a_10 | a | 1 | 1 | 1 |
  732. part_c_1_100 | b | 12 | 96 | 1 |
  733. part_c_1_100 | b | 13 | 97 | 2 |
  734. part_d_15_20 | b | 15 | 105 | 16 |
  735. part_d_15_20 | b | 17 | 105 | 19 |
  736. part_def | d | 9 | | |
  737. (7 rows)
  738. -- Update row movement from non-default to default partition.
  739. -- fail, default partition is not under part_a_10_a_20;
  740. UPDATE part_a_10_a_20 set a = 'ad' WHERE a = 'a';
  741. ERROR: new row for relation "part_a_10_a_20" violates partition constraint
  742. DETAIL: Failing row contains (ad, 10, 200, 1, null).
  743. -- ok
  744. UPDATE range_parted set a = 'ad' WHERE a = 'a';
  745. UPDATE range_parted set a = 'bd' WHERE a = 'b';
  746. :show_data;
  747. partname | a | b | c | d | e
  748. ----------+----+----+-----+----+---
  749. part_def | ad | 1 | 1 | 1 |
  750. part_def | ad | 10 | 200 | 1 |
  751. part_def | bd | 12 | 96 | 1 |
  752. part_def | bd | 13 | 97 | 2 |
  753. part_def | bd | 15 | 105 | 16 |
  754. part_def | bd | 17 | 105 | 19 |
  755. part_def | d | 9 | | |
  756. (7 rows)
  757. -- Update row movement from default to non-default partitions.
  758. -- ok
  759. UPDATE range_parted set a = 'a' WHERE a = 'ad';
  760. UPDATE range_parted set a = 'b' WHERE a = 'bd';
  761. :show_data;
  762. partname | a | b | c | d | e
  763. ----------------+---+----+-----+----+---
  764. part_a_10_a_20 | a | 10 | 200 | 1 |
  765. part_a_1_a_10 | a | 1 | 1 | 1 |
  766. part_c_1_100 | b | 12 | 96 | 1 |
  767. part_c_1_100 | b | 13 | 97 | 2 |
  768. part_d_15_20 | b | 15 | 105 | 16 |
  769. part_d_15_20 | b | 17 | 105 | 19 |
  770. part_def | d | 9 | | |
  771. (7 rows)
  772. -- Cleanup: range_parted no longer needed.
  773. DROP TABLE range_parted;
  774. CREATE TABLE list_parted (
  775. a text,
  776. b int
  777. ) PARTITION BY list (a);
  778. CREATE TABLE list_part1 PARTITION OF list_parted for VALUES in ('a', 'b');
  779. CREATE TABLE list_default PARTITION OF list_parted default;
  780. INSERT into list_part1 VALUES ('a', 1);
  781. INSERT into list_default VALUES ('d', 10);
  782. -- fail
  783. UPDATE list_default set a = 'a' WHERE a = 'd';
  784. ERROR: new row for relation "list_default" violates partition constraint
  785. DETAIL: Failing row contains (a, 10).
  786. -- ok
  787. UPDATE list_default set a = 'x' WHERE a = 'd';
  788. DROP TABLE list_parted;
  789. -- Test retrieval of system columns with non-consistent partition row types.
  790. -- This is only partially supported, as seen in the results.
  791. create table utrtest (a int, b text) partition by list (a);
  792. create table utr1 (a int check (a in (1)), q text, b text);
  793. create table utr2 (a int check (a in (2)), b text);
  794. alter table utr1 drop column q;
  795. alter table utrtest attach partition utr1 for values in (1);
  796. alter table utrtest attach partition utr2 for values in (2);
  797. insert into utrtest values (1, 'foo')
  798. returning *, tableoid::regclass, xmin = pg_current_xact_id()::xid as xmin_ok;
  799. a | b | tableoid | xmin_ok
  800. ---+-----+----------+---------
  801. 1 | foo | utr1 | t
  802. (1 row)
  803. insert into utrtest values (2, 'bar')
  804. returning *, tableoid::regclass, xmin = pg_current_xact_id()::xid as xmin_ok; -- fails
  805. ERROR: cannot retrieve a system column in this context
  806. insert into utrtest values (2, 'bar')
  807. returning *, tableoid::regclass;
  808. a | b | tableoid
  809. ---+-----+----------
  810. 2 | bar | utr2
  811. (1 row)
  812. update utrtest set b = b || b from (values (1), (2)) s(x) where a = s.x
  813. returning *, tableoid::regclass, xmin = pg_current_xact_id()::xid as xmin_ok;
  814. a | b | x | tableoid | xmin_ok
  815. ---+--------+---+----------+---------
  816. 1 | foofoo | 1 | utr1 | t
  817. 2 | barbar | 2 | utr2 | t
  818. (2 rows)
  819. update utrtest set a = 3 - a from (values (1), (2)) s(x) where a = s.x
  820. returning *, tableoid::regclass, xmin = pg_current_xact_id()::xid as xmin_ok; -- fails
  821. ERROR: cannot retrieve a system column in this context
  822. update utrtest set a = 3 - a from (values (1), (2)) s(x) where a = s.x
  823. returning *, tableoid::regclass;
  824. a | b | x | tableoid
  825. ---+--------+---+----------
  826. 2 | foofoo | 1 | utr2
  827. 1 | barbar | 2 | utr1
  828. (2 rows)
  829. delete from utrtest
  830. returning *, tableoid::regclass, xmax = pg_current_xact_id()::xid as xmax_ok;
  831. a | b | tableoid | xmax_ok
  832. ---+--------+----------+---------
  833. 1 | barbar | utr1 | t
  834. 2 | foofoo | utr2 | t
  835. (2 rows)
  836. drop table utrtest;
  837. --------------
  838. -- Some more update-partition-key test scenarios below. This time use list
  839. -- partitions.
  840. --------------
  841. -- Setup for list partitions
  842. CREATE TABLE list_parted (a numeric, b int, c int8) PARTITION BY list (a);
  843. CREATE TABLE sub_parted PARTITION OF list_parted for VALUES in (1) PARTITION BY list (b);
  844. CREATE TABLE sub_part1(b int, c int8, a numeric);
  845. ALTER TABLE sub_parted ATTACH PARTITION sub_part1 for VALUES in (1);
  846. CREATE TABLE sub_part2(b int, c int8, a numeric);
  847. ALTER TABLE sub_parted ATTACH PARTITION sub_part2 for VALUES in (2);
  848. CREATE TABLE list_part1(a numeric, b int, c int8);
  849. ALTER TABLE list_parted ATTACH PARTITION list_part1 for VALUES in (2,3);
  850. INSERT into list_parted VALUES (2,5,50);
  851. INSERT into list_parted VALUES (3,6,60);
  852. INSERT into sub_parted VALUES (1,1,60);
  853. INSERT into sub_parted VALUES (1,2,10);
  854. -- Test partition constraint violation when intermediate ancestor is used and
  855. -- constraint is inherited from upper root.
  856. UPDATE sub_parted set a = 2 WHERE c = 10;
  857. ERROR: new row for relation "sub_parted" violates partition constraint
  858. DETAIL: Failing row contains (2, 2, 10).
  859. -- Test update-partition-key, where the unpruned partitions do not have their
  860. -- partition keys updated.
  861. SELECT tableoid::regclass::text, * FROM list_parted WHERE a = 2 ORDER BY 1;
  862. tableoid | a | b | c
  863. ------------+---+---+----
  864. list_part1 | 2 | 5 | 50
  865. (1 row)
  866. UPDATE list_parted set b = c + a WHERE a = 2;
  867. SELECT tableoid::regclass::text, * FROM list_parted WHERE a = 2 ORDER BY 1;
  868. tableoid | a | b | c
  869. ------------+---+----+----
  870. list_part1 | 2 | 52 | 50
  871. (1 row)
  872. -- Test the case where BR UPDATE triggers change the partition key.
  873. CREATE FUNCTION func_parted_mod_b() returns trigger as $$
  874. BEGIN
  875. NEW.b = 2; -- This is changing partition key column.
  876. return NEW;
  877. END $$ LANGUAGE plpgsql;
  878. CREATE TRIGGER parted_mod_b before update on sub_part1
  879. for each row execute procedure func_parted_mod_b();
  880. SELECT tableoid::regclass::text, * FROM list_parted ORDER BY 1, 2, 3, 4;
  881. tableoid | a | b | c
  882. ------------+---+----+----
  883. list_part1 | 2 | 52 | 50
  884. list_part1 | 3 | 6 | 60
  885. sub_part1 | 1 | 1 | 60
  886. sub_part2 | 1 | 2 | 10
  887. (4 rows)
  888. -- This should do the tuple routing even though there is no explicit
  889. -- partition-key update, because there is a trigger on sub_part1.
  890. UPDATE list_parted set c = 70 WHERE b = 1;
  891. SELECT tableoid::regclass::text, * FROM list_parted ORDER BY 1, 2, 3, 4;
  892. tableoid | a | b | c
  893. ------------+---+----+----
  894. list_part1 | 2 | 52 | 50
  895. list_part1 | 3 | 6 | 60
  896. sub_part2 | 1 | 2 | 10
  897. sub_part2 | 1 | 2 | 70
  898. (4 rows)
  899. DROP TRIGGER parted_mod_b ON sub_part1;
  900. -- If BR DELETE trigger prevented DELETE from happening, we should also skip
  901. -- the INSERT if that delete is part of UPDATE=>DELETE+INSERT.
  902. CREATE OR REPLACE FUNCTION func_parted_mod_b() returns trigger as $$
  903. BEGIN
  904. raise notice 'Trigger: Got OLD row %, but returning NULL', OLD;
  905. return NULL;
  906. END $$ LANGUAGE plpgsql;
  907. CREATE TRIGGER trig_skip_delete before delete on sub_part2
  908. for each row execute procedure func_parted_mod_b();
  909. UPDATE list_parted set b = 1 WHERE c = 70;
  910. NOTICE: Trigger: Got OLD row (2,70,1), but returning NULL
  911. SELECT tableoid::regclass::text, * FROM list_parted ORDER BY 1, 2, 3, 4;
  912. tableoid | a | b | c
  913. ------------+---+----+----
  914. list_part1 | 2 | 52 | 50
  915. list_part1 | 3 | 6 | 60
  916. sub_part2 | 1 | 2 | 10
  917. sub_part2 | 1 | 2 | 70
  918. (4 rows)
  919. -- Drop the trigger. Now the row should be moved.
  920. DROP TRIGGER trig_skip_delete ON sub_part2;
  921. UPDATE list_parted set b = 1 WHERE c = 70;
  922. SELECT tableoid::regclass::text, * FROM list_parted ORDER BY 1, 2, 3, 4;
  923. tableoid | a | b | c
  924. ------------+---+----+----
  925. list_part1 | 2 | 52 | 50
  926. list_part1 | 3 | 6 | 60
  927. sub_part1 | 1 | 1 | 70
  928. sub_part2 | 1 | 2 | 10
  929. (4 rows)
  930. DROP FUNCTION func_parted_mod_b();
  931. -- UPDATE partition-key with FROM clause. If join produces multiple output
  932. -- rows for the same row to be modified, we should tuple-route the row only
  933. -- once. There should not be any rows inserted.
  934. CREATE TABLE non_parted (id int);
  935. INSERT into non_parted VALUES (1), (1), (1), (2), (2), (2), (3), (3), (3);
  936. UPDATE list_parted t1 set a = 2 FROM non_parted t2 WHERE t1.a = t2.id and a = 1;
  937. SELECT tableoid::regclass::text, * FROM list_parted ORDER BY 1, 2, 3, 4;
  938. tableoid | a | b | c
  939. ------------+---+----+----
  940. list_part1 | 2 | 1 | 70
  941. list_part1 | 2 | 2 | 10
  942. list_part1 | 2 | 52 | 50
  943. list_part1 | 3 | 6 | 60
  944. (4 rows)
  945. DROP TABLE non_parted;
  946. -- Cleanup: list_parted no longer needed.
  947. DROP TABLE list_parted;
  948. -- create custom operator class and hash function, for the same reason
  949. -- explained in alter_table.sql
  950. create or replace function dummy_hashint4(a int4, seed int8) returns int8 as
  951. $$ begin return (a + seed); end; $$ language 'plpgsql' immutable;
  952. create operator class custom_opclass for type int4 using hash as
  953. operator 1 = , function 2 dummy_hashint4(int4, int8);
  954. create table hash_parted (
  955. a int,
  956. b int
  957. ) partition by hash (a custom_opclass, b custom_opclass);
  958. create table hpart1 partition of hash_parted for values with (modulus 2, remainder 1);
  959. create table hpart2 partition of hash_parted for values with (modulus 4, remainder 2);
  960. create table hpart3 partition of hash_parted for values with (modulus 8, remainder 0);
  961. create table hpart4 partition of hash_parted for values with (modulus 8, remainder 4);
  962. insert into hpart1 values (1, 1);
  963. insert into hpart2 values (2, 5);
  964. insert into hpart4 values (3, 4);
  965. -- fail
  966. update hpart1 set a = 3, b=4 where a = 1;
  967. ERROR: new row for relation "hpart1" violates partition constraint
  968. DETAIL: Failing row contains (3, 4).
  969. -- ok, row movement
  970. update hash_parted set b = b - 1 where b = 1;
  971. -- ok
  972. update hash_parted set b = b + 8 where b = 1;
  973. -- cleanup
  974. drop table hash_parted;
  975. drop operator class custom_opclass using hash;
  976. drop function dummy_hashint4(a int4, seed int8);