diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 13:44:03 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 13:44:03 +0000 |
commit | 293913568e6a7a86fd1479e1cff8e2ecb58d6568 (patch) | |
tree | fc3b469a3ec5ab71b36ea97cc7aaddb838423a0c /contrib/pg_trgm | |
parent | Initial commit. (diff) | |
download | postgresql-16-293913568e6a7a86fd1479e1cff8e2ecb58d6568.tar.xz postgresql-16-293913568e6a7a86fd1479e1cff8e2ecb58d6568.zip |
Adding upstream version 16.2.upstream/16.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'contrib/pg_trgm')
24 files changed, 15252 insertions, 0 deletions
diff --git a/contrib/pg_trgm/.gitignore b/contrib/pg_trgm/.gitignore new file mode 100644 index 0000000..5dcb3ff --- /dev/null +++ b/contrib/pg_trgm/.gitignore @@ -0,0 +1,4 @@ +# Generated subdirectories +/log/ +/results/ +/tmp_check/ diff --git a/contrib/pg_trgm/Makefile b/contrib/pg_trgm/Makefile new file mode 100644 index 0000000..1fbdc9e --- /dev/null +++ b/contrib/pg_trgm/Makefile @@ -0,0 +1,28 @@ +# contrib/pg_trgm/Makefile + +MODULE_big = pg_trgm +OBJS = \ + $(WIN32RES) \ + trgm_gin.o \ + trgm_gist.o \ + trgm_op.o \ + trgm_regexp.o + +EXTENSION = pg_trgm +DATA = pg_trgm--1.5--1.6.sql pg_trgm--1.4--1.5.sql pg_trgm--1.3--1.4.sql \ + pg_trgm--1.3.sql pg_trgm--1.2--1.3.sql pg_trgm--1.1--1.2.sql \ + pg_trgm--1.0--1.1.sql +PGFILEDESC = "pg_trgm - trigram matching" + +REGRESS = pg_trgm pg_word_trgm pg_strict_word_trgm + +ifdef USE_PGXS +PG_CONFIG = pg_config +PGXS := $(shell $(PG_CONFIG) --pgxs) +include $(PGXS) +else +subdir = contrib/pg_trgm +top_builddir = ../.. +include $(top_builddir)/src/Makefile.global +include $(top_srcdir)/contrib/contrib-global.mk +endif diff --git a/contrib/pg_trgm/data/trgm.data b/contrib/pg_trgm/data/trgm.data new file mode 100644 index 0000000..37319d6 --- /dev/null +++ b/contrib/pg_trgm/data/trgm.data @@ -0,0 +1,1000 @@ +qwertyu0001 +qwertyu0002 +qwertyu0003 +qwertyu0004 +qwertyu0005 +qwertyu0006 +qwertyu0007 +qwertyu0008 +qwertyu0009 +qwertyu0010 +qwertyu0011 +qwertyu0012 +qwertyu0013 +qwertyu0014 +qwertyu0015 +qwertyu0016 +qwertyu0017 +qwertyu0018 +qwertyu0019 +qwertyu0020 +qwertyu0021 +qwertyu0022 +qwertyu0023 +qwertyu0024 +qwertyu0025 +qwertyu0026 +qwertyu0027 +qwertyu0028 +qwertyu0029 +qwertyu0030 +qwertyu0031 +qwertyu0032 +qwertyu0033 +qwertyu0034 +qwertyu0035 +qwertyu0036 +qwertyu0037 +qwertyu0038 +qwertyu0039 +qwertyu0040 +qwertyu0041 +qwertyu0042 +qwertyu0043 +qwertyu0044 +qwertyu0045 +qwertyu0046 +qwertyu0047 +qwertyu0048 +qwertyu0049 +qwertyu0050 +qwertyu0051 +qwertyu0052 +qwertyu0053 +qwertyu0054 +qwertyu0055 +qwertyu0056 +qwertyu0057 +qwertyu0058 +qwertyu0059 +qwertyu0060 +qwertyu0061 +qwertyu0062 +qwertyu0063 +qwertyu0064 +qwertyu0065 +qwertyu0066 +qwertyu0067 +qwertyu0068 +qwertyu0069 +qwertyu0070 +qwertyu0071 +qwertyu0072 +qwertyu0073 +qwertyu0074 +qwertyu0075 +qwertyu0076 +qwertyu0077 +qwertyu0078 +qwertyu0079 +qwertyu0080 +qwertyu0081 +qwertyu0082 +qwertyu0083 +qwertyu0084 +qwertyu0085 +qwertyu0086 +qwertyu0087 +qwertyu0088 +qwertyu0089 +qwertyu0090 +qwertyu0091 +qwertyu0092 +qwertyu0093 +qwertyu0094 +qwertyu0095 +qwertyu0096 +qwertyu0097 +qwertyu0098 +qwertyu0099 +qwertyu0100 +qwertyu0101 +qwertyu0102 +qwertyu0103 +qwertyu0104 +qwertyu0105 +qwertyu0106 +qwertyu0107 +qwertyu0108 +qwertyu0109 +qwertyu0110 +qwertyu0111 +qwertyu0112 +qwertyu0113 +qwertyu0114 +qwertyu0115 +qwertyu0116 +qwertyu0117 +qwertyu0118 +qwertyu0119 +qwertyu0120 +qwertyu0121 +qwertyu0122 +qwertyu0123 +qwertyu0124 +qwertyu0125 +qwertyu0126 +qwertyu0127 +qwertyu0128 +qwertyu0129 +qwertyu0130 +qwertyu0131 +qwertyu0132 +qwertyu0133 +qwertyu0134 +qwertyu0135 +qwertyu0136 +qwertyu0137 +qwertyu0138 +qwertyu0139 +qwertyu0140 +qwertyu0141 +qwertyu0142 +qwertyu0143 +qwertyu0144 +qwertyu0145 +qwertyu0146 +qwertyu0147 +qwertyu0148 +qwertyu0149 +qwertyu0150 +qwertyu0151 +qwertyu0152 +qwertyu0153 +qwertyu0154 +qwertyu0155 +qwertyu0156 +qwertyu0157 +qwertyu0158 +qwertyu0159 +qwertyu0160 +qwertyu0161 +qwertyu0162 +qwertyu0163 +qwertyu0164 +qwertyu0165 +qwertyu0166 +qwertyu0167 +qwertyu0168 +qwertyu0169 +qwertyu0170 +qwertyu0171 +qwertyu0172 +qwertyu0173 +qwertyu0174 +qwertyu0175 +qwertyu0176 +qwertyu0177 +qwertyu0178 +qwertyu0179 +qwertyu0180 +qwertyu0181 +qwertyu0182 +qwertyu0183 +qwertyu0184 +qwertyu0185 +qwertyu0186 +qwertyu0187 +qwertyu0188 +qwertyu0189 +qwertyu0190 +qwertyu0191 +qwertyu0192 +qwertyu0193 +qwertyu0194 +qwertyu0195 +qwertyu0196 +qwertyu0197 +qwertyu0198 +qwertyu0199 +qwertyu0200 +qwertyu0201 +qwertyu0202 +qwertyu0203 +qwertyu0204 +qwertyu0205 +qwertyu0206 +qwertyu0207 +qwertyu0208 +qwertyu0209 +qwertyu0210 +qwertyu0211 +qwertyu0212 +qwertyu0213 +qwertyu0214 +qwertyu0215 +qwertyu0216 +qwertyu0217 +qwertyu0218 +qwertyu0219 +qwertyu0220 +qwertyu0221 +qwertyu0222 +qwertyu0223 +qwertyu0224 +qwertyu0225 +qwertyu0226 +qwertyu0227 +qwertyu0228 +qwertyu0229 +qwertyu0230 +qwertyu0231 +qwertyu0232 +qwertyu0233 +qwertyu0234 +qwertyu0235 +qwertyu0236 +qwertyu0237 +qwertyu0238 +qwertyu0239 +qwertyu0240 +qwertyu0241 +qwertyu0242 +qwertyu0243 +qwertyu0244 +qwertyu0245 +qwertyu0246 +qwertyu0247 +qwertyu0248 +qwertyu0249 +qwertyu0250 +qwertyu0251 +qwertyu0252 +qwertyu0253 +qwertyu0254 +qwertyu0255 +qwertyu0256 +qwertyu0257 +qwertyu0258 +qwertyu0259 +qwertyu0260 +qwertyu0261 +qwertyu0262 +qwertyu0263 +qwertyu0264 +qwertyu0265 +qwertyu0266 +qwertyu0267 +qwertyu0268 +qwertyu0269 +qwertyu0270 +qwertyu0271 +qwertyu0272 +qwertyu0273 +qwertyu0274 +qwertyu0275 +qwertyu0276 +qwertyu0277 +qwertyu0278 +qwertyu0279 +qwertyu0280 +qwertyu0281 +qwertyu0282 +qwertyu0283 +qwertyu0284 +qwertyu0285 +qwertyu0286 +qwertyu0287 +qwertyu0288 +qwertyu0289 +qwertyu0290 +qwertyu0291 +qwertyu0292 +qwertyu0293 +qwertyu0294 +qwertyu0295 +qwertyu0296 +qwertyu0297 +qwertyu0298 +qwertyu0299 +qwertyu0300 +qwertyu0301 +qwertyu0302 +qwertyu0303 +qwertyu0304 +qwertyu0305 +qwertyu0306 +qwertyu0307 +qwertyu0308 +qwertyu0309 +qwertyu0310 +qwertyu0311 +qwertyu0312 +qwertyu0313 +qwertyu0314 +qwertyu0315 +qwertyu0316 +qwertyu0317 +qwertyu0318 +qwertyu0319 +qwertyu0320 +qwertyu0321 +qwertyu0322 +qwertyu0323 +qwertyu0324 +qwertyu0325 +qwertyu0326 +qwertyu0327 +qwertyu0328 +qwertyu0329 +qwertyu0330 +qwertyu0331 +qwertyu0332 +qwertyu0333 +qwertyu0334 +qwertyu0335 +qwertyu0336 +qwertyu0337 +qwertyu0338 +qwertyu0339 +qwertyu0340 +qwertyu0341 +qwertyu0342 +qwertyu0343 +qwertyu0344 +qwertyu0345 +qwertyu0346 +qwertyu0347 +qwertyu0348 +qwertyu0349 +qwertyu0350 +qwertyu0351 +qwertyu0352 +qwertyu0353 +qwertyu0354 +qwertyu0355 +qwertyu0356 +qwertyu0357 +qwertyu0358 +qwertyu0359 +qwertyu0360 +qwertyu0361 +qwertyu0362 +qwertyu0363 +qwertyu0364 +qwertyu0365 +qwertyu0366 +qwertyu0367 +qwertyu0368 +qwertyu0369 +qwertyu0370 +qwertyu0371 +qwertyu0372 +qwertyu0373 +qwertyu0374 +qwertyu0375 +qwertyu0376 +qwertyu0377 +qwertyu0378 +qwertyu0379 +qwertyu0380 +qwertyu0381 +qwertyu0382 +qwertyu0383 +qwertyu0384 +qwertyu0385 +qwertyu0386 +qwertyu0387 +qwertyu0388 +qwertyu0389 +qwertyu0390 +qwertyu0391 +qwertyu0392 +qwertyu0393 +qwertyu0394 +qwertyu0395 +qwertyu0396 +qwertyu0397 +qwertyu0398 +qwertyu0399 +qwertyu0400 +qwertyu0401 +qwertyu0402 +qwertyu0403 +qwertyu0404 +qwertyu0405 +qwertyu0406 +qwertyu0407 +qwertyu0408 +qwertyu0409 +qwertyu0410 +qwertyu0411 +qwertyu0412 +qwertyu0413 +qwertyu0414 +qwertyu0415 +qwertyu0416 +qwertyu0417 +qwertyu0418 +qwertyu0419 +qwertyu0420 +qwertyu0421 +qwertyu0422 +qwertyu0423 +qwertyu0424 +qwertyu0425 +qwertyu0426 +qwertyu0427 +qwertyu0428 +qwertyu0429 +qwertyu0430 +qwertyu0431 +qwertyu0432 +qwertyu0433 +qwertyu0434 +qwertyu0435 +qwertyu0436 +qwertyu0437 +qwertyu0438 +qwertyu0439 +qwertyu0440 +qwertyu0441 +qwertyu0442 +qwertyu0443 +qwertyu0444 +qwertyu0445 +qwertyu0446 +qwertyu0447 +qwertyu0448 +qwertyu0449 +qwertyu0450 +qwertyu0451 +qwertyu0452 +qwertyu0453 +qwertyu0454 +qwertyu0455 +qwertyu0456 +qwertyu0457 +qwertyu0458 +qwertyu0459 +qwertyu0460 +qwertyu0461 +qwertyu0462 +qwertyu0463 +qwertyu0464 +qwertyu0465 +qwertyu0466 +qwertyu0467 +qwertyu0468 +qwertyu0469 +qwertyu0470 +qwertyu0471 +qwertyu0472 +qwertyu0473 +qwertyu0474 +qwertyu0475 +qwertyu0476 +qwertyu0477 +qwertyu0478 +qwertyu0479 +qwertyu0480 +qwertyu0481 +qwertyu0482 +qwertyu0483 +qwertyu0484 +qwertyu0485 +qwertyu0486 +qwertyu0487 +qwertyu0488 +qwertyu0489 +qwertyu0490 +qwertyu0491 +qwertyu0492 +qwertyu0493 +qwertyu0494 +qwertyu0495 +qwertyu0496 +qwertyu0497 +qwertyu0498 +qwertyu0499 +qwertyu0500 +qwertyu0501 +qwertyu0502 +qwertyu0503 +qwertyu0504 +qwertyu0505 +qwertyu0506 +qwertyu0507 +qwertyu0508 +qwertyu0509 +qwertyu0510 +qwertyu0511 +qwertyu0512 +qwertyu0513 +qwertyu0514 +qwertyu0515 +qwertyu0516 +qwertyu0517 +qwertyu0518 +qwertyu0519 +qwertyu0520 +qwertyu0521 +qwertyu0522 +qwertyu0523 +qwertyu0524 +qwertyu0525 +qwertyu0526 +qwertyu0527 +qwertyu0528 +qwertyu0529 +qwertyu0530 +qwertyu0531 +qwertyu0532 +qwertyu0533 +qwertyu0534 +qwertyu0535 +qwertyu0536 +qwertyu0537 +qwertyu0538 +qwertyu0539 +qwertyu0540 +qwertyu0541 +qwertyu0542 +qwertyu0543 +qwertyu0544 +qwertyu0545 +qwertyu0546 +qwertyu0547 +qwertyu0548 +qwertyu0549 +qwertyu0550 +qwertyu0551 +qwertyu0552 +qwertyu0553 +qwertyu0554 +qwertyu0555 +qwertyu0556 +qwertyu0557 +qwertyu0558 +qwertyu0559 +qwertyu0560 +qwertyu0561 +qwertyu0562 +qwertyu0563 +qwertyu0564 +qwertyu0565 +qwertyu0566 +qwertyu0567 +qwertyu0568 +qwertyu0569 +qwertyu0570 +qwertyu0571 +qwertyu0572 +qwertyu0573 +qwertyu0574 +qwertyu0575 +qwertyu0576 +qwertyu0577 +qwertyu0578 +qwertyu0579 +qwertyu0580 +qwertyu0581 +qwertyu0582 +qwertyu0583 +qwertyu0584 +qwertyu0585 +qwertyu0586 +qwertyu0587 +qwertyu0588 +qwertyu0589 +qwertyu0590 +qwertyu0591 +qwertyu0592 +qwertyu0593 +qwertyu0594 +qwertyu0595 +qwertyu0596 +qwertyu0597 +qwertyu0598 +qwertyu0599 +qwertyu0600 +qwertyu0601 +qwertyu0602 +qwertyu0603 +qwertyu0604 +qwertyu0605 +qwertyu0606 +qwertyu0607 +qwertyu0608 +qwertyu0609 +qwertyu0610 +qwertyu0611 +qwertyu0612 +qwertyu0613 +qwertyu0614 +qwertyu0615 +qwertyu0616 +qwertyu0617 +qwertyu0618 +qwertyu0619 +qwertyu0620 +qwertyu0621 +qwertyu0622 +qwertyu0623 +qwertyu0624 +qwertyu0625 +qwertyu0626 +qwertyu0627 +qwertyu0628 +qwertyu0629 +qwertyu0630 +qwertyu0631 +qwertyu0632 +qwertyu0633 +qwertyu0634 +qwertyu0635 +qwertyu0636 +qwertyu0637 +qwertyu0638 +qwertyu0639 +qwertyu0640 +qwertyu0641 +qwertyu0642 +qwertyu0643 +qwertyu0644 +qwertyu0645 +qwertyu0646 +qwertyu0647 +qwertyu0648 +qwertyu0649 +qwertyu0650 +qwertyu0651 +qwertyu0652 +qwertyu0653 +qwertyu0654 +qwertyu0655 +qwertyu0656 +qwertyu0657 +qwertyu0658 +qwertyu0659 +qwertyu0660 +qwertyu0661 +qwertyu0662 +qwertyu0663 +qwertyu0664 +qwertyu0665 +qwertyu0666 +qwertyu0667 +qwertyu0668 +qwertyu0669 +qwertyu0670 +qwertyu0671 +qwertyu0672 +qwertyu0673 +qwertyu0674 +qwertyu0675 +qwertyu0676 +qwertyu0677 +qwertyu0678 +qwertyu0679 +qwertyu0680 +qwertyu0681 +qwertyu0682 +qwertyu0683 +qwertyu0684 +qwertyu0685 +qwertyu0686 +qwertyu0687 +qwertyu0688 +qwertyu0689 +qwertyu0690 +qwertyu0691 +qwertyu0692 +qwertyu0693 +qwertyu0694 +qwertyu0695 +qwertyu0696 +qwertyu0697 +qwertyu0698 +qwertyu0699 +qwertyu0700 +qwertyu0701 +qwertyu0702 +qwertyu0703 +qwertyu0704 +qwertyu0705 +qwertyu0706 +qwertyu0707 +qwertyu0708 +qwertyu0709 +qwertyu0710 +qwertyu0711 +qwertyu0712 +qwertyu0713 +qwertyu0714 +qwertyu0715 +qwertyu0716 +qwertyu0717 +qwertyu0718 +qwertyu0719 +qwertyu0720 +qwertyu0721 +qwertyu0722 +qwertyu0723 +qwertyu0724 +qwertyu0725 +qwertyu0726 +qwertyu0727 +qwertyu0728 +qwertyu0729 +qwertyu0730 +qwertyu0731 +qwertyu0732 +qwertyu0733 +qwertyu0734 +qwertyu0735 +qwertyu0736 +qwertyu0737 +qwertyu0738 +qwertyu0739 +qwertyu0740 +qwertyu0741 +qwertyu0742 +qwertyu0743 +qwertyu0744 +qwertyu0745 +qwertyu0746 +qwertyu0747 +qwertyu0748 +qwertyu0749 +qwertyu0750 +qwertyu0751 +qwertyu0752 +qwertyu0753 +qwertyu0754 +qwertyu0755 +qwertyu0756 +qwertyu0757 +qwertyu0758 +qwertyu0759 +qwertyu0760 +qwertyu0761 +qwertyu0762 +qwertyu0763 +qwertyu0764 +qwertyu0765 +qwertyu0766 +qwertyu0767 +qwertyu0768 +qwertyu0769 +qwertyu0770 +qwertyu0771 +qwertyu0772 +qwertyu0773 +qwertyu0774 +qwertyu0775 +qwertyu0776 +qwertyu0777 +qwertyu0778 +qwertyu0779 +qwertyu0780 +qwertyu0781 +qwertyu0782 +qwertyu0783 +qwertyu0784 +qwertyu0785 +qwertyu0786 +qwertyu0787 +qwertyu0788 +qwertyu0789 +qwertyu0790 +qwertyu0791 +qwertyu0792 +qwertyu0793 +qwertyu0794 +qwertyu0795 +qwertyu0796 +qwertyu0797 +qwertyu0798 +qwertyu0799 +qwertyu0800 +qwertyu0801 +qwertyu0802 +qwertyu0803 +qwertyu0804 +qwertyu0805 +qwertyu0806 +qwertyu0807 +qwertyu0808 +qwertyu0809 +qwertyu0810 +qwertyu0811 +qwertyu0812 +qwertyu0813 +qwertyu0814 +qwertyu0815 +qwertyu0816 +qwertyu0817 +qwertyu0818 +qwertyu0819 +qwertyu0820 +qwertyu0821 +qwertyu0822 +qwertyu0823 +qwertyu0824 +qwertyu0825 +qwertyu0826 +qwertyu0827 +qwertyu0828 +qwertyu0829 +qwertyu0830 +qwertyu0831 +qwertyu0832 +qwertyu0833 +qwertyu0834 +qwertyu0835 +qwertyu0836 +qwertyu0837 +qwertyu0838 +qwertyu0839 +qwertyu0840 +qwertyu0841 +qwertyu0842 +qwertyu0843 +qwertyu0844 +qwertyu0845 +qwertyu0846 +qwertyu0847 +qwertyu0848 +qwertyu0849 +qwertyu0850 +qwertyu0851 +qwertyu0852 +qwertyu0853 +qwertyu0854 +qwertyu0855 +qwertyu0856 +qwertyu0857 +qwertyu0858 +qwertyu0859 +qwertyu0860 +qwertyu0861 +qwertyu0862 +qwertyu0863 +qwertyu0864 +qwertyu0865 +qwertyu0866 +qwertyu0867 +qwertyu0868 +qwertyu0869 +qwertyu0870 +qwertyu0871 +qwertyu0872 +qwertyu0873 +qwertyu0874 +qwertyu0875 +qwertyu0876 +qwertyu0877 +qwertyu0878 +qwertyu0879 +qwertyu0880 +qwertyu0881 +qwertyu0882 +qwertyu0883 +qwertyu0884 +qwertyu0885 +qwertyu0886 +qwertyu0887 +qwertyu0888 +qwertyu0889 +qwertyu0890 +qwertyu0891 +qwertyu0892 +qwertyu0893 +qwertyu0894 +qwertyu0895 +qwertyu0896 +qwertyu0897 +qwertyu0898 +qwertyu0899 +qwertyu0900 +qwertyu0901 +qwertyu0902 +qwertyu0903 +qwertyu0904 +qwertyu0905 +qwertyu0906 +qwertyu0907 +qwertyu0908 +qwertyu0909 +qwertyu0910 +qwertyu0911 +qwertyu0912 +qwertyu0913 +qwertyu0914 +qwertyu0915 +qwertyu0916 +qwertyu0917 +qwertyu0918 +qwertyu0919 +qwertyu0920 +qwertyu0921 +qwertyu0922 +qwertyu0923 +qwertyu0924 +qwertyu0925 +qwertyu0926 +qwertyu0927 +qwertyu0928 +qwertyu0929 +qwertyu0930 +qwertyu0931 +qwertyu0932 +qwertyu0933 +qwertyu0934 +qwertyu0935 +qwertyu0936 +qwertyu0937 +qwertyu0938 +qwertyu0939 +qwertyu0940 +qwertyu0941 +qwertyu0942 +qwertyu0943 +qwertyu0944 +qwertyu0945 +qwertyu0946 +qwertyu0947 +qwertyu0948 +qwertyu0949 +qwertyu0950 +qwertyu0951 +qwertyu0952 +qwertyu0953 +qwertyu0954 +qwertyu0955 +qwertyu0956 +qwertyu0957 +qwertyu0958 +qwertyu0959 +qwertyu0960 +qwertyu0961 +qwertyu0962 +qwertyu0963 +qwertyu0964 +qwertyu0965 +qwertyu0966 +qwertyu0967 +qwertyu0968 +qwertyu0969 +qwertyu0970 +qwertyu0971 +qwertyu0972 +qwertyu0973 +qwertyu0974 +qwertyu0975 +qwertyu0976 +qwertyu0977 +qwertyu0978 +qwertyu0979 +qwertyu0980 +qwertyu0981 +qwertyu0982 +qwertyu0983 +qwertyu0984 +qwertyu0985 +qwertyu0986 +qwertyu0987 +qwertyu0988 +qwertyu0989 +qwertyu0990 +qwertyu0991 +qwertyu0992 +qwertyu0993 +qwertyu0994 +qwertyu0995 +qwertyu0996 +qwertyu0997 +qwertyu0998 +qwertyu0999 +qwertyu1000 diff --git a/contrib/pg_trgm/data/trgm2.data b/contrib/pg_trgm/data/trgm2.data new file mode 100644 index 0000000..664e079 --- /dev/null +++ b/contrib/pg_trgm/data/trgm2.data @@ -0,0 +1,696 @@ +Baikal +Baikaluobbal +Lake Baikal +Baikalakko +Baikal Business Centre +Baikal Listvyanka Hotel +Baikal Airfield +Baikalovo +Transbaikalia +Baikal Mountains +Baikal Hotel Moscow +Zabaikalie +Pribaikalskaya +Baikal Plaza +Rubaikale +Tandobai Algad +Daikalay +Bakall +Stubaital +Neustift im Stubaital +Anonyme Appartments Stubaital +Barkaladja Pool +Awabakal Nature Reserve +Awabakal Field Studies Centre +Barkala +Bailallie +Barkala Park +Purba Kalaujan +Nabakalas +Barkal +Baikanthapur +Baikarjhuti +Baika +Baikari +Bakalia Char +Dakshin Bakalia +Purba Kalmegha +Efreytor-Bakalovo +Baykalsko +Baykal +Baskaltsi +Bakalite +Bajkal +Efrejtor Bakalovo +Kampong Bakaladong +Riacho do Sambaibal +Sambaibal +Barkalabava +Zabaykal +Bakalar Lake +Kaikalahun Indian Reserve 25 +Tumba-Kalamba +Kamba-Kalele +Boyagbakala +Bombakalo +Batikalengbe +Bakalukudu +Bakalawa +Bakala +Matamba-Kalenge +Kusu-Bakali +Kambakala +Bakali +Abakalu +Bonagbakala +Bakalua +Bikala Madila +Bikala +Bumba-Kaloki +Tumba-Kalunga +Kabankala +Mambakala +Tumba-Kalumba +Kabakala +Bikalabwa +Bomba-Kalende +Mwalaba-Kalamba +Matamba-Kalenga +Bumba-Kalumba +Bikalange +Kabikala +Mubikale +Kanampumba-Kalawa +Tshiabakale +Bakaly +Bakalongo +Bakale +Bakala Koupi +Bambakala +Bakalou +Tsibakala +Kimbakala +Dabakalakoro +Dabakala +Bakalafoulou +Ngao Bakala +Mobaika +Baimalou +Xibaitaling +Baikai +Baikang +Baitaling +Baikan +Baimaling Linchang +Baimalong +Baikanzui +Baiyali +Baimaling +Baimalang Donggang +Baikangshuoma +Baitaliao +Taikale +Babainale +Bailale +Baibale +Baiwale +Baikangnei +Baitali +Xiabaikan +Bailalong +Baimaluo +Baikacun +Baisala +Bailalin +Baimala +Baidalong +Dabaika +Caikalong +Cuobaikacun +Baikadangcun +Baimalin +Subaika +Gabakkale +Barkallou +Embatkala +Bodega Tabaibal +Golba Kalo +Haikala +Kaikale +Waikaloulevu +Waikalou Creek +Waikalou +Ndelaikalou +Ndelaikalokalo +Bay of Backaland +Bankali +Ker Samba Kalla +Demba Kali +Bakalarr +Baipal +Kalibakalako +Dalabakala +Bikal +Sembaikan +Praikalogu +Tanjung Ompaikalio +Bonebabakal +Tanjung Batikala +Pulau Bakalanpauno +Teluk Bakalan +Bakaltua Bank +Bakalrejo +Bakalan +Sungai Bakaladiyan +Bakal +Buku Baikole +Pulau Baika +Tanjung Bakalinga +Pulau Bakalan +Desa Bakalan +Kebakkalang +Ngambakalang +Mota Sabakal +Bakalan Lor +Babakalo +Buyu Rapanbakalai +Kalimundubakalan +Bakalpokok +Bakaldukuh +Tanabakal +Tanjung Aikaluin +Desa Bakalrejo +Bakalan Kidul +Desa Kebakalan +Kebakalan +Bakalan Kulon +Gunung Bakalan +Kalibakal +Bakaljaya +Trobakal +Bakalan Wetan +Desa Bakal +Alue Bakkala +Uruk Bakal +Bakalbuah +Kwala Bakala +Bakal Lama +Bakal Julu +Bakal Batu +Moncong Baika +Sampangbakalan +Bakalam +Desa Bakalankrapyak +Lebakkalapa Tonggoh +Trembakal +Bakalan Tengah +Kali Bakalan +Desa Cemengbakalan +Desa Bakalanpule +Gunung Bakal +Desa Tambakkalisogo +Tambakkalisogo +Desa Bakalanrayung +Salu Bakalaeng +Bakalaeng +Danau Bakalan +Selat Bakalan +Selat Bakalanpauno +Laikalanda +Bakalinga +Tanjung Mbakalang +Desa Bakalankrajan +Bakalan Dua +Kali Purbakala +Desa Bakalanwringinpitu +Tukad Kubakal +Praikalangga +Banjar Kubakal +Eat Bakal +Sungai Bakala +Kombakalada +Sori Rabakalo +Kahambikalela +Baikarara +Baikapaka +Tukad Bakalan +Teluk Haludubakal +Yabakalewa +Praikalumbang +Waikalowo +Praikalubu +Loko Praikalubu +Ramuk Ombakalada +Praikalebung +Praikaleka +Andabakal +Praikalau +Praikalokat +Praikalimbung +Bambakalo +Leubakkalian +Pematang Baitalimbangan +Lebakalil +Gereba Kaler +Krajan Bakalan +Bakalan Barat +Muarabakal +Umbulan Maharobakal +Bakaldalam +Talang Bakal +Pematang Bakalpanang +Baidaloen +Jatibakal +Tubu Bakalekuk +Dola Peimambakal +Bakalang +Teluk Bakalang +Salu Baidale +Bakalerek +Ile Bakalibu +Parbakalan +Praikalembu +Palindi Laikali +Praikalu +Sori Labakalate +Air Bakal-kecil +Sungaikalung +Sungaikalong +Pematang Bakalpanjang +Payabakal +Waikala +Sungaikali +Sungai Pebakalan +Parit Membakal +Bakalpakebo +Baikat Abu Jaraban +Maikalganj +Maikala Range +Bakalha +Baitalpur +Baikanthpur +Baihal +Barkala Reserved Forest +Babaipalli +Kaikalapettai +Kambainallur +Bakkalale +Kaikalui +Baijalpur +Nehalla Bankalah Reserved Forest +Barkala Rao +Barkali +Baidal +Barkaleh +Darreh Pumba Kal +Bahkalleh +Wibakale +Gaikali +Gagaba Kalo +Barkalare +Bakkalmal +Gora Bakalyadyr +Rodnik Bakalybulak +Urochishche Bakaly +Sopka Bakaly +Gory Bakaly +Bugor Arba-Kalgan +Ozero Baykal +Kolodets Tabakkalgan +Walangivattu Vaikal +Vattevaikal Anicut +Vaikali Tevar Kulam +Vaikalitevan Kulam +Vaikaladichchenai +Uchchodaikallu +Sellapattu Vaikal +Savata Vaikal +Puttadivali Vaikal +Palukadu Vaikal +Mulaikallu Kulam +Koraikallimadu +Koraikalapu Kulam +Karaiyamullivaikal +Karaivaikal Kulam +Kanawali Vaikal +Habakkala +Chalam Vaikal Aru +Ambakala Wewa +Alaikallupoddakulam +Alaikallupodda Alankulam +Akamadi Vaikal +Alaikalluppodda Kulam +Vaikaliththevakulam +Baikole +Sidi Mohammed el Bakali +Sidi Mohammed Bakkal +Sidi Bakal +Oulad el Bakkal +Zaouia Oulad Bakal +Azib el Bakkali +Tombakala +Malaikaly +Ambadikala +Bakalica +Bakalnica +Abankala +Kombakala +Bawkalut +Bakaleko +Bawkalut Chaung +Baukala +Cerro Bainaltzin +Sungai Bakal +Bukit Ubaibalih +Kampong Sombakal +Kampung Lebai Ali +Batikal +Bakalalan Airport +Maikali +Bakalum +Bakalambani +Abakaliki +Tsaunin Maikalaji +Baikaha +Llano Limbaika +Barkald +Barkald stasjon +Barkaleitet +Barkaldfossen +Barkaldvola +Bakkalegskardet +Baikajavri +Barkalden +Bakkalia +Siljabaika +Aikaluokta +Blombakkali +Bavkalasis +Baikajohka +Bakkalykkja +Bakalauri +Bakalauri1 +Bakalauri2 +Bakalauri3 +Bakalauri4 +Bakalauri5 +Bakalauri6 +Bakalauri7 +Bakalauri8 +Bakalauri9 +Bakalsen +Baiyaldi +Naikala +Baikanda +Barkalne +Bakalipur +Bakaldum +Raikal +Baikatte +Maikal +Bakalbhar +Waikalabubu Bay +Baikai Island +Abikal +Boikalakalawa Bay +Maikal River +Bakalao Asibi Point +Bankal +Bakalod Island +Bakalao Point +Bakalan River +Bakal Dos +Bakal Uno +Daang Bakal +Bankal School +Bakal Tres +Kabankalan City Public Plaza +Ranra Tabai Algad +Bairkal Jabal +Bairkal Dhora +Bairkal +Zaibai Algad +Gulba Kalle +Ragha Bakalzai +Dabbarkal Sar +Tabai Algad +Haikalzai +Wuchobai Algad +Jabba Kalai +Goth Soba Kaloi +Baikar Tsarai +Dudgaikal +Baixale Kamar +Zebai Algad +Bakal Khel +Goth Haikal +Haikal +Jaba Kalle +Bakalovina +Salabaikasy +Guba Kalita +Guba Kalgalaksha +Guba Kaldo +Bakalovo +Baykalovo +Baskalino +Sopka Barkaleptskaya +Bakalovskaya Ferma +Bakalinskiy Rayon +Sovkhoz Bakalinskiy +Bakalinskiy +Bakaldy +Bakaldinskoye +Urochishche Bakaldikha +Zabaykalovskiy +Barkalova +Barkalovka +Gora Barkalova +Gora Barkalyu +Bikalamakhi +Stantsiya Bakal +Baykalovskiy Rayon +Baykalovskiy +Baykalovsk +Bakalda +Boloto Malyy Baykal +Boloto Baykal +Zabaykalka +Stantsiya Baykal +Baykalo-Amurskaya Zheleznaya Doroga +Kolkhoz Krasnyy Baykal +Zaliv Baykal +Bakalino +Ovrag Bakalda +Bakaldovshchina +Prud Novyy Baykal +Bakaleyka +Bakalka +Bakaly TV Mast +Urochishche Bakalovo +Kambaika +Maloye Baykalovo +Bakalinskiy Leskhoz +Bikalikha +Kordon Barkalo +Sanatoriy Baykal +Port Baykal +Baykalikha +Polevoy Stan Baykal +Bakalovka +Ramada Makkah Shubaika +Mount Tohebakala +Tambakale Island +Mbanitambaika Island +Mbakalaka Island +Kumbakale +Kaikaloka +Kelesaikal +Nasb Gabakallah +Jabal Barkal +Jabal Abakallah +Al Barkali +Shabakal Abbass +Mabaikuli +Bambakalema +Bambakalia +Baiwala +Babakalia +Baikama +Bankalol +Kundebakali +Yumbaikamadu +Tabakali +Daba Kalharereh +Barkale +Bakalshile +Bakaloolay +Buur Bakaley +Bakaley +Buur Bakale +Bakalaale +Jabal Mobakali +Khor Bakallii +Korombaital +Ambakali +Ba Kaliin +Mbay Bakala +Tagobikala +Fayzabadkala +Aghbai Allazy +Aghbai Alikagar +Gora Fayzabadkala +Daraikalot +Aghbai Alakisirak +Beikala +Foho Berbakalau +Mota Caicabaisala +Sungai Utabailale +Urochishche Bakalarnyn-Ayasy +Urochishche Batkali +Khrebet Batkali +Ras Barkallah +Babakale +Fabrikalar +Bakalukalu Shan +Bakalukalu +Laikala +Waikalakaka +Columbus Bakalar Municipal Airport +Bakalar Library +Bakkala Cemetery +Clifton T Barkalow Elementary School +Barkalow Hollow +Kailuapuhi Waikalua Homesteads +Kawaikalia Gulch +Waikalae +Waikaloa Stream +Waikalua-Loko Fish Pond +Halekou Waikaluakai Homesteads +East Waikalua +Omar Haikal Islamic Academy +Bakalar Air Force Base (historical) +Koshbakaly +Bagkalen +Gora Baikara +Mfumbaika +Mbakalungu +Chumbaika +Ntombankala School +Bakalabwa Pans +Khobai al Janhra +Holiday Inn Dubai Al Barsha +Novotel Dubai Al Barsha +Doubletree Res.Dubai-Al Barsha +Doubletree By Hilton Hotel and Apartments Dubai Al Barsha +Doubletree By Hilton Dubai Al Barsha Hotel and Res +Park Inn By Radisson Dubai Al Barsha +Ramee Rose Hotel Dubai Al Barsha +Aparthotel Adagio Premium Dubai Al Barsha +Ataikala +Selman Marrakech +Riad Ain Marrakech +Taj Palace Marrakech +Delano Marrakech +Pullman Marrakech Palmeraie Resort And Spa +Lalla Calipau Marrakech +Hotel Fashion Marrakech +Four Seasons Resort Marrakech +Adama Resort Marrakech +Pullman Marrakech Palmeraie Re +Ramada Resort Marrakech Douar Al Hana +Hotel Zahia Marrakech +Hotel Marrakech Le Tichka +Le Chems Marrakech +Beachcomber Royal Palm Marrakech +Residence Marrakech +Riad Hermes Marrakech +Riad La Lune De Marrakech +Hotel Marrakech Le Sangho Privilege +Tempoo Hotel Marrakech +Ag Hotel & Spa Marrakech +Palm Appart Club Marrakech +Hotel Ibis Moussafir Marrakech Palmeraie +Ibis Marrakech Gare Voyageurs +Marrakech Ryads Parc And Spa +Terra Mia Marrakech Riad +Residence Dar Lamia Marrakech +Pullman Marrakech Palmeraie Rs +Moussaf Marrakech Centre Gare +Tempoo Hotel Marrakech Adults Only +Sahara Palace Marrakech +Moroccan House Marrakech +El Andalouss And Spa Marrakech +Suite Novotel Marrakech Rs +Dar Catalina Marrakech Hotel Non Refundable Room +Marrakech Hotel +Oued Tammarrakech +Tammarrakech +Cercle de Marrakech-Banlieue +Marrakech-Tensift-Al Haouz +Koudia Marrakech +Hotel Tichka Salam Marrakech +L'Atlas Marrakech +Royal Mirage Deluxe Marrakech +Golden Tulip Farah Marrakech +Ryad Mogador Marrakech +Coralia Club Marrakech Palmariva +La Sultana Marrakech +Marrakech-Medina +Marrakech +Museum of Marrakech +Douar Marrakechiyinc +Ibis Marrakech Centre Gare +Golden Tulip Rawabi Marrakech +Murano Resort Marrakech +Marrakech Garden Hotel +Pullman Marrakech Palmerai Resort & Spa +The Pearl Marrakech +Palais Calipau Marrakech +Hostal Equity Point Marrakech +Sofitel Marrakech Lounge And Spa +Pullman Marrakech Hotel And Spa +Sofitel Marrakech Palais Imperial +Hotel Ibis Moussafir Marrakech Centre Gare +Red Hotel Marrakech +Riad Zenith Marrakech +Ksar Catalina Marrakech Hotel +Blue Sea Hotel Marrakech Ryads Parc & Spa +Bluebay Marrakech +Pullman Marrakech Palmeraie Resort & Spa Hotel +Riad Litzy Marrakech +Sultana Hotel & Spa Marrakech +Albatros Club Marrakech +Hotel Sangho Club Marrakech +Suite Novotel Marrakech Hotel +Riad Utopia Suites & Spa Marrakech +Riad Fatinat Marrakech +Riad Dar El Aila Marrakech +Es Saadi And Casino De Marrakech +Dar Catalina Marrakech Hotel +Grace Marrakech +Marrakesh Apartments +Marrakesh Country Club +Koudiat Lmerrakechiyine +Sidi Mohammed el Marrakchi +Marrakesh +Marrakchien +Marrakchia +Marrakesh Menara Airport +Marrakesh Hua Hin Resort & Spa +Marrakesh Hua Hin Resort And Spa +Marrakesh Resort And Spa (Pool Suite) +Marrakesh Huahin Resort & Spa +Ibis Moussafir Marrakesh Centre Gare Hotel +Maerak-chi +Dar Hammou Ben Merrakchi +Lalla el Marakchia +Khrebet Marrakh +Sungai Maru Kechil +Marrache +Goth Marracha +Maramech Hill +Maramech Woods Nature Preserve +Oued Karakech +Samarra School +Jangal-e Marakeh Sar diff --git a/contrib/pg_trgm/expected/pg_strict_word_trgm.out b/contrib/pg_trgm/expected/pg_strict_word_trgm.out new file mode 100644 index 0000000..1e1ee16 --- /dev/null +++ b/contrib/pg_trgm/expected/pg_strict_word_trgm.out @@ -0,0 +1,1027 @@ +DROP INDEX trgm_idx2; +\copy test_trgm3 from 'data/trgm2.data' +ERROR: relation "test_trgm3" does not exist +-- reduce noise +set extra_float_digits = 0; +select t,strict_word_similarity('Baykal',t) as sml from test_trgm2 where 'Baykal' <<% t order by sml desc, t; + t | sml +-------------------------------------+---------- + Baykal | 1 + Boloto Baykal | 1 + Boloto Malyy Baykal | 1 + Kolkhoz Krasnyy Baykal | 1 + Ozero Baykal | 1 + Polevoy Stan Baykal | 1 + Port Baykal | 1 + Prud Novyy Baykal | 1 + Sanatoriy Baykal | 1 + Stantsiya Baykal | 1 + Zaliv Baykal | 1 + Baykalo-Amurskaya Zheleznaya Doroga | 0.666667 + Baykalovo | 0.545455 + Baykalsko | 0.545455 + Maloye Baykalovo | 0.545455 + Baykalikha | 0.5 + Baykalovsk | 0.5 +(17 rows) + +select t,strict_word_similarity('Kabankala',t) as sml from test_trgm2 where 'Kabankala' <<% t order by sml desc, t; + t | sml +------------------------------+---------- + Kabankala | 1 + Kabankalan City Public Plaza | 0.75 + Abankala | 0.583333 + Kabakala | 0.583333 +(4 rows) + +select t,strict_word_similarity('Baykal',t) as sml from test_trgm2 where t %>> 'Baykal' order by sml desc, t; + t | sml +-------------------------------------+---------- + Baykal | 1 + Boloto Baykal | 1 + Boloto Malyy Baykal | 1 + Kolkhoz Krasnyy Baykal | 1 + Ozero Baykal | 1 + Polevoy Stan Baykal | 1 + Port Baykal | 1 + Prud Novyy Baykal | 1 + Sanatoriy Baykal | 1 + Stantsiya Baykal | 1 + Zaliv Baykal | 1 + Baykalo-Amurskaya Zheleznaya Doroga | 0.666667 + Baykalovo | 0.545455 + Baykalsko | 0.545455 + Maloye Baykalovo | 0.545455 + Baykalikha | 0.5 + Baykalovsk | 0.5 +(17 rows) + +select t,strict_word_similarity('Kabankala',t) as sml from test_trgm2 where t %>> 'Kabankala' order by sml desc, t; + t | sml +------------------------------+---------- + Kabankala | 1 + Kabankalan City Public Plaza | 0.75 + Abankala | 0.583333 + Kabakala | 0.583333 +(4 rows) + +select t <->>> 'Alaikallupoddakulam', t from test_trgm2 order by t <->>> 'Alaikallupoddakulam' limit 7; + ?column? | t +----------+-------------------------- + 0 | Alaikallupoddakulam + 0.25 | Alaikallupodda Alankulam + 0.32 | Alaikalluppodda Kulam + 0.615385 | Mulaikallu Kulam + 0.724138 | Koraikalapu Kulam + 0.75 | Vaikaliththevakulam + 0.766667 | Karaivaikal Kulam +(7 rows) + +create index trgm_idx2 on test_trgm2 using gist (t gist_trgm_ops); +set enable_seqscan=off; +select t,strict_word_similarity('Baykal',t) as sml from test_trgm2 where 'Baykal' <<% t order by sml desc, t; + t | sml +-------------------------------------+---------- + Baykal | 1 + Boloto Baykal | 1 + Boloto Malyy Baykal | 1 + Kolkhoz Krasnyy Baykal | 1 + Ozero Baykal | 1 + Polevoy Stan Baykal | 1 + Port Baykal | 1 + Prud Novyy Baykal | 1 + Sanatoriy Baykal | 1 + Stantsiya Baykal | 1 + Zaliv Baykal | 1 + Baykalo-Amurskaya Zheleznaya Doroga | 0.666667 + Baykalovo | 0.545455 + Baykalsko | 0.545455 + Maloye Baykalovo | 0.545455 + Baykalikha | 0.5 + Baykalovsk | 0.5 +(17 rows) + +select t,strict_word_similarity('Kabankala',t) as sml from test_trgm2 where 'Kabankala' <<% t order by sml desc, t; + t | sml +------------------------------+---------- + Kabankala | 1 + Kabankalan City Public Plaza | 0.75 + Abankala | 0.583333 + Kabakala | 0.583333 +(4 rows) + +select t,strict_word_similarity('Baykal',t) as sml from test_trgm2 where t %>> 'Baykal' order by sml desc, t; + t | sml +-------------------------------------+---------- + Baykal | 1 + Boloto Baykal | 1 + Boloto Malyy Baykal | 1 + Kolkhoz Krasnyy Baykal | 1 + Ozero Baykal | 1 + Polevoy Stan Baykal | 1 + Port Baykal | 1 + Prud Novyy Baykal | 1 + Sanatoriy Baykal | 1 + Stantsiya Baykal | 1 + Zaliv Baykal | 1 + Baykalo-Amurskaya Zheleznaya Doroga | 0.666667 + Baykalovo | 0.545455 + Baykalsko | 0.545455 + Maloye Baykalovo | 0.545455 + Baykalikha | 0.5 + Baykalovsk | 0.5 +(17 rows) + +select t,strict_word_similarity('Kabankala',t) as sml from test_trgm2 where t %>> 'Kabankala' order by sml desc, t; + t | sml +------------------------------+---------- + Kabankala | 1 + Kabankalan City Public Plaza | 0.75 + Abankala | 0.583333 + Kabakala | 0.583333 +(4 rows) + +explain (costs off) +select t <->>> 'Alaikallupoddakulam', t from test_trgm2 order by t <->>> 'Alaikallupoddakulam' limit 7; + QUERY PLAN +--------------------------------------------------------- + Limit + -> Index Scan using trgm_idx2 on test_trgm2 + Order By: (t <->>> 'Alaikallupoddakulam'::text) +(3 rows) + +select t <->>> 'Alaikallupoddakulam', t from test_trgm2 order by t <->>> 'Alaikallupoddakulam' limit 7; + ?column? | t +----------+-------------------------- + 0 | Alaikallupoddakulam + 0.25 | Alaikallupodda Alankulam + 0.32 | Alaikalluppodda Kulam + 0.615385 | Mulaikallu Kulam + 0.724138 | Koraikalapu Kulam + 0.75 | Vaikaliththevakulam + 0.766667 | Karaivaikal Kulam +(7 rows) + +drop index trgm_idx2; +create index trgm_idx2 on test_trgm2 using gin (t gin_trgm_ops); +set enable_seqscan=off; +select t,strict_word_similarity('Baykal',t) as sml from test_trgm2 where 'Baykal' <<% t order by sml desc, t; + t | sml +-------------------------------------+---------- + Baykal | 1 + Boloto Baykal | 1 + Boloto Malyy Baykal | 1 + Kolkhoz Krasnyy Baykal | 1 + Ozero Baykal | 1 + Polevoy Stan Baykal | 1 + Port Baykal | 1 + Prud Novyy Baykal | 1 + Sanatoriy Baykal | 1 + Stantsiya Baykal | 1 + Zaliv Baykal | 1 + Baykalo-Amurskaya Zheleznaya Doroga | 0.666667 + Baykalovo | 0.545455 + Baykalsko | 0.545455 + Maloye Baykalovo | 0.545455 + Baykalikha | 0.5 + Baykalovsk | 0.5 +(17 rows) + +select t,strict_word_similarity('Kabankala',t) as sml from test_trgm2 where 'Kabankala' <<% t order by sml desc, t; + t | sml +------------------------------+---------- + Kabankala | 1 + Kabankalan City Public Plaza | 0.75 + Abankala | 0.583333 + Kabakala | 0.583333 +(4 rows) + +select t,strict_word_similarity('Baykal',t) as sml from test_trgm2 where t %>> 'Baykal' order by sml desc, t; + t | sml +-------------------------------------+---------- + Baykal | 1 + Boloto Baykal | 1 + Boloto Malyy Baykal | 1 + Kolkhoz Krasnyy Baykal | 1 + Ozero Baykal | 1 + Polevoy Stan Baykal | 1 + Port Baykal | 1 + Prud Novyy Baykal | 1 + Sanatoriy Baykal | 1 + Stantsiya Baykal | 1 + Zaliv Baykal | 1 + Baykalo-Amurskaya Zheleznaya Doroga | 0.666667 + Baykalovo | 0.545455 + Baykalsko | 0.545455 + Maloye Baykalovo | 0.545455 + Baykalikha | 0.5 + Baykalovsk | 0.5 +(17 rows) + +select t,strict_word_similarity('Kabankala',t) as sml from test_trgm2 where t %>> 'Kabankala' order by sml desc, t; + t | sml +------------------------------+---------- + Kabankala | 1 + Kabankalan City Public Plaza | 0.75 + Abankala | 0.583333 + Kabakala | 0.583333 +(4 rows) + +set "pg_trgm.strict_word_similarity_threshold" to 0.4; +select t,strict_word_similarity('Baykal',t) as sml from test_trgm2 where 'Baykal' <<% t order by sml desc, t; + t | sml +-------------------------------------+---------- + Baykal | 1 + Boloto Baykal | 1 + Boloto Malyy Baykal | 1 + Kolkhoz Krasnyy Baykal | 1 + Ozero Baykal | 1 + Polevoy Stan Baykal | 1 + Port Baykal | 1 + Prud Novyy Baykal | 1 + Sanatoriy Baykal | 1 + Stantsiya Baykal | 1 + Zaliv Baykal | 1 + Baykalo-Amurskaya Zheleznaya Doroga | 0.666667 + Baykalovo | 0.545455 + Baykalsko | 0.545455 + Maloye Baykalovo | 0.545455 + Baykalikha | 0.5 + Baykalovsk | 0.5 + Zabaykal | 0.454545 + Air Bakal-kecil | 0.444444 + Bakal | 0.444444 + Bakal Batu | 0.444444 + Bakal Dos | 0.444444 + Bakal Julu | 0.444444 + Bakal Khel | 0.444444 + Bakal Lama | 0.444444 + Bakal Tres | 0.444444 + Bakal Uno | 0.444444 + Daang Bakal | 0.444444 + Desa Bakal | 0.444444 + Eat Bakal | 0.444444 + Gunung Bakal | 0.444444 + Sidi Bakal | 0.444444 + Stantsiya Bakal | 0.444444 + Sungai Bakal | 0.444444 + Talang Bakal | 0.444444 + Uruk Bakal | 0.444444 + Zaouia Oulad Bakal | 0.444444 + Baykalovskiy | 0.428571 + Baykalovskiy Rayon | 0.428571 + Baikal | 0.4 + Baikal Airfield | 0.4 + Baikal Business Centre | 0.4 + Baikal Hotel Moscow | 0.4 + Baikal Listvyanka Hotel | 0.4 + Baikal Mountains | 0.4 + Baikal Plaza | 0.4 + Bajkal | 0.4 + Bankal | 0.4 + Bankal School | 0.4 + Barkal | 0.4 + Jabal Barkal | 0.4 + Lake Baikal | 0.4 + Oulad el Bakkal | 0.4 + Sidi Mohammed Bakkal | 0.4 +(54 rows) + +select t,strict_word_similarity('Kabankala',t) as sml from test_trgm2 where 'Kabankala' <<% t order by sml desc, t; + t | sml +------------------------------+---------- + Kabankala | 1 + Kabankalan City Public Plaza | 0.75 + Abankala | 0.583333 + Kabakala | 0.583333 + Kabikala | 0.461538 +(5 rows) + +select t,strict_word_similarity('Baykal',t) as sml from test_trgm2 where t %>> 'Baykal' order by sml desc, t; + t | sml +-------------------------------------+---------- + Baykal | 1 + Boloto Baykal | 1 + Boloto Malyy Baykal | 1 + Kolkhoz Krasnyy Baykal | 1 + Ozero Baykal | 1 + Polevoy Stan Baykal | 1 + Port Baykal | 1 + Prud Novyy Baykal | 1 + Sanatoriy Baykal | 1 + Stantsiya Baykal | 1 + Zaliv Baykal | 1 + Baykalo-Amurskaya Zheleznaya Doroga | 0.666667 + Baykalovo | 0.545455 + Baykalsko | 0.545455 + Maloye Baykalovo | 0.545455 + Baykalikha | 0.5 + Baykalovsk | 0.5 + Zabaykal | 0.454545 + Air Bakal-kecil | 0.444444 + Bakal | 0.444444 + Bakal Batu | 0.444444 + Bakal Dos | 0.444444 + Bakal Julu | 0.444444 + Bakal Khel | 0.444444 + Bakal Lama | 0.444444 + Bakal Tres | 0.444444 + Bakal Uno | 0.444444 + Daang Bakal | 0.444444 + Desa Bakal | 0.444444 + Eat Bakal | 0.444444 + Gunung Bakal | 0.444444 + Sidi Bakal | 0.444444 + Stantsiya Bakal | 0.444444 + Sungai Bakal | 0.444444 + Talang Bakal | 0.444444 + Uruk Bakal | 0.444444 + Zaouia Oulad Bakal | 0.444444 + Baykalovskiy | 0.428571 + Baykalovskiy Rayon | 0.428571 + Baikal | 0.4 + Baikal Airfield | 0.4 + Baikal Business Centre | 0.4 + Baikal Hotel Moscow | 0.4 + Baikal Listvyanka Hotel | 0.4 + Baikal Mountains | 0.4 + Baikal Plaza | 0.4 + Bajkal | 0.4 + Bankal | 0.4 + Bankal School | 0.4 + Barkal | 0.4 + Jabal Barkal | 0.4 + Lake Baikal | 0.4 + Oulad el Bakkal | 0.4 + Sidi Mohammed Bakkal | 0.4 +(54 rows) + +select t,strict_word_similarity('Kabankala',t) as sml from test_trgm2 where t %>> 'Kabankala' order by sml desc, t; + t | sml +------------------------------+---------- + Kabankala | 1 + Kabankalan City Public Plaza | 0.75 + Abankala | 0.583333 + Kabakala | 0.583333 + Kabikala | 0.461538 +(5 rows) + +set "pg_trgm.strict_word_similarity_threshold" to 0.2; +select t,strict_word_similarity('Baykal',t) as sml from test_trgm2 where 'Baykal' <<% t order by sml desc, t; + t | sml +-----------------------------------------------------------+---------- + Baykal | 1 + Boloto Baykal | 1 + Boloto Malyy Baykal | 1 + Kolkhoz Krasnyy Baykal | 1 + Ozero Baykal | 1 + Polevoy Stan Baykal | 1 + Port Baykal | 1 + Prud Novyy Baykal | 1 + Sanatoriy Baykal | 1 + Stantsiya Baykal | 1 + Zaliv Baykal | 1 + Baykalo-Amurskaya Zheleznaya Doroga | 0.666667 + Baykalovo | 0.545455 + Baykalsko | 0.545455 + Maloye Baykalovo | 0.545455 + Baykalikha | 0.5 + Baykalovsk | 0.5 + Zabaykal | 0.454545 + Air Bakal-kecil | 0.444444 + Bakal | 0.444444 + Bakal Batu | 0.444444 + Bakal Dos | 0.444444 + Bakal Julu | 0.444444 + Bakal Khel | 0.444444 + Bakal Lama | 0.444444 + Bakal Tres | 0.444444 + Bakal Uno | 0.444444 + Daang Bakal | 0.444444 + Desa Bakal | 0.444444 + Eat Bakal | 0.444444 + Gunung Bakal | 0.444444 + Sidi Bakal | 0.444444 + Stantsiya Bakal | 0.444444 + Sungai Bakal | 0.444444 + Talang Bakal | 0.444444 + Uruk Bakal | 0.444444 + Zaouia Oulad Bakal | 0.444444 + Baykalovskiy | 0.428571 + Baykalovskiy Rayon | 0.428571 + Baikal | 0.4 + Baikal Airfield | 0.4 + Baikal Business Centre | 0.4 + Baikal Hotel Moscow | 0.4 + Baikal Listvyanka Hotel | 0.4 + Baikal Mountains | 0.4 + Baikal Plaza | 0.4 + Bajkal | 0.4 + Bankal | 0.4 + Bankal School | 0.4 + Barkal | 0.4 + Jabal Barkal | 0.4 + Lake Baikal | 0.4 + Oulad el Bakkal | 0.4 + Sidi Mohammed Bakkal | 0.4 + Bay of Backaland | 0.375 + Boikalakalawa Bay | 0.375 + Waikalabubu Bay | 0.375 + Bairkal | 0.363636 + Bairkal Dhora | 0.363636 + Bairkal Jabal | 0.363636 + Batikal | 0.363636 + Bakaleyka | 0.307692 + Bakkalmal | 0.307692 + Bikal | 0.3 + Al Barkali | 0.285714 + Zabaykalka | 0.285714 + Baidal | 0.272727 + Baihal | 0.272727 + Baipal | 0.272727 + Bakala | 0.272727 + Bakala Koupi | 0.272727 + Bakale | 0.272727 + Bakali | 0.272727 + Bakall | 0.272727 + Bakaly | 0.272727 + Bakaly TV Mast | 0.272727 + Buur Bakale | 0.272727 + Gory Bakaly | 0.272727 + Kusu-Bakali | 0.272727 + Kwala Bakala | 0.272727 + Mbay Bakala | 0.272727 + Ngao Bakala | 0.272727 + Sidi Mohammed el Bakali | 0.272727 + Sopka Bakaly | 0.272727 + Sungai Bakala | 0.272727 + Urochishche Bakaly | 0.272727 + Alue Bakkala | 0.25 + Azib el Bakkali | 0.25 + Ba Kaliin | 0.25 + Baikaluobbal | 0.25 + Bakalam | 0.25 + Bakalan | 0.25 + Bakalan Barat | 0.25 + Bakalan Dua | 0.25 + Bakalan Kidul | 0.25 + Bakalan Kulon | 0.25 + Bakalan Lor | 0.25 + Bakalan River | 0.25 + Bakalan Tengah | 0.25 + Bakalan Wetan | 0.25 + Bakalao Asibi Point | 0.25 + Bakalao Point | 0.25 + Bakalar Air Force Base (historical) | 0.25 + Bakalar Lake | 0.25 + Bakalar Library | 0.25 + Bakalda | 0.25 + Bakaldy | 0.25 + Bakaley | 0.25 + Bakalha | 0.25 + Bakalia Char | 0.25 + Bakalka | 0.25 + Bakalod Island | 0.25 + Bakalou | 0.25 + Bakalua | 0.25 + Bakalum | 0.25 + Bakkala Cemetery | 0.25 + Bankali | 0.25 + Barkala | 0.25 + Barkala Park | 0.25 + Barkala Rao | 0.25 + Barkala Reserved Forest | 0.25 + Barkald | 0.25 + Barkald stasjon | 0.25 + Barkale | 0.25 + Barkali | 0.25 + Baukala | 0.25 + Buur Bakaley | 0.25 + Columbus Bakalar Municipal Airport | 0.25 + Dakshin Bakalia | 0.25 + Danau Bakalan | 0.25 + Desa Bakalan | 0.25 + Gunung Bakalan | 0.25 + Kali Bakalan | 0.25 + Khrebet Batkali | 0.25 + Kordon Barkalo | 0.25 + Krajan Bakalan | 0.25 + Ovrag Bakalda | 0.25 + Pulau Bakalan | 0.25 + Selat Bakalan | 0.25 + Teluk Bakalan | 0.25 + Tukad Bakalan | 0.25 + Urochishche Batkali | 0.25 + Babakale | 0.230769 + Babakalo | 0.230769 + Bagkalen | 0.230769 + Bakalalan Airport | 0.230769 + Bakalang | 0.230769 + Bakalarr | 0.230769 + Bakalawa | 0.230769 + Bakaldum | 0.230769 + Bakaleko | 0.230769 + Bakalica | 0.230769 + Bakalino | 0.230769 + Bakalite | 0.230769 + Bakalovo | 0.230769 + Bakalsen | 0.230769 + Bakaltua Bank | 0.230769 + Bakalukalu | 0.230769 + Bakalukalu Shan | 0.230769 + Bakkalia | 0.230769 + Bankalol | 0.230769 + Barkaleh | 0.230769 + Barkalne | 0.230769 + Barkalow Hollow | 0.230769 + Bawkalut | 0.230769 + Bawkalut Chaung | 0.230769 + Clifton T Barkalow Elementary School | 0.230769 + Efrejtor Bakalovo | 0.230769 + Efreytor-Bakalovo | 0.230769 + Gora Barkalyu | 0.230769 + Ile Bakalibu | 0.230769 + Khor Bakallii | 0.230769 + Nehalla Bankalah Reserved Forest | 0.230769 + Ragha Bakalzai | 0.230769 + Tanjung Batikala | 0.230769 + Teluk Bakalang | 0.230769 + Urochishche Bakalovo | 0.230769 + Banjar Kubakal | 0.222222 + Darreh Pumba Kal | 0.222222 + Zabaykalovskiy | 0.222222 + Aparthotel Adagio Premium Dubai Al Barsha | 0.214286 + Babakalia | 0.214286 + Bahkalleh | 0.214286 + Baikalovo | 0.214286 + Bakalaale | 0.214286 + Bakalabwa Pans | 0.214286 + Bakalaeng | 0.214286 + Bakalauri | 0.214286 + Bakalbhar | 0.214286 + Bakalbuah | 0.214286 + Bakalerek | 0.214286 + Bakalinga | 0.214286 + Bakalipur | 0.214286 + Bakaljaya | 0.214286 + Bakalnica | 0.214286 + Bakalongo | 0.214286 + Bakalovka | 0.214286 + Bakalrejo | 0.214286 + Bakkalale | 0.214286 + Bambakala | 0.214286 + Bambakalo | 0.214286 + Barkalare | 0.214286 + Barkalden | 0.214286 + Barkallou | 0.214286 + Barkalova | 0.214286 + Baskalino | 0.214286 + Baskaltsi | 0.214286 + Desa Bakalrejo | 0.214286 + Doubletree By Hilton Dubai Al Barsha Hotel and Res | 0.214286 + Doubletree By Hilton Hotel and Apartments Dubai Al Barsha | 0.214286 + Doubletree Res.Dubai-Al Barsha | 0.214286 + Gora Barkalova | 0.214286 + Holiday Inn Dubai Al Barsha | 0.214286 + Novotel Dubai Al Barsha | 0.214286 + Park Inn By Radisson Dubai Al Barsha | 0.214286 + Ramee Rose Hotel Dubai Al Barsha | 0.214286 + Ras Barkallah | 0.214286 + Salu Bakalaeng | 0.214286 + Tanjung Bakalinga | 0.214286 + Tubu Bakalekuk | 0.214286 + Baikalakko | 0.2 + Bakalauri1 | 0.2 + Bakalauri2 | 0.2 + Bakalauri3 | 0.2 + Bakalauri4 | 0.2 + Bakalauri5 | 0.2 + Bakalauri6 | 0.2 + Bakalauri7 | 0.2 + Bakalauri8 | 0.2 + Bakalauri9 | 0.2 + Bakaldalam | 0.2 + Bakaldukuh | 0.2 + Bakaloolay | 0.2 + Bakalovina | 0.2 + Bakalpokok | 0.2 + Bakalshile | 0.2 + Bakalukudu | 0.2 + Bambakalia | 0.2 + Barkaladja Pool | 0.2 + Barkalovka | 0.2 + Bavkalasis | 0.2 + Gora Bakalyadyr | 0.2 + Kampong Bakaladong | 0.2 + Urochishche Bakalarnyn-Ayasy | 0.2 + Urochishche Bakaldikha | 0.2 +(245 rows) + +select t,strict_word_similarity('Kabankala',t) as sml from test_trgm2 where 'Kabankala' <<% t order by sml desc, t; + t | sml +----------------------------------+---------- + Kabankala | 1 + Kabankalan City Public Plaza | 0.75 + Abankala | 0.583333 + Kabakala | 0.583333 + Kabikala | 0.461538 + Ntombankala School | 0.375 + Nehalla Bankalah Reserved Forest | 0.357143 + Jabba Kalai | 0.333333 + Kambakala | 0.333333 + Ker Samba Kalla | 0.333333 + Bankal | 0.307692 + Bankal School | 0.307692 + Kanampumba-Kalawa | 0.307692 + Bankali | 0.285714 + Mwalaba-Kalamba | 0.285714 + Tumba-Kalamba | 0.285714 + Darreh Pumba Kal | 0.272727 + Bankalol | 0.266667 + Dabakala | 0.266667 + Purba Kalaujan | 0.266667 + Kali Purbakala | 0.263158 + Dalabakala | 0.25 + Demba Kali | 0.25 + Gagaba Kalo | 0.25 + Golba Kalo | 0.25 + Habakkala | 0.25 + Kali Bakalan | 0.25 + Kimbakala | 0.25 + Kombakala | 0.25 + Jaba Kalle | 0.235294 + Kaikalahun Indian Reserve 25 | 0.235294 + Kwala Bakala | 0.235294 + Gereba Kaler | 0.230769 + Goth Soba Kaloi | 0.230769 + Guba Kaldo | 0.230769 + Gulba Kalle | 0.230769 + Guba Kalgalaksha | 0.222222 + Kalibakalako | 0.222222 + Ba Kaliin | 0.214286 + Bakala | 0.214286 + Bakala Koupi | 0.214286 + Bikala | 0.214286 + Bikala Madila | 0.214286 + Bugor Arba-Kalgan | 0.214286 + Bumba-Kaloki | 0.214286 + Guba Kalita | 0.214286 + Kamba-Kalele | 0.214286 + Mbay Bakala | 0.214286 + Ngao Bakala | 0.214286 + Sungai Bakala | 0.214286 + Fayzabadkala | 0.210526 + Gora Fayzabadkala | 0.210526 + Alue Bakkala | 0.2 + Bakkala Cemetery | 0.2 + Barkala | 0.2 + Barkala Park | 0.2 + Barkala Rao | 0.2 + Barkala Reserved Forest | 0.2 + Baukala | 0.2 + Beikala | 0.2 + Bomba-Kalende | 0.2 + Bumba-Kalumba | 0.2 + Haikala | 0.2 + Kahambikalela | 0.2 + Kaikalapettai | 0.2 + Kaikale | 0.2 + Laikala | 0.2 + Maikala Range | 0.2 + Matamba-Kalenga | 0.2 + Matamba-Kalenge | 0.2 + Naikala | 0.2 + Tumba-Kalumba | 0.2 + Tumba-Kalunga | 0.2 + Waikala | 0.2 +(74 rows) + +select t,strict_word_similarity('Baykal',t) as sml from test_trgm2 where t %>> 'Baykal' order by sml desc, t; + t | sml +-----------------------------------------------------------+---------- + Baykal | 1 + Boloto Baykal | 1 + Boloto Malyy Baykal | 1 + Kolkhoz Krasnyy Baykal | 1 + Ozero Baykal | 1 + Polevoy Stan Baykal | 1 + Port Baykal | 1 + Prud Novyy Baykal | 1 + Sanatoriy Baykal | 1 + Stantsiya Baykal | 1 + Zaliv Baykal | 1 + Baykalo-Amurskaya Zheleznaya Doroga | 0.666667 + Baykalovo | 0.545455 + Baykalsko | 0.545455 + Maloye Baykalovo | 0.545455 + Baykalikha | 0.5 + Baykalovsk | 0.5 + Zabaykal | 0.454545 + Air Bakal-kecil | 0.444444 + Bakal | 0.444444 + Bakal Batu | 0.444444 + Bakal Dos | 0.444444 + Bakal Julu | 0.444444 + Bakal Khel | 0.444444 + Bakal Lama | 0.444444 + Bakal Tres | 0.444444 + Bakal Uno | 0.444444 + Daang Bakal | 0.444444 + Desa Bakal | 0.444444 + Eat Bakal | 0.444444 + Gunung Bakal | 0.444444 + Sidi Bakal | 0.444444 + Stantsiya Bakal | 0.444444 + Sungai Bakal | 0.444444 + Talang Bakal | 0.444444 + Uruk Bakal | 0.444444 + Zaouia Oulad Bakal | 0.444444 + Baykalovskiy | 0.428571 + Baykalovskiy Rayon | 0.428571 + Baikal | 0.4 + Baikal Airfield | 0.4 + Baikal Business Centre | 0.4 + Baikal Hotel Moscow | 0.4 + Baikal Listvyanka Hotel | 0.4 + Baikal Mountains | 0.4 + Baikal Plaza | 0.4 + Bajkal | 0.4 + Bankal | 0.4 + Bankal School | 0.4 + Barkal | 0.4 + Jabal Barkal | 0.4 + Lake Baikal | 0.4 + Oulad el Bakkal | 0.4 + Sidi Mohammed Bakkal | 0.4 + Bay of Backaland | 0.375 + Boikalakalawa Bay | 0.375 + Waikalabubu Bay | 0.375 + Bairkal | 0.363636 + Bairkal Dhora | 0.363636 + Bairkal Jabal | 0.363636 + Batikal | 0.363636 + Bakaleyka | 0.307692 + Bakkalmal | 0.307692 + Bikal | 0.3 + Al Barkali | 0.285714 + Zabaykalka | 0.285714 + Baidal | 0.272727 + Baihal | 0.272727 + Baipal | 0.272727 + Bakala | 0.272727 + Bakala Koupi | 0.272727 + Bakale | 0.272727 + Bakali | 0.272727 + Bakall | 0.272727 + Bakaly | 0.272727 + Bakaly TV Mast | 0.272727 + Buur Bakale | 0.272727 + Gory Bakaly | 0.272727 + Kusu-Bakali | 0.272727 + Kwala Bakala | 0.272727 + Mbay Bakala | 0.272727 + Ngao Bakala | 0.272727 + Sidi Mohammed el Bakali | 0.272727 + Sopka Bakaly | 0.272727 + Sungai Bakala | 0.272727 + Urochishche Bakaly | 0.272727 + Alue Bakkala | 0.25 + Azib el Bakkali | 0.25 + Ba Kaliin | 0.25 + Baikaluobbal | 0.25 + Bakalam | 0.25 + Bakalan | 0.25 + Bakalan Barat | 0.25 + Bakalan Dua | 0.25 + Bakalan Kidul | 0.25 + Bakalan Kulon | 0.25 + Bakalan Lor | 0.25 + Bakalan River | 0.25 + Bakalan Tengah | 0.25 + Bakalan Wetan | 0.25 + Bakalao Asibi Point | 0.25 + Bakalao Point | 0.25 + Bakalar Air Force Base (historical) | 0.25 + Bakalar Lake | 0.25 + Bakalar Library | 0.25 + Bakalda | 0.25 + Bakaldy | 0.25 + Bakaley | 0.25 + Bakalha | 0.25 + Bakalia Char | 0.25 + Bakalka | 0.25 + Bakalod Island | 0.25 + Bakalou | 0.25 + Bakalua | 0.25 + Bakalum | 0.25 + Bakkala Cemetery | 0.25 + Bankali | 0.25 + Barkala | 0.25 + Barkala Park | 0.25 + Barkala Rao | 0.25 + Barkala Reserved Forest | 0.25 + Barkald | 0.25 + Barkald stasjon | 0.25 + Barkale | 0.25 + Barkali | 0.25 + Baukala | 0.25 + Buur Bakaley | 0.25 + Columbus Bakalar Municipal Airport | 0.25 + Dakshin Bakalia | 0.25 + Danau Bakalan | 0.25 + Desa Bakalan | 0.25 + Gunung Bakalan | 0.25 + Kali Bakalan | 0.25 + Khrebet Batkali | 0.25 + Kordon Barkalo | 0.25 + Krajan Bakalan | 0.25 + Ovrag Bakalda | 0.25 + Pulau Bakalan | 0.25 + Selat Bakalan | 0.25 + Teluk Bakalan | 0.25 + Tukad Bakalan | 0.25 + Urochishche Batkali | 0.25 + Babakale | 0.230769 + Babakalo | 0.230769 + Bagkalen | 0.230769 + Bakalalan Airport | 0.230769 + Bakalang | 0.230769 + Bakalarr | 0.230769 + Bakalawa | 0.230769 + Bakaldum | 0.230769 + Bakaleko | 0.230769 + Bakalica | 0.230769 + Bakalino | 0.230769 + Bakalite | 0.230769 + Bakalovo | 0.230769 + Bakalsen | 0.230769 + Bakaltua Bank | 0.230769 + Bakalukalu | 0.230769 + Bakalukalu Shan | 0.230769 + Bakkalia | 0.230769 + Bankalol | 0.230769 + Barkaleh | 0.230769 + Barkalne | 0.230769 + Barkalow Hollow | 0.230769 + Bawkalut | 0.230769 + Bawkalut Chaung | 0.230769 + Clifton T Barkalow Elementary School | 0.230769 + Efrejtor Bakalovo | 0.230769 + Efreytor-Bakalovo | 0.230769 + Gora Barkalyu | 0.230769 + Ile Bakalibu | 0.230769 + Khor Bakallii | 0.230769 + Nehalla Bankalah Reserved Forest | 0.230769 + Ragha Bakalzai | 0.230769 + Tanjung Batikala | 0.230769 + Teluk Bakalang | 0.230769 + Urochishche Bakalovo | 0.230769 + Banjar Kubakal | 0.222222 + Darreh Pumba Kal | 0.222222 + Zabaykalovskiy | 0.222222 + Aparthotel Adagio Premium Dubai Al Barsha | 0.214286 + Babakalia | 0.214286 + Bahkalleh | 0.214286 + Baikalovo | 0.214286 + Bakalaale | 0.214286 + Bakalabwa Pans | 0.214286 + Bakalaeng | 0.214286 + Bakalauri | 0.214286 + Bakalbhar | 0.214286 + Bakalbuah | 0.214286 + Bakalerek | 0.214286 + Bakalinga | 0.214286 + Bakalipur | 0.214286 + Bakaljaya | 0.214286 + Bakalnica | 0.214286 + Bakalongo | 0.214286 + Bakalovka | 0.214286 + Bakalrejo | 0.214286 + Bakkalale | 0.214286 + Bambakala | 0.214286 + Bambakalo | 0.214286 + Barkalare | 0.214286 + Barkalden | 0.214286 + Barkallou | 0.214286 + Barkalova | 0.214286 + Baskalino | 0.214286 + Baskaltsi | 0.214286 + Desa Bakalrejo | 0.214286 + Doubletree By Hilton Dubai Al Barsha Hotel and Res | 0.214286 + Doubletree By Hilton Hotel and Apartments Dubai Al Barsha | 0.214286 + Doubletree Res.Dubai-Al Barsha | 0.214286 + Gora Barkalova | 0.214286 + Holiday Inn Dubai Al Barsha | 0.214286 + Novotel Dubai Al Barsha | 0.214286 + Park Inn By Radisson Dubai Al Barsha | 0.214286 + Ramee Rose Hotel Dubai Al Barsha | 0.214286 + Ras Barkallah | 0.214286 + Salu Bakalaeng | 0.214286 + Tanjung Bakalinga | 0.214286 + Tubu Bakalekuk | 0.214286 + Baikalakko | 0.2 + Bakalauri1 | 0.2 + Bakalauri2 | 0.2 + Bakalauri3 | 0.2 + Bakalauri4 | 0.2 + Bakalauri5 | 0.2 + Bakalauri6 | 0.2 + Bakalauri7 | 0.2 + Bakalauri8 | 0.2 + Bakalauri9 | 0.2 + Bakaldalam | 0.2 + Bakaldukuh | 0.2 + Bakaloolay | 0.2 + Bakalovina | 0.2 + Bakalpokok | 0.2 + Bakalshile | 0.2 + Bakalukudu | 0.2 + Bambakalia | 0.2 + Barkaladja Pool | 0.2 + Barkalovka | 0.2 + Bavkalasis | 0.2 + Gora Bakalyadyr | 0.2 + Kampong Bakaladong | 0.2 + Urochishche Bakalarnyn-Ayasy | 0.2 + Urochishche Bakaldikha | 0.2 +(245 rows) + +select t,strict_word_similarity('Kabankala',t) as sml from test_trgm2 where t %>> 'Kabankala' order by sml desc, t; + t | sml +----------------------------------+---------- + Kabankala | 1 + Kabankalan City Public Plaza | 0.75 + Abankala | 0.583333 + Kabakala | 0.583333 + Kabikala | 0.461538 + Ntombankala School | 0.375 + Nehalla Bankalah Reserved Forest | 0.357143 + Jabba Kalai | 0.333333 + Kambakala | 0.333333 + Ker Samba Kalla | 0.333333 + Bankal | 0.307692 + Bankal School | 0.307692 + Kanampumba-Kalawa | 0.307692 + Bankali | 0.285714 + Mwalaba-Kalamba | 0.285714 + Tumba-Kalamba | 0.285714 + Darreh Pumba Kal | 0.272727 + Bankalol | 0.266667 + Dabakala | 0.266667 + Purba Kalaujan | 0.266667 + Kali Purbakala | 0.263158 + Dalabakala | 0.25 + Demba Kali | 0.25 + Gagaba Kalo | 0.25 + Golba Kalo | 0.25 + Habakkala | 0.25 + Kali Bakalan | 0.25 + Kimbakala | 0.25 + Kombakala | 0.25 + Jaba Kalle | 0.235294 + Kaikalahun Indian Reserve 25 | 0.235294 + Kwala Bakala | 0.235294 + Gereba Kaler | 0.230769 + Goth Soba Kaloi | 0.230769 + Guba Kaldo | 0.230769 + Gulba Kalle | 0.230769 + Guba Kalgalaksha | 0.222222 + Kalibakalako | 0.222222 + Ba Kaliin | 0.214286 + Bakala | 0.214286 + Bakala Koupi | 0.214286 + Bikala | 0.214286 + Bikala Madila | 0.214286 + Bugor Arba-Kalgan | 0.214286 + Bumba-Kaloki | 0.214286 + Guba Kalita | 0.214286 + Kamba-Kalele | 0.214286 + Mbay Bakala | 0.214286 + Ngao Bakala | 0.214286 + Sungai Bakala | 0.214286 + Fayzabadkala | 0.210526 + Gora Fayzabadkala | 0.210526 + Alue Bakkala | 0.2 + Bakkala Cemetery | 0.2 + Barkala | 0.2 + Barkala Park | 0.2 + Barkala Rao | 0.2 + Barkala Reserved Forest | 0.2 + Baukala | 0.2 + Beikala | 0.2 + Bomba-Kalende | 0.2 + Bumba-Kalumba | 0.2 + Haikala | 0.2 + Kahambikalela | 0.2 + Kaikalapettai | 0.2 + Kaikale | 0.2 + Laikala | 0.2 + Maikala Range | 0.2 + Matamba-Kalenga | 0.2 + Matamba-Kalenge | 0.2 + Naikala | 0.2 + Tumba-Kalumba | 0.2 + Tumba-Kalunga | 0.2 + Waikala | 0.2 +(74 rows) + diff --git a/contrib/pg_trgm/expected/pg_trgm.out b/contrib/pg_trgm/expected/pg_trgm.out new file mode 100644 index 0000000..ce4bf1d --- /dev/null +++ b/contrib/pg_trgm/expected/pg_trgm.out @@ -0,0 +1,5404 @@ +CREATE EXTENSION pg_trgm; +-- Check whether any of our opclasses fail amvalidate +SELECT amname, opcname +FROM pg_opclass opc LEFT JOIN pg_am am ON am.oid = opcmethod +WHERE opc.oid >= 16384 AND NOT amvalidate(opc.oid); + amname | opcname +--------+--------- +(0 rows) + +--backslash is used in tests below, installcheck will fail if +--standard_conforming_string is off +set standard_conforming_strings=on; +-- reduce noise +set extra_float_digits = 0; +select show_trgm(''); + show_trgm +----------- + {} +(1 row) + +select show_trgm('(*&^$@%@'); + show_trgm +----------- + {} +(1 row) + +select show_trgm('a b c'); + show_trgm +--------------------------------------- + {" a"," b"," c"," a "," b "," c "} +(1 row) + +select show_trgm(' a b c '); + show_trgm +--------------------------------------- + {" a"," b"," c"," a "," b "," c "} +(1 row) + +select show_trgm('aA bB cC'); + show_trgm +--------------------------------------------------------- + {" a"," b"," c"," aa"," bb"," cc","aa ","bb ","cc "} +(1 row) + +select show_trgm(' aA bB cC '); + show_trgm +--------------------------------------------------------- + {" a"," b"," c"," aa"," bb"," cc","aa ","bb ","cc "} +(1 row) + +select show_trgm('a b C0*%^'); + show_trgm +--------------------------------------------- + {" a"," b"," c"," a "," b "," c0","c0 "} +(1 row) + +select similarity('wow','WOWa '); + similarity +------------ + 0.5 +(1 row) + +select similarity('wow',' WOW '); + similarity +------------ + 1 +(1 row) + +select similarity('---', '####---'); + similarity +------------ + 0 +(1 row) + +CREATE TABLE test_trgm(t text COLLATE "C"); +\copy test_trgm from 'data/trgm.data' +select t,similarity(t,'qwertyu0988') as sml from test_trgm where t % 'qwertyu0988' order by sml desc, t; + t | sml +-------------+---------- + qwertyu0988 | 1 + qwertyu0980 | 0.714286 + qwertyu0981 | 0.714286 + qwertyu0982 | 0.714286 + qwertyu0983 | 0.714286 + qwertyu0984 | 0.714286 + qwertyu0985 | 0.714286 + qwertyu0986 | 0.714286 + qwertyu0987 | 0.714286 + qwertyu0989 | 0.714286 + qwertyu0088 | 0.6 + qwertyu0098 | 0.6 + qwertyu0188 | 0.6 + qwertyu0288 | 0.6 + qwertyu0388 | 0.6 + qwertyu0488 | 0.6 + qwertyu0588 | 0.6 + qwertyu0688 | 0.6 + qwertyu0788 | 0.6 + qwertyu0888 | 0.6 + qwertyu0900 | 0.6 + qwertyu0901 | 0.6 + qwertyu0902 | 0.6 + qwertyu0903 | 0.6 + qwertyu0904 | 0.6 + qwertyu0905 | 0.6 + qwertyu0906 | 0.6 + qwertyu0907 | 0.6 + qwertyu0908 | 0.6 + qwertyu0909 | 0.6 + qwertyu0910 | 0.6 + qwertyu0911 | 0.6 + qwertyu0912 | 0.6 + qwertyu0913 | 0.6 + qwertyu0914 | 0.6 + qwertyu0915 | 0.6 + qwertyu0916 | 0.6 + qwertyu0917 | 0.6 + qwertyu0918 | 0.6 + qwertyu0919 | 0.6 + qwertyu0920 | 0.6 + qwertyu0921 | 0.6 + qwertyu0922 | 0.6 + qwertyu0923 | 0.6 + qwertyu0924 | 0.6 + qwertyu0925 | 0.6 + qwertyu0926 | 0.6 + qwertyu0927 | 0.6 + qwertyu0928 | 0.6 + qwertyu0929 | 0.6 + qwertyu0930 | 0.6 + qwertyu0931 | 0.6 + qwertyu0932 | 0.6 + qwertyu0933 | 0.6 + qwertyu0934 | 0.6 + qwertyu0935 | 0.6 + qwertyu0936 | 0.6 + qwertyu0937 | 0.6 + qwertyu0938 | 0.6 + qwertyu0939 | 0.6 + qwertyu0940 | 0.6 + qwertyu0941 | 0.6 + qwertyu0942 | 0.6 + qwertyu0943 | 0.6 + qwertyu0944 | 0.6 + qwertyu0945 | 0.6 + qwertyu0946 | 0.6 + qwertyu0947 | 0.6 + qwertyu0948 | 0.6 + qwertyu0949 | 0.6 + qwertyu0950 | 0.6 + qwertyu0951 | 0.6 + qwertyu0952 | 0.6 + qwertyu0953 | 0.6 + qwertyu0954 | 0.6 + qwertyu0955 | 0.6 + qwertyu0956 | 0.6 + qwertyu0957 | 0.6 + qwertyu0958 | 0.6 + qwertyu0959 | 0.6 + qwertyu0960 | 0.6 + qwertyu0961 | 0.6 + qwertyu0962 | 0.6 + qwertyu0963 | 0.6 + qwertyu0964 | 0.6 + qwertyu0965 | 0.6 + qwertyu0966 | 0.6 + qwertyu0967 | 0.6 + qwertyu0968 | 0.6 + qwertyu0969 | 0.6 + qwertyu0970 | 0.6 + qwertyu0971 | 0.6 + qwertyu0972 | 0.6 + qwertyu0973 | 0.6 + qwertyu0974 | 0.6 + qwertyu0975 | 0.6 + qwertyu0976 | 0.6 + qwertyu0977 | 0.6 + qwertyu0978 | 0.6 + qwertyu0979 | 0.6 + qwertyu0990 | 0.6 + qwertyu0991 | 0.6 + qwertyu0992 | 0.6 + qwertyu0993 | 0.6 + qwertyu0994 | 0.6 + qwertyu0995 | 0.6 + qwertyu0996 | 0.6 + qwertyu0997 | 0.6 + qwertyu0998 | 0.6 + qwertyu0999 | 0.6 + qwertyu0001 | 0.5 + qwertyu0002 | 0.5 + qwertyu0003 | 0.5 + qwertyu0004 | 0.5 + qwertyu0005 | 0.5 + qwertyu0006 | 0.5 + qwertyu0007 | 0.5 + qwertyu0008 | 0.5 + qwertyu0009 | 0.5 + qwertyu0010 | 0.5 + qwertyu0011 | 0.5 + qwertyu0012 | 0.5 + qwertyu0013 | 0.5 + qwertyu0014 | 0.5 + qwertyu0015 | 0.5 + qwertyu0016 | 0.5 + qwertyu0017 | 0.5 + qwertyu0018 | 0.5 + qwertyu0019 | 0.5 + qwertyu0020 | 0.5 + qwertyu0021 | 0.5 + qwertyu0022 | 0.5 + qwertyu0023 | 0.5 + qwertyu0024 | 0.5 + qwertyu0025 | 0.5 + qwertyu0026 | 0.5 + qwertyu0027 | 0.5 + qwertyu0028 | 0.5 + qwertyu0029 | 0.5 + qwertyu0030 | 0.5 + qwertyu0031 | 0.5 + qwertyu0032 | 0.5 + qwertyu0033 | 0.5 + qwertyu0034 | 0.5 + qwertyu0035 | 0.5 + qwertyu0036 | 0.5 + qwertyu0037 | 0.5 + qwertyu0038 | 0.5 + qwertyu0039 | 0.5 + qwertyu0040 | 0.5 + qwertyu0041 | 0.5 + qwertyu0042 | 0.5 + qwertyu0043 | 0.5 + qwertyu0044 | 0.5 + qwertyu0045 | 0.5 + qwertyu0046 | 0.5 + qwertyu0047 | 0.5 + qwertyu0048 | 0.5 + qwertyu0049 | 0.5 + qwertyu0050 | 0.5 + qwertyu0051 | 0.5 + qwertyu0052 | 0.5 + qwertyu0053 | 0.5 + qwertyu0054 | 0.5 + qwertyu0055 | 0.5 + qwertyu0056 | 0.5 + qwertyu0057 | 0.5 + qwertyu0058 | 0.5 + qwertyu0059 | 0.5 + qwertyu0060 | 0.5 + qwertyu0061 | 0.5 + qwertyu0062 | 0.5 + qwertyu0063 | 0.5 + qwertyu0064 | 0.5 + qwertyu0065 | 0.5 + qwertyu0066 | 0.5 + qwertyu0067 | 0.5 + qwertyu0068 | 0.5 + qwertyu0069 | 0.5 + qwertyu0070 | 0.5 + qwertyu0071 | 0.5 + qwertyu0072 | 0.5 + qwertyu0073 | 0.5 + qwertyu0074 | 0.5 + qwertyu0075 | 0.5 + qwertyu0076 | 0.5 + qwertyu0077 | 0.5 + qwertyu0078 | 0.5 + qwertyu0079 | 0.5 + qwertyu0080 | 0.5 + qwertyu0081 | 0.5 + qwertyu0082 | 0.5 + qwertyu0083 | 0.5 + qwertyu0084 | 0.5 + qwertyu0085 | 0.5 + qwertyu0086 | 0.5 + qwertyu0087 | 0.5 + qwertyu0089 | 0.5 + qwertyu0090 | 0.5 + qwertyu0091 | 0.5 + qwertyu0092 | 0.5 + qwertyu0093 | 0.5 + qwertyu0094 | 0.5 + qwertyu0095 | 0.5 + qwertyu0096 | 0.5 + qwertyu0097 | 0.5 + qwertyu0099 | 0.5 + qwertyu0100 | 0.5 + qwertyu0101 | 0.5 + qwertyu0102 | 0.5 + qwertyu0103 | 0.5 + qwertyu0104 | 0.5 + qwertyu0105 | 0.5 + qwertyu0106 | 0.5 + qwertyu0107 | 0.5 + qwertyu0108 | 0.5 + qwertyu0109 | 0.5 + qwertyu0110 | 0.5 + qwertyu0111 | 0.5 + qwertyu0112 | 0.5 + qwertyu0113 | 0.5 + qwertyu0114 | 0.5 + qwertyu0115 | 0.5 + qwertyu0116 | 0.5 + qwertyu0117 | 0.5 + qwertyu0118 | 0.5 + qwertyu0119 | 0.5 + qwertyu0120 | 0.5 + qwertyu0121 | 0.5 + qwertyu0122 | 0.5 + qwertyu0123 | 0.5 + qwertyu0124 | 0.5 + qwertyu0125 | 0.5 + qwertyu0126 | 0.5 + qwertyu0127 | 0.5 + qwertyu0128 | 0.5 + qwertyu0129 | 0.5 + qwertyu0130 | 0.5 + qwertyu0131 | 0.5 + qwertyu0132 | 0.5 + qwertyu0133 | 0.5 + qwertyu0134 | 0.5 + qwertyu0135 | 0.5 + qwertyu0136 | 0.5 + qwertyu0137 | 0.5 + qwertyu0138 | 0.5 + qwertyu0139 | 0.5 + qwertyu0140 | 0.5 + qwertyu0141 | 0.5 + qwertyu0142 | 0.5 + qwertyu0143 | 0.5 + qwertyu0144 | 0.5 + qwertyu0145 | 0.5 + qwertyu0146 | 0.5 + qwertyu0147 | 0.5 + qwertyu0148 | 0.5 + qwertyu0149 | 0.5 + qwertyu0150 | 0.5 + qwertyu0151 | 0.5 + qwertyu0152 | 0.5 + qwertyu0153 | 0.5 + qwertyu0154 | 0.5 + qwertyu0155 | 0.5 + qwertyu0156 | 0.5 + qwertyu0157 | 0.5 + qwertyu0158 | 0.5 + qwertyu0159 | 0.5 + qwertyu0160 | 0.5 + qwertyu0161 | 0.5 + qwertyu0162 | 0.5 + qwertyu0163 | 0.5 + qwertyu0164 | 0.5 + qwertyu0165 | 0.5 + qwertyu0166 | 0.5 + qwertyu0167 | 0.5 + qwertyu0168 | 0.5 + qwertyu0169 | 0.5 + qwertyu0170 | 0.5 + qwertyu0171 | 0.5 + qwertyu0172 | 0.5 + qwertyu0173 | 0.5 + qwertyu0174 | 0.5 + qwertyu0175 | 0.5 + qwertyu0176 | 0.5 + qwertyu0177 | 0.5 + qwertyu0178 | 0.5 + qwertyu0179 | 0.5 + qwertyu0180 | 0.5 + qwertyu0181 | 0.5 + qwertyu0182 | 0.5 + qwertyu0183 | 0.5 + qwertyu0184 | 0.5 + qwertyu0185 | 0.5 + qwertyu0186 | 0.5 + qwertyu0187 | 0.5 + qwertyu0189 | 0.5 + qwertyu0190 | 0.5 + qwertyu0191 | 0.5 + qwertyu0192 | 0.5 + qwertyu0193 | 0.5 + qwertyu0194 | 0.5 + qwertyu0195 | 0.5 + qwertyu0196 | 0.5 + qwertyu0197 | 0.5 + qwertyu0198 | 0.5 + qwertyu0199 | 0.5 + qwertyu0200 | 0.5 + qwertyu0201 | 0.5 + qwertyu0202 | 0.5 + qwertyu0203 | 0.5 + qwertyu0204 | 0.5 + qwertyu0205 | 0.5 + qwertyu0206 | 0.5 + qwertyu0207 | 0.5 + qwertyu0208 | 0.5 + qwertyu0209 | 0.5 + qwertyu0210 | 0.5 + qwertyu0211 | 0.5 + qwertyu0212 | 0.5 + qwertyu0213 | 0.5 + qwertyu0214 | 0.5 + qwertyu0215 | 0.5 + qwertyu0216 | 0.5 + qwertyu0217 | 0.5 + qwertyu0218 | 0.5 + qwertyu0219 | 0.5 + qwertyu0220 | 0.5 + qwertyu0221 | 0.5 + qwertyu0222 | 0.5 + qwertyu0223 | 0.5 + qwertyu0224 | 0.5 + qwertyu0225 | 0.5 + qwertyu0226 | 0.5 + qwertyu0227 | 0.5 + qwertyu0228 | 0.5 + qwertyu0229 | 0.5 + qwertyu0230 | 0.5 + qwertyu0231 | 0.5 + qwertyu0232 | 0.5 + qwertyu0233 | 0.5 + qwertyu0234 | 0.5 + qwertyu0235 | 0.5 + qwertyu0236 | 0.5 + qwertyu0237 | 0.5 + qwertyu0238 | 0.5 + qwertyu0239 | 0.5 + qwertyu0240 | 0.5 + qwertyu0241 | 0.5 + qwertyu0242 | 0.5 + qwertyu0243 | 0.5 + qwertyu0244 | 0.5 + qwertyu0245 | 0.5 + qwertyu0246 | 0.5 + qwertyu0247 | 0.5 + qwertyu0248 | 0.5 + qwertyu0249 | 0.5 + qwertyu0250 | 0.5 + qwertyu0251 | 0.5 + qwertyu0252 | 0.5 + qwertyu0253 | 0.5 + qwertyu0254 | 0.5 + qwertyu0255 | 0.5 + qwertyu0256 | 0.5 + qwertyu0257 | 0.5 + qwertyu0258 | 0.5 + qwertyu0259 | 0.5 + qwertyu0260 | 0.5 + qwertyu0261 | 0.5 + qwertyu0262 | 0.5 + qwertyu0263 | 0.5 + qwertyu0264 | 0.5 + qwertyu0265 | 0.5 + qwertyu0266 | 0.5 + qwertyu0267 | 0.5 + qwertyu0268 | 0.5 + qwertyu0269 | 0.5 + qwertyu0270 | 0.5 + qwertyu0271 | 0.5 + qwertyu0272 | 0.5 + qwertyu0273 | 0.5 + qwertyu0274 | 0.5 + qwertyu0275 | 0.5 + qwertyu0276 | 0.5 + qwertyu0277 | 0.5 + qwertyu0278 | 0.5 + qwertyu0279 | 0.5 + qwertyu0280 | 0.5 + qwertyu0281 | 0.5 + qwertyu0282 | 0.5 + qwertyu0283 | 0.5 + qwertyu0284 | 0.5 + qwertyu0285 | 0.5 + qwertyu0286 | 0.5 + qwertyu0287 | 0.5 + qwertyu0289 | 0.5 + qwertyu0290 | 0.5 + qwertyu0291 | 0.5 + qwertyu0292 | 0.5 + qwertyu0293 | 0.5 + qwertyu0294 | 0.5 + qwertyu0295 | 0.5 + qwertyu0296 | 0.5 + qwertyu0297 | 0.5 + qwertyu0298 | 0.5 + qwertyu0299 | 0.5 + qwertyu0300 | 0.5 + qwertyu0301 | 0.5 + qwertyu0302 | 0.5 + qwertyu0303 | 0.5 + qwertyu0304 | 0.5 + qwertyu0305 | 0.5 + qwertyu0306 | 0.5 + qwertyu0307 | 0.5 + qwertyu0308 | 0.5 + qwertyu0309 | 0.5 + qwertyu0310 | 0.5 + qwertyu0311 | 0.5 + qwertyu0312 | 0.5 + qwertyu0313 | 0.5 + qwertyu0314 | 0.5 + qwertyu0315 | 0.5 + qwertyu0316 | 0.5 + qwertyu0317 | 0.5 + qwertyu0318 | 0.5 + qwertyu0319 | 0.5 + qwertyu0320 | 0.5 + qwertyu0321 | 0.5 + qwertyu0322 | 0.5 + qwertyu0323 | 0.5 + qwertyu0324 | 0.5 + qwertyu0325 | 0.5 + qwertyu0326 | 0.5 + qwertyu0327 | 0.5 + qwertyu0328 | 0.5 + qwertyu0329 | 0.5 + qwertyu0330 | 0.5 + qwertyu0331 | 0.5 + qwertyu0332 | 0.5 + qwertyu0333 | 0.5 + qwertyu0334 | 0.5 + qwertyu0335 | 0.5 + qwertyu0336 | 0.5 + qwertyu0337 | 0.5 + qwertyu0338 | 0.5 + qwertyu0339 | 0.5 + qwertyu0340 | 0.5 + qwertyu0341 | 0.5 + qwertyu0342 | 0.5 + qwertyu0343 | 0.5 + qwertyu0344 | 0.5 + qwertyu0345 | 0.5 + qwertyu0346 | 0.5 + qwertyu0347 | 0.5 + qwertyu0348 | 0.5 + qwertyu0349 | 0.5 + qwertyu0350 | 0.5 + qwertyu0351 | 0.5 + qwertyu0352 | 0.5 + qwertyu0353 | 0.5 + qwertyu0354 | 0.5 + qwertyu0355 | 0.5 + qwertyu0356 | 0.5 + qwertyu0357 | 0.5 + qwertyu0358 | 0.5 + qwertyu0359 | 0.5 + qwertyu0360 | 0.5 + qwertyu0361 | 0.5 + qwertyu0362 | 0.5 + qwertyu0363 | 0.5 + qwertyu0364 | 0.5 + qwertyu0365 | 0.5 + qwertyu0366 | 0.5 + qwertyu0367 | 0.5 + qwertyu0368 | 0.5 + qwertyu0369 | 0.5 + qwertyu0370 | 0.5 + qwertyu0371 | 0.5 + qwertyu0372 | 0.5 + qwertyu0373 | 0.5 + qwertyu0374 | 0.5 + qwertyu0375 | 0.5 + qwertyu0376 | 0.5 + qwertyu0377 | 0.5 + qwertyu0378 | 0.5 + qwertyu0379 | 0.5 + qwertyu0380 | 0.5 + qwertyu0381 | 0.5 + qwertyu0382 | 0.5 + qwertyu0383 | 0.5 + qwertyu0384 | 0.5 + qwertyu0385 | 0.5 + qwertyu0386 | 0.5 + qwertyu0387 | 0.5 + qwertyu0389 | 0.5 + qwertyu0390 | 0.5 + qwertyu0391 | 0.5 + qwertyu0392 | 0.5 + qwertyu0393 | 0.5 + qwertyu0394 | 0.5 + qwertyu0395 | 0.5 + qwertyu0396 | 0.5 + qwertyu0397 | 0.5 + qwertyu0398 | 0.5 + qwertyu0399 | 0.5 + qwertyu0400 | 0.5 + qwertyu0401 | 0.5 + qwertyu0402 | 0.5 + qwertyu0403 | 0.5 + qwertyu0404 | 0.5 + qwertyu0405 | 0.5 + qwertyu0406 | 0.5 + qwertyu0407 | 0.5 + qwertyu0408 | 0.5 + qwertyu0409 | 0.5 + qwertyu0410 | 0.5 + qwertyu0411 | 0.5 + qwertyu0412 | 0.5 + qwertyu0413 | 0.5 + qwertyu0414 | 0.5 + qwertyu0415 | 0.5 + qwertyu0416 | 0.5 + qwertyu0417 | 0.5 + qwertyu0418 | 0.5 + qwertyu0419 | 0.5 + qwertyu0420 | 0.5 + qwertyu0421 | 0.5 + qwertyu0422 | 0.5 + qwertyu0423 | 0.5 + qwertyu0424 | 0.5 + qwertyu0425 | 0.5 + qwertyu0426 | 0.5 + qwertyu0427 | 0.5 + qwertyu0428 | 0.5 + qwertyu0429 | 0.5 + qwertyu0430 | 0.5 + qwertyu0431 | 0.5 + qwertyu0432 | 0.5 + qwertyu0433 | 0.5 + qwertyu0434 | 0.5 + qwertyu0435 | 0.5 + qwertyu0436 | 0.5 + qwertyu0437 | 0.5 + qwertyu0438 | 0.5 + qwertyu0439 | 0.5 + qwertyu0440 | 0.5 + qwertyu0441 | 0.5 + qwertyu0442 | 0.5 + qwertyu0443 | 0.5 + qwertyu0444 | 0.5 + qwertyu0445 | 0.5 + qwertyu0446 | 0.5 + qwertyu0447 | 0.5 + qwertyu0448 | 0.5 + qwertyu0449 | 0.5 + qwertyu0450 | 0.5 + qwertyu0451 | 0.5 + qwertyu0452 | 0.5 + qwertyu0453 | 0.5 + qwertyu0454 | 0.5 + qwertyu0455 | 0.5 + qwertyu0456 | 0.5 + qwertyu0457 | 0.5 + qwertyu0458 | 0.5 + qwertyu0459 | 0.5 + qwertyu0460 | 0.5 + qwertyu0461 | 0.5 + qwertyu0462 | 0.5 + qwertyu0463 | 0.5 + qwertyu0464 | 0.5 + qwertyu0465 | 0.5 + qwertyu0466 | 0.5 + qwertyu0467 | 0.5 + qwertyu0468 | 0.5 + qwertyu0469 | 0.5 + qwertyu0470 | 0.5 + qwertyu0471 | 0.5 + qwertyu0472 | 0.5 + qwertyu0473 | 0.5 + qwertyu0474 | 0.5 + qwertyu0475 | 0.5 + qwertyu0476 | 0.5 + qwertyu0477 | 0.5 + qwertyu0478 | 0.5 + qwertyu0479 | 0.5 + qwertyu0480 | 0.5 + qwertyu0481 | 0.5 + qwertyu0482 | 0.5 + qwertyu0483 | 0.5 + qwertyu0484 | 0.5 + qwertyu0485 | 0.5 + qwertyu0486 | 0.5 + qwertyu0487 | 0.5 + qwertyu0489 | 0.5 + qwertyu0490 | 0.5 + qwertyu0491 | 0.5 + qwertyu0492 | 0.5 + qwertyu0493 | 0.5 + qwertyu0494 | 0.5 + qwertyu0495 | 0.5 + qwertyu0496 | 0.5 + qwertyu0497 | 0.5 + qwertyu0498 | 0.5 + qwertyu0499 | 0.5 + qwertyu0500 | 0.5 + qwertyu0501 | 0.5 + qwertyu0502 | 0.5 + qwertyu0503 | 0.5 + qwertyu0504 | 0.5 + qwertyu0505 | 0.5 + qwertyu0506 | 0.5 + qwertyu0507 | 0.5 + qwertyu0508 | 0.5 + qwertyu0509 | 0.5 + qwertyu0510 | 0.5 + qwertyu0511 | 0.5 + qwertyu0512 | 0.5 + qwertyu0513 | 0.5 + qwertyu0514 | 0.5 + qwertyu0515 | 0.5 + qwertyu0516 | 0.5 + qwertyu0517 | 0.5 + qwertyu0518 | 0.5 + qwertyu0519 | 0.5 + qwertyu0520 | 0.5 + qwertyu0521 | 0.5 + qwertyu0522 | 0.5 + qwertyu0523 | 0.5 + qwertyu0524 | 0.5 + qwertyu0525 | 0.5 + qwertyu0526 | 0.5 + qwertyu0527 | 0.5 + qwertyu0528 | 0.5 + qwertyu0529 | 0.5 + qwertyu0530 | 0.5 + qwertyu0531 | 0.5 + qwertyu0532 | 0.5 + qwertyu0533 | 0.5 + qwertyu0534 | 0.5 + qwertyu0535 | 0.5 + qwertyu0536 | 0.5 + qwertyu0537 | 0.5 + qwertyu0538 | 0.5 + qwertyu0539 | 0.5 + qwertyu0540 | 0.5 + qwertyu0541 | 0.5 + qwertyu0542 | 0.5 + qwertyu0543 | 0.5 + qwertyu0544 | 0.5 + qwertyu0545 | 0.5 + qwertyu0546 | 0.5 + qwertyu0547 | 0.5 + qwertyu0548 | 0.5 + qwertyu0549 | 0.5 + qwertyu0550 | 0.5 + qwertyu0551 | 0.5 + qwertyu0552 | 0.5 + qwertyu0553 | 0.5 + qwertyu0554 | 0.5 + qwertyu0555 | 0.5 + qwertyu0556 | 0.5 + qwertyu0557 | 0.5 + qwertyu0558 | 0.5 + qwertyu0559 | 0.5 + qwertyu0560 | 0.5 + qwertyu0561 | 0.5 + qwertyu0562 | 0.5 + qwertyu0563 | 0.5 + qwertyu0564 | 0.5 + qwertyu0565 | 0.5 + qwertyu0566 | 0.5 + qwertyu0567 | 0.5 + qwertyu0568 | 0.5 + qwertyu0569 | 0.5 + qwertyu0570 | 0.5 + qwertyu0571 | 0.5 + qwertyu0572 | 0.5 + qwertyu0573 | 0.5 + qwertyu0574 | 0.5 + qwertyu0575 | 0.5 + qwertyu0576 | 0.5 + qwertyu0577 | 0.5 + qwertyu0578 | 0.5 + qwertyu0579 | 0.5 + qwertyu0580 | 0.5 + qwertyu0581 | 0.5 + qwertyu0582 | 0.5 + qwertyu0583 | 0.5 + qwertyu0584 | 0.5 + qwertyu0585 | 0.5 + qwertyu0586 | 0.5 + qwertyu0587 | 0.5 + qwertyu0589 | 0.5 + qwertyu0590 | 0.5 + qwertyu0591 | 0.5 + qwertyu0592 | 0.5 + qwertyu0593 | 0.5 + qwertyu0594 | 0.5 + qwertyu0595 | 0.5 + qwertyu0596 | 0.5 + qwertyu0597 | 0.5 + qwertyu0598 | 0.5 + qwertyu0599 | 0.5 + qwertyu0600 | 0.5 + qwertyu0601 | 0.5 + qwertyu0602 | 0.5 + qwertyu0603 | 0.5 + qwertyu0604 | 0.5 + qwertyu0605 | 0.5 + qwertyu0606 | 0.5 + qwertyu0607 | 0.5 + qwertyu0608 | 0.5 + qwertyu0609 | 0.5 + qwertyu0610 | 0.5 + qwertyu0611 | 0.5 + qwertyu0612 | 0.5 + qwertyu0613 | 0.5 + qwertyu0614 | 0.5 + qwertyu0615 | 0.5 + qwertyu0616 | 0.5 + qwertyu0617 | 0.5 + qwertyu0618 | 0.5 + qwertyu0619 | 0.5 + qwertyu0620 | 0.5 + qwertyu0621 | 0.5 + qwertyu0622 | 0.5 + qwertyu0623 | 0.5 + qwertyu0624 | 0.5 + qwertyu0625 | 0.5 + qwertyu0626 | 0.5 + qwertyu0627 | 0.5 + qwertyu0628 | 0.5 + qwertyu0629 | 0.5 + qwertyu0630 | 0.5 + qwertyu0631 | 0.5 + qwertyu0632 | 0.5 + qwertyu0633 | 0.5 + qwertyu0634 | 0.5 + qwertyu0635 | 0.5 + qwertyu0636 | 0.5 + qwertyu0637 | 0.5 + qwertyu0638 | 0.5 + qwertyu0639 | 0.5 + qwertyu0640 | 0.5 + qwertyu0641 | 0.5 + qwertyu0642 | 0.5 + qwertyu0643 | 0.5 + qwertyu0644 | 0.5 + qwertyu0645 | 0.5 + qwertyu0646 | 0.5 + qwertyu0647 | 0.5 + qwertyu0648 | 0.5 + qwertyu0649 | 0.5 + qwertyu0650 | 0.5 + qwertyu0651 | 0.5 + qwertyu0652 | 0.5 + qwertyu0653 | 0.5 + qwertyu0654 | 0.5 + qwertyu0655 | 0.5 + qwertyu0656 | 0.5 + qwertyu0657 | 0.5 + qwertyu0658 | 0.5 + qwertyu0659 | 0.5 + qwertyu0660 | 0.5 + qwertyu0661 | 0.5 + qwertyu0662 | 0.5 + qwertyu0663 | 0.5 + qwertyu0664 | 0.5 + qwertyu0665 | 0.5 + qwertyu0666 | 0.5 + qwertyu0667 | 0.5 + qwertyu0668 | 0.5 + qwertyu0669 | 0.5 + qwertyu0670 | 0.5 + qwertyu0671 | 0.5 + qwertyu0672 | 0.5 + qwertyu0673 | 0.5 + qwertyu0674 | 0.5 + qwertyu0675 | 0.5 + qwertyu0676 | 0.5 + qwertyu0677 | 0.5 + qwertyu0678 | 0.5 + qwertyu0679 | 0.5 + qwertyu0680 | 0.5 + qwertyu0681 | 0.5 + qwertyu0682 | 0.5 + qwertyu0683 | 0.5 + qwertyu0684 | 0.5 + qwertyu0685 | 0.5 + qwertyu0686 | 0.5 + qwertyu0687 | 0.5 + qwertyu0689 | 0.5 + qwertyu0690 | 0.5 + qwertyu0691 | 0.5 + qwertyu0692 | 0.5 + qwertyu0693 | 0.5 + qwertyu0694 | 0.5 + qwertyu0695 | 0.5 + qwertyu0696 | 0.5 + qwertyu0697 | 0.5 + qwertyu0698 | 0.5 + qwertyu0699 | 0.5 + qwertyu0700 | 0.5 + qwertyu0701 | 0.5 + qwertyu0702 | 0.5 + qwertyu0703 | 0.5 + qwertyu0704 | 0.5 + qwertyu0705 | 0.5 + qwertyu0706 | 0.5 + qwertyu0707 | 0.5 + qwertyu0708 | 0.5 + qwertyu0709 | 0.5 + qwertyu0710 | 0.5 + qwertyu0711 | 0.5 + qwertyu0712 | 0.5 + qwertyu0713 | 0.5 + qwertyu0714 | 0.5 + qwertyu0715 | 0.5 + qwertyu0716 | 0.5 + qwertyu0717 | 0.5 + qwertyu0718 | 0.5 + qwertyu0719 | 0.5 + qwertyu0720 | 0.5 + qwertyu0721 | 0.5 + qwertyu0722 | 0.5 + qwertyu0723 | 0.5 + qwertyu0724 | 0.5 + qwertyu0725 | 0.5 + qwertyu0726 | 0.5 + qwertyu0727 | 0.5 + qwertyu0728 | 0.5 + qwertyu0729 | 0.5 + qwertyu0730 | 0.5 + qwertyu0731 | 0.5 + qwertyu0732 | 0.5 + qwertyu0733 | 0.5 + qwertyu0734 | 0.5 + qwertyu0735 | 0.5 + qwertyu0736 | 0.5 + qwertyu0737 | 0.5 + qwertyu0738 | 0.5 + qwertyu0739 | 0.5 + qwertyu0740 | 0.5 + qwertyu0741 | 0.5 + qwertyu0742 | 0.5 + qwertyu0743 | 0.5 + qwertyu0744 | 0.5 + qwertyu0745 | 0.5 + qwertyu0746 | 0.5 + qwertyu0747 | 0.5 + qwertyu0748 | 0.5 + qwertyu0749 | 0.5 + qwertyu0750 | 0.5 + qwertyu0751 | 0.5 + qwertyu0752 | 0.5 + qwertyu0753 | 0.5 + qwertyu0754 | 0.5 + qwertyu0755 | 0.5 + qwertyu0756 | 0.5 + qwertyu0757 | 0.5 + qwertyu0758 | 0.5 + qwertyu0759 | 0.5 + qwertyu0760 | 0.5 + qwertyu0761 | 0.5 + qwertyu0762 | 0.5 + qwertyu0763 | 0.5 + qwertyu0764 | 0.5 + qwertyu0765 | 0.5 + qwertyu0766 | 0.5 + qwertyu0767 | 0.5 + qwertyu0768 | 0.5 + qwertyu0769 | 0.5 + qwertyu0770 | 0.5 + qwertyu0771 | 0.5 + qwertyu0772 | 0.5 + qwertyu0773 | 0.5 + qwertyu0774 | 0.5 + qwertyu0775 | 0.5 + qwertyu0776 | 0.5 + qwertyu0777 | 0.5 + qwertyu0778 | 0.5 + qwertyu0779 | 0.5 + qwertyu0780 | 0.5 + qwertyu0781 | 0.5 + qwertyu0782 | 0.5 + qwertyu0783 | 0.5 + qwertyu0784 | 0.5 + qwertyu0785 | 0.5 + qwertyu0786 | 0.5 + qwertyu0787 | 0.5 + qwertyu0789 | 0.5 + qwertyu0790 | 0.5 + qwertyu0791 | 0.5 + qwertyu0792 | 0.5 + qwertyu0793 | 0.5 + qwertyu0794 | 0.5 + qwertyu0795 | 0.5 + qwertyu0796 | 0.5 + qwertyu0797 | 0.5 + qwertyu0798 | 0.5 + qwertyu0799 | 0.5 + qwertyu0800 | 0.5 + qwertyu0801 | 0.5 + qwertyu0802 | 0.5 + qwertyu0803 | 0.5 + qwertyu0804 | 0.5 + qwertyu0805 | 0.5 + qwertyu0806 | 0.5 + qwertyu0807 | 0.5 + qwertyu0808 | 0.5 + qwertyu0809 | 0.5 + qwertyu0810 | 0.5 + qwertyu0811 | 0.5 + qwertyu0812 | 0.5 + qwertyu0813 | 0.5 + qwertyu0814 | 0.5 + qwertyu0815 | 0.5 + qwertyu0816 | 0.5 + qwertyu0817 | 0.5 + qwertyu0818 | 0.5 + qwertyu0819 | 0.5 + qwertyu0820 | 0.5 + qwertyu0821 | 0.5 + qwertyu0822 | 0.5 + qwertyu0823 | 0.5 + qwertyu0824 | 0.5 + qwertyu0825 | 0.5 + qwertyu0826 | 0.5 + qwertyu0827 | 0.5 + qwertyu0828 | 0.5 + qwertyu0829 | 0.5 + qwertyu0830 | 0.5 + qwertyu0831 | 0.5 + qwertyu0832 | 0.5 + qwertyu0833 | 0.5 + qwertyu0834 | 0.5 + qwertyu0835 | 0.5 + qwertyu0836 | 0.5 + qwertyu0837 | 0.5 + qwertyu0838 | 0.5 + qwertyu0839 | 0.5 + qwertyu0840 | 0.5 + qwertyu0841 | 0.5 + qwertyu0842 | 0.5 + qwertyu0843 | 0.5 + qwertyu0844 | 0.5 + qwertyu0845 | 0.5 + qwertyu0846 | 0.5 + qwertyu0847 | 0.5 + qwertyu0848 | 0.5 + qwertyu0849 | 0.5 + qwertyu0850 | 0.5 + qwertyu0851 | 0.5 + qwertyu0852 | 0.5 + qwertyu0853 | 0.5 + qwertyu0854 | 0.5 + qwertyu0855 | 0.5 + qwertyu0856 | 0.5 + qwertyu0857 | 0.5 + qwertyu0858 | 0.5 + qwertyu0859 | 0.5 + qwertyu0860 | 0.5 + qwertyu0861 | 0.5 + qwertyu0862 | 0.5 + qwertyu0863 | 0.5 + qwertyu0864 | 0.5 + qwertyu0865 | 0.5 + qwertyu0866 | 0.5 + qwertyu0867 | 0.5 + qwertyu0868 | 0.5 + qwertyu0869 | 0.5 + qwertyu0870 | 0.5 + qwertyu0871 | 0.5 + qwertyu0872 | 0.5 + qwertyu0873 | 0.5 + qwertyu0874 | 0.5 + qwertyu0875 | 0.5 + qwertyu0876 | 0.5 + qwertyu0877 | 0.5 + qwertyu0878 | 0.5 + qwertyu0879 | 0.5 + qwertyu0880 | 0.5 + qwertyu0881 | 0.5 + qwertyu0882 | 0.5 + qwertyu0883 | 0.5 + qwertyu0884 | 0.5 + qwertyu0885 | 0.5 + qwertyu0886 | 0.5 + qwertyu0887 | 0.5 + qwertyu0889 | 0.5 + qwertyu0890 | 0.5 + qwertyu0891 | 0.5 + qwertyu0892 | 0.5 + qwertyu0893 | 0.5 + qwertyu0894 | 0.5 + qwertyu0895 | 0.5 + qwertyu0896 | 0.5 + qwertyu0897 | 0.5 + qwertyu0898 | 0.5 + qwertyu0899 | 0.5 + qwertyu1000 | 0.411765 +(1000 rows) + +select t,similarity(t,'gwertyu0988') as sml from test_trgm where t % 'gwertyu0988' order by sml desc, t; + t | sml +-------------+---------- + qwertyu0988 | 0.6 + qwertyu0980 | 0.411765 + qwertyu0981 | 0.411765 + qwertyu0982 | 0.411765 + qwertyu0983 | 0.411765 + qwertyu0984 | 0.411765 + qwertyu0985 | 0.411765 + qwertyu0986 | 0.411765 + qwertyu0987 | 0.411765 + qwertyu0989 | 0.411765 + qwertyu0088 | 0.333333 + qwertyu0098 | 0.333333 + qwertyu0188 | 0.333333 + qwertyu0288 | 0.333333 + qwertyu0388 | 0.333333 + qwertyu0488 | 0.333333 + qwertyu0588 | 0.333333 + qwertyu0688 | 0.333333 + qwertyu0788 | 0.333333 + qwertyu0888 | 0.333333 + qwertyu0900 | 0.333333 + qwertyu0901 | 0.333333 + qwertyu0902 | 0.333333 + qwertyu0903 | 0.333333 + qwertyu0904 | 0.333333 + qwertyu0905 | 0.333333 + qwertyu0906 | 0.333333 + qwertyu0907 | 0.333333 + qwertyu0908 | 0.333333 + qwertyu0909 | 0.333333 + qwertyu0910 | 0.333333 + qwertyu0911 | 0.333333 + qwertyu0912 | 0.333333 + qwertyu0913 | 0.333333 + qwertyu0914 | 0.333333 + qwertyu0915 | 0.333333 + qwertyu0916 | 0.333333 + qwertyu0917 | 0.333333 + qwertyu0918 | 0.333333 + qwertyu0919 | 0.333333 + qwertyu0920 | 0.333333 + qwertyu0921 | 0.333333 + qwertyu0922 | 0.333333 + qwertyu0923 | 0.333333 + qwertyu0924 | 0.333333 + qwertyu0925 | 0.333333 + qwertyu0926 | 0.333333 + qwertyu0927 | 0.333333 + qwertyu0928 | 0.333333 + qwertyu0929 | 0.333333 + qwertyu0930 | 0.333333 + qwertyu0931 | 0.333333 + qwertyu0932 | 0.333333 + qwertyu0933 | 0.333333 + qwertyu0934 | 0.333333 + qwertyu0935 | 0.333333 + qwertyu0936 | 0.333333 + qwertyu0937 | 0.333333 + qwertyu0938 | 0.333333 + qwertyu0939 | 0.333333 + qwertyu0940 | 0.333333 + qwertyu0941 | 0.333333 + qwertyu0942 | 0.333333 + qwertyu0943 | 0.333333 + qwertyu0944 | 0.333333 + qwertyu0945 | 0.333333 + qwertyu0946 | 0.333333 + qwertyu0947 | 0.333333 + qwertyu0948 | 0.333333 + qwertyu0949 | 0.333333 + qwertyu0950 | 0.333333 + qwertyu0951 | 0.333333 + qwertyu0952 | 0.333333 + qwertyu0953 | 0.333333 + qwertyu0954 | 0.333333 + qwertyu0955 | 0.333333 + qwertyu0956 | 0.333333 + qwertyu0957 | 0.333333 + qwertyu0958 | 0.333333 + qwertyu0959 | 0.333333 + qwertyu0960 | 0.333333 + qwertyu0961 | 0.333333 + qwertyu0962 | 0.333333 + qwertyu0963 | 0.333333 + qwertyu0964 | 0.333333 + qwertyu0965 | 0.333333 + qwertyu0966 | 0.333333 + qwertyu0967 | 0.333333 + qwertyu0968 | 0.333333 + qwertyu0969 | 0.333333 + qwertyu0970 | 0.333333 + qwertyu0971 | 0.333333 + qwertyu0972 | 0.333333 + qwertyu0973 | 0.333333 + qwertyu0974 | 0.333333 + qwertyu0975 | 0.333333 + qwertyu0976 | 0.333333 + qwertyu0977 | 0.333333 + qwertyu0978 | 0.333333 + qwertyu0979 | 0.333333 + qwertyu0990 | 0.333333 + qwertyu0991 | 0.333333 + qwertyu0992 | 0.333333 + qwertyu0993 | 0.333333 + qwertyu0994 | 0.333333 + qwertyu0995 | 0.333333 + qwertyu0996 | 0.333333 + qwertyu0997 | 0.333333 + qwertyu0998 | 0.333333 + qwertyu0999 | 0.333333 +(110 rows) + +select t,similarity(t,'gwertyu1988') as sml from test_trgm where t % 'gwertyu1988' order by sml desc, t; + t | sml +-------------+---------- + qwertyu0988 | 0.333333 +(1 row) + +select t <-> 'q0987wertyu0988', t from test_trgm order by t <-> 'q0987wertyu0988' limit 2; + ?column? | t +----------+------------- + 0.411765 | qwertyu0988 + 0.5 | qwertyu0987 +(2 rows) + +select count(*) from test_trgm where t ~ '[qwerty]{2}-?[qwerty]{2}'; + count +------- + 1000 +(1 row) + +create index trgm_idx on test_trgm using gist (t gist_trgm_ops); +set enable_seqscan=off; +select t,similarity(t,'qwertyu0988') as sml from test_trgm where t % 'qwertyu0988' order by sml desc, t; + t | sml +-------------+---------- + qwertyu0988 | 1 + qwertyu0980 | 0.714286 + qwertyu0981 | 0.714286 + qwertyu0982 | 0.714286 + qwertyu0983 | 0.714286 + qwertyu0984 | 0.714286 + qwertyu0985 | 0.714286 + qwertyu0986 | 0.714286 + qwertyu0987 | 0.714286 + qwertyu0989 | 0.714286 + qwertyu0088 | 0.6 + qwertyu0098 | 0.6 + qwertyu0188 | 0.6 + qwertyu0288 | 0.6 + qwertyu0388 | 0.6 + qwertyu0488 | 0.6 + qwertyu0588 | 0.6 + qwertyu0688 | 0.6 + qwertyu0788 | 0.6 + qwertyu0888 | 0.6 + qwertyu0900 | 0.6 + qwertyu0901 | 0.6 + qwertyu0902 | 0.6 + qwertyu0903 | 0.6 + qwertyu0904 | 0.6 + qwertyu0905 | 0.6 + qwertyu0906 | 0.6 + qwertyu0907 | 0.6 + qwertyu0908 | 0.6 + qwertyu0909 | 0.6 + qwertyu0910 | 0.6 + qwertyu0911 | 0.6 + qwertyu0912 | 0.6 + qwertyu0913 | 0.6 + qwertyu0914 | 0.6 + qwertyu0915 | 0.6 + qwertyu0916 | 0.6 + qwertyu0917 | 0.6 + qwertyu0918 | 0.6 + qwertyu0919 | 0.6 + qwertyu0920 | 0.6 + qwertyu0921 | 0.6 + qwertyu0922 | 0.6 + qwertyu0923 | 0.6 + qwertyu0924 | 0.6 + qwertyu0925 | 0.6 + qwertyu0926 | 0.6 + qwertyu0927 | 0.6 + qwertyu0928 | 0.6 + qwertyu0929 | 0.6 + qwertyu0930 | 0.6 + qwertyu0931 | 0.6 + qwertyu0932 | 0.6 + qwertyu0933 | 0.6 + qwertyu0934 | 0.6 + qwertyu0935 | 0.6 + qwertyu0936 | 0.6 + qwertyu0937 | 0.6 + qwertyu0938 | 0.6 + qwertyu0939 | 0.6 + qwertyu0940 | 0.6 + qwertyu0941 | 0.6 + qwertyu0942 | 0.6 + qwertyu0943 | 0.6 + qwertyu0944 | 0.6 + qwertyu0945 | 0.6 + qwertyu0946 | 0.6 + qwertyu0947 | 0.6 + qwertyu0948 | 0.6 + qwertyu0949 | 0.6 + qwertyu0950 | 0.6 + qwertyu0951 | 0.6 + qwertyu0952 | 0.6 + qwertyu0953 | 0.6 + qwertyu0954 | 0.6 + qwertyu0955 | 0.6 + qwertyu0956 | 0.6 + qwertyu0957 | 0.6 + qwertyu0958 | 0.6 + qwertyu0959 | 0.6 + qwertyu0960 | 0.6 + qwertyu0961 | 0.6 + qwertyu0962 | 0.6 + qwertyu0963 | 0.6 + qwertyu0964 | 0.6 + qwertyu0965 | 0.6 + qwertyu0966 | 0.6 + qwertyu0967 | 0.6 + qwertyu0968 | 0.6 + qwertyu0969 | 0.6 + qwertyu0970 | 0.6 + qwertyu0971 | 0.6 + qwertyu0972 | 0.6 + qwertyu0973 | 0.6 + qwertyu0974 | 0.6 + qwertyu0975 | 0.6 + qwertyu0976 | 0.6 + qwertyu0977 | 0.6 + qwertyu0978 | 0.6 + qwertyu0979 | 0.6 + qwertyu0990 | 0.6 + qwertyu0991 | 0.6 + qwertyu0992 | 0.6 + qwertyu0993 | 0.6 + qwertyu0994 | 0.6 + qwertyu0995 | 0.6 + qwertyu0996 | 0.6 + qwertyu0997 | 0.6 + qwertyu0998 | 0.6 + qwertyu0999 | 0.6 + qwertyu0001 | 0.5 + qwertyu0002 | 0.5 + qwertyu0003 | 0.5 + qwertyu0004 | 0.5 + qwertyu0005 | 0.5 + qwertyu0006 | 0.5 + qwertyu0007 | 0.5 + qwertyu0008 | 0.5 + qwertyu0009 | 0.5 + qwertyu0010 | 0.5 + qwertyu0011 | 0.5 + qwertyu0012 | 0.5 + qwertyu0013 | 0.5 + qwertyu0014 | 0.5 + qwertyu0015 | 0.5 + qwertyu0016 | 0.5 + qwertyu0017 | 0.5 + qwertyu0018 | 0.5 + qwertyu0019 | 0.5 + qwertyu0020 | 0.5 + qwertyu0021 | 0.5 + qwertyu0022 | 0.5 + qwertyu0023 | 0.5 + qwertyu0024 | 0.5 + qwertyu0025 | 0.5 + qwertyu0026 | 0.5 + qwertyu0027 | 0.5 + qwertyu0028 | 0.5 + qwertyu0029 | 0.5 + qwertyu0030 | 0.5 + qwertyu0031 | 0.5 + qwertyu0032 | 0.5 + qwertyu0033 | 0.5 + qwertyu0034 | 0.5 + qwertyu0035 | 0.5 + qwertyu0036 | 0.5 + qwertyu0037 | 0.5 + qwertyu0038 | 0.5 + qwertyu0039 | 0.5 + qwertyu0040 | 0.5 + qwertyu0041 | 0.5 + qwertyu0042 | 0.5 + qwertyu0043 | 0.5 + qwertyu0044 | 0.5 + qwertyu0045 | 0.5 + qwertyu0046 | 0.5 + qwertyu0047 | 0.5 + qwertyu0048 | 0.5 + qwertyu0049 | 0.5 + qwertyu0050 | 0.5 + qwertyu0051 | 0.5 + qwertyu0052 | 0.5 + qwertyu0053 | 0.5 + qwertyu0054 | 0.5 + qwertyu0055 | 0.5 + qwertyu0056 | 0.5 + qwertyu0057 | 0.5 + qwertyu0058 | 0.5 + qwertyu0059 | 0.5 + qwertyu0060 | 0.5 + qwertyu0061 | 0.5 + qwertyu0062 | 0.5 + qwertyu0063 | 0.5 + qwertyu0064 | 0.5 + qwertyu0065 | 0.5 + qwertyu0066 | 0.5 + qwertyu0067 | 0.5 + qwertyu0068 | 0.5 + qwertyu0069 | 0.5 + qwertyu0070 | 0.5 + qwertyu0071 | 0.5 + qwertyu0072 | 0.5 + qwertyu0073 | 0.5 + qwertyu0074 | 0.5 + qwertyu0075 | 0.5 + qwertyu0076 | 0.5 + qwertyu0077 | 0.5 + qwertyu0078 | 0.5 + qwertyu0079 | 0.5 + qwertyu0080 | 0.5 + qwertyu0081 | 0.5 + qwertyu0082 | 0.5 + qwertyu0083 | 0.5 + qwertyu0084 | 0.5 + qwertyu0085 | 0.5 + qwertyu0086 | 0.5 + qwertyu0087 | 0.5 + qwertyu0089 | 0.5 + qwertyu0090 | 0.5 + qwertyu0091 | 0.5 + qwertyu0092 | 0.5 + qwertyu0093 | 0.5 + qwertyu0094 | 0.5 + qwertyu0095 | 0.5 + qwertyu0096 | 0.5 + qwertyu0097 | 0.5 + qwertyu0099 | 0.5 + qwertyu0100 | 0.5 + qwertyu0101 | 0.5 + qwertyu0102 | 0.5 + qwertyu0103 | 0.5 + qwertyu0104 | 0.5 + qwertyu0105 | 0.5 + qwertyu0106 | 0.5 + qwertyu0107 | 0.5 + qwertyu0108 | 0.5 + qwertyu0109 | 0.5 + qwertyu0110 | 0.5 + qwertyu0111 | 0.5 + qwertyu0112 | 0.5 + qwertyu0113 | 0.5 + qwertyu0114 | 0.5 + qwertyu0115 | 0.5 + qwertyu0116 | 0.5 + qwertyu0117 | 0.5 + qwertyu0118 | 0.5 + qwertyu0119 | 0.5 + qwertyu0120 | 0.5 + qwertyu0121 | 0.5 + qwertyu0122 | 0.5 + qwertyu0123 | 0.5 + qwertyu0124 | 0.5 + qwertyu0125 | 0.5 + qwertyu0126 | 0.5 + qwertyu0127 | 0.5 + qwertyu0128 | 0.5 + qwertyu0129 | 0.5 + qwertyu0130 | 0.5 + qwertyu0131 | 0.5 + qwertyu0132 | 0.5 + qwertyu0133 | 0.5 + qwertyu0134 | 0.5 + qwertyu0135 | 0.5 + qwertyu0136 | 0.5 + qwertyu0137 | 0.5 + qwertyu0138 | 0.5 + qwertyu0139 | 0.5 + qwertyu0140 | 0.5 + qwertyu0141 | 0.5 + qwertyu0142 | 0.5 + qwertyu0143 | 0.5 + qwertyu0144 | 0.5 + qwertyu0145 | 0.5 + qwertyu0146 | 0.5 + qwertyu0147 | 0.5 + qwertyu0148 | 0.5 + qwertyu0149 | 0.5 + qwertyu0150 | 0.5 + qwertyu0151 | 0.5 + qwertyu0152 | 0.5 + qwertyu0153 | 0.5 + qwertyu0154 | 0.5 + qwertyu0155 | 0.5 + qwertyu0156 | 0.5 + qwertyu0157 | 0.5 + qwertyu0158 | 0.5 + qwertyu0159 | 0.5 + qwertyu0160 | 0.5 + qwertyu0161 | 0.5 + qwertyu0162 | 0.5 + qwertyu0163 | 0.5 + qwertyu0164 | 0.5 + qwertyu0165 | 0.5 + qwertyu0166 | 0.5 + qwertyu0167 | 0.5 + qwertyu0168 | 0.5 + qwertyu0169 | 0.5 + qwertyu0170 | 0.5 + qwertyu0171 | 0.5 + qwertyu0172 | 0.5 + qwertyu0173 | 0.5 + qwertyu0174 | 0.5 + qwertyu0175 | 0.5 + qwertyu0176 | 0.5 + qwertyu0177 | 0.5 + qwertyu0178 | 0.5 + qwertyu0179 | 0.5 + qwertyu0180 | 0.5 + qwertyu0181 | 0.5 + qwertyu0182 | 0.5 + qwertyu0183 | 0.5 + qwertyu0184 | 0.5 + qwertyu0185 | 0.5 + qwertyu0186 | 0.5 + qwertyu0187 | 0.5 + qwertyu0189 | 0.5 + qwertyu0190 | 0.5 + qwertyu0191 | 0.5 + qwertyu0192 | 0.5 + qwertyu0193 | 0.5 + qwertyu0194 | 0.5 + qwertyu0195 | 0.5 + qwertyu0196 | 0.5 + qwertyu0197 | 0.5 + qwertyu0198 | 0.5 + qwertyu0199 | 0.5 + qwertyu0200 | 0.5 + qwertyu0201 | 0.5 + qwertyu0202 | 0.5 + qwertyu0203 | 0.5 + qwertyu0204 | 0.5 + qwertyu0205 | 0.5 + qwertyu0206 | 0.5 + qwertyu0207 | 0.5 + qwertyu0208 | 0.5 + qwertyu0209 | 0.5 + qwertyu0210 | 0.5 + qwertyu0211 | 0.5 + qwertyu0212 | 0.5 + qwertyu0213 | 0.5 + qwertyu0214 | 0.5 + qwertyu0215 | 0.5 + qwertyu0216 | 0.5 + qwertyu0217 | 0.5 + qwertyu0218 | 0.5 + qwertyu0219 | 0.5 + qwertyu0220 | 0.5 + qwertyu0221 | 0.5 + qwertyu0222 | 0.5 + qwertyu0223 | 0.5 + qwertyu0224 | 0.5 + qwertyu0225 | 0.5 + qwertyu0226 | 0.5 + qwertyu0227 | 0.5 + qwertyu0228 | 0.5 + qwertyu0229 | 0.5 + qwertyu0230 | 0.5 + qwertyu0231 | 0.5 + qwertyu0232 | 0.5 + qwertyu0233 | 0.5 + qwertyu0234 | 0.5 + qwertyu0235 | 0.5 + qwertyu0236 | 0.5 + qwertyu0237 | 0.5 + qwertyu0238 | 0.5 + qwertyu0239 | 0.5 + qwertyu0240 | 0.5 + qwertyu0241 | 0.5 + qwertyu0242 | 0.5 + qwertyu0243 | 0.5 + qwertyu0244 | 0.5 + qwertyu0245 | 0.5 + qwertyu0246 | 0.5 + qwertyu0247 | 0.5 + qwertyu0248 | 0.5 + qwertyu0249 | 0.5 + qwertyu0250 | 0.5 + qwertyu0251 | 0.5 + qwertyu0252 | 0.5 + qwertyu0253 | 0.5 + qwertyu0254 | 0.5 + qwertyu0255 | 0.5 + qwertyu0256 | 0.5 + qwertyu0257 | 0.5 + qwertyu0258 | 0.5 + qwertyu0259 | 0.5 + qwertyu0260 | 0.5 + qwertyu0261 | 0.5 + qwertyu0262 | 0.5 + qwertyu0263 | 0.5 + qwertyu0264 | 0.5 + qwertyu0265 | 0.5 + qwertyu0266 | 0.5 + qwertyu0267 | 0.5 + qwertyu0268 | 0.5 + qwertyu0269 | 0.5 + qwertyu0270 | 0.5 + qwertyu0271 | 0.5 + qwertyu0272 | 0.5 + qwertyu0273 | 0.5 + qwertyu0274 | 0.5 + qwertyu0275 | 0.5 + qwertyu0276 | 0.5 + qwertyu0277 | 0.5 + qwertyu0278 | 0.5 + qwertyu0279 | 0.5 + qwertyu0280 | 0.5 + qwertyu0281 | 0.5 + qwertyu0282 | 0.5 + qwertyu0283 | 0.5 + qwertyu0284 | 0.5 + qwertyu0285 | 0.5 + qwertyu0286 | 0.5 + qwertyu0287 | 0.5 + qwertyu0289 | 0.5 + qwertyu0290 | 0.5 + qwertyu0291 | 0.5 + qwertyu0292 | 0.5 + qwertyu0293 | 0.5 + qwertyu0294 | 0.5 + qwertyu0295 | 0.5 + qwertyu0296 | 0.5 + qwertyu0297 | 0.5 + qwertyu0298 | 0.5 + qwertyu0299 | 0.5 + qwertyu0300 | 0.5 + qwertyu0301 | 0.5 + qwertyu0302 | 0.5 + qwertyu0303 | 0.5 + qwertyu0304 | 0.5 + qwertyu0305 | 0.5 + qwertyu0306 | 0.5 + qwertyu0307 | 0.5 + qwertyu0308 | 0.5 + qwertyu0309 | 0.5 + qwertyu0310 | 0.5 + qwertyu0311 | 0.5 + qwertyu0312 | 0.5 + qwertyu0313 | 0.5 + qwertyu0314 | 0.5 + qwertyu0315 | 0.5 + qwertyu0316 | 0.5 + qwertyu0317 | 0.5 + qwertyu0318 | 0.5 + qwertyu0319 | 0.5 + qwertyu0320 | 0.5 + qwertyu0321 | 0.5 + qwertyu0322 | 0.5 + qwertyu0323 | 0.5 + qwertyu0324 | 0.5 + qwertyu0325 | 0.5 + qwertyu0326 | 0.5 + qwertyu0327 | 0.5 + qwertyu0328 | 0.5 + qwertyu0329 | 0.5 + qwertyu0330 | 0.5 + qwertyu0331 | 0.5 + qwertyu0332 | 0.5 + qwertyu0333 | 0.5 + qwertyu0334 | 0.5 + qwertyu0335 | 0.5 + qwertyu0336 | 0.5 + qwertyu0337 | 0.5 + qwertyu0338 | 0.5 + qwertyu0339 | 0.5 + qwertyu0340 | 0.5 + qwertyu0341 | 0.5 + qwertyu0342 | 0.5 + qwertyu0343 | 0.5 + qwertyu0344 | 0.5 + qwertyu0345 | 0.5 + qwertyu0346 | 0.5 + qwertyu0347 | 0.5 + qwertyu0348 | 0.5 + qwertyu0349 | 0.5 + qwertyu0350 | 0.5 + qwertyu0351 | 0.5 + qwertyu0352 | 0.5 + qwertyu0353 | 0.5 + qwertyu0354 | 0.5 + qwertyu0355 | 0.5 + qwertyu0356 | 0.5 + qwertyu0357 | 0.5 + qwertyu0358 | 0.5 + qwertyu0359 | 0.5 + qwertyu0360 | 0.5 + qwertyu0361 | 0.5 + qwertyu0362 | 0.5 + qwertyu0363 | 0.5 + qwertyu0364 | 0.5 + qwertyu0365 | 0.5 + qwertyu0366 | 0.5 + qwertyu0367 | 0.5 + qwertyu0368 | 0.5 + qwertyu0369 | 0.5 + qwertyu0370 | 0.5 + qwertyu0371 | 0.5 + qwertyu0372 | 0.5 + qwertyu0373 | 0.5 + qwertyu0374 | 0.5 + qwertyu0375 | 0.5 + qwertyu0376 | 0.5 + qwertyu0377 | 0.5 + qwertyu0378 | 0.5 + qwertyu0379 | 0.5 + qwertyu0380 | 0.5 + qwertyu0381 | 0.5 + qwertyu0382 | 0.5 + qwertyu0383 | 0.5 + qwertyu0384 | 0.5 + qwertyu0385 | 0.5 + qwertyu0386 | 0.5 + qwertyu0387 | 0.5 + qwertyu0389 | 0.5 + qwertyu0390 | 0.5 + qwertyu0391 | 0.5 + qwertyu0392 | 0.5 + qwertyu0393 | 0.5 + qwertyu0394 | 0.5 + qwertyu0395 | 0.5 + qwertyu0396 | 0.5 + qwertyu0397 | 0.5 + qwertyu0398 | 0.5 + qwertyu0399 | 0.5 + qwertyu0400 | 0.5 + qwertyu0401 | 0.5 + qwertyu0402 | 0.5 + qwertyu0403 | 0.5 + qwertyu0404 | 0.5 + qwertyu0405 | 0.5 + qwertyu0406 | 0.5 + qwertyu0407 | 0.5 + qwertyu0408 | 0.5 + qwertyu0409 | 0.5 + qwertyu0410 | 0.5 + qwertyu0411 | 0.5 + qwertyu0412 | 0.5 + qwertyu0413 | 0.5 + qwertyu0414 | 0.5 + qwertyu0415 | 0.5 + qwertyu0416 | 0.5 + qwertyu0417 | 0.5 + qwertyu0418 | 0.5 + qwertyu0419 | 0.5 + qwertyu0420 | 0.5 + qwertyu0421 | 0.5 + qwertyu0422 | 0.5 + qwertyu0423 | 0.5 + qwertyu0424 | 0.5 + qwertyu0425 | 0.5 + qwertyu0426 | 0.5 + qwertyu0427 | 0.5 + qwertyu0428 | 0.5 + qwertyu0429 | 0.5 + qwertyu0430 | 0.5 + qwertyu0431 | 0.5 + qwertyu0432 | 0.5 + qwertyu0433 | 0.5 + qwertyu0434 | 0.5 + qwertyu0435 | 0.5 + qwertyu0436 | 0.5 + qwertyu0437 | 0.5 + qwertyu0438 | 0.5 + qwertyu0439 | 0.5 + qwertyu0440 | 0.5 + qwertyu0441 | 0.5 + qwertyu0442 | 0.5 + qwertyu0443 | 0.5 + qwertyu0444 | 0.5 + qwertyu0445 | 0.5 + qwertyu0446 | 0.5 + qwertyu0447 | 0.5 + qwertyu0448 | 0.5 + qwertyu0449 | 0.5 + qwertyu0450 | 0.5 + qwertyu0451 | 0.5 + qwertyu0452 | 0.5 + qwertyu0453 | 0.5 + qwertyu0454 | 0.5 + qwertyu0455 | 0.5 + qwertyu0456 | 0.5 + qwertyu0457 | 0.5 + qwertyu0458 | 0.5 + qwertyu0459 | 0.5 + qwertyu0460 | 0.5 + qwertyu0461 | 0.5 + qwertyu0462 | 0.5 + qwertyu0463 | 0.5 + qwertyu0464 | 0.5 + qwertyu0465 | 0.5 + qwertyu0466 | 0.5 + qwertyu0467 | 0.5 + qwertyu0468 | 0.5 + qwertyu0469 | 0.5 + qwertyu0470 | 0.5 + qwertyu0471 | 0.5 + qwertyu0472 | 0.5 + qwertyu0473 | 0.5 + qwertyu0474 | 0.5 + qwertyu0475 | 0.5 + qwertyu0476 | 0.5 + qwertyu0477 | 0.5 + qwertyu0478 | 0.5 + qwertyu0479 | 0.5 + qwertyu0480 | 0.5 + qwertyu0481 | 0.5 + qwertyu0482 | 0.5 + qwertyu0483 | 0.5 + qwertyu0484 | 0.5 + qwertyu0485 | 0.5 + qwertyu0486 | 0.5 + qwertyu0487 | 0.5 + qwertyu0489 | 0.5 + qwertyu0490 | 0.5 + qwertyu0491 | 0.5 + qwertyu0492 | 0.5 + qwertyu0493 | 0.5 + qwertyu0494 | 0.5 + qwertyu0495 | 0.5 + qwertyu0496 | 0.5 + qwertyu0497 | 0.5 + qwertyu0498 | 0.5 + qwertyu0499 | 0.5 + qwertyu0500 | 0.5 + qwertyu0501 | 0.5 + qwertyu0502 | 0.5 + qwertyu0503 | 0.5 + qwertyu0504 | 0.5 + qwertyu0505 | 0.5 + qwertyu0506 | 0.5 + qwertyu0507 | 0.5 + qwertyu0508 | 0.5 + qwertyu0509 | 0.5 + qwertyu0510 | 0.5 + qwertyu0511 | 0.5 + qwertyu0512 | 0.5 + qwertyu0513 | 0.5 + qwertyu0514 | 0.5 + qwertyu0515 | 0.5 + qwertyu0516 | 0.5 + qwertyu0517 | 0.5 + qwertyu0518 | 0.5 + qwertyu0519 | 0.5 + qwertyu0520 | 0.5 + qwertyu0521 | 0.5 + qwertyu0522 | 0.5 + qwertyu0523 | 0.5 + qwertyu0524 | 0.5 + qwertyu0525 | 0.5 + qwertyu0526 | 0.5 + qwertyu0527 | 0.5 + qwertyu0528 | 0.5 + qwertyu0529 | 0.5 + qwertyu0530 | 0.5 + qwertyu0531 | 0.5 + qwertyu0532 | 0.5 + qwertyu0533 | 0.5 + qwertyu0534 | 0.5 + qwertyu0535 | 0.5 + qwertyu0536 | 0.5 + qwertyu0537 | 0.5 + qwertyu0538 | 0.5 + qwertyu0539 | 0.5 + qwertyu0540 | 0.5 + qwertyu0541 | 0.5 + qwertyu0542 | 0.5 + qwertyu0543 | 0.5 + qwertyu0544 | 0.5 + qwertyu0545 | 0.5 + qwertyu0546 | 0.5 + qwertyu0547 | 0.5 + qwertyu0548 | 0.5 + qwertyu0549 | 0.5 + qwertyu0550 | 0.5 + qwertyu0551 | 0.5 + qwertyu0552 | 0.5 + qwertyu0553 | 0.5 + qwertyu0554 | 0.5 + qwertyu0555 | 0.5 + qwertyu0556 | 0.5 + qwertyu0557 | 0.5 + qwertyu0558 | 0.5 + qwertyu0559 | 0.5 + qwertyu0560 | 0.5 + qwertyu0561 | 0.5 + qwertyu0562 | 0.5 + qwertyu0563 | 0.5 + qwertyu0564 | 0.5 + qwertyu0565 | 0.5 + qwertyu0566 | 0.5 + qwertyu0567 | 0.5 + qwertyu0568 | 0.5 + qwertyu0569 | 0.5 + qwertyu0570 | 0.5 + qwertyu0571 | 0.5 + qwertyu0572 | 0.5 + qwertyu0573 | 0.5 + qwertyu0574 | 0.5 + qwertyu0575 | 0.5 + qwertyu0576 | 0.5 + qwertyu0577 | 0.5 + qwertyu0578 | 0.5 + qwertyu0579 | 0.5 + qwertyu0580 | 0.5 + qwertyu0581 | 0.5 + qwertyu0582 | 0.5 + qwertyu0583 | 0.5 + qwertyu0584 | 0.5 + qwertyu0585 | 0.5 + qwertyu0586 | 0.5 + qwertyu0587 | 0.5 + qwertyu0589 | 0.5 + qwertyu0590 | 0.5 + qwertyu0591 | 0.5 + qwertyu0592 | 0.5 + qwertyu0593 | 0.5 + qwertyu0594 | 0.5 + qwertyu0595 | 0.5 + qwertyu0596 | 0.5 + qwertyu0597 | 0.5 + qwertyu0598 | 0.5 + qwertyu0599 | 0.5 + qwertyu0600 | 0.5 + qwertyu0601 | 0.5 + qwertyu0602 | 0.5 + qwertyu0603 | 0.5 + qwertyu0604 | 0.5 + qwertyu0605 | 0.5 + qwertyu0606 | 0.5 + qwertyu0607 | 0.5 + qwertyu0608 | 0.5 + qwertyu0609 | 0.5 + qwertyu0610 | 0.5 + qwertyu0611 | 0.5 + qwertyu0612 | 0.5 + qwertyu0613 | 0.5 + qwertyu0614 | 0.5 + qwertyu0615 | 0.5 + qwertyu0616 | 0.5 + qwertyu0617 | 0.5 + qwertyu0618 | 0.5 + qwertyu0619 | 0.5 + qwertyu0620 | 0.5 + qwertyu0621 | 0.5 + qwertyu0622 | 0.5 + qwertyu0623 | 0.5 + qwertyu0624 | 0.5 + qwertyu0625 | 0.5 + qwertyu0626 | 0.5 + qwertyu0627 | 0.5 + qwertyu0628 | 0.5 + qwertyu0629 | 0.5 + qwertyu0630 | 0.5 + qwertyu0631 | 0.5 + qwertyu0632 | 0.5 + qwertyu0633 | 0.5 + qwertyu0634 | 0.5 + qwertyu0635 | 0.5 + qwertyu0636 | 0.5 + qwertyu0637 | 0.5 + qwertyu0638 | 0.5 + qwertyu0639 | 0.5 + qwertyu0640 | 0.5 + qwertyu0641 | 0.5 + qwertyu0642 | 0.5 + qwertyu0643 | 0.5 + qwertyu0644 | 0.5 + qwertyu0645 | 0.5 + qwertyu0646 | 0.5 + qwertyu0647 | 0.5 + qwertyu0648 | 0.5 + qwertyu0649 | 0.5 + qwertyu0650 | 0.5 + qwertyu0651 | 0.5 + qwertyu0652 | 0.5 + qwertyu0653 | 0.5 + qwertyu0654 | 0.5 + qwertyu0655 | 0.5 + qwertyu0656 | 0.5 + qwertyu0657 | 0.5 + qwertyu0658 | 0.5 + qwertyu0659 | 0.5 + qwertyu0660 | 0.5 + qwertyu0661 | 0.5 + qwertyu0662 | 0.5 + qwertyu0663 | 0.5 + qwertyu0664 | 0.5 + qwertyu0665 | 0.5 + qwertyu0666 | 0.5 + qwertyu0667 | 0.5 + qwertyu0668 | 0.5 + qwertyu0669 | 0.5 + qwertyu0670 | 0.5 + qwertyu0671 | 0.5 + qwertyu0672 | 0.5 + qwertyu0673 | 0.5 + qwertyu0674 | 0.5 + qwertyu0675 | 0.5 + qwertyu0676 | 0.5 + qwertyu0677 | 0.5 + qwertyu0678 | 0.5 + qwertyu0679 | 0.5 + qwertyu0680 | 0.5 + qwertyu0681 | 0.5 + qwertyu0682 | 0.5 + qwertyu0683 | 0.5 + qwertyu0684 | 0.5 + qwertyu0685 | 0.5 + qwertyu0686 | 0.5 + qwertyu0687 | 0.5 + qwertyu0689 | 0.5 + qwertyu0690 | 0.5 + qwertyu0691 | 0.5 + qwertyu0692 | 0.5 + qwertyu0693 | 0.5 + qwertyu0694 | 0.5 + qwertyu0695 | 0.5 + qwertyu0696 | 0.5 + qwertyu0697 | 0.5 + qwertyu0698 | 0.5 + qwertyu0699 | 0.5 + qwertyu0700 | 0.5 + qwertyu0701 | 0.5 + qwertyu0702 | 0.5 + qwertyu0703 | 0.5 + qwertyu0704 | 0.5 + qwertyu0705 | 0.5 + qwertyu0706 | 0.5 + qwertyu0707 | 0.5 + qwertyu0708 | 0.5 + qwertyu0709 | 0.5 + qwertyu0710 | 0.5 + qwertyu0711 | 0.5 + qwertyu0712 | 0.5 + qwertyu0713 | 0.5 + qwertyu0714 | 0.5 + qwertyu0715 | 0.5 + qwertyu0716 | 0.5 + qwertyu0717 | 0.5 + qwertyu0718 | 0.5 + qwertyu0719 | 0.5 + qwertyu0720 | 0.5 + qwertyu0721 | 0.5 + qwertyu0722 | 0.5 + qwertyu0723 | 0.5 + qwertyu0724 | 0.5 + qwertyu0725 | 0.5 + qwertyu0726 | 0.5 + qwertyu0727 | 0.5 + qwertyu0728 | 0.5 + qwertyu0729 | 0.5 + qwertyu0730 | 0.5 + qwertyu0731 | 0.5 + qwertyu0732 | 0.5 + qwertyu0733 | 0.5 + qwertyu0734 | 0.5 + qwertyu0735 | 0.5 + qwertyu0736 | 0.5 + qwertyu0737 | 0.5 + qwertyu0738 | 0.5 + qwertyu0739 | 0.5 + qwertyu0740 | 0.5 + qwertyu0741 | 0.5 + qwertyu0742 | 0.5 + qwertyu0743 | 0.5 + qwertyu0744 | 0.5 + qwertyu0745 | 0.5 + qwertyu0746 | 0.5 + qwertyu0747 | 0.5 + qwertyu0748 | 0.5 + qwertyu0749 | 0.5 + qwertyu0750 | 0.5 + qwertyu0751 | 0.5 + qwertyu0752 | 0.5 + qwertyu0753 | 0.5 + qwertyu0754 | 0.5 + qwertyu0755 | 0.5 + qwertyu0756 | 0.5 + qwertyu0757 | 0.5 + qwertyu0758 | 0.5 + qwertyu0759 | 0.5 + qwertyu0760 | 0.5 + qwertyu0761 | 0.5 + qwertyu0762 | 0.5 + qwertyu0763 | 0.5 + qwertyu0764 | 0.5 + qwertyu0765 | 0.5 + qwertyu0766 | 0.5 + qwertyu0767 | 0.5 + qwertyu0768 | 0.5 + qwertyu0769 | 0.5 + qwertyu0770 | 0.5 + qwertyu0771 | 0.5 + qwertyu0772 | 0.5 + qwertyu0773 | 0.5 + qwertyu0774 | 0.5 + qwertyu0775 | 0.5 + qwertyu0776 | 0.5 + qwertyu0777 | 0.5 + qwertyu0778 | 0.5 + qwertyu0779 | 0.5 + qwertyu0780 | 0.5 + qwertyu0781 | 0.5 + qwertyu0782 | 0.5 + qwertyu0783 | 0.5 + qwertyu0784 | 0.5 + qwertyu0785 | 0.5 + qwertyu0786 | 0.5 + qwertyu0787 | 0.5 + qwertyu0789 | 0.5 + qwertyu0790 | 0.5 + qwertyu0791 | 0.5 + qwertyu0792 | 0.5 + qwertyu0793 | 0.5 + qwertyu0794 | 0.5 + qwertyu0795 | 0.5 + qwertyu0796 | 0.5 + qwertyu0797 | 0.5 + qwertyu0798 | 0.5 + qwertyu0799 | 0.5 + qwertyu0800 | 0.5 + qwertyu0801 | 0.5 + qwertyu0802 | 0.5 + qwertyu0803 | 0.5 + qwertyu0804 | 0.5 + qwertyu0805 | 0.5 + qwertyu0806 | 0.5 + qwertyu0807 | 0.5 + qwertyu0808 | 0.5 + qwertyu0809 | 0.5 + qwertyu0810 | 0.5 + qwertyu0811 | 0.5 + qwertyu0812 | 0.5 + qwertyu0813 | 0.5 + qwertyu0814 | 0.5 + qwertyu0815 | 0.5 + qwertyu0816 | 0.5 + qwertyu0817 | 0.5 + qwertyu0818 | 0.5 + qwertyu0819 | 0.5 + qwertyu0820 | 0.5 + qwertyu0821 | 0.5 + qwertyu0822 | 0.5 + qwertyu0823 | 0.5 + qwertyu0824 | 0.5 + qwertyu0825 | 0.5 + qwertyu0826 | 0.5 + qwertyu0827 | 0.5 + qwertyu0828 | 0.5 + qwertyu0829 | 0.5 + qwertyu0830 | 0.5 + qwertyu0831 | 0.5 + qwertyu0832 | 0.5 + qwertyu0833 | 0.5 + qwertyu0834 | 0.5 + qwertyu0835 | 0.5 + qwertyu0836 | 0.5 + qwertyu0837 | 0.5 + qwertyu0838 | 0.5 + qwertyu0839 | 0.5 + qwertyu0840 | 0.5 + qwertyu0841 | 0.5 + qwertyu0842 | 0.5 + qwertyu0843 | 0.5 + qwertyu0844 | 0.5 + qwertyu0845 | 0.5 + qwertyu0846 | 0.5 + qwertyu0847 | 0.5 + qwertyu0848 | 0.5 + qwertyu0849 | 0.5 + qwertyu0850 | 0.5 + qwertyu0851 | 0.5 + qwertyu0852 | 0.5 + qwertyu0853 | 0.5 + qwertyu0854 | 0.5 + qwertyu0855 | 0.5 + qwertyu0856 | 0.5 + qwertyu0857 | 0.5 + qwertyu0858 | 0.5 + qwertyu0859 | 0.5 + qwertyu0860 | 0.5 + qwertyu0861 | 0.5 + qwertyu0862 | 0.5 + qwertyu0863 | 0.5 + qwertyu0864 | 0.5 + qwertyu0865 | 0.5 + qwertyu0866 | 0.5 + qwertyu0867 | 0.5 + qwertyu0868 | 0.5 + qwertyu0869 | 0.5 + qwertyu0870 | 0.5 + qwertyu0871 | 0.5 + qwertyu0872 | 0.5 + qwertyu0873 | 0.5 + qwertyu0874 | 0.5 + qwertyu0875 | 0.5 + qwertyu0876 | 0.5 + qwertyu0877 | 0.5 + qwertyu0878 | 0.5 + qwertyu0879 | 0.5 + qwertyu0880 | 0.5 + qwertyu0881 | 0.5 + qwertyu0882 | 0.5 + qwertyu0883 | 0.5 + qwertyu0884 | 0.5 + qwertyu0885 | 0.5 + qwertyu0886 | 0.5 + qwertyu0887 | 0.5 + qwertyu0889 | 0.5 + qwertyu0890 | 0.5 + qwertyu0891 | 0.5 + qwertyu0892 | 0.5 + qwertyu0893 | 0.5 + qwertyu0894 | 0.5 + qwertyu0895 | 0.5 + qwertyu0896 | 0.5 + qwertyu0897 | 0.5 + qwertyu0898 | 0.5 + qwertyu0899 | 0.5 + qwertyu1000 | 0.411765 +(1000 rows) + +select t,similarity(t,'gwertyu0988') as sml from test_trgm where t % 'gwertyu0988' order by sml desc, t; + t | sml +-------------+---------- + qwertyu0988 | 0.6 + qwertyu0980 | 0.411765 + qwertyu0981 | 0.411765 + qwertyu0982 | 0.411765 + qwertyu0983 | 0.411765 + qwertyu0984 | 0.411765 + qwertyu0985 | 0.411765 + qwertyu0986 | 0.411765 + qwertyu0987 | 0.411765 + qwertyu0989 | 0.411765 + qwertyu0088 | 0.333333 + qwertyu0098 | 0.333333 + qwertyu0188 | 0.333333 + qwertyu0288 | 0.333333 + qwertyu0388 | 0.333333 + qwertyu0488 | 0.333333 + qwertyu0588 | 0.333333 + qwertyu0688 | 0.333333 + qwertyu0788 | 0.333333 + qwertyu0888 | 0.333333 + qwertyu0900 | 0.333333 + qwertyu0901 | 0.333333 + qwertyu0902 | 0.333333 + qwertyu0903 | 0.333333 + qwertyu0904 | 0.333333 + qwertyu0905 | 0.333333 + qwertyu0906 | 0.333333 + qwertyu0907 | 0.333333 + qwertyu0908 | 0.333333 + qwertyu0909 | 0.333333 + qwertyu0910 | 0.333333 + qwertyu0911 | 0.333333 + qwertyu0912 | 0.333333 + qwertyu0913 | 0.333333 + qwertyu0914 | 0.333333 + qwertyu0915 | 0.333333 + qwertyu0916 | 0.333333 + qwertyu0917 | 0.333333 + qwertyu0918 | 0.333333 + qwertyu0919 | 0.333333 + qwertyu0920 | 0.333333 + qwertyu0921 | 0.333333 + qwertyu0922 | 0.333333 + qwertyu0923 | 0.333333 + qwertyu0924 | 0.333333 + qwertyu0925 | 0.333333 + qwertyu0926 | 0.333333 + qwertyu0927 | 0.333333 + qwertyu0928 | 0.333333 + qwertyu0929 | 0.333333 + qwertyu0930 | 0.333333 + qwertyu0931 | 0.333333 + qwertyu0932 | 0.333333 + qwertyu0933 | 0.333333 + qwertyu0934 | 0.333333 + qwertyu0935 | 0.333333 + qwertyu0936 | 0.333333 + qwertyu0937 | 0.333333 + qwertyu0938 | 0.333333 + qwertyu0939 | 0.333333 + qwertyu0940 | 0.333333 + qwertyu0941 | 0.333333 + qwertyu0942 | 0.333333 + qwertyu0943 | 0.333333 + qwertyu0944 | 0.333333 + qwertyu0945 | 0.333333 + qwertyu0946 | 0.333333 + qwertyu0947 | 0.333333 + qwertyu0948 | 0.333333 + qwertyu0949 | 0.333333 + qwertyu0950 | 0.333333 + qwertyu0951 | 0.333333 + qwertyu0952 | 0.333333 + qwertyu0953 | 0.333333 + qwertyu0954 | 0.333333 + qwertyu0955 | 0.333333 + qwertyu0956 | 0.333333 + qwertyu0957 | 0.333333 + qwertyu0958 | 0.333333 + qwertyu0959 | 0.333333 + qwertyu0960 | 0.333333 + qwertyu0961 | 0.333333 + qwertyu0962 | 0.333333 + qwertyu0963 | 0.333333 + qwertyu0964 | 0.333333 + qwertyu0965 | 0.333333 + qwertyu0966 | 0.333333 + qwertyu0967 | 0.333333 + qwertyu0968 | 0.333333 + qwertyu0969 | 0.333333 + qwertyu0970 | 0.333333 + qwertyu0971 | 0.333333 + qwertyu0972 | 0.333333 + qwertyu0973 | 0.333333 + qwertyu0974 | 0.333333 + qwertyu0975 | 0.333333 + qwertyu0976 | 0.333333 + qwertyu0977 | 0.333333 + qwertyu0978 | 0.333333 + qwertyu0979 | 0.333333 + qwertyu0990 | 0.333333 + qwertyu0991 | 0.333333 + qwertyu0992 | 0.333333 + qwertyu0993 | 0.333333 + qwertyu0994 | 0.333333 + qwertyu0995 | 0.333333 + qwertyu0996 | 0.333333 + qwertyu0997 | 0.333333 + qwertyu0998 | 0.333333 + qwertyu0999 | 0.333333 +(110 rows) + +select t,similarity(t,'gwertyu1988') as sml from test_trgm where t % 'gwertyu1988' order by sml desc, t; + t | sml +-------------+---------- + qwertyu0988 | 0.333333 +(1 row) + +explain (costs off) +select t <-> 'q0987wertyu0988', t from test_trgm order by t <-> 'q0987wertyu0988' limit 2; + QUERY PLAN +--------------------------------------------------- + Limit + -> Index Scan using trgm_idx on test_trgm + Order By: (t <-> 'q0987wertyu0988'::text) +(3 rows) + +select t <-> 'q0987wertyu0988', t from test_trgm order by t <-> 'q0987wertyu0988' limit 2; + ?column? | t +----------+------------- + 0.411765 | qwertyu0988 + 0.5 | qwertyu0987 +(2 rows) + +select count(*) from test_trgm where t ~ '[qwerty]{2}-?[qwerty]{2}'; + count +------- + 1000 +(1 row) + +drop index trgm_idx; +create index trgm_idx on test_trgm using gist (t gist_trgm_ops(siglen=0)); +ERROR: value 0 out of bounds for option "siglen" +DETAIL: Valid values are between "1" and "2024". +create index trgm_idx on test_trgm using gist (t gist_trgm_ops(siglen=2025)); +ERROR: value 2025 out of bounds for option "siglen" +DETAIL: Valid values are between "1" and "2024". +create index trgm_idx on test_trgm using gist (t gist_trgm_ops(siglen=2024)); +set enable_seqscan=off; +select t,similarity(t,'qwertyu0988') as sml from test_trgm where t % 'qwertyu0988' order by sml desc, t; + t | sml +-------------+---------- + qwertyu0988 | 1 + qwertyu0980 | 0.714286 + qwertyu0981 | 0.714286 + qwertyu0982 | 0.714286 + qwertyu0983 | 0.714286 + qwertyu0984 | 0.714286 + qwertyu0985 | 0.714286 + qwertyu0986 | 0.714286 + qwertyu0987 | 0.714286 + qwertyu0989 | 0.714286 + qwertyu0088 | 0.6 + qwertyu0098 | 0.6 + qwertyu0188 | 0.6 + qwertyu0288 | 0.6 + qwertyu0388 | 0.6 + qwertyu0488 | 0.6 + qwertyu0588 | 0.6 + qwertyu0688 | 0.6 + qwertyu0788 | 0.6 + qwertyu0888 | 0.6 + qwertyu0900 | 0.6 + qwertyu0901 | 0.6 + qwertyu0902 | 0.6 + qwertyu0903 | 0.6 + qwertyu0904 | 0.6 + qwertyu0905 | 0.6 + qwertyu0906 | 0.6 + qwertyu0907 | 0.6 + qwertyu0908 | 0.6 + qwertyu0909 | 0.6 + qwertyu0910 | 0.6 + qwertyu0911 | 0.6 + qwertyu0912 | 0.6 + qwertyu0913 | 0.6 + qwertyu0914 | 0.6 + qwertyu0915 | 0.6 + qwertyu0916 | 0.6 + qwertyu0917 | 0.6 + qwertyu0918 | 0.6 + qwertyu0919 | 0.6 + qwertyu0920 | 0.6 + qwertyu0921 | 0.6 + qwertyu0922 | 0.6 + qwertyu0923 | 0.6 + qwertyu0924 | 0.6 + qwertyu0925 | 0.6 + qwertyu0926 | 0.6 + qwertyu0927 | 0.6 + qwertyu0928 | 0.6 + qwertyu0929 | 0.6 + qwertyu0930 | 0.6 + qwertyu0931 | 0.6 + qwertyu0932 | 0.6 + qwertyu0933 | 0.6 + qwertyu0934 | 0.6 + qwertyu0935 | 0.6 + qwertyu0936 | 0.6 + qwertyu0937 | 0.6 + qwertyu0938 | 0.6 + qwertyu0939 | 0.6 + qwertyu0940 | 0.6 + qwertyu0941 | 0.6 + qwertyu0942 | 0.6 + qwertyu0943 | 0.6 + qwertyu0944 | 0.6 + qwertyu0945 | 0.6 + qwertyu0946 | 0.6 + qwertyu0947 | 0.6 + qwertyu0948 | 0.6 + qwertyu0949 | 0.6 + qwertyu0950 | 0.6 + qwertyu0951 | 0.6 + qwertyu0952 | 0.6 + qwertyu0953 | 0.6 + qwertyu0954 | 0.6 + qwertyu0955 | 0.6 + qwertyu0956 | 0.6 + qwertyu0957 | 0.6 + qwertyu0958 | 0.6 + qwertyu0959 | 0.6 + qwertyu0960 | 0.6 + qwertyu0961 | 0.6 + qwertyu0962 | 0.6 + qwertyu0963 | 0.6 + qwertyu0964 | 0.6 + qwertyu0965 | 0.6 + qwertyu0966 | 0.6 + qwertyu0967 | 0.6 + qwertyu0968 | 0.6 + qwertyu0969 | 0.6 + qwertyu0970 | 0.6 + qwertyu0971 | 0.6 + qwertyu0972 | 0.6 + qwertyu0973 | 0.6 + qwertyu0974 | 0.6 + qwertyu0975 | 0.6 + qwertyu0976 | 0.6 + qwertyu0977 | 0.6 + qwertyu0978 | 0.6 + qwertyu0979 | 0.6 + qwertyu0990 | 0.6 + qwertyu0991 | 0.6 + qwertyu0992 | 0.6 + qwertyu0993 | 0.6 + qwertyu0994 | 0.6 + qwertyu0995 | 0.6 + qwertyu0996 | 0.6 + qwertyu0997 | 0.6 + qwertyu0998 | 0.6 + qwertyu0999 | 0.6 + qwertyu0001 | 0.5 + qwertyu0002 | 0.5 + qwertyu0003 | 0.5 + qwertyu0004 | 0.5 + qwertyu0005 | 0.5 + qwertyu0006 | 0.5 + qwertyu0007 | 0.5 + qwertyu0008 | 0.5 + qwertyu0009 | 0.5 + qwertyu0010 | 0.5 + qwertyu0011 | 0.5 + qwertyu0012 | 0.5 + qwertyu0013 | 0.5 + qwertyu0014 | 0.5 + qwertyu0015 | 0.5 + qwertyu0016 | 0.5 + qwertyu0017 | 0.5 + qwertyu0018 | 0.5 + qwertyu0019 | 0.5 + qwertyu0020 | 0.5 + qwertyu0021 | 0.5 + qwertyu0022 | 0.5 + qwertyu0023 | 0.5 + qwertyu0024 | 0.5 + qwertyu0025 | 0.5 + qwertyu0026 | 0.5 + qwertyu0027 | 0.5 + qwertyu0028 | 0.5 + qwertyu0029 | 0.5 + qwertyu0030 | 0.5 + qwertyu0031 | 0.5 + qwertyu0032 | 0.5 + qwertyu0033 | 0.5 + qwertyu0034 | 0.5 + qwertyu0035 | 0.5 + qwertyu0036 | 0.5 + qwertyu0037 | 0.5 + qwertyu0038 | 0.5 + qwertyu0039 | 0.5 + qwertyu0040 | 0.5 + qwertyu0041 | 0.5 + qwertyu0042 | 0.5 + qwertyu0043 | 0.5 + qwertyu0044 | 0.5 + qwertyu0045 | 0.5 + qwertyu0046 | 0.5 + qwertyu0047 | 0.5 + qwertyu0048 | 0.5 + qwertyu0049 | 0.5 + qwertyu0050 | 0.5 + qwertyu0051 | 0.5 + qwertyu0052 | 0.5 + qwertyu0053 | 0.5 + qwertyu0054 | 0.5 + qwertyu0055 | 0.5 + qwertyu0056 | 0.5 + qwertyu0057 | 0.5 + qwertyu0058 | 0.5 + qwertyu0059 | 0.5 + qwertyu0060 | 0.5 + qwertyu0061 | 0.5 + qwertyu0062 | 0.5 + qwertyu0063 | 0.5 + qwertyu0064 | 0.5 + qwertyu0065 | 0.5 + qwertyu0066 | 0.5 + qwertyu0067 | 0.5 + qwertyu0068 | 0.5 + qwertyu0069 | 0.5 + qwertyu0070 | 0.5 + qwertyu0071 | 0.5 + qwertyu0072 | 0.5 + qwertyu0073 | 0.5 + qwertyu0074 | 0.5 + qwertyu0075 | 0.5 + qwertyu0076 | 0.5 + qwertyu0077 | 0.5 + qwertyu0078 | 0.5 + qwertyu0079 | 0.5 + qwertyu0080 | 0.5 + qwertyu0081 | 0.5 + qwertyu0082 | 0.5 + qwertyu0083 | 0.5 + qwertyu0084 | 0.5 + qwertyu0085 | 0.5 + qwertyu0086 | 0.5 + qwertyu0087 | 0.5 + qwertyu0089 | 0.5 + qwertyu0090 | 0.5 + qwertyu0091 | 0.5 + qwertyu0092 | 0.5 + qwertyu0093 | 0.5 + qwertyu0094 | 0.5 + qwertyu0095 | 0.5 + qwertyu0096 | 0.5 + qwertyu0097 | 0.5 + qwertyu0099 | 0.5 + qwertyu0100 | 0.5 + qwertyu0101 | 0.5 + qwertyu0102 | 0.5 + qwertyu0103 | 0.5 + qwertyu0104 | 0.5 + qwertyu0105 | 0.5 + qwertyu0106 | 0.5 + qwertyu0107 | 0.5 + qwertyu0108 | 0.5 + qwertyu0109 | 0.5 + qwertyu0110 | 0.5 + qwertyu0111 | 0.5 + qwertyu0112 | 0.5 + qwertyu0113 | 0.5 + qwertyu0114 | 0.5 + qwertyu0115 | 0.5 + qwertyu0116 | 0.5 + qwertyu0117 | 0.5 + qwertyu0118 | 0.5 + qwertyu0119 | 0.5 + qwertyu0120 | 0.5 + qwertyu0121 | 0.5 + qwertyu0122 | 0.5 + qwertyu0123 | 0.5 + qwertyu0124 | 0.5 + qwertyu0125 | 0.5 + qwertyu0126 | 0.5 + qwertyu0127 | 0.5 + qwertyu0128 | 0.5 + qwertyu0129 | 0.5 + qwertyu0130 | 0.5 + qwertyu0131 | 0.5 + qwertyu0132 | 0.5 + qwertyu0133 | 0.5 + qwertyu0134 | 0.5 + qwertyu0135 | 0.5 + qwertyu0136 | 0.5 + qwertyu0137 | 0.5 + qwertyu0138 | 0.5 + qwertyu0139 | 0.5 + qwertyu0140 | 0.5 + qwertyu0141 | 0.5 + qwertyu0142 | 0.5 + qwertyu0143 | 0.5 + qwertyu0144 | 0.5 + qwertyu0145 | 0.5 + qwertyu0146 | 0.5 + qwertyu0147 | 0.5 + qwertyu0148 | 0.5 + qwertyu0149 | 0.5 + qwertyu0150 | 0.5 + qwertyu0151 | 0.5 + qwertyu0152 | 0.5 + qwertyu0153 | 0.5 + qwertyu0154 | 0.5 + qwertyu0155 | 0.5 + qwertyu0156 | 0.5 + qwertyu0157 | 0.5 + qwertyu0158 | 0.5 + qwertyu0159 | 0.5 + qwertyu0160 | 0.5 + qwertyu0161 | 0.5 + qwertyu0162 | 0.5 + qwertyu0163 | 0.5 + qwertyu0164 | 0.5 + qwertyu0165 | 0.5 + qwertyu0166 | 0.5 + qwertyu0167 | 0.5 + qwertyu0168 | 0.5 + qwertyu0169 | 0.5 + qwertyu0170 | 0.5 + qwertyu0171 | 0.5 + qwertyu0172 | 0.5 + qwertyu0173 | 0.5 + qwertyu0174 | 0.5 + qwertyu0175 | 0.5 + qwertyu0176 | 0.5 + qwertyu0177 | 0.5 + qwertyu0178 | 0.5 + qwertyu0179 | 0.5 + qwertyu0180 | 0.5 + qwertyu0181 | 0.5 + qwertyu0182 | 0.5 + qwertyu0183 | 0.5 + qwertyu0184 | 0.5 + qwertyu0185 | 0.5 + qwertyu0186 | 0.5 + qwertyu0187 | 0.5 + qwertyu0189 | 0.5 + qwertyu0190 | 0.5 + qwertyu0191 | 0.5 + qwertyu0192 | 0.5 + qwertyu0193 | 0.5 + qwertyu0194 | 0.5 + qwertyu0195 | 0.5 + qwertyu0196 | 0.5 + qwertyu0197 | 0.5 + qwertyu0198 | 0.5 + qwertyu0199 | 0.5 + qwertyu0200 | 0.5 + qwertyu0201 | 0.5 + qwertyu0202 | 0.5 + qwertyu0203 | 0.5 + qwertyu0204 | 0.5 + qwertyu0205 | 0.5 + qwertyu0206 | 0.5 + qwertyu0207 | 0.5 + qwertyu0208 | 0.5 + qwertyu0209 | 0.5 + qwertyu0210 | 0.5 + qwertyu0211 | 0.5 + qwertyu0212 | 0.5 + qwertyu0213 | 0.5 + qwertyu0214 | 0.5 + qwertyu0215 | 0.5 + qwertyu0216 | 0.5 + qwertyu0217 | 0.5 + qwertyu0218 | 0.5 + qwertyu0219 | 0.5 + qwertyu0220 | 0.5 + qwertyu0221 | 0.5 + qwertyu0222 | 0.5 + qwertyu0223 | 0.5 + qwertyu0224 | 0.5 + qwertyu0225 | 0.5 + qwertyu0226 | 0.5 + qwertyu0227 | 0.5 + qwertyu0228 | 0.5 + qwertyu0229 | 0.5 + qwertyu0230 | 0.5 + qwertyu0231 | 0.5 + qwertyu0232 | 0.5 + qwertyu0233 | 0.5 + qwertyu0234 | 0.5 + qwertyu0235 | 0.5 + qwertyu0236 | 0.5 + qwertyu0237 | 0.5 + qwertyu0238 | 0.5 + qwertyu0239 | 0.5 + qwertyu0240 | 0.5 + qwertyu0241 | 0.5 + qwertyu0242 | 0.5 + qwertyu0243 | 0.5 + qwertyu0244 | 0.5 + qwertyu0245 | 0.5 + qwertyu0246 | 0.5 + qwertyu0247 | 0.5 + qwertyu0248 | 0.5 + qwertyu0249 | 0.5 + qwertyu0250 | 0.5 + qwertyu0251 | 0.5 + qwertyu0252 | 0.5 + qwertyu0253 | 0.5 + qwertyu0254 | 0.5 + qwertyu0255 | 0.5 + qwertyu0256 | 0.5 + qwertyu0257 | 0.5 + qwertyu0258 | 0.5 + qwertyu0259 | 0.5 + qwertyu0260 | 0.5 + qwertyu0261 | 0.5 + qwertyu0262 | 0.5 + qwertyu0263 | 0.5 + qwertyu0264 | 0.5 + qwertyu0265 | 0.5 + qwertyu0266 | 0.5 + qwertyu0267 | 0.5 + qwertyu0268 | 0.5 + qwertyu0269 | 0.5 + qwertyu0270 | 0.5 + qwertyu0271 | 0.5 + qwertyu0272 | 0.5 + qwertyu0273 | 0.5 + qwertyu0274 | 0.5 + qwertyu0275 | 0.5 + qwertyu0276 | 0.5 + qwertyu0277 | 0.5 + qwertyu0278 | 0.5 + qwertyu0279 | 0.5 + qwertyu0280 | 0.5 + qwertyu0281 | 0.5 + qwertyu0282 | 0.5 + qwertyu0283 | 0.5 + qwertyu0284 | 0.5 + qwertyu0285 | 0.5 + qwertyu0286 | 0.5 + qwertyu0287 | 0.5 + qwertyu0289 | 0.5 + qwertyu0290 | 0.5 + qwertyu0291 | 0.5 + qwertyu0292 | 0.5 + qwertyu0293 | 0.5 + qwertyu0294 | 0.5 + qwertyu0295 | 0.5 + qwertyu0296 | 0.5 + qwertyu0297 | 0.5 + qwertyu0298 | 0.5 + qwertyu0299 | 0.5 + qwertyu0300 | 0.5 + qwertyu0301 | 0.5 + qwertyu0302 | 0.5 + qwertyu0303 | 0.5 + qwertyu0304 | 0.5 + qwertyu0305 | 0.5 + qwertyu0306 | 0.5 + qwertyu0307 | 0.5 + qwertyu0308 | 0.5 + qwertyu0309 | 0.5 + qwertyu0310 | 0.5 + qwertyu0311 | 0.5 + qwertyu0312 | 0.5 + qwertyu0313 | 0.5 + qwertyu0314 | 0.5 + qwertyu0315 | 0.5 + qwertyu0316 | 0.5 + qwertyu0317 | 0.5 + qwertyu0318 | 0.5 + qwertyu0319 | 0.5 + qwertyu0320 | 0.5 + qwertyu0321 | 0.5 + qwertyu0322 | 0.5 + qwertyu0323 | 0.5 + qwertyu0324 | 0.5 + qwertyu0325 | 0.5 + qwertyu0326 | 0.5 + qwertyu0327 | 0.5 + qwertyu0328 | 0.5 + qwertyu0329 | 0.5 + qwertyu0330 | 0.5 + qwertyu0331 | 0.5 + qwertyu0332 | 0.5 + qwertyu0333 | 0.5 + qwertyu0334 | 0.5 + qwertyu0335 | 0.5 + qwertyu0336 | 0.5 + qwertyu0337 | 0.5 + qwertyu0338 | 0.5 + qwertyu0339 | 0.5 + qwertyu0340 | 0.5 + qwertyu0341 | 0.5 + qwertyu0342 | 0.5 + qwertyu0343 | 0.5 + qwertyu0344 | 0.5 + qwertyu0345 | 0.5 + qwertyu0346 | 0.5 + qwertyu0347 | 0.5 + qwertyu0348 | 0.5 + qwertyu0349 | 0.5 + qwertyu0350 | 0.5 + qwertyu0351 | 0.5 + qwertyu0352 | 0.5 + qwertyu0353 | 0.5 + qwertyu0354 | 0.5 + qwertyu0355 | 0.5 + qwertyu0356 | 0.5 + qwertyu0357 | 0.5 + qwertyu0358 | 0.5 + qwertyu0359 | 0.5 + qwertyu0360 | 0.5 + qwertyu0361 | 0.5 + qwertyu0362 | 0.5 + qwertyu0363 | 0.5 + qwertyu0364 | 0.5 + qwertyu0365 | 0.5 + qwertyu0366 | 0.5 + qwertyu0367 | 0.5 + qwertyu0368 | 0.5 + qwertyu0369 | 0.5 + qwertyu0370 | 0.5 + qwertyu0371 | 0.5 + qwertyu0372 | 0.5 + qwertyu0373 | 0.5 + qwertyu0374 | 0.5 + qwertyu0375 | 0.5 + qwertyu0376 | 0.5 + qwertyu0377 | 0.5 + qwertyu0378 | 0.5 + qwertyu0379 | 0.5 + qwertyu0380 | 0.5 + qwertyu0381 | 0.5 + qwertyu0382 | 0.5 + qwertyu0383 | 0.5 + qwertyu0384 | 0.5 + qwertyu0385 | 0.5 + qwertyu0386 | 0.5 + qwertyu0387 | 0.5 + qwertyu0389 | 0.5 + qwertyu0390 | 0.5 + qwertyu0391 | 0.5 + qwertyu0392 | 0.5 + qwertyu0393 | 0.5 + qwertyu0394 | 0.5 + qwertyu0395 | 0.5 + qwertyu0396 | 0.5 + qwertyu0397 | 0.5 + qwertyu0398 | 0.5 + qwertyu0399 | 0.5 + qwertyu0400 | 0.5 + qwertyu0401 | 0.5 + qwertyu0402 | 0.5 + qwertyu0403 | 0.5 + qwertyu0404 | 0.5 + qwertyu0405 | 0.5 + qwertyu0406 | 0.5 + qwertyu0407 | 0.5 + qwertyu0408 | 0.5 + qwertyu0409 | 0.5 + qwertyu0410 | 0.5 + qwertyu0411 | 0.5 + qwertyu0412 | 0.5 + qwertyu0413 | 0.5 + qwertyu0414 | 0.5 + qwertyu0415 | 0.5 + qwertyu0416 | 0.5 + qwertyu0417 | 0.5 + qwertyu0418 | 0.5 + qwertyu0419 | 0.5 + qwertyu0420 | 0.5 + qwertyu0421 | 0.5 + qwertyu0422 | 0.5 + qwertyu0423 | 0.5 + qwertyu0424 | 0.5 + qwertyu0425 | 0.5 + qwertyu0426 | 0.5 + qwertyu0427 | 0.5 + qwertyu0428 | 0.5 + qwertyu0429 | 0.5 + qwertyu0430 | 0.5 + qwertyu0431 | 0.5 + qwertyu0432 | 0.5 + qwertyu0433 | 0.5 + qwertyu0434 | 0.5 + qwertyu0435 | 0.5 + qwertyu0436 | 0.5 + qwertyu0437 | 0.5 + qwertyu0438 | 0.5 + qwertyu0439 | 0.5 + qwertyu0440 | 0.5 + qwertyu0441 | 0.5 + qwertyu0442 | 0.5 + qwertyu0443 | 0.5 + qwertyu0444 | 0.5 + qwertyu0445 | 0.5 + qwertyu0446 | 0.5 + qwertyu0447 | 0.5 + qwertyu0448 | 0.5 + qwertyu0449 | 0.5 + qwertyu0450 | 0.5 + qwertyu0451 | 0.5 + qwertyu0452 | 0.5 + qwertyu0453 | 0.5 + qwertyu0454 | 0.5 + qwertyu0455 | 0.5 + qwertyu0456 | 0.5 + qwertyu0457 | 0.5 + qwertyu0458 | 0.5 + qwertyu0459 | 0.5 + qwertyu0460 | 0.5 + qwertyu0461 | 0.5 + qwertyu0462 | 0.5 + qwertyu0463 | 0.5 + qwertyu0464 | 0.5 + qwertyu0465 | 0.5 + qwertyu0466 | 0.5 + qwertyu0467 | 0.5 + qwertyu0468 | 0.5 + qwertyu0469 | 0.5 + qwertyu0470 | 0.5 + qwertyu0471 | 0.5 + qwertyu0472 | 0.5 + qwertyu0473 | 0.5 + qwertyu0474 | 0.5 + qwertyu0475 | 0.5 + qwertyu0476 | 0.5 + qwertyu0477 | 0.5 + qwertyu0478 | 0.5 + qwertyu0479 | 0.5 + qwertyu0480 | 0.5 + qwertyu0481 | 0.5 + qwertyu0482 | 0.5 + qwertyu0483 | 0.5 + qwertyu0484 | 0.5 + qwertyu0485 | 0.5 + qwertyu0486 | 0.5 + qwertyu0487 | 0.5 + qwertyu0489 | 0.5 + qwertyu0490 | 0.5 + qwertyu0491 | 0.5 + qwertyu0492 | 0.5 + qwertyu0493 | 0.5 + qwertyu0494 | 0.5 + qwertyu0495 | 0.5 + qwertyu0496 | 0.5 + qwertyu0497 | 0.5 + qwertyu0498 | 0.5 + qwertyu0499 | 0.5 + qwertyu0500 | 0.5 + qwertyu0501 | 0.5 + qwertyu0502 | 0.5 + qwertyu0503 | 0.5 + qwertyu0504 | 0.5 + qwertyu0505 | 0.5 + qwertyu0506 | 0.5 + qwertyu0507 | 0.5 + qwertyu0508 | 0.5 + qwertyu0509 | 0.5 + qwertyu0510 | 0.5 + qwertyu0511 | 0.5 + qwertyu0512 | 0.5 + qwertyu0513 | 0.5 + qwertyu0514 | 0.5 + qwertyu0515 | 0.5 + qwertyu0516 | 0.5 + qwertyu0517 | 0.5 + qwertyu0518 | 0.5 + qwertyu0519 | 0.5 + qwertyu0520 | 0.5 + qwertyu0521 | 0.5 + qwertyu0522 | 0.5 + qwertyu0523 | 0.5 + qwertyu0524 | 0.5 + qwertyu0525 | 0.5 + qwertyu0526 | 0.5 + qwertyu0527 | 0.5 + qwertyu0528 | 0.5 + qwertyu0529 | 0.5 + qwertyu0530 | 0.5 + qwertyu0531 | 0.5 + qwertyu0532 | 0.5 + qwertyu0533 | 0.5 + qwertyu0534 | 0.5 + qwertyu0535 | 0.5 + qwertyu0536 | 0.5 + qwertyu0537 | 0.5 + qwertyu0538 | 0.5 + qwertyu0539 | 0.5 + qwertyu0540 | 0.5 + qwertyu0541 | 0.5 + qwertyu0542 | 0.5 + qwertyu0543 | 0.5 + qwertyu0544 | 0.5 + qwertyu0545 | 0.5 + qwertyu0546 | 0.5 + qwertyu0547 | 0.5 + qwertyu0548 | 0.5 + qwertyu0549 | 0.5 + qwertyu0550 | 0.5 + qwertyu0551 | 0.5 + qwertyu0552 | 0.5 + qwertyu0553 | 0.5 + qwertyu0554 | 0.5 + qwertyu0555 | 0.5 + qwertyu0556 | 0.5 + qwertyu0557 | 0.5 + qwertyu0558 | 0.5 + qwertyu0559 | 0.5 + qwertyu0560 | 0.5 + qwertyu0561 | 0.5 + qwertyu0562 | 0.5 + qwertyu0563 | 0.5 + qwertyu0564 | 0.5 + qwertyu0565 | 0.5 + qwertyu0566 | 0.5 + qwertyu0567 | 0.5 + qwertyu0568 | 0.5 + qwertyu0569 | 0.5 + qwertyu0570 | 0.5 + qwertyu0571 | 0.5 + qwertyu0572 | 0.5 + qwertyu0573 | 0.5 + qwertyu0574 | 0.5 + qwertyu0575 | 0.5 + qwertyu0576 | 0.5 + qwertyu0577 | 0.5 + qwertyu0578 | 0.5 + qwertyu0579 | 0.5 + qwertyu0580 | 0.5 + qwertyu0581 | 0.5 + qwertyu0582 | 0.5 + qwertyu0583 | 0.5 + qwertyu0584 | 0.5 + qwertyu0585 | 0.5 + qwertyu0586 | 0.5 + qwertyu0587 | 0.5 + qwertyu0589 | 0.5 + qwertyu0590 | 0.5 + qwertyu0591 | 0.5 + qwertyu0592 | 0.5 + qwertyu0593 | 0.5 + qwertyu0594 | 0.5 + qwertyu0595 | 0.5 + qwertyu0596 | 0.5 + qwertyu0597 | 0.5 + qwertyu0598 | 0.5 + qwertyu0599 | 0.5 + qwertyu0600 | 0.5 + qwertyu0601 | 0.5 + qwertyu0602 | 0.5 + qwertyu0603 | 0.5 + qwertyu0604 | 0.5 + qwertyu0605 | 0.5 + qwertyu0606 | 0.5 + qwertyu0607 | 0.5 + qwertyu0608 | 0.5 + qwertyu0609 | 0.5 + qwertyu0610 | 0.5 + qwertyu0611 | 0.5 + qwertyu0612 | 0.5 + qwertyu0613 | 0.5 + qwertyu0614 | 0.5 + qwertyu0615 | 0.5 + qwertyu0616 | 0.5 + qwertyu0617 | 0.5 + qwertyu0618 | 0.5 + qwertyu0619 | 0.5 + qwertyu0620 | 0.5 + qwertyu0621 | 0.5 + qwertyu0622 | 0.5 + qwertyu0623 | 0.5 + qwertyu0624 | 0.5 + qwertyu0625 | 0.5 + qwertyu0626 | 0.5 + qwertyu0627 | 0.5 + qwertyu0628 | 0.5 + qwertyu0629 | 0.5 + qwertyu0630 | 0.5 + qwertyu0631 | 0.5 + qwertyu0632 | 0.5 + qwertyu0633 | 0.5 + qwertyu0634 | 0.5 + qwertyu0635 | 0.5 + qwertyu0636 | 0.5 + qwertyu0637 | 0.5 + qwertyu0638 | 0.5 + qwertyu0639 | 0.5 + qwertyu0640 | 0.5 + qwertyu0641 | 0.5 + qwertyu0642 | 0.5 + qwertyu0643 | 0.5 + qwertyu0644 | 0.5 + qwertyu0645 | 0.5 + qwertyu0646 | 0.5 + qwertyu0647 | 0.5 + qwertyu0648 | 0.5 + qwertyu0649 | 0.5 + qwertyu0650 | 0.5 + qwertyu0651 | 0.5 + qwertyu0652 | 0.5 + qwertyu0653 | 0.5 + qwertyu0654 | 0.5 + qwertyu0655 | 0.5 + qwertyu0656 | 0.5 + qwertyu0657 | 0.5 + qwertyu0658 | 0.5 + qwertyu0659 | 0.5 + qwertyu0660 | 0.5 + qwertyu0661 | 0.5 + qwertyu0662 | 0.5 + qwertyu0663 | 0.5 + qwertyu0664 | 0.5 + qwertyu0665 | 0.5 + qwertyu0666 | 0.5 + qwertyu0667 | 0.5 + qwertyu0668 | 0.5 + qwertyu0669 | 0.5 + qwertyu0670 | 0.5 + qwertyu0671 | 0.5 + qwertyu0672 | 0.5 + qwertyu0673 | 0.5 + qwertyu0674 | 0.5 + qwertyu0675 | 0.5 + qwertyu0676 | 0.5 + qwertyu0677 | 0.5 + qwertyu0678 | 0.5 + qwertyu0679 | 0.5 + qwertyu0680 | 0.5 + qwertyu0681 | 0.5 + qwertyu0682 | 0.5 + qwertyu0683 | 0.5 + qwertyu0684 | 0.5 + qwertyu0685 | 0.5 + qwertyu0686 | 0.5 + qwertyu0687 | 0.5 + qwertyu0689 | 0.5 + qwertyu0690 | 0.5 + qwertyu0691 | 0.5 + qwertyu0692 | 0.5 + qwertyu0693 | 0.5 + qwertyu0694 | 0.5 + qwertyu0695 | 0.5 + qwertyu0696 | 0.5 + qwertyu0697 | 0.5 + qwertyu0698 | 0.5 + qwertyu0699 | 0.5 + qwertyu0700 | 0.5 + qwertyu0701 | 0.5 + qwertyu0702 | 0.5 + qwertyu0703 | 0.5 + qwertyu0704 | 0.5 + qwertyu0705 | 0.5 + qwertyu0706 | 0.5 + qwertyu0707 | 0.5 + qwertyu0708 | 0.5 + qwertyu0709 | 0.5 + qwertyu0710 | 0.5 + qwertyu0711 | 0.5 + qwertyu0712 | 0.5 + qwertyu0713 | 0.5 + qwertyu0714 | 0.5 + qwertyu0715 | 0.5 + qwertyu0716 | 0.5 + qwertyu0717 | 0.5 + qwertyu0718 | 0.5 + qwertyu0719 | 0.5 + qwertyu0720 | 0.5 + qwertyu0721 | 0.5 + qwertyu0722 | 0.5 + qwertyu0723 | 0.5 + qwertyu0724 | 0.5 + qwertyu0725 | 0.5 + qwertyu0726 | 0.5 + qwertyu0727 | 0.5 + qwertyu0728 | 0.5 + qwertyu0729 | 0.5 + qwertyu0730 | 0.5 + qwertyu0731 | 0.5 + qwertyu0732 | 0.5 + qwertyu0733 | 0.5 + qwertyu0734 | 0.5 + qwertyu0735 | 0.5 + qwertyu0736 | 0.5 + qwertyu0737 | 0.5 + qwertyu0738 | 0.5 + qwertyu0739 | 0.5 + qwertyu0740 | 0.5 + qwertyu0741 | 0.5 + qwertyu0742 | 0.5 + qwertyu0743 | 0.5 + qwertyu0744 | 0.5 + qwertyu0745 | 0.5 + qwertyu0746 | 0.5 + qwertyu0747 | 0.5 + qwertyu0748 | 0.5 + qwertyu0749 | 0.5 + qwertyu0750 | 0.5 + qwertyu0751 | 0.5 + qwertyu0752 | 0.5 + qwertyu0753 | 0.5 + qwertyu0754 | 0.5 + qwertyu0755 | 0.5 + qwertyu0756 | 0.5 + qwertyu0757 | 0.5 + qwertyu0758 | 0.5 + qwertyu0759 | 0.5 + qwertyu0760 | 0.5 + qwertyu0761 | 0.5 + qwertyu0762 | 0.5 + qwertyu0763 | 0.5 + qwertyu0764 | 0.5 + qwertyu0765 | 0.5 + qwertyu0766 | 0.5 + qwertyu0767 | 0.5 + qwertyu0768 | 0.5 + qwertyu0769 | 0.5 + qwertyu0770 | 0.5 + qwertyu0771 | 0.5 + qwertyu0772 | 0.5 + qwertyu0773 | 0.5 + qwertyu0774 | 0.5 + qwertyu0775 | 0.5 + qwertyu0776 | 0.5 + qwertyu0777 | 0.5 + qwertyu0778 | 0.5 + qwertyu0779 | 0.5 + qwertyu0780 | 0.5 + qwertyu0781 | 0.5 + qwertyu0782 | 0.5 + qwertyu0783 | 0.5 + qwertyu0784 | 0.5 + qwertyu0785 | 0.5 + qwertyu0786 | 0.5 + qwertyu0787 | 0.5 + qwertyu0789 | 0.5 + qwertyu0790 | 0.5 + qwertyu0791 | 0.5 + qwertyu0792 | 0.5 + qwertyu0793 | 0.5 + qwertyu0794 | 0.5 + qwertyu0795 | 0.5 + qwertyu0796 | 0.5 + qwertyu0797 | 0.5 + qwertyu0798 | 0.5 + qwertyu0799 | 0.5 + qwertyu0800 | 0.5 + qwertyu0801 | 0.5 + qwertyu0802 | 0.5 + qwertyu0803 | 0.5 + qwertyu0804 | 0.5 + qwertyu0805 | 0.5 + qwertyu0806 | 0.5 + qwertyu0807 | 0.5 + qwertyu0808 | 0.5 + qwertyu0809 | 0.5 + qwertyu0810 | 0.5 + qwertyu0811 | 0.5 + qwertyu0812 | 0.5 + qwertyu0813 | 0.5 + qwertyu0814 | 0.5 + qwertyu0815 | 0.5 + qwertyu0816 | 0.5 + qwertyu0817 | 0.5 + qwertyu0818 | 0.5 + qwertyu0819 | 0.5 + qwertyu0820 | 0.5 + qwertyu0821 | 0.5 + qwertyu0822 | 0.5 + qwertyu0823 | 0.5 + qwertyu0824 | 0.5 + qwertyu0825 | 0.5 + qwertyu0826 | 0.5 + qwertyu0827 | 0.5 + qwertyu0828 | 0.5 + qwertyu0829 | 0.5 + qwertyu0830 | 0.5 + qwertyu0831 | 0.5 + qwertyu0832 | 0.5 + qwertyu0833 | 0.5 + qwertyu0834 | 0.5 + qwertyu0835 | 0.5 + qwertyu0836 | 0.5 + qwertyu0837 | 0.5 + qwertyu0838 | 0.5 + qwertyu0839 | 0.5 + qwertyu0840 | 0.5 + qwertyu0841 | 0.5 + qwertyu0842 | 0.5 + qwertyu0843 | 0.5 + qwertyu0844 | 0.5 + qwertyu0845 | 0.5 + qwertyu0846 | 0.5 + qwertyu0847 | 0.5 + qwertyu0848 | 0.5 + qwertyu0849 | 0.5 + qwertyu0850 | 0.5 + qwertyu0851 | 0.5 + qwertyu0852 | 0.5 + qwertyu0853 | 0.5 + qwertyu0854 | 0.5 + qwertyu0855 | 0.5 + qwertyu0856 | 0.5 + qwertyu0857 | 0.5 + qwertyu0858 | 0.5 + qwertyu0859 | 0.5 + qwertyu0860 | 0.5 + qwertyu0861 | 0.5 + qwertyu0862 | 0.5 + qwertyu0863 | 0.5 + qwertyu0864 | 0.5 + qwertyu0865 | 0.5 + qwertyu0866 | 0.5 + qwertyu0867 | 0.5 + qwertyu0868 | 0.5 + qwertyu0869 | 0.5 + qwertyu0870 | 0.5 + qwertyu0871 | 0.5 + qwertyu0872 | 0.5 + qwertyu0873 | 0.5 + qwertyu0874 | 0.5 + qwertyu0875 | 0.5 + qwertyu0876 | 0.5 + qwertyu0877 | 0.5 + qwertyu0878 | 0.5 + qwertyu0879 | 0.5 + qwertyu0880 | 0.5 + qwertyu0881 | 0.5 + qwertyu0882 | 0.5 + qwertyu0883 | 0.5 + qwertyu0884 | 0.5 + qwertyu0885 | 0.5 + qwertyu0886 | 0.5 + qwertyu0887 | 0.5 + qwertyu0889 | 0.5 + qwertyu0890 | 0.5 + qwertyu0891 | 0.5 + qwertyu0892 | 0.5 + qwertyu0893 | 0.5 + qwertyu0894 | 0.5 + qwertyu0895 | 0.5 + qwertyu0896 | 0.5 + qwertyu0897 | 0.5 + qwertyu0898 | 0.5 + qwertyu0899 | 0.5 + qwertyu1000 | 0.411765 +(1000 rows) + +select t,similarity(t,'gwertyu0988') as sml from test_trgm where t % 'gwertyu0988' order by sml desc, t; + t | sml +-------------+---------- + qwertyu0988 | 0.6 + qwertyu0980 | 0.411765 + qwertyu0981 | 0.411765 + qwertyu0982 | 0.411765 + qwertyu0983 | 0.411765 + qwertyu0984 | 0.411765 + qwertyu0985 | 0.411765 + qwertyu0986 | 0.411765 + qwertyu0987 | 0.411765 + qwertyu0989 | 0.411765 + qwertyu0088 | 0.333333 + qwertyu0098 | 0.333333 + qwertyu0188 | 0.333333 + qwertyu0288 | 0.333333 + qwertyu0388 | 0.333333 + qwertyu0488 | 0.333333 + qwertyu0588 | 0.333333 + qwertyu0688 | 0.333333 + qwertyu0788 | 0.333333 + qwertyu0888 | 0.333333 + qwertyu0900 | 0.333333 + qwertyu0901 | 0.333333 + qwertyu0902 | 0.333333 + qwertyu0903 | 0.333333 + qwertyu0904 | 0.333333 + qwertyu0905 | 0.333333 + qwertyu0906 | 0.333333 + qwertyu0907 | 0.333333 + qwertyu0908 | 0.333333 + qwertyu0909 | 0.333333 + qwertyu0910 | 0.333333 + qwertyu0911 | 0.333333 + qwertyu0912 | 0.333333 + qwertyu0913 | 0.333333 + qwertyu0914 | 0.333333 + qwertyu0915 | 0.333333 + qwertyu0916 | 0.333333 + qwertyu0917 | 0.333333 + qwertyu0918 | 0.333333 + qwertyu0919 | 0.333333 + qwertyu0920 | 0.333333 + qwertyu0921 | 0.333333 + qwertyu0922 | 0.333333 + qwertyu0923 | 0.333333 + qwertyu0924 | 0.333333 + qwertyu0925 | 0.333333 + qwertyu0926 | 0.333333 + qwertyu0927 | 0.333333 + qwertyu0928 | 0.333333 + qwertyu0929 | 0.333333 + qwertyu0930 | 0.333333 + qwertyu0931 | 0.333333 + qwertyu0932 | 0.333333 + qwertyu0933 | 0.333333 + qwertyu0934 | 0.333333 + qwertyu0935 | 0.333333 + qwertyu0936 | 0.333333 + qwertyu0937 | 0.333333 + qwertyu0938 | 0.333333 + qwertyu0939 | 0.333333 + qwertyu0940 | 0.333333 + qwertyu0941 | 0.333333 + qwertyu0942 | 0.333333 + qwertyu0943 | 0.333333 + qwertyu0944 | 0.333333 + qwertyu0945 | 0.333333 + qwertyu0946 | 0.333333 + qwertyu0947 | 0.333333 + qwertyu0948 | 0.333333 + qwertyu0949 | 0.333333 + qwertyu0950 | 0.333333 + qwertyu0951 | 0.333333 + qwertyu0952 | 0.333333 + qwertyu0953 | 0.333333 + qwertyu0954 | 0.333333 + qwertyu0955 | 0.333333 + qwertyu0956 | 0.333333 + qwertyu0957 | 0.333333 + qwertyu0958 | 0.333333 + qwertyu0959 | 0.333333 + qwertyu0960 | 0.333333 + qwertyu0961 | 0.333333 + qwertyu0962 | 0.333333 + qwertyu0963 | 0.333333 + qwertyu0964 | 0.333333 + qwertyu0965 | 0.333333 + qwertyu0966 | 0.333333 + qwertyu0967 | 0.333333 + qwertyu0968 | 0.333333 + qwertyu0969 | 0.333333 + qwertyu0970 | 0.333333 + qwertyu0971 | 0.333333 + qwertyu0972 | 0.333333 + qwertyu0973 | 0.333333 + qwertyu0974 | 0.333333 + qwertyu0975 | 0.333333 + qwertyu0976 | 0.333333 + qwertyu0977 | 0.333333 + qwertyu0978 | 0.333333 + qwertyu0979 | 0.333333 + qwertyu0990 | 0.333333 + qwertyu0991 | 0.333333 + qwertyu0992 | 0.333333 + qwertyu0993 | 0.333333 + qwertyu0994 | 0.333333 + qwertyu0995 | 0.333333 + qwertyu0996 | 0.333333 + qwertyu0997 | 0.333333 + qwertyu0998 | 0.333333 + qwertyu0999 | 0.333333 +(110 rows) + +select t,similarity(t,'gwertyu1988') as sml from test_trgm where t % 'gwertyu1988' order by sml desc, t; + t | sml +-------------+---------- + qwertyu0988 | 0.333333 +(1 row) + +explain (costs off) +select t <-> 'q0987wertyu0988', t from test_trgm order by t <-> 'q0987wertyu0988' limit 2; + QUERY PLAN +--------------------------------------------------- + Limit + -> Index Scan using trgm_idx on test_trgm + Order By: (t <-> 'q0987wertyu0988'::text) +(3 rows) + +select t <-> 'q0987wertyu0988', t from test_trgm order by t <-> 'q0987wertyu0988' limit 2; + ?column? | t +----------+------------- + 0.411765 | qwertyu0988 + 0.5 | qwertyu0987 +(2 rows) + +select count(*) from test_trgm where t ~ '[qwerty]{2}-?[qwerty]{2}'; + count +------- + 1000 +(1 row) + +drop index trgm_idx; +create index trgm_idx on test_trgm using gin (t gin_trgm_ops); +set enable_seqscan=off; +select t,similarity(t,'qwertyu0988') as sml from test_trgm where t % 'qwertyu0988' order by sml desc, t; + t | sml +-------------+---------- + qwertyu0988 | 1 + qwertyu0980 | 0.714286 + qwertyu0981 | 0.714286 + qwertyu0982 | 0.714286 + qwertyu0983 | 0.714286 + qwertyu0984 | 0.714286 + qwertyu0985 | 0.714286 + qwertyu0986 | 0.714286 + qwertyu0987 | 0.714286 + qwertyu0989 | 0.714286 + qwertyu0088 | 0.6 + qwertyu0098 | 0.6 + qwertyu0188 | 0.6 + qwertyu0288 | 0.6 + qwertyu0388 | 0.6 + qwertyu0488 | 0.6 + qwertyu0588 | 0.6 + qwertyu0688 | 0.6 + qwertyu0788 | 0.6 + qwertyu0888 | 0.6 + qwertyu0900 | 0.6 + qwertyu0901 | 0.6 + qwertyu0902 | 0.6 + qwertyu0903 | 0.6 + qwertyu0904 | 0.6 + qwertyu0905 | 0.6 + qwertyu0906 | 0.6 + qwertyu0907 | 0.6 + qwertyu0908 | 0.6 + qwertyu0909 | 0.6 + qwertyu0910 | 0.6 + qwertyu0911 | 0.6 + qwertyu0912 | 0.6 + qwertyu0913 | 0.6 + qwertyu0914 | 0.6 + qwertyu0915 | 0.6 + qwertyu0916 | 0.6 + qwertyu0917 | 0.6 + qwertyu0918 | 0.6 + qwertyu0919 | 0.6 + qwertyu0920 | 0.6 + qwertyu0921 | 0.6 + qwertyu0922 | 0.6 + qwertyu0923 | 0.6 + qwertyu0924 | 0.6 + qwertyu0925 | 0.6 + qwertyu0926 | 0.6 + qwertyu0927 | 0.6 + qwertyu0928 | 0.6 + qwertyu0929 | 0.6 + qwertyu0930 | 0.6 + qwertyu0931 | 0.6 + qwertyu0932 | 0.6 + qwertyu0933 | 0.6 + qwertyu0934 | 0.6 + qwertyu0935 | 0.6 + qwertyu0936 | 0.6 + qwertyu0937 | 0.6 + qwertyu0938 | 0.6 + qwertyu0939 | 0.6 + qwertyu0940 | 0.6 + qwertyu0941 | 0.6 + qwertyu0942 | 0.6 + qwertyu0943 | 0.6 + qwertyu0944 | 0.6 + qwertyu0945 | 0.6 + qwertyu0946 | 0.6 + qwertyu0947 | 0.6 + qwertyu0948 | 0.6 + qwertyu0949 | 0.6 + qwertyu0950 | 0.6 + qwertyu0951 | 0.6 + qwertyu0952 | 0.6 + qwertyu0953 | 0.6 + qwertyu0954 | 0.6 + qwertyu0955 | 0.6 + qwertyu0956 | 0.6 + qwertyu0957 | 0.6 + qwertyu0958 | 0.6 + qwertyu0959 | 0.6 + qwertyu0960 | 0.6 + qwertyu0961 | 0.6 + qwertyu0962 | 0.6 + qwertyu0963 | 0.6 + qwertyu0964 | 0.6 + qwertyu0965 | 0.6 + qwertyu0966 | 0.6 + qwertyu0967 | 0.6 + qwertyu0968 | 0.6 + qwertyu0969 | 0.6 + qwertyu0970 | 0.6 + qwertyu0971 | 0.6 + qwertyu0972 | 0.6 + qwertyu0973 | 0.6 + qwertyu0974 | 0.6 + qwertyu0975 | 0.6 + qwertyu0976 | 0.6 + qwertyu0977 | 0.6 + qwertyu0978 | 0.6 + qwertyu0979 | 0.6 + qwertyu0990 | 0.6 + qwertyu0991 | 0.6 + qwertyu0992 | 0.6 + qwertyu0993 | 0.6 + qwertyu0994 | 0.6 + qwertyu0995 | 0.6 + qwertyu0996 | 0.6 + qwertyu0997 | 0.6 + qwertyu0998 | 0.6 + qwertyu0999 | 0.6 + qwertyu0001 | 0.5 + qwertyu0002 | 0.5 + qwertyu0003 | 0.5 + qwertyu0004 | 0.5 + qwertyu0005 | 0.5 + qwertyu0006 | 0.5 + qwertyu0007 | 0.5 + qwertyu0008 | 0.5 + qwertyu0009 | 0.5 + qwertyu0010 | 0.5 + qwertyu0011 | 0.5 + qwertyu0012 | 0.5 + qwertyu0013 | 0.5 + qwertyu0014 | 0.5 + qwertyu0015 | 0.5 + qwertyu0016 | 0.5 + qwertyu0017 | 0.5 + qwertyu0018 | 0.5 + qwertyu0019 | 0.5 + qwertyu0020 | 0.5 + qwertyu0021 | 0.5 + qwertyu0022 | 0.5 + qwertyu0023 | 0.5 + qwertyu0024 | 0.5 + qwertyu0025 | 0.5 + qwertyu0026 | 0.5 + qwertyu0027 | 0.5 + qwertyu0028 | 0.5 + qwertyu0029 | 0.5 + qwertyu0030 | 0.5 + qwertyu0031 | 0.5 + qwertyu0032 | 0.5 + qwertyu0033 | 0.5 + qwertyu0034 | 0.5 + qwertyu0035 | 0.5 + qwertyu0036 | 0.5 + qwertyu0037 | 0.5 + qwertyu0038 | 0.5 + qwertyu0039 | 0.5 + qwertyu0040 | 0.5 + qwertyu0041 | 0.5 + qwertyu0042 | 0.5 + qwertyu0043 | 0.5 + qwertyu0044 | 0.5 + qwertyu0045 | 0.5 + qwertyu0046 | 0.5 + qwertyu0047 | 0.5 + qwertyu0048 | 0.5 + qwertyu0049 | 0.5 + qwertyu0050 | 0.5 + qwertyu0051 | 0.5 + qwertyu0052 | 0.5 + qwertyu0053 | 0.5 + qwertyu0054 | 0.5 + qwertyu0055 | 0.5 + qwertyu0056 | 0.5 + qwertyu0057 | 0.5 + qwertyu0058 | 0.5 + qwertyu0059 | 0.5 + qwertyu0060 | 0.5 + qwertyu0061 | 0.5 + qwertyu0062 | 0.5 + qwertyu0063 | 0.5 + qwertyu0064 | 0.5 + qwertyu0065 | 0.5 + qwertyu0066 | 0.5 + qwertyu0067 | 0.5 + qwertyu0068 | 0.5 + qwertyu0069 | 0.5 + qwertyu0070 | 0.5 + qwertyu0071 | 0.5 + qwertyu0072 | 0.5 + qwertyu0073 | 0.5 + qwertyu0074 | 0.5 + qwertyu0075 | 0.5 + qwertyu0076 | 0.5 + qwertyu0077 | 0.5 + qwertyu0078 | 0.5 + qwertyu0079 | 0.5 + qwertyu0080 | 0.5 + qwertyu0081 | 0.5 + qwertyu0082 | 0.5 + qwertyu0083 | 0.5 + qwertyu0084 | 0.5 + qwertyu0085 | 0.5 + qwertyu0086 | 0.5 + qwertyu0087 | 0.5 + qwertyu0089 | 0.5 + qwertyu0090 | 0.5 + qwertyu0091 | 0.5 + qwertyu0092 | 0.5 + qwertyu0093 | 0.5 + qwertyu0094 | 0.5 + qwertyu0095 | 0.5 + qwertyu0096 | 0.5 + qwertyu0097 | 0.5 + qwertyu0099 | 0.5 + qwertyu0100 | 0.5 + qwertyu0101 | 0.5 + qwertyu0102 | 0.5 + qwertyu0103 | 0.5 + qwertyu0104 | 0.5 + qwertyu0105 | 0.5 + qwertyu0106 | 0.5 + qwertyu0107 | 0.5 + qwertyu0108 | 0.5 + qwertyu0109 | 0.5 + qwertyu0110 | 0.5 + qwertyu0111 | 0.5 + qwertyu0112 | 0.5 + qwertyu0113 | 0.5 + qwertyu0114 | 0.5 + qwertyu0115 | 0.5 + qwertyu0116 | 0.5 + qwertyu0117 | 0.5 + qwertyu0118 | 0.5 + qwertyu0119 | 0.5 + qwertyu0120 | 0.5 + qwertyu0121 | 0.5 + qwertyu0122 | 0.5 + qwertyu0123 | 0.5 + qwertyu0124 | 0.5 + qwertyu0125 | 0.5 + qwertyu0126 | 0.5 + qwertyu0127 | 0.5 + qwertyu0128 | 0.5 + qwertyu0129 | 0.5 + qwertyu0130 | 0.5 + qwertyu0131 | 0.5 + qwertyu0132 | 0.5 + qwertyu0133 | 0.5 + qwertyu0134 | 0.5 + qwertyu0135 | 0.5 + qwertyu0136 | 0.5 + qwertyu0137 | 0.5 + qwertyu0138 | 0.5 + qwertyu0139 | 0.5 + qwertyu0140 | 0.5 + qwertyu0141 | 0.5 + qwertyu0142 | 0.5 + qwertyu0143 | 0.5 + qwertyu0144 | 0.5 + qwertyu0145 | 0.5 + qwertyu0146 | 0.5 + qwertyu0147 | 0.5 + qwertyu0148 | 0.5 + qwertyu0149 | 0.5 + qwertyu0150 | 0.5 + qwertyu0151 | 0.5 + qwertyu0152 | 0.5 + qwertyu0153 | 0.5 + qwertyu0154 | 0.5 + qwertyu0155 | 0.5 + qwertyu0156 | 0.5 + qwertyu0157 | 0.5 + qwertyu0158 | 0.5 + qwertyu0159 | 0.5 + qwertyu0160 | 0.5 + qwertyu0161 | 0.5 + qwertyu0162 | 0.5 + qwertyu0163 | 0.5 + qwertyu0164 | 0.5 + qwertyu0165 | 0.5 + qwertyu0166 | 0.5 + qwertyu0167 | 0.5 + qwertyu0168 | 0.5 + qwertyu0169 | 0.5 + qwertyu0170 | 0.5 + qwertyu0171 | 0.5 + qwertyu0172 | 0.5 + qwertyu0173 | 0.5 + qwertyu0174 | 0.5 + qwertyu0175 | 0.5 + qwertyu0176 | 0.5 + qwertyu0177 | 0.5 + qwertyu0178 | 0.5 + qwertyu0179 | 0.5 + qwertyu0180 | 0.5 + qwertyu0181 | 0.5 + qwertyu0182 | 0.5 + qwertyu0183 | 0.5 + qwertyu0184 | 0.5 + qwertyu0185 | 0.5 + qwertyu0186 | 0.5 + qwertyu0187 | 0.5 + qwertyu0189 | 0.5 + qwertyu0190 | 0.5 + qwertyu0191 | 0.5 + qwertyu0192 | 0.5 + qwertyu0193 | 0.5 + qwertyu0194 | 0.5 + qwertyu0195 | 0.5 + qwertyu0196 | 0.5 + qwertyu0197 | 0.5 + qwertyu0198 | 0.5 + qwertyu0199 | 0.5 + qwertyu0200 | 0.5 + qwertyu0201 | 0.5 + qwertyu0202 | 0.5 + qwertyu0203 | 0.5 + qwertyu0204 | 0.5 + qwertyu0205 | 0.5 + qwertyu0206 | 0.5 + qwertyu0207 | 0.5 + qwertyu0208 | 0.5 + qwertyu0209 | 0.5 + qwertyu0210 | 0.5 + qwertyu0211 | 0.5 + qwertyu0212 | 0.5 + qwertyu0213 | 0.5 + qwertyu0214 | 0.5 + qwertyu0215 | 0.5 + qwertyu0216 | 0.5 + qwertyu0217 | 0.5 + qwertyu0218 | 0.5 + qwertyu0219 | 0.5 + qwertyu0220 | 0.5 + qwertyu0221 | 0.5 + qwertyu0222 | 0.5 + qwertyu0223 | 0.5 + qwertyu0224 | 0.5 + qwertyu0225 | 0.5 + qwertyu0226 | 0.5 + qwertyu0227 | 0.5 + qwertyu0228 | 0.5 + qwertyu0229 | 0.5 + qwertyu0230 | 0.5 + qwertyu0231 | 0.5 + qwertyu0232 | 0.5 + qwertyu0233 | 0.5 + qwertyu0234 | 0.5 + qwertyu0235 | 0.5 + qwertyu0236 | 0.5 + qwertyu0237 | 0.5 + qwertyu0238 | 0.5 + qwertyu0239 | 0.5 + qwertyu0240 | 0.5 + qwertyu0241 | 0.5 + qwertyu0242 | 0.5 + qwertyu0243 | 0.5 + qwertyu0244 | 0.5 + qwertyu0245 | 0.5 + qwertyu0246 | 0.5 + qwertyu0247 | 0.5 + qwertyu0248 | 0.5 + qwertyu0249 | 0.5 + qwertyu0250 | 0.5 + qwertyu0251 | 0.5 + qwertyu0252 | 0.5 + qwertyu0253 | 0.5 + qwertyu0254 | 0.5 + qwertyu0255 | 0.5 + qwertyu0256 | 0.5 + qwertyu0257 | 0.5 + qwertyu0258 | 0.5 + qwertyu0259 | 0.5 + qwertyu0260 | 0.5 + qwertyu0261 | 0.5 + qwertyu0262 | 0.5 + qwertyu0263 | 0.5 + qwertyu0264 | 0.5 + qwertyu0265 | 0.5 + qwertyu0266 | 0.5 + qwertyu0267 | 0.5 + qwertyu0268 | 0.5 + qwertyu0269 | 0.5 + qwertyu0270 | 0.5 + qwertyu0271 | 0.5 + qwertyu0272 | 0.5 + qwertyu0273 | 0.5 + qwertyu0274 | 0.5 + qwertyu0275 | 0.5 + qwertyu0276 | 0.5 + qwertyu0277 | 0.5 + qwertyu0278 | 0.5 + qwertyu0279 | 0.5 + qwertyu0280 | 0.5 + qwertyu0281 | 0.5 + qwertyu0282 | 0.5 + qwertyu0283 | 0.5 + qwertyu0284 | 0.5 + qwertyu0285 | 0.5 + qwertyu0286 | 0.5 + qwertyu0287 | 0.5 + qwertyu0289 | 0.5 + qwertyu0290 | 0.5 + qwertyu0291 | 0.5 + qwertyu0292 | 0.5 + qwertyu0293 | 0.5 + qwertyu0294 | 0.5 + qwertyu0295 | 0.5 + qwertyu0296 | 0.5 + qwertyu0297 | 0.5 + qwertyu0298 | 0.5 + qwertyu0299 | 0.5 + qwertyu0300 | 0.5 + qwertyu0301 | 0.5 + qwertyu0302 | 0.5 + qwertyu0303 | 0.5 + qwertyu0304 | 0.5 + qwertyu0305 | 0.5 + qwertyu0306 | 0.5 + qwertyu0307 | 0.5 + qwertyu0308 | 0.5 + qwertyu0309 | 0.5 + qwertyu0310 | 0.5 + qwertyu0311 | 0.5 + qwertyu0312 | 0.5 + qwertyu0313 | 0.5 + qwertyu0314 | 0.5 + qwertyu0315 | 0.5 + qwertyu0316 | 0.5 + qwertyu0317 | 0.5 + qwertyu0318 | 0.5 + qwertyu0319 | 0.5 + qwertyu0320 | 0.5 + qwertyu0321 | 0.5 + qwertyu0322 | 0.5 + qwertyu0323 | 0.5 + qwertyu0324 | 0.5 + qwertyu0325 | 0.5 + qwertyu0326 | 0.5 + qwertyu0327 | 0.5 + qwertyu0328 | 0.5 + qwertyu0329 | 0.5 + qwertyu0330 | 0.5 + qwertyu0331 | 0.5 + qwertyu0332 | 0.5 + qwertyu0333 | 0.5 + qwertyu0334 | 0.5 + qwertyu0335 | 0.5 + qwertyu0336 | 0.5 + qwertyu0337 | 0.5 + qwertyu0338 | 0.5 + qwertyu0339 | 0.5 + qwertyu0340 | 0.5 + qwertyu0341 | 0.5 + qwertyu0342 | 0.5 + qwertyu0343 | 0.5 + qwertyu0344 | 0.5 + qwertyu0345 | 0.5 + qwertyu0346 | 0.5 + qwertyu0347 | 0.5 + qwertyu0348 | 0.5 + qwertyu0349 | 0.5 + qwertyu0350 | 0.5 + qwertyu0351 | 0.5 + qwertyu0352 | 0.5 + qwertyu0353 | 0.5 + qwertyu0354 | 0.5 + qwertyu0355 | 0.5 + qwertyu0356 | 0.5 + qwertyu0357 | 0.5 + qwertyu0358 | 0.5 + qwertyu0359 | 0.5 + qwertyu0360 | 0.5 + qwertyu0361 | 0.5 + qwertyu0362 | 0.5 + qwertyu0363 | 0.5 + qwertyu0364 | 0.5 + qwertyu0365 | 0.5 + qwertyu0366 | 0.5 + qwertyu0367 | 0.5 + qwertyu0368 | 0.5 + qwertyu0369 | 0.5 + qwertyu0370 | 0.5 + qwertyu0371 | 0.5 + qwertyu0372 | 0.5 + qwertyu0373 | 0.5 + qwertyu0374 | 0.5 + qwertyu0375 | 0.5 + qwertyu0376 | 0.5 + qwertyu0377 | 0.5 + qwertyu0378 | 0.5 + qwertyu0379 | 0.5 + qwertyu0380 | 0.5 + qwertyu0381 | 0.5 + qwertyu0382 | 0.5 + qwertyu0383 | 0.5 + qwertyu0384 | 0.5 + qwertyu0385 | 0.5 + qwertyu0386 | 0.5 + qwertyu0387 | 0.5 + qwertyu0389 | 0.5 + qwertyu0390 | 0.5 + qwertyu0391 | 0.5 + qwertyu0392 | 0.5 + qwertyu0393 | 0.5 + qwertyu0394 | 0.5 + qwertyu0395 | 0.5 + qwertyu0396 | 0.5 + qwertyu0397 | 0.5 + qwertyu0398 | 0.5 + qwertyu0399 | 0.5 + qwertyu0400 | 0.5 + qwertyu0401 | 0.5 + qwertyu0402 | 0.5 + qwertyu0403 | 0.5 + qwertyu0404 | 0.5 + qwertyu0405 | 0.5 + qwertyu0406 | 0.5 + qwertyu0407 | 0.5 + qwertyu0408 | 0.5 + qwertyu0409 | 0.5 + qwertyu0410 | 0.5 + qwertyu0411 | 0.5 + qwertyu0412 | 0.5 + qwertyu0413 | 0.5 + qwertyu0414 | 0.5 + qwertyu0415 | 0.5 + qwertyu0416 | 0.5 + qwertyu0417 | 0.5 + qwertyu0418 | 0.5 + qwertyu0419 | 0.5 + qwertyu0420 | 0.5 + qwertyu0421 | 0.5 + qwertyu0422 | 0.5 + qwertyu0423 | 0.5 + qwertyu0424 | 0.5 + qwertyu0425 | 0.5 + qwertyu0426 | 0.5 + qwertyu0427 | 0.5 + qwertyu0428 | 0.5 + qwertyu0429 | 0.5 + qwertyu0430 | 0.5 + qwertyu0431 | 0.5 + qwertyu0432 | 0.5 + qwertyu0433 | 0.5 + qwertyu0434 | 0.5 + qwertyu0435 | 0.5 + qwertyu0436 | 0.5 + qwertyu0437 | 0.5 + qwertyu0438 | 0.5 + qwertyu0439 | 0.5 + qwertyu0440 | 0.5 + qwertyu0441 | 0.5 + qwertyu0442 | 0.5 + qwertyu0443 | 0.5 + qwertyu0444 | 0.5 + qwertyu0445 | 0.5 + qwertyu0446 | 0.5 + qwertyu0447 | 0.5 + qwertyu0448 | 0.5 + qwertyu0449 | 0.5 + qwertyu0450 | 0.5 + qwertyu0451 | 0.5 + qwertyu0452 | 0.5 + qwertyu0453 | 0.5 + qwertyu0454 | 0.5 + qwertyu0455 | 0.5 + qwertyu0456 | 0.5 + qwertyu0457 | 0.5 + qwertyu0458 | 0.5 + qwertyu0459 | 0.5 + qwertyu0460 | 0.5 + qwertyu0461 | 0.5 + qwertyu0462 | 0.5 + qwertyu0463 | 0.5 + qwertyu0464 | 0.5 + qwertyu0465 | 0.5 + qwertyu0466 | 0.5 + qwertyu0467 | 0.5 + qwertyu0468 | 0.5 + qwertyu0469 | 0.5 + qwertyu0470 | 0.5 + qwertyu0471 | 0.5 + qwertyu0472 | 0.5 + qwertyu0473 | 0.5 + qwertyu0474 | 0.5 + qwertyu0475 | 0.5 + qwertyu0476 | 0.5 + qwertyu0477 | 0.5 + qwertyu0478 | 0.5 + qwertyu0479 | 0.5 + qwertyu0480 | 0.5 + qwertyu0481 | 0.5 + qwertyu0482 | 0.5 + qwertyu0483 | 0.5 + qwertyu0484 | 0.5 + qwertyu0485 | 0.5 + qwertyu0486 | 0.5 + qwertyu0487 | 0.5 + qwertyu0489 | 0.5 + qwertyu0490 | 0.5 + qwertyu0491 | 0.5 + qwertyu0492 | 0.5 + qwertyu0493 | 0.5 + qwertyu0494 | 0.5 + qwertyu0495 | 0.5 + qwertyu0496 | 0.5 + qwertyu0497 | 0.5 + qwertyu0498 | 0.5 + qwertyu0499 | 0.5 + qwertyu0500 | 0.5 + qwertyu0501 | 0.5 + qwertyu0502 | 0.5 + qwertyu0503 | 0.5 + qwertyu0504 | 0.5 + qwertyu0505 | 0.5 + qwertyu0506 | 0.5 + qwertyu0507 | 0.5 + qwertyu0508 | 0.5 + qwertyu0509 | 0.5 + qwertyu0510 | 0.5 + qwertyu0511 | 0.5 + qwertyu0512 | 0.5 + qwertyu0513 | 0.5 + qwertyu0514 | 0.5 + qwertyu0515 | 0.5 + qwertyu0516 | 0.5 + qwertyu0517 | 0.5 + qwertyu0518 | 0.5 + qwertyu0519 | 0.5 + qwertyu0520 | 0.5 + qwertyu0521 | 0.5 + qwertyu0522 | 0.5 + qwertyu0523 | 0.5 + qwertyu0524 | 0.5 + qwertyu0525 | 0.5 + qwertyu0526 | 0.5 + qwertyu0527 | 0.5 + qwertyu0528 | 0.5 + qwertyu0529 | 0.5 + qwertyu0530 | 0.5 + qwertyu0531 | 0.5 + qwertyu0532 | 0.5 + qwertyu0533 | 0.5 + qwertyu0534 | 0.5 + qwertyu0535 | 0.5 + qwertyu0536 | 0.5 + qwertyu0537 | 0.5 + qwertyu0538 | 0.5 + qwertyu0539 | 0.5 + qwertyu0540 | 0.5 + qwertyu0541 | 0.5 + qwertyu0542 | 0.5 + qwertyu0543 | 0.5 + qwertyu0544 | 0.5 + qwertyu0545 | 0.5 + qwertyu0546 | 0.5 + qwertyu0547 | 0.5 + qwertyu0548 | 0.5 + qwertyu0549 | 0.5 + qwertyu0550 | 0.5 + qwertyu0551 | 0.5 + qwertyu0552 | 0.5 + qwertyu0553 | 0.5 + qwertyu0554 | 0.5 + qwertyu0555 | 0.5 + qwertyu0556 | 0.5 + qwertyu0557 | 0.5 + qwertyu0558 | 0.5 + qwertyu0559 | 0.5 + qwertyu0560 | 0.5 + qwertyu0561 | 0.5 + qwertyu0562 | 0.5 + qwertyu0563 | 0.5 + qwertyu0564 | 0.5 + qwertyu0565 | 0.5 + qwertyu0566 | 0.5 + qwertyu0567 | 0.5 + qwertyu0568 | 0.5 + qwertyu0569 | 0.5 + qwertyu0570 | 0.5 + qwertyu0571 | 0.5 + qwertyu0572 | 0.5 + qwertyu0573 | 0.5 + qwertyu0574 | 0.5 + qwertyu0575 | 0.5 + qwertyu0576 | 0.5 + qwertyu0577 | 0.5 + qwertyu0578 | 0.5 + qwertyu0579 | 0.5 + qwertyu0580 | 0.5 + qwertyu0581 | 0.5 + qwertyu0582 | 0.5 + qwertyu0583 | 0.5 + qwertyu0584 | 0.5 + qwertyu0585 | 0.5 + qwertyu0586 | 0.5 + qwertyu0587 | 0.5 + qwertyu0589 | 0.5 + qwertyu0590 | 0.5 + qwertyu0591 | 0.5 + qwertyu0592 | 0.5 + qwertyu0593 | 0.5 + qwertyu0594 | 0.5 + qwertyu0595 | 0.5 + qwertyu0596 | 0.5 + qwertyu0597 | 0.5 + qwertyu0598 | 0.5 + qwertyu0599 | 0.5 + qwertyu0600 | 0.5 + qwertyu0601 | 0.5 + qwertyu0602 | 0.5 + qwertyu0603 | 0.5 + qwertyu0604 | 0.5 + qwertyu0605 | 0.5 + qwertyu0606 | 0.5 + qwertyu0607 | 0.5 + qwertyu0608 | 0.5 + qwertyu0609 | 0.5 + qwertyu0610 | 0.5 + qwertyu0611 | 0.5 + qwertyu0612 | 0.5 + qwertyu0613 | 0.5 + qwertyu0614 | 0.5 + qwertyu0615 | 0.5 + qwertyu0616 | 0.5 + qwertyu0617 | 0.5 + qwertyu0618 | 0.5 + qwertyu0619 | 0.5 + qwertyu0620 | 0.5 + qwertyu0621 | 0.5 + qwertyu0622 | 0.5 + qwertyu0623 | 0.5 + qwertyu0624 | 0.5 + qwertyu0625 | 0.5 + qwertyu0626 | 0.5 + qwertyu0627 | 0.5 + qwertyu0628 | 0.5 + qwertyu0629 | 0.5 + qwertyu0630 | 0.5 + qwertyu0631 | 0.5 + qwertyu0632 | 0.5 + qwertyu0633 | 0.5 + qwertyu0634 | 0.5 + qwertyu0635 | 0.5 + qwertyu0636 | 0.5 + qwertyu0637 | 0.5 + qwertyu0638 | 0.5 + qwertyu0639 | 0.5 + qwertyu0640 | 0.5 + qwertyu0641 | 0.5 + qwertyu0642 | 0.5 + qwertyu0643 | 0.5 + qwertyu0644 | 0.5 + qwertyu0645 | 0.5 + qwertyu0646 | 0.5 + qwertyu0647 | 0.5 + qwertyu0648 | 0.5 + qwertyu0649 | 0.5 + qwertyu0650 | 0.5 + qwertyu0651 | 0.5 + qwertyu0652 | 0.5 + qwertyu0653 | 0.5 + qwertyu0654 | 0.5 + qwertyu0655 | 0.5 + qwertyu0656 | 0.5 + qwertyu0657 | 0.5 + qwertyu0658 | 0.5 + qwertyu0659 | 0.5 + qwertyu0660 | 0.5 + qwertyu0661 | 0.5 + qwertyu0662 | 0.5 + qwertyu0663 | 0.5 + qwertyu0664 | 0.5 + qwertyu0665 | 0.5 + qwertyu0666 | 0.5 + qwertyu0667 | 0.5 + qwertyu0668 | 0.5 + qwertyu0669 | 0.5 + qwertyu0670 | 0.5 + qwertyu0671 | 0.5 + qwertyu0672 | 0.5 + qwertyu0673 | 0.5 + qwertyu0674 | 0.5 + qwertyu0675 | 0.5 + qwertyu0676 | 0.5 + qwertyu0677 | 0.5 + qwertyu0678 | 0.5 + qwertyu0679 | 0.5 + qwertyu0680 | 0.5 + qwertyu0681 | 0.5 + qwertyu0682 | 0.5 + qwertyu0683 | 0.5 + qwertyu0684 | 0.5 + qwertyu0685 | 0.5 + qwertyu0686 | 0.5 + qwertyu0687 | 0.5 + qwertyu0689 | 0.5 + qwertyu0690 | 0.5 + qwertyu0691 | 0.5 + qwertyu0692 | 0.5 + qwertyu0693 | 0.5 + qwertyu0694 | 0.5 + qwertyu0695 | 0.5 + qwertyu0696 | 0.5 + qwertyu0697 | 0.5 + qwertyu0698 | 0.5 + qwertyu0699 | 0.5 + qwertyu0700 | 0.5 + qwertyu0701 | 0.5 + qwertyu0702 | 0.5 + qwertyu0703 | 0.5 + qwertyu0704 | 0.5 + qwertyu0705 | 0.5 + qwertyu0706 | 0.5 + qwertyu0707 | 0.5 + qwertyu0708 | 0.5 + qwertyu0709 | 0.5 + qwertyu0710 | 0.5 + qwertyu0711 | 0.5 + qwertyu0712 | 0.5 + qwertyu0713 | 0.5 + qwertyu0714 | 0.5 + qwertyu0715 | 0.5 + qwertyu0716 | 0.5 + qwertyu0717 | 0.5 + qwertyu0718 | 0.5 + qwertyu0719 | 0.5 + qwertyu0720 | 0.5 + qwertyu0721 | 0.5 + qwertyu0722 | 0.5 + qwertyu0723 | 0.5 + qwertyu0724 | 0.5 + qwertyu0725 | 0.5 + qwertyu0726 | 0.5 + qwertyu0727 | 0.5 + qwertyu0728 | 0.5 + qwertyu0729 | 0.5 + qwertyu0730 | 0.5 + qwertyu0731 | 0.5 + qwertyu0732 | 0.5 + qwertyu0733 | 0.5 + qwertyu0734 | 0.5 + qwertyu0735 | 0.5 + qwertyu0736 | 0.5 + qwertyu0737 | 0.5 + qwertyu0738 | 0.5 + qwertyu0739 | 0.5 + qwertyu0740 | 0.5 + qwertyu0741 | 0.5 + qwertyu0742 | 0.5 + qwertyu0743 | 0.5 + qwertyu0744 | 0.5 + qwertyu0745 | 0.5 + qwertyu0746 | 0.5 + qwertyu0747 | 0.5 + qwertyu0748 | 0.5 + qwertyu0749 | 0.5 + qwertyu0750 | 0.5 + qwertyu0751 | 0.5 + qwertyu0752 | 0.5 + qwertyu0753 | 0.5 + qwertyu0754 | 0.5 + qwertyu0755 | 0.5 + qwertyu0756 | 0.5 + qwertyu0757 | 0.5 + qwertyu0758 | 0.5 + qwertyu0759 | 0.5 + qwertyu0760 | 0.5 + qwertyu0761 | 0.5 + qwertyu0762 | 0.5 + qwertyu0763 | 0.5 + qwertyu0764 | 0.5 + qwertyu0765 | 0.5 + qwertyu0766 | 0.5 + qwertyu0767 | 0.5 + qwertyu0768 | 0.5 + qwertyu0769 | 0.5 + qwertyu0770 | 0.5 + qwertyu0771 | 0.5 + qwertyu0772 | 0.5 + qwertyu0773 | 0.5 + qwertyu0774 | 0.5 + qwertyu0775 | 0.5 + qwertyu0776 | 0.5 + qwertyu0777 | 0.5 + qwertyu0778 | 0.5 + qwertyu0779 | 0.5 + qwertyu0780 | 0.5 + qwertyu0781 | 0.5 + qwertyu0782 | 0.5 + qwertyu0783 | 0.5 + qwertyu0784 | 0.5 + qwertyu0785 | 0.5 + qwertyu0786 | 0.5 + qwertyu0787 | 0.5 + qwertyu0789 | 0.5 + qwertyu0790 | 0.5 + qwertyu0791 | 0.5 + qwertyu0792 | 0.5 + qwertyu0793 | 0.5 + qwertyu0794 | 0.5 + qwertyu0795 | 0.5 + qwertyu0796 | 0.5 + qwertyu0797 | 0.5 + qwertyu0798 | 0.5 + qwertyu0799 | 0.5 + qwertyu0800 | 0.5 + qwertyu0801 | 0.5 + qwertyu0802 | 0.5 + qwertyu0803 | 0.5 + qwertyu0804 | 0.5 + qwertyu0805 | 0.5 + qwertyu0806 | 0.5 + qwertyu0807 | 0.5 + qwertyu0808 | 0.5 + qwertyu0809 | 0.5 + qwertyu0810 | 0.5 + qwertyu0811 | 0.5 + qwertyu0812 | 0.5 + qwertyu0813 | 0.5 + qwertyu0814 | 0.5 + qwertyu0815 | 0.5 + qwertyu0816 | 0.5 + qwertyu0817 | 0.5 + qwertyu0818 | 0.5 + qwertyu0819 | 0.5 + qwertyu0820 | 0.5 + qwertyu0821 | 0.5 + qwertyu0822 | 0.5 + qwertyu0823 | 0.5 + qwertyu0824 | 0.5 + qwertyu0825 | 0.5 + qwertyu0826 | 0.5 + qwertyu0827 | 0.5 + qwertyu0828 | 0.5 + qwertyu0829 | 0.5 + qwertyu0830 | 0.5 + qwertyu0831 | 0.5 + qwertyu0832 | 0.5 + qwertyu0833 | 0.5 + qwertyu0834 | 0.5 + qwertyu0835 | 0.5 + qwertyu0836 | 0.5 + qwertyu0837 | 0.5 + qwertyu0838 | 0.5 + qwertyu0839 | 0.5 + qwertyu0840 | 0.5 + qwertyu0841 | 0.5 + qwertyu0842 | 0.5 + qwertyu0843 | 0.5 + qwertyu0844 | 0.5 + qwertyu0845 | 0.5 + qwertyu0846 | 0.5 + qwertyu0847 | 0.5 + qwertyu0848 | 0.5 + qwertyu0849 | 0.5 + qwertyu0850 | 0.5 + qwertyu0851 | 0.5 + qwertyu0852 | 0.5 + qwertyu0853 | 0.5 + qwertyu0854 | 0.5 + qwertyu0855 | 0.5 + qwertyu0856 | 0.5 + qwertyu0857 | 0.5 + qwertyu0858 | 0.5 + qwertyu0859 | 0.5 + qwertyu0860 | 0.5 + qwertyu0861 | 0.5 + qwertyu0862 | 0.5 + qwertyu0863 | 0.5 + qwertyu0864 | 0.5 + qwertyu0865 | 0.5 + qwertyu0866 | 0.5 + qwertyu0867 | 0.5 + qwertyu0868 | 0.5 + qwertyu0869 | 0.5 + qwertyu0870 | 0.5 + qwertyu0871 | 0.5 + qwertyu0872 | 0.5 + qwertyu0873 | 0.5 + qwertyu0874 | 0.5 + qwertyu0875 | 0.5 + qwertyu0876 | 0.5 + qwertyu0877 | 0.5 + qwertyu0878 | 0.5 + qwertyu0879 | 0.5 + qwertyu0880 | 0.5 + qwertyu0881 | 0.5 + qwertyu0882 | 0.5 + qwertyu0883 | 0.5 + qwertyu0884 | 0.5 + qwertyu0885 | 0.5 + qwertyu0886 | 0.5 + qwertyu0887 | 0.5 + qwertyu0889 | 0.5 + qwertyu0890 | 0.5 + qwertyu0891 | 0.5 + qwertyu0892 | 0.5 + qwertyu0893 | 0.5 + qwertyu0894 | 0.5 + qwertyu0895 | 0.5 + qwertyu0896 | 0.5 + qwertyu0897 | 0.5 + qwertyu0898 | 0.5 + qwertyu0899 | 0.5 + qwertyu1000 | 0.411765 +(1000 rows) + +select t,similarity(t,'gwertyu0988') as sml from test_trgm where t % 'gwertyu0988' order by sml desc, t; + t | sml +-------------+---------- + qwertyu0988 | 0.6 + qwertyu0980 | 0.411765 + qwertyu0981 | 0.411765 + qwertyu0982 | 0.411765 + qwertyu0983 | 0.411765 + qwertyu0984 | 0.411765 + qwertyu0985 | 0.411765 + qwertyu0986 | 0.411765 + qwertyu0987 | 0.411765 + qwertyu0989 | 0.411765 + qwertyu0088 | 0.333333 + qwertyu0098 | 0.333333 + qwertyu0188 | 0.333333 + qwertyu0288 | 0.333333 + qwertyu0388 | 0.333333 + qwertyu0488 | 0.333333 + qwertyu0588 | 0.333333 + qwertyu0688 | 0.333333 + qwertyu0788 | 0.333333 + qwertyu0888 | 0.333333 + qwertyu0900 | 0.333333 + qwertyu0901 | 0.333333 + qwertyu0902 | 0.333333 + qwertyu0903 | 0.333333 + qwertyu0904 | 0.333333 + qwertyu0905 | 0.333333 + qwertyu0906 | 0.333333 + qwertyu0907 | 0.333333 + qwertyu0908 | 0.333333 + qwertyu0909 | 0.333333 + qwertyu0910 | 0.333333 + qwertyu0911 | 0.333333 + qwertyu0912 | 0.333333 + qwertyu0913 | 0.333333 + qwertyu0914 | 0.333333 + qwertyu0915 | 0.333333 + qwertyu0916 | 0.333333 + qwertyu0917 | 0.333333 + qwertyu0918 | 0.333333 + qwertyu0919 | 0.333333 + qwertyu0920 | 0.333333 + qwertyu0921 | 0.333333 + qwertyu0922 | 0.333333 + qwertyu0923 | 0.333333 + qwertyu0924 | 0.333333 + qwertyu0925 | 0.333333 + qwertyu0926 | 0.333333 + qwertyu0927 | 0.333333 + qwertyu0928 | 0.333333 + qwertyu0929 | 0.333333 + qwertyu0930 | 0.333333 + qwertyu0931 | 0.333333 + qwertyu0932 | 0.333333 + qwertyu0933 | 0.333333 + qwertyu0934 | 0.333333 + qwertyu0935 | 0.333333 + qwertyu0936 | 0.333333 + qwertyu0937 | 0.333333 + qwertyu0938 | 0.333333 + qwertyu0939 | 0.333333 + qwertyu0940 | 0.333333 + qwertyu0941 | 0.333333 + qwertyu0942 | 0.333333 + qwertyu0943 | 0.333333 + qwertyu0944 | 0.333333 + qwertyu0945 | 0.333333 + qwertyu0946 | 0.333333 + qwertyu0947 | 0.333333 + qwertyu0948 | 0.333333 + qwertyu0949 | 0.333333 + qwertyu0950 | 0.333333 + qwertyu0951 | 0.333333 + qwertyu0952 | 0.333333 + qwertyu0953 | 0.333333 + qwertyu0954 | 0.333333 + qwertyu0955 | 0.333333 + qwertyu0956 | 0.333333 + qwertyu0957 | 0.333333 + qwertyu0958 | 0.333333 + qwertyu0959 | 0.333333 + qwertyu0960 | 0.333333 + qwertyu0961 | 0.333333 + qwertyu0962 | 0.333333 + qwertyu0963 | 0.333333 + qwertyu0964 | 0.333333 + qwertyu0965 | 0.333333 + qwertyu0966 | 0.333333 + qwertyu0967 | 0.333333 + qwertyu0968 | 0.333333 + qwertyu0969 | 0.333333 + qwertyu0970 | 0.333333 + qwertyu0971 | 0.333333 + qwertyu0972 | 0.333333 + qwertyu0973 | 0.333333 + qwertyu0974 | 0.333333 + qwertyu0975 | 0.333333 + qwertyu0976 | 0.333333 + qwertyu0977 | 0.333333 + qwertyu0978 | 0.333333 + qwertyu0979 | 0.333333 + qwertyu0990 | 0.333333 + qwertyu0991 | 0.333333 + qwertyu0992 | 0.333333 + qwertyu0993 | 0.333333 + qwertyu0994 | 0.333333 + qwertyu0995 | 0.333333 + qwertyu0996 | 0.333333 + qwertyu0997 | 0.333333 + qwertyu0998 | 0.333333 + qwertyu0999 | 0.333333 +(110 rows) + +select t,similarity(t,'gwertyu1988') as sml from test_trgm where t % 'gwertyu1988' order by sml desc, t; + t | sml +-------------+---------- + qwertyu0988 | 0.333333 +(1 row) + +select count(*) from test_trgm where t ~ '[qwerty]{2}-?[qwerty]{2}'; + count +------- + 1000 +(1 row) + +-- check handling of indexquals that generate no searchable conditions +explain (costs off) +select count(*) from test_trgm where t like '%99%' and t like '%qwerty%'; + QUERY PLAN +----------------------------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on test_trgm + Recheck Cond: ((t ~~ '%99%'::text) AND (t ~~ '%qwerty%'::text)) + -> Bitmap Index Scan on trgm_idx + Index Cond: ((t ~~ '%99%'::text) AND (t ~~ '%qwerty%'::text)) +(5 rows) + +select count(*) from test_trgm where t like '%99%' and t like '%qwerty%'; + count +------- + 19 +(1 row) + +explain (costs off) +select count(*) from test_trgm where t like '%99%' and t like '%qw%'; + QUERY PLAN +------------------------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on test_trgm + Recheck Cond: ((t ~~ '%99%'::text) AND (t ~~ '%qw%'::text)) + -> Bitmap Index Scan on trgm_idx + Index Cond: ((t ~~ '%99%'::text) AND (t ~~ '%qw%'::text)) +(5 rows) + +select count(*) from test_trgm where t like '%99%' and t like '%qw%'; + count +------- + 19 +(1 row) + +-- ensure that pending-list items are handled correctly, too +create temp table t_test_trgm(t text COLLATE "C"); +create index t_trgm_idx on t_test_trgm using gin (t gin_trgm_ops); +insert into t_test_trgm values ('qwerty99'), ('qwerty01'); +explain (costs off) +select count(*) from t_test_trgm where t like '%99%' and t like '%qwerty%'; + QUERY PLAN +----------------------------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on t_test_trgm + Recheck Cond: ((t ~~ '%99%'::text) AND (t ~~ '%qwerty%'::text)) + -> Bitmap Index Scan on t_trgm_idx + Index Cond: ((t ~~ '%99%'::text) AND (t ~~ '%qwerty%'::text)) +(5 rows) + +select count(*) from t_test_trgm where t like '%99%' and t like '%qwerty%'; + count +------- + 1 +(1 row) + +explain (costs off) +select count(*) from t_test_trgm where t like '%99%' and t like '%qw%'; + QUERY PLAN +------------------------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on t_test_trgm + Recheck Cond: ((t ~~ '%99%'::text) AND (t ~~ '%qw%'::text)) + -> Bitmap Index Scan on t_trgm_idx + Index Cond: ((t ~~ '%99%'::text) AND (t ~~ '%qw%'::text)) +(5 rows) + +select count(*) from t_test_trgm where t like '%99%' and t like '%qw%'; + count +------- + 1 +(1 row) + +-- run the same queries with sequential scan to check the results +set enable_bitmapscan=off; +set enable_seqscan=on; +select count(*) from test_trgm where t like '%99%' and t like '%qwerty%'; + count +------- + 19 +(1 row) + +select count(*) from test_trgm where t like '%99%' and t like '%qw%'; + count +------- + 19 +(1 row) + +select count(*) from t_test_trgm where t like '%99%' and t like '%qwerty%'; + count +------- + 1 +(1 row) + +select count(*) from t_test_trgm where t like '%99%' and t like '%qw%'; + count +------- + 1 +(1 row) + +reset enable_bitmapscan; +create table test2(t text COLLATE "C"); +insert into test2 values ('abcdef'); +insert into test2 values ('quark'); +insert into test2 values (' z foo bar'); +insert into test2 values ('/123/-45/'); +insert into test2 values ('line 1'); +insert into test2 values ('%line 2'); +insert into test2 values ('line 3%'); +insert into test2 values ('%line 4%'); +insert into test2 values ('%li%ne 5%'); +insert into test2 values ('li_e 6'); +create index test2_idx_gin on test2 using gin (t gin_trgm_ops); +set enable_seqscan=off; +explain (costs off) + select * from test2 where t like '%BCD%'; + QUERY PLAN +------------------------------------------ + Bitmap Heap Scan on test2 + Recheck Cond: (t ~~ '%BCD%'::text) + -> Bitmap Index Scan on test2_idx_gin + Index Cond: (t ~~ '%BCD%'::text) +(4 rows) + +explain (costs off) + select * from test2 where t ilike '%BCD%'; + QUERY PLAN +------------------------------------------- + Bitmap Heap Scan on test2 + Recheck Cond: (t ~~* '%BCD%'::text) + -> Bitmap Index Scan on test2_idx_gin + Index Cond: (t ~~* '%BCD%'::text) +(4 rows) + +select * from test2 where t like '%BCD%'; + t +--- +(0 rows) + +select * from test2 where t like '%bcd%'; + t +-------- + abcdef +(1 row) + +select * from test2 where t like E'%\\bcd%'; + t +-------- + abcdef +(1 row) + +select * from test2 where t ilike '%BCD%'; + t +-------- + abcdef +(1 row) + +select * from test2 where t ilike 'qua%'; + t +------- + quark +(1 row) + +select * from test2 where t like '%z foo bar%'; + t +------------- + z foo bar +(1 row) + +select * from test2 where t like ' z foo%'; + t +------------- + z foo bar +(1 row) + +explain (costs off) + select * from test2 where t ~ '[abc]{3}'; + QUERY PLAN +-------------------------------------------- + Bitmap Heap Scan on test2 + Recheck Cond: (t ~ '[abc]{3}'::text) + -> Bitmap Index Scan on test2_idx_gin + Index Cond: (t ~ '[abc]{3}'::text) +(4 rows) + +explain (costs off) + select * from test2 where t ~* 'DEF'; + QUERY PLAN +------------------------------------------ + Bitmap Heap Scan on test2 + Recheck Cond: (t ~* 'DEF'::text) + -> Bitmap Index Scan on test2_idx_gin + Index Cond: (t ~* 'DEF'::text) +(4 rows) + +select * from test2 where t ~ '[abc]{3}'; + t +-------- + abcdef +(1 row) + +select * from test2 where t ~ 'a[bc]+d'; + t +-------- + abcdef +(1 row) + +select * from test2 where t ~ '(abc)*$'; + t +------------- + abcdef + quark + z foo bar + /123/-45/ + line 1 + %line 2 + line 3% + %line 4% + %li%ne 5% + li_e 6 +(10 rows) + +select * from test2 where t ~* 'DEF'; + t +-------- + abcdef +(1 row) + +select * from test2 where t ~ 'dEf'; + t +--- +(0 rows) + +select * from test2 where t ~* '^q'; + t +------- + quark +(1 row) + +select * from test2 where t ~* '[abc]{3}[def]{3}'; + t +-------- + abcdef +(1 row) + +select * from test2 where t ~* 'ab[a-z]{3}'; + t +-------- + abcdef +(1 row) + +select * from test2 where t ~* '(^| )qua'; + t +------- + quark +(1 row) + +select * from test2 where t ~ 'q.*rk$'; + t +------- + quark +(1 row) + +select * from test2 where t ~ 'q'; + t +------- + quark +(1 row) + +select * from test2 where t ~ '[a-z]{3}'; + t +------------- + abcdef + quark + z foo bar + line 1 + %line 2 + line 3% + %line 4% +(7 rows) + +select * from test2 where t ~* '(a{10}|b{10}|c{10}){10}'; + t +--- +(0 rows) + +select * from test2 where t ~ 'z foo bar'; + t +------------- + z foo bar +(1 row) + +select * from test2 where t ~ ' z foo bar'; + t +------------- + z foo bar +(1 row) + +select * from test2 where t ~ ' z foo bar'; + t +------------- + z foo bar +(1 row) + +select * from test2 where t ~ ' z foo'; + t +------------- + z foo bar +(1 row) + +select * from test2 where t ~ 'qua(?!foo)'; + t +------- + quark +(1 row) + +select * from test2 where t ~ '/\d+/-\d'; + t +----------- + /123/-45/ +(1 row) + +-- test = operator +explain (costs off) + select * from test2 where t = 'abcdef'; + QUERY PLAN +------------------------------------------ + Bitmap Heap Scan on test2 + Recheck Cond: (t = 'abcdef'::text) + -> Bitmap Index Scan on test2_idx_gin + Index Cond: (t = 'abcdef'::text) +(4 rows) + +select * from test2 where t = 'abcdef'; + t +-------- + abcdef +(1 row) + +explain (costs off) + select * from test2 where t = '%line%'; + QUERY PLAN +------------------------------------------ + Bitmap Heap Scan on test2 + Recheck Cond: (t = '%line%'::text) + -> Bitmap Index Scan on test2_idx_gin + Index Cond: (t = '%line%'::text) +(4 rows) + +select * from test2 where t = '%line%'; + t +--- +(0 rows) + +select * from test2 where t = 'li_e 1'; + t +--- +(0 rows) + +select * from test2 where t = '%line 2'; + t +--------- + %line 2 +(1 row) + +select * from test2 where t = 'line 3%'; + t +--------- + line 3% +(1 row) + +select * from test2 where t = '%line 3%'; + t +--- +(0 rows) + +select * from test2 where t = '%line 4%'; + t +---------- + %line 4% +(1 row) + +select * from test2 where t = '%line 5%'; + t +--- +(0 rows) + +select * from test2 where t = '%li_ne 5%'; + t +--- +(0 rows) + +select * from test2 where t = '%li%ne 5%'; + t +----------- + %li%ne 5% +(1 row) + +select * from test2 where t = 'line 6'; + t +--- +(0 rows) + +select * from test2 where t = 'li_e 6'; + t +-------- + li_e 6 +(1 row) + +drop index test2_idx_gin; +create index test2_idx_gist on test2 using gist (t gist_trgm_ops); +set enable_seqscan=off; +explain (costs off) + select * from test2 where t like '%BCD%'; + QUERY PLAN +------------------------------------------ + Index Scan using test2_idx_gist on test2 + Index Cond: (t ~~ '%BCD%'::text) +(2 rows) + +explain (costs off) + select * from test2 where t ilike '%BCD%'; + QUERY PLAN +------------------------------------------ + Index Scan using test2_idx_gist on test2 + Index Cond: (t ~~* '%BCD%'::text) +(2 rows) + +select * from test2 where t like '%BCD%'; + t +--- +(0 rows) + +select * from test2 where t like '%bcd%'; + t +-------- + abcdef +(1 row) + +select * from test2 where t like E'%\\bcd%'; + t +-------- + abcdef +(1 row) + +select * from test2 where t ilike '%BCD%'; + t +-------- + abcdef +(1 row) + +select * from test2 where t ilike 'qua%'; + t +------- + quark +(1 row) + +select * from test2 where t like '%z foo bar%'; + t +------------- + z foo bar +(1 row) + +select * from test2 where t like ' z foo%'; + t +------------- + z foo bar +(1 row) + +explain (costs off) + select * from test2 where t ~ '[abc]{3}'; + QUERY PLAN +------------------------------------------ + Index Scan using test2_idx_gist on test2 + Index Cond: (t ~ '[abc]{3}'::text) +(2 rows) + +explain (costs off) + select * from test2 where t ~* 'DEF'; + QUERY PLAN +------------------------------------------ + Index Scan using test2_idx_gist on test2 + Index Cond: (t ~* 'DEF'::text) +(2 rows) + +select * from test2 where t ~ '[abc]{3}'; + t +-------- + abcdef +(1 row) + +select * from test2 where t ~ 'a[bc]+d'; + t +-------- + abcdef +(1 row) + +select * from test2 where t ~ '(abc)*$'; + t +------------- + abcdef + quark + z foo bar + /123/-45/ + line 1 + %line 2 + line 3% + %line 4% + %li%ne 5% + li_e 6 +(10 rows) + +select * from test2 where t ~* 'DEF'; + t +-------- + abcdef +(1 row) + +select * from test2 where t ~ 'dEf'; + t +--- +(0 rows) + +select * from test2 where t ~* '^q'; + t +------- + quark +(1 row) + +select * from test2 where t ~* '[abc]{3}[def]{3}'; + t +-------- + abcdef +(1 row) + +select * from test2 where t ~* 'ab[a-z]{3}'; + t +-------- + abcdef +(1 row) + +select * from test2 where t ~* '(^| )qua'; + t +------- + quark +(1 row) + +select * from test2 where t ~ 'q.*rk$'; + t +------- + quark +(1 row) + +select * from test2 where t ~ 'q'; + t +------- + quark +(1 row) + +select * from test2 where t ~ '[a-z]{3}'; + t +------------- + abcdef + quark + z foo bar + line 1 + %line 2 + line 3% + %line 4% +(7 rows) + +select * from test2 where t ~* '(a{10}|b{10}|c{10}){10}'; + t +--- +(0 rows) + +select * from test2 where t ~ 'z foo bar'; + t +------------- + z foo bar +(1 row) + +select * from test2 where t ~ ' z foo bar'; + t +------------- + z foo bar +(1 row) + +select * from test2 where t ~ ' z foo bar'; + t +------------- + z foo bar +(1 row) + +select * from test2 where t ~ ' z foo'; + t +------------- + z foo bar +(1 row) + +select * from test2 where t ~ 'qua(?!foo)'; + t +------- + quark +(1 row) + +select * from test2 where t ~ '/\d+/-\d'; + t +----------- + /123/-45/ +(1 row) + +-- test = operator +explain (costs off) + select * from test2 where t = 'abcdef'; + QUERY PLAN +------------------------------------------ + Index Scan using test2_idx_gist on test2 + Index Cond: (t = 'abcdef'::text) +(2 rows) + +select * from test2 where t = 'abcdef'; + t +-------- + abcdef +(1 row) + +explain (costs off) + select * from test2 where t = '%line%'; + QUERY PLAN +------------------------------------------ + Index Scan using test2_idx_gist on test2 + Index Cond: (t = '%line%'::text) +(2 rows) + +select * from test2 where t = '%line%'; + t +--- +(0 rows) + +select * from test2 where t = 'li_e 1'; + t +--- +(0 rows) + +select * from test2 where t = '%line 2'; + t +--------- + %line 2 +(1 row) + +select * from test2 where t = 'line 3%'; + t +--------- + line 3% +(1 row) + +select * from test2 where t = '%line 3%'; + t +--- +(0 rows) + +select * from test2 where t = '%line 4%'; + t +---------- + %line 4% +(1 row) + +select * from test2 where t = '%line 5%'; + t +--- +(0 rows) + +select * from test2 where t = '%li_ne 5%'; + t +--- +(0 rows) + +select * from test2 where t = '%li%ne 5%'; + t +----------- + %li%ne 5% +(1 row) + +select * from test2 where t = 'line 6'; + t +--- +(0 rows) + +select * from test2 where t = 'li_e 6'; + t +-------- + li_e 6 +(1 row) + +-- Check similarity threshold (bug #14202) +CREATE TEMP TABLE restaurants (city text); +INSERT INTO restaurants SELECT 'Warsaw' FROM generate_series(1, 10000); +INSERT INTO restaurants SELECT 'Szczecin' FROM generate_series(1, 10000); +CREATE INDEX ON restaurants USING gist(city gist_trgm_ops); +-- Similarity of the two names (for reference). +SELECT similarity('Szczecin', 'Warsaw'); + similarity +------------ + 0 +(1 row) + +-- Should get only 'Warsaw' for either setting of set_limit. +EXPLAIN (COSTS OFF) +SELECT DISTINCT city, similarity(city, 'Warsaw'), show_limit() + FROM restaurants WHERE city % 'Warsaw'; + QUERY PLAN +------------------------------------------------------- + HashAggregate + Group Key: city, similarity(city, 'Warsaw'::text) + -> Bitmap Heap Scan on restaurants + Recheck Cond: (city % 'Warsaw'::text) + -> Bitmap Index Scan on restaurants_city_idx + Index Cond: (city % 'Warsaw'::text) +(6 rows) + +SELECT set_limit(0.3); + set_limit +----------- + 0.3 +(1 row) + +SELECT DISTINCT city, similarity(city, 'Warsaw'), show_limit() + FROM restaurants WHERE city % 'Warsaw'; + city | similarity | show_limit +--------+------------+------------ + Warsaw | 1 | 0.3 +(1 row) + +SELECT set_limit(0.5); + set_limit +----------- + 0.5 +(1 row) + +SELECT DISTINCT city, similarity(city, 'Warsaw'), show_limit() + FROM restaurants WHERE city % 'Warsaw'; + city | similarity | show_limit +--------+------------+------------ + Warsaw | 1 | 0.5 +(1 row) + diff --git a/contrib/pg_trgm/expected/pg_word_trgm.out b/contrib/pg_trgm/expected/pg_word_trgm.out new file mode 100644 index 0000000..c66a67f --- /dev/null +++ b/contrib/pg_trgm/expected/pg_word_trgm.out @@ -0,0 +1,1052 @@ +CREATE TABLE test_trgm2(t text COLLATE "C"); +\copy test_trgm2 from 'data/trgm2.data' +-- reduce noise +set extra_float_digits = 0; +select t,word_similarity('Baykal',t) as sml from test_trgm2 where 'Baykal' <% t order by sml desc, t; + t | sml +-------------------------------------+---------- + Baykal | 1 + Boloto Baykal | 1 + Boloto Malyy Baykal | 1 + Kolkhoz Krasnyy Baykal | 1 + Ozero Baykal | 1 + Polevoy Stan Baykal | 1 + Port Baykal | 1 + Prud Novyy Baykal | 1 + Sanatoriy Baykal | 1 + Stantsiya Baykal | 1 + Zaliv Baykal | 1 + Baykalikha | 0.857143 + Baykalo-Amurskaya Zheleznaya Doroga | 0.857143 + Baykalovo | 0.857143 + Baykalovsk | 0.857143 + Baykalovskiy | 0.857143 + Baykalovskiy Rayon | 0.857143 + Baykalsko | 0.857143 + Maloye Baykalovo | 0.857143 + Zabaykal | 0.714286 +(20 rows) + +select t,word_similarity('Kabankala',t) as sml from test_trgm2 where 'Kabankala' <% t order by sml desc, t; + t | sml +------------------------------+----- + Kabankala | 1 + Kabankalan City Public Plaza | 0.9 + Abankala | 0.7 + Ntombankala School | 0.6 +(4 rows) + +select t,word_similarity('Baykal',t) as sml from test_trgm2 where t %> 'Baykal' order by sml desc, t; + t | sml +-------------------------------------+---------- + Baykal | 1 + Boloto Baykal | 1 + Boloto Malyy Baykal | 1 + Kolkhoz Krasnyy Baykal | 1 + Ozero Baykal | 1 + Polevoy Stan Baykal | 1 + Port Baykal | 1 + Prud Novyy Baykal | 1 + Sanatoriy Baykal | 1 + Stantsiya Baykal | 1 + Zaliv Baykal | 1 + Baykalikha | 0.857143 + Baykalo-Amurskaya Zheleznaya Doroga | 0.857143 + Baykalovo | 0.857143 + Baykalovsk | 0.857143 + Baykalovskiy | 0.857143 + Baykalovskiy Rayon | 0.857143 + Baykalsko | 0.857143 + Maloye Baykalovo | 0.857143 + Zabaykal | 0.714286 +(20 rows) + +select t,word_similarity('Kabankala',t) as sml from test_trgm2 where t %> 'Kabankala' order by sml desc, t; + t | sml +------------------------------+----- + Kabankala | 1 + Kabankalan City Public Plaza | 0.9 + Abankala | 0.7 + Ntombankala School | 0.6 +(4 rows) + +select t <->> 'Kabankala', t from test_trgm2 order by t <->> 'Kabankala' limit 7; + ?column? | t +----------+---------------------------------- + 0 | Kabankala + 0.1 | Kabankalan City Public Plaza + 0.3 | Abankala + 0.4 | Ntombankala School + 0.416667 | Kabakala + 0.5 | Nehalla Bankalah Reserved Forest + 0.538462 | Kabikala +(7 rows) + +create index trgm_idx2 on test_trgm2 using gist (t gist_trgm_ops); +set enable_seqscan=off; +select t,word_similarity('Baykal',t) as sml from test_trgm2 where 'Baykal' <% t order by sml desc, t; + t | sml +-------------------------------------+---------- + Baykal | 1 + Boloto Baykal | 1 + Boloto Malyy Baykal | 1 + Kolkhoz Krasnyy Baykal | 1 + Ozero Baykal | 1 + Polevoy Stan Baykal | 1 + Port Baykal | 1 + Prud Novyy Baykal | 1 + Sanatoriy Baykal | 1 + Stantsiya Baykal | 1 + Zaliv Baykal | 1 + Baykalikha | 0.857143 + Baykalo-Amurskaya Zheleznaya Doroga | 0.857143 + Baykalovo | 0.857143 + Baykalovsk | 0.857143 + Baykalovskiy | 0.857143 + Baykalovskiy Rayon | 0.857143 + Baykalsko | 0.857143 + Maloye Baykalovo | 0.857143 + Zabaykal | 0.714286 +(20 rows) + +select t,word_similarity('Kabankala',t) as sml from test_trgm2 where 'Kabankala' <% t order by sml desc, t; + t | sml +------------------------------+----- + Kabankala | 1 + Kabankalan City Public Plaza | 0.9 + Abankala | 0.7 + Ntombankala School | 0.6 +(4 rows) + +select t,word_similarity('Baykal',t) as sml from test_trgm2 where t %> 'Baykal' order by sml desc, t; + t | sml +-------------------------------------+---------- + Baykal | 1 + Boloto Baykal | 1 + Boloto Malyy Baykal | 1 + Kolkhoz Krasnyy Baykal | 1 + Ozero Baykal | 1 + Polevoy Stan Baykal | 1 + Port Baykal | 1 + Prud Novyy Baykal | 1 + Sanatoriy Baykal | 1 + Stantsiya Baykal | 1 + Zaliv Baykal | 1 + Baykalikha | 0.857143 + Baykalo-Amurskaya Zheleznaya Doroga | 0.857143 + Baykalovo | 0.857143 + Baykalovsk | 0.857143 + Baykalovskiy | 0.857143 + Baykalovskiy Rayon | 0.857143 + Baykalsko | 0.857143 + Maloye Baykalovo | 0.857143 + Zabaykal | 0.714286 +(20 rows) + +select t,word_similarity('Kabankala',t) as sml from test_trgm2 where t %> 'Kabankala' order by sml desc, t; + t | sml +------------------------------+----- + Kabankala | 1 + Kabankalan City Public Plaza | 0.9 + Abankala | 0.7 + Ntombankala School | 0.6 +(4 rows) + +explain (costs off) +select t <->> 'Kabankala', t from test_trgm2 order by t <->> 'Kabankala' limit 7; + QUERY PLAN +------------------------------------------------ + Limit + -> Index Scan using trgm_idx2 on test_trgm2 + Order By: (t <->> 'Kabankala'::text) +(3 rows) + +select t <->> 'Kabankala', t from test_trgm2 order by t <->> 'Kabankala' limit 7; + ?column? | t +----------+---------------------------------- + 0 | Kabankala + 0.1 | Kabankalan City Public Plaza + 0.3 | Abankala + 0.4 | Ntombankala School + 0.416667 | Kabakala + 0.5 | Nehalla Bankalah Reserved Forest + 0.538462 | Kabikala +(7 rows) + +drop index trgm_idx2; +create index trgm_idx2 on test_trgm2 using gin (t gin_trgm_ops); +set enable_seqscan=off; +select t,word_similarity('Baykal',t) as sml from test_trgm2 where 'Baykal' <% t order by sml desc, t; + t | sml +-------------------------------------+---------- + Baykal | 1 + Boloto Baykal | 1 + Boloto Malyy Baykal | 1 + Kolkhoz Krasnyy Baykal | 1 + Ozero Baykal | 1 + Polevoy Stan Baykal | 1 + Port Baykal | 1 + Prud Novyy Baykal | 1 + Sanatoriy Baykal | 1 + Stantsiya Baykal | 1 + Zaliv Baykal | 1 + Baykalikha | 0.857143 + Baykalo-Amurskaya Zheleznaya Doroga | 0.857143 + Baykalovo | 0.857143 + Baykalovsk | 0.857143 + Baykalovskiy | 0.857143 + Baykalovskiy Rayon | 0.857143 + Baykalsko | 0.857143 + Maloye Baykalovo | 0.857143 + Zabaykal | 0.714286 +(20 rows) + +select t,word_similarity('Kabankala',t) as sml from test_trgm2 where 'Kabankala' <% t order by sml desc, t; + t | sml +------------------------------+----- + Kabankala | 1 + Kabankalan City Public Plaza | 0.9 + Abankala | 0.7 + Ntombankala School | 0.6 +(4 rows) + +select t,word_similarity('Baykal',t) as sml from test_trgm2 where t %> 'Baykal' order by sml desc, t; + t | sml +-------------------------------------+---------- + Baykal | 1 + Boloto Baykal | 1 + Boloto Malyy Baykal | 1 + Kolkhoz Krasnyy Baykal | 1 + Ozero Baykal | 1 + Polevoy Stan Baykal | 1 + Port Baykal | 1 + Prud Novyy Baykal | 1 + Sanatoriy Baykal | 1 + Stantsiya Baykal | 1 + Zaliv Baykal | 1 + Baykalikha | 0.857143 + Baykalo-Amurskaya Zheleznaya Doroga | 0.857143 + Baykalovo | 0.857143 + Baykalovsk | 0.857143 + Baykalovskiy | 0.857143 + Baykalovskiy Rayon | 0.857143 + Baykalsko | 0.857143 + Maloye Baykalovo | 0.857143 + Zabaykal | 0.714286 +(20 rows) + +select t,word_similarity('Kabankala',t) as sml from test_trgm2 where t %> 'Kabankala' order by sml desc, t; + t | sml +------------------------------+----- + Kabankala | 1 + Kabankalan City Public Plaza | 0.9 + Abankala | 0.7 + Ntombankala School | 0.6 +(4 rows) + +set "pg_trgm.word_similarity_threshold" to 0.5; +select t,word_similarity('Baykal',t) as sml from test_trgm2 where 'Baykal' <% t order by sml desc, t; + t | sml +-------------------------------------+---------- + Baykal | 1 + Boloto Baykal | 1 + Boloto Malyy Baykal | 1 + Kolkhoz Krasnyy Baykal | 1 + Ozero Baykal | 1 + Polevoy Stan Baykal | 1 + Port Baykal | 1 + Prud Novyy Baykal | 1 + Sanatoriy Baykal | 1 + Stantsiya Baykal | 1 + Zaliv Baykal | 1 + Baykalikha | 0.857143 + Baykalo-Amurskaya Zheleznaya Doroga | 0.857143 + Baykalovo | 0.857143 + Baykalovsk | 0.857143 + Baykalovskiy | 0.857143 + Baykalovskiy Rayon | 0.857143 + Baykalsko | 0.857143 + Maloye Baykalovo | 0.857143 + Zabaykal | 0.714286 + Bakal Batu | 0.571429 + Zabaykalka | 0.571429 + Zabaykalovskiy | 0.571429 +(23 rows) + +select t,word_similarity('Kabankala',t) as sml from test_trgm2 where 'Kabankala' <% t order by sml desc, t; + t | sml +----------------------------------+---------- + Kabankala | 1 + Kabankalan City Public Plaza | 0.9 + Abankala | 0.7 + Ntombankala School | 0.6 + Kabakala | 0.583333 + Nehalla Bankalah Reserved Forest | 0.5 +(6 rows) + +select t,word_similarity('Baykal',t) as sml from test_trgm2 where t %> 'Baykal' order by sml desc, t; + t | sml +-------------------------------------+---------- + Baykal | 1 + Boloto Baykal | 1 + Boloto Malyy Baykal | 1 + Kolkhoz Krasnyy Baykal | 1 + Ozero Baykal | 1 + Polevoy Stan Baykal | 1 + Port Baykal | 1 + Prud Novyy Baykal | 1 + Sanatoriy Baykal | 1 + Stantsiya Baykal | 1 + Zaliv Baykal | 1 + Baykalikha | 0.857143 + Baykalo-Amurskaya Zheleznaya Doroga | 0.857143 + Baykalovo | 0.857143 + Baykalovsk | 0.857143 + Baykalovskiy | 0.857143 + Baykalovskiy Rayon | 0.857143 + Baykalsko | 0.857143 + Maloye Baykalovo | 0.857143 + Zabaykal | 0.714286 + Bakal Batu | 0.571429 + Zabaykalka | 0.571429 + Zabaykalovskiy | 0.571429 +(23 rows) + +select t,word_similarity('Kabankala',t) as sml from test_trgm2 where t %> 'Kabankala' order by sml desc, t; + t | sml +----------------------------------+---------- + Kabankala | 1 + Kabankalan City Public Plaza | 0.9 + Abankala | 0.7 + Ntombankala School | 0.6 + Kabakala | 0.583333 + Nehalla Bankalah Reserved Forest | 0.5 +(6 rows) + +set "pg_trgm.word_similarity_threshold" to 0.3; +select t,word_similarity('Baykal',t) as sml from test_trgm2 where 'Baykal' <% t order by sml desc, t; + t | sml +-----------------------------------------------------------+---------- + Baykal | 1 + Boloto Baykal | 1 + Boloto Malyy Baykal | 1 + Kolkhoz Krasnyy Baykal | 1 + Ozero Baykal | 1 + Polevoy Stan Baykal | 1 + Port Baykal | 1 + Prud Novyy Baykal | 1 + Sanatoriy Baykal | 1 + Stantsiya Baykal | 1 + Zaliv Baykal | 1 + Baykalikha | 0.857143 + Baykalo-Amurskaya Zheleznaya Doroga | 0.857143 + Baykalovo | 0.857143 + Baykalovsk | 0.857143 + Baykalovskiy | 0.857143 + Baykalovskiy Rayon | 0.857143 + Baykalsko | 0.857143 + Maloye Baykalovo | 0.857143 + Zabaykal | 0.714286 + Bakal Batu | 0.571429 + Zabaykalka | 0.571429 + Zabaykalovskiy | 0.571429 + Air Bakal-kecil | 0.444444 + Bakal | 0.444444 + Bakal Dos | 0.444444 + Bakal Julu | 0.444444 + Bakal Khel | 0.444444 + Bakal Lama | 0.444444 + Bakal Tres | 0.444444 + Bakal Uno | 0.444444 + Daang Bakal | 0.444444 + Desa Bakal | 0.444444 + Eat Bakal | 0.444444 + Gunung Bakal | 0.444444 + Sidi Bakal | 0.444444 + Stantsiya Bakal | 0.444444 + Sungai Bakal | 0.444444 + Talang Bakal | 0.444444 + Uruk Bakal | 0.444444 + Zaouia Oulad Bakal | 0.444444 + Al Barkali | 0.428571 + Aparthotel Adagio Premium Dubai Al Barsha | 0.428571 + Baikal Business Centre | 0.428571 + Bay of Backaland | 0.428571 + Boikalakalawa Bay | 0.428571 + Doubletree By Hilton Dubai Al Barsha Hotel and Res | 0.428571 + Doubletree By Hilton Hotel and Apartments Dubai Al Barsha | 0.428571 + Doubletree Res.Dubai-Al Barsha | 0.428571 + Holiday Inn Dubai Al Barsha | 0.428571 + Jabal Barkal | 0.428571 + Novotel Dubai Al Barsha | 0.428571 + Park Inn By Radisson Dubai Al Barsha | 0.428571 + Ramee Rose Hotel Dubai Al Barsha | 0.428571 + Waikalabubu Bay | 0.428571 + Baikal | 0.4 + Baikal Airfield | 0.4 + Baikal Hotel Moscow | 0.4 + Baikal Listvyanka Hotel | 0.4 + Baikal Mountains | 0.4 + Baikal Plaza | 0.4 + Bajkal | 0.4 + Bankal | 0.4 + Bankal School | 0.4 + Barkal | 0.4 + Lake Baikal | 0.4 + Mbay Bakala | 0.4 + Oulad el Bakkal | 0.4 + Sidi Mohammed Bakkal | 0.4 + Bairkal | 0.363636 + Bairkal Dhora | 0.363636 + Bairkal Jabal | 0.363636 + Batikal | 0.363636 + Bakala | 0.333333 + Bakala Koupi | 0.333333 + Bakalaale | 0.333333 + Bakalabwa Pans | 0.333333 + Bakalaeng | 0.333333 + Bakalafoulou | 0.333333 + Bakalalan Airport | 0.333333 + Bakalam | 0.333333 + Bakalambani | 0.333333 + Bakalan | 0.333333 + Bakalan Barat | 0.333333 + Bakalan Dua | 0.333333 + Bakalan Kidul | 0.333333 + Bakalan Kulon | 0.333333 + Bakalan Lor | 0.333333 + Bakalan River | 0.333333 + Bakalan Tengah | 0.333333 + Bakalan Wetan | 0.333333 + Bakalang | 0.333333 + Bakalao Asibi Point | 0.333333 + Bakalao Point | 0.333333 + Bakalar Air Force Base (historical) | 0.333333 + Bakalar Lake | 0.333333 + Bakalar Library | 0.333333 + Bakalarr | 0.333333 + Bakalauri | 0.333333 + Bakalauri1 | 0.333333 + Bakalauri2 | 0.333333 + Bakalauri3 | 0.333333 + Bakalauri4 | 0.333333 + Bakalauri5 | 0.333333 + Bakalauri6 | 0.333333 + Bakalauri7 | 0.333333 + Bakalauri8 | 0.333333 + Bakalauri9 | 0.333333 + Bakalawa | 0.333333 + Bakalbhar | 0.333333 + Bakalbuah | 0.333333 + Bakalda | 0.333333 + Bakaldalam | 0.333333 + Bakaldinskoye | 0.333333 + Bakaldovshchina | 0.333333 + Bakaldukuh | 0.333333 + Bakaldum | 0.333333 + Bakaldy | 0.333333 + Bakale | 0.333333 + Bakaleko | 0.333333 + Bakalerek | 0.333333 + Bakaley | 0.333333 + Bakaleyka | 0.333333 + Bakalha | 0.333333 + Bakali | 0.333333 + Bakalia Char | 0.333333 + Bakalica | 0.333333 + Bakalinga | 0.333333 + Bakalino | 0.333333 + Bakalinskiy | 0.333333 + Bakalinskiy Leskhoz | 0.333333 + Bakalinskiy Rayon | 0.333333 + Bakalipur | 0.333333 + Bakalite | 0.333333 + Bakaljaya | 0.333333 + Bakalka | 0.333333 + Bakall | 0.333333 + Bakalnica | 0.333333 + Bakalod Island | 0.333333 + Bakalongo | 0.333333 + Bakaloolay | 0.333333 + Bakalou | 0.333333 + Bakalovina | 0.333333 + Bakalovka | 0.333333 + Bakalovo | 0.333333 + Bakalovskaya Ferma | 0.333333 + Bakalpakebo | 0.333333 + Bakalpokok | 0.333333 + Bakalrejo | 0.333333 + Bakalsen | 0.333333 + Bakalshile | 0.333333 + Bakaltua Bank | 0.333333 + Bakalua | 0.333333 + Bakalukalu | 0.333333 + Bakalukalu Shan | 0.333333 + Bakalukudu | 0.333333 + Bakalum | 0.333333 + Bakaly | 0.333333 + Bakaly TV Mast | 0.333333 + Buur Bakale | 0.333333 + Buur Bakaley | 0.333333 + Columbus Bakalar Municipal Airport | 0.333333 + Dakshin Bakalia | 0.333333 + Danau Bakalan | 0.333333 + Desa Bakalan | 0.333333 + Desa Bakalankrajan | 0.333333 + Desa Bakalankrapyak | 0.333333 + Desa Bakalanpule | 0.333333 + Desa Bakalanrayung | 0.333333 + Desa Bakalanwringinpitu | 0.333333 + Desa Bakalrejo | 0.333333 + Efrejtor Bakalovo | 0.333333 + Efreytor-Bakalovo | 0.333333 + Gora Bakalyadyr | 0.333333 + Gory Bakaly | 0.333333 + Gunung Bakalan | 0.333333 + Ile Bakalibu | 0.333333 + Kali Bakalan | 0.333333 + Kampong Bakaladong | 0.333333 + Khor Bakallii | 0.333333 + Krajan Bakalan | 0.333333 + Kusu-Bakali | 0.333333 + Kwala Bakala | 0.333333 + Ngao Bakala | 0.333333 + Ovrag Bakalda | 0.333333 + Pematang Bakalpanang | 0.333333 + Pematang Bakalpanjang | 0.333333 + Pulau Bakalan | 0.333333 + Pulau Bakalanpauno | 0.333333 + Ragha Bakalzai | 0.333333 + Rodnik Bakalybulak | 0.333333 + Salu Bakalaeng | 0.333333 + Selat Bakalan | 0.333333 + Selat Bakalanpauno | 0.333333 + Sidi Mohammed el Bakali | 0.333333 + Sopka Bakaly | 0.333333 + Sovkhoz Bakalinskiy | 0.333333 + Sungai Bakala | 0.333333 + Sungai Bakaladiyan | 0.333333 + Tanjung Bakalinga | 0.333333 + Teluk Bakalan | 0.333333 + Teluk Bakalang | 0.333333 + Tubu Bakalekuk | 0.333333 + Tukad Bakalan | 0.333333 + Urochishche Bakalarnyn-Ayasy | 0.333333 + Urochishche Bakaldikha | 0.333333 + Urochishche Bakalovo | 0.333333 + Urochishche Bakaly | 0.333333 + Bakkalmal | 0.307692 + Alue Bakkala | 0.3 + Azib el Bakkali | 0.3 + Ba Kaliin | 0.3 + Bagkalen | 0.3 + Bahkalleh | 0.3 + Baikalakko | 0.3 + Baikalovo | 0.3 + Baikaluobbal | 0.3 + Bakkala Cemetery | 0.3 + Bakkalale | 0.3 + Bakkalegskardet | 0.3 + Bakkalia | 0.3 + Bakkalykkja | 0.3 + Bankali | 0.3 + Bankalol | 0.3 + Barkala | 0.3 + Barkala Park | 0.3 + Barkala Rao | 0.3 + Barkala Reserved Forest | 0.3 + Barkalabava | 0.3 + Barkaladja Pool | 0.3 + Barkalare | 0.3 + Barkald | 0.3 + Barkald stasjon | 0.3 + Barkalden | 0.3 + Barkaldfossen | 0.3 + Barkaldvola | 0.3 + Barkale | 0.3 + Barkaleh | 0.3 + Barkaleitet | 0.3 + Barkali | 0.3 + Barkallou | 0.3 + Barkalne | 0.3 + Barkalova | 0.3 + Barkalovka | 0.3 + Barkalow Hollow | 0.3 + Baskalino | 0.3 + Baskaltsi | 0.3 + Baukala | 0.3 + Bavkalasis | 0.3 + Bawkalut | 0.3 + Bawkalut Chaung | 0.3 + Bikal | 0.3 + Clifton T Barkalow Elementary School | 0.3 + Gora Barkalova | 0.3 + Gora Barkalyu | 0.3 + Khrebet Batkali | 0.3 + Kordon Barkalo | 0.3 + Nehalla Bankalah Reserved Forest | 0.3 + Ras Barkallah | 0.3 + Sopka Barkaleptskaya | 0.3 + Urochishche Batkali | 0.3 +(261 rows) + +select t,word_similarity('Kabankala',t) as sml from test_trgm2 where 'Kabankala' <% t order by sml desc, t; + t | sml +----------------------------------+---------- + Kabankala | 1 + Kabankalan City Public Plaza | 0.9 + Abankala | 0.7 + Ntombankala School | 0.6 + Kabakala | 0.583333 + Nehalla Bankalah Reserved Forest | 0.5 + Kabikala | 0.461538 + Mwalaba-Kalamba | 0.454545 + Bakala Koupi | 0.4 + Bankal | 0.4 + Bankal School | 0.4 + Bankali | 0.4 + Bankalol | 0.4 + Jabba Kalai | 0.4 + Kanampumba-Kalawa | 0.4 + Purba Kalaujan | 0.4 + Tumba-Kalamba | 0.4 + Daba Kalharereh | 0.363636 + Gagaba Kalo | 0.363636 + Jaba Kalle | 0.363636 + Dabakala | 0.333333 + Dalabakala | 0.333333 + Kambakala | 0.333333 + Ker Samba Kalla | 0.333333 + Fayzabadkala | 0.307692 + Gora Fayzabadkala | 0.307692 + Guba Kalgalaksha | 0.307692 + Habakkala | 0.307692 + Kaikalahun Indian Reserve 25 | 0.307692 + Kaikalapettai | 0.307692 + Alue Bakkala | 0.3 + Ambadikala | 0.3 + Ambakala Wewa | 0.3 + Ataikala | 0.3 + Ba Kaliin | 0.3 + Bakala | 0.3 + Bakkala Cemetery | 0.3 + Bambakala | 0.3 + Barkala | 0.3 + Barkala Park | 0.3 + Barkala Rao | 0.3 + Barkala Reserved Forest | 0.3 + Baukala | 0.3 + Beikala | 0.3 + Bikala | 0.3 + Bikala Madila | 0.3 + Bomba-Kalende | 0.3 + Bonagbakala | 0.3 + Boyagbakala | 0.3 + Bugor Arba-Kalgan | 0.3 + Bumba-Kaloki | 0.3 + Bumba-Kalumba | 0.3 + Darreh Pumba Kal | 0.3 + Demba Kali | 0.3 + Embatkala | 0.3 + Gereba Kaler | 0.3 + Golba Kalo | 0.3 + Goth Soba Kaloi | 0.3 + Guba Kaldo | 0.3 + Guba Kalita | 0.3 + Gulba Kalle | 0.3 + Haikala | 0.3 + Kali Bakalan | 0.3 + Kali Purbakala | 0.3 + Kalibakal | 0.3 + Kalibakalako | 0.3 + Kalimundubakalan | 0.3 + Kamba-Kalele | 0.3 + Kimbakala | 0.3 + Kombakala | 0.3 + Kwala Bakala | 0.3 + Laikala | 0.3 + Maikala Range | 0.3 + Mambakala | 0.3 + Matamba-Kalenga | 0.3 + Matamba-Kalenge | 0.3 + Mbay Bakala | 0.3 + Mount Tohebakala | 0.3 + Naikala | 0.3 + Ngao Bakala | 0.3 + Purba Kalmegha | 0.3 + Sungai Bakala | 0.3 + Tagobikala | 0.3 + Tanjung Batikala | 0.3 + Tombakala | 0.3 + Tsibakala | 0.3 + Tumba-Kalumba | 0.3 + Tumba-Kalunga | 0.3 + Waikala | 0.3 +(89 rows) + +select t,word_similarity('Baykal',t) as sml from test_trgm2 where t %> 'Baykal' order by sml desc, t; + t | sml +-----------------------------------------------------------+---------- + Baykal | 1 + Boloto Baykal | 1 + Boloto Malyy Baykal | 1 + Kolkhoz Krasnyy Baykal | 1 + Ozero Baykal | 1 + Polevoy Stan Baykal | 1 + Port Baykal | 1 + Prud Novyy Baykal | 1 + Sanatoriy Baykal | 1 + Stantsiya Baykal | 1 + Zaliv Baykal | 1 + Baykalikha | 0.857143 + Baykalo-Amurskaya Zheleznaya Doroga | 0.857143 + Baykalovo | 0.857143 + Baykalovsk | 0.857143 + Baykalovskiy | 0.857143 + Baykalovskiy Rayon | 0.857143 + Baykalsko | 0.857143 + Maloye Baykalovo | 0.857143 + Zabaykal | 0.714286 + Bakal Batu | 0.571429 + Zabaykalka | 0.571429 + Zabaykalovskiy | 0.571429 + Air Bakal-kecil | 0.444444 + Bakal | 0.444444 + Bakal Dos | 0.444444 + Bakal Julu | 0.444444 + Bakal Khel | 0.444444 + Bakal Lama | 0.444444 + Bakal Tres | 0.444444 + Bakal Uno | 0.444444 + Daang Bakal | 0.444444 + Desa Bakal | 0.444444 + Eat Bakal | 0.444444 + Gunung Bakal | 0.444444 + Sidi Bakal | 0.444444 + Stantsiya Bakal | 0.444444 + Sungai Bakal | 0.444444 + Talang Bakal | 0.444444 + Uruk Bakal | 0.444444 + Zaouia Oulad Bakal | 0.444444 + Al Barkali | 0.428571 + Aparthotel Adagio Premium Dubai Al Barsha | 0.428571 + Baikal Business Centre | 0.428571 + Bay of Backaland | 0.428571 + Boikalakalawa Bay | 0.428571 + Doubletree By Hilton Dubai Al Barsha Hotel and Res | 0.428571 + Doubletree By Hilton Hotel and Apartments Dubai Al Barsha | 0.428571 + Doubletree Res.Dubai-Al Barsha | 0.428571 + Holiday Inn Dubai Al Barsha | 0.428571 + Jabal Barkal | 0.428571 + Novotel Dubai Al Barsha | 0.428571 + Park Inn By Radisson Dubai Al Barsha | 0.428571 + Ramee Rose Hotel Dubai Al Barsha | 0.428571 + Waikalabubu Bay | 0.428571 + Baikal | 0.4 + Baikal Airfield | 0.4 + Baikal Hotel Moscow | 0.4 + Baikal Listvyanka Hotel | 0.4 + Baikal Mountains | 0.4 + Baikal Plaza | 0.4 + Bajkal | 0.4 + Bankal | 0.4 + Bankal School | 0.4 + Barkal | 0.4 + Lake Baikal | 0.4 + Mbay Bakala | 0.4 + Oulad el Bakkal | 0.4 + Sidi Mohammed Bakkal | 0.4 + Bairkal | 0.363636 + Bairkal Dhora | 0.363636 + Bairkal Jabal | 0.363636 + Batikal | 0.363636 + Bakala | 0.333333 + Bakala Koupi | 0.333333 + Bakalaale | 0.333333 + Bakalabwa Pans | 0.333333 + Bakalaeng | 0.333333 + Bakalafoulou | 0.333333 + Bakalalan Airport | 0.333333 + Bakalam | 0.333333 + Bakalambani | 0.333333 + Bakalan | 0.333333 + Bakalan Barat | 0.333333 + Bakalan Dua | 0.333333 + Bakalan Kidul | 0.333333 + Bakalan Kulon | 0.333333 + Bakalan Lor | 0.333333 + Bakalan River | 0.333333 + Bakalan Tengah | 0.333333 + Bakalan Wetan | 0.333333 + Bakalang | 0.333333 + Bakalao Asibi Point | 0.333333 + Bakalao Point | 0.333333 + Bakalar Air Force Base (historical) | 0.333333 + Bakalar Lake | 0.333333 + Bakalar Library | 0.333333 + Bakalarr | 0.333333 + Bakalauri | 0.333333 + Bakalauri1 | 0.333333 + Bakalauri2 | 0.333333 + Bakalauri3 | 0.333333 + Bakalauri4 | 0.333333 + Bakalauri5 | 0.333333 + Bakalauri6 | 0.333333 + Bakalauri7 | 0.333333 + Bakalauri8 | 0.333333 + Bakalauri9 | 0.333333 + Bakalawa | 0.333333 + Bakalbhar | 0.333333 + Bakalbuah | 0.333333 + Bakalda | 0.333333 + Bakaldalam | 0.333333 + Bakaldinskoye | 0.333333 + Bakaldovshchina | 0.333333 + Bakaldukuh | 0.333333 + Bakaldum | 0.333333 + Bakaldy | 0.333333 + Bakale | 0.333333 + Bakaleko | 0.333333 + Bakalerek | 0.333333 + Bakaley | 0.333333 + Bakaleyka | 0.333333 + Bakalha | 0.333333 + Bakali | 0.333333 + Bakalia Char | 0.333333 + Bakalica | 0.333333 + Bakalinga | 0.333333 + Bakalino | 0.333333 + Bakalinskiy | 0.333333 + Bakalinskiy Leskhoz | 0.333333 + Bakalinskiy Rayon | 0.333333 + Bakalipur | 0.333333 + Bakalite | 0.333333 + Bakaljaya | 0.333333 + Bakalka | 0.333333 + Bakall | 0.333333 + Bakalnica | 0.333333 + Bakalod Island | 0.333333 + Bakalongo | 0.333333 + Bakaloolay | 0.333333 + Bakalou | 0.333333 + Bakalovina | 0.333333 + Bakalovka | 0.333333 + Bakalovo | 0.333333 + Bakalovskaya Ferma | 0.333333 + Bakalpakebo | 0.333333 + Bakalpokok | 0.333333 + Bakalrejo | 0.333333 + Bakalsen | 0.333333 + Bakalshile | 0.333333 + Bakaltua Bank | 0.333333 + Bakalua | 0.333333 + Bakalukalu | 0.333333 + Bakalukalu Shan | 0.333333 + Bakalukudu | 0.333333 + Bakalum | 0.333333 + Bakaly | 0.333333 + Bakaly TV Mast | 0.333333 + Buur Bakale | 0.333333 + Buur Bakaley | 0.333333 + Columbus Bakalar Municipal Airport | 0.333333 + Dakshin Bakalia | 0.333333 + Danau Bakalan | 0.333333 + Desa Bakalan | 0.333333 + Desa Bakalankrajan | 0.333333 + Desa Bakalankrapyak | 0.333333 + Desa Bakalanpule | 0.333333 + Desa Bakalanrayung | 0.333333 + Desa Bakalanwringinpitu | 0.333333 + Desa Bakalrejo | 0.333333 + Efrejtor Bakalovo | 0.333333 + Efreytor-Bakalovo | 0.333333 + Gora Bakalyadyr | 0.333333 + Gory Bakaly | 0.333333 + Gunung Bakalan | 0.333333 + Ile Bakalibu | 0.333333 + Kali Bakalan | 0.333333 + Kampong Bakaladong | 0.333333 + Khor Bakallii | 0.333333 + Krajan Bakalan | 0.333333 + Kusu-Bakali | 0.333333 + Kwala Bakala | 0.333333 + Ngao Bakala | 0.333333 + Ovrag Bakalda | 0.333333 + Pematang Bakalpanang | 0.333333 + Pematang Bakalpanjang | 0.333333 + Pulau Bakalan | 0.333333 + Pulau Bakalanpauno | 0.333333 + Ragha Bakalzai | 0.333333 + Rodnik Bakalybulak | 0.333333 + Salu Bakalaeng | 0.333333 + Selat Bakalan | 0.333333 + Selat Bakalanpauno | 0.333333 + Sidi Mohammed el Bakali | 0.333333 + Sopka Bakaly | 0.333333 + Sovkhoz Bakalinskiy | 0.333333 + Sungai Bakala | 0.333333 + Sungai Bakaladiyan | 0.333333 + Tanjung Bakalinga | 0.333333 + Teluk Bakalan | 0.333333 + Teluk Bakalang | 0.333333 + Tubu Bakalekuk | 0.333333 + Tukad Bakalan | 0.333333 + Urochishche Bakalarnyn-Ayasy | 0.333333 + Urochishche Bakaldikha | 0.333333 + Urochishche Bakalovo | 0.333333 + Urochishche Bakaly | 0.333333 + Bakkalmal | 0.307692 + Alue Bakkala | 0.3 + Azib el Bakkali | 0.3 + Ba Kaliin | 0.3 + Bagkalen | 0.3 + Bahkalleh | 0.3 + Baikalakko | 0.3 + Baikalovo | 0.3 + Baikaluobbal | 0.3 + Bakkala Cemetery | 0.3 + Bakkalale | 0.3 + Bakkalegskardet | 0.3 + Bakkalia | 0.3 + Bakkalykkja | 0.3 + Bankali | 0.3 + Bankalol | 0.3 + Barkala | 0.3 + Barkala Park | 0.3 + Barkala Rao | 0.3 + Barkala Reserved Forest | 0.3 + Barkalabava | 0.3 + Barkaladja Pool | 0.3 + Barkalare | 0.3 + Barkald | 0.3 + Barkald stasjon | 0.3 + Barkalden | 0.3 + Barkaldfossen | 0.3 + Barkaldvola | 0.3 + Barkale | 0.3 + Barkaleh | 0.3 + Barkaleitet | 0.3 + Barkali | 0.3 + Barkallou | 0.3 + Barkalne | 0.3 + Barkalova | 0.3 + Barkalovka | 0.3 + Barkalow Hollow | 0.3 + Baskalino | 0.3 + Baskaltsi | 0.3 + Baukala | 0.3 + Bavkalasis | 0.3 + Bawkalut | 0.3 + Bawkalut Chaung | 0.3 + Bikal | 0.3 + Clifton T Barkalow Elementary School | 0.3 + Gora Barkalova | 0.3 + Gora Barkalyu | 0.3 + Khrebet Batkali | 0.3 + Kordon Barkalo | 0.3 + Nehalla Bankalah Reserved Forest | 0.3 + Ras Barkallah | 0.3 + Sopka Barkaleptskaya | 0.3 + Urochishche Batkali | 0.3 +(261 rows) + +select t,word_similarity('Kabankala',t) as sml from test_trgm2 where t %> 'Kabankala' order by sml desc, t; + t | sml +----------------------------------+---------- + Kabankala | 1 + Kabankalan City Public Plaza | 0.9 + Abankala | 0.7 + Ntombankala School | 0.6 + Kabakala | 0.583333 + Nehalla Bankalah Reserved Forest | 0.5 + Kabikala | 0.461538 + Mwalaba-Kalamba | 0.454545 + Bakala Koupi | 0.4 + Bankal | 0.4 + Bankal School | 0.4 + Bankali | 0.4 + Bankalol | 0.4 + Jabba Kalai | 0.4 + Kanampumba-Kalawa | 0.4 + Purba Kalaujan | 0.4 + Tumba-Kalamba | 0.4 + Daba Kalharereh | 0.363636 + Gagaba Kalo | 0.363636 + Jaba Kalle | 0.363636 + Dabakala | 0.333333 + Dalabakala | 0.333333 + Kambakala | 0.333333 + Ker Samba Kalla | 0.333333 + Fayzabadkala | 0.307692 + Gora Fayzabadkala | 0.307692 + Guba Kalgalaksha | 0.307692 + Habakkala | 0.307692 + Kaikalahun Indian Reserve 25 | 0.307692 + Kaikalapettai | 0.307692 + Alue Bakkala | 0.3 + Ambadikala | 0.3 + Ambakala Wewa | 0.3 + Ataikala | 0.3 + Ba Kaliin | 0.3 + Bakala | 0.3 + Bakkala Cemetery | 0.3 + Bambakala | 0.3 + Barkala | 0.3 + Barkala Park | 0.3 + Barkala Rao | 0.3 + Barkala Reserved Forest | 0.3 + Baukala | 0.3 + Beikala | 0.3 + Bikala | 0.3 + Bikala Madila | 0.3 + Bomba-Kalende | 0.3 + Bonagbakala | 0.3 + Boyagbakala | 0.3 + Bugor Arba-Kalgan | 0.3 + Bumba-Kaloki | 0.3 + Bumba-Kalumba | 0.3 + Darreh Pumba Kal | 0.3 + Demba Kali | 0.3 + Embatkala | 0.3 + Gereba Kaler | 0.3 + Golba Kalo | 0.3 + Goth Soba Kaloi | 0.3 + Guba Kaldo | 0.3 + Guba Kalita | 0.3 + Gulba Kalle | 0.3 + Haikala | 0.3 + Kali Bakalan | 0.3 + Kali Purbakala | 0.3 + Kalibakal | 0.3 + Kalibakalako | 0.3 + Kalimundubakalan | 0.3 + Kamba-Kalele | 0.3 + Kimbakala | 0.3 + Kombakala | 0.3 + Kwala Bakala | 0.3 + Laikala | 0.3 + Maikala Range | 0.3 + Mambakala | 0.3 + Matamba-Kalenga | 0.3 + Matamba-Kalenge | 0.3 + Mbay Bakala | 0.3 + Mount Tohebakala | 0.3 + Naikala | 0.3 + Ngao Bakala | 0.3 + Purba Kalmegha | 0.3 + Sungai Bakala | 0.3 + Tagobikala | 0.3 + Tanjung Batikala | 0.3 + Tombakala | 0.3 + Tsibakala | 0.3 + Tumba-Kalumba | 0.3 + Tumba-Kalunga | 0.3 + Waikala | 0.3 +(89 rows) + +-- test unsatisfiable pattern +select * from test_trgm2 where t ~ '.*$x'; + t +--- +(0 rows) + diff --git a/contrib/pg_trgm/meson.build b/contrib/pg_trgm/meson.build new file mode 100644 index 0000000..093ac18 --- /dev/null +++ b/contrib/pg_trgm/meson.build @@ -0,0 +1,46 @@ +# Copyright (c) 2022-2023, PostgreSQL Global Development Group + +pg_trgm_sources = files( + 'trgm_gin.c', + 'trgm_gist.c', + 'trgm_op.c', + 'trgm_regexp.c', +) + +if host_system == 'windows' + pg_trgm_sources += rc_lib_gen.process(win32ver_rc, extra_args: [ + '--NAME', 'pg_trgm', + '--FILEDESC', 'pg_trgm - trigram matching',]) +endif + +pg_trgm = shared_module('pg_trgm', + pg_trgm_sources, + c_pch: pch_postgres_h, + kwargs: contrib_mod_args, +) +contrib_targets += pg_trgm + +install_data( + 'pg_trgm--1.0--1.1.sql', + 'pg_trgm--1.1--1.2.sql', + 'pg_trgm--1.2--1.3.sql', + 'pg_trgm--1.3--1.4.sql', + 'pg_trgm--1.3.sql', + 'pg_trgm--1.4--1.5.sql', + 'pg_trgm--1.5--1.6.sql', + 'pg_trgm.control', + kwargs: contrib_data_args, +) + +tests += { + 'name': 'pg_trgm', + 'sd': meson.current_source_dir(), + 'bd': meson.current_build_dir(), + 'regress': { + 'sql': [ + 'pg_trgm', + 'pg_word_trgm', + 'pg_strict_word_trgm', + ], + }, +} diff --git a/contrib/pg_trgm/pg_trgm--1.0--1.1.sql b/contrib/pg_trgm/pg_trgm--1.0--1.1.sql new file mode 100644 index 0000000..b4e3e26 --- /dev/null +++ b/contrib/pg_trgm/pg_trgm--1.0--1.1.sql @@ -0,0 +1,12 @@ +/* contrib/pg_trgm/pg_trgm--1.0--1.1.sql */ + +-- complain if script is sourced in psql, rather than via ALTER EXTENSION +\echo Use "ALTER EXTENSION pg_trgm UPDATE TO '1.1'" to load this file. \quit + +ALTER OPERATOR FAMILY gist_trgm_ops USING gist ADD + OPERATOR 5 pg_catalog.~ (text, text), + OPERATOR 6 pg_catalog.~* (text, text); + +ALTER OPERATOR FAMILY gin_trgm_ops USING gin ADD + OPERATOR 5 pg_catalog.~ (text, text), + OPERATOR 6 pg_catalog.~* (text, text); diff --git a/contrib/pg_trgm/pg_trgm--1.1--1.2.sql b/contrib/pg_trgm/pg_trgm--1.1--1.2.sql new file mode 100644 index 0000000..ff0f13f --- /dev/null +++ b/contrib/pg_trgm/pg_trgm--1.1--1.2.sql @@ -0,0 +1,74 @@ +/* contrib/pg_trgm/pg_trgm--1.1--1.2.sql */ + +-- complain if script is sourced in psql, rather than via ALTER EXTENSION +\echo Use "ALTER EXTENSION pg_trgm UPDATE TO '1.2'" to load this file. \quit + +CREATE FUNCTION word_similarity(text,text) +RETURNS float4 +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT IMMUTABLE; + +CREATE FUNCTION word_similarity_op(text,text) +RETURNS bool +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT STABLE; -- stable because depends on pg_trgm.word_similarity_threshold + +CREATE FUNCTION word_similarity_commutator_op(text,text) +RETURNS bool +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT STABLE; -- stable because depends on pg_trgm.word_similarity_threshold + +CREATE FUNCTION word_similarity_dist_op(text,text) +RETURNS float4 +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT IMMUTABLE; + +CREATE FUNCTION word_similarity_dist_commutator_op(text,text) +RETURNS float4 +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT IMMUTABLE; + +CREATE OPERATOR <% ( + LEFTARG = text, + RIGHTARG = text, + PROCEDURE = word_similarity_op, + COMMUTATOR = '%>', + RESTRICT = contsel, + JOIN = contjoinsel +); + +CREATE OPERATOR %> ( + LEFTARG = text, + RIGHTARG = text, + PROCEDURE = word_similarity_commutator_op, + COMMUTATOR = '<%', + RESTRICT = contsel, + JOIN = contjoinsel +); + +CREATE OPERATOR <<-> ( + LEFTARG = text, + RIGHTARG = text, + PROCEDURE = word_similarity_dist_op, + COMMUTATOR = '<->>' +); + +CREATE OPERATOR <->> ( + LEFTARG = text, + RIGHTARG = text, + PROCEDURE = word_similarity_dist_commutator_op, + COMMUTATOR = '<<->' +); + +CREATE FUNCTION gin_trgm_triconsistent(internal, int2, text, int4, internal, internal, internal) +RETURNS "char" +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_trgm_ops USING gist ADD + OPERATOR 7 %> (text, text), + OPERATOR 8 <->> (text, text) FOR ORDER BY pg_catalog.float_ops; + +ALTER OPERATOR FAMILY gin_trgm_ops USING gin ADD + OPERATOR 7 %> (text, text), + FUNCTION 6 (text, text) gin_trgm_triconsistent (internal, int2, text, int4, internal, internal, internal); diff --git a/contrib/pg_trgm/pg_trgm--1.2--1.3.sql b/contrib/pg_trgm/pg_trgm--1.2--1.3.sql new file mode 100644 index 0000000..8dc772c --- /dev/null +++ b/contrib/pg_trgm/pg_trgm--1.2--1.3.sql @@ -0,0 +1,65 @@ +/* contrib/pg_trgm/pg_trgm--1.2--1.3.sql */ + +-- complain if script is sourced in psql, rather than via ALTER EXTENSION +\echo Use "ALTER EXTENSION pg_trgm UPDATE TO '1.3'" to load this file. \quit + +-- Update procedure signatures the hard way. +-- We use to_regprocedure() so that query doesn't fail if run against 9.6beta1 definitions, +-- wherein the signatures have been updated already. In that case to_regprocedure() will +-- return NULL and no updates will happen. +DO LANGUAGE plpgsql +$$ +DECLARE + my_schema pg_catalog.text := pg_catalog.quote_ident(pg_catalog.current_schema()); + old_path pg_catalog.text := pg_catalog.current_setting('search_path'); +BEGIN +-- for safety, transiently set search_path to just pg_catalog+pg_temp +PERFORM pg_catalog.set_config('search_path', 'pg_catalog, pg_temp', true); + +UPDATE pg_catalog.pg_proc SET + proargtypes = pg_catalog.array_to_string(newtypes::pg_catalog.oid[], ' ')::pg_catalog.oidvector, + pronargs = pg_catalog.array_length(newtypes, 1) +FROM (VALUES +(NULL::pg_catalog.text, NULL::pg_catalog.text[]), -- establish column types +('gtrgm_consistent(internal,text,int4,oid,internal)', '{internal,text,int2,oid,internal}'), +('gtrgm_distance(internal,text,int4,oid)', '{internal,text,int2,oid,internal}'), +('gtrgm_union(bytea,internal)', '{internal,internal}') +) AS update_data (oldproc, newtypestext), +LATERAL ( + SELECT array_agg(replace(typ, 'SCH', my_schema)::regtype) as newtypes FROM unnest(newtypestext) typ +) ls +WHERE oid = to_regprocedure(my_schema || '.' || replace(oldproc, 'SCH', my_schema)); + +UPDATE pg_catalog.pg_proc SET + prorettype = (my_schema || '.gtrgm')::pg_catalog.regtype +WHERE oid = pg_catalog.to_regprocedure(my_schema || '.gtrgm_union(internal,internal)'); + +PERFORM pg_catalog.set_config('search_path', old_path, true); +END +$$; + +ALTER FUNCTION set_limit(float4) PARALLEL UNSAFE; +ALTER FUNCTION show_limit() PARALLEL SAFE; +ALTER FUNCTION show_trgm(text) PARALLEL SAFE; +ALTER FUNCTION similarity(text, text) PARALLEL SAFE; +ALTER FUNCTION similarity_op(text, text) PARALLEL SAFE; +ALTER FUNCTION word_similarity(text, text) PARALLEL SAFE; +ALTER FUNCTION word_similarity_op(text, text) PARALLEL SAFE; +ALTER FUNCTION word_similarity_commutator_op(text, text) PARALLEL SAFE; +ALTER FUNCTION similarity_dist(text, text) PARALLEL SAFE; +ALTER FUNCTION word_similarity_dist_op(text, text) PARALLEL SAFE; +ALTER FUNCTION word_similarity_dist_commutator_op(text, text) PARALLEL SAFE; +ALTER FUNCTION gtrgm_in(cstring) PARALLEL SAFE; +ALTER FUNCTION gtrgm_out(gtrgm) PARALLEL SAFE; +ALTER FUNCTION gtrgm_consistent(internal, text, smallint, oid, internal) PARALLEL SAFE; +ALTER FUNCTION gtrgm_distance(internal, text, smallint, oid, internal) PARALLEL SAFE; +ALTER FUNCTION gtrgm_compress(internal) PARALLEL SAFE; +ALTER FUNCTION gtrgm_decompress(internal) PARALLEL SAFE; +ALTER FUNCTION gtrgm_penalty(internal, internal, internal) PARALLEL SAFE; +ALTER FUNCTION gtrgm_picksplit(internal, internal) PARALLEL SAFE; +ALTER FUNCTION gtrgm_union(internal, internal) PARALLEL SAFE; +ALTER FUNCTION gtrgm_same(gtrgm, gtrgm, internal) PARALLEL SAFE; +ALTER FUNCTION gin_extract_value_trgm(text, internal) PARALLEL SAFE; +ALTER FUNCTION gin_extract_query_trgm(text, internal, int2, internal, internal, internal, internal) PARALLEL SAFE; +ALTER FUNCTION gin_trgm_consistent(internal, int2, text, int4, internal, internal, internal, internal) PARALLEL SAFE; +ALTER FUNCTION gin_trgm_triconsistent(internal, int2, text, int4, internal, internal, internal) PARALLEL SAFE; diff --git a/contrib/pg_trgm/pg_trgm--1.3--1.4.sql b/contrib/pg_trgm/pg_trgm--1.3--1.4.sql new file mode 100644 index 0000000..64a0c21 --- /dev/null +++ b/contrib/pg_trgm/pg_trgm--1.3--1.4.sql @@ -0,0 +1,68 @@ +/* contrib/pg_trgm/pg_trgm--1.3--1.4.sql */ + +-- complain if script is sourced in psql, rather than via ALTER EXTENSION +\echo Use "ALTER EXTENSION pg_trgm UPDATE TO '1.4'" to load this file. \quit + +CREATE FUNCTION strict_word_similarity(text,text) +RETURNS float4 +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE; + +CREATE FUNCTION strict_word_similarity_op(text,text) +RETURNS bool +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT STABLE PARALLEL SAFE; -- stable because depends on pg_trgm.word_similarity_threshold + +CREATE FUNCTION strict_word_similarity_commutator_op(text,text) +RETURNS bool +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT STABLE PARALLEL SAFE; -- stable because depends on pg_trgm.word_similarity_threshold + +CREATE OPERATOR <<% ( + LEFTARG = text, + RIGHTARG = text, + PROCEDURE = strict_word_similarity_op, + COMMUTATOR = '%>>', + RESTRICT = contsel, + JOIN = contjoinsel +); + +CREATE OPERATOR %>> ( + LEFTARG = text, + RIGHTARG = text, + PROCEDURE = strict_word_similarity_commutator_op, + COMMUTATOR = '<<%', + RESTRICT = contsel, + JOIN = contjoinsel +); + +CREATE FUNCTION strict_word_similarity_dist_op(text,text) +RETURNS float4 +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE; + +CREATE FUNCTION strict_word_similarity_dist_commutator_op(text,text) +RETURNS float4 +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE; + +CREATE OPERATOR <<<-> ( + LEFTARG = text, + RIGHTARG = text, + PROCEDURE = strict_word_similarity_dist_op, + COMMUTATOR = '<->>>' +); + +CREATE OPERATOR <->>> ( + LEFTARG = text, + RIGHTARG = text, + PROCEDURE = strict_word_similarity_dist_commutator_op, + COMMUTATOR = '<<<->' +); + +ALTER OPERATOR FAMILY gist_trgm_ops USING gist ADD + OPERATOR 9 %>> (text, text), + OPERATOR 10 <->>> (text, text) FOR ORDER BY pg_catalog.float_ops; + +ALTER OPERATOR FAMILY gin_trgm_ops USING gin ADD + OPERATOR 9 %>> (text, text); diff --git a/contrib/pg_trgm/pg_trgm--1.3.sql b/contrib/pg_trgm/pg_trgm--1.3.sql new file mode 100644 index 0000000..4c6edf8 --- /dev/null +++ b/contrib/pg_trgm/pg_trgm--1.3.sql @@ -0,0 +1,254 @@ +/* contrib/pg_trgm/pg_trgm--1.3.sql */ + +-- complain if script is sourced in psql, rather than via CREATE EXTENSION +\echo Use "CREATE EXTENSION pg_trgm" to load this file. \quit + +-- Deprecated function +CREATE FUNCTION set_limit(float4) +RETURNS float4 +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT VOLATILE PARALLEL UNSAFE; + +-- Deprecated function +CREATE FUNCTION show_limit() +RETURNS float4 +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT STABLE PARALLEL SAFE; + +CREATE FUNCTION show_trgm(text) +RETURNS _text +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE; + +CREATE FUNCTION similarity(text,text) +RETURNS float4 +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE; + +CREATE FUNCTION similarity_op(text,text) +RETURNS bool +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT STABLE PARALLEL SAFE; -- stable because depends on pg_trgm.similarity_threshold + +CREATE OPERATOR % ( + LEFTARG = text, + RIGHTARG = text, + PROCEDURE = similarity_op, + COMMUTATOR = '%', + RESTRICT = contsel, + JOIN = contjoinsel +); + +CREATE FUNCTION word_similarity(text,text) +RETURNS float4 +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE; + +CREATE FUNCTION word_similarity_op(text,text) +RETURNS bool +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT STABLE PARALLEL SAFE; -- stable because depends on pg_trgm.word_similarity_threshold + +CREATE FUNCTION word_similarity_commutator_op(text,text) +RETURNS bool +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT STABLE PARALLEL SAFE; -- stable because depends on pg_trgm.word_similarity_threshold + +CREATE OPERATOR <% ( + LEFTARG = text, + RIGHTARG = text, + PROCEDURE = word_similarity_op, + COMMUTATOR = '%>', + RESTRICT = contsel, + JOIN = contjoinsel +); + +CREATE OPERATOR %> ( + LEFTARG = text, + RIGHTARG = text, + PROCEDURE = word_similarity_commutator_op, + COMMUTATOR = '<%', + RESTRICT = contsel, + JOIN = contjoinsel +); + +CREATE FUNCTION similarity_dist(text,text) +RETURNS float4 +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE; + +CREATE OPERATOR <-> ( + LEFTARG = text, + RIGHTARG = text, + PROCEDURE = similarity_dist, + COMMUTATOR = '<->' +); + +CREATE FUNCTION word_similarity_dist_op(text,text) +RETURNS float4 +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE; + +CREATE FUNCTION word_similarity_dist_commutator_op(text,text) +RETURNS float4 +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE; + +CREATE OPERATOR <<-> ( + LEFTARG = text, + RIGHTARG = text, + PROCEDURE = word_similarity_dist_op, + COMMUTATOR = '<->>' +); + +CREATE OPERATOR <->> ( + LEFTARG = text, + RIGHTARG = text, + PROCEDURE = word_similarity_dist_commutator_op, + COMMUTATOR = '<<->' +); + +-- gist key +CREATE FUNCTION gtrgm_in(cstring) +RETURNS gtrgm +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE; + +CREATE FUNCTION gtrgm_out(gtrgm) +RETURNS cstring +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE; + +CREATE TYPE gtrgm ( + INTERNALLENGTH = -1, + INPUT = gtrgm_in, + OUTPUT = gtrgm_out +); + +-- support functions for gist +CREATE FUNCTION gtrgm_consistent(internal,text,smallint,oid,internal) +RETURNS bool +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION gtrgm_distance(internal,text,smallint,oid,internal) +RETURNS float8 +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION gtrgm_compress(internal) +RETURNS internal +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION gtrgm_decompress(internal) +RETURNS internal +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION gtrgm_penalty(internal,internal,internal) +RETURNS internal +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION gtrgm_picksplit(internal, internal) +RETURNS internal +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION gtrgm_union(internal, internal) +RETURNS gtrgm +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION gtrgm_same(gtrgm, gtrgm, internal) +RETURNS internal +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +-- create the operator class for gist +CREATE OPERATOR CLASS gist_trgm_ops +FOR TYPE text USING gist +AS + OPERATOR 1 % (text, text), + FUNCTION 1 gtrgm_consistent (internal, text, smallint, oid, internal), + FUNCTION 2 gtrgm_union (internal, internal), + FUNCTION 3 gtrgm_compress (internal), + FUNCTION 4 gtrgm_decompress (internal), + FUNCTION 5 gtrgm_penalty (internal, internal, internal), + FUNCTION 6 gtrgm_picksplit (internal, internal), + FUNCTION 7 gtrgm_same (gtrgm, gtrgm, internal), + STORAGE gtrgm; + +-- Add operators and support functions that are new in 9.1. We do it like +-- this, leaving them "loose" in the operator family rather than bound into +-- the gist_trgm_ops opclass, because that's the only state that could be +-- reproduced during an upgrade from 9.0. + +ALTER OPERATOR FAMILY gist_trgm_ops USING gist ADD + OPERATOR 2 <-> (text, text) FOR ORDER BY pg_catalog.float_ops, + OPERATOR 3 pg_catalog.~~ (text, text), + OPERATOR 4 pg_catalog.~~* (text, text), + FUNCTION 8 (text, text) gtrgm_distance (internal, text, smallint, oid, internal); + +-- Add operators that are new in 9.3. + +ALTER OPERATOR FAMILY gist_trgm_ops USING gist ADD + OPERATOR 5 pg_catalog.~ (text, text), + OPERATOR 6 pg_catalog.~* (text, text); + +-- Add operators that are new in 9.6 (pg_trgm 1.2). + +ALTER OPERATOR FAMILY gist_trgm_ops USING gist ADD + OPERATOR 7 %> (text, text), + OPERATOR 8 <->> (text, text) FOR ORDER BY pg_catalog.float_ops; + +-- support functions for gin +CREATE FUNCTION gin_extract_value_trgm(text, internal) +RETURNS internal +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION gin_extract_query_trgm(text, internal, int2, internal, internal, internal, internal) +RETURNS internal +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION gin_trgm_consistent(internal, int2, text, int4, internal, internal, internal, internal) +RETURNS bool +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +-- create the operator class for gin +CREATE OPERATOR CLASS gin_trgm_ops +FOR TYPE text USING gin +AS + OPERATOR 1 % (text, text), + FUNCTION 1 btint4cmp (int4, int4), + FUNCTION 2 gin_extract_value_trgm (text, internal), + FUNCTION 3 gin_extract_query_trgm (text, internal, int2, internal, internal, internal, internal), + FUNCTION 4 gin_trgm_consistent (internal, int2, text, int4, internal, internal, internal, internal), + STORAGE int4; + +-- Add operators that are new in 9.1. + +ALTER OPERATOR FAMILY gin_trgm_ops USING gin ADD + OPERATOR 3 pg_catalog.~~ (text, text), + OPERATOR 4 pg_catalog.~~* (text, text); + +-- Add operators that are new in 9.3. + +ALTER OPERATOR FAMILY gin_trgm_ops USING gin ADD + OPERATOR 5 pg_catalog.~ (text, text), + OPERATOR 6 pg_catalog.~* (text, text); + +-- Add functions that are new in 9.6 (pg_trgm 1.2). + +CREATE FUNCTION gin_trgm_triconsistent(internal, int2, text, int4, internal, internal, internal) +RETURNS "char" +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +ALTER OPERATOR FAMILY gin_trgm_ops USING gin ADD + OPERATOR 7 %> (text, text), + FUNCTION 6 (text,text) gin_trgm_triconsistent (internal, int2, text, int4, internal, internal, internal); diff --git a/contrib/pg_trgm/pg_trgm--1.4--1.5.sql b/contrib/pg_trgm/pg_trgm--1.4--1.5.sql new file mode 100644 index 0000000..db122fc --- /dev/null +++ b/contrib/pg_trgm/pg_trgm--1.4--1.5.sql @@ -0,0 +1,23 @@ +/* contrib/pg_trgm/pg_trgm--1.4--1.5.sql */ + +-- complain if script is sourced in psql, rather than via ALTER EXTENSION +\echo Use "ALTER EXTENSION pg_trgm UPDATE TO '1.5'" to load this file. \quit + +CREATE FUNCTION gtrgm_options(internal) +RETURNS void +AS 'MODULE_PATHNAME', 'gtrgm_options' +LANGUAGE C IMMUTABLE PARALLEL SAFE; + +ALTER OPERATOR FAMILY gist_trgm_ops USING gist +ADD FUNCTION 10 (text) gtrgm_options (internal); + +ALTER OPERATOR % (text, text) + SET (RESTRICT = matchingsel, JOIN = matchingjoinsel); +ALTER OPERATOR <% (text, text) + SET (RESTRICT = matchingsel, JOIN = matchingjoinsel); +ALTER OPERATOR %> (text, text) + SET (RESTRICT = matchingsel, JOIN = matchingjoinsel); +ALTER OPERATOR <<% (text, text) + SET (RESTRICT = matchingsel, JOIN = matchingjoinsel); +ALTER OPERATOR %>> (text, text) + SET (RESTRICT = matchingsel, JOIN = matchingjoinsel); diff --git a/contrib/pg_trgm/pg_trgm--1.5--1.6.sql b/contrib/pg_trgm/pg_trgm--1.5--1.6.sql new file mode 100644 index 0000000..9e74684 --- /dev/null +++ b/contrib/pg_trgm/pg_trgm--1.5--1.6.sql @@ -0,0 +1,10 @@ +/* contrib/pg_trgm/pg_trgm--1.5--1.6.sql */ + +-- complain if script is sourced in psql, rather than via ALTER EXTENSION +\echo Use "ALTER EXTENSION pg_trgm UPDATE TO '1.6'" to load this file. \quit + +ALTER OPERATOR FAMILY gin_trgm_ops USING gin ADD + OPERATOR 11 pg_catalog.= (text, text); + +ALTER OPERATOR FAMILY gist_trgm_ops USING gist ADD + OPERATOR 11 pg_catalog.= (text, text); diff --git a/contrib/pg_trgm/pg_trgm.control b/contrib/pg_trgm/pg_trgm.control new file mode 100644 index 0000000..1d6a9dd --- /dev/null +++ b/contrib/pg_trgm/pg_trgm.control @@ -0,0 +1,6 @@ +# pg_trgm extension +comment = 'text similarity measurement and index searching based on trigrams' +default_version = '1.6' +module_pathname = '$libdir/pg_trgm' +relocatable = true +trusted = true diff --git a/contrib/pg_trgm/sql/pg_strict_word_trgm.sql b/contrib/pg_trgm/sql/pg_strict_word_trgm.sql new file mode 100644 index 0000000..ce0791f --- /dev/null +++ b/contrib/pg_trgm/sql/pg_strict_word_trgm.sql @@ -0,0 +1,45 @@ +DROP INDEX trgm_idx2; + +\copy test_trgm3 from 'data/trgm2.data' + +-- reduce noise +set extra_float_digits = 0; + +select t,strict_word_similarity('Baykal',t) as sml from test_trgm2 where 'Baykal' <<% t order by sml desc, t; +select t,strict_word_similarity('Kabankala',t) as sml from test_trgm2 where 'Kabankala' <<% t order by sml desc, t; +select t,strict_word_similarity('Baykal',t) as sml from test_trgm2 where t %>> 'Baykal' order by sml desc, t; +select t,strict_word_similarity('Kabankala',t) as sml from test_trgm2 where t %>> 'Kabankala' order by sml desc, t; +select t <->>> 'Alaikallupoddakulam', t from test_trgm2 order by t <->>> 'Alaikallupoddakulam' limit 7; + +create index trgm_idx2 on test_trgm2 using gist (t gist_trgm_ops); +set enable_seqscan=off; + +select t,strict_word_similarity('Baykal',t) as sml from test_trgm2 where 'Baykal' <<% t order by sml desc, t; +select t,strict_word_similarity('Kabankala',t) as sml from test_trgm2 where 'Kabankala' <<% t order by sml desc, t; +select t,strict_word_similarity('Baykal',t) as sml from test_trgm2 where t %>> 'Baykal' order by sml desc, t; +select t,strict_word_similarity('Kabankala',t) as sml from test_trgm2 where t %>> 'Kabankala' order by sml desc, t; + +explain (costs off) +select t <->>> 'Alaikallupoddakulam', t from test_trgm2 order by t <->>> 'Alaikallupoddakulam' limit 7; +select t <->>> 'Alaikallupoddakulam', t from test_trgm2 order by t <->>> 'Alaikallupoddakulam' limit 7; + +drop index trgm_idx2; +create index trgm_idx2 on test_trgm2 using gin (t gin_trgm_ops); +set enable_seqscan=off; + +select t,strict_word_similarity('Baykal',t) as sml from test_trgm2 where 'Baykal' <<% t order by sml desc, t; +select t,strict_word_similarity('Kabankala',t) as sml from test_trgm2 where 'Kabankala' <<% t order by sml desc, t; +select t,strict_word_similarity('Baykal',t) as sml from test_trgm2 where t %>> 'Baykal' order by sml desc, t; +select t,strict_word_similarity('Kabankala',t) as sml from test_trgm2 where t %>> 'Kabankala' order by sml desc, t; + +set "pg_trgm.strict_word_similarity_threshold" to 0.4; +select t,strict_word_similarity('Baykal',t) as sml from test_trgm2 where 'Baykal' <<% t order by sml desc, t; +select t,strict_word_similarity('Kabankala',t) as sml from test_trgm2 where 'Kabankala' <<% t order by sml desc, t; +select t,strict_word_similarity('Baykal',t) as sml from test_trgm2 where t %>> 'Baykal' order by sml desc, t; +select t,strict_word_similarity('Kabankala',t) as sml from test_trgm2 where t %>> 'Kabankala' order by sml desc, t; + +set "pg_trgm.strict_word_similarity_threshold" to 0.2; +select t,strict_word_similarity('Baykal',t) as sml from test_trgm2 where 'Baykal' <<% t order by sml desc, t; +select t,strict_word_similarity('Kabankala',t) as sml from test_trgm2 where 'Kabankala' <<% t order by sml desc, t; +select t,strict_word_similarity('Baykal',t) as sml from test_trgm2 where t %>> 'Baykal' order by sml desc, t; +select t,strict_word_similarity('Kabankala',t) as sml from test_trgm2 where t %>> 'Kabankala' order by sml desc, t; diff --git a/contrib/pg_trgm/sql/pg_trgm.sql b/contrib/pg_trgm/sql/pg_trgm.sql new file mode 100644 index 0000000..6a9da24 --- /dev/null +++ b/contrib/pg_trgm/sql/pg_trgm.sql @@ -0,0 +1,238 @@ +CREATE EXTENSION pg_trgm; + +-- Check whether any of our opclasses fail amvalidate +SELECT amname, opcname +FROM pg_opclass opc LEFT JOIN pg_am am ON am.oid = opcmethod +WHERE opc.oid >= 16384 AND NOT amvalidate(opc.oid); + +--backslash is used in tests below, installcheck will fail if +--standard_conforming_string is off +set standard_conforming_strings=on; + +-- reduce noise +set extra_float_digits = 0; + +select show_trgm(''); +select show_trgm('(*&^$@%@'); +select show_trgm('a b c'); +select show_trgm(' a b c '); +select show_trgm('aA bB cC'); +select show_trgm(' aA bB cC '); +select show_trgm('a b C0*%^'); + +select similarity('wow','WOWa '); +select similarity('wow',' WOW '); + +select similarity('---', '####---'); + +CREATE TABLE test_trgm(t text COLLATE "C"); + +\copy test_trgm from 'data/trgm.data' + +select t,similarity(t,'qwertyu0988') as sml from test_trgm where t % 'qwertyu0988' order by sml desc, t; +select t,similarity(t,'gwertyu0988') as sml from test_trgm where t % 'gwertyu0988' order by sml desc, t; +select t,similarity(t,'gwertyu1988') as sml from test_trgm where t % 'gwertyu1988' order by sml desc, t; +select t <-> 'q0987wertyu0988', t from test_trgm order by t <-> 'q0987wertyu0988' limit 2; +select count(*) from test_trgm where t ~ '[qwerty]{2}-?[qwerty]{2}'; + +create index trgm_idx on test_trgm using gist (t gist_trgm_ops); +set enable_seqscan=off; + +select t,similarity(t,'qwertyu0988') as sml from test_trgm where t % 'qwertyu0988' order by sml desc, t; +select t,similarity(t,'gwertyu0988') as sml from test_trgm where t % 'gwertyu0988' order by sml desc, t; +select t,similarity(t,'gwertyu1988') as sml from test_trgm where t % 'gwertyu1988' order by sml desc, t; +explain (costs off) +select t <-> 'q0987wertyu0988', t from test_trgm order by t <-> 'q0987wertyu0988' limit 2; +select t <-> 'q0987wertyu0988', t from test_trgm order by t <-> 'q0987wertyu0988' limit 2; +select count(*) from test_trgm where t ~ '[qwerty]{2}-?[qwerty]{2}'; + +drop index trgm_idx; +create index trgm_idx on test_trgm using gist (t gist_trgm_ops(siglen=0)); +create index trgm_idx on test_trgm using gist (t gist_trgm_ops(siglen=2025)); +create index trgm_idx on test_trgm using gist (t gist_trgm_ops(siglen=2024)); +set enable_seqscan=off; + +select t,similarity(t,'qwertyu0988') as sml from test_trgm where t % 'qwertyu0988' order by sml desc, t; +select t,similarity(t,'gwertyu0988') as sml from test_trgm where t % 'gwertyu0988' order by sml desc, t; +select t,similarity(t,'gwertyu1988') as sml from test_trgm where t % 'gwertyu1988' order by sml desc, t; +explain (costs off) +select t <-> 'q0987wertyu0988', t from test_trgm order by t <-> 'q0987wertyu0988' limit 2; +select t <-> 'q0987wertyu0988', t from test_trgm order by t <-> 'q0987wertyu0988' limit 2; +select count(*) from test_trgm where t ~ '[qwerty]{2}-?[qwerty]{2}'; + +drop index trgm_idx; +create index trgm_idx on test_trgm using gin (t gin_trgm_ops); +set enable_seqscan=off; + +select t,similarity(t,'qwertyu0988') as sml from test_trgm where t % 'qwertyu0988' order by sml desc, t; +select t,similarity(t,'gwertyu0988') as sml from test_trgm where t % 'gwertyu0988' order by sml desc, t; +select t,similarity(t,'gwertyu1988') as sml from test_trgm where t % 'gwertyu1988' order by sml desc, t; +select count(*) from test_trgm where t ~ '[qwerty]{2}-?[qwerty]{2}'; + +-- check handling of indexquals that generate no searchable conditions +explain (costs off) +select count(*) from test_trgm where t like '%99%' and t like '%qwerty%'; +select count(*) from test_trgm where t like '%99%' and t like '%qwerty%'; +explain (costs off) +select count(*) from test_trgm where t like '%99%' and t like '%qw%'; +select count(*) from test_trgm where t like '%99%' and t like '%qw%'; +-- ensure that pending-list items are handled correctly, too +create temp table t_test_trgm(t text COLLATE "C"); +create index t_trgm_idx on t_test_trgm using gin (t gin_trgm_ops); +insert into t_test_trgm values ('qwerty99'), ('qwerty01'); +explain (costs off) +select count(*) from t_test_trgm where t like '%99%' and t like '%qwerty%'; +select count(*) from t_test_trgm where t like '%99%' and t like '%qwerty%'; +explain (costs off) +select count(*) from t_test_trgm where t like '%99%' and t like '%qw%'; +select count(*) from t_test_trgm where t like '%99%' and t like '%qw%'; + +-- run the same queries with sequential scan to check the results +set enable_bitmapscan=off; +set enable_seqscan=on; +select count(*) from test_trgm where t like '%99%' and t like '%qwerty%'; +select count(*) from test_trgm where t like '%99%' and t like '%qw%'; +select count(*) from t_test_trgm where t like '%99%' and t like '%qwerty%'; +select count(*) from t_test_trgm where t like '%99%' and t like '%qw%'; +reset enable_bitmapscan; + +create table test2(t text COLLATE "C"); +insert into test2 values ('abcdef'); +insert into test2 values ('quark'); +insert into test2 values (' z foo bar'); +insert into test2 values ('/123/-45/'); +insert into test2 values ('line 1'); +insert into test2 values ('%line 2'); +insert into test2 values ('line 3%'); +insert into test2 values ('%line 4%'); +insert into test2 values ('%li%ne 5%'); +insert into test2 values ('li_e 6'); +create index test2_idx_gin on test2 using gin (t gin_trgm_ops); +set enable_seqscan=off; +explain (costs off) + select * from test2 where t like '%BCD%'; +explain (costs off) + select * from test2 where t ilike '%BCD%'; +select * from test2 where t like '%BCD%'; +select * from test2 where t like '%bcd%'; +select * from test2 where t like E'%\\bcd%'; +select * from test2 where t ilike '%BCD%'; +select * from test2 where t ilike 'qua%'; +select * from test2 where t like '%z foo bar%'; +select * from test2 where t like ' z foo%'; +explain (costs off) + select * from test2 where t ~ '[abc]{3}'; +explain (costs off) + select * from test2 where t ~* 'DEF'; +select * from test2 where t ~ '[abc]{3}'; +select * from test2 where t ~ 'a[bc]+d'; +select * from test2 where t ~ '(abc)*$'; +select * from test2 where t ~* 'DEF'; +select * from test2 where t ~ 'dEf'; +select * from test2 where t ~* '^q'; +select * from test2 where t ~* '[abc]{3}[def]{3}'; +select * from test2 where t ~* 'ab[a-z]{3}'; +select * from test2 where t ~* '(^| )qua'; +select * from test2 where t ~ 'q.*rk$'; +select * from test2 where t ~ 'q'; +select * from test2 where t ~ '[a-z]{3}'; +select * from test2 where t ~* '(a{10}|b{10}|c{10}){10}'; +select * from test2 where t ~ 'z foo bar'; +select * from test2 where t ~ ' z foo bar'; +select * from test2 where t ~ ' z foo bar'; +select * from test2 where t ~ ' z foo'; +select * from test2 where t ~ 'qua(?!foo)'; +select * from test2 where t ~ '/\d+/-\d'; +-- test = operator +explain (costs off) + select * from test2 where t = 'abcdef'; +select * from test2 where t = 'abcdef'; +explain (costs off) + select * from test2 where t = '%line%'; +select * from test2 where t = '%line%'; +select * from test2 where t = 'li_e 1'; +select * from test2 where t = '%line 2'; +select * from test2 where t = 'line 3%'; +select * from test2 where t = '%line 3%'; +select * from test2 where t = '%line 4%'; +select * from test2 where t = '%line 5%'; +select * from test2 where t = '%li_ne 5%'; +select * from test2 where t = '%li%ne 5%'; +select * from test2 where t = 'line 6'; +select * from test2 where t = 'li_e 6'; +drop index test2_idx_gin; + +create index test2_idx_gist on test2 using gist (t gist_trgm_ops); +set enable_seqscan=off; +explain (costs off) + select * from test2 where t like '%BCD%'; +explain (costs off) + select * from test2 where t ilike '%BCD%'; +select * from test2 where t like '%BCD%'; +select * from test2 where t like '%bcd%'; +select * from test2 where t like E'%\\bcd%'; +select * from test2 where t ilike '%BCD%'; +select * from test2 where t ilike 'qua%'; +select * from test2 where t like '%z foo bar%'; +select * from test2 where t like ' z foo%'; +explain (costs off) + select * from test2 where t ~ '[abc]{3}'; +explain (costs off) + select * from test2 where t ~* 'DEF'; +select * from test2 where t ~ '[abc]{3}'; +select * from test2 where t ~ 'a[bc]+d'; +select * from test2 where t ~ '(abc)*$'; +select * from test2 where t ~* 'DEF'; +select * from test2 where t ~ 'dEf'; +select * from test2 where t ~* '^q'; +select * from test2 where t ~* '[abc]{3}[def]{3}'; +select * from test2 where t ~* 'ab[a-z]{3}'; +select * from test2 where t ~* '(^| )qua'; +select * from test2 where t ~ 'q.*rk$'; +select * from test2 where t ~ 'q'; +select * from test2 where t ~ '[a-z]{3}'; +select * from test2 where t ~* '(a{10}|b{10}|c{10}){10}'; +select * from test2 where t ~ 'z foo bar'; +select * from test2 where t ~ ' z foo bar'; +select * from test2 where t ~ ' z foo bar'; +select * from test2 where t ~ ' z foo'; +select * from test2 where t ~ 'qua(?!foo)'; +select * from test2 where t ~ '/\d+/-\d'; +-- test = operator +explain (costs off) + select * from test2 where t = 'abcdef'; +select * from test2 where t = 'abcdef'; +explain (costs off) + select * from test2 where t = '%line%'; +select * from test2 where t = '%line%'; +select * from test2 where t = 'li_e 1'; +select * from test2 where t = '%line 2'; +select * from test2 where t = 'line 3%'; +select * from test2 where t = '%line 3%'; +select * from test2 where t = '%line 4%'; +select * from test2 where t = '%line 5%'; +select * from test2 where t = '%li_ne 5%'; +select * from test2 where t = '%li%ne 5%'; +select * from test2 where t = 'line 6'; +select * from test2 where t = 'li_e 6'; + +-- Check similarity threshold (bug #14202) + +CREATE TEMP TABLE restaurants (city text); +INSERT INTO restaurants SELECT 'Warsaw' FROM generate_series(1, 10000); +INSERT INTO restaurants SELECT 'Szczecin' FROM generate_series(1, 10000); +CREATE INDEX ON restaurants USING gist(city gist_trgm_ops); + +-- Similarity of the two names (for reference). +SELECT similarity('Szczecin', 'Warsaw'); + +-- Should get only 'Warsaw' for either setting of set_limit. +EXPLAIN (COSTS OFF) +SELECT DISTINCT city, similarity(city, 'Warsaw'), show_limit() + FROM restaurants WHERE city % 'Warsaw'; +SELECT set_limit(0.3); +SELECT DISTINCT city, similarity(city, 'Warsaw'), show_limit() + FROM restaurants WHERE city % 'Warsaw'; +SELECT set_limit(0.5); +SELECT DISTINCT city, similarity(city, 'Warsaw'), show_limit() + FROM restaurants WHERE city % 'Warsaw'; diff --git a/contrib/pg_trgm/sql/pg_word_trgm.sql b/contrib/pg_trgm/sql/pg_word_trgm.sql new file mode 100644 index 0000000..d2ada49 --- /dev/null +++ b/contrib/pg_trgm/sql/pg_word_trgm.sql @@ -0,0 +1,48 @@ +CREATE TABLE test_trgm2(t text COLLATE "C"); + +\copy test_trgm2 from 'data/trgm2.data' + +-- reduce noise +set extra_float_digits = 0; + +select t,word_similarity('Baykal',t) as sml from test_trgm2 where 'Baykal' <% t order by sml desc, t; +select t,word_similarity('Kabankala',t) as sml from test_trgm2 where 'Kabankala' <% t order by sml desc, t; +select t,word_similarity('Baykal',t) as sml from test_trgm2 where t %> 'Baykal' order by sml desc, t; +select t,word_similarity('Kabankala',t) as sml from test_trgm2 where t %> 'Kabankala' order by sml desc, t; +select t <->> 'Kabankala', t from test_trgm2 order by t <->> 'Kabankala' limit 7; + +create index trgm_idx2 on test_trgm2 using gist (t gist_trgm_ops); +set enable_seqscan=off; + +select t,word_similarity('Baykal',t) as sml from test_trgm2 where 'Baykal' <% t order by sml desc, t; +select t,word_similarity('Kabankala',t) as sml from test_trgm2 where 'Kabankala' <% t order by sml desc, t; +select t,word_similarity('Baykal',t) as sml from test_trgm2 where t %> 'Baykal' order by sml desc, t; +select t,word_similarity('Kabankala',t) as sml from test_trgm2 where t %> 'Kabankala' order by sml desc, t; + +explain (costs off) +select t <->> 'Kabankala', t from test_trgm2 order by t <->> 'Kabankala' limit 7; +select t <->> 'Kabankala', t from test_trgm2 order by t <->> 'Kabankala' limit 7; + +drop index trgm_idx2; +create index trgm_idx2 on test_trgm2 using gin (t gin_trgm_ops); +set enable_seqscan=off; + +select t,word_similarity('Baykal',t) as sml from test_trgm2 where 'Baykal' <% t order by sml desc, t; +select t,word_similarity('Kabankala',t) as sml from test_trgm2 where 'Kabankala' <% t order by sml desc, t; +select t,word_similarity('Baykal',t) as sml from test_trgm2 where t %> 'Baykal' order by sml desc, t; +select t,word_similarity('Kabankala',t) as sml from test_trgm2 where t %> 'Kabankala' order by sml desc, t; + +set "pg_trgm.word_similarity_threshold" to 0.5; +select t,word_similarity('Baykal',t) as sml from test_trgm2 where 'Baykal' <% t order by sml desc, t; +select t,word_similarity('Kabankala',t) as sml from test_trgm2 where 'Kabankala' <% t order by sml desc, t; +select t,word_similarity('Baykal',t) as sml from test_trgm2 where t %> 'Baykal' order by sml desc, t; +select t,word_similarity('Kabankala',t) as sml from test_trgm2 where t %> 'Kabankala' order by sml desc, t; + +set "pg_trgm.word_similarity_threshold" to 0.3; +select t,word_similarity('Baykal',t) as sml from test_trgm2 where 'Baykal' <% t order by sml desc, t; +select t,word_similarity('Kabankala',t) as sml from test_trgm2 where 'Kabankala' <% t order by sml desc, t; +select t,word_similarity('Baykal',t) as sml from test_trgm2 where t %> 'Baykal' order by sml desc, t; +select t,word_similarity('Kabankala',t) as sml from test_trgm2 where t %> 'Kabankala' order by sml desc, t; + +-- test unsatisfiable pattern +select * from test_trgm2 where t ~ '.*$x'; diff --git a/contrib/pg_trgm/trgm.h b/contrib/pg_trgm/trgm.h new file mode 100644 index 0000000..afb0adb --- /dev/null +++ b/contrib/pg_trgm/trgm.h @@ -0,0 +1,140 @@ +/* + * contrib/pg_trgm/trgm.h + */ +#ifndef __TRGM_H__ +#define __TRGM_H__ + +#include "access/gist.h" +#include "access/itup.h" +#include "access/stratnum.h" +#include "storage/bufpage.h" + +/* + * Options ... but note that trgm_regexp.c effectively assumes these values + * of LPADDING and RPADDING. + */ +#define LPADDING 2 +#define RPADDING 1 +#define KEEPONLYALNUM +/* + * Caution: IGNORECASE macro means that trigrams are case-insensitive. + * If this macro is disabled, the ~* and ~~* operators must be removed from + * the operator classes, because we can't handle case-insensitive wildcard + * search with case-sensitive trigrams. Failure to do this will result in + * "cannot handle ~*(~~*) with case-sensitive trigrams" errors. + */ +#define IGNORECASE +#define DIVUNION + +/* operator strategy numbers */ +#define SimilarityStrategyNumber 1 +#define DistanceStrategyNumber 2 +#define LikeStrategyNumber 3 +#define ILikeStrategyNumber 4 +#define RegExpStrategyNumber 5 +#define RegExpICaseStrategyNumber 6 +#define WordSimilarityStrategyNumber 7 +#define WordDistanceStrategyNumber 8 +#define StrictWordSimilarityStrategyNumber 9 +#define StrictWordDistanceStrategyNumber 10 +#define EqualStrategyNumber 11 + +typedef char trgm[3]; + +#define CMPCHAR(a,b) ( ((a)==(b)) ? 0 : ( ((a)<(b)) ? -1 : 1 ) ) +#define CMPPCHAR(a,b,i) CMPCHAR( *(((const char*)(a))+i), *(((const char*)(b))+i) ) +#define CMPTRGM(a,b) ( CMPPCHAR(a,b,0) ? CMPPCHAR(a,b,0) : ( CMPPCHAR(a,b,1) ? CMPPCHAR(a,b,1) : CMPPCHAR(a,b,2) ) ) + +#define CPTRGM(a,b) do { \ + *(((char*)(a))+0) = *(((char*)(b))+0); \ + *(((char*)(a))+1) = *(((char*)(b))+1); \ + *(((char*)(a))+2) = *(((char*)(b))+2); \ +} while(0) + +#ifdef KEEPONLYALNUM +#define ISWORDCHR(c) (t_isalnum(c)) +#define ISPRINTABLECHAR(a) ( isascii( *(unsigned char*)(a) ) && (isalnum( *(unsigned char*)(a) ) || *(unsigned char*)(a)==' ') ) +#else +#define ISWORDCHR(c) (!t_isspace(c)) +#define ISPRINTABLECHAR(a) ( isascii( *(unsigned char*)(a) ) && isprint( *(unsigned char*)(a) ) ) +#endif +#define ISPRINTABLETRGM(t) ( ISPRINTABLECHAR( ((char*)(t)) ) && ISPRINTABLECHAR( ((char*)(t))+1 ) && ISPRINTABLECHAR( ((char*)(t))+2 ) ) + +#define ISESCAPECHAR(x) (*(x) == '\\') /* Wildcard escape character */ +#define ISWILDCARDCHAR(x) (*(x) == '_' || *(x) == '%') /* Wildcard + * meta-character */ + +typedef struct +{ + int32 vl_len_; /* varlena header (do not touch directly!) */ + uint8 flag; + char data[FLEXIBLE_ARRAY_MEMBER]; +} TRGM; + +#define TRGMHDRSIZE (VARHDRSZ + sizeof(uint8)) + +/* gist */ +#define SIGLEN_DEFAULT (sizeof(int) * 3) +#define SIGLEN_MAX GISTMaxIndexKeySize +#define BITBYTE 8 + +#define SIGLENBIT(siglen) ((siglen) * BITBYTE - 1) /* see makesign */ + +typedef char *BITVECP; + +#define LOOPBYTE(siglen) \ + for (i = 0; i < (siglen); i++) + +#define GETBYTE(x,i) ( *( (BITVECP)(x) + (int)( (i) / BITBYTE ) ) ) +#define GETBITBYTE(x,i) ( (((char)(x)) >> (i)) & 0x01 ) +#define CLRBIT(x,i) GETBYTE(x,i) &= ~( 0x01 << ( (i) % BITBYTE ) ) +#define SETBIT(x,i) GETBYTE(x,i) |= ( 0x01 << ( (i) % BITBYTE ) ) +#define GETBIT(x,i) ( (GETBYTE(x,i) >> ( (i) % BITBYTE )) & 0x01 ) + +#define HASHVAL(val, siglen) (((unsigned int)(val)) % SIGLENBIT(siglen)) +#define HASH(sign, val, siglen) SETBIT((sign), HASHVAL(val, siglen)) + +#define ARRKEY 0x01 +#define SIGNKEY 0x02 +#define ALLISTRUE 0x04 + +#define ISARRKEY(x) ( ((TRGM*)x)->flag & ARRKEY ) +#define ISSIGNKEY(x) ( ((TRGM*)x)->flag & SIGNKEY ) +#define ISALLTRUE(x) ( ((TRGM*)x)->flag & ALLISTRUE ) + +#define CALCGTSIZE(flag, len) ( TRGMHDRSIZE + ( ( (flag) & ARRKEY ) ? ((len)*sizeof(trgm)) : (((flag) & ALLISTRUE) ? 0 : (len)) ) ) +#define GETSIGN(x) ( (BITVECP)( (char*)x+TRGMHDRSIZE ) ) +#define GETARR(x) ( (trgm*)( (char*)x+TRGMHDRSIZE ) ) +#define ARRNELEM(x) ( ( VARSIZE(x) - TRGMHDRSIZE )/sizeof(trgm) ) + +/* + * If DIVUNION is defined then similarity formula is: + * count / (len1 + len2 - count) + * else if DIVUNION is not defined then similarity formula is: + * count / max(len1, len2) + */ +#ifdef DIVUNION +#define CALCSML(count, len1, len2) ((float4) (count)) / ((float4) ((len1) + (len2) - (count))) +#else +#define CALCSML(count, len1, len2) ((float4) (count)) / ((float4) (((len1) > (len2)) ? (len1) : (len2))) +#endif + +typedef struct TrgmPackedGraph TrgmPackedGraph; + +extern double similarity_threshold; +extern double word_similarity_threshold; +extern double strict_word_similarity_threshold; + +extern double index_strategy_get_limit(StrategyNumber strategy); +extern uint32 trgm2int(trgm *ptr); +extern void compact_trigram(trgm *tptr, char *str, int bytelen); +extern TRGM *generate_trgm(char *str, int slen); +extern TRGM *generate_wildcard_trgm(const char *str, int slen); +extern float4 cnt_sml(TRGM *trg1, TRGM *trg2, bool inexact); +extern bool trgm_contained_by(TRGM *trg1, TRGM *trg2); +extern bool *trgm_presence_map(TRGM *query, TRGM *key); +extern TRGM *createTrgmNFA(text *text_re, Oid collation, + TrgmPackedGraph **graph, MemoryContext rcontext); +extern bool trigramsMatchGraph(TrgmPackedGraph *graph, bool *check); + +#endif /* __TRGM_H__ */ diff --git a/contrib/pg_trgm/trgm_gin.c b/contrib/pg_trgm/trgm_gin.c new file mode 100644 index 0000000..29a52ea --- /dev/null +++ b/contrib/pg_trgm/trgm_gin.c @@ -0,0 +1,360 @@ +/* + * contrib/pg_trgm/trgm_gin.c + */ +#include "postgres.h" + +#include "access/gin.h" +#include "access/stratnum.h" +#include "fmgr.h" +#include "trgm.h" +#include "varatt.h" + +PG_FUNCTION_INFO_V1(gin_extract_trgm); +PG_FUNCTION_INFO_V1(gin_extract_value_trgm); +PG_FUNCTION_INFO_V1(gin_extract_query_trgm); +PG_FUNCTION_INFO_V1(gin_trgm_consistent); +PG_FUNCTION_INFO_V1(gin_trgm_triconsistent); + +/* + * This function can only be called if a pre-9.1 version of the GIN operator + * class definition is present in the catalogs (probably as a consequence + * of upgrade-in-place). Cope. + */ +Datum +gin_extract_trgm(PG_FUNCTION_ARGS) +{ + if (PG_NARGS() == 3) + return gin_extract_value_trgm(fcinfo); + if (PG_NARGS() == 7) + return gin_extract_query_trgm(fcinfo); + elog(ERROR, "unexpected number of arguments to gin_extract_trgm"); + PG_RETURN_NULL(); +} + +Datum +gin_extract_value_trgm(PG_FUNCTION_ARGS) +{ + text *val = (text *) PG_GETARG_TEXT_PP(0); + int32 *nentries = (int32 *) PG_GETARG_POINTER(1); + Datum *entries = NULL; + TRGM *trg; + int32 trglen; + + *nentries = 0; + + trg = generate_trgm(VARDATA_ANY(val), VARSIZE_ANY_EXHDR(val)); + trglen = ARRNELEM(trg); + + if (trglen > 0) + { + trgm *ptr; + int32 i; + + *nentries = trglen; + entries = (Datum *) palloc(sizeof(Datum) * trglen); + + ptr = GETARR(trg); + for (i = 0; i < trglen; i++) + { + int32 item = trgm2int(ptr); + + entries[i] = Int32GetDatum(item); + ptr++; + } + } + + PG_RETURN_POINTER(entries); +} + +Datum +gin_extract_query_trgm(PG_FUNCTION_ARGS) +{ + text *val = (text *) PG_GETARG_TEXT_PP(0); + int32 *nentries = (int32 *) PG_GETARG_POINTER(1); + StrategyNumber strategy = PG_GETARG_UINT16(2); + + /* bool **pmatch = (bool **) PG_GETARG_POINTER(3); */ + Pointer **extra_data = (Pointer **) PG_GETARG_POINTER(4); + + /* bool **nullFlags = (bool **) PG_GETARG_POINTER(5); */ + int32 *searchMode = (int32 *) PG_GETARG_POINTER(6); + Datum *entries = NULL; + TRGM *trg; + int32 trglen; + trgm *ptr; + TrgmPackedGraph *graph; + int32 i; + + switch (strategy) + { + case SimilarityStrategyNumber: + case WordSimilarityStrategyNumber: + case StrictWordSimilarityStrategyNumber: + case EqualStrategyNumber: + trg = generate_trgm(VARDATA_ANY(val), VARSIZE_ANY_EXHDR(val)); + break; + case ILikeStrategyNumber: +#ifndef IGNORECASE + elog(ERROR, "cannot handle ~~* with case-sensitive trigrams"); +#endif + /* FALL THRU */ + case LikeStrategyNumber: + + /* + * For wildcard search we extract all the trigrams that every + * potentially-matching string must include. + */ + trg = generate_wildcard_trgm(VARDATA_ANY(val), + VARSIZE_ANY_EXHDR(val)); + break; + case RegExpICaseStrategyNumber: +#ifndef IGNORECASE + elog(ERROR, "cannot handle ~* with case-sensitive trigrams"); +#endif + /* FALL THRU */ + case RegExpStrategyNumber: + trg = createTrgmNFA(val, PG_GET_COLLATION(), + &graph, CurrentMemoryContext); + if (trg && ARRNELEM(trg) > 0) + { + /* + * Successful regex processing: store NFA-like graph as + * extra_data. GIN API requires an array of nentries + * Pointers, but we just put the same value in each element. + */ + trglen = ARRNELEM(trg); + *extra_data = (Pointer *) palloc(sizeof(Pointer) * trglen); + for (i = 0; i < trglen; i++) + (*extra_data)[i] = (Pointer) graph; + } + else + { + /* No result: have to do full index scan. */ + *nentries = 0; + *searchMode = GIN_SEARCH_MODE_ALL; + PG_RETURN_POINTER(entries); + } + break; + default: + elog(ERROR, "unrecognized strategy number: %d", strategy); + trg = NULL; /* keep compiler quiet */ + break; + } + + trglen = ARRNELEM(trg); + *nentries = trglen; + + if (trglen > 0) + { + entries = (Datum *) palloc(sizeof(Datum) * trglen); + ptr = GETARR(trg); + for (i = 0; i < trglen; i++) + { + int32 item = trgm2int(ptr); + + entries[i] = Int32GetDatum(item); + ptr++; + } + } + + /* + * If no trigram was extracted then we have to scan all the index. + */ + if (trglen == 0) + *searchMode = GIN_SEARCH_MODE_ALL; + + PG_RETURN_POINTER(entries); +} + +Datum +gin_trgm_consistent(PG_FUNCTION_ARGS) +{ + bool *check = (bool *) PG_GETARG_POINTER(0); + StrategyNumber strategy = PG_GETARG_UINT16(1); + + /* text *query = PG_GETARG_TEXT_PP(2); */ + int32 nkeys = PG_GETARG_INT32(3); + Pointer *extra_data = (Pointer *) PG_GETARG_POINTER(4); + bool *recheck = (bool *) PG_GETARG_POINTER(5); + bool res; + int32 i, + ntrue; + double nlimit; + + /* All cases served by this function are inexact */ + *recheck = true; + + switch (strategy) + { + case SimilarityStrategyNumber: + case WordSimilarityStrategyNumber: + case StrictWordSimilarityStrategyNumber: + nlimit = index_strategy_get_limit(strategy); + + /* Count the matches */ + ntrue = 0; + for (i = 0; i < nkeys; i++) + { + if (check[i]) + ntrue++; + } + + /*-------------------- + * If DIVUNION is defined then similarity formula is: + * c / (len1 + len2 - c) + * where c is number of common trigrams and it stands as ntrue in + * this code. Here we don't know value of len2 but we can assume + * that c (ntrue) is a lower bound of len2, so upper bound of + * similarity is: + * c / (len1 + c - c) => c / len1 + * If DIVUNION is not defined then similarity formula is: + * c / max(len1, len2) + * And again, c (ntrue) is a lower bound of len2, but c <= len1 + * just by definition and, consequently, upper bound of + * similarity is just c / len1. + * So, independently on DIVUNION the upper bound formula is the same. + */ + res = (nkeys == 0) ? false : + (((((float4) ntrue) / ((float4) nkeys))) >= nlimit); + break; + case ILikeStrategyNumber: +#ifndef IGNORECASE + elog(ERROR, "cannot handle ~~* with case-sensitive trigrams"); +#endif + /* FALL THRU */ + case LikeStrategyNumber: + case EqualStrategyNumber: + /* Check if all extracted trigrams are presented. */ + res = true; + for (i = 0; i < nkeys; i++) + { + if (!check[i]) + { + res = false; + break; + } + } + break; + case RegExpICaseStrategyNumber: +#ifndef IGNORECASE + elog(ERROR, "cannot handle ~* with case-sensitive trigrams"); +#endif + /* FALL THRU */ + case RegExpStrategyNumber: + if (nkeys < 1) + { + /* Regex processing gave no result: do full index scan */ + res = true; + } + else + res = trigramsMatchGraph((TrgmPackedGraph *) extra_data[0], + check); + break; + default: + elog(ERROR, "unrecognized strategy number: %d", strategy); + res = false; /* keep compiler quiet */ + break; + } + + PG_RETURN_BOOL(res); +} + +/* + * In all cases, GIN_TRUE is at least as favorable to inclusion as + * GIN_MAYBE. If no better option is available, simply treat + * GIN_MAYBE as if it were GIN_TRUE and apply the same test as the binary + * consistent function. + */ +Datum +gin_trgm_triconsistent(PG_FUNCTION_ARGS) +{ + GinTernaryValue *check = (GinTernaryValue *) PG_GETARG_POINTER(0); + StrategyNumber strategy = PG_GETARG_UINT16(1); + + /* text *query = PG_GETARG_TEXT_PP(2); */ + int32 nkeys = PG_GETARG_INT32(3); + Pointer *extra_data = (Pointer *) PG_GETARG_POINTER(4); + GinTernaryValue res = GIN_MAYBE; + int32 i, + ntrue; + bool *boolcheck; + double nlimit; + + switch (strategy) + { + case SimilarityStrategyNumber: + case WordSimilarityStrategyNumber: + case StrictWordSimilarityStrategyNumber: + nlimit = index_strategy_get_limit(strategy); + + /* Count the matches */ + ntrue = 0; + for (i = 0; i < nkeys; i++) + { + if (check[i] != GIN_FALSE) + ntrue++; + } + + /* + * See comment in gin_trgm_consistent() about * upper bound + * formula + */ + res = (nkeys == 0) + ? GIN_FALSE : (((((float4) ntrue) / ((float4) nkeys)) >= nlimit) + ? GIN_MAYBE : GIN_FALSE); + break; + case ILikeStrategyNumber: +#ifndef IGNORECASE + elog(ERROR, "cannot handle ~~* with case-sensitive trigrams"); +#endif + /* FALL THRU */ + case LikeStrategyNumber: + case EqualStrategyNumber: + /* Check if all extracted trigrams are presented. */ + res = GIN_MAYBE; + for (i = 0; i < nkeys; i++) + { + if (check[i] == GIN_FALSE) + { + res = GIN_FALSE; + break; + } + } + break; + case RegExpICaseStrategyNumber: +#ifndef IGNORECASE + elog(ERROR, "cannot handle ~* with case-sensitive trigrams"); +#endif + /* FALL THRU */ + case RegExpStrategyNumber: + if (nkeys < 1) + { + /* Regex processing gave no result: do full index scan */ + res = GIN_MAYBE; + } + else + { + /* + * As trigramsMatchGraph implements a monotonic boolean + * function, promoting all GIN_MAYBE keys to GIN_TRUE will + * give a conservative result. + */ + boolcheck = (bool *) palloc(sizeof(bool) * nkeys); + for (i = 0; i < nkeys; i++) + boolcheck[i] = (check[i] != GIN_FALSE); + if (!trigramsMatchGraph((TrgmPackedGraph *) extra_data[0], + boolcheck)) + res = GIN_FALSE; + pfree(boolcheck); + } + break; + default: + elog(ERROR, "unrecognized strategy number: %d", strategy); + res = GIN_FALSE; /* keep compiler quiet */ + break; + } + + /* All cases served by this function are inexact */ + Assert(res != GIN_TRUE); + PG_RETURN_GIN_TERNARY_VALUE(res); +} diff --git a/contrib/pg_trgm/trgm_gist.c b/contrib/pg_trgm/trgm_gist.c new file mode 100644 index 0000000..9ef2e38 --- /dev/null +++ b/contrib/pg_trgm/trgm_gist.c @@ -0,0 +1,971 @@ +/* + * contrib/pg_trgm/trgm_gist.c + */ +#include "postgres.h" + +#include "access/reloptions.h" +#include "access/stratnum.h" +#include "fmgr.h" +#include "port/pg_bitutils.h" +#include "trgm.h" +#include "varatt.h" + +/* gist_trgm_ops opclass options */ +typedef struct +{ + int32 vl_len_; /* varlena header (do not touch directly!) */ + int siglen; /* signature length in bytes */ +} TrgmGistOptions; + +#define GET_SIGLEN() (PG_HAS_OPCLASS_OPTIONS() ? \ + ((TrgmGistOptions *) PG_GET_OPCLASS_OPTIONS())->siglen : \ + SIGLEN_DEFAULT) + +typedef struct +{ + /* most recent inputs to gtrgm_consistent */ + StrategyNumber strategy; + text *query; + /* extracted trigrams for query */ + TRGM *trigrams; + /* if a regex operator, the extracted graph */ + TrgmPackedGraph *graph; + + /* + * The "query" and "trigrams" are stored in the same palloc block as this + * cache struct, at MAXALIGN'ed offsets. The graph however isn't. + */ +} gtrgm_consistent_cache; + +#define GETENTRY(vec,pos) ((TRGM *) DatumGetPointer((vec)->vector[(pos)].key)) + + +PG_FUNCTION_INFO_V1(gtrgm_in); +PG_FUNCTION_INFO_V1(gtrgm_out); +PG_FUNCTION_INFO_V1(gtrgm_compress); +PG_FUNCTION_INFO_V1(gtrgm_decompress); +PG_FUNCTION_INFO_V1(gtrgm_consistent); +PG_FUNCTION_INFO_V1(gtrgm_distance); +PG_FUNCTION_INFO_V1(gtrgm_union); +PG_FUNCTION_INFO_V1(gtrgm_same); +PG_FUNCTION_INFO_V1(gtrgm_penalty); +PG_FUNCTION_INFO_V1(gtrgm_picksplit); +PG_FUNCTION_INFO_V1(gtrgm_options); + + +Datum +gtrgm_in(PG_FUNCTION_ARGS) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot accept a value of type %s", "gtrgm"))); + + PG_RETURN_VOID(); /* keep compiler quiet */ +} + +Datum +gtrgm_out(PG_FUNCTION_ARGS) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot display a value of type %s", "gtrgm"))); + + PG_RETURN_VOID(); /* keep compiler quiet */ +} + +static TRGM * +gtrgm_alloc(bool isalltrue, int siglen, BITVECP sign) +{ + int flag = SIGNKEY | (isalltrue ? ALLISTRUE : 0); + int size = CALCGTSIZE(flag, siglen); + TRGM *res = palloc(size); + + SET_VARSIZE(res, size); + res->flag = flag; + + if (!isalltrue) + { + if (sign) + memcpy(GETSIGN(res), sign, siglen); + else + memset(GETSIGN(res), 0, siglen); + } + + return res; +} + +static void +makesign(BITVECP sign, TRGM *a, int siglen) +{ + int32 k, + len = ARRNELEM(a); + trgm *ptr = GETARR(a); + int32 tmp = 0; + + MemSet(sign, 0, siglen); + SETBIT(sign, SIGLENBIT(siglen)); /* set last unused bit */ + for (k = 0; k < len; k++) + { + CPTRGM(((char *) &tmp), ptr + k); + HASH(sign, tmp, siglen); + } +} + +Datum +gtrgm_compress(PG_FUNCTION_ARGS) +{ + GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); + int siglen = GET_SIGLEN(); + GISTENTRY *retval = entry; + + if (entry->leafkey) + { /* trgm */ + TRGM *res; + text *val = DatumGetTextPP(entry->key); + + res = generate_trgm(VARDATA_ANY(val), VARSIZE_ANY_EXHDR(val)); + retval = (GISTENTRY *) palloc(sizeof(GISTENTRY)); + gistentryinit(*retval, PointerGetDatum(res), + entry->rel, entry->page, + entry->offset, false); + } + else if (ISSIGNKEY(DatumGetPointer(entry->key)) && + !ISALLTRUE(DatumGetPointer(entry->key))) + { + int32 i; + TRGM *res; + BITVECP sign = GETSIGN(DatumGetPointer(entry->key)); + + LOOPBYTE(siglen) + { + if ((sign[i] & 0xff) != 0xff) + PG_RETURN_POINTER(retval); + } + + res = gtrgm_alloc(true, siglen, sign); + retval = (GISTENTRY *) palloc(sizeof(GISTENTRY)); + gistentryinit(*retval, PointerGetDatum(res), + entry->rel, entry->page, + entry->offset, false); + } + PG_RETURN_POINTER(retval); +} + +Datum +gtrgm_decompress(PG_FUNCTION_ARGS) +{ + GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); + GISTENTRY *retval; + text *key; + + key = DatumGetTextPP(entry->key); + + if (key != (text *) DatumGetPointer(entry->key)) + { + /* need to pass back the decompressed item */ + retval = palloc(sizeof(GISTENTRY)); + gistentryinit(*retval, PointerGetDatum(key), + entry->rel, entry->page, entry->offset, entry->leafkey); + PG_RETURN_POINTER(retval); + } + else + { + /* we can return the entry as-is */ + PG_RETURN_POINTER(entry); + } +} + +static int32 +cnt_sml_sign_common(TRGM *qtrg, BITVECP sign, int siglen) +{ + int32 count = 0; + int32 k, + len = ARRNELEM(qtrg); + trgm *ptr = GETARR(qtrg); + int32 tmp = 0; + + for (k = 0; k < len; k++) + { + CPTRGM(((char *) &tmp), ptr + k); + count += GETBIT(sign, HASHVAL(tmp, siglen)); + } + + return count; +} + +Datum +gtrgm_consistent(PG_FUNCTION_ARGS) +{ + GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); + text *query = PG_GETARG_TEXT_P(1); + StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2); + + /* Oid subtype = PG_GETARG_OID(3); */ + bool *recheck = (bool *) PG_GETARG_POINTER(4); + int siglen = GET_SIGLEN(); + TRGM *key = (TRGM *) DatumGetPointer(entry->key); + TRGM *qtrg; + bool res; + Size querysize = VARSIZE(query); + gtrgm_consistent_cache *cache; + double nlimit; + + /* + * We keep the extracted trigrams in cache, because trigram extraction is + * relatively CPU-expensive. When trying to reuse a cached value, check + * strategy number not just query itself, because trigram extraction + * depends on strategy. + * + * The cached structure is a single palloc chunk containing the + * gtrgm_consistent_cache header, then the input query (4-byte length + * word, uncompressed, starting at a MAXALIGN boundary), then the TRGM + * value (also starting at a MAXALIGN boundary). However we don't try to + * include the regex graph (if any) in that struct. (XXX currently, this + * approach can leak regex graphs across index rescans. Not clear if + * that's worth fixing.) + */ + cache = (gtrgm_consistent_cache *) fcinfo->flinfo->fn_extra; + if (cache == NULL || + cache->strategy != strategy || + VARSIZE(cache->query) != querysize || + memcmp((char *) cache->query, (char *) query, querysize) != 0) + { + gtrgm_consistent_cache *newcache; + TrgmPackedGraph *graph = NULL; + Size qtrgsize; + + switch (strategy) + { + case SimilarityStrategyNumber: + case WordSimilarityStrategyNumber: + case StrictWordSimilarityStrategyNumber: + case EqualStrategyNumber: + qtrg = generate_trgm(VARDATA(query), + querysize - VARHDRSZ); + break; + case ILikeStrategyNumber: +#ifndef IGNORECASE + elog(ERROR, "cannot handle ~~* with case-sensitive trigrams"); +#endif + /* FALL THRU */ + case LikeStrategyNumber: + qtrg = generate_wildcard_trgm(VARDATA(query), + querysize - VARHDRSZ); + break; + case RegExpICaseStrategyNumber: +#ifndef IGNORECASE + elog(ERROR, "cannot handle ~* with case-sensitive trigrams"); +#endif + /* FALL THRU */ + case RegExpStrategyNumber: + qtrg = createTrgmNFA(query, PG_GET_COLLATION(), + &graph, fcinfo->flinfo->fn_mcxt); + /* just in case an empty array is returned ... */ + if (qtrg && ARRNELEM(qtrg) <= 0) + { + pfree(qtrg); + qtrg = NULL; + } + break; + default: + elog(ERROR, "unrecognized strategy number: %d", strategy); + qtrg = NULL; /* keep compiler quiet */ + break; + } + + qtrgsize = qtrg ? VARSIZE(qtrg) : 0; + + newcache = (gtrgm_consistent_cache *) + MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, + MAXALIGN(sizeof(gtrgm_consistent_cache)) + + MAXALIGN(querysize) + + qtrgsize); + + newcache->strategy = strategy; + newcache->query = (text *) + ((char *) newcache + MAXALIGN(sizeof(gtrgm_consistent_cache))); + memcpy((char *) newcache->query, (char *) query, querysize); + if (qtrg) + { + newcache->trigrams = (TRGM *) + ((char *) newcache->query + MAXALIGN(querysize)); + memcpy((char *) newcache->trigrams, (char *) qtrg, qtrgsize); + /* release qtrg in case it was made in fn_mcxt */ + pfree(qtrg); + } + else + newcache->trigrams = NULL; + newcache->graph = graph; + + if (cache) + pfree(cache); + fcinfo->flinfo->fn_extra = (void *) newcache; + cache = newcache; + } + + qtrg = cache->trigrams; + + switch (strategy) + { + case SimilarityStrategyNumber: + case WordSimilarityStrategyNumber: + case StrictWordSimilarityStrategyNumber: + + /* + * Similarity search is exact. (Strict) word similarity search is + * inexact + */ + *recheck = (strategy != SimilarityStrategyNumber); + + nlimit = index_strategy_get_limit(strategy); + + if (GIST_LEAF(entry)) + { /* all leafs contains orig trgm */ + double tmpsml = cnt_sml(qtrg, key, *recheck); + + res = (tmpsml >= nlimit); + } + else if (ISALLTRUE(key)) + { /* non-leaf contains signature */ + res = true; + } + else + { /* non-leaf contains signature */ + int32 count = cnt_sml_sign_common(qtrg, GETSIGN(key), siglen); + int32 len = ARRNELEM(qtrg); + + if (len == 0) + res = false; + else + res = (((((float8) count) / ((float8) len))) >= nlimit); + } + break; + case ILikeStrategyNumber: +#ifndef IGNORECASE + elog(ERROR, "cannot handle ~~* with case-sensitive trigrams"); +#endif + /* FALL THRU */ + case LikeStrategyNumber: + case EqualStrategyNumber: + /* Wildcard and equal search are inexact */ + *recheck = true; + + /* + * Check if all the extracted trigrams can be present in child + * nodes. + */ + if (GIST_LEAF(entry)) + { /* all leafs contains orig trgm */ + res = trgm_contained_by(qtrg, key); + } + else if (ISALLTRUE(key)) + { /* non-leaf contains signature */ + res = true; + } + else + { /* non-leaf contains signature */ + int32 k, + tmp = 0, + len = ARRNELEM(qtrg); + trgm *ptr = GETARR(qtrg); + BITVECP sign = GETSIGN(key); + + res = true; + for (k = 0; k < len; k++) + { + CPTRGM(((char *) &tmp), ptr + k); + if (!GETBIT(sign, HASHVAL(tmp, siglen))) + { + res = false; + break; + } + } + } + break; + case RegExpICaseStrategyNumber: +#ifndef IGNORECASE + elog(ERROR, "cannot handle ~* with case-sensitive trigrams"); +#endif + /* FALL THRU */ + case RegExpStrategyNumber: + /* Regexp search is inexact */ + *recheck = true; + + /* Check regex match as much as we can with available info */ + if (qtrg) + { + if (GIST_LEAF(entry)) + { /* all leafs contains orig trgm */ + bool *check; + + check = trgm_presence_map(qtrg, key); + res = trigramsMatchGraph(cache->graph, check); + pfree(check); + } + else if (ISALLTRUE(key)) + { /* non-leaf contains signature */ + res = true; + } + else + { /* non-leaf contains signature */ + int32 k, + tmp = 0, + len = ARRNELEM(qtrg); + trgm *ptr = GETARR(qtrg); + BITVECP sign = GETSIGN(key); + bool *check; + + /* + * GETBIT() tests may give false positives, due to limited + * size of the sign array. But since trigramsMatchGraph() + * implements a monotone boolean function, false positives + * in the check array can't lead to false negative answer. + * So we can apply trigramsMatchGraph despite uncertainty, + * and that usefully improves the quality of the search. + */ + check = (bool *) palloc(len * sizeof(bool)); + for (k = 0; k < len; k++) + { + CPTRGM(((char *) &tmp), ptr + k); + check[k] = GETBIT(sign, HASHVAL(tmp, siglen)); + } + res = trigramsMatchGraph(cache->graph, check); + pfree(check); + } + } + else + { + /* trigram-free query must be rechecked everywhere */ + res = true; + } + break; + default: + elog(ERROR, "unrecognized strategy number: %d", strategy); + res = false; /* keep compiler quiet */ + break; + } + + PG_RETURN_BOOL(res); +} + +Datum +gtrgm_distance(PG_FUNCTION_ARGS) +{ + GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); + text *query = PG_GETARG_TEXT_P(1); + StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2); + + /* Oid subtype = PG_GETARG_OID(3); */ + bool *recheck = (bool *) PG_GETARG_POINTER(4); + int siglen = GET_SIGLEN(); + TRGM *key = (TRGM *) DatumGetPointer(entry->key); + TRGM *qtrg; + float8 res; + Size querysize = VARSIZE(query); + char *cache = (char *) fcinfo->flinfo->fn_extra; + + /* + * Cache the generated trigrams across multiple calls with the same query. + */ + if (cache == NULL || + VARSIZE(cache) != querysize || + memcmp(cache, query, querysize) != 0) + { + char *newcache; + + qtrg = generate_trgm(VARDATA(query), querysize - VARHDRSZ); + + newcache = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, + MAXALIGN(querysize) + + VARSIZE(qtrg)); + + memcpy(newcache, query, querysize); + memcpy(newcache + MAXALIGN(querysize), qtrg, VARSIZE(qtrg)); + + if (cache) + pfree(cache); + fcinfo->flinfo->fn_extra = newcache; + cache = newcache; + } + + qtrg = (TRGM *) (cache + MAXALIGN(querysize)); + + switch (strategy) + { + case DistanceStrategyNumber: + case WordDistanceStrategyNumber: + case StrictWordDistanceStrategyNumber: + /* Only plain trigram distance is exact */ + *recheck = (strategy != DistanceStrategyNumber); + if (GIST_LEAF(entry)) + { /* all leafs contains orig trgm */ + + /* + * Prevent gcc optimizing the sml variable using volatile + * keyword. Otherwise res can differ from the + * word_similarity_dist_op() function. + */ + float4 volatile sml = cnt_sml(qtrg, key, *recheck); + + res = 1.0 - sml; + } + else if (ISALLTRUE(key)) + { /* all leafs contains orig trgm */ + res = 0.0; + } + else + { /* non-leaf contains signature */ + int32 count = cnt_sml_sign_common(qtrg, GETSIGN(key), siglen); + int32 len = ARRNELEM(qtrg); + + res = (len == 0) ? -1.0 : 1.0 - ((float8) count) / ((float8) len); + } + break; + default: + elog(ERROR, "unrecognized strategy number: %d", strategy); + res = 0; /* keep compiler quiet */ + break; + } + + PG_RETURN_FLOAT8(res); +} + +static int32 +unionkey(BITVECP sbase, TRGM *add, int siglen) +{ + int32 i; + + if (ISSIGNKEY(add)) + { + BITVECP sadd = GETSIGN(add); + + if (ISALLTRUE(add)) + return 1; + + LOOPBYTE(siglen) + sbase[i] |= sadd[i]; + } + else + { + trgm *ptr = GETARR(add); + int32 tmp = 0; + + for (i = 0; i < ARRNELEM(add); i++) + { + CPTRGM(((char *) &tmp), ptr + i); + HASH(sbase, tmp, siglen); + } + } + return 0; +} + + +Datum +gtrgm_union(PG_FUNCTION_ARGS) +{ + GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0); + int32 len = entryvec->n; + int *size = (int *) PG_GETARG_POINTER(1); + int siglen = GET_SIGLEN(); + int32 i; + TRGM *result = gtrgm_alloc(false, siglen, NULL); + BITVECP base = GETSIGN(result); + + for (i = 0; i < len; i++) + { + if (unionkey(base, GETENTRY(entryvec, i), siglen)) + { + result->flag = ALLISTRUE; + SET_VARSIZE(result, CALCGTSIZE(ALLISTRUE, siglen)); + break; + } + } + + *size = VARSIZE(result); + + PG_RETURN_POINTER(result); +} + +Datum +gtrgm_same(PG_FUNCTION_ARGS) +{ + TRGM *a = (TRGM *) PG_GETARG_POINTER(0); + TRGM *b = (TRGM *) PG_GETARG_POINTER(1); + bool *result = (bool *) PG_GETARG_POINTER(2); + int siglen = GET_SIGLEN(); + + if (ISSIGNKEY(a)) + { /* then b also ISSIGNKEY */ + if (ISALLTRUE(a) && ISALLTRUE(b)) + *result = true; + else if (ISALLTRUE(a)) + *result = false; + else if (ISALLTRUE(b)) + *result = false; + else + { + int32 i; + BITVECP sa = GETSIGN(a), + sb = GETSIGN(b); + + *result = true; + LOOPBYTE(siglen) + { + if (sa[i] != sb[i]) + { + *result = false; + break; + } + } + } + } + else + { /* a and b ISARRKEY */ + int32 lena = ARRNELEM(a), + lenb = ARRNELEM(b); + + if (lena != lenb) + *result = false; + else + { + trgm *ptra = GETARR(a), + *ptrb = GETARR(b); + int32 i; + + *result = true; + for (i = 0; i < lena; i++) + if (CMPTRGM(ptra + i, ptrb + i)) + { + *result = false; + break; + } + } + } + + PG_RETURN_POINTER(result); +} + +static int32 +sizebitvec(BITVECP sign, int siglen) +{ + return pg_popcount(sign, siglen); +} + +static int +hemdistsign(BITVECP a, BITVECP b, int siglen) +{ + int i, + diff, + dist = 0; + + LOOPBYTE(siglen) + { + diff = (unsigned char) (a[i] ^ b[i]); + /* Using the popcount functions here isn't likely to win */ + dist += pg_number_of_ones[diff]; + } + return dist; +} + +static int +hemdist(TRGM *a, TRGM *b, int siglen) +{ + if (ISALLTRUE(a)) + { + if (ISALLTRUE(b)) + return 0; + else + return SIGLENBIT(siglen) - sizebitvec(GETSIGN(b), siglen); + } + else if (ISALLTRUE(b)) + return SIGLENBIT(siglen) - sizebitvec(GETSIGN(a), siglen); + + return hemdistsign(GETSIGN(a), GETSIGN(b), siglen); +} + +Datum +gtrgm_penalty(PG_FUNCTION_ARGS) +{ + GISTENTRY *origentry = (GISTENTRY *) PG_GETARG_POINTER(0); /* always ISSIGNKEY */ + GISTENTRY *newentry = (GISTENTRY *) PG_GETARG_POINTER(1); + float *penalty = (float *) PG_GETARG_POINTER(2); + int siglen = GET_SIGLEN(); + TRGM *origval = (TRGM *) DatumGetPointer(origentry->key); + TRGM *newval = (TRGM *) DatumGetPointer(newentry->key); + BITVECP orig = GETSIGN(origval); + + *penalty = 0.0; + + if (ISARRKEY(newval)) + { + char *cache = (char *) fcinfo->flinfo->fn_extra; + TRGM *cachedVal = (TRGM *) (cache + MAXALIGN(siglen)); + Size newvalsize = VARSIZE(newval); + BITVECP sign; + + /* + * Cache the sign data across multiple calls with the same newval. + */ + if (cache == NULL || + VARSIZE(cachedVal) != newvalsize || + memcmp(cachedVal, newval, newvalsize) != 0) + { + char *newcache; + + newcache = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, + MAXALIGN(siglen) + + newvalsize); + + makesign((BITVECP) newcache, newval, siglen); + + cachedVal = (TRGM *) (newcache + MAXALIGN(siglen)); + memcpy(cachedVal, newval, newvalsize); + + if (cache) + pfree(cache); + fcinfo->flinfo->fn_extra = newcache; + cache = newcache; + } + + sign = (BITVECP) cache; + + if (ISALLTRUE(origval)) + *penalty = ((float) (SIGLENBIT(siglen) - sizebitvec(sign, siglen))) / (float) (SIGLENBIT(siglen) + 1); + else + *penalty = hemdistsign(sign, orig, siglen); + } + else + *penalty = hemdist(origval, newval, siglen); + PG_RETURN_POINTER(penalty); +} + +typedef struct +{ + bool allistrue; + BITVECP sign; +} CACHESIGN; + +static void +fillcache(CACHESIGN *item, TRGM *key, BITVECP sign, int siglen) +{ + item->allistrue = false; + item->sign = sign; + if (ISARRKEY(key)) + makesign(item->sign, key, siglen); + else if (ISALLTRUE(key)) + item->allistrue = true; + else + memcpy(item->sign, GETSIGN(key), siglen); +} + +#define WISH_F(a,b,c) (double)( -(double)(((a)-(b))*((a)-(b))*((a)-(b)))*(c) ) +typedef struct +{ + OffsetNumber pos; + int32 cost; +} SPLITCOST; + +static int +comparecost(const void *a, const void *b) +{ + if (((const SPLITCOST *) a)->cost == ((const SPLITCOST *) b)->cost) + return 0; + else + return (((const SPLITCOST *) a)->cost > ((const SPLITCOST *) b)->cost) ? 1 : -1; +} + + +static int +hemdistcache(CACHESIGN *a, CACHESIGN *b, int siglen) +{ + if (a->allistrue) + { + if (b->allistrue) + return 0; + else + return SIGLENBIT(siglen) - sizebitvec(b->sign, siglen); + } + else if (b->allistrue) + return SIGLENBIT(siglen) - sizebitvec(a->sign, siglen); + + return hemdistsign(a->sign, b->sign, siglen); +} + +Datum +gtrgm_picksplit(PG_FUNCTION_ARGS) +{ + GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0); + OffsetNumber maxoff = entryvec->n - 1; + GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1); + int siglen = GET_SIGLEN(); + OffsetNumber k, + j; + TRGM *datum_l, + *datum_r; + BITVECP union_l, + union_r; + int32 size_alpha, + size_beta; + int32 size_waste, + waste = -1; + int32 nbytes; + OffsetNumber seed_1 = 0, + seed_2 = 0; + OffsetNumber *left, + *right; + BITVECP ptr; + int i; + CACHESIGN *cache; + char *cache_sign; + SPLITCOST *costvector; + + /* cache the sign data for each existing item */ + cache = (CACHESIGN *) palloc(sizeof(CACHESIGN) * (maxoff + 1)); + cache_sign = palloc(siglen * (maxoff + 1)); + + for (k = FirstOffsetNumber; k <= maxoff; k = OffsetNumberNext(k)) + fillcache(&cache[k], GETENTRY(entryvec, k), &cache_sign[siglen * k], + siglen); + + /* now find the two furthest-apart items */ + for (k = FirstOffsetNumber; k < maxoff; k = OffsetNumberNext(k)) + { + for (j = OffsetNumberNext(k); j <= maxoff; j = OffsetNumberNext(j)) + { + size_waste = hemdistcache(&(cache[j]), &(cache[k]), siglen); + if (size_waste > waste) + { + waste = size_waste; + seed_1 = k; + seed_2 = j; + } + } + } + + /* just in case we didn't make a selection ... */ + if (seed_1 == 0 || seed_2 == 0) + { + seed_1 = 1; + seed_2 = 2; + } + + /* initialize the result vectors */ + nbytes = maxoff * sizeof(OffsetNumber); + v->spl_left = left = (OffsetNumber *) palloc(nbytes); + v->spl_right = right = (OffsetNumber *) palloc(nbytes); + v->spl_nleft = 0; + v->spl_nright = 0; + + /* form initial .. */ + datum_l = gtrgm_alloc(cache[seed_1].allistrue, siglen, cache[seed_1].sign); + datum_r = gtrgm_alloc(cache[seed_2].allistrue, siglen, cache[seed_2].sign); + + union_l = GETSIGN(datum_l); + union_r = GETSIGN(datum_r); + + /* sort before ... */ + costvector = (SPLITCOST *) palloc(sizeof(SPLITCOST) * maxoff); + for (j = FirstOffsetNumber; j <= maxoff; j = OffsetNumberNext(j)) + { + costvector[j - 1].pos = j; + size_alpha = hemdistcache(&(cache[seed_1]), &(cache[j]), siglen); + size_beta = hemdistcache(&(cache[seed_2]), &(cache[j]), siglen); + costvector[j - 1].cost = abs(size_alpha - size_beta); + } + qsort(costvector, maxoff, sizeof(SPLITCOST), comparecost); + + for (k = 0; k < maxoff; k++) + { + j = costvector[k].pos; + if (j == seed_1) + { + *left++ = j; + v->spl_nleft++; + continue; + } + else if (j == seed_2) + { + *right++ = j; + v->spl_nright++; + continue; + } + + if (ISALLTRUE(datum_l) || cache[j].allistrue) + { + if (ISALLTRUE(datum_l) && cache[j].allistrue) + size_alpha = 0; + else + size_alpha = SIGLENBIT(siglen) - + sizebitvec((cache[j].allistrue) ? GETSIGN(datum_l) : + GETSIGN(cache[j].sign), + siglen); + } + else + size_alpha = hemdistsign(cache[j].sign, GETSIGN(datum_l), siglen); + + if (ISALLTRUE(datum_r) || cache[j].allistrue) + { + if (ISALLTRUE(datum_r) && cache[j].allistrue) + size_beta = 0; + else + size_beta = SIGLENBIT(siglen) - + sizebitvec((cache[j].allistrue) ? GETSIGN(datum_r) : + GETSIGN(cache[j].sign), + siglen); + } + else + size_beta = hemdistsign(cache[j].sign, GETSIGN(datum_r), siglen); + + if (size_alpha < size_beta + WISH_F(v->spl_nleft, v->spl_nright, 0.1)) + { + if (ISALLTRUE(datum_l) || cache[j].allistrue) + { + if (!ISALLTRUE(datum_l)) + memset(GETSIGN(datum_l), 0xff, siglen); + } + else + { + ptr = cache[j].sign; + LOOPBYTE(siglen) + union_l[i] |= ptr[i]; + } + *left++ = j; + v->spl_nleft++; + } + else + { + if (ISALLTRUE(datum_r) || cache[j].allistrue) + { + if (!ISALLTRUE(datum_r)) + memset(GETSIGN(datum_r), 0xff, siglen); + } + else + { + ptr = cache[j].sign; + LOOPBYTE(siglen) + union_r[i] |= ptr[i]; + } + *right++ = j; + v->spl_nright++; + } + } + + v->spl_ldatum = PointerGetDatum(datum_l); + v->spl_rdatum = PointerGetDatum(datum_r); + + PG_RETURN_POINTER(v); +} + +Datum +gtrgm_options(PG_FUNCTION_ARGS) +{ + local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0); + + init_local_reloptions(relopts, sizeof(TrgmGistOptions)); + add_local_int_reloption(relopts, "siglen", + "signature length in bytes", + SIGLEN_DEFAULT, 1, SIGLEN_MAX, + offsetof(TrgmGistOptions, siglen)); + + PG_RETURN_VOID(); +} diff --git a/contrib/pg_trgm/trgm_op.c b/contrib/pg_trgm/trgm_op.c new file mode 100644 index 0000000..49d4497 --- /dev/null +++ b/contrib/pg_trgm/trgm_op.c @@ -0,0 +1,1323 @@ +/* + * contrib/pg_trgm/trgm_op.c + */ +#include "postgres.h" + +#include <ctype.h> + +#include "catalog/pg_type.h" +#include "lib/qunique.h" +#include "miscadmin.h" +#include "trgm.h" +#include "tsearch/ts_locale.h" +#include "utils/guc.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" +#include "utils/pg_crc.h" + +PG_MODULE_MAGIC; + +/* GUC variables */ +double similarity_threshold = 0.3f; +double word_similarity_threshold = 0.6f; +double strict_word_similarity_threshold = 0.5f; + +PG_FUNCTION_INFO_V1(set_limit); +PG_FUNCTION_INFO_V1(show_limit); +PG_FUNCTION_INFO_V1(show_trgm); +PG_FUNCTION_INFO_V1(similarity); +PG_FUNCTION_INFO_V1(word_similarity); +PG_FUNCTION_INFO_V1(strict_word_similarity); +PG_FUNCTION_INFO_V1(similarity_dist); +PG_FUNCTION_INFO_V1(similarity_op); +PG_FUNCTION_INFO_V1(word_similarity_op); +PG_FUNCTION_INFO_V1(word_similarity_commutator_op); +PG_FUNCTION_INFO_V1(word_similarity_dist_op); +PG_FUNCTION_INFO_V1(word_similarity_dist_commutator_op); +PG_FUNCTION_INFO_V1(strict_word_similarity_op); +PG_FUNCTION_INFO_V1(strict_word_similarity_commutator_op); +PG_FUNCTION_INFO_V1(strict_word_similarity_dist_op); +PG_FUNCTION_INFO_V1(strict_word_similarity_dist_commutator_op); + +/* Trigram with position */ +typedef struct +{ + trgm trg; + int index; +} pos_trgm; + +/* Trigram bound type */ +typedef uint8 TrgmBound; +#define TRGM_BOUND_LEFT 0x01 /* trigram is left bound of word */ +#define TRGM_BOUND_RIGHT 0x02 /* trigram is right bound of word */ + +/* Word similarity flags */ +#define WORD_SIMILARITY_CHECK_ONLY 0x01 /* only check existence of similar + * search pattern in text */ +#define WORD_SIMILARITY_STRICT 0x02 /* force bounds of extent to match + * word bounds */ + +/* + * Module load callback + */ +void +_PG_init(void) +{ + /* Define custom GUC variables. */ + DefineCustomRealVariable("pg_trgm.similarity_threshold", + "Sets the threshold used by the % operator.", + "Valid range is 0.0 .. 1.0.", + &similarity_threshold, + 0.3f, + 0.0, + 1.0, + PGC_USERSET, + 0, + NULL, + NULL, + NULL); + DefineCustomRealVariable("pg_trgm.word_similarity_threshold", + "Sets the threshold used by the <% operator.", + "Valid range is 0.0 .. 1.0.", + &word_similarity_threshold, + 0.6f, + 0.0, + 1.0, + PGC_USERSET, + 0, + NULL, + NULL, + NULL); + DefineCustomRealVariable("pg_trgm.strict_word_similarity_threshold", + "Sets the threshold used by the <<% operator.", + "Valid range is 0.0 .. 1.0.", + &strict_word_similarity_threshold, + 0.5f, + 0.0, + 1.0, + PGC_USERSET, + 0, + NULL, + NULL, + NULL); + + MarkGUCPrefixReserved("pg_trgm"); +} + +/* + * Deprecated function. + * Use "pg_trgm.similarity_threshold" GUC variable instead of this function. + */ +Datum +set_limit(PG_FUNCTION_ARGS) +{ + float4 nlimit = PG_GETARG_FLOAT4(0); + char *nlimit_str; + Oid func_out_oid; + bool is_varlena; + + getTypeOutputInfo(FLOAT4OID, &func_out_oid, &is_varlena); + + nlimit_str = OidOutputFunctionCall(func_out_oid, Float4GetDatum(nlimit)); + + SetConfigOption("pg_trgm.similarity_threshold", nlimit_str, + PGC_USERSET, PGC_S_SESSION); + + PG_RETURN_FLOAT4(similarity_threshold); +} + + +/* + * Get similarity threshold for given index scan strategy number. + */ +double +index_strategy_get_limit(StrategyNumber strategy) +{ + switch (strategy) + { + case SimilarityStrategyNumber: + return similarity_threshold; + case WordSimilarityStrategyNumber: + return word_similarity_threshold; + case StrictWordSimilarityStrategyNumber: + return strict_word_similarity_threshold; + default: + elog(ERROR, "unrecognized strategy number: %d", strategy); + break; + } + + return 0.0; /* keep compiler quiet */ +} + +/* + * Deprecated function. + * Use "pg_trgm.similarity_threshold" GUC variable instead of this function. + */ +Datum +show_limit(PG_FUNCTION_ARGS) +{ + PG_RETURN_FLOAT4(similarity_threshold); +} + +static int +comp_trgm(const void *a, const void *b) +{ + return CMPTRGM(a, b); +} + +/* + * Finds first word in string, returns pointer to the word, + * endword points to the character after word + */ +static char * +find_word(char *str, int lenstr, char **endword, int *charlen) +{ + char *beginword = str; + + while (beginword - str < lenstr && !ISWORDCHR(beginword)) + beginword += pg_mblen(beginword); + + if (beginword - str >= lenstr) + return NULL; + + *endword = beginword; + *charlen = 0; + while (*endword - str < lenstr && ISWORDCHR(*endword)) + { + *endword += pg_mblen(*endword); + (*charlen)++; + } + + return beginword; +} + +/* + * Reduce a trigram (three possibly multi-byte characters) to a trgm, + * which is always exactly three bytes. If we have three single-byte + * characters, we just use them as-is; otherwise we form a hash value. + */ +void +compact_trigram(trgm *tptr, char *str, int bytelen) +{ + if (bytelen == 3) + { + CPTRGM(tptr, str); + } + else + { + pg_crc32 crc; + + INIT_LEGACY_CRC32(crc); + COMP_LEGACY_CRC32(crc, str, bytelen); + FIN_LEGACY_CRC32(crc); + + /* + * use only 3 upper bytes from crc, hope, it's good enough hashing + */ + CPTRGM(tptr, &crc); + } +} + +/* + * Adds trigrams from words (already padded). + */ +static trgm * +make_trigrams(trgm *tptr, char *str, int bytelen, int charlen) +{ + char *ptr = str; + + if (charlen < 3) + return tptr; + + if (bytelen > charlen) + { + /* Find multibyte character boundaries and apply compact_trigram */ + int lenfirst = pg_mblen(str), + lenmiddle = pg_mblen(str + lenfirst), + lenlast = pg_mblen(str + lenfirst + lenmiddle); + + while ((ptr - str) + lenfirst + lenmiddle + lenlast <= bytelen) + { + compact_trigram(tptr, ptr, lenfirst + lenmiddle + lenlast); + + ptr += lenfirst; + tptr++; + + lenfirst = lenmiddle; + lenmiddle = lenlast; + lenlast = pg_mblen(ptr + lenfirst + lenmiddle); + } + } + else + { + /* Fast path when there are no multibyte characters */ + Assert(bytelen == charlen); + + while (ptr - str < bytelen - 2 /* number of trigrams = strlen - 2 */ ) + { + CPTRGM(tptr, ptr); + ptr++; + tptr++; + } + } + + return tptr; +} + +/* + * Make array of trigrams without sorting and removing duplicate items. + * + * trg: where to return the array of trigrams. + * str: source string, of length slen bytes. + * bounds: where to return bounds of trigrams (if needed). + * + * Returns length of the generated array. + */ +static int +generate_trgm_only(trgm *trg, char *str, int slen, TrgmBound *bounds) +{ + trgm *tptr; + char *buf; + int charlen, + bytelen; + char *bword, + *eword; + + if (slen + LPADDING + RPADDING < 3 || slen == 0) + return 0; + + tptr = trg; + + /* Allocate a buffer for case-folded, blank-padded words */ + buf = (char *) palloc(slen * pg_database_encoding_max_length() + 4); + + if (LPADDING > 0) + { + *buf = ' '; + if (LPADDING > 1) + *(buf + 1) = ' '; + } + + eword = str; + while ((bword = find_word(eword, slen - (eword - str), &eword, &charlen)) != NULL) + { +#ifdef IGNORECASE + bword = lowerstr_with_len(bword, eword - bword); + bytelen = strlen(bword); +#else + bytelen = eword - bword; +#endif + + memcpy(buf + LPADDING, bword, bytelen); + +#ifdef IGNORECASE + pfree(bword); +#endif + + buf[LPADDING + bytelen] = ' '; + buf[LPADDING + bytelen + 1] = ' '; + + /* Calculate trigrams marking their bounds if needed */ + if (bounds) + bounds[tptr - trg] |= TRGM_BOUND_LEFT; + tptr = make_trigrams(tptr, buf, bytelen + LPADDING + RPADDING, + charlen + LPADDING + RPADDING); + if (bounds) + bounds[tptr - trg - 1] |= TRGM_BOUND_RIGHT; + } + + pfree(buf); + + return tptr - trg; +} + +/* + * Guard against possible overflow in the palloc requests below. (We + * don't worry about the additive constants, since palloc can detect + * requests that are a little above MaxAllocSize --- we just need to + * prevent integer overflow in the multiplications.) + */ +static void +protect_out_of_mem(int slen) +{ + if ((Size) (slen / 2) >= (MaxAllocSize / (sizeof(trgm) * 3)) || + (Size) slen >= (MaxAllocSize / pg_database_encoding_max_length())) + ereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("out of memory"))); +} + +/* + * Make array of trigrams with sorting and removing duplicate items. + * + * str: source string, of length slen bytes. + * + * Returns the sorted array of unique trigrams. + */ +TRGM * +generate_trgm(char *str, int slen) +{ + TRGM *trg; + int len; + + protect_out_of_mem(slen); + + trg = (TRGM *) palloc(TRGMHDRSIZE + sizeof(trgm) * (slen / 2 + 1) * 3); + trg->flag = ARRKEY; + + len = generate_trgm_only(GETARR(trg), str, slen, NULL); + SET_VARSIZE(trg, CALCGTSIZE(ARRKEY, len)); + + if (len == 0) + return trg; + + /* + * Make trigrams unique. + */ + if (len > 1) + { + qsort(GETARR(trg), len, sizeof(trgm), comp_trgm); + len = qunique(GETARR(trg), len, sizeof(trgm), comp_trgm); + } + + SET_VARSIZE(trg, CALCGTSIZE(ARRKEY, len)); + + return trg; +} + +/* + * Make array of positional trigrams from two trigram arrays trg1 and trg2. + * + * trg1: trigram array of search pattern, of length len1. trg1 is required + * word which positions don't matter and replaced with -1. + * trg2: trigram array of text, of length len2. trg2 is haystack where we + * search and have to store its positions. + * + * Returns concatenated trigram array. + */ +static pos_trgm * +make_positional_trgm(trgm *trg1, int len1, trgm *trg2, int len2) +{ + pos_trgm *result; + int i, + len = len1 + len2; + + result = (pos_trgm *) palloc(sizeof(pos_trgm) * len); + + for (i = 0; i < len1; i++) + { + memcpy(&result[i].trg, &trg1[i], sizeof(trgm)); + result[i].index = -1; + } + + for (i = 0; i < len2; i++) + { + memcpy(&result[i + len1].trg, &trg2[i], sizeof(trgm)); + result[i + len1].index = i; + } + + return result; +} + +/* + * Compare position trigrams: compare trigrams first and position second. + */ +static int +comp_ptrgm(const void *v1, const void *v2) +{ + const pos_trgm *p1 = (const pos_trgm *) v1; + const pos_trgm *p2 = (const pos_trgm *) v2; + int cmp; + + cmp = CMPTRGM(p1->trg, p2->trg); + if (cmp != 0) + return cmp; + + if (p1->index < p2->index) + return -1; + else if (p1->index == p2->index) + return 0; + else + return 1; +} + +/* + * Iterative search function which calculates maximum similarity with word in + * the string. Maximum similarity is only calculated only if the flag + * WORD_SIMILARITY_CHECK_ONLY isn't set. + * + * trg2indexes: array which stores indexes of the array "found". + * found: array which stores true of false values. + * ulen1: count of unique trigrams of array "trg1". + * len2: length of array "trg2" and array "trg2indexes". + * len: length of the array "found". + * flags: set of boolean flags parameterizing similarity calculation. + * bounds: whether each trigram is left/right bound of word. + * + * Returns word similarity. + */ +static float4 +iterate_word_similarity(int *trg2indexes, + bool *found, + int ulen1, + int len2, + int len, + uint8 flags, + TrgmBound *bounds) +{ + int *lastpos, + i, + ulen2 = 0, + count = 0, + upper = -1, + lower; + float4 smlr_cur, + smlr_max = 0.0f; + double threshold; + + Assert(bounds || !(flags & WORD_SIMILARITY_STRICT)); + + /* Select appropriate threshold */ + threshold = (flags & WORD_SIMILARITY_STRICT) ? + strict_word_similarity_threshold : + word_similarity_threshold; + + /* + * Consider first trigram as initial lower bound for strict word + * similarity, or initialize it later with first trigram present for plain + * word similarity. + */ + lower = (flags & WORD_SIMILARITY_STRICT) ? 0 : -1; + + /* Memorise last position of each trigram */ + lastpos = (int *) palloc(sizeof(int) * len); + memset(lastpos, -1, sizeof(int) * len); + + for (i = 0; i < len2; i++) + { + int trgindex; + + CHECK_FOR_INTERRUPTS(); + + /* Get index of next trigram */ + trgindex = trg2indexes[i]; + + /* Update last position of this trigram */ + if (lower >= 0 || found[trgindex]) + { + if (lastpos[trgindex] < 0) + { + ulen2++; + if (found[trgindex]) + count++; + } + lastpos[trgindex] = i; + } + + /* + * Adjust upper bound if trigram is upper bound of word for strict + * word similarity, or if trigram is present in required substring for + * plain word similarity + */ + if ((flags & WORD_SIMILARITY_STRICT) ? (bounds[i] & TRGM_BOUND_RIGHT) + : found[trgindex]) + { + int prev_lower, + tmp_ulen2, + tmp_lower, + tmp_count; + + upper = i; + if (lower == -1) + { + lower = i; + ulen2 = 1; + } + + smlr_cur = CALCSML(count, ulen1, ulen2); + + /* Also try to adjust lower bound for greater similarity */ + tmp_count = count; + tmp_ulen2 = ulen2; + prev_lower = lower; + for (tmp_lower = lower; tmp_lower <= upper; tmp_lower++) + { + float smlr_tmp; + int tmp_trgindex; + + /* + * Adjust lower bound only if trigram is lower bound of word + * for strict word similarity, or consider every trigram as + * lower bound for plain word similarity. + */ + if (!(flags & WORD_SIMILARITY_STRICT) + || (bounds[tmp_lower] & TRGM_BOUND_LEFT)) + { + smlr_tmp = CALCSML(tmp_count, ulen1, tmp_ulen2); + if (smlr_tmp > smlr_cur) + { + smlr_cur = smlr_tmp; + ulen2 = tmp_ulen2; + lower = tmp_lower; + count = tmp_count; + } + + /* + * If we only check that word similarity is greater than + * threshold we do not need to calculate a maximum + * similarity. + */ + if ((flags & WORD_SIMILARITY_CHECK_ONLY) + && smlr_cur >= threshold) + break; + } + + tmp_trgindex = trg2indexes[tmp_lower]; + if (lastpos[tmp_trgindex] == tmp_lower) + { + tmp_ulen2--; + if (found[tmp_trgindex]) + tmp_count--; + } + } + + smlr_max = Max(smlr_max, smlr_cur); + + /* + * if we only check that word similarity is greater than threshold + * we do not need to calculate a maximum similarity. + */ + if ((flags & WORD_SIMILARITY_CHECK_ONLY) && smlr_max >= threshold) + break; + + for (tmp_lower = prev_lower; tmp_lower < lower; tmp_lower++) + { + int tmp_trgindex; + + tmp_trgindex = trg2indexes[tmp_lower]; + if (lastpos[tmp_trgindex] == tmp_lower) + lastpos[tmp_trgindex] = -1; + } + } + } + + pfree(lastpos); + + return smlr_max; +} + +/* + * Calculate word similarity. + * This function prepare two arrays: "trg2indexes" and "found". Then this arrays + * are used to calculate word similarity using iterate_word_similarity(). + * + * "trg2indexes" is array which stores indexes of the array "found". + * In other words: + * trg2indexes[j] = i; + * found[i] = true (or false); + * If found[i] == true then there is trigram trg2[j] in array "trg1". + * If found[i] == false then there is not trigram trg2[j] in array "trg1". + * + * str1: search pattern string, of length slen1 bytes. + * str2: text in which we are looking for a word, of length slen2 bytes. + * flags: set of boolean flags parameterizing similarity calculation. + * + * Returns word similarity. + */ +static float4 +calc_word_similarity(char *str1, int slen1, char *str2, int slen2, + uint8 flags) +{ + bool *found; + pos_trgm *ptrg; + trgm *trg1; + trgm *trg2; + int len1, + len2, + len, + i, + j, + ulen1; + int *trg2indexes; + float4 result; + TrgmBound *bounds; + + protect_out_of_mem(slen1 + slen2); + + /* Make positional trigrams */ + trg1 = (trgm *) palloc(sizeof(trgm) * (slen1 / 2 + 1) * 3); + trg2 = (trgm *) palloc(sizeof(trgm) * (slen2 / 2 + 1) * 3); + if (flags & WORD_SIMILARITY_STRICT) + bounds = (TrgmBound *) palloc0(sizeof(TrgmBound) * (slen2 / 2 + 1) * 3); + else + bounds = NULL; + + len1 = generate_trgm_only(trg1, str1, slen1, NULL); + len2 = generate_trgm_only(trg2, str2, slen2, bounds); + + ptrg = make_positional_trgm(trg1, len1, trg2, len2); + len = len1 + len2; + qsort(ptrg, len, sizeof(pos_trgm), comp_ptrgm); + + pfree(trg1); + pfree(trg2); + + /* + * Merge positional trigrams array: enumerate each trigram and find its + * presence in required word. + */ + trg2indexes = (int *) palloc(sizeof(int) * len2); + found = (bool *) palloc0(sizeof(bool) * len); + + ulen1 = 0; + j = 0; + for (i = 0; i < len; i++) + { + if (i > 0) + { + int cmp = CMPTRGM(ptrg[i - 1].trg, ptrg[i].trg); + + if (cmp != 0) + { + if (found[j]) + ulen1++; + j++; + } + } + + if (ptrg[i].index >= 0) + { + trg2indexes[ptrg[i].index] = j; + } + else + { + found[j] = true; + } + } + if (found[j]) + ulen1++; + + /* Run iterative procedure to find maximum similarity with word */ + result = iterate_word_similarity(trg2indexes, found, ulen1, len2, len, + flags, bounds); + + pfree(trg2indexes); + pfree(found); + pfree(ptrg); + + return result; +} + + +/* + * Extract the next non-wildcard part of a search string, i.e. a word bounded + * by '_' or '%' meta-characters, non-word characters or string end. + * + * str: source string, of length lenstr bytes (need not be null-terminated) + * buf: where to return the substring (must be long enough) + * *bytelen: receives byte length of the found substring + * *charlen: receives character length of the found substring + * + * Returns pointer to end+1 of the found substring in the source string. + * Returns NULL if no word found (in which case buf, bytelen, charlen not set) + * + * If the found word is bounded by non-word characters or string boundaries + * then this function will include corresponding padding spaces into buf. + */ +static const char * +get_wildcard_part(const char *str, int lenstr, + char *buf, int *bytelen, int *charlen) +{ + const char *beginword = str; + const char *endword; + char *s = buf; + bool in_leading_wildcard_meta = false; + bool in_trailing_wildcard_meta = false; + bool in_escape = false; + int clen; + + /* + * Find the first word character, remembering whether preceding character + * was wildcard meta-character. Note that the in_escape state persists + * from this loop to the next one, since we may exit at a word character + * that is in_escape. + */ + while (beginword - str < lenstr) + { + if (in_escape) + { + if (ISWORDCHR(beginword)) + break; + in_escape = false; + in_leading_wildcard_meta = false; + } + else + { + if (ISESCAPECHAR(beginword)) + in_escape = true; + else if (ISWILDCARDCHAR(beginword)) + in_leading_wildcard_meta = true; + else if (ISWORDCHR(beginword)) + break; + else + in_leading_wildcard_meta = false; + } + beginword += pg_mblen(beginword); + } + + /* + * Handle string end. + */ + if (beginword - str >= lenstr) + return NULL; + + /* + * Add left padding spaces if preceding character wasn't wildcard + * meta-character. + */ + *charlen = 0; + if (!in_leading_wildcard_meta) + { + if (LPADDING > 0) + { + *s++ = ' '; + (*charlen)++; + if (LPADDING > 1) + { + *s++ = ' '; + (*charlen)++; + } + } + } + + /* + * Copy data into buf until wildcard meta-character, non-word character or + * string boundary. Strip escapes during copy. + */ + endword = beginword; + while (endword - str < lenstr) + { + clen = pg_mblen(endword); + if (in_escape) + { + if (ISWORDCHR(endword)) + { + memcpy(s, endword, clen); + (*charlen)++; + s += clen; + } + else + { + /* + * Back up endword to the escape character when stopping at an + * escaped char, so that subsequent get_wildcard_part will + * restart from the escape character. We assume here that + * escape chars are single-byte. + */ + endword--; + break; + } + in_escape = false; + } + else + { + if (ISESCAPECHAR(endword)) + in_escape = true; + else if (ISWILDCARDCHAR(endword)) + { + in_trailing_wildcard_meta = true; + break; + } + else if (ISWORDCHR(endword)) + { + memcpy(s, endword, clen); + (*charlen)++; + s += clen; + } + else + break; + } + endword += clen; + } + + /* + * Add right padding spaces if next character isn't wildcard + * meta-character. + */ + if (!in_trailing_wildcard_meta) + { + if (RPADDING > 0) + { + *s++ = ' '; + (*charlen)++; + if (RPADDING > 1) + { + *s++ = ' '; + (*charlen)++; + } + } + } + + *bytelen = s - buf; + return endword; +} + +/* + * Generates trigrams for wildcard search string. + * + * Returns array of trigrams that must occur in any string that matches the + * wildcard string. For example, given pattern "a%bcd%" the trigrams + * " a", "bcd" would be extracted. + */ +TRGM * +generate_wildcard_trgm(const char *str, int slen) +{ + TRGM *trg; + char *buf, + *buf2; + trgm *tptr; + int len, + charlen, + bytelen; + const char *eword; + + protect_out_of_mem(slen); + + trg = (TRGM *) palloc(TRGMHDRSIZE + sizeof(trgm) * (slen / 2 + 1) * 3); + trg->flag = ARRKEY; + SET_VARSIZE(trg, TRGMHDRSIZE); + + if (slen + LPADDING + RPADDING < 3 || slen == 0) + return trg; + + tptr = GETARR(trg); + + /* Allocate a buffer for blank-padded, but not yet case-folded, words */ + buf = palloc(sizeof(char) * (slen + 4)); + + /* + * Extract trigrams from each substring extracted by get_wildcard_part. + */ + eword = str; + while ((eword = get_wildcard_part(eword, slen - (eword - str), + buf, &bytelen, &charlen)) != NULL) + { +#ifdef IGNORECASE + buf2 = lowerstr_with_len(buf, bytelen); + bytelen = strlen(buf2); +#else + buf2 = buf; +#endif + + /* + * count trigrams + */ + tptr = make_trigrams(tptr, buf2, bytelen, charlen); + +#ifdef IGNORECASE + pfree(buf2); +#endif + } + + pfree(buf); + + if ((len = tptr - GETARR(trg)) == 0) + return trg; + + /* + * Make trigrams unique. + */ + if (len > 1) + { + qsort(GETARR(trg), len, sizeof(trgm), comp_trgm); + len = qunique(GETARR(trg), len, sizeof(trgm), comp_trgm); + } + + SET_VARSIZE(trg, CALCGTSIZE(ARRKEY, len)); + + return trg; +} + +uint32 +trgm2int(trgm *ptr) +{ + uint32 val = 0; + + val |= *(((unsigned char *) ptr)); + val <<= 8; + val |= *(((unsigned char *) ptr) + 1); + val <<= 8; + val |= *(((unsigned char *) ptr) + 2); + + return val; +} + +Datum +show_trgm(PG_FUNCTION_ARGS) +{ + text *in = PG_GETARG_TEXT_PP(0); + TRGM *trg; + Datum *d; + ArrayType *a; + trgm *ptr; + int i; + + trg = generate_trgm(VARDATA_ANY(in), VARSIZE_ANY_EXHDR(in)); + d = (Datum *) palloc(sizeof(Datum) * (1 + ARRNELEM(trg))); + + for (i = 0, ptr = GETARR(trg); i < ARRNELEM(trg); i++, ptr++) + { + text *item = (text *) palloc(VARHDRSZ + Max(12, pg_database_encoding_max_length() * 3)); + + if (pg_database_encoding_max_length() > 1 && !ISPRINTABLETRGM(ptr)) + { + snprintf(VARDATA(item), 12, "0x%06x", trgm2int(ptr)); + SET_VARSIZE(item, VARHDRSZ + strlen(VARDATA(item))); + } + else + { + SET_VARSIZE(item, VARHDRSZ + 3); + CPTRGM(VARDATA(item), ptr); + } + d[i] = PointerGetDatum(item); + } + + a = construct_array_builtin(d, ARRNELEM(trg), TEXTOID); + + for (i = 0; i < ARRNELEM(trg); i++) + pfree(DatumGetPointer(d[i])); + + pfree(d); + pfree(trg); + PG_FREE_IF_COPY(in, 0); + + PG_RETURN_POINTER(a); +} + +float4 +cnt_sml(TRGM *trg1, TRGM *trg2, bool inexact) +{ + trgm *ptr1, + *ptr2; + int count = 0; + int len1, + len2; + + ptr1 = GETARR(trg1); + ptr2 = GETARR(trg2); + + len1 = ARRNELEM(trg1); + len2 = ARRNELEM(trg2); + + /* explicit test is needed to avoid 0/0 division when both lengths are 0 */ + if (len1 <= 0 || len2 <= 0) + return (float4) 0.0; + + while (ptr1 - GETARR(trg1) < len1 && ptr2 - GETARR(trg2) < len2) + { + int res = CMPTRGM(ptr1, ptr2); + + if (res < 0) + ptr1++; + else if (res > 0) + ptr2++; + else + { + ptr1++; + ptr2++; + count++; + } + } + + /* + * If inexact then len2 is equal to count, because we don't know actual + * length of second string in inexact search and we can assume that count + * is a lower bound of len2. + */ + return CALCSML(count, len1, inexact ? count : len2); +} + + +/* + * Returns whether trg2 contains all trigrams in trg1. + * This relies on the trigram arrays being sorted. + */ +bool +trgm_contained_by(TRGM *trg1, TRGM *trg2) +{ + trgm *ptr1, + *ptr2; + int len1, + len2; + + ptr1 = GETARR(trg1); + ptr2 = GETARR(trg2); + + len1 = ARRNELEM(trg1); + len2 = ARRNELEM(trg2); + + while (ptr1 - GETARR(trg1) < len1 && ptr2 - GETARR(trg2) < len2) + { + int res = CMPTRGM(ptr1, ptr2); + + if (res < 0) + return false; + else if (res > 0) + ptr2++; + else + { + ptr1++; + ptr2++; + } + } + if (ptr1 - GETARR(trg1) < len1) + return false; + else + return true; +} + +/* + * Return a palloc'd boolean array showing, for each trigram in "query", + * whether it is present in the trigram array "key". + * This relies on the "key" array being sorted, but "query" need not be. + */ +bool * +trgm_presence_map(TRGM *query, TRGM *key) +{ + bool *result; + trgm *ptrq = GETARR(query), + *ptrk = GETARR(key); + int lenq = ARRNELEM(query), + lenk = ARRNELEM(key), + i; + + result = (bool *) palloc0(lenq * sizeof(bool)); + + /* for each query trigram, do a binary search in the key array */ + for (i = 0; i < lenq; i++) + { + int lo = 0; + int hi = lenk; + + while (lo < hi) + { + int mid = (lo + hi) / 2; + int res = CMPTRGM(ptrq, ptrk + mid); + + if (res < 0) + hi = mid; + else if (res > 0) + lo = mid + 1; + else + { + result[i] = true; + break; + } + } + ptrq++; + } + + return result; +} + +Datum +similarity(PG_FUNCTION_ARGS) +{ + text *in1 = PG_GETARG_TEXT_PP(0); + text *in2 = PG_GETARG_TEXT_PP(1); + TRGM *trg1, + *trg2; + float4 res; + + trg1 = generate_trgm(VARDATA_ANY(in1), VARSIZE_ANY_EXHDR(in1)); + trg2 = generate_trgm(VARDATA_ANY(in2), VARSIZE_ANY_EXHDR(in2)); + + res = cnt_sml(trg1, trg2, false); + + pfree(trg1); + pfree(trg2); + PG_FREE_IF_COPY(in1, 0); + PG_FREE_IF_COPY(in2, 1); + + PG_RETURN_FLOAT4(res); +} + +Datum +word_similarity(PG_FUNCTION_ARGS) +{ + text *in1 = PG_GETARG_TEXT_PP(0); + text *in2 = PG_GETARG_TEXT_PP(1); + float4 res; + + res = calc_word_similarity(VARDATA_ANY(in1), VARSIZE_ANY_EXHDR(in1), + VARDATA_ANY(in2), VARSIZE_ANY_EXHDR(in2), + 0); + + PG_FREE_IF_COPY(in1, 0); + PG_FREE_IF_COPY(in2, 1); + PG_RETURN_FLOAT4(res); +} + +Datum +strict_word_similarity(PG_FUNCTION_ARGS) +{ + text *in1 = PG_GETARG_TEXT_PP(0); + text *in2 = PG_GETARG_TEXT_PP(1); + float4 res; + + res = calc_word_similarity(VARDATA_ANY(in1), VARSIZE_ANY_EXHDR(in1), + VARDATA_ANY(in2), VARSIZE_ANY_EXHDR(in2), + WORD_SIMILARITY_STRICT); + + PG_FREE_IF_COPY(in1, 0); + PG_FREE_IF_COPY(in2, 1); + PG_RETURN_FLOAT4(res); +} + +Datum +similarity_dist(PG_FUNCTION_ARGS) +{ + float4 res = DatumGetFloat4(DirectFunctionCall2(similarity, + PG_GETARG_DATUM(0), + PG_GETARG_DATUM(1))); + + PG_RETURN_FLOAT4(1.0 - res); +} + +Datum +similarity_op(PG_FUNCTION_ARGS) +{ + float4 res = DatumGetFloat4(DirectFunctionCall2(similarity, + PG_GETARG_DATUM(0), + PG_GETARG_DATUM(1))); + + PG_RETURN_BOOL(res >= similarity_threshold); +} + +Datum +word_similarity_op(PG_FUNCTION_ARGS) +{ + text *in1 = PG_GETARG_TEXT_PP(0); + text *in2 = PG_GETARG_TEXT_PP(1); + float4 res; + + res = calc_word_similarity(VARDATA_ANY(in1), VARSIZE_ANY_EXHDR(in1), + VARDATA_ANY(in2), VARSIZE_ANY_EXHDR(in2), + WORD_SIMILARITY_CHECK_ONLY); + + PG_FREE_IF_COPY(in1, 0); + PG_FREE_IF_COPY(in2, 1); + PG_RETURN_BOOL(res >= word_similarity_threshold); +} + +Datum +word_similarity_commutator_op(PG_FUNCTION_ARGS) +{ + text *in1 = PG_GETARG_TEXT_PP(0); + text *in2 = PG_GETARG_TEXT_PP(1); + float4 res; + + res = calc_word_similarity(VARDATA_ANY(in2), VARSIZE_ANY_EXHDR(in2), + VARDATA_ANY(in1), VARSIZE_ANY_EXHDR(in1), + WORD_SIMILARITY_CHECK_ONLY); + + PG_FREE_IF_COPY(in1, 0); + PG_FREE_IF_COPY(in2, 1); + PG_RETURN_BOOL(res >= word_similarity_threshold); +} + +Datum +word_similarity_dist_op(PG_FUNCTION_ARGS) +{ + text *in1 = PG_GETARG_TEXT_PP(0); + text *in2 = PG_GETARG_TEXT_PP(1); + float4 res; + + res = calc_word_similarity(VARDATA_ANY(in1), VARSIZE_ANY_EXHDR(in1), + VARDATA_ANY(in2), VARSIZE_ANY_EXHDR(in2), + 0); + + PG_FREE_IF_COPY(in1, 0); + PG_FREE_IF_COPY(in2, 1); + PG_RETURN_FLOAT4(1.0 - res); +} + +Datum +word_similarity_dist_commutator_op(PG_FUNCTION_ARGS) +{ + text *in1 = PG_GETARG_TEXT_PP(0); + text *in2 = PG_GETARG_TEXT_PP(1); + float4 res; + + res = calc_word_similarity(VARDATA_ANY(in2), VARSIZE_ANY_EXHDR(in2), + VARDATA_ANY(in1), VARSIZE_ANY_EXHDR(in1), + 0); + + PG_FREE_IF_COPY(in1, 0); + PG_FREE_IF_COPY(in2, 1); + PG_RETURN_FLOAT4(1.0 - res); +} + +Datum +strict_word_similarity_op(PG_FUNCTION_ARGS) +{ + text *in1 = PG_GETARG_TEXT_PP(0); + text *in2 = PG_GETARG_TEXT_PP(1); + float4 res; + + res = calc_word_similarity(VARDATA_ANY(in1), VARSIZE_ANY_EXHDR(in1), + VARDATA_ANY(in2), VARSIZE_ANY_EXHDR(in2), + WORD_SIMILARITY_CHECK_ONLY | WORD_SIMILARITY_STRICT); + + PG_FREE_IF_COPY(in1, 0); + PG_FREE_IF_COPY(in2, 1); + PG_RETURN_BOOL(res >= strict_word_similarity_threshold); +} + +Datum +strict_word_similarity_commutator_op(PG_FUNCTION_ARGS) +{ + text *in1 = PG_GETARG_TEXT_PP(0); + text *in2 = PG_GETARG_TEXT_PP(1); + float4 res; + + res = calc_word_similarity(VARDATA_ANY(in2), VARSIZE_ANY_EXHDR(in2), + VARDATA_ANY(in1), VARSIZE_ANY_EXHDR(in1), + WORD_SIMILARITY_CHECK_ONLY | WORD_SIMILARITY_STRICT); + + PG_FREE_IF_COPY(in1, 0); + PG_FREE_IF_COPY(in2, 1); + PG_RETURN_BOOL(res >= strict_word_similarity_threshold); +} + +Datum +strict_word_similarity_dist_op(PG_FUNCTION_ARGS) +{ + text *in1 = PG_GETARG_TEXT_PP(0); + text *in2 = PG_GETARG_TEXT_PP(1); + float4 res; + + res = calc_word_similarity(VARDATA_ANY(in1), VARSIZE_ANY_EXHDR(in1), + VARDATA_ANY(in2), VARSIZE_ANY_EXHDR(in2), + WORD_SIMILARITY_STRICT); + + PG_FREE_IF_COPY(in1, 0); + PG_FREE_IF_COPY(in2, 1); + PG_RETURN_FLOAT4(1.0 - res); +} + +Datum +strict_word_similarity_dist_commutator_op(PG_FUNCTION_ARGS) +{ + text *in1 = PG_GETARG_TEXT_PP(0); + text *in2 = PG_GETARG_TEXT_PP(1); + float4 res; + + res = calc_word_similarity(VARDATA_ANY(in2), VARSIZE_ANY_EXHDR(in2), + VARDATA_ANY(in1), VARSIZE_ANY_EXHDR(in1), + WORD_SIMILARITY_STRICT); + + PG_FREE_IF_COPY(in1, 0); + PG_FREE_IF_COPY(in2, 1); + PG_RETURN_FLOAT4(1.0 - res); +} diff --git a/contrib/pg_trgm/trgm_regexp.c b/contrib/pg_trgm/trgm_regexp.c new file mode 100644 index 0000000..1d36946 --- /dev/null +++ b/contrib/pg_trgm/trgm_regexp.c @@ -0,0 +1,2358 @@ +/*------------------------------------------------------------------------- + * + * trgm_regexp.c + * Regular expression matching using trigrams. + * + * The general idea of trigram index support for a regular expression (regex) + * search is to transform the regex into a logical expression on trigrams. + * For example: + * + * (ab|cd)efg => ((abe & bef) | (cde & def)) & efg + * + * If a string matches the regex, then it must match the logical expression on + * trigrams. The opposite is not necessarily true, however: a string that + * matches the logical expression might not match the original regex. Such + * false positives are removed via recheck, by running the regular regex match + * operator on the retrieved heap tuple. + * + * Since the trigram expression involves both AND and OR operators, we can't + * expect the core index machinery to evaluate it completely. Instead, the + * result of regex analysis is a list of trigrams to be sought in the index, + * plus a simplified graph that is used by trigramsMatchGraph() to determine + * whether a particular indexed value matches the expression. + * + * Converting a regex to a trigram expression is based on analysis of an + * automaton corresponding to the regex. The algorithm consists of four + * stages: + * + * 1) Compile the regexp to NFA form. This is handled by the PostgreSQL + * regexp library, which provides accessors for its opaque regex_t struct + * to expose the NFA state graph and the "colors" (sets of equivalent + * characters) used as state transition labels. + * + * 2) Transform the original NFA into an expanded graph, where arcs + * are labeled with trigrams that must be present in order to move from + * one state to another via the arcs. The trigrams used in this stage + * consist of colors, not characters, as in the original NFA. + * + * 3) Expand the color trigrams into regular trigrams consisting of + * characters. If too many distinct trigrams are produced, trigrams are + * eliminated and the graph is simplified until it's simple enough. + * + * 4) Finally, the resulting graph is packed into a TrgmPackedGraph struct, + * and returned to the caller. + * + * 1) Compile the regexp to NFA form + * --------------------------------- + * The automaton returned by the regexp compiler is a graph where vertices + * are "states" and arcs are labeled with colors. Each color represents + * a set of characters, so that all characters assigned to the same color + * are interchangeable, so far as matching the regexp is concerned. There + * are two special states: "initial" and "final". A state can have multiple + * outgoing arcs labeled with the same color, which makes the automaton + * non-deterministic, because it can be in many states simultaneously. + * + * Note that this NFA is already lossy compared to the original regexp, + * since it ignores some regex features such as lookahead constraints and + * backref matching. This is OK for our purposes since it's still the case + * that only strings matching the NFA can possibly satisfy the regexp. + * + * 2) Transform the original NFA into an expanded graph + * ---------------------------------------------------- + * In the 2nd stage, the automaton is transformed into a graph based on the + * original NFA. Each state in the expanded graph represents a state from + * the original NFA, plus a prefix identifying the last two characters + * (colors, to be precise) seen before entering the state. There can be + * multiple states in the expanded graph for each state in the original NFA, + * depending on what characters can precede it. A prefix position can be + * "unknown" if it's uncertain what the preceding character was, or "blank" + * if the character was a non-word character (we don't need to distinguish + * which non-word character it was, so just think of all of them as blanks). + * + * For convenience in description, call an expanded-state identifier + * (two prefix colors plus a state number from the original NFA) an + * "enter key". + * + * Each arc of the expanded graph is labeled with a trigram that must be + * present in the string to match. We can construct this from an out-arc of + * the underlying NFA state by combining the expanded state's prefix with the + * color label of the underlying out-arc, if neither prefix position is + * "unknown". But note that some of the colors in the trigram might be + * "blank". This is OK since we want to generate word-boundary trigrams as + * the regular trigram machinery would, if we know that some word characters + * must be adjacent to a word boundary in all strings matching the NFA. + * + * The expanded graph can also have fewer states than the original NFA, + * because we don't bother to make a separate state entry unless the state + * is reachable by a valid arc. When an enter key is reachable from a state + * of the expanded graph, but we do not know a complete trigram associated + * with that transition, we cannot make a valid arc; instead we insert the + * enter key into the enterKeys list of the source state. This effectively + * means that the two expanded states are not reliably distinguishable based + * on examining trigrams. + * + * So the expanded graph resembles the original NFA, but the arcs are + * labeled with trigrams instead of individual characters, and there may be + * more or fewer states. It is a lossy representation of the original NFA: + * any string that matches the original regexp must match the expanded graph, + * but the reverse is not true. + * + * We build the expanded graph through a breadth-first traversal of states + * reachable from the initial state. At each reachable state, we identify the + * states reachable from it without traversing a predictable trigram, and add + * those states' enter keys to the current state. Then we generate all + * out-arcs leading out of this collection of states that have predictable + * trigrams, adding their target states to the queue of states to examine. + * + * When building the graph, if the number of states or arcs exceed pre-defined + * limits, we give up and simply mark any states not yet processed as final + * states. Roughly speaking, that means that we make use of some portion from + * the beginning of the regexp. Also, any colors that have too many member + * characters are treated as "unknown", so that we can't derive trigrams + * from them. + * + * 3) Expand the color trigrams into regular trigrams + * -------------------------------------------------- + * The trigrams in the expanded graph are "color trigrams", consisting + * of three consecutive colors that must be present in the string. But for + * search, we need regular trigrams consisting of characters. In the 3rd + * stage, the color trigrams are expanded into regular trigrams. Since each + * color can represent many characters, the total number of regular trigrams + * after expansion could be very large. Because searching the index for + * thousands of trigrams would be slow, and would likely produce so many + * false positives that we would have to traverse a large fraction of the + * index, the graph is simplified further in a lossy fashion by removing + * color trigrams. When a color trigram is removed, the states connected by + * any arcs labeled with that trigram are merged. + * + * Trigrams do not all have equivalent value for searching: some of them are + * more frequent and some of them are less frequent. Ideally, we would like + * to know the distribution of trigrams, but we don't. But because of padding + * we know for sure that the empty character is more frequent than others, + * so we can penalize trigrams according to presence of whitespace. The + * penalty assigned to each color trigram is the number of simple trigrams + * it would produce, times the penalties[] multiplier associated with its + * whitespace content. (The penalties[] constants were calculated by analysis + * of some real-life text.) We eliminate color trigrams starting with the + * highest-penalty one, until we get to a total penalty of no more than + * WISH_TRGM_PENALTY. However, we cannot remove a color trigram if that would + * lead to merging the initial and final states, so we may not be able to + * reach WISH_TRGM_PENALTY. It's still okay so long as we have no more than + * MAX_TRGM_COUNT simple trigrams in total, otherwise we fail. + * + * 4) Pack the graph into a compact representation + * ----------------------------------------------- + * The 2nd and 3rd stages might have eliminated or merged many of the states + * and trigrams created earlier, so in this final stage, the graph is + * compacted and packed into a simpler struct that contains only the + * information needed to evaluate it. + * + * ALGORITHM EXAMPLE: + * + * Consider the example regex "ab[cd]". This regex is transformed into the + * following NFA (for simplicity we show colors as their single members): + * + * 4# + * c/ + * a b / + * 1* --- 2 ---- 3 + * \ + * d\ + * 5# + * + * We use * to mark initial state and # to mark final state. It's not depicted, + * but states 1, 4, 5 have self-referencing arcs for all possible characters, + * because this pattern can match to any part of a string. + * + * As the result of stage 2 we will have the following graph: + * + * abc abd + * 2# <---- 1* ----> 3# + * + * The process for generating this graph is: + * 1) Create state 1 with enter key (UNKNOWN, UNKNOWN, 1). + * 2) Add key (UNKNOWN, "a", 2) to state 1. + * 3) Add key ("a", "b", 3) to state 1. + * 4) Create new state 2 with enter key ("b", "c", 4). Add an arc + * from state 1 to state 2 with label trigram "abc". + * 5) Mark state 2 final because state 4 of source NFA is marked as final. + * 6) Create new state 3 with enter key ("b", "d", 5). Add an arc + * from state 1 to state 3 with label trigram "abd". + * 7) Mark state 3 final because state 5 of source NFA is marked as final. + * + * + * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * contrib/pg_trgm/trgm_regexp.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "regex/regexport.h" +#include "trgm.h" +#include "tsearch/ts_locale.h" +#include "utils/hsearch.h" +#include "utils/memutils.h" +#include "varatt.h" + +/* + * Uncomment (or use -DTRGM_REGEXP_DEBUG) to print debug info, + * for exploring and debugging the algorithm implementation. + * This produces three graph files in /tmp, in Graphviz .gv format. + * Some progress information is also printed to postmaster stderr. + */ +/* #define TRGM_REGEXP_DEBUG */ + +/* + * These parameters are used to limit the amount of work done. + * Otherwise regex processing could be too slow and memory-consuming. + * + * MAX_EXPANDED_STATES - How many states we allow in expanded graph + * MAX_EXPANDED_ARCS - How many arcs we allow in expanded graph + * MAX_TRGM_COUNT - How many simple trigrams we allow to be extracted + * WISH_TRGM_PENALTY - Maximum desired sum of color trigram penalties + * COLOR_COUNT_LIMIT - Maximum number of characters per color + */ +#define MAX_EXPANDED_STATES 128 +#define MAX_EXPANDED_ARCS 1024 +#define MAX_TRGM_COUNT 256 +#define WISH_TRGM_PENALTY 16 +#define COLOR_COUNT_LIMIT 256 + +/* + * Penalty multipliers for trigram counts depending on whitespace contents. + * Numbers based on analysis of real-life texts. + */ +static const float4 penalties[8] = { + 1.0f, /* "aaa" */ + 3.5f, /* "aa " */ + 0.0f, /* "a a" (impossible) */ + 0.0f, /* "a " (impossible) */ + 4.2f, /* " aa" */ + 2.1f, /* " a " */ + 25.0f, /* " a" */ + 0.0f /* " " (impossible) */ +}; + +/* Struct representing a single pg_wchar, converted back to multibyte form */ +typedef struct +{ + char bytes[MAX_MULTIBYTE_CHAR_LEN]; +} trgm_mb_char; + +/* + * Attributes of NFA colors: + * + * expandable - we know the character expansion of this color + * containsNonWord - color contains non-word characters + * (which will not be extracted into trigrams) + * wordCharsCount - count of word characters in color + * wordChars - array of this color's word characters + * (which can be extracted into trigrams) + * + * When expandable is false, the other attributes don't matter; we just + * assume this color represents unknown character(s). + */ +typedef struct +{ + bool expandable; + bool containsNonWord; + int wordCharsCount; + trgm_mb_char *wordChars; +} TrgmColorInfo; + +/* + * A "prefix" is information about the colors of the last two characters read + * before reaching a specific NFA state. These colors can have special values + * COLOR_UNKNOWN and COLOR_BLANK. COLOR_UNKNOWN means that we have no + * information, for example because we read some character of an unexpandable + * color. COLOR_BLANK means that we read a non-word character. + * + * We call a prefix ambiguous if at least one of its colors is unknown. It's + * fully ambiguous if both are unknown, partially ambiguous if only the first + * is unknown. (The case of first color known, second unknown is not valid.) + * + * Wholly- or partly-blank prefixes are mostly handled the same as regular + * color prefixes. This allows us to generate appropriate partly-blank + * trigrams when the NFA requires word character(s) to appear adjacent to + * non-word character(s). + */ +typedef int TrgmColor; + +/* We assume that colors returned by the regexp engine cannot be these: */ +#define COLOR_UNKNOWN (-3) +#define COLOR_BLANK (-4) + +typedef struct +{ + TrgmColor colors[2]; +} TrgmPrefix; + +/* + * Color-trigram data type. Note that some elements of the trigram can be + * COLOR_BLANK, but we don't allow COLOR_UNKNOWN. + */ +typedef struct +{ + TrgmColor colors[3]; +} ColorTrgm; + +/* + * Key identifying a state of our expanded graph: color prefix, and number + * of the corresponding state in the underlying regex NFA. The color prefix + * shows how we reached the regex state (to the extent that we know it). + */ +typedef struct +{ + TrgmPrefix prefix; + int nstate; +} TrgmStateKey; + +/* + * One state of the expanded graph. + * + * stateKey - ID of this state + * arcs - outgoing arcs of this state (List of TrgmArc) + * enterKeys - enter keys reachable from this state without reading any + * predictable trigram (List of TrgmStateKey) + * flags - flag bits + * snumber - number of this state (initially assigned as -1, -2, etc, + * for debugging purposes only; then at the packaging stage, + * surviving states are renumbered with positive numbers) + * parent - parent state, if this state has been merged into another + * tentFlags - flags this state would acquire via planned merges + * tentParent - planned parent state, if considering a merge + */ +#define TSTATE_INIT 0x01 /* flag indicating this state is initial */ +#define TSTATE_FIN 0x02 /* flag indicating this state is final */ + +typedef struct TrgmState +{ + TrgmStateKey stateKey; /* hashtable key: must be first field */ + List *arcs; + List *enterKeys; + int flags; + int snumber; + struct TrgmState *parent; + int tentFlags; + struct TrgmState *tentParent; +} TrgmState; + +/* + * One arc in the expanded graph. + */ +typedef struct +{ + ColorTrgm ctrgm; /* trigram needed to traverse arc */ + TrgmState *target; /* next state */ +} TrgmArc; + +/* + * Information about arc of specific color trigram (used in stage 3) + * + * Contains pointers to the source and target states. + */ +typedef struct +{ + TrgmState *source; + TrgmState *target; +} TrgmArcInfo; + +/* + * Information about color trigram (used in stage 3) + * + * ctrgm - trigram itself + * cnumber - number of this trigram (used in the packaging stage) + * count - number of simple trigrams created from this color trigram + * expanded - indicates this color trigram is expanded into simple trigrams + * arcs - list of all arcs labeled with this color trigram. + */ +typedef struct +{ + ColorTrgm ctrgm; + int cnumber; + int count; + float4 penalty; + bool expanded; + List *arcs; +} ColorTrgmInfo; + +/* + * Data structure representing all the data we need during regex processing. + * + * regex - compiled regex + * colorInfo - extracted information about regex's colors + * ncolors - number of colors in colorInfo[] + * states - hashtable of TrgmStates (states of expanded graph) + * initState - pointer to initial state of expanded graph + * queue - queue of to-be-processed TrgmStates + * keysQueue - queue of to-be-processed TrgmStateKeys + * arcsCount - total number of arcs of expanded graph (for resource + * limiting) + * overflowed - we have exceeded resource limit for transformation + * colorTrgms - array of all color trigrams present in graph + * colorTrgmsCount - count of those color trigrams + * totalTrgmCount - total count of extracted simple trigrams + */ +typedef struct +{ + /* Source regexp, and color information extracted from it (stage 1) */ + regex_t *regex; + TrgmColorInfo *colorInfo; + int ncolors; + + /* Expanded graph (stage 2) */ + HTAB *states; + TrgmState *initState; + int nstates; + + /* Workspace for stage 2 */ + List *queue; + List *keysQueue; + int arcsCount; + bool overflowed; + + /* Information about distinct color trigrams in the graph (stage 3) */ + ColorTrgmInfo *colorTrgms; + int colorTrgmsCount; + int totalTrgmCount; +} TrgmNFA; + +/* + * Final, compact representation of expanded graph. + */ +typedef struct +{ + int targetState; /* index of target state (zero-based) */ + int colorTrgm; /* index of color trigram for transition */ +} TrgmPackedArc; + +typedef struct +{ + int arcsCount; /* number of out-arcs for this state */ + TrgmPackedArc *arcs; /* array of arcsCount packed arcs */ +} TrgmPackedState; + +/* "typedef struct TrgmPackedGraph TrgmPackedGraph" appears in trgm.h */ +struct TrgmPackedGraph +{ + /* + * colorTrigramsCount and colorTrigramGroups contain information about how + * trigrams are grouped into color trigrams. "colorTrigramsCount" is the + * count of color trigrams and "colorTrigramGroups" contains number of + * simple trigrams for each color trigram. The array of simple trigrams + * (stored separately from this struct) is ordered so that the simple + * trigrams for each color trigram are consecutive, and they're in order + * by color trigram number. + */ + int colorTrigramsCount; + int *colorTrigramGroups; /* array of size colorTrigramsCount */ + + /* + * The states of the simplified NFA. State number 0 is always initial + * state and state number 1 is always final state. + */ + int statesCount; + TrgmPackedState *states; /* array of size statesCount */ + + /* Temporary work space for trigramsMatchGraph() */ + bool *colorTrigramsActive; /* array of size colorTrigramsCount */ + bool *statesActive; /* array of size statesCount */ + int *statesQueue; /* array of size statesCount */ +}; + +/* + * Temporary structure for representing an arc during packaging. + */ +typedef struct +{ + int sourceState; + int targetState; + int colorTrgm; +} TrgmPackArcInfo; + + +/* prototypes for private functions */ +static TRGM *createTrgmNFAInternal(regex_t *regex, TrgmPackedGraph **graph, + MemoryContext rcontext); +static void RE_compile(regex_t *regex, text *text_re, + int cflags, Oid collation); +static void getColorInfo(regex_t *regex, TrgmNFA *trgmNFA); +static bool convertPgWchar(pg_wchar c, trgm_mb_char *result); +static void transformGraph(TrgmNFA *trgmNFA); +static void processState(TrgmNFA *trgmNFA, TrgmState *state); +static void addKey(TrgmNFA *trgmNFA, TrgmState *state, TrgmStateKey *key); +static void addKeyToQueue(TrgmNFA *trgmNFA, TrgmStateKey *key); +static void addArcs(TrgmNFA *trgmNFA, TrgmState *state); +static void addArc(TrgmNFA *trgmNFA, TrgmState *state, TrgmStateKey *key, + TrgmColor co, TrgmStateKey *destKey); +static bool validArcLabel(TrgmStateKey *key, TrgmColor co); +static TrgmState *getState(TrgmNFA *trgmNFA, TrgmStateKey *key); +static bool prefixContains(TrgmPrefix *prefix1, TrgmPrefix *prefix2); +static bool selectColorTrigrams(TrgmNFA *trgmNFA); +static TRGM *expandColorTrigrams(TrgmNFA *trgmNFA, MemoryContext rcontext); +static void fillTrgm(trgm *ptrgm, trgm_mb_char s[3]); +static void mergeStates(TrgmState *state1, TrgmState *state2); +static int colorTrgmInfoCmp(const void *p1, const void *p2); +static int colorTrgmInfoPenaltyCmp(const void *p1, const void *p2); +static TrgmPackedGraph *packGraph(TrgmNFA *trgmNFA, MemoryContext rcontext); +static int packArcInfoCmp(const void *a1, const void *a2); + +#ifdef TRGM_REGEXP_DEBUG +static void printSourceNFA(regex_t *regex, TrgmColorInfo *colors, int ncolors); +static void printTrgmNFA(TrgmNFA *trgmNFA); +static void printTrgmColor(StringInfo buf, TrgmColor co); +static void printTrgmPackedGraph(TrgmPackedGraph *packedGraph, TRGM *trigrams); +#endif + + +/* + * Main entry point to process a regular expression. + * + * Returns an array of trigrams required by the regular expression, or NULL if + * the regular expression was too complex to analyze. In addition, a packed + * graph representation of the regex is returned into *graph. The results + * must be allocated in rcontext (which might or might not be the current + * context). + */ +TRGM * +createTrgmNFA(text *text_re, Oid collation, + TrgmPackedGraph **graph, MemoryContext rcontext) +{ + TRGM *trg; + regex_t regex; + MemoryContext tmpcontext; + MemoryContext oldcontext; + + /* + * This processing generates a great deal of cruft, which we'd like to + * clean up before returning (since this function may be called in a + * query-lifespan memory context). Make a temp context we can work in so + * that cleanup is easy. + */ + tmpcontext = AllocSetContextCreate(CurrentMemoryContext, + "createTrgmNFA temporary context", + ALLOCSET_DEFAULT_SIZES); + oldcontext = MemoryContextSwitchTo(tmpcontext); + + /* + * Stage 1: Compile the regexp into a NFA, using the regexp library. + */ +#ifdef IGNORECASE + RE_compile(®ex, text_re, + REG_ADVANCED | REG_NOSUB | REG_ICASE, collation); +#else + RE_compile(®ex, text_re, + REG_ADVANCED | REG_NOSUB, collation); +#endif + + trg = createTrgmNFAInternal(®ex, graph, rcontext); + + /* Clean up all the cruft we created (including regex) */ + MemoryContextSwitchTo(oldcontext); + MemoryContextDelete(tmpcontext); + + return trg; +} + +/* + * Body of createTrgmNFA, exclusive of regex compilation/freeing. + */ +static TRGM * +createTrgmNFAInternal(regex_t *regex, TrgmPackedGraph **graph, + MemoryContext rcontext) +{ + TRGM *trg; + TrgmNFA trgmNFA; + + trgmNFA.regex = regex; + + /* Collect color information from the regex */ + getColorInfo(regex, &trgmNFA); + +#ifdef TRGM_REGEXP_DEBUG + printSourceNFA(regex, trgmNFA.colorInfo, trgmNFA.ncolors); +#endif + + /* + * Stage 2: Create an expanded graph from the source NFA. + */ + transformGraph(&trgmNFA); + +#ifdef TRGM_REGEXP_DEBUG + printTrgmNFA(&trgmNFA); +#endif + + /* + * Fail if we were unable to make a nontrivial graph, ie it is possible to + * get from the initial state to the final state without reading any + * predictable trigram. + */ + if (trgmNFA.initState->flags & TSTATE_FIN) + return NULL; + + /* + * Stage 3: Select color trigrams to expand. Fail if too many trigrams. + */ + if (!selectColorTrigrams(&trgmNFA)) + return NULL; + + /* + * Stage 4: Expand color trigrams and pack graph into final + * representation. + */ + trg = expandColorTrigrams(&trgmNFA, rcontext); + + *graph = packGraph(&trgmNFA, rcontext); + +#ifdef TRGM_REGEXP_DEBUG + printTrgmPackedGraph(*graph, trg); +#endif + + return trg; +} + +/* + * Main entry point for evaluating a graph during index scanning. + * + * The check[] array is indexed by trigram number (in the array of simple + * trigrams returned by createTrgmNFA), and holds true for those trigrams + * that are present in the index entry being checked. + */ +bool +trigramsMatchGraph(TrgmPackedGraph *graph, bool *check) +{ + int i, + j, + k, + queueIn, + queueOut; + + /* + * Reset temporary working areas. + */ + memset(graph->colorTrigramsActive, 0, + sizeof(bool) * graph->colorTrigramsCount); + memset(graph->statesActive, 0, sizeof(bool) * graph->statesCount); + + /* + * Check which color trigrams were matched. A match for any simple + * trigram associated with a color trigram counts as a match of the color + * trigram. + */ + j = 0; + for (i = 0; i < graph->colorTrigramsCount; i++) + { + int cnt = graph->colorTrigramGroups[i]; + + for (k = j; k < j + cnt; k++) + { + if (check[k]) + { + /* + * Found one matched trigram in the group. Can skip the rest + * of them and go to the next group. + */ + graph->colorTrigramsActive[i] = true; + break; + } + } + j = j + cnt; + } + + /* + * Initialize the statesQueue to hold just the initial state. Note: + * statesQueue has room for statesCount entries, which is certainly enough + * since no state will be put in the queue more than once. The + * statesActive array marks which states have been queued. + */ + graph->statesActive[0] = true; + graph->statesQueue[0] = 0; + queueIn = 0; + queueOut = 1; + + /* Process queued states as long as there are any. */ + while (queueIn < queueOut) + { + int stateno = graph->statesQueue[queueIn++]; + TrgmPackedState *state = &graph->states[stateno]; + int cnt = state->arcsCount; + + /* Loop over state's out-arcs */ + for (i = 0; i < cnt; i++) + { + TrgmPackedArc *arc = &state->arcs[i]; + + /* + * If corresponding color trigram is present then activate the + * corresponding state. We're done if that's the final state, + * otherwise queue the state if it's not been queued already. + */ + if (graph->colorTrigramsActive[arc->colorTrgm]) + { + int nextstate = arc->targetState; + + if (nextstate == 1) + return true; /* success: final state is reachable */ + + if (!graph->statesActive[nextstate]) + { + graph->statesActive[nextstate] = true; + graph->statesQueue[queueOut++] = nextstate; + } + } + } + } + + /* Queue is empty, so match fails. */ + return false; +} + +/* + * Compile regex string into struct at *regex. + * NB: pg_regfree must be applied to regex if this completes successfully. + */ +static void +RE_compile(regex_t *regex, text *text_re, int cflags, Oid collation) +{ + int text_re_len = VARSIZE_ANY_EXHDR(text_re); + char *text_re_val = VARDATA_ANY(text_re); + pg_wchar *pattern; + int pattern_len; + int regcomp_result; + char errMsg[100]; + + /* Convert pattern string to wide characters */ + pattern = (pg_wchar *) palloc((text_re_len + 1) * sizeof(pg_wchar)); + pattern_len = pg_mb2wchar_with_len(text_re_val, + pattern, + text_re_len); + + /* Compile regex */ + regcomp_result = pg_regcomp(regex, + pattern, + pattern_len, + cflags, + collation); + + pfree(pattern); + + if (regcomp_result != REG_OKAY) + { + /* re didn't compile (no need for pg_regfree, if so) */ + pg_regerror(regcomp_result, regex, errMsg, sizeof(errMsg)); + ereport(ERROR, + (errcode(ERRCODE_INVALID_REGULAR_EXPRESSION), + errmsg("invalid regular expression: %s", errMsg))); + } +} + + +/*--------------------- + * Subroutines for pre-processing the color map (stage 1). + *--------------------- + */ + +/* + * Fill TrgmColorInfo structure for each color using regex export functions. + */ +static void +getColorInfo(regex_t *regex, TrgmNFA *trgmNFA) +{ + int colorsCount = pg_reg_getnumcolors(regex); + int i; + + trgmNFA->ncolors = colorsCount; + trgmNFA->colorInfo = (TrgmColorInfo *) + palloc0(colorsCount * sizeof(TrgmColorInfo)); + + /* + * Loop over colors, filling TrgmColorInfo about each. Note we include + * WHITE (0) even though we know it'll be reported as non-expandable. + */ + for (i = 0; i < colorsCount; i++) + { + TrgmColorInfo *colorInfo = &trgmNFA->colorInfo[i]; + int charsCount = pg_reg_getnumcharacters(regex, i); + pg_wchar *chars; + int j; + + if (charsCount < 0 || charsCount > COLOR_COUNT_LIMIT) + { + /* Non expandable, or too large to work with */ + colorInfo->expandable = false; + continue; + } + + colorInfo->expandable = true; + colorInfo->containsNonWord = false; + colorInfo->wordChars = (trgm_mb_char *) + palloc(sizeof(trgm_mb_char) * charsCount); + colorInfo->wordCharsCount = 0; + + /* Extract all the chars in this color */ + chars = (pg_wchar *) palloc(sizeof(pg_wchar) * charsCount); + pg_reg_getcharacters(regex, i, chars, charsCount); + + /* + * Convert characters back to multibyte form, and save only those that + * are word characters. Set "containsNonWord" if any non-word + * character. (Note: it'd probably be nicer to keep the chars in + * pg_wchar format for now, but ISWORDCHR wants to see multibyte.) + */ + for (j = 0; j < charsCount; j++) + { + trgm_mb_char c; + + if (!convertPgWchar(chars[j], &c)) + continue; /* ok to ignore it altogether */ + if (ISWORDCHR(c.bytes)) + colorInfo->wordChars[colorInfo->wordCharsCount++] = c; + else + colorInfo->containsNonWord = true; + } + + pfree(chars); + } +} + +/* + * Convert pg_wchar to multibyte format. + * Returns false if the character should be ignored completely. + */ +static bool +convertPgWchar(pg_wchar c, trgm_mb_char *result) +{ + /* "s" has enough space for a multibyte character and a trailing NUL */ + char s[MAX_MULTIBYTE_CHAR_LEN + 1]; + + /* + * We can ignore the NUL character, since it can never appear in a PG text + * string. This avoids the need for various special cases when + * reconstructing trigrams. + */ + if (c == 0) + return false; + + /* Do the conversion, making sure the result is NUL-terminated */ + memset(s, 0, sizeof(s)); + pg_wchar2mb_with_len(&c, s, 1); + + /* + * In IGNORECASE mode, we can ignore uppercase characters. We assume that + * the regex engine generated both uppercase and lowercase equivalents + * within each color, since we used the REG_ICASE option; so there's no + * need to process the uppercase version. + * + * XXX this code is dependent on the assumption that lowerstr() works the + * same as the regex engine's internal case folding machinery. Might be + * wiser to expose pg_wc_tolower and test whether c == pg_wc_tolower(c). + * On the other hand, the trigrams in the index were created using + * lowerstr(), so we're probably screwed if there's any incompatibility + * anyway. + */ +#ifdef IGNORECASE + { + char *lowerCased = lowerstr(s); + + if (strcmp(lowerCased, s) != 0) + { + pfree(lowerCased); + return false; + } + pfree(lowerCased); + } +#endif + + /* Fill result with exactly MAX_MULTIBYTE_CHAR_LEN bytes */ + memcpy(result->bytes, s, MAX_MULTIBYTE_CHAR_LEN); + return true; +} + + +/*--------------------- + * Subroutines for expanding original NFA graph into a trigram graph (stage 2). + *--------------------- + */ + +/* + * Transform the graph, given a regex and extracted color information. + * + * We create and process a queue of expanded-graph states until all the states + * are processed. + * + * This algorithm may be stopped due to resource limitation. In this case we + * force every unprocessed branch to immediately finish with matching (this + * can give us false positives but no false negatives) by marking all + * unprocessed states as final. + */ +static void +transformGraph(TrgmNFA *trgmNFA) +{ + HASHCTL hashCtl; + TrgmStateKey initkey; + TrgmState *initstate; + ListCell *lc; + + /* Initialize this stage's workspace in trgmNFA struct */ + trgmNFA->queue = NIL; + trgmNFA->keysQueue = NIL; + trgmNFA->arcsCount = 0; + trgmNFA->overflowed = false; + + /* Create hashtable for states */ + hashCtl.keysize = sizeof(TrgmStateKey); + hashCtl.entrysize = sizeof(TrgmState); + hashCtl.hcxt = CurrentMemoryContext; + trgmNFA->states = hash_create("Trigram NFA", + 1024, + &hashCtl, + HASH_ELEM | HASH_BLOBS | HASH_CONTEXT); + trgmNFA->nstates = 0; + + /* Create initial state: ambiguous prefix, NFA's initial state */ + MemSet(&initkey, 0, sizeof(initkey)); + initkey.prefix.colors[0] = COLOR_UNKNOWN; + initkey.prefix.colors[1] = COLOR_UNKNOWN; + initkey.nstate = pg_reg_getinitialstate(trgmNFA->regex); + + initstate = getState(trgmNFA, &initkey); + initstate->flags |= TSTATE_INIT; + trgmNFA->initState = initstate; + + /* + * Recursively build the expanded graph by processing queue of states + * (breadth-first search). getState already put initstate in the queue. + * Note that getState will append new states to the queue within the loop, + * too; this works as long as we don't do repeat fetches using the "lc" + * pointer. + */ + foreach(lc, trgmNFA->queue) + { + TrgmState *state = (TrgmState *) lfirst(lc); + + /* + * If we overflowed then just mark state as final. Otherwise do + * actual processing. + */ + if (trgmNFA->overflowed) + state->flags |= TSTATE_FIN; + else + processState(trgmNFA, state); + + /* Did we overflow? */ + if (trgmNFA->arcsCount > MAX_EXPANDED_ARCS || + hash_get_num_entries(trgmNFA->states) > MAX_EXPANDED_STATES) + trgmNFA->overflowed = true; + } +} + +/* + * Process one state: add enter keys and then add outgoing arcs. + */ +static void +processState(TrgmNFA *trgmNFA, TrgmState *state) +{ + ListCell *lc; + + /* keysQueue should be NIL already, but make sure */ + trgmNFA->keysQueue = NIL; + + /* + * Add state's own key, and then process all keys added to keysQueue until + * queue is finished. But we can quit if the state gets marked final. + */ + addKey(trgmNFA, state, &state->stateKey); + foreach(lc, trgmNFA->keysQueue) + { + TrgmStateKey *key = (TrgmStateKey *) lfirst(lc); + + if (state->flags & TSTATE_FIN) + break; + addKey(trgmNFA, state, key); + } + + /* Release keysQueue to clean up for next cycle */ + list_free(trgmNFA->keysQueue); + trgmNFA->keysQueue = NIL; + + /* + * Add outgoing arcs only if state isn't final (we have no interest in + * outgoing arcs if we already match) + */ + if (!(state->flags & TSTATE_FIN)) + addArcs(trgmNFA, state); +} + +/* + * Add the given enter key into the state's enterKeys list, and determine + * whether this should result in any further enter keys being added. + * If so, add those keys to keysQueue so that processState will handle them. + * + * If the enter key is for the NFA's final state, mark state as TSTATE_FIN. + * This situation means that we can reach the final state from this expanded + * state without reading any predictable trigram, so we must consider this + * state as an accepting one. + * + * The given key could be a duplicate of one already in enterKeys, or be + * redundant with some enterKeys. So we check that before doing anything. + * + * Note that we don't generate any actual arcs here. addArcs will do that + * later, after we have identified all the enter keys for this state. + */ +static void +addKey(TrgmNFA *trgmNFA, TrgmState *state, TrgmStateKey *key) +{ + regex_arc_t *arcs; + TrgmStateKey destKey; + ListCell *cell; + int i, + arcsCount; + + /* + * Ensure any pad bytes in destKey are zero, since it may get used as a + * hashtable key by getState. + */ + MemSet(&destKey, 0, sizeof(destKey)); + + /* + * Compare key to each existing enter key of the state to check for + * redundancy. We can drop either old key(s) or the new key if we find + * redundancy. + */ + foreach(cell, state->enterKeys) + { + TrgmStateKey *existingKey = (TrgmStateKey *) lfirst(cell); + + if (existingKey->nstate == key->nstate) + { + if (prefixContains(&existingKey->prefix, &key->prefix)) + { + /* This old key already covers the new key. Nothing to do */ + return; + } + if (prefixContains(&key->prefix, &existingKey->prefix)) + { + /* + * The new key covers this old key. Remove the old key, it's + * no longer needed once we add this key to the list. + */ + state->enterKeys = foreach_delete_current(state->enterKeys, + cell); + } + } + } + + /* No redundancy, so add this key to the state's list */ + state->enterKeys = lappend(state->enterKeys, key); + + /* If state is now known final, mark it and we're done */ + if (key->nstate == pg_reg_getfinalstate(trgmNFA->regex)) + { + state->flags |= TSTATE_FIN; + return; + } + + /* + * Loop through all outgoing arcs of the corresponding state in the + * original NFA. + */ + arcsCount = pg_reg_getnumoutarcs(trgmNFA->regex, key->nstate); + arcs = (regex_arc_t *) palloc(sizeof(regex_arc_t) * arcsCount); + pg_reg_getoutarcs(trgmNFA->regex, key->nstate, arcs, arcsCount); + + for (i = 0; i < arcsCount; i++) + { + regex_arc_t *arc = &arcs[i]; + + if (pg_reg_colorisbegin(trgmNFA->regex, arc->co)) + { + /* + * Start of line/string (^). Trigram extraction treats start of + * line same as start of word: double space prefix is added. + * Hence, make an enter key showing we can reach the arc + * destination with all-blank prefix. + */ + destKey.prefix.colors[0] = COLOR_BLANK; + destKey.prefix.colors[1] = COLOR_BLANK; + destKey.nstate = arc->to; + + /* Add enter key to this state */ + addKeyToQueue(trgmNFA, &destKey); + } + else if (pg_reg_colorisend(trgmNFA->regex, arc->co)) + { + /* + * End of line/string ($). We must consider this arc as a + * transition that doesn't read anything. The reason for adding + * this enter key to the state is that if the arc leads to the + * NFA's final state, we must mark this expanded state as final. + */ + destKey.prefix.colors[0] = COLOR_UNKNOWN; + destKey.prefix.colors[1] = COLOR_UNKNOWN; + destKey.nstate = arc->to; + + /* Add enter key to this state */ + addKeyToQueue(trgmNFA, &destKey); + } + else if (arc->co >= 0) + { + /* Regular color (including WHITE) */ + TrgmColorInfo *colorInfo = &trgmNFA->colorInfo[arc->co]; + + if (colorInfo->expandable) + { + if (colorInfo->containsNonWord && + !validArcLabel(key, COLOR_BLANK)) + { + /* + * We can reach the arc destination after reading a + * non-word character, but the prefix is not something + * that addArc will accept with COLOR_BLANK, so no trigram + * arc can get made for this transition. We must make an + * enter key to show that the arc destination is + * reachable. Set it up with an all-blank prefix, since + * that corresponds to what the trigram extraction code + * will do at a word starting boundary. + */ + destKey.prefix.colors[0] = COLOR_BLANK; + destKey.prefix.colors[1] = COLOR_BLANK; + destKey.nstate = arc->to; + addKeyToQueue(trgmNFA, &destKey); + } + + if (colorInfo->wordCharsCount > 0 && + !validArcLabel(key, arc->co)) + { + /* + * We can reach the arc destination after reading a word + * character, but the prefix is not something that addArc + * will accept, so no trigram arc can get made for this + * transition. We must make an enter key to show that the + * arc destination is reachable. The prefix for the enter + * key should reflect the info we have for this arc. + */ + destKey.prefix.colors[0] = key->prefix.colors[1]; + destKey.prefix.colors[1] = arc->co; + destKey.nstate = arc->to; + addKeyToQueue(trgmNFA, &destKey); + } + } + else + { + /* + * Unexpandable color. Add enter key with ambiguous prefix, + * showing we can reach the destination from this state, but + * the preceding colors will be uncertain. (We do not set the + * first prefix color to key->prefix.colors[1], because a + * prefix of known followed by unknown is invalid.) + */ + destKey.prefix.colors[0] = COLOR_UNKNOWN; + destKey.prefix.colors[1] = COLOR_UNKNOWN; + destKey.nstate = arc->to; + addKeyToQueue(trgmNFA, &destKey); + } + } + else + { + /* RAINBOW: treat as unexpandable color */ + destKey.prefix.colors[0] = COLOR_UNKNOWN; + destKey.prefix.colors[1] = COLOR_UNKNOWN; + destKey.nstate = arc->to; + addKeyToQueue(trgmNFA, &destKey); + } + } + + pfree(arcs); +} + +/* + * Add copy of given key to keysQueue for later processing. + */ +static void +addKeyToQueue(TrgmNFA *trgmNFA, TrgmStateKey *key) +{ + TrgmStateKey *keyCopy = (TrgmStateKey *) palloc(sizeof(TrgmStateKey)); + + memcpy(keyCopy, key, sizeof(TrgmStateKey)); + trgmNFA->keysQueue = lappend(trgmNFA->keysQueue, keyCopy); +} + +/* + * Add outgoing arcs from given state, whose enter keys are all now known. + */ +static void +addArcs(TrgmNFA *trgmNFA, TrgmState *state) +{ + TrgmStateKey destKey; + ListCell *cell; + regex_arc_t *arcs; + int arcsCount, + i; + + /* + * Ensure any pad bytes in destKey are zero, since it may get used as a + * hashtable key by getState. + */ + MemSet(&destKey, 0, sizeof(destKey)); + + /* + * Iterate over enter keys associated with this expanded-graph state. This + * includes both the state's own stateKey, and any enter keys we added to + * it during addKey (which represent expanded-graph states that are not + * distinguishable from this one by means of trigrams). For each such + * enter key, examine all the out-arcs of the key's underlying NFA state, + * and try to make a trigram arc leading to where the out-arc leads. + * (addArc will deal with whether the arc is valid or not.) + */ + foreach(cell, state->enterKeys) + { + TrgmStateKey *key = (TrgmStateKey *) lfirst(cell); + + arcsCount = pg_reg_getnumoutarcs(trgmNFA->regex, key->nstate); + arcs = (regex_arc_t *) palloc(sizeof(regex_arc_t) * arcsCount); + pg_reg_getoutarcs(trgmNFA->regex, key->nstate, arcs, arcsCount); + + for (i = 0; i < arcsCount; i++) + { + regex_arc_t *arc = &arcs[i]; + TrgmColorInfo *colorInfo; + + /* + * Ignore non-expandable colors; addKey already handled the case. + * + * We need no special check for WHITE or begin/end pseudocolors + * here. We don't need to do any processing for them, and they + * will be marked non-expandable since the regex engine will have + * reported them that way. We do have to watch out for RAINBOW, + * which has a negative color number. + */ + if (arc->co < 0) + continue; + Assert(arc->co < trgmNFA->ncolors); + + colorInfo = &trgmNFA->colorInfo[arc->co]; + if (!colorInfo->expandable) + continue; + + if (colorInfo->containsNonWord) + { + /* + * Color includes non-word character(s). + * + * Generate an arc, treating this transition as occurring on + * BLANK. This allows word-ending trigrams to be manufactured + * if possible. + */ + destKey.prefix.colors[0] = key->prefix.colors[1]; + destKey.prefix.colors[1] = COLOR_BLANK; + destKey.nstate = arc->to; + + addArc(trgmNFA, state, key, COLOR_BLANK, &destKey); + } + + if (colorInfo->wordCharsCount > 0) + { + /* + * Color includes word character(s). + * + * Generate an arc. Color is pushed into prefix of target + * state. + */ + destKey.prefix.colors[0] = key->prefix.colors[1]; + destKey.prefix.colors[1] = arc->co; + destKey.nstate = arc->to; + + addArc(trgmNFA, state, key, arc->co, &destKey); + } + } + + pfree(arcs); + } +} + +/* + * Generate an out-arc of the expanded graph, if it's valid and not redundant. + * + * state: expanded-graph state we want to add an out-arc to + * key: provides prefix colors (key->nstate is not used) + * co: transition color + * destKey: identifier for destination state of expanded graph + */ +static void +addArc(TrgmNFA *trgmNFA, TrgmState *state, TrgmStateKey *key, + TrgmColor co, TrgmStateKey *destKey) +{ + TrgmArc *arc; + ListCell *cell; + + /* Do nothing if this wouldn't be a valid arc label trigram */ + if (!validArcLabel(key, co)) + return; + + /* + * Check if we are going to reach key which is covered by a key which is + * already listed in this state. If so arc is useless: the NFA can bypass + * it through a path that doesn't require any predictable trigram, so + * whether the arc's trigram is present or not doesn't really matter. + */ + foreach(cell, state->enterKeys) + { + TrgmStateKey *existingKey = (TrgmStateKey *) lfirst(cell); + + if (existingKey->nstate == destKey->nstate && + prefixContains(&existingKey->prefix, &destKey->prefix)) + return; + } + + /* Checks were successful, add new arc */ + arc = (TrgmArc *) palloc(sizeof(TrgmArc)); + arc->target = getState(trgmNFA, destKey); + arc->ctrgm.colors[0] = key->prefix.colors[0]; + arc->ctrgm.colors[1] = key->prefix.colors[1]; + arc->ctrgm.colors[2] = co; + + state->arcs = lappend(state->arcs, arc); + trgmNFA->arcsCount++; +} + +/* + * Can we make a valid trigram arc label from the given prefix and arc color? + * + * This is split out so that tests in addKey and addArc will stay in sync. + */ +static bool +validArcLabel(TrgmStateKey *key, TrgmColor co) +{ + /* + * We have to know full trigram in order to add outgoing arc. So we can't + * do it if prefix is ambiguous. + */ + if (key->prefix.colors[0] == COLOR_UNKNOWN) + return false; + + /* If key->prefix.colors[0] isn't unknown, its second color isn't either */ + Assert(key->prefix.colors[1] != COLOR_UNKNOWN); + /* And we should not be called with an unknown arc color anytime */ + Assert(co != COLOR_UNKNOWN); + + /* + * We don't bother with making arcs representing three non-word + * characters, since that's useless for trigram extraction. + */ + if (key->prefix.colors[0] == COLOR_BLANK && + key->prefix.colors[1] == COLOR_BLANK && + co == COLOR_BLANK) + return false; + + /* + * We also reject nonblank-blank-anything. The nonblank-blank-nonblank + * case doesn't correspond to any trigram the trigram extraction code + * would make. The nonblank-blank-blank case is also not possible with + * RPADDING = 1. (Note that in many cases we'd fail to generate such a + * trigram even if it were valid, for example processing "foo bar" will + * not result in considering the trigram "o ". So if you want to support + * RPADDING = 2, there's more to do than just twiddle this test.) + */ + if (key->prefix.colors[0] != COLOR_BLANK && + key->prefix.colors[1] == COLOR_BLANK) + return false; + + /* + * Other combinations involving blank are valid, in particular we assume + * blank-blank-nonblank is valid, which presumes that LPADDING is 2. + * + * Note: Using again the example "foo bar", we will not consider the + * trigram " b", though this trigram would be found by the trigram + * extraction code. Since we will find " ba", it doesn't seem worth + * trying to hack the algorithm to generate the additional trigram. + */ + + /* arc label is valid */ + return true; +} + +/* + * Get state of expanded graph for given state key, + * and queue the state for processing if it didn't already exist. + */ +static TrgmState * +getState(TrgmNFA *trgmNFA, TrgmStateKey *key) +{ + TrgmState *state; + bool found; + + state = (TrgmState *) hash_search(trgmNFA->states, key, HASH_ENTER, + &found); + if (!found) + { + /* New state: initialize and queue it */ + state->arcs = NIL; + state->enterKeys = NIL; + state->flags = 0; + /* states are initially given negative numbers */ + state->snumber = -(++trgmNFA->nstates); + state->parent = NULL; + state->tentFlags = 0; + state->tentParent = NULL; + + trgmNFA->queue = lappend(trgmNFA->queue, state); + } + return state; +} + +/* + * Check if prefix1 "contains" prefix2. + * + * "contains" means that any exact prefix (with no ambiguity) that satisfies + * prefix2 also satisfies prefix1. + */ +static bool +prefixContains(TrgmPrefix *prefix1, TrgmPrefix *prefix2) +{ + if (prefix1->colors[1] == COLOR_UNKNOWN) + { + /* Fully ambiguous prefix contains everything */ + return true; + } + else if (prefix1->colors[0] == COLOR_UNKNOWN) + { + /* + * Prefix with only first unknown color contains every prefix with + * same second color. + */ + if (prefix1->colors[1] == prefix2->colors[1]) + return true; + else + return false; + } + else + { + /* Exact prefix contains only the exact same prefix */ + if (prefix1->colors[0] == prefix2->colors[0] && + prefix1->colors[1] == prefix2->colors[1]) + return true; + else + return false; + } +} + + +/*--------------------- + * Subroutines for expanding color trigrams into regular trigrams (stage 3). + *--------------------- + */ + +/* + * Get vector of all color trigrams in graph and select which of them + * to expand into simple trigrams. + * + * Returns true if OK, false if exhausted resource limits. + */ +static bool +selectColorTrigrams(TrgmNFA *trgmNFA) +{ + HASH_SEQ_STATUS scan_status; + int arcsCount = trgmNFA->arcsCount, + i; + TrgmState *state; + ColorTrgmInfo *colorTrgms; + int64 totalTrgmCount; + float4 totalTrgmPenalty; + int cnumber; + + /* Collect color trigrams from all arcs */ + colorTrgms = (ColorTrgmInfo *) palloc0(sizeof(ColorTrgmInfo) * arcsCount); + trgmNFA->colorTrgms = colorTrgms; + + i = 0; + hash_seq_init(&scan_status, trgmNFA->states); + while ((state = (TrgmState *) hash_seq_search(&scan_status)) != NULL) + { + ListCell *cell; + + foreach(cell, state->arcs) + { + TrgmArc *arc = (TrgmArc *) lfirst(cell); + TrgmArcInfo *arcInfo = (TrgmArcInfo *) palloc(sizeof(TrgmArcInfo)); + ColorTrgmInfo *trgmInfo = &colorTrgms[i]; + + arcInfo->source = state; + arcInfo->target = arc->target; + trgmInfo->ctrgm = arc->ctrgm; + trgmInfo->cnumber = -1; + /* count and penalty will be set below */ + trgmInfo->expanded = true; + trgmInfo->arcs = list_make1(arcInfo); + i++; + } + } + Assert(i == arcsCount); + + /* Remove duplicates, merging their arcs lists */ + if (arcsCount >= 2) + { + ColorTrgmInfo *p1, + *p2; + + /* Sort trigrams to ease duplicate detection */ + qsort(colorTrgms, arcsCount, sizeof(ColorTrgmInfo), colorTrgmInfoCmp); + + /* p1 is probe point, p2 is last known non-duplicate. */ + p2 = colorTrgms; + for (p1 = colorTrgms + 1; p1 < colorTrgms + arcsCount; p1++) + { + if (colorTrgmInfoCmp(p1, p2) > 0) + { + p2++; + *p2 = *p1; + } + else + { + p2->arcs = list_concat(p2->arcs, p1->arcs); + } + } + trgmNFA->colorTrgmsCount = (p2 - colorTrgms) + 1; + } + else + { + trgmNFA->colorTrgmsCount = arcsCount; + } + + /* + * Count number of simple trigrams generated by each color trigram, and + * also compute a penalty value, which is the number of simple trigrams + * times a multiplier that depends on its whitespace content. + * + * Note: per-color-trigram counts cannot overflow an int so long as + * COLOR_COUNT_LIMIT is not more than the cube root of INT_MAX, ie about + * 1290. However, the grand total totalTrgmCount might conceivably + * overflow an int, so we use int64 for that within this routine. Also, + * penalties are calculated in float4 arithmetic to avoid any overflow + * worries. + */ + totalTrgmCount = 0; + totalTrgmPenalty = 0.0f; + for (i = 0; i < trgmNFA->colorTrgmsCount; i++) + { + ColorTrgmInfo *trgmInfo = &colorTrgms[i]; + int j, + count = 1, + typeIndex = 0; + + for (j = 0; j < 3; j++) + { + TrgmColor c = trgmInfo->ctrgm.colors[j]; + + typeIndex *= 2; + if (c == COLOR_BLANK) + typeIndex++; + else + count *= trgmNFA->colorInfo[c].wordCharsCount; + } + trgmInfo->count = count; + totalTrgmCount += count; + trgmInfo->penalty = penalties[typeIndex] * (float4) count; + totalTrgmPenalty += trgmInfo->penalty; + } + + /* Sort color trigrams in descending order of their penalties */ + qsort(colorTrgms, trgmNFA->colorTrgmsCount, sizeof(ColorTrgmInfo), + colorTrgmInfoPenaltyCmp); + + /* + * Remove color trigrams from the graph so long as total penalty of color + * trigrams exceeds WISH_TRGM_PENALTY. (If we fail to get down to + * WISH_TRGM_PENALTY, it's OK so long as total count is no more than + * MAX_TRGM_COUNT.) We prefer to remove color trigrams with higher + * penalty, since those are the most promising for reducing the total + * penalty. When removing a color trigram we have to merge states + * connected by arcs labeled with that trigram. It's necessary to not + * merge initial and final states, because our graph becomes useless if + * that happens; so we cannot always remove the trigram we'd prefer to. + */ + for (i = 0; i < trgmNFA->colorTrgmsCount; i++) + { + ColorTrgmInfo *trgmInfo = &colorTrgms[i]; + bool canRemove = true; + ListCell *cell; + + /* Done if we've reached the target */ + if (totalTrgmPenalty <= WISH_TRGM_PENALTY) + break; + +#ifdef TRGM_REGEXP_DEBUG + fprintf(stderr, "considering ctrgm %d %d %d, penalty %f, %d arcs\n", + trgmInfo->ctrgm.colors[0], + trgmInfo->ctrgm.colors[1], + trgmInfo->ctrgm.colors[2], + trgmInfo->penalty, + list_length(trgmInfo->arcs)); +#endif + + /* + * Does any arc of this color trigram connect initial and final + * states? If so we can't remove it. + */ + foreach(cell, trgmInfo->arcs) + { + TrgmArcInfo *arcInfo = (TrgmArcInfo *) lfirst(cell); + TrgmState *source = arcInfo->source, + *target = arcInfo->target; + int source_flags, + target_flags; + +#ifdef TRGM_REGEXP_DEBUG + fprintf(stderr, "examining arc to s%d (%x) from s%d (%x)\n", + -target->snumber, target->flags, + -source->snumber, source->flags); +#endif + + /* examine parent states, if any merging has already happened */ + while (source->parent) + source = source->parent; + while (target->parent) + target = target->parent; + +#ifdef TRGM_REGEXP_DEBUG + fprintf(stderr, " ... after completed merges: to s%d (%x) from s%d (%x)\n", + -target->snumber, target->flags, + -source->snumber, source->flags); +#endif + + /* we must also consider merges we are planning right now */ + source_flags = source->flags | source->tentFlags; + while (source->tentParent) + { + source = source->tentParent; + source_flags |= source->flags | source->tentFlags; + } + target_flags = target->flags | target->tentFlags; + while (target->tentParent) + { + target = target->tentParent; + target_flags |= target->flags | target->tentFlags; + } + +#ifdef TRGM_REGEXP_DEBUG + fprintf(stderr, " ... after tentative merges: to s%d (%x) from s%d (%x)\n", + -target->snumber, target_flags, + -source->snumber, source_flags); +#endif + + /* would fully-merged state have both INIT and FIN set? */ + if (((source_flags | target_flags) & (TSTATE_INIT | TSTATE_FIN)) == + (TSTATE_INIT | TSTATE_FIN)) + { + canRemove = false; + break; + } + + /* ok so far, so remember planned merge */ + if (source != target) + { +#ifdef TRGM_REGEXP_DEBUG + fprintf(stderr, " ... tentatively merging s%d into s%d\n", + -target->snumber, -source->snumber); +#endif + target->tentParent = source; + source->tentFlags |= target_flags; + } + } + + /* + * We must reset all the tentFlags/tentParent fields before + * continuing. tentFlags could only have become set in states that + * are the source or parent or tentative parent of one of the current + * arcs; likewise tentParent could only have become set in states that + * are the target or parent or tentative parent of one of the current + * arcs. There might be some overlap between those sets, but if we + * clear tentFlags in target states as well as source states, we + * should be okay even if we visit a state as target before visiting + * it as a source. + */ + foreach(cell, trgmInfo->arcs) + { + TrgmArcInfo *arcInfo = (TrgmArcInfo *) lfirst(cell); + TrgmState *source = arcInfo->source, + *target = arcInfo->target; + TrgmState *ttarget; + + /* no need to touch previously-merged states */ + while (source->parent) + source = source->parent; + while (target->parent) + target = target->parent; + + while (source) + { + source->tentFlags = 0; + source = source->tentParent; + } + + while ((ttarget = target->tentParent) != NULL) + { + target->tentParent = NULL; + target->tentFlags = 0; /* in case it was also a source */ + target = ttarget; + } + } + + /* Now, move on if we can't drop this trigram */ + if (!canRemove) + { +#ifdef TRGM_REGEXP_DEBUG + fprintf(stderr, " ... not ok to merge\n"); +#endif + continue; + } + + /* OK, merge states linked by each arc labeled by the trigram */ + foreach(cell, trgmInfo->arcs) + { + TrgmArcInfo *arcInfo = (TrgmArcInfo *) lfirst(cell); + TrgmState *source = arcInfo->source, + *target = arcInfo->target; + + while (source->parent) + source = source->parent; + while (target->parent) + target = target->parent; + if (source != target) + { +#ifdef TRGM_REGEXP_DEBUG + fprintf(stderr, "merging s%d into s%d\n", + -target->snumber, -source->snumber); +#endif + mergeStates(source, target); + /* Assert we didn't merge initial and final states */ + Assert((source->flags & (TSTATE_INIT | TSTATE_FIN)) != + (TSTATE_INIT | TSTATE_FIN)); + } + } + + /* Mark trigram unexpanded, and update totals */ + trgmInfo->expanded = false; + totalTrgmCount -= trgmInfo->count; + totalTrgmPenalty -= trgmInfo->penalty; + } + + /* Did we succeed in fitting into MAX_TRGM_COUNT? */ + if (totalTrgmCount > MAX_TRGM_COUNT) + return false; + + trgmNFA->totalTrgmCount = (int) totalTrgmCount; + + /* + * Sort color trigrams by colors (will be useful for bsearch in packGraph) + * and enumerate the color trigrams that are expanded. + */ + cnumber = 0; + qsort(colorTrgms, trgmNFA->colorTrgmsCount, sizeof(ColorTrgmInfo), + colorTrgmInfoCmp); + for (i = 0; i < trgmNFA->colorTrgmsCount; i++) + { + if (colorTrgms[i].expanded) + { + colorTrgms[i].cnumber = cnumber; + cnumber++; + } + } + + return true; +} + +/* + * Expand selected color trigrams into regular trigrams. + * + * Returns the TRGM array to be passed to the index machinery. + * The array must be allocated in rcontext. + */ +static TRGM * +expandColorTrigrams(TrgmNFA *trgmNFA, MemoryContext rcontext) +{ + TRGM *trg; + trgm *p; + int i; + TrgmColorInfo blankColor; + trgm_mb_char blankChar; + + /* Set up "blank" color structure containing a single zero character */ + memset(blankChar.bytes, 0, sizeof(blankChar.bytes)); + blankColor.wordCharsCount = 1; + blankColor.wordChars = &blankChar; + + /* Construct the trgm array */ + trg = (TRGM *) + MemoryContextAllocZero(rcontext, + TRGMHDRSIZE + + trgmNFA->totalTrgmCount * sizeof(trgm)); + trg->flag = ARRKEY; + SET_VARSIZE(trg, CALCGTSIZE(ARRKEY, trgmNFA->totalTrgmCount)); + p = GETARR(trg); + for (i = 0; i < trgmNFA->colorTrgmsCount; i++) + { + ColorTrgmInfo *colorTrgm = &trgmNFA->colorTrgms[i]; + TrgmColorInfo *c[3]; + trgm_mb_char s[3]; + int j, + i1, + i2, + i3; + + /* Ignore any unexpanded trigrams ... */ + if (!colorTrgm->expanded) + continue; + + /* Get colors, substituting the dummy struct for COLOR_BLANK */ + for (j = 0; j < 3; j++) + { + if (colorTrgm->ctrgm.colors[j] != COLOR_BLANK) + c[j] = &trgmNFA->colorInfo[colorTrgm->ctrgm.colors[j]]; + else + c[j] = &blankColor; + } + + /* Iterate over all possible combinations of colors' characters */ + for (i1 = 0; i1 < c[0]->wordCharsCount; i1++) + { + s[0] = c[0]->wordChars[i1]; + for (i2 = 0; i2 < c[1]->wordCharsCount; i2++) + { + s[1] = c[1]->wordChars[i2]; + for (i3 = 0; i3 < c[2]->wordCharsCount; i3++) + { + s[2] = c[2]->wordChars[i3]; + fillTrgm(p, s); + p++; + } + } + } + } + + return trg; +} + +/* + * Convert trigram into trgm datatype. + */ +static void +fillTrgm(trgm *ptrgm, trgm_mb_char s[3]) +{ + char str[3 * MAX_MULTIBYTE_CHAR_LEN], + *p; + int i, + j; + + /* Write multibyte string into "str" (we don't need null termination) */ + p = str; + + for (i = 0; i < 3; i++) + { + if (s[i].bytes[0] != 0) + { + for (j = 0; j < MAX_MULTIBYTE_CHAR_LEN && s[i].bytes[j]; j++) + *p++ = s[i].bytes[j]; + } + else + { + /* Emit a space in place of COLOR_BLANK */ + *p++ = ' '; + } + } + + /* Convert "str" to a standard trigram (possibly hashing it) */ + compact_trigram(ptrgm, str, p - str); +} + +/* + * Merge two states of graph. + */ +static void +mergeStates(TrgmState *state1, TrgmState *state2) +{ + Assert(state1 != state2); + Assert(!state1->parent); + Assert(!state2->parent); + + /* state1 absorbs state2's flags */ + state1->flags |= state2->flags; + + /* state2, and indirectly all its children, become children of state1 */ + state2->parent = state1; +} + +/* + * Compare function for sorting of color trigrams by their colors. + */ +static int +colorTrgmInfoCmp(const void *p1, const void *p2) +{ + const ColorTrgmInfo *c1 = (const ColorTrgmInfo *) p1; + const ColorTrgmInfo *c2 = (const ColorTrgmInfo *) p2; + + return memcmp(&c1->ctrgm, &c2->ctrgm, sizeof(ColorTrgm)); +} + +/* + * Compare function for sorting color trigrams in descending order of + * their penalty fields. + */ +static int +colorTrgmInfoPenaltyCmp(const void *p1, const void *p2) +{ + float4 penalty1 = ((const ColorTrgmInfo *) p1)->penalty; + float4 penalty2 = ((const ColorTrgmInfo *) p2)->penalty; + + if (penalty1 < penalty2) + return 1; + else if (penalty1 == penalty2) + return 0; + else + return -1; +} + + +/*--------------------- + * Subroutines for packing the graph into final representation (stage 4). + *--------------------- + */ + +/* + * Pack expanded graph into final representation. + * + * The result data must be allocated in rcontext. + */ +static TrgmPackedGraph * +packGraph(TrgmNFA *trgmNFA, MemoryContext rcontext) +{ + int snumber = 2, + arcIndex, + arcsCount; + HASH_SEQ_STATUS scan_status; + TrgmState *state; + TrgmPackArcInfo *arcs; + TrgmPackedArc *packedArcs; + TrgmPackedGraph *result; + int i, + j; + + /* Enumerate surviving states, giving init and fin reserved numbers */ + hash_seq_init(&scan_status, trgmNFA->states); + while ((state = (TrgmState *) hash_seq_search(&scan_status)) != NULL) + { + while (state->parent) + state = state->parent; + + if (state->snumber < 0) + { + if (state->flags & TSTATE_INIT) + state->snumber = 0; + else if (state->flags & TSTATE_FIN) + state->snumber = 1; + else + { + state->snumber = snumber; + snumber++; + } + } + } + + /* Collect array of all arcs */ + arcs = (TrgmPackArcInfo *) + palloc(sizeof(TrgmPackArcInfo) * trgmNFA->arcsCount); + arcIndex = 0; + hash_seq_init(&scan_status, trgmNFA->states); + while ((state = (TrgmState *) hash_seq_search(&scan_status)) != NULL) + { + TrgmState *source = state; + ListCell *cell; + + while (source->parent) + source = source->parent; + + foreach(cell, state->arcs) + { + TrgmArc *arc = (TrgmArc *) lfirst(cell); + TrgmState *target = arc->target; + + while (target->parent) + target = target->parent; + + if (source->snumber != target->snumber) + { + ColorTrgmInfo *ctrgm; + + ctrgm = (ColorTrgmInfo *) bsearch(&arc->ctrgm, + trgmNFA->colorTrgms, + trgmNFA->colorTrgmsCount, + sizeof(ColorTrgmInfo), + colorTrgmInfoCmp); + Assert(ctrgm != NULL); + Assert(ctrgm->expanded); + + arcs[arcIndex].sourceState = source->snumber; + arcs[arcIndex].targetState = target->snumber; + arcs[arcIndex].colorTrgm = ctrgm->cnumber; + arcIndex++; + } + } + } + + /* Sort arcs to ease duplicate detection */ + qsort(arcs, arcIndex, sizeof(TrgmPackArcInfo), packArcInfoCmp); + + /* We could have duplicates because states were merged. Remove them. */ + if (arcIndex > 1) + { + /* p1 is probe point, p2 is last known non-duplicate. */ + TrgmPackArcInfo *p1, + *p2; + + p2 = arcs; + for (p1 = arcs + 1; p1 < arcs + arcIndex; p1++) + { + if (packArcInfoCmp(p1, p2) > 0) + { + p2++; + *p2 = *p1; + } + } + arcsCount = (p2 - arcs) + 1; + } + else + arcsCount = arcIndex; + + /* Create packed representation */ + result = (TrgmPackedGraph *) + MemoryContextAlloc(rcontext, sizeof(TrgmPackedGraph)); + + /* Pack color trigrams information */ + result->colorTrigramsCount = 0; + for (i = 0; i < trgmNFA->colorTrgmsCount; i++) + { + if (trgmNFA->colorTrgms[i].expanded) + result->colorTrigramsCount++; + } + result->colorTrigramGroups = (int *) + MemoryContextAlloc(rcontext, sizeof(int) * result->colorTrigramsCount); + j = 0; + for (i = 0; i < trgmNFA->colorTrgmsCount; i++) + { + if (trgmNFA->colorTrgms[i].expanded) + { + result->colorTrigramGroups[j] = trgmNFA->colorTrgms[i].count; + j++; + } + } + + /* Pack states and arcs information */ + result->statesCount = snumber; + result->states = (TrgmPackedState *) + MemoryContextAlloc(rcontext, snumber * sizeof(TrgmPackedState)); + packedArcs = (TrgmPackedArc *) + MemoryContextAlloc(rcontext, arcsCount * sizeof(TrgmPackedArc)); + j = 0; + for (i = 0; i < snumber; i++) + { + int cnt = 0; + + result->states[i].arcs = &packedArcs[j]; + while (j < arcsCount && arcs[j].sourceState == i) + { + packedArcs[j].targetState = arcs[j].targetState; + packedArcs[j].colorTrgm = arcs[j].colorTrgm; + cnt++; + j++; + } + result->states[i].arcsCount = cnt; + } + + /* Allocate working memory for trigramsMatchGraph() */ + result->colorTrigramsActive = (bool *) + MemoryContextAlloc(rcontext, sizeof(bool) * result->colorTrigramsCount); + result->statesActive = (bool *) + MemoryContextAlloc(rcontext, sizeof(bool) * result->statesCount); + result->statesQueue = (int *) + MemoryContextAlloc(rcontext, sizeof(int) * result->statesCount); + + return result; +} + +/* + * Comparison function for sorting TrgmPackArcInfos. + * + * Compares arcs in following order: sourceState, colorTrgm, targetState. + */ +static int +packArcInfoCmp(const void *a1, const void *a2) +{ + const TrgmPackArcInfo *p1 = (const TrgmPackArcInfo *) a1; + const TrgmPackArcInfo *p2 = (const TrgmPackArcInfo *) a2; + + if (p1->sourceState < p2->sourceState) + return -1; + if (p1->sourceState > p2->sourceState) + return 1; + if (p1->colorTrgm < p2->colorTrgm) + return -1; + if (p1->colorTrgm > p2->colorTrgm) + return 1; + if (p1->targetState < p2->targetState) + return -1; + if (p1->targetState > p2->targetState) + return 1; + return 0; +} + + +/*--------------------- + * Debugging functions + * + * These are designed to emit GraphViz files. + *--------------------- + */ + +#ifdef TRGM_REGEXP_DEBUG + +/* + * Print initial NFA, in regexp library's representation + */ +static void +printSourceNFA(regex_t *regex, TrgmColorInfo *colors, int ncolors) +{ + StringInfoData buf; + int nstates = pg_reg_getnumstates(regex); + int state; + int i; + + initStringInfo(&buf); + + appendStringInfoString(&buf, "\ndigraph sourceNFA {\n"); + + for (state = 0; state < nstates; state++) + { + regex_arc_t *arcs; + int i, + arcsCount; + + appendStringInfo(&buf, "s%d", state); + if (pg_reg_getfinalstate(regex) == state) + appendStringInfoString(&buf, " [shape = doublecircle]"); + appendStringInfoString(&buf, ";\n"); + + arcsCount = pg_reg_getnumoutarcs(regex, state); + arcs = (regex_arc_t *) palloc(sizeof(regex_arc_t) * arcsCount); + pg_reg_getoutarcs(regex, state, arcs, arcsCount); + + for (i = 0; i < arcsCount; i++) + { + appendStringInfo(&buf, " s%d -> s%d [label = \"%d\"];\n", + state, arcs[i].to, arcs[i].co); + } + + pfree(arcs); + } + + appendStringInfoString(&buf, " node [shape = point ]; initial;\n"); + appendStringInfo(&buf, " initial -> s%d;\n", + pg_reg_getinitialstate(regex)); + + /* Print colors */ + appendStringInfoString(&buf, " { rank = sink;\n"); + appendStringInfoString(&buf, " Colors [shape = none, margin=0, label=<\n"); + + for (i = 0; i < ncolors; i++) + { + TrgmColorInfo *color = &colors[i]; + int j; + + appendStringInfo(&buf, "<br/>Color %d: ", i); + if (color->expandable) + { + for (j = 0; j < color->wordCharsCount; j++) + { + char s[MAX_MULTIBYTE_CHAR_LEN + 1]; + + memcpy(s, color->wordChars[j].bytes, MAX_MULTIBYTE_CHAR_LEN); + s[MAX_MULTIBYTE_CHAR_LEN] = '\0'; + appendStringInfoString(&buf, s); + } + } + else + appendStringInfoString(&buf, "not expandable"); + appendStringInfoChar(&buf, '\n'); + } + + appendStringInfoString(&buf, " >];\n"); + appendStringInfoString(&buf, " }\n"); + appendStringInfoString(&buf, "}\n"); + + { + /* dot -Tpng -o /tmp/source.png < /tmp/source.gv */ + FILE *fp = fopen("/tmp/source.gv", "w"); + + fprintf(fp, "%s", buf.data); + fclose(fp); + } + + pfree(buf.data); +} + +/* + * Print expanded graph. + */ +static void +printTrgmNFA(TrgmNFA *trgmNFA) +{ + StringInfoData buf; + HASH_SEQ_STATUS scan_status; + TrgmState *state; + TrgmState *initstate = NULL; + + initStringInfo(&buf); + + appendStringInfoString(&buf, "\ndigraph transformedNFA {\n"); + + hash_seq_init(&scan_status, trgmNFA->states); + while ((state = (TrgmState *) hash_seq_search(&scan_status)) != NULL) + { + ListCell *cell; + + appendStringInfo(&buf, "s%d", -state->snumber); + if (state->flags & TSTATE_FIN) + appendStringInfoString(&buf, " [shape = doublecircle]"); + if (state->flags & TSTATE_INIT) + initstate = state; + appendStringInfo(&buf, " [label = \"%d\"]", state->stateKey.nstate); + appendStringInfoString(&buf, ";\n"); + + foreach(cell, state->arcs) + { + TrgmArc *arc = (TrgmArc *) lfirst(cell); + + appendStringInfo(&buf, " s%d -> s%d [label = \"", + -state->snumber, -arc->target->snumber); + printTrgmColor(&buf, arc->ctrgm.colors[0]); + appendStringInfoChar(&buf, ' '); + printTrgmColor(&buf, arc->ctrgm.colors[1]); + appendStringInfoChar(&buf, ' '); + printTrgmColor(&buf, arc->ctrgm.colors[2]); + appendStringInfoString(&buf, "\"];\n"); + } + } + + if (initstate) + { + appendStringInfoString(&buf, " node [shape = point ]; initial;\n"); + appendStringInfo(&buf, " initial -> s%d;\n", -initstate->snumber); + } + + appendStringInfoString(&buf, "}\n"); + + { + /* dot -Tpng -o /tmp/transformed.png < /tmp/transformed.gv */ + FILE *fp = fopen("/tmp/transformed.gv", "w"); + + fprintf(fp, "%s", buf.data); + fclose(fp); + } + + pfree(buf.data); +} + +/* + * Print a TrgmColor readably. + */ +static void +printTrgmColor(StringInfo buf, TrgmColor co) +{ + if (co == COLOR_UNKNOWN) + appendStringInfoChar(buf, 'u'); + else if (co == COLOR_BLANK) + appendStringInfoChar(buf, 'b'); + else + appendStringInfo(buf, "%d", (int) co); +} + +/* + * Print final packed representation of trigram-based expanded graph. + */ +static void +printTrgmPackedGraph(TrgmPackedGraph *packedGraph, TRGM *trigrams) +{ + StringInfoData buf; + trgm *p; + int i; + + initStringInfo(&buf); + + appendStringInfoString(&buf, "\ndigraph packedGraph {\n"); + + for (i = 0; i < packedGraph->statesCount; i++) + { + TrgmPackedState *state = &packedGraph->states[i]; + int j; + + appendStringInfo(&buf, " s%d", i); + if (i == 1) + appendStringInfoString(&buf, " [shape = doublecircle]"); + + appendStringInfo(&buf, " [label = <s%d>];\n", i); + + for (j = 0; j < state->arcsCount; j++) + { + TrgmPackedArc *arc = &state->arcs[j]; + + appendStringInfo(&buf, " s%d -> s%d [label = \"trigram %d\"];\n", + i, arc->targetState, arc->colorTrgm); + } + } + + appendStringInfoString(&buf, " node [shape = point ]; initial;\n"); + appendStringInfo(&buf, " initial -> s%d;\n", 0); + + /* Print trigrams */ + appendStringInfoString(&buf, " { rank = sink;\n"); + appendStringInfoString(&buf, " Trigrams [shape = none, margin=0, label=<\n"); + + p = GETARR(trigrams); + for (i = 0; i < packedGraph->colorTrigramsCount; i++) + { + int count = packedGraph->colorTrigramGroups[i]; + int j; + + appendStringInfo(&buf, "<br/>Trigram %d: ", i); + + for (j = 0; j < count; j++) + { + if (j > 0) + appendStringInfoString(&buf, ", "); + + /* + * XXX This representation is nice only for all-ASCII trigrams. + */ + appendStringInfo(&buf, "\"%c%c%c\"", (*p)[0], (*p)[1], (*p)[2]); + p++; + } + } + + appendStringInfoString(&buf, " >];\n"); + appendStringInfoString(&buf, " }\n"); + appendStringInfoString(&buf, "}\n"); + + { + /* dot -Tpng -o /tmp/packed.png < /tmp/packed.gv */ + FILE *fp = fopen("/tmp/packed.gv", "w"); + + fprintf(fp, "%s", buf.data); + fclose(fp); + } + + pfree(buf.data); +} + +#endif /* TRGM_REGEXP_DEBUG */ |