forked from timescale/timescaledb
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathskip_scan.out
More file actions
2955 lines (2940 loc) · 201 KB
/
Copy pathskip_scan.out
File metadata and controls
2955 lines (2940 loc) · 201 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
-- This file and its contents are licensed under the Timescale License.
-- Please see the included NOTICE for copyright information and
-- LICENSE-TIMESCALE for a copy of the license.
-- need superuser to adjust statistics in load script
\c :TEST_DBNAME :ROLE_SUPERUSER
\set TEST_BASE_NAME skip_scan
SELECT format('include/%s_load.sql', :'TEST_BASE_NAME') AS "TEST_LOAD_NAME",
format('include/%s_multi_load.sql', :'TEST_BASE_NAME') AS "TEST_MULTI_LOAD_NAME",
format('include/%s_query.sql', :'TEST_BASE_NAME') AS "TEST_QUERY_NAME",
format('%s/results/%s_results_unoptimized.out', :'TEST_OUTPUT_DIR', :'TEST_BASE_NAME') AS "TEST_RESULTS_UNOPTIMIZED",
format('%s/results/%s_results_optimized.out', :'TEST_OUTPUT_DIR', :'TEST_BASE_NAME') AS "TEST_RESULTS_OPTIMIZED" \gset
SELECT format('\! diff -u --label "Unoptimized results" --label "Optimized results" %s %s', :'TEST_RESULTS_UNOPTIMIZED', :'TEST_RESULTS_OPTIMIZED') AS "DIFF_CMD" \gset
\ir :TEST_LOAD_NAME
-- This file and its contents are licensed under the Timescale License.
-- Please see the included NOTICE for copyright information and
-- LICENSE-TIMESCALE for a copy of the license.
CREATE TABLE skip_scan(time int, dev int, dev_name text, val int);
-- Turn off autovacuum to not trigger new vacuums that restores the
-- adjusted statistics
ALTER TABLE skip_scan SET (autovacuum_enabled = off);
INSERT INTO skip_scan SELECT t, d, 'device_' || d::text, t*d FROM generate_series(1, 1000) t, generate_series(1, 10) d;
INSERT INTO skip_scan VALUES (NULL, 0, -1, NULL), (0, NULL, -1, NULL);
INSERT INTO skip_scan(time,dev,dev_name,val) SELECT t, NULL, NULL, NULL FROM generate_series(0, 999, 50) t;
ANALYZE skip_scan;
CREATE TABLE skip_scan_nulls(time int);
-- Turn off autovacuum to not trigger new vacuums that restores the
-- adjusted statistics
ALTER TABLE skip_scan_nulls SET (autovacuum_enabled = off);
CREATE INDEX ON skip_scan_nulls(time);
INSERT INTO skip_scan_nulls SELECT NULL FROM generate_series(1,100);
ANALYZE skip_scan_nulls;
-- create hypertable with different physical layouts in the chunks
CREATE TABLE skip_scan_ht(f1 int, f2 int, f3 int, time int NOT NULL, dev int, dev_name text, val int);
-- Turn off autovacuum to not trigger new vacuums that restores the
-- adjusted statistics
ALTER TABLE skip_scan_ht SET (autovacuum_enabled = off);
SELECT create_hypertable('skip_scan_ht', 'time', chunk_time_interval => 250, create_default_indexes => false);
create_hypertable
---------------------------
(1,public,skip_scan_ht,t)
INSERT INTO skip_scan_ht(time,dev,dev_name,val) SELECT t, d, 'device_' || d::text, random() FROM generate_series(0, 249) t, generate_series(1, 10) d;
ALTER TABLE skip_scan_ht DROP COLUMN f1;
INSERT INTO skip_scan_ht(time,dev,dev_name,val) SELECT t, d, 'device_' || d::text, random() FROM generate_series(250, 499) t, generate_series(1, 10) d;
ALTER TABLE skip_scan_ht DROP COLUMN f2;
INSERT INTO skip_scan_ht(time,dev,dev_name,val) SELECT t, d, 'device_' || d::text, random() FROM generate_series(500, 749) t, generate_series(1, 10) d;
ALTER TABLE skip_scan_ht DROP COLUMN f3;
INSERT INTO skip_scan_ht(time,dev,dev_name,val) SELECT t, d, 'device_' || d::text, random() FROM generate_series(750, 999) t, generate_series(1, 10) d;
INSERT INTO skip_scan_ht(time,dev,dev_name,val) SELECT t, NULL, NULL, NULL FROM generate_series(0, 999, 50) t;
ANALYZE skip_scan_ht;
ALTER TABLE skip_scan_ht SET (timescaledb.compress,timescaledb.compress_orderby='time desc', timescaledb.compress_segmentby='dev');
-- create compressed hypertable with different physical layouts in the chunks
CREATE TABLE skip_scan_htc(f1 int, f2 int, f3 int, time int NOT NULL, dev int, dev_name text, val int);
-- Turn off autovacuum to not trigger new vacuums that restores the
-- adjusted statistics
ALTER TABLE skip_scan_htc SET (autovacuum_enabled = off);
SELECT create_hypertable('skip_scan_htc', 'time', chunk_time_interval => 250, create_default_indexes => false);
create_hypertable
----------------------------
(3,public,skip_scan_htc,t)
ALTER TABLE skip_scan_htc SET (timescaledb.compress, timescaledb.compress_orderby='time desc', timescaledb.compress_segmentby='dev');
INSERT INTO skip_scan_htc(time,dev,dev_name,val) SELECT t, d, 'device_' || d::text, random() FROM generate_series(0, 249) t, generate_series(1, 10) d;
ALTER TABLE skip_scan_htc DROP COLUMN f1;
INSERT INTO skip_scan_htc(time,dev,dev_name,val) SELECT t, d, 'device_' || d::text, random() FROM generate_series(250, 499) t, generate_series(1, 10) d;
ALTER TABLE skip_scan_htc DROP COLUMN f2;
INSERT INTO skip_scan_htc(time,dev,dev_name,val) SELECT t, d, 'device_' || d::text, random() FROM generate_series(500, 749) t, generate_series(1, 10) d;
ALTER TABLE skip_scan_htc DROP COLUMN f3;
INSERT INTO skip_scan_htc(time,dev,dev_name,val) SELECT t, d, 'device_' || d::text, random() FROM generate_series(750, 999) t, generate_series(1, 10) d;
INSERT INTO skip_scan_htc(time,dev,dev_name,val) SELECT t, NULL, NULL, NULL FROM generate_series(0, 999, 50) t;
ANALYZE skip_scan_htc;
CREATE TABLE skip_scan_insert(time int, dev int, dev_name text, val int, query text);
CREATE OR REPLACE FUNCTION int_func_immutable() RETURNS int LANGUAGE SQL IMMUTABLE SECURITY DEFINER AS $$SELECT 1; $$;
CREATE OR REPLACE FUNCTION int_func_stable() RETURNS int LANGUAGE SQL STABLE SECURITY DEFINER AS $$ SELECT 2; $$;
CREATE OR REPLACE FUNCTION int_func_volatile() RETURNS int LANGUAGE SQL VOLATILE SECURITY DEFINER AS $$ SELECT 3; $$;
CREATE OR REPLACE FUNCTION inta_func_immutable() RETURNS int[] LANGUAGE SQL IMMUTABLE SECURITY DEFINER AS $$ SELECT ARRAY[1,2,3]; $$;
CREATE OR REPLACE FUNCTION inta_func_stable() RETURNS int[] LANGUAGE SQL STABLE SECURITY DEFINER AS $$ SELECT ARRAY[2,3,4]; $$;
CREATE OR REPLACE FUNCTION inta_func_volatile() RETURNS int[] LANGUAGE SQL VOLATILE SECURITY DEFINER AS $$ SELECT ARRAY[3,4,5]; $$;
-- adjust statistics so we get skipscan plans
UPDATE pg_statistic SET stadistinct=1, stanullfrac=0.5 WHERE starelid='skip_scan'::regclass;
UPDATE pg_statistic SET stadistinct=1, stanullfrac=0.5 WHERE starelid='skip_scan_nulls'::regclass;
UPDATE pg_statistic SET stadistinct=1, stanullfrac=0.5 WHERE starelid='skip_scan_ht'::regclass;
UPDATE pg_statistic SET stadistinct=1, stanullfrac=0.5 WHERE starelid IN (select inhrelid from pg_inherits where inhparent='skip_scan_ht'::regclass);
UPDATE pg_statistic SET stadistinct=1, stanullfrac=0.5 WHERE starelid='skip_scan_htc'::regclass;
UPDATE pg_statistic SET stadistinct=1, stanullfrac=0.5 WHERE starelid IN (select inhrelid from pg_inherits where inhparent='skip_scan_htc'::regclass);
-- create compressed hypertable with different physical layouts in the compressed chunks
CREATE TABLE skip_scan_htcl(f1 int, f2 int, f3 int, time int NOT NULL, dev int, dev_name text, val int);
-- Turn off autovacuum to not trigger new vacuums that restores the
-- adjusted statistics
ALTER TABLE skip_scan_htcl SET (autovacuum_enabled = off);
SELECT create_hypertable('skip_scan_htcl', 'time', chunk_time_interval => 250, create_default_indexes => false);
create_hypertable
-----------------------------
(5,public,skip_scan_htcl,t)
ALTER TABLE skip_scan_htcl SET (timescaledb.compress, timescaledb.compress_orderby='time desc', timescaledb.compress_segmentby='dev');
INSERT INTO skip_scan_htcl(time,dev,dev_name,val) SELECT t, d, 'device_' || d::text, random() FROM generate_series(0, 249) t, generate_series(1, 10) d;
-- Make sure 1st compressed chunk has columns which will be dropped later, it also doesn't have NULLs
SELECT compress_chunk(ch) FROM show_chunks('skip_scan_htcl') ch;
compress_chunk
----------------------------------------
_timescaledb_internal._hyper_5_9_chunk
ALTER TABLE skip_scan_htcl DROP COLUMN f1;
INSERT INTO skip_scan_htcl(time,dev,dev_name,val) SELECT t, d, 'device_' || d::text, random() FROM generate_series(250, 499) t, generate_series(1, 10) d;
ALTER TABLE skip_scan_htcl DROP COLUMN f2;
INSERT INTO skip_scan_htcl(time,dev,dev_name,val) SELECT t, d, 'device_' || d::text, random() FROM generate_series(500, 749) t, generate_series(1, 10) d;
ALTER TABLE skip_scan_htcl DROP COLUMN f3;
INSERT INTO skip_scan_htcl(time,dev,dev_name,val) SELECT t, d, 'device_' || d::text, random() FROM generate_series(750, 999) t, generate_series(1, 10) d;
INSERT INTO skip_scan_htcl(time,dev,dev_name,val) SELECT t, NULL, NULL, NULL FROM generate_series(0, 999, 50) t;
-- The rest of the compressed chunks do not have dropped columns
-- compressed chunks #2 and #3 have attnos out of sync with uncompressed chunks
SELECT compress_chunk(ch) FROM show_chunks('skip_scan_htcl') ch order by 1 desc limit 3;
compress_chunk
-----------------------------------------
_timescaledb_internal._hyper_5_13_chunk
_timescaledb_internal._hyper_5_12_chunk
_timescaledb_internal._hyper_5_11_chunk
ANALYZE skip_scan_htcl;
-- adjust statistics so we get skipscan plans
UPDATE pg_statistic SET stadistinct=1, stanullfrac=0.5 WHERE starelid='skip_scan_htcl'::regclass;
UPDATE pg_statistic SET stadistinct=1, stanullfrac=0.5 WHERE starelid IN (select inhrelid from pg_inherits where inhparent='skip_scan_htcl'::regclass);
-- run tests on normal table and diff results
\set TABLE skip_scan
\set PREFIX ''
\o :TEST_RESULTS_OPTIMIZED
\ir :TEST_QUERY_NAME
-- This file and its contents are licensed under the Timescale License.
-- Please see the included NOTICE for copyright information and
-- LICENSE-TIMESCALE for a copy of the license.
-- canary for result diff
SELECT current_setting('timescaledb.enable_skipscan') AS enable_skipscan;
-- test different index configurations
-- no index so we cant do SkipScan
:PREFIX SELECT DISTINCT dev FROM :TABLE ORDER BY dev;
-- NULLS LAST index on dev
CREATE INDEX skip_scan_idx_dev_nulls_last ON :TABLE(dev);
:PREFIX SELECT DISTINCT dev FROM :TABLE ORDER BY dev;
:PREFIX SELECT DISTINCT dev FROM :TABLE ORDER BY dev DESC;
:PREFIX SELECT DISTINCT ON (dev) dev FROM :TABLE;
-- Test not-NULL mode
:PREFIX SELECT DISTINCT dev FROM :TABLE WHERE dev IS NOT NULL ORDER BY dev;
DROP INDEX skip_scan_idx_dev_nulls_last;
-- NULLS FIRST index on dev
CREATE INDEX skip_scan_idx_dev_nulls_first ON :TABLE(dev NULLS FIRST);
:PREFIX SELECT DISTINCT dev FROM :TABLE ORDER BY dev NULLS FIRST;
:PREFIX SELECT DISTINCT ON (dev) dev FROM :TABLE ORDER BY dev NULLS FIRST;
-- Test not-NULL mode
:PREFIX SELECT DISTINCT dev FROM :TABLE WHERE dev IS NOT NULL ORDER BY dev NULLS FIRST;
DROP INDEX skip_scan_idx_dev_nulls_first;
-- multicolumn index with dev as leading column
CREATE INDEX skip_scan_idx_dev_time_idx ON :TABLE(dev, time);
:PREFIX SELECT DISTINCT dev FROM :TABLE ORDER BY dev;
:PREFIX SELECT DISTINCT ON (dev) dev FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev) dev FROM :TABLE ORDER BY dev DESC, time DESC;
-- Test not-NULL mode
:PREFIX SELECT DISTINCT dev FROM :TABLE WHERE dev IS NOT NULL ORDER BY dev;
DROP INDEX skip_scan_idx_dev_time_idx;
-- multicolumn index with dev as non-leading column
CREATE INDEX skip_scan_idx_time_dev_idx ON :TABLE(time, dev);
:PREFIX SELECT DISTINCT dev FROM :TABLE WHERE time = 100 ORDER BY dev;
:PREFIX SELECT DISTINCT ON (dev) dev FROM :TABLE WHERE time = 100;
-- Test not-NULL mode
:PREFIX SELECT DISTINCT dev FROM :TABLE WHERE time = 100 AND dev IS NOT NULL ORDER BY dev;
DROP INDEX skip_scan_idx_time_dev_idx;
-- hash index is not ordered so can't use skipscan
CREATE INDEX skip_scan_idx_hash ON :TABLE USING hash(dev_name);
:PREFIX SELECT DISTINCT dev_name FROM :TABLE WHERE dev_name IN ('device_1','device_2') ORDER BY dev_name;
DROP INDEX skip_scan_idx_hash;
-- expression indexes
-- currently not supported by skipscan
CREATE INDEX skip_scan_expr_idx ON :TABLE((dev % 3));
:PREFIX SELECT DISTINCT dev%3 FROM :TABLE ORDER BY dev%3;
:PREFIX SELECT DISTINCT ON (dev%3) dev FROM :TABLE ORDER BY dev%3;
DROP INDEX skip_scan_expr_idx;
CREATE INDEX ON :TABLE(dev_name);
-- Tests for #8107: accounting for scanning most of the input due to non-index quals
-- Non-index qual, no tuples match: SeqScan preferred
:PREFIX SELECT DISTINCT dev_name FROM :TABLE WHERE val < 0 ORDER BY 1;
-- Highly selective non-index qual, have to scan many tuples to match: SeqScan preferred
:PREFIX SELECT DISTINCT dev_name FROM :TABLE WHERE time = 100 ORDER BY 1;
-- Same but a combo of non-index quals
:PREFIX SELECT DISTINCT dev_name FROM :TABLE WHERE time = 100 and dev = 1 ORDER BY 1;
-- Highly selective index qual: less tuples to scan for low-selective non-index qual, can choose SkipScan
:PREFIX SELECT DISTINCT dev_name FROM :TABLE WHERE dev_name IS NULL and dev = 1 ORDER BY 1;
CREATE INDEX ON :TABLE(dev);
-- RowCompareExpr where some args are not in the index:
-- we extract lossy "dev >=5" index condition which is strict and doesn't allow NULLs
SET timescaledb.debug_skip_scan_info TO true;
:PREFIX SELECT DISTINCT ON (dev) * FROM :TABLE WHERE (dev, time) > (5,100);
psql:include/skip_scan_query.sql:74: INFO: SkipScan used on skip_scan_dev_idx(dev NOT NULL)
RESET timescaledb.debug_skip_scan_info;
CREATE INDEX ON :TABLE(dev, time);
CREATE INDEX ON :TABLE(time,dev);
CREATE INDEX ON :TABLE(time,dev,val);
\qecho basic DISTINCT queries on :TABLE
:PREFIX SELECT DISTINCT dev, 'q1_1' FROM :TABLE ORDER BY dev;
:PREFIX SELECT DISTINCT dev_name, 'q1_2' FROM :TABLE ORDER BY dev_name;
:PREFIX SELECT DISTINCT dev, 'q1_3', NULL FROM :TABLE ORDER BY dev;
\qecho stable expression in targetlist on :TABLE
:PREFIX SELECT DISTINCT dev, 'q1_4', length(md5(now()::text)) FROM :TABLE ORDER BY dev;
:PREFIX SELECT DISTINCT dev_name, 'q1_5', length(md5(now()::text)) FROM :TABLE ORDER BY dev_name;
-- volatile expression in targetlist
:PREFIX SELECT DISTINCT dev, 'q1_6', length(md5(random()::text)) FROM :TABLE ORDER BY dev;
:PREFIX SELECT DISTINCT dev_name, 'q1_7', length(md5(random()::text)) FROM :TABLE ORDER BY dev_name;
-- queries without skipscan because distinct is not limited to specific column
:PREFIX SELECT DISTINCT * FROM :TABLE ORDER BY dev;
:PREFIX SELECT DISTINCT *, 'q1_9' FROM :TABLE ORDER BY dev;
:PREFIX SELECT DISTINCT dev, time, 'q1_10' FROM :TABLE ORDER BY dev;
:PREFIX SELECT DISTINCT dev, NULL, 'q1_11' FROM :TABLE ORDER BY dev;
-- distinct on expressions not supported
:PREFIX SELECT DISTINCT time_bucket(10,time), 'q1_12' FROM :TABLE;
:PREFIX SELECT DISTINCT length(dev_name), 'q1_13' FROM :TABLE;
:PREFIX SELECT DISTINCT 3*time, 'q1_14' FROM :TABLE;
:PREFIX SELECT DISTINCT 'Device ' || dev_name FROM :TABLE;
-- DISTINCT ON queries
:PREFIX SELECT DISTINCT ON (dev) dev FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev) dev, 'q2_2' FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev) dev, 'q2_3', NULL FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev) dev, 'q2_4', length(md5(now()::text)) FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev) dev, 'q2_5', length(md5(random()::text)) FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev) * FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev) *, 'q2_7' FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev) dev, time, 'q2_8' FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev) dev, NULL, 'q2_9' FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev) time, 'q2_10' FROM :TABLE ORDER by dev, time;
:PREFIX SELECT DISTINCT ON (dev) dev, tableoid::regclass, 'q2_11' FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev) dev, int_func_immutable(), 'q2_12' FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev) dev, int_func_stable(), 'q2_13' FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev) dev, int_func_volatile(), 'q2_14' FROM :TABLE;
-- DISTINCT ON queries on TEXT column
:PREFIX SELECT DISTINCT ON (dev_name) dev_name FROM :TABLE;
-- Test not-NULL mode
:PREFIX SELECT DISTINCT ON (dev_name) dev_name FROM :TABLE WHERE dev_name IS NOT NULL;
:PREFIX SELECT DISTINCT ON (dev_name) dev_name, 'q3_2' FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev_name) dev_name, 'q3_3', NULL FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev_name) dev_name, 'q3_4', length(md5(now()::text)) FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev_name) dev_name, 'q3_5', length(md5(random()::text)) FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev_name) * FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev_name) *, 'q3_7' FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev_name) dev_name, time, 'q3_8' FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev_name) dev_name, NULL, 'q3_9' FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev_name) time, 'q3_10' FROM :TABLE ORDER by dev_name, time;
:PREFIX SELECT DISTINCT ON (dev_name) dev_name, tableoid::regclass, 'q3_11' FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev_name::varchar) dev_name::varchar FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev_name) dev, int_func_immutable(), 'q3_13' FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev_name) dev, int_func_stable(), 'q3_14' FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev_name) dev, int_func_volatile(), 'q3_15' FROM :TABLE;
\qecho DISTINCT with wholerow var
:PREFIX SELECT DISTINCT ON (dev) :TABLE FROM :TABLE;
-- should not use SkipScan since we only support SkipScan on single-column distinct
:PREFIX SELECT DISTINCT :TABLE FROM :TABLE;
\qecho LIMIT queries on :TABLE
:PREFIX SELECT DISTINCT ON (dev) dev FROM :TABLE LIMIT 3;
:PREFIX SELECT DISTINCT ON (dev) dev FROM :TABLE ORDER BY dev DESC, time DESC LIMIT 3;
:PREFIX SELECT DISTINCT ON (dev) dev FROM :TABLE ORDER BY dev, time LIMIT 3;
\qecho range queries on :TABLE
:PREFIX SELECT DISTINCT ON (dev) dev FROM :TABLE WHERE time BETWEEN 100 AND 300;
:PREFIX SELECT DISTINCT ON (dev) dev FROM :TABLE WHERE time < 200;
:PREFIX SELECT DISTINCT ON (dev) dev FROM :TABLE WHERE time > 800;
\qecho ordered append on :TABLE
:PREFIX SELECT * FROM :TABLE ORDER BY time;
:PREFIX SELECT DISTINCT ON (time) time FROM :TABLE WHERE time BETWEEN 0 AND 5000;
\qecho SUBSELECTS on :TABLE
:PREFIX SELECT time, dev, val, 'q4_1' FROM (SELECT DISTINCT ON (dev) * FROM :TABLE) a;
:PREFIX SELECT NULL, dev, NULL, 'q4_3' FROM (SELECT DISTINCT ON (dev) dev FROM :TABLE) a;
:PREFIX SELECT time, dev, NULL, 'q4_4' FROM (SELECT DISTINCT ON (dev) dev, time FROM :TABLE) a;
\qecho ORDER BY
:PREFIX SELECT time, dev, val, 'q5_1' FROM (SELECT DISTINCT ON (dev) * FROM :TABLE ORDER BY dev, time) a;
:PREFIX SELECT time, dev, val, 'q5_2' FROM (SELECT DISTINCT ON (dev) * FROM :TABLE ORDER BY dev DESC, time DESC) a;
\qecho WHERE CLAUSES
:PREFIX SELECT time, dev, val, 'q6_1' FROM (SELECT DISTINCT ON (dev) * FROM :TABLE WHERE dev > 5) a;
:PREFIX SELECT time, dev, val, 'q6_2' FROM (SELECT DISTINCT ON (dev) * FROM :TABLE WHERE time > 5) a;
:PREFIX SELECT time, dev, val, 'q6_3' FROM (SELECT DISTINCT ON (dev) * FROM :TABLE) a WHERE dev > 5;
:PREFIX SELECT time, dev, val, 'q6_4' FROM (SELECT DISTINCT ON (dev) * FROM :TABLE) a WHERE time > 5;
--\qecho immutable func in WHERE clause on :TABLE
:PREFIX SELECT DISTINCT ON (dev) *, 'q6_5' FROM :TABLE WHERE dev > int_func_immutable();
--\qecho stable func in WHERE clause on :TABLE
:PREFIX SELECT DISTINCT ON (dev) *, 'q6_6' FROM :TABLE WHERE dev > int_func_stable();
--\qecho volatile func in WHERE clause on :TABLE
:PREFIX SELECT DISTINCT ON (dev) * FROM :TABLE WHERE dev > int_func_volatile();
:PREFIX SELECT DISTINCT ON (dev) * FROM :TABLE WHERE dev = ANY(inta_func_immutable());
:PREFIX SELECT DISTINCT ON (dev) * FROM :TABLE WHERE dev = ANY(inta_func_stable());
:PREFIX SELECT DISTINCT ON (dev) * FROM :TABLE WHERE dev = ANY(inta_func_volatile());
-- RowCompareExpr
:PREFIX SELECT DISTINCT ON (dev) * FROM :TABLE WHERE (dev, time) > (5,100);
-- always false expr similar to our initial skip qual
:PREFIX SELECT DISTINCT ON (dev) * FROM :TABLE WHERE dev > NULL;
-- no tuples matching
:PREFIX SELECT DISTINCT ON (dev) * FROM :TABLE WHERE dev > 20;
-- multiple constraints in WHERE clause
:PREFIX SELECT DISTINCT ON (dev) dev,time FROM :TABLE WHERE dev > 5 AND time = 100;
:PREFIX SELECT DISTINCT ON (dev) dev,time FROM :TABLE WHERE dev > 5 AND time > 200;
:PREFIX SELECT DISTINCT ON (dev) dev,time FROM :TABLE WHERE dev >= 5 AND dev < 7 AND dev >= 2;
:PREFIX SELECT DISTINCT ON (dev) dev,time,val FROM :TABLE WHERE time > 100 AND time < 200 AND val > 10 AND val < 10000 AND dev > 2 AND dev < 7 ORDER BY dev,time;
:PREFIX SELECT DISTINCT ON (dev) dev FROM :TABLE WHERE dev IS NULL;
:PREFIX SELECT DISTINCT ON (dev_name) dev_name FROM :TABLE WHERE dev_name IS NULL;
-- test constants in ORDER BY
:PREFIX SELECT DISTINCT ON (dev) * FROM :TABLE WHERE dev = 1 ORDER BY dev, time DESC;
:PREFIX SELECT DISTINCT dev, time FROM :TABLE WHERE dev = 1 and time = 100 ORDER BY dev, time DESC;
:PREFIX SELECT DISTINCT dev_name::varchar FROM :TABLE WHERE dev_name::varchar = 'device_1' ORDER BY 1;
-- test distinct PathKey with sortref = 0 in PG15 due to FALSE filter not pushed into relation (should not crash in PG15)
:PREFIX SELECT DISTINCT sq.dev FROM (SELECT dev FROM :TABLE) sq JOIN :TABLE ref ON (sq.dev = ref.dev) WHERE 1 > 2;
-- test distinct PathKey with sortref not aligning with targets sortrefs in PG15 due to Window function
:PREFIX SELECT DISTINCT
case when false then c10 else c10 end as c0,
max(c8) over (partition by c10 order by c6) as c1
FROM (SELECT time as c6, val as c8, dev as c10 FROM :TABLE WHERE false) subq WHERE false;
-- test multiple distincts with all but one pinned: #7998
:PREFIX SELECT DISTINCT dev, dev FROM :TABLE ORDER BY 1;
:PREFIX SELECT DISTINCT dev, time FROM :TABLE WHERE time = 100 ORDER BY 1;
:PREFIX SELECT DISTINCT dev, time, val FROM :TABLE WHERE time = 100 and val = 100 ORDER BY 1;
:PREFIX SELECT DISTINCT ON (dev, time) * FROM :TABLE WHERE time = 100 ORDER BY dev;
:PREFIX SELECT DISTINCT ON (dev, time) dev, time FROM :TABLE WHERE dev = 1 AND time < 20 ORDER BY dev, time;
-- CTE
:PREFIX WITH devices AS (
SELECT DISTINCT ON (dev) dev FROM :TABLE
)
SELECT * FROM devices;
:PREFIX WITH devices AS (
SELECT DISTINCT dev FROM :TABLE
)
SELECT * FROM devices ORDER BY dev;
-- prepared statements
PREPARE prep AS SELECT DISTINCT ON (dev_name) dev_name FROM :TABLE;
:PREFIX EXECUTE prep;
:PREFIX EXECUTE prep;
:PREFIX EXECUTE prep;
DEALLOCATE prep;
-- ReScan tests
:PREFIX SELECT time, dev, val, 'q7_1' FROM (SELECT DISTINCT ON (dev) * FROM (
VALUES (1), (2)) a(v),
LATERAL (SELECT * FROM :TABLE WHERE time != a.v) b) a;
:PREFIX SELECT time, dev, val, 'q7_2' FROM (SELECT * FROM (
VALUES (1), (2)) a(v),
LATERAL (SELECT DISTINCT ON (dev) * FROM :TABLE WHERE dev != a.v) b) a;
-- RuntimeKeys
:PREFIX SELECT time, dev, val, 'q8_1' FROM (SELECT * FROM (
VALUES (1), (2)) a(v),
LATERAL (SELECT DISTINCT ON (dev) * FROM :TABLE WHERE dev >= a.v) b) c;
-- Emulate multi-column DISTINCT using multiple SkipSkans
:PREFIX SELECT time, dev, val, 'q9_1' FROM (SELECT b.* FROM
(SELECT DISTINCT ON (dev) dev FROM :TABLE) a,
LATERAL (SELECT DISTINCT ON (time) * FROM :TABLE WHERE dev = a.dev) b) c;
:PREFIX SELECT time, dev, NULL, 'q9_2' FROM (SELECT b.* FROM
(SELECT DISTINCT ON (dev) dev FROM :TABLE) a,
LATERAL (SELECT DISTINCT ON (time) dev, time FROM :TABLE WHERE dev = a.dev) b) c;
-- Test that the multi-column DISTINCT emulation is equivalent to a real multi-column DISTINCT
:PREFIX SELECT * FROM
(SELECT DISTINCT ON (dev) dev FROM :TABLE) a,
LATERAL (SELECT DISTINCT ON (time) dev, time FROM :TABLE WHERE dev = a.dev) b;
:PREFIX SELECT DISTINCT ON (dev, time) dev, time FROM :TABLE WHERE coalesce(dev, -1) >= 0;
:PREFIX SELECT DISTINCT ON (dev, time) dev, time FROM :TABLE WHERE coalesce(dev, -1) >= 0
UNION SELECT b.* FROM
(SELECT DISTINCT ON (dev) dev FROM :TABLE) a,
LATERAL (SELECT DISTINCT ON (time) dev, time FROM :TABLE WHERE dev = a.dev) b;
-- SkipScan into INSERT
:PREFIX INSERT INTO skip_scan_insert(time, dev, val, query) SELECT time, dev, val, 'q10_1' FROM (SELECT DISTINCT ON (dev) * FROM :TABLE) a;
-- parallel query
SELECT set_config(CASE WHEN current_setting('server_version_num')::int < 160000 THEN 'force_parallel_mode' ELSE 'debug_parallel_query' END,'on', false);
:PREFIX SELECT DISTINCT dev FROM :TABLE ORDER BY dev;
SELECT set_config(CASE WHEN current_setting('server_version_num')::int < 160000 THEN 'force_parallel_mode' ELSE 'debug_parallel_query' END,'off', false);
TRUNCATE skip_scan_insert;
-- table with only nulls
:PREFIX SELECT DISTINCT ON (time) time FROM skip_scan_nulls;
-- no tuples in resultset
:PREFIX SELECT DISTINCT ON (time) time FROM skip_scan_nulls WHERE time IS NOT NULL;
-- Test for SDC issue #2976
-- We should use index "btree ("time", dev, dev_name)" where "dev_name" index key is #3 but we have 2 index quals including SkipScan qual
-- We should not drop qual on "dev_name"
CREATE INDEX skip_scan_idx_time_dev_dname ON :TABLE(time,dev,dev_name);
:PREFIX SELECT DISTINCT time FROM :TABLE WHERE dev_name IS NULL ORDER BY 1;
DROP INDEX skip_scan_idx_time_dev_dname;
-- "dev_name" is not a key column: it's not in the index quals and should be a filter
CREATE INDEX skip_scan_idx_time_dev_dname ON :TABLE(time,dev) INCLUDE(dev_name);
:PREFIX SELECT DISTINCT time FROM :TABLE WHERE dev_name IS NULL ORDER BY 1;
DROP INDEX skip_scan_idx_time_dev_dname;
\o
SET timescaledb.enable_skipscan TO false;
\o :TEST_RESULTS_UNOPTIMIZED
\ir :TEST_QUERY_NAME
-- This file and its contents are licensed under the Timescale License.
-- Please see the included NOTICE for copyright information and
-- LICENSE-TIMESCALE for a copy of the license.
-- canary for result diff
SELECT current_setting('timescaledb.enable_skipscan') AS enable_skipscan;
-- test different index configurations
-- no index so we cant do SkipScan
:PREFIX SELECT DISTINCT dev FROM :TABLE ORDER BY dev;
-- NULLS LAST index on dev
CREATE INDEX skip_scan_idx_dev_nulls_last ON :TABLE(dev);
:PREFIX SELECT DISTINCT dev FROM :TABLE ORDER BY dev;
:PREFIX SELECT DISTINCT dev FROM :TABLE ORDER BY dev DESC;
:PREFIX SELECT DISTINCT ON (dev) dev FROM :TABLE;
-- Test not-NULL mode
:PREFIX SELECT DISTINCT dev FROM :TABLE WHERE dev IS NOT NULL ORDER BY dev;
DROP INDEX skip_scan_idx_dev_nulls_last;
-- NULLS FIRST index on dev
CREATE INDEX skip_scan_idx_dev_nulls_first ON :TABLE(dev NULLS FIRST);
:PREFIX SELECT DISTINCT dev FROM :TABLE ORDER BY dev NULLS FIRST;
:PREFIX SELECT DISTINCT ON (dev) dev FROM :TABLE ORDER BY dev NULLS FIRST;
-- Test not-NULL mode
:PREFIX SELECT DISTINCT dev FROM :TABLE WHERE dev IS NOT NULL ORDER BY dev NULLS FIRST;
DROP INDEX skip_scan_idx_dev_nulls_first;
-- multicolumn index with dev as leading column
CREATE INDEX skip_scan_idx_dev_time_idx ON :TABLE(dev, time);
:PREFIX SELECT DISTINCT dev FROM :TABLE ORDER BY dev;
:PREFIX SELECT DISTINCT ON (dev) dev FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev) dev FROM :TABLE ORDER BY dev DESC, time DESC;
-- Test not-NULL mode
:PREFIX SELECT DISTINCT dev FROM :TABLE WHERE dev IS NOT NULL ORDER BY dev;
DROP INDEX skip_scan_idx_dev_time_idx;
-- multicolumn index with dev as non-leading column
CREATE INDEX skip_scan_idx_time_dev_idx ON :TABLE(time, dev);
:PREFIX SELECT DISTINCT dev FROM :TABLE WHERE time = 100 ORDER BY dev;
:PREFIX SELECT DISTINCT ON (dev) dev FROM :TABLE WHERE time = 100;
-- Test not-NULL mode
:PREFIX SELECT DISTINCT dev FROM :TABLE WHERE time = 100 AND dev IS NOT NULL ORDER BY dev;
DROP INDEX skip_scan_idx_time_dev_idx;
-- hash index is not ordered so can't use skipscan
CREATE INDEX skip_scan_idx_hash ON :TABLE USING hash(dev_name);
:PREFIX SELECT DISTINCT dev_name FROM :TABLE WHERE dev_name IN ('device_1','device_2') ORDER BY dev_name;
DROP INDEX skip_scan_idx_hash;
-- expression indexes
-- currently not supported by skipscan
CREATE INDEX skip_scan_expr_idx ON :TABLE((dev % 3));
:PREFIX SELECT DISTINCT dev%3 FROM :TABLE ORDER BY dev%3;
:PREFIX SELECT DISTINCT ON (dev%3) dev FROM :TABLE ORDER BY dev%3;
DROP INDEX skip_scan_expr_idx;
CREATE INDEX ON :TABLE(dev_name);
-- Tests for #8107: accounting for scanning most of the input due to non-index quals
-- Non-index qual, no tuples match: SeqScan preferred
:PREFIX SELECT DISTINCT dev_name FROM :TABLE WHERE val < 0 ORDER BY 1;
-- Highly selective non-index qual, have to scan many tuples to match: SeqScan preferred
:PREFIX SELECT DISTINCT dev_name FROM :TABLE WHERE time = 100 ORDER BY 1;
-- Same but a combo of non-index quals
:PREFIX SELECT DISTINCT dev_name FROM :TABLE WHERE time = 100 and dev = 1 ORDER BY 1;
-- Highly selective index qual: less tuples to scan for low-selective non-index qual, can choose SkipScan
:PREFIX SELECT DISTINCT dev_name FROM :TABLE WHERE dev_name IS NULL and dev = 1 ORDER BY 1;
CREATE INDEX ON :TABLE(dev);
-- RowCompareExpr where some args are not in the index:
-- we extract lossy "dev >=5" index condition which is strict and doesn't allow NULLs
SET timescaledb.debug_skip_scan_info TO true;
:PREFIX SELECT DISTINCT ON (dev) * FROM :TABLE WHERE (dev, time) > (5,100);
RESET timescaledb.debug_skip_scan_info;
CREATE INDEX ON :TABLE(dev, time);
CREATE INDEX ON :TABLE(time,dev);
CREATE INDEX ON :TABLE(time,dev,val);
\qecho basic DISTINCT queries on :TABLE
:PREFIX SELECT DISTINCT dev, 'q1_1' FROM :TABLE ORDER BY dev;
:PREFIX SELECT DISTINCT dev_name, 'q1_2' FROM :TABLE ORDER BY dev_name;
:PREFIX SELECT DISTINCT dev, 'q1_3', NULL FROM :TABLE ORDER BY dev;
\qecho stable expression in targetlist on :TABLE
:PREFIX SELECT DISTINCT dev, 'q1_4', length(md5(now()::text)) FROM :TABLE ORDER BY dev;
:PREFIX SELECT DISTINCT dev_name, 'q1_5', length(md5(now()::text)) FROM :TABLE ORDER BY dev_name;
-- volatile expression in targetlist
:PREFIX SELECT DISTINCT dev, 'q1_6', length(md5(random()::text)) FROM :TABLE ORDER BY dev;
:PREFIX SELECT DISTINCT dev_name, 'q1_7', length(md5(random()::text)) FROM :TABLE ORDER BY dev_name;
-- queries without skipscan because distinct is not limited to specific column
:PREFIX SELECT DISTINCT * FROM :TABLE ORDER BY dev;
:PREFIX SELECT DISTINCT *, 'q1_9' FROM :TABLE ORDER BY dev;
:PREFIX SELECT DISTINCT dev, time, 'q1_10' FROM :TABLE ORDER BY dev;
:PREFIX SELECT DISTINCT dev, NULL, 'q1_11' FROM :TABLE ORDER BY dev;
-- distinct on expressions not supported
:PREFIX SELECT DISTINCT time_bucket(10,time), 'q1_12' FROM :TABLE;
:PREFIX SELECT DISTINCT length(dev_name), 'q1_13' FROM :TABLE;
:PREFIX SELECT DISTINCT 3*time, 'q1_14' FROM :TABLE;
:PREFIX SELECT DISTINCT 'Device ' || dev_name FROM :TABLE;
-- DISTINCT ON queries
:PREFIX SELECT DISTINCT ON (dev) dev FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev) dev, 'q2_2' FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev) dev, 'q2_3', NULL FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev) dev, 'q2_4', length(md5(now()::text)) FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev) dev, 'q2_5', length(md5(random()::text)) FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev) * FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev) *, 'q2_7' FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev) dev, time, 'q2_8' FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev) dev, NULL, 'q2_9' FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev) time, 'q2_10' FROM :TABLE ORDER by dev, time;
:PREFIX SELECT DISTINCT ON (dev) dev, tableoid::regclass, 'q2_11' FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev) dev, int_func_immutable(), 'q2_12' FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev) dev, int_func_stable(), 'q2_13' FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev) dev, int_func_volatile(), 'q2_14' FROM :TABLE;
-- DISTINCT ON queries on TEXT column
:PREFIX SELECT DISTINCT ON (dev_name) dev_name FROM :TABLE;
-- Test not-NULL mode
:PREFIX SELECT DISTINCT ON (dev_name) dev_name FROM :TABLE WHERE dev_name IS NOT NULL;
:PREFIX SELECT DISTINCT ON (dev_name) dev_name, 'q3_2' FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev_name) dev_name, 'q3_3', NULL FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev_name) dev_name, 'q3_4', length(md5(now()::text)) FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev_name) dev_name, 'q3_5', length(md5(random()::text)) FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev_name) * FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev_name) *, 'q3_7' FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev_name) dev_name, time, 'q3_8' FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev_name) dev_name, NULL, 'q3_9' FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev_name) time, 'q3_10' FROM :TABLE ORDER by dev_name, time;
:PREFIX SELECT DISTINCT ON (dev_name) dev_name, tableoid::regclass, 'q3_11' FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev_name::varchar) dev_name::varchar FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev_name) dev, int_func_immutable(), 'q3_13' FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev_name) dev, int_func_stable(), 'q3_14' FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev_name) dev, int_func_volatile(), 'q3_15' FROM :TABLE;
\qecho DISTINCT with wholerow var
:PREFIX SELECT DISTINCT ON (dev) :TABLE FROM :TABLE;
-- should not use SkipScan since we only support SkipScan on single-column distinct
:PREFIX SELECT DISTINCT :TABLE FROM :TABLE;
\qecho LIMIT queries on :TABLE
:PREFIX SELECT DISTINCT ON (dev) dev FROM :TABLE LIMIT 3;
:PREFIX SELECT DISTINCT ON (dev) dev FROM :TABLE ORDER BY dev DESC, time DESC LIMIT 3;
:PREFIX SELECT DISTINCT ON (dev) dev FROM :TABLE ORDER BY dev, time LIMIT 3;
\qecho range queries on :TABLE
:PREFIX SELECT DISTINCT ON (dev) dev FROM :TABLE WHERE time BETWEEN 100 AND 300;
:PREFIX SELECT DISTINCT ON (dev) dev FROM :TABLE WHERE time < 200;
:PREFIX SELECT DISTINCT ON (dev) dev FROM :TABLE WHERE time > 800;
\qecho ordered append on :TABLE
:PREFIX SELECT * FROM :TABLE ORDER BY time;
:PREFIX SELECT DISTINCT ON (time) time FROM :TABLE WHERE time BETWEEN 0 AND 5000;
\qecho SUBSELECTS on :TABLE
:PREFIX SELECT time, dev, val, 'q4_1' FROM (SELECT DISTINCT ON (dev) * FROM :TABLE) a;
:PREFIX SELECT NULL, dev, NULL, 'q4_3' FROM (SELECT DISTINCT ON (dev) dev FROM :TABLE) a;
:PREFIX SELECT time, dev, NULL, 'q4_4' FROM (SELECT DISTINCT ON (dev) dev, time FROM :TABLE) a;
\qecho ORDER BY
:PREFIX SELECT time, dev, val, 'q5_1' FROM (SELECT DISTINCT ON (dev) * FROM :TABLE ORDER BY dev, time) a;
:PREFIX SELECT time, dev, val, 'q5_2' FROM (SELECT DISTINCT ON (dev) * FROM :TABLE ORDER BY dev DESC, time DESC) a;
\qecho WHERE CLAUSES
:PREFIX SELECT time, dev, val, 'q6_1' FROM (SELECT DISTINCT ON (dev) * FROM :TABLE WHERE dev > 5) a;
:PREFIX SELECT time, dev, val, 'q6_2' FROM (SELECT DISTINCT ON (dev) * FROM :TABLE WHERE time > 5) a;
:PREFIX SELECT time, dev, val, 'q6_3' FROM (SELECT DISTINCT ON (dev) * FROM :TABLE) a WHERE dev > 5;
:PREFIX SELECT time, dev, val, 'q6_4' FROM (SELECT DISTINCT ON (dev) * FROM :TABLE) a WHERE time > 5;
--\qecho immutable func in WHERE clause on :TABLE
:PREFIX SELECT DISTINCT ON (dev) *, 'q6_5' FROM :TABLE WHERE dev > int_func_immutable();
--\qecho stable func in WHERE clause on :TABLE
:PREFIX SELECT DISTINCT ON (dev) *, 'q6_6' FROM :TABLE WHERE dev > int_func_stable();
--\qecho volatile func in WHERE clause on :TABLE
:PREFIX SELECT DISTINCT ON (dev) * FROM :TABLE WHERE dev > int_func_volatile();
:PREFIX SELECT DISTINCT ON (dev) * FROM :TABLE WHERE dev = ANY(inta_func_immutable());
:PREFIX SELECT DISTINCT ON (dev) * FROM :TABLE WHERE dev = ANY(inta_func_stable());
:PREFIX SELECT DISTINCT ON (dev) * FROM :TABLE WHERE dev = ANY(inta_func_volatile());
-- RowCompareExpr
:PREFIX SELECT DISTINCT ON (dev) * FROM :TABLE WHERE (dev, time) > (5,100);
-- always false expr similar to our initial skip qual
:PREFIX SELECT DISTINCT ON (dev) * FROM :TABLE WHERE dev > NULL;
-- no tuples matching
:PREFIX SELECT DISTINCT ON (dev) * FROM :TABLE WHERE dev > 20;
-- multiple constraints in WHERE clause
:PREFIX SELECT DISTINCT ON (dev) dev,time FROM :TABLE WHERE dev > 5 AND time = 100;
:PREFIX SELECT DISTINCT ON (dev) dev,time FROM :TABLE WHERE dev > 5 AND time > 200;
:PREFIX SELECT DISTINCT ON (dev) dev,time FROM :TABLE WHERE dev >= 5 AND dev < 7 AND dev >= 2;
:PREFIX SELECT DISTINCT ON (dev) dev,time,val FROM :TABLE WHERE time > 100 AND time < 200 AND val > 10 AND val < 10000 AND dev > 2 AND dev < 7 ORDER BY dev,time;
:PREFIX SELECT DISTINCT ON (dev) dev FROM :TABLE WHERE dev IS NULL;
:PREFIX SELECT DISTINCT ON (dev_name) dev_name FROM :TABLE WHERE dev_name IS NULL;
-- test constants in ORDER BY
:PREFIX SELECT DISTINCT ON (dev) * FROM :TABLE WHERE dev = 1 ORDER BY dev, time DESC;
:PREFIX SELECT DISTINCT dev, time FROM :TABLE WHERE dev = 1 and time = 100 ORDER BY dev, time DESC;
:PREFIX SELECT DISTINCT dev_name::varchar FROM :TABLE WHERE dev_name::varchar = 'device_1' ORDER BY 1;
-- test distinct PathKey with sortref = 0 in PG15 due to FALSE filter not pushed into relation (should not crash in PG15)
:PREFIX SELECT DISTINCT sq.dev FROM (SELECT dev FROM :TABLE) sq JOIN :TABLE ref ON (sq.dev = ref.dev) WHERE 1 > 2;
-- test distinct PathKey with sortref not aligning with targets sortrefs in PG15 due to Window function
:PREFIX SELECT DISTINCT
case when false then c10 else c10 end as c0,
max(c8) over (partition by c10 order by c6) as c1
FROM (SELECT time as c6, val as c8, dev as c10 FROM :TABLE WHERE false) subq WHERE false;
-- test multiple distincts with all but one pinned: #7998
:PREFIX SELECT DISTINCT dev, dev FROM :TABLE ORDER BY 1;
:PREFIX SELECT DISTINCT dev, time FROM :TABLE WHERE time = 100 ORDER BY 1;
:PREFIX SELECT DISTINCT dev, time, val FROM :TABLE WHERE time = 100 and val = 100 ORDER BY 1;
:PREFIX SELECT DISTINCT ON (dev, time) * FROM :TABLE WHERE time = 100 ORDER BY dev;
:PREFIX SELECT DISTINCT ON (dev, time) dev, time FROM :TABLE WHERE dev = 1 AND time < 20 ORDER BY dev, time;
-- CTE
:PREFIX WITH devices AS (
SELECT DISTINCT ON (dev) dev FROM :TABLE
)
SELECT * FROM devices;
:PREFIX WITH devices AS (
SELECT DISTINCT dev FROM :TABLE
)
SELECT * FROM devices ORDER BY dev;
-- prepared statements
PREPARE prep AS SELECT DISTINCT ON (dev_name) dev_name FROM :TABLE;
:PREFIX EXECUTE prep;
:PREFIX EXECUTE prep;
:PREFIX EXECUTE prep;
DEALLOCATE prep;
-- ReScan tests
:PREFIX SELECT time, dev, val, 'q7_1' FROM (SELECT DISTINCT ON (dev) * FROM (
VALUES (1), (2)) a(v),
LATERAL (SELECT * FROM :TABLE WHERE time != a.v) b) a;
:PREFIX SELECT time, dev, val, 'q7_2' FROM (SELECT * FROM (
VALUES (1), (2)) a(v),
LATERAL (SELECT DISTINCT ON (dev) * FROM :TABLE WHERE dev != a.v) b) a;
-- RuntimeKeys
:PREFIX SELECT time, dev, val, 'q8_1' FROM (SELECT * FROM (
VALUES (1), (2)) a(v),
LATERAL (SELECT DISTINCT ON (dev) * FROM :TABLE WHERE dev >= a.v) b) c;
-- Emulate multi-column DISTINCT using multiple SkipSkans
:PREFIX SELECT time, dev, val, 'q9_1' FROM (SELECT b.* FROM
(SELECT DISTINCT ON (dev) dev FROM :TABLE) a,
LATERAL (SELECT DISTINCT ON (time) * FROM :TABLE WHERE dev = a.dev) b) c;
:PREFIX SELECT time, dev, NULL, 'q9_2' FROM (SELECT b.* FROM
(SELECT DISTINCT ON (dev) dev FROM :TABLE) a,
LATERAL (SELECT DISTINCT ON (time) dev, time FROM :TABLE WHERE dev = a.dev) b) c;
-- Test that the multi-column DISTINCT emulation is equivalent to a real multi-column DISTINCT
:PREFIX SELECT * FROM
(SELECT DISTINCT ON (dev) dev FROM :TABLE) a,
LATERAL (SELECT DISTINCT ON (time) dev, time FROM :TABLE WHERE dev = a.dev) b;
:PREFIX SELECT DISTINCT ON (dev, time) dev, time FROM :TABLE WHERE coalesce(dev, -1) >= 0;
:PREFIX SELECT DISTINCT ON (dev, time) dev, time FROM :TABLE WHERE coalesce(dev, -1) >= 0
UNION SELECT b.* FROM
(SELECT DISTINCT ON (dev) dev FROM :TABLE) a,
LATERAL (SELECT DISTINCT ON (time) dev, time FROM :TABLE WHERE dev = a.dev) b;
-- SkipScan into INSERT
:PREFIX INSERT INTO skip_scan_insert(time, dev, val, query) SELECT time, dev, val, 'q10_1' FROM (SELECT DISTINCT ON (dev) * FROM :TABLE) a;
-- parallel query
SELECT set_config(CASE WHEN current_setting('server_version_num')::int < 160000 THEN 'force_parallel_mode' ELSE 'debug_parallel_query' END,'on', false);
:PREFIX SELECT DISTINCT dev FROM :TABLE ORDER BY dev;
SELECT set_config(CASE WHEN current_setting('server_version_num')::int < 160000 THEN 'force_parallel_mode' ELSE 'debug_parallel_query' END,'off', false);
TRUNCATE skip_scan_insert;
-- table with only nulls
:PREFIX SELECT DISTINCT ON (time) time FROM skip_scan_nulls;
-- no tuples in resultset
:PREFIX SELECT DISTINCT ON (time) time FROM skip_scan_nulls WHERE time IS NOT NULL;
-- Test for SDC issue #2976
-- We should use index "btree ("time", dev, dev_name)" where "dev_name" index key is #3 but we have 2 index quals including SkipScan qual
-- We should not drop qual on "dev_name"
CREATE INDEX skip_scan_idx_time_dev_dname ON :TABLE(time,dev,dev_name);
:PREFIX SELECT DISTINCT time FROM :TABLE WHERE dev_name IS NULL ORDER BY 1;
DROP INDEX skip_scan_idx_time_dev_dname;
-- "dev_name" is not a key column: it's not in the index quals and should be a filter
CREATE INDEX skip_scan_idx_time_dev_dname ON :TABLE(time,dev) INCLUDE(dev_name);
:PREFIX SELECT DISTINCT time FROM :TABLE WHERE dev_name IS NULL ORDER BY 1;
DROP INDEX skip_scan_idx_time_dev_dname;
\o
RESET timescaledb.enable_skipscan;
-- compare SkipScan results on normal table
:DIFF_CMD
--- Unoptimized results
+++ Optimized results
@@ -1,6 +1,6 @@
enable_skipscan
-----------------
- off
+ on
dev
-- run tests on hypertable and diff results
\set TABLE skip_scan_ht
\set PREFIX ''
\o :TEST_RESULTS_OPTIMIZED
\ir :TEST_QUERY_NAME
-- This file and its contents are licensed under the Timescale License.
-- Please see the included NOTICE for copyright information and
-- LICENSE-TIMESCALE for a copy of the license.
-- canary for result diff
SELECT current_setting('timescaledb.enable_skipscan') AS enable_skipscan;
-- test different index configurations
-- no index so we cant do SkipScan
:PREFIX SELECT DISTINCT dev FROM :TABLE ORDER BY dev;
-- NULLS LAST index on dev
CREATE INDEX skip_scan_idx_dev_nulls_last ON :TABLE(dev);
:PREFIX SELECT DISTINCT dev FROM :TABLE ORDER BY dev;
:PREFIX SELECT DISTINCT dev FROM :TABLE ORDER BY dev DESC;
:PREFIX SELECT DISTINCT ON (dev) dev FROM :TABLE;
-- Test not-NULL mode
:PREFIX SELECT DISTINCT dev FROM :TABLE WHERE dev IS NOT NULL ORDER BY dev;
DROP INDEX skip_scan_idx_dev_nulls_last;
-- NULLS FIRST index on dev
CREATE INDEX skip_scan_idx_dev_nulls_first ON :TABLE(dev NULLS FIRST);
:PREFIX SELECT DISTINCT dev FROM :TABLE ORDER BY dev NULLS FIRST;
:PREFIX SELECT DISTINCT ON (dev) dev FROM :TABLE ORDER BY dev NULLS FIRST;
-- Test not-NULL mode
:PREFIX SELECT DISTINCT dev FROM :TABLE WHERE dev IS NOT NULL ORDER BY dev NULLS FIRST;
DROP INDEX skip_scan_idx_dev_nulls_first;
-- multicolumn index with dev as leading column
CREATE INDEX skip_scan_idx_dev_time_idx ON :TABLE(dev, time);
:PREFIX SELECT DISTINCT dev FROM :TABLE ORDER BY dev;
:PREFIX SELECT DISTINCT ON (dev) dev FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev) dev FROM :TABLE ORDER BY dev DESC, time DESC;
-- Test not-NULL mode
:PREFIX SELECT DISTINCT dev FROM :TABLE WHERE dev IS NOT NULL ORDER BY dev;
DROP INDEX skip_scan_idx_dev_time_idx;
-- multicolumn index with dev as non-leading column
CREATE INDEX skip_scan_idx_time_dev_idx ON :TABLE(time, dev);
:PREFIX SELECT DISTINCT dev FROM :TABLE WHERE time = 100 ORDER BY dev;
:PREFIX SELECT DISTINCT ON (dev) dev FROM :TABLE WHERE time = 100;
-- Test not-NULL mode
:PREFIX SELECT DISTINCT dev FROM :TABLE WHERE time = 100 AND dev IS NOT NULL ORDER BY dev;
DROP INDEX skip_scan_idx_time_dev_idx;
-- hash index is not ordered so can't use skipscan
CREATE INDEX skip_scan_idx_hash ON :TABLE USING hash(dev_name);
:PREFIX SELECT DISTINCT dev_name FROM :TABLE WHERE dev_name IN ('device_1','device_2') ORDER BY dev_name;
DROP INDEX skip_scan_idx_hash;
-- expression indexes
-- currently not supported by skipscan
CREATE INDEX skip_scan_expr_idx ON :TABLE((dev % 3));
:PREFIX SELECT DISTINCT dev%3 FROM :TABLE ORDER BY dev%3;
:PREFIX SELECT DISTINCT ON (dev%3) dev FROM :TABLE ORDER BY dev%3;
DROP INDEX skip_scan_expr_idx;
CREATE INDEX ON :TABLE(dev_name);
-- Tests for #8107: accounting for scanning most of the input due to non-index quals
-- Non-index qual, no tuples match: SeqScan preferred
:PREFIX SELECT DISTINCT dev_name FROM :TABLE WHERE val < 0 ORDER BY 1;
-- Highly selective non-index qual, have to scan many tuples to match: SeqScan preferred
:PREFIX SELECT DISTINCT dev_name FROM :TABLE WHERE time = 100 ORDER BY 1;
-- Same but a combo of non-index quals
:PREFIX SELECT DISTINCT dev_name FROM :TABLE WHERE time = 100 and dev = 1 ORDER BY 1;
-- Highly selective index qual: less tuples to scan for low-selective non-index qual, can choose SkipScan
:PREFIX SELECT DISTINCT dev_name FROM :TABLE WHERE dev_name IS NULL and dev = 1 ORDER BY 1;
CREATE INDEX ON :TABLE(dev);
-- RowCompareExpr where some args are not in the index:
-- we extract lossy "dev >=5" index condition which is strict and doesn't allow NULLs
SET timescaledb.debug_skip_scan_info TO true;
:PREFIX SELECT DISTINCT ON (dev) * FROM :TABLE WHERE (dev, time) > (5,100);
psql:include/skip_scan_query.sql:74: INFO: SkipScan used on _hyper_1_1_chunk_skip_scan_ht_dev_idx(dev NOT NULL)
psql:include/skip_scan_query.sql:74: INFO: SkipScan used on _hyper_1_2_chunk_skip_scan_ht_dev_idx(dev NOT NULL)
psql:include/skip_scan_query.sql:74: INFO: SkipScan used on _hyper_1_3_chunk_skip_scan_ht_dev_idx(dev NOT NULL)
psql:include/skip_scan_query.sql:74: INFO: SkipScan used on _hyper_1_4_chunk_skip_scan_ht_dev_idx(dev NOT NULL)
RESET timescaledb.debug_skip_scan_info;
CREATE INDEX ON :TABLE(dev, time);
CREATE INDEX ON :TABLE(time,dev);
CREATE INDEX ON :TABLE(time,dev,val);
\qecho basic DISTINCT queries on :TABLE
:PREFIX SELECT DISTINCT dev, 'q1_1' FROM :TABLE ORDER BY dev;
:PREFIX SELECT DISTINCT dev_name, 'q1_2' FROM :TABLE ORDER BY dev_name;
:PREFIX SELECT DISTINCT dev, 'q1_3', NULL FROM :TABLE ORDER BY dev;
\qecho stable expression in targetlist on :TABLE
:PREFIX SELECT DISTINCT dev, 'q1_4', length(md5(now()::text)) FROM :TABLE ORDER BY dev;
:PREFIX SELECT DISTINCT dev_name, 'q1_5', length(md5(now()::text)) FROM :TABLE ORDER BY dev_name;
-- volatile expression in targetlist
:PREFIX SELECT DISTINCT dev, 'q1_6', length(md5(random()::text)) FROM :TABLE ORDER BY dev;
:PREFIX SELECT DISTINCT dev_name, 'q1_7', length(md5(random()::text)) FROM :TABLE ORDER BY dev_name;
-- queries without skipscan because distinct is not limited to specific column
:PREFIX SELECT DISTINCT * FROM :TABLE ORDER BY dev;
:PREFIX SELECT DISTINCT *, 'q1_9' FROM :TABLE ORDER BY dev;
:PREFIX SELECT DISTINCT dev, time, 'q1_10' FROM :TABLE ORDER BY dev;
:PREFIX SELECT DISTINCT dev, NULL, 'q1_11' FROM :TABLE ORDER BY dev;
-- distinct on expressions not supported
:PREFIX SELECT DISTINCT time_bucket(10,time), 'q1_12' FROM :TABLE;
:PREFIX SELECT DISTINCT length(dev_name), 'q1_13' FROM :TABLE;
:PREFIX SELECT DISTINCT 3*time, 'q1_14' FROM :TABLE;
:PREFIX SELECT DISTINCT 'Device ' || dev_name FROM :TABLE;
-- DISTINCT ON queries
:PREFIX SELECT DISTINCT ON (dev) dev FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev) dev, 'q2_2' FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev) dev, 'q2_3', NULL FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev) dev, 'q2_4', length(md5(now()::text)) FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev) dev, 'q2_5', length(md5(random()::text)) FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev) * FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev) *, 'q2_7' FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev) dev, time, 'q2_8' FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev) dev, NULL, 'q2_9' FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev) time, 'q2_10' FROM :TABLE ORDER by dev, time;
:PREFIX SELECT DISTINCT ON (dev) dev, tableoid::regclass, 'q2_11' FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev) dev, int_func_immutable(), 'q2_12' FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev) dev, int_func_stable(), 'q2_13' FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev) dev, int_func_volatile(), 'q2_14' FROM :TABLE;
-- DISTINCT ON queries on TEXT column
:PREFIX SELECT DISTINCT ON (dev_name) dev_name FROM :TABLE;
-- Test not-NULL mode
:PREFIX SELECT DISTINCT ON (dev_name) dev_name FROM :TABLE WHERE dev_name IS NOT NULL;
:PREFIX SELECT DISTINCT ON (dev_name) dev_name, 'q3_2' FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev_name) dev_name, 'q3_3', NULL FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev_name) dev_name, 'q3_4', length(md5(now()::text)) FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev_name) dev_name, 'q3_5', length(md5(random()::text)) FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev_name) * FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev_name) *, 'q3_7' FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev_name) dev_name, time, 'q3_8' FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev_name) dev_name, NULL, 'q3_9' FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev_name) time, 'q3_10' FROM :TABLE ORDER by dev_name, time;
:PREFIX SELECT DISTINCT ON (dev_name) dev_name, tableoid::regclass, 'q3_11' FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev_name::varchar) dev_name::varchar FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev_name) dev, int_func_immutable(), 'q3_13' FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev_name) dev, int_func_stable(), 'q3_14' FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev_name) dev, int_func_volatile(), 'q3_15' FROM :TABLE;
\qecho DISTINCT with wholerow var
:PREFIX SELECT DISTINCT ON (dev) :TABLE FROM :TABLE;
-- should not use SkipScan since we only support SkipScan on single-column distinct
:PREFIX SELECT DISTINCT :TABLE FROM :TABLE;
\qecho LIMIT queries on :TABLE
:PREFIX SELECT DISTINCT ON (dev) dev FROM :TABLE LIMIT 3;
:PREFIX SELECT DISTINCT ON (dev) dev FROM :TABLE ORDER BY dev DESC, time DESC LIMIT 3;
:PREFIX SELECT DISTINCT ON (dev) dev FROM :TABLE ORDER BY dev, time LIMIT 3;
\qecho range queries on :TABLE
:PREFIX SELECT DISTINCT ON (dev) dev FROM :TABLE WHERE time BETWEEN 100 AND 300;
:PREFIX SELECT DISTINCT ON (dev) dev FROM :TABLE WHERE time < 200;
:PREFIX SELECT DISTINCT ON (dev) dev FROM :TABLE WHERE time > 800;
\qecho ordered append on :TABLE
:PREFIX SELECT * FROM :TABLE ORDER BY time;
:PREFIX SELECT DISTINCT ON (time) time FROM :TABLE WHERE time BETWEEN 0 AND 5000;
\qecho SUBSELECTS on :TABLE
:PREFIX SELECT time, dev, val, 'q4_1' FROM (SELECT DISTINCT ON (dev) * FROM :TABLE) a;
:PREFIX SELECT NULL, dev, NULL, 'q4_3' FROM (SELECT DISTINCT ON (dev) dev FROM :TABLE) a;
:PREFIX SELECT time, dev, NULL, 'q4_4' FROM (SELECT DISTINCT ON (dev) dev, time FROM :TABLE) a;
\qecho ORDER BY
:PREFIX SELECT time, dev, val, 'q5_1' FROM (SELECT DISTINCT ON (dev) * FROM :TABLE ORDER BY dev, time) a;
:PREFIX SELECT time, dev, val, 'q5_2' FROM (SELECT DISTINCT ON (dev) * FROM :TABLE ORDER BY dev DESC, time DESC) a;
\qecho WHERE CLAUSES
:PREFIX SELECT time, dev, val, 'q6_1' FROM (SELECT DISTINCT ON (dev) * FROM :TABLE WHERE dev > 5) a;
:PREFIX SELECT time, dev, val, 'q6_2' FROM (SELECT DISTINCT ON (dev) * FROM :TABLE WHERE time > 5) a;
:PREFIX SELECT time, dev, val, 'q6_3' FROM (SELECT DISTINCT ON (dev) * FROM :TABLE) a WHERE dev > 5;
:PREFIX SELECT time, dev, val, 'q6_4' FROM (SELECT DISTINCT ON (dev) * FROM :TABLE) a WHERE time > 5;
--\qecho immutable func in WHERE clause on :TABLE
:PREFIX SELECT DISTINCT ON (dev) *, 'q6_5' FROM :TABLE WHERE dev > int_func_immutable();
--\qecho stable func in WHERE clause on :TABLE
:PREFIX SELECT DISTINCT ON (dev) *, 'q6_6' FROM :TABLE WHERE dev > int_func_stable();
--\qecho volatile func in WHERE clause on :TABLE
:PREFIX SELECT DISTINCT ON (dev) * FROM :TABLE WHERE dev > int_func_volatile();
:PREFIX SELECT DISTINCT ON (dev) * FROM :TABLE WHERE dev = ANY(inta_func_immutable());
:PREFIX SELECT DISTINCT ON (dev) * FROM :TABLE WHERE dev = ANY(inta_func_stable());
:PREFIX SELECT DISTINCT ON (dev) * FROM :TABLE WHERE dev = ANY(inta_func_volatile());
-- RowCompareExpr
:PREFIX SELECT DISTINCT ON (dev) * FROM :TABLE WHERE (dev, time) > (5,100);
-- always false expr similar to our initial skip qual
:PREFIX SELECT DISTINCT ON (dev) * FROM :TABLE WHERE dev > NULL;
-- no tuples matching
:PREFIX SELECT DISTINCT ON (dev) * FROM :TABLE WHERE dev > 20;
-- multiple constraints in WHERE clause
:PREFIX SELECT DISTINCT ON (dev) dev,time FROM :TABLE WHERE dev > 5 AND time = 100;
:PREFIX SELECT DISTINCT ON (dev) dev,time FROM :TABLE WHERE dev > 5 AND time > 200;
:PREFIX SELECT DISTINCT ON (dev) dev,time FROM :TABLE WHERE dev >= 5 AND dev < 7 AND dev >= 2;
:PREFIX SELECT DISTINCT ON (dev) dev,time,val FROM :TABLE WHERE time > 100 AND time < 200 AND val > 10 AND val < 10000 AND dev > 2 AND dev < 7 ORDER BY dev,time;
:PREFIX SELECT DISTINCT ON (dev) dev FROM :TABLE WHERE dev IS NULL;
:PREFIX SELECT DISTINCT ON (dev_name) dev_name FROM :TABLE WHERE dev_name IS NULL;
-- test constants in ORDER BY
:PREFIX SELECT DISTINCT ON (dev) * FROM :TABLE WHERE dev = 1 ORDER BY dev, time DESC;
:PREFIX SELECT DISTINCT dev, time FROM :TABLE WHERE dev = 1 and time = 100 ORDER BY dev, time DESC;
:PREFIX SELECT DISTINCT dev_name::varchar FROM :TABLE WHERE dev_name::varchar = 'device_1' ORDER BY 1;
-- test distinct PathKey with sortref = 0 in PG15 due to FALSE filter not pushed into relation (should not crash in PG15)
:PREFIX SELECT DISTINCT sq.dev FROM (SELECT dev FROM :TABLE) sq JOIN :TABLE ref ON (sq.dev = ref.dev) WHERE 1 > 2;
-- test distinct PathKey with sortref not aligning with targets sortrefs in PG15 due to Window function
:PREFIX SELECT DISTINCT
case when false then c10 else c10 end as c0,
max(c8) over (partition by c10 order by c6) as c1
FROM (SELECT time as c6, val as c8, dev as c10 FROM :TABLE WHERE false) subq WHERE false;
-- test multiple distincts with all but one pinned: #7998
:PREFIX SELECT DISTINCT dev, dev FROM :TABLE ORDER BY 1;
:PREFIX SELECT DISTINCT dev, time FROM :TABLE WHERE time = 100 ORDER BY 1;
:PREFIX SELECT DISTINCT dev, time, val FROM :TABLE WHERE time = 100 and val = 100 ORDER BY 1;
:PREFIX SELECT DISTINCT ON (dev, time) * FROM :TABLE WHERE time = 100 ORDER BY dev;
:PREFIX SELECT DISTINCT ON (dev, time) dev, time FROM :TABLE WHERE dev = 1 AND time < 20 ORDER BY dev, time;
-- CTE
:PREFIX WITH devices AS (
SELECT DISTINCT ON (dev) dev FROM :TABLE
)
SELECT * FROM devices;
:PREFIX WITH devices AS (
SELECT DISTINCT dev FROM :TABLE
)
SELECT * FROM devices ORDER BY dev;
-- prepared statements
PREPARE prep AS SELECT DISTINCT ON (dev_name) dev_name FROM :TABLE;
:PREFIX EXECUTE prep;
:PREFIX EXECUTE prep;
:PREFIX EXECUTE prep;
DEALLOCATE prep;
-- ReScan tests
:PREFIX SELECT time, dev, val, 'q7_1' FROM (SELECT DISTINCT ON (dev) * FROM (
VALUES (1), (2)) a(v),
LATERAL (SELECT * FROM :TABLE WHERE time != a.v) b) a;
:PREFIX SELECT time, dev, val, 'q7_2' FROM (SELECT * FROM (
VALUES (1), (2)) a(v),
LATERAL (SELECT DISTINCT ON (dev) * FROM :TABLE WHERE dev != a.v) b) a;
-- RuntimeKeys
:PREFIX SELECT time, dev, val, 'q8_1' FROM (SELECT * FROM (
VALUES (1), (2)) a(v),
LATERAL (SELECT DISTINCT ON (dev) * FROM :TABLE WHERE dev >= a.v) b) c;
-- Emulate multi-column DISTINCT using multiple SkipSkans
:PREFIX SELECT time, dev, val, 'q9_1' FROM (SELECT b.* FROM
(SELECT DISTINCT ON (dev) dev FROM :TABLE) a,
LATERAL (SELECT DISTINCT ON (time) * FROM :TABLE WHERE dev = a.dev) b) c;
:PREFIX SELECT time, dev, NULL, 'q9_2' FROM (SELECT b.* FROM
(SELECT DISTINCT ON (dev) dev FROM :TABLE) a,
LATERAL (SELECT DISTINCT ON (time) dev, time FROM :TABLE WHERE dev = a.dev) b) c;
-- Test that the multi-column DISTINCT emulation is equivalent to a real multi-column DISTINCT
:PREFIX SELECT * FROM
(SELECT DISTINCT ON (dev) dev FROM :TABLE) a,
LATERAL (SELECT DISTINCT ON (time) dev, time FROM :TABLE WHERE dev = a.dev) b;
:PREFIX SELECT DISTINCT ON (dev, time) dev, time FROM :TABLE WHERE coalesce(dev, -1) >= 0;
:PREFIX SELECT DISTINCT ON (dev, time) dev, time FROM :TABLE WHERE coalesce(dev, -1) >= 0
UNION SELECT b.* FROM
(SELECT DISTINCT ON (dev) dev FROM :TABLE) a,
LATERAL (SELECT DISTINCT ON (time) dev, time FROM :TABLE WHERE dev = a.dev) b;
-- SkipScan into INSERT
:PREFIX INSERT INTO skip_scan_insert(time, dev, val, query) SELECT time, dev, val, 'q10_1' FROM (SELECT DISTINCT ON (dev) * FROM :TABLE) a;
-- parallel query
SELECT set_config(CASE WHEN current_setting('server_version_num')::int < 160000 THEN 'force_parallel_mode' ELSE 'debug_parallel_query' END,'on', false);
:PREFIX SELECT DISTINCT dev FROM :TABLE ORDER BY dev;
SELECT set_config(CASE WHEN current_setting('server_version_num')::int < 160000 THEN 'force_parallel_mode' ELSE 'debug_parallel_query' END,'off', false);
TRUNCATE skip_scan_insert;
-- table with only nulls
:PREFIX SELECT DISTINCT ON (time) time FROM skip_scan_nulls;
-- no tuples in resultset
:PREFIX SELECT DISTINCT ON (time) time FROM skip_scan_nulls WHERE time IS NOT NULL;
-- Test for SDC issue #2976
-- We should use index "btree ("time", dev, dev_name)" where "dev_name" index key is #3 but we have 2 index quals including SkipScan qual
-- We should not drop qual on "dev_name"
CREATE INDEX skip_scan_idx_time_dev_dname ON :TABLE(time,dev,dev_name);
:PREFIX SELECT DISTINCT time FROM :TABLE WHERE dev_name IS NULL ORDER BY 1;
DROP INDEX skip_scan_idx_time_dev_dname;
-- "dev_name" is not a key column: it's not in the index quals and should be a filter
CREATE INDEX skip_scan_idx_time_dev_dname ON :TABLE(time,dev) INCLUDE(dev_name);
:PREFIX SELECT DISTINCT time FROM :TABLE WHERE dev_name IS NULL ORDER BY 1;
DROP INDEX skip_scan_idx_time_dev_dname;
\o
SET timescaledb.enable_skipscan TO false;
\o :TEST_RESULTS_UNOPTIMIZED
\ir :TEST_QUERY_NAME
-- This file and its contents are licensed under the Timescale License.
-- Please see the included NOTICE for copyright information and
-- LICENSE-TIMESCALE for a copy of the license.
-- canary for result diff
SELECT current_setting('timescaledb.enable_skipscan') AS enable_skipscan;
-- test different index configurations
-- no index so we cant do SkipScan
:PREFIX SELECT DISTINCT dev FROM :TABLE ORDER BY dev;
-- NULLS LAST index on dev
CREATE INDEX skip_scan_idx_dev_nulls_last ON :TABLE(dev);
:PREFIX SELECT DISTINCT dev FROM :TABLE ORDER BY dev;
:PREFIX SELECT DISTINCT dev FROM :TABLE ORDER BY dev DESC;
:PREFIX SELECT DISTINCT ON (dev) dev FROM :TABLE;
-- Test not-NULL mode
:PREFIX SELECT DISTINCT dev FROM :TABLE WHERE dev IS NOT NULL ORDER BY dev;
DROP INDEX skip_scan_idx_dev_nulls_last;
-- NULLS FIRST index on dev
CREATE INDEX skip_scan_idx_dev_nulls_first ON :TABLE(dev NULLS FIRST);
:PREFIX SELECT DISTINCT dev FROM :TABLE ORDER BY dev NULLS FIRST;
:PREFIX SELECT DISTINCT ON (dev) dev FROM :TABLE ORDER BY dev NULLS FIRST;
-- Test not-NULL mode
:PREFIX SELECT DISTINCT dev FROM :TABLE WHERE dev IS NOT NULL ORDER BY dev NULLS FIRST;
DROP INDEX skip_scan_idx_dev_nulls_first;
-- multicolumn index with dev as leading column
CREATE INDEX skip_scan_idx_dev_time_idx ON :TABLE(dev, time);
:PREFIX SELECT DISTINCT dev FROM :TABLE ORDER BY dev;
:PREFIX SELECT DISTINCT ON (dev) dev FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev) dev FROM :TABLE ORDER BY dev DESC, time DESC;
-- Test not-NULL mode
:PREFIX SELECT DISTINCT dev FROM :TABLE WHERE dev IS NOT NULL ORDER BY dev;
DROP INDEX skip_scan_idx_dev_time_idx;
-- multicolumn index with dev as non-leading column
CREATE INDEX skip_scan_idx_time_dev_idx ON :TABLE(time, dev);
:PREFIX SELECT DISTINCT dev FROM :TABLE WHERE time = 100 ORDER BY dev;
:PREFIX SELECT DISTINCT ON (dev) dev FROM :TABLE WHERE time = 100;
-- Test not-NULL mode
:PREFIX SELECT DISTINCT dev FROM :TABLE WHERE time = 100 AND dev IS NOT NULL ORDER BY dev;
DROP INDEX skip_scan_idx_time_dev_idx;
-- hash index is not ordered so can't use skipscan
CREATE INDEX skip_scan_idx_hash ON :TABLE USING hash(dev_name);
:PREFIX SELECT DISTINCT dev_name FROM :TABLE WHERE dev_name IN ('device_1','device_2') ORDER BY dev_name;
DROP INDEX skip_scan_idx_hash;
-- expression indexes
-- currently not supported by skipscan
CREATE INDEX skip_scan_expr_idx ON :TABLE((dev % 3));
:PREFIX SELECT DISTINCT dev%3 FROM :TABLE ORDER BY dev%3;
:PREFIX SELECT DISTINCT ON (dev%3) dev FROM :TABLE ORDER BY dev%3;
DROP INDEX skip_scan_expr_idx;
CREATE INDEX ON :TABLE(dev_name);
-- Tests for #8107: accounting for scanning most of the input due to non-index quals
-- Non-index qual, no tuples match: SeqScan preferred
:PREFIX SELECT DISTINCT dev_name FROM :TABLE WHERE val < 0 ORDER BY 1;
-- Highly selective non-index qual, have to scan many tuples to match: SeqScan preferred
:PREFIX SELECT DISTINCT dev_name FROM :TABLE WHERE time = 100 ORDER BY 1;
-- Same but a combo of non-index quals
:PREFIX SELECT DISTINCT dev_name FROM :TABLE WHERE time = 100 and dev = 1 ORDER BY 1;
-- Highly selective index qual: less tuples to scan for low-selective non-index qual, can choose SkipScan
:PREFIX SELECT DISTINCT dev_name FROM :TABLE WHERE dev_name IS NULL and dev = 1 ORDER BY 1;
CREATE INDEX ON :TABLE(dev);
-- RowCompareExpr where some args are not in the index:
-- we extract lossy "dev >=5" index condition which is strict and doesn't allow NULLs
SET timescaledb.debug_skip_scan_info TO true;
:PREFIX SELECT DISTINCT ON (dev) * FROM :TABLE WHERE (dev, time) > (5,100);
RESET timescaledb.debug_skip_scan_info;
CREATE INDEX ON :TABLE(dev, time);
CREATE INDEX ON :TABLE(time,dev);
CREATE INDEX ON :TABLE(time,dev,val);
\qecho basic DISTINCT queries on :TABLE
:PREFIX SELECT DISTINCT dev, 'q1_1' FROM :TABLE ORDER BY dev;
:PREFIX SELECT DISTINCT dev_name, 'q1_2' FROM :TABLE ORDER BY dev_name;
:PREFIX SELECT DISTINCT dev, 'q1_3', NULL FROM :TABLE ORDER BY dev;
\qecho stable expression in targetlist on :TABLE
:PREFIX SELECT DISTINCT dev, 'q1_4', length(md5(now()::text)) FROM :TABLE ORDER BY dev;
:PREFIX SELECT DISTINCT dev_name, 'q1_5', length(md5(now()::text)) FROM :TABLE ORDER BY dev_name;
-- volatile expression in targetlist
:PREFIX SELECT DISTINCT dev, 'q1_6', length(md5(random()::text)) FROM :TABLE ORDER BY dev;
:PREFIX SELECT DISTINCT dev_name, 'q1_7', length(md5(random()::text)) FROM :TABLE ORDER BY dev_name;
-- queries without skipscan because distinct is not limited to specific column
:PREFIX SELECT DISTINCT * FROM :TABLE ORDER BY dev;
:PREFIX SELECT DISTINCT *, 'q1_9' FROM :TABLE ORDER BY dev;
:PREFIX SELECT DISTINCT dev, time, 'q1_10' FROM :TABLE ORDER BY dev;
:PREFIX SELECT DISTINCT dev, NULL, 'q1_11' FROM :TABLE ORDER BY dev;
-- distinct on expressions not supported
:PREFIX SELECT DISTINCT time_bucket(10,time), 'q1_12' FROM :TABLE;
:PREFIX SELECT DISTINCT length(dev_name), 'q1_13' FROM :TABLE;
:PREFIX SELECT DISTINCT 3*time, 'q1_14' FROM :TABLE;
:PREFIX SELECT DISTINCT 'Device ' || dev_name FROM :TABLE;
-- DISTINCT ON queries
:PREFIX SELECT DISTINCT ON (dev) dev FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev) dev, 'q2_2' FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev) dev, 'q2_3', NULL FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev) dev, 'q2_4', length(md5(now()::text)) FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev) dev, 'q2_5', length(md5(random()::text)) FROM :TABLE;
:PREFIX SELECT DISTINCT ON (dev) * FROM :TABLE;